001/*
002 * Copyright 2009-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-2020 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2009-2020 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.persist;
037
038
039
040import java.io.Serializable;
041import java.lang.reflect.Field;
042import java.lang.reflect.Method;
043import java.lang.reflect.Type;
044
045import com.unboundid.ldap.sdk.Attribute;
046import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
047import com.unboundid.util.Debug;
048import com.unboundid.util.Extensible;
049import com.unboundid.util.StaticUtils;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052
053import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
054
055
056
057/**
058 * This class provides an API for converting between Java objects and LDAP
059 * attributes.  Concrete instances of this class must provide a default
060 * zero-argument constructor.
061 */
062@Extensible()
063@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
064public abstract class ObjectEncoder
065       implements Serializable
066{
067  /**
068   * The serial version UID for this serializable class.
069   */
070  private static final long serialVersionUID = -5139516629886911696L;
071
072
073
074  /**
075   * Indicates whether this object encoder may be used to encode or decode
076   * objects of the specified type.
077   *
078   * @param  t  The type of object for which to make the determination.
079   *
080   * @return  {@code true} if this object encoder may be used for objects of
081   *          the specified type, or {@code false} if not.
082   */
083  public abstract boolean supportsType(Type t);
084
085
086
087  /**
088   * Constructs a definition for an LDAP attribute type which may be added to
089   * the directory server schema to allow it to hold the value of the specified
090   * field.  Note that the object identifier used for the constructed attribute
091   * type definition is not required to be valid or unique.
092   *
093   * @param  f  The field for which to construct an LDAP attribute type
094   *            definition.  It will include the {@link LDAPField} annotation
095   *            type.
096   *
097   * @return  The constructed attribute type definition.
098   *
099   * @throws  LDAPPersistException  If this object encoder does not support
100   *                                encoding values for the associated field
101   *                                type.
102   */
103  public final AttributeTypeDefinition constructAttributeType(final Field f)
104         throws LDAPPersistException
105  {
106    return constructAttributeType(f, DefaultOIDAllocator.getInstance());
107  }
108
109
110
111  /**
112   * Constructs a definition for an LDAP attribute type which may be added to
113   * the directory server schema to allow it to hold the value of the specified
114   * field.
115   *
116   * @param  f  The field for which to construct an LDAP attribute type
117   *            definition.  It will include the {@link LDAPField} annotation
118   *            type.
119   * @param  a  The OID allocator to use to generate the object identifier.  It
120   *            must not be {@code null}.
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 field
126   *                                type.
127   */
128  public abstract AttributeTypeDefinition constructAttributeType(Field f,
129                                                                 OIDAllocator a)
130         throws LDAPPersistException;
131
132
133
134  /**
135   * Constructs a definition for an LDAP attribute type which may be added to
136   * the directory server schema to allow it to hold the value returned by the
137   * specified method.  Note that the object identifier used for the constructed
138   * attribute type definition is not required to be valid or unique.
139   *
140   * @param  m  The method for which to construct an LDAP attribute type
141   *            definition.  It will include the {@link LDAPGetter}
142   *            annotation type.
143   *
144   * @return  The constructed attribute type definition.
145   *
146   * @throws  LDAPPersistException  If this object encoder does not support
147   *                                encoding values for the associated method
148   *                                type.
149   */
150  public final AttributeTypeDefinition constructAttributeType(final Method m)
151         throws LDAPPersistException
152  {
153    return constructAttributeType(m, DefaultOIDAllocator.getInstance());
154  }
155
156
157
158  /**
159   * Constructs a definition for an LDAP attribute type which may be added to
160   * the directory server schema to allow it to hold the value returned by the
161   * specified method.  Note that the object identifier used for the constructed
162   * attribute type definition is not required to be valid or unique.
163   *
164   * @param  m  The method for which to construct an LDAP attribute type
165   *            definition.  It will include the {@link LDAPGetter}
166   *            annotation type.
167   * @param  a  The OID allocator to use to generate the object identifier.  It
168   *            must not be {@code null}.
169   *
170   * @return  The constructed attribute type definition.
171   *
172   * @throws  LDAPPersistException  If this object encoder does not support
173   *                                encoding values for the associated method
174   *                                type.
175   */
176  public abstract AttributeTypeDefinition constructAttributeType(Method m,
177                                                                 OIDAllocator a)
178         throws LDAPPersistException;
179
180
181
182  /**
183   * Indicates whether the provided field can hold multiple values.
184   *
185   * @param  field  The field for which to make the determination.  It must be
186   *                marked with the {@link LDAPField} annotation.
187   *
188   * @return  {@code true} if the provided field can hold multiple values, or
189   *          {@code false} if not.
190   */
191  public abstract boolean supportsMultipleValues(Field field);
192
193
194
195  /**
196   * Indicates whether the provided setter method takes an argument that can
197   * hold multiple values.
198   *
199   * @param  method  The setter method for which to make the determination.  It
200   *                 must be marked with the {@link LDAPSetter} annotation
201   *                 type and conform to the constraints associated with that
202   *                 annotation.
203   *
204   * @return  {@code true} if the provided method takes an argument that can
205   *          hold multiple values, or {@code false} if not.
206   */
207  public abstract boolean supportsMultipleValues(Method method);
208
209
210
211  /**
212   * Encodes the provided field to an LDAP attribute.
213   *
214   * @param  field  The field to be encoded.
215   * @param  value  The value for the field in the object to be encoded.
216   * @param  name   The name to use for the constructed attribute.
217   *
218   * @return  The attribute containing the encoded representation of the
219   *          provided field.
220   *
221   * @throws  LDAPPersistException  If a problem occurs while attempting to
222   *                                construct an attribute for the field.
223   */
224  public abstract Attribute encodeFieldValue(Field field, Object value,
225                                             String name)
226         throws LDAPPersistException;
227
228
229
230  /**
231   * Encodes the provided method to an LDAP attribute.
232   *
233   * @param  method  The method to be encoded.
234   * @param  value   The value returned by the method in the object to be
235   *                 encoded.
236   * @param  name    The name to use for the constructed attribute.
237   *
238   * @return  The attribute containing the encoded representation of the
239   *          provided method value.
240   *
241   * @throws  LDAPPersistException  If a problem occurs while attempting to
242   *                                construct an attribute for the method.
243   */
244  public abstract Attribute encodeMethodValue(Method method, Object value,
245                                              String name)
246         throws LDAPPersistException;
247
248
249
250  /**
251   * Updates the provided object to assign a value for the specified field from
252   * the contents of the given attribute.
253   *
254   * @param  field      The field to update in the provided object.
255   * @param  object     The object to be updated.
256   * @param  attribute  The attribute whose value(s) should be used to update
257   *                    the specified field in the given object.
258   *
259   * @throws  LDAPPersistException  If a problem occurs while attempting to
260   *                                assign a value to the specified field.
261   */
262  public abstract void decodeField(Field field, Object object,
263                                   Attribute attribute)
264         throws LDAPPersistException;
265
266
267
268  /**
269   * Assigns a {@code null} value to the provided field, if possible.  If the
270   * field type is primitive and cannot be assigned a {@code null} value, then a
271   * default primitive value will be assigned instead (0 for numeric values,
272   * false for {@code boolean} values, and the null character for {@code char}
273   * values).
274   *
275   * @param  f  The field to which the {@code null} value should be assigned.
276   *            It must not be {@code null} and must be marked with the
277   *            {@link LDAPField} annotation.
278   * @param  o  The object to be updated.  It must not be {@code null}, and the
279   *            class must be marked with the {@link LDAPObject annotation}.
280   *
281   * @throws  LDAPPersistException  If a problem occurs while attempting to
282   *                                assign a {@code null} value to the specified
283   *                                field.
284   */
285  public void setNull(final Field f, final Object o)
286         throws LDAPPersistException
287  {
288    try
289    {
290      f.setAccessible(true);
291
292      final Class<?> type = f.getType();
293      if (type.equals(Boolean.TYPE))
294      {
295        f.set(o, Boolean.FALSE);
296      }
297      else if (type.equals(Byte.TYPE))
298      {
299        f.set(o, (byte) 0);
300      }
301      else if (type.equals(Character.TYPE))
302      {
303        f.set(o, '\u0000');
304      }
305      else if (type.equals(Double.TYPE))
306      {
307        f.set(o, 0.0d);
308      }
309      else if (type.equals(Float.TYPE))
310      {
311        f.set(o, 0.0f);
312      }
313      else if (type.equals(Integer.TYPE))
314      {
315        f.set(o, 0);
316      }
317      else if (type.equals(Long.TYPE))
318      {
319        f.set(o, 0L);
320      }
321      else if (type.equals(Short.TYPE))
322      {
323        f.set(o, (short) 0);
324      }
325      else
326      {
327        f.set(o, null);
328      }
329    }
330    catch (final Exception e)
331    {
332      Debug.debugException(e);
333      throw new LDAPPersistException(
334           ERR_ENCODER_CANNOT_SET_NULL_FIELD_VALUE.get(f.getName(),
335                o.getClass().getName(), StaticUtils.getExceptionMessage(e)),
336           e);
337    }
338  }
339
340
341
342  /**
343   * Invokes the provided setter method with a single argument that will set a
344   * {@code null} value for that method, if possible.  If the argument type is
345   * and cannot be assigned a {@code null} value, then a default primitive value
346   * will be assigned instead (0 for numeric values, false for {@code boolean}
347   * values, and the null character for {@code char} values).
348   *
349   * @param  m  The setter method that should be used to set the {@code null}
350   *            value.  It must not be {@code null}, and must have the
351   *            {@code LDAPSetter} annotation.
352   * @param  o  The object to be updated.  It must not be {@code null}, and the
353   *            class must be marked with the {@link LDAPObject annotation}.
354   *
355   * @throws  LDAPPersistException  If a problem occurs while attempting to
356   *                                assign a {@code null} value to the specified
357   *                                field.
358   */
359  public void setNull(final Method m, final Object o)
360         throws LDAPPersistException
361  {
362    try
363    {
364      m.setAccessible(true);
365
366      final Class<?> type = m.getParameterTypes()[0];
367      if (type.equals(Boolean.TYPE))
368      {
369        m.invoke(o, Boolean.FALSE);
370      }
371      else if (type.equals(Byte.TYPE))
372      {
373        m.invoke(o, (byte) 0);
374      }
375      else if (type.equals(Character.TYPE))
376      {
377        m.invoke(o, '\u0000');
378      }
379      else if (type.equals(Double.TYPE))
380      {
381        m.invoke(o, 0.0d);
382      }
383      else if (type.equals(Float.TYPE))
384      {
385        m.invoke(o, 0.0f);
386      }
387      else if (type.equals(Integer.TYPE))
388      {
389        m.invoke(o, 0);
390      }
391      else if (type.equals(Long.TYPE))
392      {
393        m.invoke(o, 0L);
394      }
395      else if (type.equals(Short.TYPE))
396      {
397        m.invoke(o, (short) 0);
398      }
399      else
400      {
401        m.invoke(o, type.cast(null));
402      }
403    }
404    catch (final Exception e)
405    {
406      Debug.debugException(e);
407      throw new LDAPPersistException(
408           ERR_ENCODER_CANNOT_SET_NULL_METHOD_VALUE.get(m.getName(),
409                o.getClass().getName(), StaticUtils.getExceptionMessage(e)),
410           e);
411    }
412  }
413
414
415
416  /**
417   * Updates the provided object to invoke the specified method to set a value
418   * from the contents of the given attribute.
419   *
420   * @param  method     The method to invoke in the provided object.
421   * @param  object     The object to be updated.
422   * @param  attribute  The attribute whose value(s) should be used to update
423   *                    the specified method in the given object.
424   *
425   * @throws  LDAPPersistException  If a problem occurs while attempting to
426   *                                determine the value or invoke the specified
427   *                                method.
428   */
429  public abstract void invokeSetter(Method method, Object object,
430                                    Attribute attribute)
431         throws LDAPPersistException;
432}