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