001 /*
002 * Copyright 2009-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2016 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 * 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 }