001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-2024 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-2024 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.NotNull;
050import com.unboundid.util.StaticUtils;
051import com.unboundid.util.ThreadSafety;
052import com.unboundid.util.ThreadSafetyLevel;
053
054import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
055
056
057
058/**
059 * This class provides an API for converting between Java objects and LDAP
060 * attributes.  Concrete instances of this class must provide a default
061 * zero-argument constructor.
062 */
063@Extensible()
064@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
065public abstract class ObjectEncoder
066       implements Serializable
067{
068  /**
069   * The serial version UID for this serializable class.
070   */
071  private static final long serialVersionUID = -5139516629886911696L;
072
073
074
075  /**
076   * Indicates whether this object encoder may be used to encode or decode
077   * objects of the specified type.
078   *
079   * @param  t  The type of object for which to make the determination.
080   *
081   * @return  {@code true} if this object encoder may be used for objects of
082   *          the specified type, or {@code false} if not.
083   */
084  public abstract boolean supportsType(@NotNull Type t);
085
086
087
088  /**
089   * Constructs a definition for an LDAP attribute type which may be added to
090   * the directory server schema to allow it to hold the value of the specified
091   * field.  Note that the object identifier used for the constructed attribute
092   * type definition is not required to be valid or unique.
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   *
098   * @return  The constructed attribute type definition.
099   *
100   * @throws  LDAPPersistException  If this object encoder does not support
101   *                                encoding values for the associated field
102   *                                type.
103   */
104  @NotNull()
105  public final AttributeTypeDefinition constructAttributeType(
106                                            @NotNull final Field f)
107         throws LDAPPersistException
108  {
109    return constructAttributeType(f, DefaultOIDAllocator.getInstance());
110  }
111
112
113
114  /**
115   * Constructs a definition for an LDAP attribute type which may be added to
116   * the directory server schema to allow it to hold the value of the specified
117   * field.
118   *
119   * @param  f  The field for which to construct an LDAP attribute type
120   *            definition.  It will include the {@link LDAPField} annotation
121   *            type.
122   * @param  a  The OID allocator to use to generate the object identifier.  It
123   *            must not be {@code null}.
124   *
125   * @return  The constructed attribute type definition.
126   *
127   * @throws  LDAPPersistException  If this object encoder does not support
128   *                                encoding values for the associated field
129   *                                type.
130   */
131  @NotNull()
132  public abstract AttributeTypeDefinition constructAttributeType(
133                                               @NotNull Field f,
134                                               @NotNull OIDAllocator a)
135         throws LDAPPersistException;
136
137
138
139  /**
140   * Constructs a definition for an LDAP attribute type which may be added to
141   * the directory server schema to allow it to hold the value returned by the
142   * specified method.  Note that the object identifier used for the constructed
143   * attribute type definition is not required to be valid or unique.
144   *
145   * @param  m  The method for which to construct an LDAP attribute type
146   *            definition.  It will include the {@link LDAPGetter}
147   *            annotation type.
148   *
149   * @return  The constructed attribute type definition.
150   *
151   * @throws  LDAPPersistException  If this object encoder does not support
152   *                                encoding values for the associated method
153   *                                type.
154   */
155  @NotNull()
156  public final AttributeTypeDefinition constructAttributeType(
157                                            @NotNull final Method m)
158         throws LDAPPersistException
159  {
160    return constructAttributeType(m, DefaultOIDAllocator.getInstance());
161  }
162
163
164
165  /**
166   * Constructs a definition for an LDAP attribute type which may be added to
167   * the directory server schema to allow it to hold the value returned by the
168   * specified method.  Note that the object identifier used for the constructed
169   * attribute type definition is not required to be valid or unique.
170   *
171   * @param  m  The method for which to construct an LDAP attribute type
172   *            definition.  It will include the {@link LDAPGetter}
173   *            annotation type.
174   * @param  a  The OID allocator to use to generate the object identifier.  It
175   *            must not be {@code null}.
176   *
177   * @return  The constructed attribute type definition.
178   *
179   * @throws  LDAPPersistException  If this object encoder does not support
180   *                                encoding values for the associated method
181   *                                type.
182   */
183  @NotNull()
184  public abstract AttributeTypeDefinition constructAttributeType(
185                                               @NotNull Method m,
186                                               @NotNull OIDAllocator a)
187         throws LDAPPersistException;
188
189
190
191  /**
192   * Indicates whether the provided field can hold multiple values.
193   *
194   * @param  field  The field for which to make the determination.  It must be
195   *                marked with the {@link LDAPField} annotation.
196   *
197   * @return  {@code true} if the provided field can hold multiple values, or
198   *          {@code false} if not.
199   */
200  public abstract boolean supportsMultipleValues(@NotNull Field field);
201
202
203
204  /**
205   * Indicates whether the provided setter method takes an argument that can
206   * hold multiple values.
207   *
208   * @param  method  The setter method for which to make the determination.  It
209   *                 must be marked with the {@link LDAPSetter} annotation
210   *                 type and conform to the constraints associated with that
211   *                 annotation.
212   *
213   * @return  {@code true} if the provided method takes an argument that can
214   *          hold multiple values, or {@code false} if not.
215   */
216  public abstract boolean supportsMultipleValues(@NotNull Method method);
217
218
219
220  /**
221   * Encodes the provided field to an LDAP attribute.
222   *
223   * @param  field  The field to be encoded.
224   * @param  value  The value for the field in the object to be encoded.
225   * @param  name   The name to use for the constructed attribute.
226   *
227   * @return  The attribute containing the encoded representation of the
228   *          provided field.
229   *
230   * @throws  LDAPPersistException  If a problem occurs while attempting to
231   *                                construct an attribute for the field.
232   */
233  @NotNull()
234  public abstract Attribute encodeFieldValue(@NotNull Field field,
235                                             @NotNull Object value,
236                                             @NotNull String name)
237         throws LDAPPersistException;
238
239
240
241  /**
242   * Encodes the provided method to an LDAP attribute.
243   *
244   * @param  method  The method to be encoded.
245   * @param  value   The value returned by the method in the object to be
246   *                 encoded.
247   * @param  name    The name to use for the constructed attribute.
248   *
249   * @return  The attribute containing the encoded representation of the
250   *          provided method value.
251   *
252   * @throws  LDAPPersistException  If a problem occurs while attempting to
253   *                                construct an attribute for the method.
254   */
255  @NotNull()
256  public abstract Attribute encodeMethodValue(@NotNull Method method,
257                                              @NotNull Object value,
258                                              @NotNull String name)
259         throws LDAPPersistException;
260
261
262
263  /**
264   * Updates the provided object to assign a value for the specified field from
265   * the contents of the given attribute.
266   *
267   * @param  field      The field to update in the provided object.
268   * @param  object     The object to be updated.
269   * @param  attribute  The attribute whose value(s) should be used to update
270   *                    the specified field in the given object.
271   *
272   * @throws  LDAPPersistException  If a problem occurs while attempting to
273   *                                assign a value to the specified field.
274   */
275  public abstract void decodeField(@NotNull Field field, @NotNull Object object,
276                                   @NotNull Attribute attribute)
277         throws LDAPPersistException;
278
279
280
281  /**
282   * Assigns a {@code null} value to the provided field, if possible.  If the
283   * field type is primitive and cannot be assigned a {@code null} value, then a
284   * default primitive value will be assigned instead (0 for numeric values,
285   * false for {@code boolean} values, and the null character for {@code char}
286   * values).
287   *
288   * @param  f  The field to which the {@code null} value should be assigned.
289   *            It must not be {@code null} and must be marked with the
290   *            {@link LDAPField} annotation.
291   * @param  o  The object to be updated.  It must not be {@code null}, and the
292   *            class must be marked with the {@link LDAPObject annotation}.
293   *
294   * @throws  LDAPPersistException  If a problem occurs while attempting to
295   *                                assign a {@code null} value to the specified
296   *                                field.
297   */
298  public void setNull(@NotNull final Field f, @NotNull final Object o)
299         throws LDAPPersistException
300  {
301    try
302    {
303      f.setAccessible(true);
304
305      final Class<?> type = f.getType();
306      if (type.equals(Boolean.TYPE))
307      {
308        f.set(o, Boolean.FALSE);
309      }
310      else if (type.equals(Byte.TYPE))
311      {
312        f.set(o, (byte) 0);
313      }
314      else if (type.equals(Character.TYPE))
315      {
316        f.set(o, '\u0000');
317      }
318      else if (type.equals(Double.TYPE))
319      {
320        f.set(o, 0.0d);
321      }
322      else if (type.equals(Float.TYPE))
323      {
324        f.set(o, 0.0f);
325      }
326      else if (type.equals(Integer.TYPE))
327      {
328        f.set(o, 0);
329      }
330      else if (type.equals(Long.TYPE))
331      {
332        f.set(o, 0L);
333      }
334      else if (type.equals(Short.TYPE))
335      {
336        f.set(o, (short) 0);
337      }
338      else
339      {
340        f.set(o, null);
341      }
342    }
343    catch (final Exception e)
344    {
345      Debug.debugException(e);
346      throw new LDAPPersistException(
347           ERR_ENCODER_CANNOT_SET_NULL_FIELD_VALUE.get(f.getName(),
348                o.getClass().getName(), StaticUtils.getExceptionMessage(e)),
349           e);
350    }
351  }
352
353
354
355  /**
356   * Invokes the provided setter method with a single argument that will set a
357   * {@code null} value for that method, if possible.  If the argument type is
358   * and cannot be assigned a {@code null} value, then a default primitive value
359   * will be assigned instead (0 for numeric values, false for {@code boolean}
360   * values, and the null character for {@code char} values).
361   *
362   * @param  m  The setter method that should be used to set the {@code null}
363   *            value.  It must not be {@code null}, and must have the
364   *            {@code LDAPSetter} annotation.
365   * @param  o  The object to be updated.  It must not be {@code null}, and the
366   *            class must be marked with the {@link LDAPObject annotation}.
367   *
368   * @throws  LDAPPersistException  If a problem occurs while attempting to
369   *                                assign a {@code null} value to the specified
370   *                                field.
371   */
372  public void setNull(@NotNull final Method m, @NotNull final Object o)
373         throws LDAPPersistException
374  {
375    try
376    {
377      m.setAccessible(true);
378
379      final Class<?> type = m.getParameterTypes()[0];
380      if (type.equals(Boolean.TYPE))
381      {
382        m.invoke(o, Boolean.FALSE);
383      }
384      else if (type.equals(Byte.TYPE))
385      {
386        m.invoke(o, (byte) 0);
387      }
388      else if (type.equals(Character.TYPE))
389      {
390        m.invoke(o, '\u0000');
391      }
392      else if (type.equals(Double.TYPE))
393      {
394        m.invoke(o, 0.0d);
395      }
396      else if (type.equals(Float.TYPE))
397      {
398        m.invoke(o, 0.0f);
399      }
400      else if (type.equals(Integer.TYPE))
401      {
402        m.invoke(o, 0);
403      }
404      else if (type.equals(Long.TYPE))
405      {
406        m.invoke(o, 0L);
407      }
408      else if (type.equals(Short.TYPE))
409      {
410        m.invoke(o, (short) 0);
411      }
412      else
413      {
414        m.invoke(o, type.cast(null));
415      }
416    }
417    catch (final Exception e)
418    {
419      Debug.debugException(e);
420      throw new LDAPPersistException(
421           ERR_ENCODER_CANNOT_SET_NULL_METHOD_VALUE.get(m.getName(),
422                o.getClass().getName(), StaticUtils.getExceptionMessage(e)),
423           e);
424    }
425  }
426
427
428
429  /**
430   * Updates the provided object to invoke the specified method to set a value
431   * from the contents of the given attribute.
432   *
433   * @param  method     The method to invoke in the provided object.
434   * @param  object     The object to be updated.
435   * @param  attribute  The attribute whose value(s) should be used to update
436   *                    the specified method in the given object.
437   *
438   * @throws  LDAPPersistException  If a problem occurs while attempting to
439   *                                determine the value or invoke the specified
440   *                                method.
441   */
442  public abstract void invokeSetter(@NotNull Method method,
443                                    @NotNull Object object,
444                                    @NotNull Attribute attribute)
445         throws LDAPPersistException;
446}