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}