001/* 002 * Copyright 2009-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2014 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 * Indicates whether this object encoder may be used to encode or decode 054 * objects of the specified type. 055 * 056 * @param t The type of object for which to make the determination. 057 * 058 * @return {@code true} if this object encoder may be used for objects of 059 * the specified type, or {@code false} if not. 060 */ 061 public abstract boolean supportsType(final Type t); 062 063 064 065 /** 066 * Constructs a definition for an LDAP attribute type which may be added to 067 * the directory server schema to allow it to hold the value of the specified 068 * field. Note that the object identifier used for the constructed attribute 069 * type definition is not required to be valid or unique. 070 * 071 * @param f The field for which to construct an LDAP attribute type 072 * definition. It will include the {@link LDAPField} annotation 073 * type. 074 * 075 * @return The constructed attribute type definition. 076 * 077 * @throws LDAPPersistException If this object encoder does not support 078 * encoding values for the associated field 079 * type. 080 */ 081 public final AttributeTypeDefinition constructAttributeType(final Field f) 082 throws LDAPPersistException 083 { 084 return constructAttributeType(f, DefaultOIDAllocator.getInstance()); 085 } 086 087 088 089 /** 090 * Constructs a definition for an LDAP attribute type which may be added to 091 * the directory server schema to allow it to hold the value of the specified 092 * field. 093 * 094 * @param f The field for which to construct an LDAP attribute type 095 * definition. It will include the {@link LDAPField} annotation 096 * type. 097 * @param a The OID allocator to use to generate the object identifier. It 098 * must not be {@code null}. 099 * 100 * @return The constructed attribute type definition. 101 * 102 * @throws LDAPPersistException If this object encoder does not support 103 * encoding values for the associated field 104 * type. 105 */ 106 public abstract AttributeTypeDefinition constructAttributeType(final Field f, 107 final OIDAllocator a) 108 throws LDAPPersistException; 109 110 111 112 /** 113 * Constructs a definition for an LDAP attribute type which may be added to 114 * the directory server schema to allow it to hold the value returned by the 115 * specified method. Note that the object identifier used for the constructed 116 * attribute type definition is not required to be valid or unique. 117 * 118 * @param m The method for which to construct an LDAP attribute type 119 * definition. It will include the {@link LDAPGetter} 120 * annotation type. 121 * 122 * @return The constructed attribute type definition. 123 * 124 * @throws LDAPPersistException If this object encoder does not support 125 * encoding values for the associated method 126 * type. 127 */ 128 public final AttributeTypeDefinition constructAttributeType(final Method m) 129 throws LDAPPersistException 130 { 131 return constructAttributeType(m, DefaultOIDAllocator.getInstance()); 132 } 133 134 135 136 /** 137 * Constructs a definition for an LDAP attribute type which may be added to 138 * the directory server schema to allow it to hold the value returned by the 139 * specified method. Note that the object identifier used for the constructed 140 * attribute type definition is not required to be valid or unique. 141 * 142 * @param m The method for which to construct an LDAP attribute type 143 * definition. It will include the {@link LDAPGetter} 144 * annotation type. 145 * @param a The OID allocator to use to generate the object identifier. It 146 * must not be {@code null}. 147 * 148 * @return The constructed attribute type definition. 149 * 150 * @throws LDAPPersistException If this object encoder does not support 151 * encoding values for the associated method 152 * type. 153 */ 154 public abstract AttributeTypeDefinition constructAttributeType(final Method m, 155 final OIDAllocator a) 156 throws LDAPPersistException; 157 158 159 160 /** 161 * Indicates whether the provided field can hold multiple values. 162 * 163 * @param field The field for which to make the determination. It must be 164 * marked with the {@link LDAPField} annotation. 165 * 166 * @return {@code true} if the provided field can hold multiple values, or 167 * {@code false} if not. 168 */ 169 public abstract boolean supportsMultipleValues(final Field field); 170 171 172 173 /** 174 * Indicates whether the provided setter method takes an argument that can 175 * hold multiple values. 176 * 177 * @param method The setter method for which to make the determination. It 178 * must be marked with the {@link LDAPSetter} annotation 179 * type and conform to the constraints associated with that 180 * annotation. 181 * 182 * @return {@code true} if the provided method takes an argument that can 183 * hold multiple values, or {@code false} if not. 184 */ 185 public abstract boolean supportsMultipleValues(final Method method); 186 187 188 189 /** 190 * Encodes the provided field to an LDAP attribute. 191 * 192 * @param field The field to be encoded. 193 * @param value The value for the field in the object to be encoded. 194 * @param name The name to use for the constructed attribute. 195 * 196 * @return The attribute containing the encoded representation of the 197 * provided field. 198 * 199 * @throws LDAPPersistException If a problem occurs while attempting to 200 * construct an attribute for the field. 201 */ 202 public abstract Attribute encodeFieldValue(final Field field, 203 final Object value, 204 final String name) 205 throws LDAPPersistException; 206 207 208 209 /** 210 * Encodes the provided method to an LDAP attribute. 211 * 212 * @param method The method to be encoded. 213 * @param value The value returned by the method in the object to be 214 * encoded. 215 * @param name The name to use for the constructed attribute. 216 * 217 * @return The attribute containing the encoded representation of the 218 * provided method value. 219 * 220 * @throws LDAPPersistException If a problem occurs while attempting to 221 * construct an attribute for the method. 222 */ 223 public abstract Attribute encodeMethodValue(final Method method, 224 final Object value, 225 final String name) 226 throws LDAPPersistException; 227 228 229 230 /** 231 * Updates the provided object to assign a value for the specified field from 232 * the contents of the given attribute. 233 * 234 * @param field The field to update in the provided object. 235 * @param object The object to be updated. 236 * @param attribute The attribute whose value(s) should be used to update 237 * the specified field in the given object. 238 * 239 * @throws LDAPPersistException If a problem occurs while attempting to 240 * assign a value to the specified field. 241 */ 242 public abstract void decodeField(final Field field, final Object object, 243 final Attribute attribute) 244 throws LDAPPersistException; 245 246 247 248 /** 249 * Assigns a {@code null} value to the provided field, if possible. If the 250 * field type is primitive and cannot be assigned a {@code null} value, then a 251 * default primitive value will be assigned instead (0 for numeric values, 252 * false for {@code boolean} values, and the null character for {@code char} 253 * values). 254 * 255 * @param f The field to which the {@code null} value should be assigned. 256 * It must not be {@code null} and must be marked with the 257 * {@link LDAPField} annotation. 258 * @param o The object to be updated. It must not be {@code null}, and the 259 * class must be marked with the {@link LDAPObject annotation}. 260 * 261 * @throws LDAPPersistException If a problem occurs while attempting to 262 * assign a {@code null} value to the specified 263 * field. 264 */ 265 public void setNull(final Field f, final Object o) 266 throws LDAPPersistException 267 { 268 try 269 { 270 f.setAccessible(true); 271 272 final Class<?> type = f.getType(); 273 if (type.equals(Boolean.TYPE)) 274 { 275 f.set(o, Boolean.FALSE); 276 } 277 else if (type.equals(Byte.TYPE)) 278 { 279 f.set(o, (byte) 0); 280 } 281 else if (type.equals(Character.TYPE)) 282 { 283 f.set(o, '\u0000'); 284 } 285 else if (type.equals(Double.TYPE)) 286 { 287 f.set(o, 0.0d); 288 } 289 else if (type.equals(Float.TYPE)) 290 { 291 f.set(o, 0.0f); 292 } 293 else if (type.equals(Integer.TYPE)) 294 { 295 f.set(o, 0); 296 } 297 else if (type.equals(Long.TYPE)) 298 { 299 f.set(o, 0L); 300 } 301 else if (type.equals(Short.TYPE)) 302 { 303 f.set(o, (short) 0); 304 } 305 else 306 { 307 f.set(o, null); 308 } 309 } 310 catch (Exception e) 311 { 312 debugException(e); 313 throw new LDAPPersistException( 314 ERR_ENCODER_CANNOT_SET_NULL_FIELD_VALUE.get(f.getName(), 315 o.getClass().getName(), getExceptionMessage(e)), e); 316 } 317 } 318 319 320 321 /** 322 * Invokes the provided setter method with a single argument that will set a 323 * {@code null} value for that method, if possible. If the argument type is 324 * and cannot be assigned a {@code null} value, then a default primitive value 325 * will be assigned instead (0 for numeric values, false for {@code boolean} 326 * values, and the null character for {@code char} values). 327 * 328 * @param m The setter method that should be used to set the {@code null} 329 * value. It must not be {@code null}, and must have the 330 * {@code LDAPSetter} annotation. 331 * @param o The object to be updated. It must not be {@code null}, and the 332 * class must be marked with the {@link LDAPObject annotation}. 333 * 334 * @throws LDAPPersistException If a problem occurs while attempting to 335 * assign a {@code null} value to the specified 336 * field. 337 */ 338 public void setNull(final Method m, final Object o) 339 throws LDAPPersistException 340 { 341 try 342 { 343 m.setAccessible(true); 344 345 final Class<?> type = m.getParameterTypes()[0]; 346 if (type.equals(Boolean.TYPE)) 347 { 348 m.invoke(o, Boolean.FALSE); 349 } 350 else if (type.equals(Byte.TYPE)) 351 { 352 m.invoke(o, (byte) 0); 353 } 354 else if (type.equals(Character.TYPE)) 355 { 356 m.invoke(o, '\u0000'); 357 } 358 else if (type.equals(Double.TYPE)) 359 { 360 m.invoke(o, 0.0d); 361 } 362 else if (type.equals(Float.TYPE)) 363 { 364 m.invoke(o, 0.0f); 365 } 366 else if (type.equals(Integer.TYPE)) 367 { 368 m.invoke(o, 0); 369 } 370 else if (type.equals(Long.TYPE)) 371 { 372 m.invoke(o, 0L); 373 } 374 else if (type.equals(Short.TYPE)) 375 { 376 m.invoke(o, (short) 0); 377 } 378 else 379 { 380 m.invoke(o, type.cast(null)); 381 } 382 } 383 catch (Exception e) 384 { 385 debugException(e); 386 throw new LDAPPersistException( 387 ERR_ENCODER_CANNOT_SET_NULL_METHOD_VALUE.get(m.getName(), 388 o.getClass().getName(), getExceptionMessage(e)), e); 389 } 390 } 391 392 393 394 /** 395 * Updates the provided object to invoke the specified method to set a value 396 * from the contents of the given attribute. 397 * 398 * @param method The method to invoke in the provided object. 399 * @param object The object to be updated. 400 * @param attribute The attribute whose value(s) should be used to update 401 * the specified method in the given object. 402 * 403 * @throws LDAPPersistException If a problem occurs while attempting to 404 * determine the value or invoke the specified 405 * method. 406 */ 407 public abstract void invokeSetter(final Method method, final Object object, 408 final Attribute attribute) 409 throws LDAPPersistException; 410}