001/*
002 * Copyright 2009-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2019 Ping Identity Corporation
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.Debug;
033import com.unboundid.util.Extensible;
034import com.unboundid.util.StaticUtils;
035import com.unboundid.util.ThreadSafety;
036import com.unboundid.util.ThreadSafetyLevel;
037
038import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
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(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(Field f,
114                                                                 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(Method m,
162                                                                 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(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(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(Field field, Object value,
210                                             String name)
211         throws LDAPPersistException;
212
213
214
215  /**
216   * Encodes the provided method to an LDAP attribute.
217   *
218   * @param  method  The method to be encoded.
219   * @param  value   The value returned by the method in the object to be
220   *                 encoded.
221   * @param  name    The name to use for the constructed attribute.
222   *
223   * @return  The attribute containing the encoded representation of the
224   *          provided method value.
225   *
226   * @throws  LDAPPersistException  If a problem occurs while attempting to
227   *                                construct an attribute for the method.
228   */
229  public abstract Attribute encodeMethodValue(Method method, Object value,
230                                              String name)
231         throws LDAPPersistException;
232
233
234
235  /**
236   * Updates the provided object to assign a value for the specified field from
237   * the contents of the given attribute.
238   *
239   * @param  field      The field to update in the provided object.
240   * @param  object     The object to be updated.
241   * @param  attribute  The attribute whose value(s) should be used to update
242   *                    the specified field in the given object.
243   *
244   * @throws  LDAPPersistException  If a problem occurs while attempting to
245   *                                assign a value to the specified field.
246   */
247  public abstract void decodeField(Field field, Object object,
248                                   Attribute attribute)
249         throws LDAPPersistException;
250
251
252
253  /**
254   * Assigns a {@code null} value to the provided field, if possible.  If the
255   * field type is primitive and cannot be assigned a {@code null} value, then a
256   * default primitive value will be assigned instead (0 for numeric values,
257   * false for {@code boolean} values, and the null character for {@code char}
258   * values).
259   *
260   * @param  f  The field to which the {@code null} value should be assigned.
261   *            It must not be {@code null} and must be marked with the
262   *            {@link LDAPField} annotation.
263   * @param  o  The object to be updated.  It must not be {@code null}, and the
264   *            class must be marked with the {@link LDAPObject annotation}.
265   *
266   * @throws  LDAPPersistException  If a problem occurs while attempting to
267   *                                assign a {@code null} value to the specified
268   *                                field.
269   */
270  public void setNull(final Field f, final Object o)
271         throws LDAPPersistException
272  {
273    try
274    {
275      f.setAccessible(true);
276
277      final Class<?> type = f.getType();
278      if (type.equals(Boolean.TYPE))
279      {
280        f.set(o, Boolean.FALSE);
281      }
282      else if (type.equals(Byte.TYPE))
283      {
284        f.set(o, (byte) 0);
285      }
286      else if (type.equals(Character.TYPE))
287      {
288        f.set(o, '\u0000');
289      }
290      else if (type.equals(Double.TYPE))
291      {
292        f.set(o, 0.0d);
293      }
294      else if (type.equals(Float.TYPE))
295      {
296        f.set(o, 0.0f);
297      }
298      else if (type.equals(Integer.TYPE))
299      {
300        f.set(o, 0);
301      }
302      else if (type.equals(Long.TYPE))
303      {
304        f.set(o, 0L);
305      }
306      else if (type.equals(Short.TYPE))
307      {
308        f.set(o, (short) 0);
309      }
310      else
311      {
312        f.set(o, null);
313      }
314    }
315    catch (final Exception e)
316    {
317      Debug.debugException(e);
318      throw new LDAPPersistException(
319           ERR_ENCODER_CANNOT_SET_NULL_FIELD_VALUE.get(f.getName(),
320                o.getClass().getName(), StaticUtils.getExceptionMessage(e)),
321           e);
322    }
323  }
324
325
326
327  /**
328   * Invokes the provided setter method with a single argument that will set a
329   * {@code null} value for that method, if possible.  If the argument type is
330   * and cannot be assigned a {@code null} value, then a default primitive value
331   * will be assigned instead (0 for numeric values, false for {@code boolean}
332   * values, and the null character for {@code char} values).
333   *
334   * @param  m  The setter method that should be used to set the {@code null}
335   *            value.  It must not be {@code null}, and must have the
336   *            {@code LDAPSetter} annotation.
337   * @param  o  The object to be updated.  It must not be {@code null}, and the
338   *            class must be marked with the {@link LDAPObject annotation}.
339   *
340   * @throws  LDAPPersistException  If a problem occurs while attempting to
341   *                                assign a {@code null} value to the specified
342   *                                field.
343   */
344  public void setNull(final Method m, final Object o)
345         throws LDAPPersistException
346  {
347    try
348    {
349      m.setAccessible(true);
350
351      final Class<?> type = m.getParameterTypes()[0];
352      if (type.equals(Boolean.TYPE))
353      {
354        m.invoke(o, Boolean.FALSE);
355      }
356      else if (type.equals(Byte.TYPE))
357      {
358        m.invoke(o, (byte) 0);
359      }
360      else if (type.equals(Character.TYPE))
361      {
362        m.invoke(o, '\u0000');
363      }
364      else if (type.equals(Double.TYPE))
365      {
366        m.invoke(o, 0.0d);
367      }
368      else if (type.equals(Float.TYPE))
369      {
370        m.invoke(o, 0.0f);
371      }
372      else if (type.equals(Integer.TYPE))
373      {
374        m.invoke(o, 0);
375      }
376      else if (type.equals(Long.TYPE))
377      {
378        m.invoke(o, 0L);
379      }
380      else if (type.equals(Short.TYPE))
381      {
382        m.invoke(o, (short) 0);
383      }
384      else
385      {
386        m.invoke(o, type.cast(null));
387      }
388    }
389    catch (final Exception e)
390    {
391      Debug.debugException(e);
392      throw new LDAPPersistException(
393           ERR_ENCODER_CANNOT_SET_NULL_METHOD_VALUE.get(m.getName(),
394                o.getClass().getName(), StaticUtils.getExceptionMessage(e)),
395           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(Method method, Object object,
415                                    Attribute attribute)
416         throws LDAPPersistException;
417}