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