001/* 002 * Copyright 2009-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2019 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.persist; 022 023 024 025import java.io.Serializable; 026import java.lang.reflect.Field; 027import java.lang.reflect.Method; 028import java.lang.reflect.Type; 029 030import com.unboundid.ldap.sdk.Attribute; 031import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 032import com.unboundid.util.Debug; 033import com.unboundid.util.Extensible; 034import com.unboundid.util.StaticUtils; 035import com.unboundid.util.ThreadSafety; 036import com.unboundid.util.ThreadSafetyLevel; 037 038import static com.unboundid.ldap.sdk.persist.PersistMessages.*; 039 040 041 042/** 043 * This class provides an API for converting between Java objects and LDAP 044 * attributes. Concrete instances of this class must provide a default 045 * zero-argument constructor. 046 */ 047@Extensible() 048@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 049public abstract class ObjectEncoder 050 implements Serializable 051{ 052 /** 053 * The serial version UID for this serializable class. 054 */ 055 private static final long serialVersionUID = -5139516629886911696L; 056 057 058 059 /** 060 * Indicates whether this object encoder may be used to encode or decode 061 * objects of the specified type. 062 * 063 * @param t The type of object for which to make the determination. 064 * 065 * @return {@code true} if this object encoder may be used for objects of 066 * the specified type, or {@code false} if not. 067 */ 068 public abstract boolean supportsType(Type t); 069 070 071 072 /** 073 * Constructs a definition for an LDAP attribute type which may be added to 074 * the directory server schema to allow it to hold the value of the specified 075 * field. Note that the object identifier used for the constructed attribute 076 * type definition is not required to be valid or unique. 077 * 078 * @param f The field for which to construct an LDAP attribute type 079 * definition. It will include the {@link LDAPField} annotation 080 * type. 081 * 082 * @return The constructed attribute type definition. 083 * 084 * @throws LDAPPersistException If this object encoder does not support 085 * encoding values for the associated field 086 * type. 087 */ 088 public final AttributeTypeDefinition constructAttributeType(final Field f) 089 throws LDAPPersistException 090 { 091 return constructAttributeType(f, DefaultOIDAllocator.getInstance()); 092 } 093 094 095 096 /** 097 * Constructs a definition for an LDAP attribute type which may be added to 098 * the directory server schema to allow it to hold the value of the specified 099 * field. 100 * 101 * @param f The field for which to construct an LDAP attribute type 102 * definition. It will include the {@link LDAPField} annotation 103 * type. 104 * @param a The OID allocator to use to generate the object identifier. It 105 * must not be {@code null}. 106 * 107 * @return The constructed attribute type definition. 108 * 109 * @throws LDAPPersistException If this object encoder does not support 110 * encoding values for the associated field 111 * type. 112 */ 113 public abstract AttributeTypeDefinition constructAttributeType(Field f, 114 OIDAllocator a) 115 throws LDAPPersistException; 116 117 118 119 /** 120 * Constructs a definition for an LDAP attribute type which may be added to 121 * the directory server schema to allow it to hold the value returned by the 122 * specified method. Note that the object identifier used for the constructed 123 * attribute type definition is not required to be valid or unique. 124 * 125 * @param m The method for which to construct an LDAP attribute type 126 * definition. It will include the {@link LDAPGetter} 127 * annotation type. 128 * 129 * @return The constructed attribute type definition. 130 * 131 * @throws LDAPPersistException If this object encoder does not support 132 * encoding values for the associated method 133 * type. 134 */ 135 public final AttributeTypeDefinition constructAttributeType(final Method m) 136 throws LDAPPersistException 137 { 138 return constructAttributeType(m, DefaultOIDAllocator.getInstance()); 139 } 140 141 142 143 /** 144 * Constructs a definition for an LDAP attribute type which may be added to 145 * the directory server schema to allow it to hold the value returned by the 146 * specified method. Note that the object identifier used for the constructed 147 * attribute type definition is not required to be valid or unique. 148 * 149 * @param m The method for which to construct an LDAP attribute type 150 * definition. It will include the {@link LDAPGetter} 151 * annotation type. 152 * @param a The OID allocator to use to generate the object identifier. It 153 * must not be {@code null}. 154 * 155 * @return The constructed attribute type definition. 156 * 157 * @throws LDAPPersistException If this object encoder does not support 158 * encoding values for the associated method 159 * type. 160 */ 161 public abstract AttributeTypeDefinition constructAttributeType(Method m, 162 OIDAllocator a) 163 throws LDAPPersistException; 164 165 166 167 /** 168 * Indicates whether the provided field can hold multiple values. 169 * 170 * @param field The field for which to make the determination. It must be 171 * marked with the {@link LDAPField} annotation. 172 * 173 * @return {@code true} if the provided field can hold multiple values, or 174 * {@code false} if not. 175 */ 176 public abstract boolean supportsMultipleValues(Field field); 177 178 179 180 /** 181 * Indicates whether the provided setter method takes an argument that can 182 * hold multiple values. 183 * 184 * @param method The setter method for which to make the determination. It 185 * must be marked with the {@link LDAPSetter} annotation 186 * type and conform to the constraints associated with that 187 * annotation. 188 * 189 * @return {@code true} if the provided method takes an argument that can 190 * hold multiple values, or {@code false} if not. 191 */ 192 public abstract boolean supportsMultipleValues(Method method); 193 194 195 196 /** 197 * Encodes the provided field to an LDAP attribute. 198 * 199 * @param field The field to be encoded. 200 * @param value The value for the field in the object to be encoded. 201 * @param name The name to use for the constructed attribute. 202 * 203 * @return The attribute containing the encoded representation of the 204 * provided field. 205 * 206 * @throws LDAPPersistException If a problem occurs while attempting to 207 * construct an attribute for the field. 208 */ 209 public abstract Attribute encodeFieldValue(Field field, Object value, 210 String name) 211 throws LDAPPersistException; 212 213 214 215 /** 216 * Encodes the provided method to an LDAP attribute. 217 * 218 * @param method The method to be encoded. 219 * @param value The value returned by the method in the object to be 220 * encoded. 221 * @param name The name to use for the constructed attribute. 222 * 223 * @return The attribute containing the encoded representation of the 224 * provided method value. 225 * 226 * @throws LDAPPersistException If a problem occurs while attempting to 227 * construct an attribute for the method. 228 */ 229 public abstract Attribute encodeMethodValue(Method method, Object value, 230 String name) 231 throws LDAPPersistException; 232 233 234 235 /** 236 * Updates the provided object to assign a value for the specified field from 237 * the contents of the given attribute. 238 * 239 * @param field The field to update in the provided object. 240 * @param object The object to be updated. 241 * @param attribute The attribute whose value(s) should be used to update 242 * the specified field in the given object. 243 * 244 * @throws LDAPPersistException If a problem occurs while attempting to 245 * assign a value to the specified field. 246 */ 247 public abstract void decodeField(Field field, Object object, 248 Attribute attribute) 249 throws LDAPPersistException; 250 251 252 253 /** 254 * Assigns a {@code null} value to the provided field, if possible. If the 255 * field type is primitive and cannot be assigned a {@code null} value, then a 256 * default primitive value will be assigned instead (0 for numeric values, 257 * false for {@code boolean} values, and the null character for {@code char} 258 * values). 259 * 260 * @param f The field to which the {@code null} value should be assigned. 261 * It must not be {@code null} and must be marked with the 262 * {@link LDAPField} annotation. 263 * @param o The object to be updated. It must not be {@code null}, and the 264 * class must be marked with the {@link LDAPObject annotation}. 265 * 266 * @throws LDAPPersistException If a problem occurs while attempting to 267 * assign a {@code null} value to the specified 268 * field. 269 */ 270 public void setNull(final Field f, final Object o) 271 throws LDAPPersistException 272 { 273 try 274 { 275 f.setAccessible(true); 276 277 final Class<?> type = f.getType(); 278 if (type.equals(Boolean.TYPE)) 279 { 280 f.set(o, Boolean.FALSE); 281 } 282 else if (type.equals(Byte.TYPE)) 283 { 284 f.set(o, (byte) 0); 285 } 286 else if (type.equals(Character.TYPE)) 287 { 288 f.set(o, '\u0000'); 289 } 290 else if (type.equals(Double.TYPE)) 291 { 292 f.set(o, 0.0d); 293 } 294 else if (type.equals(Float.TYPE)) 295 { 296 f.set(o, 0.0f); 297 } 298 else if (type.equals(Integer.TYPE)) 299 { 300 f.set(o, 0); 301 } 302 else if (type.equals(Long.TYPE)) 303 { 304 f.set(o, 0L); 305 } 306 else if (type.equals(Short.TYPE)) 307 { 308 f.set(o, (short) 0); 309 } 310 else 311 { 312 f.set(o, null); 313 } 314 } 315 catch (final Exception e) 316 { 317 Debug.debugException(e); 318 throw new LDAPPersistException( 319 ERR_ENCODER_CANNOT_SET_NULL_FIELD_VALUE.get(f.getName(), 320 o.getClass().getName(), StaticUtils.getExceptionMessage(e)), 321 e); 322 } 323 } 324 325 326 327 /** 328 * Invokes the provided setter method with a single argument that will set a 329 * {@code null} value for that method, if possible. If the argument type is 330 * and cannot be assigned a {@code null} value, then a default primitive value 331 * will be assigned instead (0 for numeric values, false for {@code boolean} 332 * values, and the null character for {@code char} values). 333 * 334 * @param m The setter method that should be used to set the {@code null} 335 * value. It must not be {@code null}, and must have the 336 * {@code LDAPSetter} annotation. 337 * @param o The object to be updated. It must not be {@code null}, and the 338 * class must be marked with the {@link LDAPObject annotation}. 339 * 340 * @throws LDAPPersistException If a problem occurs while attempting to 341 * assign a {@code null} value to the specified 342 * field. 343 */ 344 public void setNull(final Method m, final Object o) 345 throws LDAPPersistException 346 { 347 try 348 { 349 m.setAccessible(true); 350 351 final Class<?> type = m.getParameterTypes()[0]; 352 if (type.equals(Boolean.TYPE)) 353 { 354 m.invoke(o, Boolean.FALSE); 355 } 356 else if (type.equals(Byte.TYPE)) 357 { 358 m.invoke(o, (byte) 0); 359 } 360 else if (type.equals(Character.TYPE)) 361 { 362 m.invoke(o, '\u0000'); 363 } 364 else if (type.equals(Double.TYPE)) 365 { 366 m.invoke(o, 0.0d); 367 } 368 else if (type.equals(Float.TYPE)) 369 { 370 m.invoke(o, 0.0f); 371 } 372 else if (type.equals(Integer.TYPE)) 373 { 374 m.invoke(o, 0); 375 } 376 else if (type.equals(Long.TYPE)) 377 { 378 m.invoke(o, 0L); 379 } 380 else if (type.equals(Short.TYPE)) 381 { 382 m.invoke(o, (short) 0); 383 } 384 else 385 { 386 m.invoke(o, type.cast(null)); 387 } 388 } 389 catch (final Exception e) 390 { 391 Debug.debugException(e); 392 throw new LDAPPersistException( 393 ERR_ENCODER_CANNOT_SET_NULL_METHOD_VALUE.get(m.getName(), 394 o.getClass().getName(), StaticUtils.getExceptionMessage(e)), 395 e); 396 } 397 } 398 399 400 401 /** 402 * Updates the provided object to invoke the specified method to set a value 403 * from the contents of the given attribute. 404 * 405 * @param method The method to invoke in the provided object. 406 * @param object The object to be updated. 407 * @param attribute The attribute whose value(s) should be used to update 408 * the specified method in the given object. 409 * 410 * @throws LDAPPersistException If a problem occurs while attempting to 411 * determine the value or invoke the specified 412 * method. 413 */ 414 public abstract void invokeSetter(Method method, Object object, 415 Attribute attribute) 416 throws LDAPPersistException; 417}