001/* 002 * Copyright 2009-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2017 UnboundID Corp. 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.Extensible; 033import com.unboundid.util.ThreadSafety; 034import com.unboundid.util.ThreadSafetyLevel; 035 036import static com.unboundid.ldap.sdk.persist.PersistMessages.*; 037import static com.unboundid.util.Debug.*; 038import static com.unboundid.util.StaticUtils.*; 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(final 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(final Field f, 114 final 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(final Method m, 162 final 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(final 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(final 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(final Field field, 210 final Object value, 211 final String name) 212 throws LDAPPersistException; 213 214 215 216 /** 217 * Encodes the provided method to an LDAP attribute. 218 * 219 * @param method The method to be encoded. 220 * @param value The value returned by the method in the object to be 221 * encoded. 222 * @param name The name to use for the constructed attribute. 223 * 224 * @return The attribute containing the encoded representation of the 225 * provided method value. 226 * 227 * @throws LDAPPersistException If a problem occurs while attempting to 228 * construct an attribute for the method. 229 */ 230 public abstract Attribute encodeMethodValue(final Method method, 231 final Object value, 232 final String name) 233 throws LDAPPersistException; 234 235 236 237 /** 238 * Updates the provided object to assign a value for the specified field from 239 * the contents of the given attribute. 240 * 241 * @param field The field to update in the provided object. 242 * @param object The object to be updated. 243 * @param attribute The attribute whose value(s) should be used to update 244 * the specified field in the given object. 245 * 246 * @throws LDAPPersistException If a problem occurs while attempting to 247 * assign a value to the specified field. 248 */ 249 public abstract void decodeField(final Field field, final Object object, 250 final Attribute attribute) 251 throws LDAPPersistException; 252 253 254 255 /** 256 * Assigns a {@code null} value to the provided field, if possible. If the 257 * field type is primitive and cannot be assigned a {@code null} value, then a 258 * default primitive value will be assigned instead (0 for numeric values, 259 * false for {@code boolean} values, and the null character for {@code char} 260 * values). 261 * 262 * @param f The field to which the {@code null} value should be assigned. 263 * It must not be {@code null} and must be marked with the 264 * {@link LDAPField} annotation. 265 * @param o The object to be updated. It must not be {@code null}, and the 266 * class must be marked with the {@link LDAPObject annotation}. 267 * 268 * @throws LDAPPersistException If a problem occurs while attempting to 269 * assign a {@code null} value to the specified 270 * field. 271 */ 272 public void setNull(final Field f, final Object o) 273 throws LDAPPersistException 274 { 275 try 276 { 277 f.setAccessible(true); 278 279 final Class<?> type = f.getType(); 280 if (type.equals(Boolean.TYPE)) 281 { 282 f.set(o, Boolean.FALSE); 283 } 284 else if (type.equals(Byte.TYPE)) 285 { 286 f.set(o, (byte) 0); 287 } 288 else if (type.equals(Character.TYPE)) 289 { 290 f.set(o, '\u0000'); 291 } 292 else if (type.equals(Double.TYPE)) 293 { 294 f.set(o, 0.0d); 295 } 296 else if (type.equals(Float.TYPE)) 297 { 298 f.set(o, 0.0f); 299 } 300 else if (type.equals(Integer.TYPE)) 301 { 302 f.set(o, 0); 303 } 304 else if (type.equals(Long.TYPE)) 305 { 306 f.set(o, 0L); 307 } 308 else if (type.equals(Short.TYPE)) 309 { 310 f.set(o, (short) 0); 311 } 312 else 313 { 314 f.set(o, null); 315 } 316 } 317 catch (Exception e) 318 { 319 debugException(e); 320 throw new LDAPPersistException( 321 ERR_ENCODER_CANNOT_SET_NULL_FIELD_VALUE.get(f.getName(), 322 o.getClass().getName(), getExceptionMessage(e)), e); 323 } 324 } 325 326 327 328 /** 329 * Invokes the provided setter method with a single argument that will set a 330 * {@code null} value for that method, if possible. If the argument type is 331 * and cannot be assigned a {@code null} value, then a default primitive value 332 * will be assigned instead (0 for numeric values, false for {@code boolean} 333 * values, and the null character for {@code char} values). 334 * 335 * @param m The setter method that should be used to set the {@code null} 336 * value. It must not be {@code null}, and must have the 337 * {@code LDAPSetter} annotation. 338 * @param o The object to be updated. It must not be {@code null}, and the 339 * class must be marked with the {@link LDAPObject annotation}. 340 * 341 * @throws LDAPPersistException If a problem occurs while attempting to 342 * assign a {@code null} value to the specified 343 * field. 344 */ 345 public void setNull(final Method m, final Object o) 346 throws LDAPPersistException 347 { 348 try 349 { 350 m.setAccessible(true); 351 352 final Class<?> type = m.getParameterTypes()[0]; 353 if (type.equals(Boolean.TYPE)) 354 { 355 m.invoke(o, Boolean.FALSE); 356 } 357 else if (type.equals(Byte.TYPE)) 358 { 359 m.invoke(o, (byte) 0); 360 } 361 else if (type.equals(Character.TYPE)) 362 { 363 m.invoke(o, '\u0000'); 364 } 365 else if (type.equals(Double.TYPE)) 366 { 367 m.invoke(o, 0.0d); 368 } 369 else if (type.equals(Float.TYPE)) 370 { 371 m.invoke(o, 0.0f); 372 } 373 else if (type.equals(Integer.TYPE)) 374 { 375 m.invoke(o, 0); 376 } 377 else if (type.equals(Long.TYPE)) 378 { 379 m.invoke(o, 0L); 380 } 381 else if (type.equals(Short.TYPE)) 382 { 383 m.invoke(o, (short) 0); 384 } 385 else 386 { 387 m.invoke(o, type.cast(null)); 388 } 389 } 390 catch (Exception e) 391 { 392 debugException(e); 393 throw new LDAPPersistException( 394 ERR_ENCODER_CANNOT_SET_NULL_METHOD_VALUE.get(m.getName(), 395 o.getClass().getName(), getExceptionMessage(e)), 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(final Method method, final Object object, 415 final Attribute attribute) 416 throws LDAPPersistException; 417}