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 */
021 package com.unboundid.ldap.sdk.persist;
022
023
024
025 import java.io.Serializable;
026 import java.lang.reflect.Field;
027 import java.lang.reflect.Method;
028 import java.lang.reflect.Type;
029
030 import com.unboundid.ldap.sdk.Attribute;
031 import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
032 import com.unboundid.util.Extensible;
033 import com.unboundid.util.ThreadSafety;
034 import com.unboundid.util.ThreadSafetyLevel;
035
036 import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
037 import static com.unboundid.util.Debug.*;
038 import 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)
049 public 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 }