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    }