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 */
021 package com.unboundid.ldap.sdk.persist;
022
023
024
025 import java.io.ByteArrayInputStream;
026 import java.io.ByteArrayOutputStream;
027 import java.io.ObjectInputStream;
028 import java.io.ObjectOutputStream;
029 import java.io.Serializable;
030 import java.lang.reflect.Array;
031 import java.lang.reflect.Field;
032 import java.lang.reflect.InvocationTargetException;
033 import java.lang.reflect.Method;
034 import java.lang.reflect.Type;
035 import java.math.BigDecimal;
036 import java.math.BigInteger;
037 import java.net.URI;
038 import java.net.URL;
039 import java.util.ArrayList;
040 import java.util.Collection;
041 import java.util.Date;
042 import java.util.HashSet;
043 import java.util.LinkedHashSet;
044 import java.util.LinkedList;
045 import java.util.List;
046 import java.util.Set;
047 import java.util.TreeSet;
048 import java.util.UUID;
049 import java.util.concurrent.CopyOnWriteArrayList;
050 import java.util.concurrent.CopyOnWriteArraySet;
051 import java.util.concurrent.atomic.AtomicInteger;
052 import java.util.concurrent.atomic.AtomicLong;
053
054 import com.unboundid.asn1.ASN1OctetString;
055 import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule;
056 import com.unboundid.ldap.matchingrules.MatchingRule;
057 import com.unboundid.ldap.sdk.Attribute;
058 import com.unboundid.ldap.sdk.DN;
059 import com.unboundid.ldap.sdk.Filter;
060 import com.unboundid.ldap.sdk.LDAPURL;
061 import com.unboundid.ldap.sdk.RDN;
062 import com.unboundid.ldap.sdk.LDAPException;
063 import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
064 import com.unboundid.ldap.sdk.schema.AttributeUsage;
065 import com.unboundid.util.NotMutable;
066 import com.unboundid.util.ThreadSafety;
067 import com.unboundid.util.ThreadSafetyLevel;
068
069 import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
070 import static com.unboundid.util.Debug.*;
071 import static com.unboundid.util.StaticUtils.*;
072
073
074
075 /**
076 * This class provides the default implementation of an {@link ObjectEncoder}
077 * object that will be used when encoding and decoding fields to be written to
078 * or read from an LDAP directory server.
079 * <BR><BR>
080 * The following basic types will be supported, with the following encodings:
081 * <UL>
082 * <LI>Any kind of enumeration -- Encoded using the name of the enum
083 * value</LI>
084 * <LI>{@code java.util.concurrent.atomic.AtomicInteger} -- Encoded using the
085 * string representation of the value</LI>
086 * <LI>{@code java.util.concurrent.atomic.AtomicLong} -- Encoded using the
087 * string representation of the value</LI>
088 * <LI>{@code java.math.BigDecimal} -- Encoded using the string representation
089 * of the value</LI>
090 * <LI>{@code java.math.BigInteger} -- Encoded using the string representation
091 * of the value</LI>
092 * <LI>{@code boolean} -- Encoded as either "TRUE" or "FALSE"</LI>
093 * <LI>{@code java.lang.Boolean} -- Encoded as either "TRUE" or "FALSE"</LI>
094 * <LI>{@code byte[]} -- Encoded as the raw bytes contained in the array</LI>
095 * <LI>{@code char[]} -- Encoded as a string containing the characters in the
096 * array</LI>
097 * <LI>{@code java.util.Date} -- Encoded using the generalized time
098 * syntax</LI>
099 * <LI>{@code com.unboundid.ldap.sdk.DN} -- Encoded using the string
100 * representation of the value</LI>
101 * <LI>{@code double} -- Encoded using the string representation of the
102 * value</LI>
103 * <LI>{@code java.lang.Double} -- Encoded using the string representation of
104 * the value</LI>
105 * <LI>{@code com.unboundid.ldap.sdk.Filter} -- Encoded using the string
106 * representation of the value</LI>
107 * <LI>{@code float} -- Encoded using the string representation of the
108 * value</LI>
109 * <LI>{@code java.lang.Float} -- Encoded using the string representation of
110 * the value</LI>
111 * <LI>{@code int} -- Encoded using the string representation of the
112 * value</LI>
113 * <LI>{@code java.lang.Integer} -- Encoded using the string representation of
114 * the value</LI>
115 * <LI>{@code com.unboundid.ldap.sdk.LDAPURL} -- Encoded using the string
116 * representation of the value</LI>
117 * <LI>{@code long} -- Encoded using the string representation of the
118 * value</LI>
119 * <LI>{@code java.lang.Long} -- Encoded using the string representation of
120 * the value</LI>
121 * <LI>{@code com.unboundid.ldap.sdk.RDN} -- Encoded using the string
122 * representation of the value</LI>
123 * <LI>{@code short} -- Encoded using the string representation of the
124 * value</LI>
125 * <LI>{@code java.lang.Short} -- Encoded using the string representation of
126 * the value</LI>
127 * <LI>{@code java.lang.String} -- Encoded using the value</LI>
128 * <LI>{@code java.lang.StringBuffer} -- Encoded using the string
129 * representation of the value</LI>
130 * <LI>{@code java.lang.StringBuilder} -- Encoded using the string
131 * representation of the value</LI>
132 * <LI>{@code java.net.URI} -- Encoded using the string representation of the
133 * value.</LI>
134 * <LI>{@code java.net.URL} -- Encoded using the string representation of the
135 * value.</LI>
136 * <LI>{@code java.util.UUID} -- Encoded using the string representation of
137 * the value</LI>
138 * </UL>
139 * Serializable objects are also supported, in which case the raw bytes that
140 * comprise the serialized representation will be used. This may be
141 * undesirable, because the value may only be interpretable by Java-based
142 * clients. If you wish to better control the encoding for serialized objects,
143 * have them implement custom {@code writeObject}, {@code readObject}, and
144 * {@code readObjectNoData} methods that use the desired encoding. Alternately,
145 * you may create a custom {@link ObjectEncoder} implementation for that object
146 * type, or use getter/setter methods that convert between string/byte[]
147 * representations and the desired object types.
148 * <BR><BR>
149 * In addition, arrays of all of the above types are also supported, in which
150 * case each element of the array will be a separate value in the corresponding
151 * LDAP attribute. Lists (including {@code ArrayList}, {@code LinkedList}, and
152 * {@code CopyOnWriteArrayList}) and sets (including {@code HashSet},
153 * {@code LinkedHashSet}, {@code TreeSet}, and {@code CopyOnWriteArraySet}) of
154 * the above types are also supported.
155 * <BR><BR>
156 * Note that you should be careful when using primitive types, since they cannot
157 * be unassigned and therefore will always have a value. When using an LDAP
158 * entry to initialize an object any fields with primitive types which are
159 * associated with LDAP attributes not present in the entry will have the
160 * default value assigned to them in the zero-argument constructor, or will have
161 * the JVM-supplied default value if no value was assigned to it in the
162 * constructor. If the associated object is converted back to an LDAP entry,
163 * then those fields will be included in the entry that is generated, even if
164 * they were not present in the original entry. To avoid this problem, you can
165 * use the object types rather than the primitive types (e.g.,
166 * {@code java.lang.Boolean} instead of the {@code boolean} primitive), in which
167 * case any fields associated with attributes that are not present in the entry
168 * being de-serialized will be explicitly set to {@code null}.
169 */
170 @NotMutable()
171 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
172 public final class DefaultObjectEncoder
173 extends ObjectEncoder
174 {
175 /**
176 * The serial version UID for this serializable class.
177 */
178 private static final long serialVersionUID = -4566874784628920022L;
179
180
181
182 /**
183 * Creates a new instance of this encoder.
184 */
185 public DefaultObjectEncoder()
186 {
187 super();
188 }
189
190
191
192 /**
193 * {@inheritDoc}
194 */
195 @Override()
196 public boolean supportsType(final Type t)
197 {
198 final TypeInfo typeInfo = new TypeInfo(t);
199 if (! typeInfo.isSupported())
200 {
201 return false;
202 }
203
204 final Class<?> baseClass = typeInfo.getBaseClass();
205
206 if (supportsTypeInternal(baseClass))
207 {
208 return true;
209 }
210
211 final Class<?> componentType = typeInfo.getComponentType();
212 if (componentType == null)
213 {
214 return false;
215 }
216
217 if (typeInfo.isArray())
218 {
219 return supportsTypeInternal(componentType);
220 }
221
222 if (typeInfo.isList())
223 {
224 return (isSupportedListType(baseClass) &&
225 supportsTypeInternal(componentType));
226 }
227
228 if (typeInfo.isSet())
229 {
230 return (isSupportedSetType(baseClass) &&
231 supportsTypeInternal(componentType));
232 }
233
234 return false;
235 }
236
237
238
239 /**
240 * Indicates whether this object encoder supports objects of the specified
241 * type.
242 *
243 * @param c The object type class for which to make the determination.
244 *
245 * @return {@code true} if this object supports objects of the specified
246 * type, or {@code false} if not.
247 */
248 private static boolean supportsTypeInternal(final Class<?> c)
249 {
250 if (c.equals(AtomicInteger.class) ||
251 c.equals(AtomicLong.class) ||
252 c.equals(BigDecimal.class) ||
253 c.equals(BigInteger.class) ||
254 c.equals(Boolean.class) ||
255 c.equals(Boolean.TYPE) ||
256 c.equals(Date.class) ||
257 c.equals(DN.class) ||
258 c.equals(Double.class) ||
259 c.equals(Double.TYPE) ||
260 c.equals(Filter.class) ||
261 c.equals(Float.class) ||
262 c.equals(Float.TYPE) ||
263 c.equals(Integer.class) ||
264 c.equals(Integer.TYPE) ||
265 c.equals(LDAPURL.class) ||
266 c.equals(Long.class) ||
267 c.equals(Long.TYPE) ||
268 c.equals(RDN.class) ||
269 c.equals(Short.class) ||
270 c.equals(Short.TYPE) ||
271 c.equals(String.class) ||
272 c.equals(StringBuffer.class) ||
273 c.equals(StringBuilder.class) ||
274 c.equals(URI.class) ||
275 c.equals(URL.class) ||
276 c.equals(UUID.class))
277 {
278 return true;
279 }
280
281 if (c.isArray())
282 {
283 final Class<?> t = c.getComponentType();
284 if (t.equals(Byte.TYPE) ||
285 t.equals(Character.TYPE))
286 {
287 return true;
288 }
289 }
290
291 if (c.isEnum())
292 {
293 return true;
294 }
295
296 if (Serializable.class.isAssignableFrom(c))
297 {
298 return (! (c.isArray() || Collection.class.isAssignableFrom(c)));
299 }
300
301 return false;
302 }
303
304
305
306 /**
307 * Indicates whether the provided type is a supported list type.
308 *
309 * @param t The type for which to make the determination.
310 *
311 * @return {@code true} if the provided type is a supported list type, or
312 * or {@code false}.
313 */
314 private static boolean isSupportedListType(final Class<?> t)
315 {
316 return (t.equals(List.class) ||
317 t.equals(ArrayList.class) ||
318 t.equals(LinkedList.class) ||
319 t.equals(CopyOnWriteArrayList.class));
320 }
321
322
323
324 /**
325 * Creates a new list of the specified type.
326 *
327 * @param t The type of list to create.
328 * @param size The number of values that will be included in the list.
329 *
330 * @return The created list, or {@code null} if it is not a supported list
331 * type.
332 */
333 @SuppressWarnings("rawtypes")
334 private static List<?> createList(final Class<?> t, final int size)
335 {
336 if (t.equals(List.class) || t.equals(ArrayList.class))
337 {
338 return new ArrayList(size);
339 }
340 else if (t.equals(LinkedList.class))
341 {
342 return new LinkedList();
343 }
344 else if (t.equals(CopyOnWriteArrayList.class))
345 {
346 return new CopyOnWriteArrayList();
347 }
348
349 return null;
350 }
351
352
353
354 /**
355 * Indicates whether the provided type is a supported set type.
356 *
357 * @param t The type for which to make the determination.
358 *
359 * @return {@code true} if the provided type is a supported set type, or
360 * or {@code false}.
361 */
362 private static boolean isSupportedSetType(final Class<?> t)
363 {
364 return (t.equals(Set.class) ||
365 t.equals(HashSet.class) ||
366 t.equals(LinkedHashSet.class) ||
367 t.equals(TreeSet.class) ||
368 t.equals(CopyOnWriteArraySet.class));
369 }
370
371
372
373 /**
374 * Creates a new set of the specified type.
375 *
376 * @param t The type of set to create.
377 * @param size The number of values that will be included in the set.
378 *
379 * @return The created list, or {@code null} if it is not a supported set
380 * type.
381 */
382 @SuppressWarnings("rawtypes")
383 private static Set<?> createSet(final Class<?> t, final int size)
384 {
385 if (t.equals(Set.class) || t.equals(LinkedHashSet.class))
386 {
387 return new LinkedHashSet(size);
388 }
389 else if (t.equals(HashSet.class))
390 {
391 return new HashSet(size);
392 }
393 else if (t.equals(TreeSet.class))
394 {
395 return new TreeSet();
396 }
397 else if (t.equals(CopyOnWriteArraySet.class))
398 {
399 return new CopyOnWriteArraySet();
400 }
401
402 return null;
403 }
404
405
406
407 /**
408 * {@inheritDoc}
409 */
410 @Override()
411 public AttributeTypeDefinition constructAttributeType(final Field f,
412 final OIDAllocator a)
413 throws LDAPPersistException
414 {
415 final LDAPField at = f.getAnnotation(LDAPField.class);
416
417 final String attrName;
418 if (at.attribute().length() == 0)
419 {
420 attrName = f.getName();
421 }
422 else
423 {
424 attrName = at.attribute();
425 }
426
427 final String oid = a.allocateAttributeTypeOID(attrName);
428
429 final TypeInfo typeInfo = new TypeInfo(f.getGenericType());
430 if (! typeInfo.isSupported())
431 {
432 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
433 String.valueOf(typeInfo.getType())));
434 }
435
436 final boolean isSingleValued = (! supportsMultipleValues(typeInfo));
437
438 final String syntaxOID;
439 if (isSingleValued)
440 {
441 syntaxOID = getSyntaxOID(typeInfo.getBaseClass());
442 }
443 else
444 {
445 syntaxOID = getSyntaxOID(typeInfo.getComponentType());
446 }
447
448 final MatchingRule mr = MatchingRule.selectMatchingRuleForSyntax(syntaxOID);
449 return new AttributeTypeDefinition(oid, new String[] { attrName }, null,
450 false, null, mr.getEqualityMatchingRuleNameOrOID(),
451 mr.getOrderingMatchingRuleNameOrOID(),
452 mr.getSubstringMatchingRuleNameOrOID(), syntaxOID, isSingleValued,
453 false, false, AttributeUsage.USER_APPLICATIONS, null);
454 }
455
456
457
458 /**
459 * {@inheritDoc}
460 */
461 @Override()
462 public AttributeTypeDefinition constructAttributeType(final Method m,
463 final OIDAllocator a)
464 throws LDAPPersistException
465 {
466 final LDAPGetter at = m.getAnnotation(LDAPGetter.class);
467
468 final String attrName;
469 if (at.attribute().length() == 0)
470 {
471 attrName = toInitialLowerCase(m.getName().substring(3));
472 }
473 else
474 {
475 attrName = at.attribute();
476 }
477
478 final String oid = a.allocateAttributeTypeOID(attrName);
479
480 final TypeInfo typeInfo = new TypeInfo(m.getGenericReturnType());
481 if (! typeInfo.isSupported())
482 {
483 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
484 String.valueOf(typeInfo.getType())));
485 }
486
487 final boolean isSingleValued = (! supportsMultipleValues(typeInfo));
488
489 final String syntaxOID;
490 if (isSingleValued)
491 {
492 syntaxOID = getSyntaxOID(typeInfo.getBaseClass());
493 }
494 else
495 {
496 syntaxOID = getSyntaxOID(typeInfo.getComponentType());
497 }
498
499 return new AttributeTypeDefinition(oid, new String[] { attrName }, null,
500 false, null, null, null, null, syntaxOID, isSingleValued, false, false,
501 AttributeUsage.USER_APPLICATIONS, null);
502 }
503
504
505
506 /**
507 * Retrieves the syntax that should be used for the specified object type.
508 *
509 * @param t The type for which to make the determination.
510 *
511 * @return The syntax that should be used for the specified object type, or
512 * {@code null} if it cannot be determined.
513 */
514 private static String getSyntaxOID(final Class<?> t)
515 {
516 if (t.equals(BigDecimal.class) ||
517 t.equals(Double.class) ||
518 t.equals(Double.TYPE) ||
519 t.equals(Float.class) ||
520 t.equals(Float.TYPE) ||
521 t.equals(String.class) ||
522 t.equals(StringBuffer.class) ||
523 t.equals(StringBuilder.class) ||
524 t.equals(URI.class) ||
525 t.equals(URL.class) ||
526 t.equals(Filter.class) ||
527 t.equals(LDAPURL.class))
528 {
529 return "1.3.6.1.4.1.1466.115.121.1.15";
530 }
531 else if (t.equals(AtomicInteger.class) ||
532 t.equals(AtomicLong.class) ||
533 t.equals(BigInteger.class) ||
534 t.equals(Integer.class) ||
535 t.equals(Integer.TYPE) ||
536 t.equals(Long.class) ||
537 t.equals(Long.TYPE) ||
538 t.equals(Short.class) ||
539 t.equals(Short.TYPE))
540 {
541 return "1.3.6.1.4.1.1466.115.121.1.27";
542 }
543 else if (t.equals(UUID.class))
544 {
545 // Although "1.3.6.1.1.16.1" (which is the UUID syntax as defined in RFC
546 // 4530) might be more correct, some servers may not support this syntax
547 // since it is relatively new, so we'll fall back on the more
548 // widely-supported directory string syntax.
549 return "1.3.6.1.4.1.1466.115.121.1.15";
550 }
551 else if (t.equals(DN.class) ||
552 t.equals(RDN.class))
553 {
554 return "1.3.6.1.4.1.1466.115.121.1.12";
555 }
556 else if (t.equals(Boolean.class) ||
557 t.equals(Boolean.TYPE))
558 {
559 return "1.3.6.1.4.1.1466.115.121.1.7";
560 }
561 else if (t.equals(Date.class))
562 {
563 return "1.3.6.1.4.1.1466.115.121.1.24";
564 }
565 else if (t.isArray())
566 {
567 final Class<?> ct = t.getComponentType();
568 if (ct.equals(Byte.TYPE))
569 {
570 return "1.3.6.1.4.1.1466.115.121.1.40";
571 }
572 else if (ct.equals(Character.TYPE))
573 {
574 return "1.3.6.1.4.1.1466.115.121.1.15";
575 }
576 }
577 else if (t.isEnum())
578 {
579 return "1.3.6.1.4.1.1466.115.121.1.15";
580 }
581 else if (Serializable.class.isAssignableFrom(t))
582 {
583 return "1.3.6.1.4.1.1466.115.121.1.40";
584 }
585
586 return null;
587 }
588
589
590
591 /**
592 * {@inheritDoc}
593 */
594 @Override()
595 public boolean supportsMultipleValues(final Field field)
596 {
597 return supportsMultipleValues(new TypeInfo(field.getGenericType()));
598 }
599
600
601
602 /**
603 * {@inheritDoc}
604 */
605 @Override()
606 public boolean supportsMultipleValues(final Method method)
607 {
608 final Type[] paramTypes = method.getGenericParameterTypes();
609 if (paramTypes.length != 1)
610 {
611 return false;
612 }
613
614 return supportsMultipleValues(new TypeInfo(paramTypes[0]));
615 }
616
617
618
619 /**
620 * Indicates whether the provided object type supports multiple values.
621 *
622 * @param t The type for which to make the determination.
623 *
624 * @return {@code true} if the provided object type supports multiple values,
625 * or {@code false} if not.
626 */
627 private static boolean supportsMultipleValues(final TypeInfo t)
628 {
629 if (t.isArray())
630 {
631 final Class<?> componentType = t.getComponentType();
632 return (! (componentType.equals(Byte.TYPE) ||
633 componentType.equals(Character.TYPE)));
634 }
635 else
636 {
637 return t.isMultiValued();
638 }
639 }
640
641
642
643 /**
644 * {@inheritDoc}
645 */
646 @Override()
647 public Attribute encodeFieldValue(final Field field, final Object value,
648 final String name)
649 throws LDAPPersistException
650 {
651 return encodeValue(field.getGenericType(), value, name);
652 }
653
654
655
656 /**
657 * {@inheritDoc}
658 */
659 @Override()
660 public Attribute encodeMethodValue(final Method method, final Object value,
661 final String name)
662 throws LDAPPersistException
663 {
664 return encodeValue(method.getGenericReturnType(), value, name);
665 }
666
667
668
669 /**
670 * Encodes the provided value to an LDAP attribute.
671 *
672 * @param type The type for the provided value.
673 * @param value The value for the field in the object to be encoded.
674 * @param name The name to use for the constructed attribute.
675 *
676 * @return The attribute containing the encoded representation of the
677 * provided field.
678 *
679 * @throws LDAPPersistException If a problem occurs while attempting to
680 * construct an attribute for the field.
681 */
682 private static Attribute encodeValue(final Type type, final Object value,
683 final String name)
684 throws LDAPPersistException
685 {
686 final TypeInfo typeInfo = new TypeInfo(type);
687
688 final Class<?> c = typeInfo.getBaseClass();
689 if (c.equals(AtomicInteger.class) ||
690 c.equals(AtomicLong.class) ||
691 c.equals(BigDecimal.class) ||
692 c.equals(BigInteger.class) ||
693 c.equals(Double.class) ||
694 c.equals(Double.TYPE) ||
695 c.equals(Float.class) ||
696 c.equals(Float.TYPE) ||
697 c.equals(Integer.class) ||
698 c.equals(Integer.TYPE) ||
699 c.equals(Long.class) ||
700 c.equals(Long.TYPE) ||
701 c.equals(Short.class) ||
702 c.equals(Short.TYPE) ||
703 c.equals(String.class) ||
704 c.equals(StringBuffer.class) ||
705 c.equals(StringBuilder.class) ||
706 c.equals(UUID.class) ||
707 c.equals(DN.class) ||
708 c.equals(Filter.class) ||
709 c.equals(LDAPURL.class) ||
710 c.equals(RDN.class))
711 {
712 return new Attribute(name, String.valueOf(value));
713 }
714 else if (value instanceof URI)
715 {
716 final URI uri = (URI) value;
717 return new Attribute(name, uri.toASCIIString());
718 }
719 else if (value instanceof URL)
720 {
721 final URL url = (URL) value;
722 return new Attribute(name, url.toExternalForm());
723 }
724 else if (value instanceof byte[])
725 {
726 return new Attribute(name, (byte[]) value);
727 }
728 else if (value instanceof char[])
729 {
730 return new Attribute(name, new String((char[]) value));
731 }
732 else if (c.equals(Boolean.class) || c.equals(Boolean.TYPE))
733 {
734 final Boolean b = (Boolean) value;
735 if (b)
736 {
737 return new Attribute(name, "TRUE");
738 }
739 else
740 {
741 return new Attribute(name, "FALSE");
742 }
743 }
744 else if (c.equals(Date.class))
745 {
746 final Date d = (Date) value;
747 return new Attribute(name, encodeGeneralizedTime(d));
748 }
749 else if (typeInfo.isArray())
750 {
751 return encodeArray(typeInfo.getComponentType(), value, name);
752 }
753 else if (typeInfo.isEnum())
754 {
755 final Enum<?> e = (Enum<?>) value;
756 return new Attribute(name, e.name());
757 }
758 else if (Collection.class.isAssignableFrom(c))
759 {
760 return encodeCollection(typeInfo.getComponentType(),
761 (Collection<?>) value, name);
762 }
763 else if (Serializable.class.isAssignableFrom(c))
764 {
765 try
766 {
767 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
768 final ObjectOutputStream oos = new ObjectOutputStream(baos);
769 oos.writeObject(value);
770 oos.close();
771 return new Attribute(name, baos.toByteArray());
772 }
773 catch (final Exception e)
774 {
775 debugException(e);
776 throw new LDAPPersistException(
777 ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(name,
778 getExceptionMessage(e)),
779 e);
780 }
781 }
782
783 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
784 String.valueOf(type)));
785 }
786
787
788
789 /**
790 * Encodes the contents of the provided array object.
791 *
792 * @param arrayType The component type of the array.
793 * @param arrayObject The array object to process.
794 * @param attributeName The name to use for the attribute to create.
795 *
796 * @return The attribute containing the encoded array contents.
797 *
798 * @throws LDAPPersistException If a problem occurs while trying to create
799 * the attribute.
800 */
801 private static Attribute encodeArray(final Class<?> arrayType,
802 final Object arrayObject,
803 final String attributeName)
804 throws LDAPPersistException
805 {
806 final ASN1OctetString[] values =
807 new ASN1OctetString[Array.getLength(arrayObject)];
808 for (int i=0; i < values.length; i++)
809 {
810 final Object o = Array.get(arrayObject, i);
811 if (arrayType.equals(AtomicInteger.class) ||
812 arrayType.equals(AtomicLong.class) ||
813 arrayType.equals(BigDecimal.class) ||
814 arrayType.equals(BigInteger.class) ||
815 arrayType.equals(Double.class) ||
816 arrayType.equals(Double.TYPE) ||
817 arrayType.equals(Float.class) ||
818 arrayType.equals(Float.TYPE) ||
819 arrayType.equals(Integer.class) ||
820 arrayType.equals(Integer.TYPE) ||
821 arrayType.equals(Long.class) ||
822 arrayType.equals(Long.TYPE) ||
823 arrayType.equals(Short.class) ||
824 arrayType.equals(Short.TYPE) ||
825 arrayType.equals(String.class) ||
826 arrayType.equals(StringBuffer.class) ||
827 arrayType.equals(StringBuilder.class) ||
828 arrayType.equals(UUID.class) ||
829 arrayType.equals(DN.class) ||
830 arrayType.equals(Filter.class) ||
831 arrayType.equals(LDAPURL.class) ||
832 arrayType.equals(RDN.class))
833 {
834 values[i] = new ASN1OctetString(String.valueOf(o));
835 }
836 else if (arrayType.equals(URI.class))
837 {
838 final URI uri = (URI) o;
839 values[i] = new ASN1OctetString(uri.toASCIIString());
840 }
841 else if (arrayType.equals(URL.class))
842 {
843 final URL url = (URL) o;
844 values[i] = new ASN1OctetString(url.toExternalForm());
845 }
846 else if (o instanceof byte[])
847 {
848 values[i] = new ASN1OctetString((byte[]) o);
849 }
850 else if (o instanceof char[])
851 {
852 values[i] = new ASN1OctetString(new String((char[]) o));
853 }
854 else if (arrayType.equals(Boolean.class) ||
855 arrayType.equals(Boolean.TYPE))
856 {
857 final Boolean b = (Boolean) o;
858 if (b)
859 {
860 values[i] = new ASN1OctetString("TRUE");
861 }
862 else
863 {
864 values[i] = new ASN1OctetString("FALSE");
865 }
866 }
867 else if (arrayType.equals(Date.class))
868 {
869 final Date d = (Date) o;
870 values[i] = new ASN1OctetString(encodeGeneralizedTime(d));
871 }
872 else if (arrayType.isEnum())
873 {
874 final Enum<?> e = (Enum<?>) o;
875 values[i] = new ASN1OctetString(e.name());
876 }
877 else if (Serializable.class.isAssignableFrom(arrayType))
878 {
879 try
880 {
881 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
882 final ObjectOutputStream oos = new ObjectOutputStream(baos);
883 oos.writeObject(o);
884 oos.close();
885 values[i] = new ASN1OctetString(baos.toByteArray());
886 }
887 catch (final Exception e)
888 {
889 debugException(e);
890 throw new LDAPPersistException(
891 ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(attributeName,
892 getExceptionMessage(e)),
893 e);
894 }
895 }
896 else
897 {
898 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
899 arrayType.getName()));
900 }
901 }
902
903 return new Attribute(attributeName,
904 CaseIgnoreStringMatchingRule.getInstance(), values);
905 }
906
907
908
909 /**
910 * Encodes the contents of the provided collection.
911 *
912 * @param genericType The generic type of the collection.
913 * @param collection The collection to process.
914 * @param attributeName The name to use for the attribute to create.
915 *
916 * @return The attribute containing the encoded collection contents.
917 *
918 * @throws LDAPPersistException If a problem occurs while trying to create
919 * the attribute.
920 */
921 private static Attribute encodeCollection(final Class<?> genericType,
922 final Collection<?> collection,
923 final String attributeName)
924 throws LDAPPersistException
925 {
926 final ASN1OctetString[] values = new ASN1OctetString[collection.size()];
927
928 int i=0;
929 for (final Object o : collection)
930 {
931 if (genericType.equals(AtomicInteger.class) ||
932 genericType.equals(AtomicLong.class) ||
933 genericType.equals(BigDecimal.class) ||
934 genericType.equals(BigInteger.class) ||
935 genericType.equals(Double.class) ||
936 genericType.equals(Double.TYPE) ||
937 genericType.equals(Float.class) ||
938 genericType.equals(Float.TYPE) ||
939 genericType.equals(Integer.class) ||
940 genericType.equals(Integer.TYPE) ||
941 genericType.equals(Long.class) ||
942 genericType.equals(Long.TYPE) ||
943 genericType.equals(Short.class) ||
944 genericType.equals(Short.TYPE) ||
945 genericType.equals(String.class) ||
946 genericType.equals(StringBuffer.class) ||
947 genericType.equals(StringBuilder.class) ||
948 genericType.equals(UUID.class) ||
949 genericType.equals(DN.class) ||
950 genericType.equals(Filter.class) ||
951 genericType.equals(LDAPURL.class) ||
952 genericType.equals(RDN.class))
953 {
954 values[i] = new ASN1OctetString(String.valueOf(o));
955 }
956 else if (genericType.equals(URI.class))
957 {
958 final URI uri = (URI) o;
959 values[i] = new ASN1OctetString(uri.toASCIIString());
960 }
961 else if (genericType.equals(URL.class))
962 {
963 final URL url = (URL) o;
964 values[i] = new ASN1OctetString(url.toExternalForm());
965 }
966 else if (o instanceof byte[])
967 {
968 values[i] = new ASN1OctetString((byte[]) o);
969 }
970 else if (o instanceof char[])
971 {
972 values[i] = new ASN1OctetString(new String((char[]) o));
973 }
974 else if (genericType.equals(Boolean.class) ||
975 genericType.equals(Boolean.TYPE))
976 {
977 final Boolean b = (Boolean) o;
978 if (b)
979 {
980 values[i] = new ASN1OctetString("TRUE");
981 }
982 else
983 {
984 values[i] = new ASN1OctetString("FALSE");
985 }
986 }
987 else if (genericType.equals(Date.class))
988 {
989 final Date d = (Date) o;
990 values[i] = new ASN1OctetString(encodeGeneralizedTime(d));
991 }
992 else if (genericType.isEnum())
993 {
994 final Enum<?> e = (Enum<?>) o;
995 values[i] = new ASN1OctetString(e.name());
996 }
997 else if (Serializable.class.isAssignableFrom(genericType))
998 {
999 try
1000 {
1001 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
1002 final ObjectOutputStream oos = new ObjectOutputStream(baos);
1003 oos.writeObject(o);
1004 oos.close();
1005 values[i] = new ASN1OctetString(baos.toByteArray());
1006 }
1007 catch (final Exception e)
1008 {
1009 debugException(e);
1010 throw new LDAPPersistException(
1011 ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(attributeName,
1012 getExceptionMessage(e)),
1013 e);
1014 }
1015 }
1016 else
1017 {
1018 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1019 genericType.getName()));
1020 }
1021
1022 i++;
1023 }
1024
1025 return new Attribute(attributeName,
1026 CaseIgnoreStringMatchingRule.getInstance(), values);
1027 }
1028
1029
1030
1031 /**
1032 * {@inheritDoc}
1033 */
1034 @Override()
1035 public void decodeField(final Field field, final Object object,
1036 final Attribute attribute)
1037 throws LDAPPersistException
1038 {
1039 field.setAccessible(true);
1040 final TypeInfo typeInfo = new TypeInfo(field.getGenericType());
1041
1042 try
1043 {
1044 final Class<?> baseClass = typeInfo.getBaseClass();
1045 final Object newValue = getValue(baseClass, attribute, 0);
1046 if (newValue != null)
1047 {
1048 field.set(object, newValue);
1049 return;
1050 }
1051
1052 if (typeInfo.isArray())
1053 {
1054 final Class<?> componentType = typeInfo.getComponentType();
1055 final ASN1OctetString[] values = attribute.getRawValues();
1056 final Object arrayObject =
1057 Array.newInstance(componentType, values.length);
1058 for (int i=0; i < values.length; i++)
1059 {
1060 final Object o = getValue(componentType, attribute, i);
1061 if (o == null)
1062 {
1063 throw new LDAPPersistException(
1064 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1065 componentType.getName()));
1066 }
1067 Array.set(arrayObject, i, o);
1068 }
1069
1070 field.set(object, arrayObject);
1071 return;
1072 }
1073 else if (typeInfo.isList() && isSupportedListType(baseClass))
1074 {
1075 final Class<?> componentType = typeInfo.getComponentType();
1076 if (componentType == null)
1077 {
1078 throw new LDAPPersistException(
1079 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName()));
1080 }
1081
1082 final ASN1OctetString[] values = attribute.getRawValues();
1083 final List<?> l = createList(baseClass, values.length);
1084 for (int i=0; i < values.length; i++)
1085 {
1086 final Object o = getValue(componentType, attribute, i);
1087 if (o == null)
1088 {
1089 throw new LDAPPersistException(
1090 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1091 componentType.getName()));
1092 }
1093
1094 invokeAdd(l, o);
1095 }
1096
1097 field.set(object, l);
1098 return;
1099 }
1100 else if (typeInfo.isSet() && isSupportedSetType(baseClass))
1101 {
1102 final Class<?> componentType = typeInfo.getComponentType();
1103 if (componentType == null)
1104 {
1105 throw new LDAPPersistException(
1106 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName()));
1107 }
1108
1109 final ASN1OctetString[] values = attribute.getRawValues();
1110 final Set<?> l = createSet(baseClass, values.length);
1111 for (int i=0; i < values.length; i++)
1112 {
1113 final Object o = getValue(componentType, attribute, i);
1114 if (o == null)
1115 {
1116 throw new LDAPPersistException(
1117 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1118 componentType.getName()));
1119 }
1120
1121 invokeAdd(l, o);
1122 }
1123
1124 field.set(object, l);
1125 return;
1126 }
1127
1128 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1129 baseClass.getName()));
1130 }
1131 catch (LDAPPersistException lpe)
1132 {
1133 debugException(lpe);
1134 throw lpe;
1135 }
1136 catch (Exception e)
1137 {
1138 debugException(e);
1139 throw new LDAPPersistException(getExceptionMessage(e), e);
1140 }
1141 }
1142
1143
1144
1145 /**
1146 * {@inheritDoc}
1147 */
1148 @Override()
1149 public void invokeSetter(final Method method, final Object object,
1150 final Attribute attribute)
1151 throws LDAPPersistException
1152 {
1153 final TypeInfo typeInfo =
1154 new TypeInfo(method.getGenericParameterTypes()[0]);
1155 final Class<?> baseClass = typeInfo.getBaseClass();
1156 method.setAccessible(true);
1157
1158 try
1159 {
1160 final Object newValue = getValue(baseClass, attribute, 0);
1161 if (newValue != null)
1162 {
1163 method.invoke(object, newValue);
1164 return;
1165 }
1166
1167 if (typeInfo.isArray())
1168 {
1169 final Class<?> componentType = typeInfo.getComponentType();
1170 final ASN1OctetString[] values = attribute.getRawValues();
1171 final Object arrayObject =
1172 Array.newInstance(componentType, values.length);
1173 for (int i=0; i < values.length; i++)
1174 {
1175 final Object o = getValue(componentType, attribute, i);
1176 if (o == null)
1177 {
1178 throw new LDAPPersistException(
1179 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1180 componentType.getName()));
1181 }
1182 Array.set(arrayObject, i, o);
1183 }
1184
1185 method.invoke(object, arrayObject);
1186 return;
1187 }
1188 else if (typeInfo.isList() && isSupportedListType(baseClass))
1189 {
1190 final Class<?> componentType = typeInfo.getComponentType();
1191 if (componentType == null)
1192 {
1193 throw new LDAPPersistException(
1194 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName()));
1195 }
1196
1197 final ASN1OctetString[] values = attribute.getRawValues();
1198 final List<?> l = createList(baseClass, values.length);
1199 for (int i=0; i < values.length; i++)
1200 {
1201 final Object o = getValue(componentType, attribute, i);
1202 if (o == null)
1203 {
1204 throw new LDAPPersistException(
1205 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1206 componentType.getName()));
1207 }
1208
1209 invokeAdd(l, o);
1210 }
1211
1212 method.invoke(object, l);
1213 return;
1214 }
1215 else if (typeInfo.isSet() && isSupportedSetType(baseClass))
1216 {
1217 final Class<?> componentType = typeInfo.getComponentType();
1218 if (componentType == null)
1219 {
1220 throw new LDAPPersistException(
1221 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName()));
1222 }
1223
1224 final ASN1OctetString[] values = attribute.getRawValues();
1225 final Set<?> s = createSet(baseClass, values.length);
1226 for (int i=0; i < values.length; i++)
1227 {
1228 final Object o = getValue(componentType, attribute, i);
1229 if (o == null)
1230 {
1231 throw new LDAPPersistException(
1232 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1233 componentType.getName()));
1234 }
1235
1236 invokeAdd(s, o);
1237 }
1238
1239 method.invoke(object, s);
1240 return;
1241 }
1242
1243 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(
1244 baseClass.getName()));
1245 }
1246 catch (LDAPPersistException lpe)
1247 {
1248 debugException(lpe);
1249 throw lpe;
1250 }
1251 catch (Throwable t)
1252 {
1253 debugException(t);
1254
1255 if (t instanceof InvocationTargetException)
1256 {
1257 t = ((InvocationTargetException) t).getTargetException();
1258 }
1259
1260 throw new LDAPPersistException(getExceptionMessage(t), t);
1261 }
1262 }
1263
1264
1265
1266 /**
1267 * Creates an object of the specified type from the given attribute value.
1268 *
1269 * @param t The type of object to create.
1270 * @param a The attribute to use to create the object.
1271 * @param p The position in the set of values for the object to create.
1272 *
1273 * @return The created object, or {@code null} if the provided type is not
1274 * supported.
1275 *
1276 * @throws LDAPPersistException If a problem occurs while creating the
1277 * object.
1278 */
1279 @SuppressWarnings("unchecked")
1280 private static Object getValue(final Class<?> t, final Attribute a,
1281 final int p)
1282 throws LDAPPersistException
1283 {
1284 final ASN1OctetString v = a.getRawValues()[p];
1285
1286 if (t.equals(AtomicInteger.class))
1287 {
1288 return new AtomicInteger(Integer.valueOf(v.stringValue()));
1289 }
1290 else if (t.equals(AtomicLong.class))
1291 {
1292 return new AtomicLong(Long.valueOf(v.stringValue()));
1293 }
1294 else if (t.equals(BigDecimal.class))
1295 {
1296 return new BigDecimal(v.stringValue());
1297 }
1298 else if (t.equals(BigInteger.class))
1299 {
1300 return new BigInteger(v.stringValue());
1301 }
1302 else if (t.equals(Double.class) || t.equals(Double.TYPE))
1303 {
1304 return Double.valueOf(v.stringValue());
1305 }
1306 else if (t.equals(Float.class) || t.equals(Float.TYPE))
1307 {
1308 return Float.valueOf(v.stringValue());
1309 }
1310 else if (t.equals(Integer.class) || t.equals(Integer.TYPE))
1311 {
1312 return Integer.valueOf(v.stringValue());
1313 }
1314 else if (t.equals(Long.class) || t.equals(Long.TYPE))
1315 {
1316 return Long.valueOf(v.stringValue());
1317 }
1318 else if (t.equals(Short.class) || t.equals(Short.TYPE))
1319 {
1320 return Short.valueOf(v.stringValue());
1321 }
1322 else if (t.equals(String.class))
1323 {
1324 return String.valueOf(v.stringValue());
1325 }
1326 else if (t.equals(StringBuffer.class))
1327 {
1328 return new StringBuffer(v.stringValue());
1329 }
1330 else if (t.equals(StringBuilder.class))
1331 {
1332 return new StringBuilder(v.stringValue());
1333 }
1334 else if (t.equals(URI.class))
1335 {
1336 try
1337 {
1338 return new URI(v.stringValue());
1339 }
1340 catch (final Exception e)
1341 {
1342 debugException(e);
1343 throw new LDAPPersistException(
1344 ERR_DEFAULT_ENCODER_VALUE_INVALID_URI.get(v.stringValue(),
1345 getExceptionMessage(e)), e);
1346 }
1347 }
1348 else if (t.equals(URL.class))
1349 {
1350 try
1351 {
1352 return new URL(v.stringValue());
1353 }
1354 catch (final Exception e)
1355 {
1356 debugException(e);
1357 throw new LDAPPersistException(
1358 ERR_DEFAULT_ENCODER_VALUE_INVALID_URL.get(v.stringValue(),
1359 getExceptionMessage(e)), e);
1360 }
1361 }
1362 else if (t.equals(UUID.class))
1363 {
1364 try
1365 {
1366 return UUID.fromString(v.stringValue());
1367 }
1368 catch (Exception e)
1369 {
1370 debugException(e);
1371 throw new LDAPPersistException(
1372 ERR_DEFAULT_ENCODER_VALUE_INVALID_UUID.get(v.stringValue(),
1373 getExceptionMessage(e)), e);
1374 }
1375 }
1376 else if (t.equals(DN.class))
1377 {
1378 try
1379 {
1380 return new DN(v.stringValue());
1381 }
1382 catch (LDAPException le)
1383 {
1384 debugException(le);
1385 throw new LDAPPersistException(le.getMessage(), le);
1386 }
1387 }
1388 else if (t.equals(Filter.class))
1389 {
1390 try
1391 {
1392 return Filter.create(v.stringValue());
1393 }
1394 catch (LDAPException le)
1395 {
1396 debugException(le);
1397 throw new LDAPPersistException(le.getMessage(), le);
1398 }
1399 }
1400 else if (t.equals(LDAPURL.class))
1401 {
1402 try
1403 {
1404 return new LDAPURL(v.stringValue());
1405 }
1406 catch (LDAPException le)
1407 {
1408 debugException(le);
1409 throw new LDAPPersistException(le.getMessage(), le);
1410 }
1411 }
1412 else if (t.equals(RDN.class))
1413 {
1414 try
1415 {
1416 return new RDN(v.stringValue());
1417 }
1418 catch (LDAPException le)
1419 {
1420 debugException(le);
1421 throw new LDAPPersistException(le.getMessage(), le);
1422 }
1423 }
1424 else if (t.equals(Boolean.class) || t.equals(Boolean.TYPE))
1425 {
1426 final String s = v.stringValue();
1427 if (s.equalsIgnoreCase("TRUE"))
1428 {
1429 return Boolean.TRUE;
1430 }
1431 else if (s.equalsIgnoreCase("FALSE"))
1432 {
1433 return Boolean.FALSE;
1434 }
1435 else
1436 {
1437 throw new LDAPPersistException(
1438 ERR_DEFAULT_ENCODER_VALUE_INVALID_BOOLEAN.get(s));
1439 }
1440 }
1441 else if (t.equals(Date.class))
1442 {
1443 try
1444 {
1445 return decodeGeneralizedTime(v.stringValue());
1446 }
1447 catch (Exception e)
1448 {
1449 debugException(e);
1450 throw new LDAPPersistException(
1451 ERR_DEFAULT_ENCODER_VALUE_INVALID_DATE.get(v.stringValue(),
1452 e.getMessage()), e);
1453 }
1454 }
1455 else if (t.isArray())
1456 {
1457 final Class<?> componentType = t.getComponentType();
1458 if (componentType.equals(Byte.TYPE))
1459 {
1460 return v.getValue();
1461 }
1462 else if (componentType.equals(Character.TYPE))
1463 {
1464 return v.stringValue().toCharArray();
1465 }
1466 }
1467 else if (t.isEnum())
1468 {
1469 try
1470 {
1471 final Class<? extends Enum> enumClass = (Class<? extends Enum>) t;
1472 return Enum.valueOf(enumClass, v.stringValue());
1473 }
1474 catch (final Exception e)
1475 {
1476 debugException(e);
1477 throw new LDAPPersistException(
1478 ERR_DEFAULT_ENCODER_VALUE_INVALID_ENUM.get(v.stringValue(),
1479 getExceptionMessage(e)), e);
1480 }
1481 }
1482 else if (Serializable.class.isAssignableFrom(t))
1483 {
1484 // We shouldn't attempt to work on arrays/collections themselves. Return
1485 // null and then we'll work on each element.
1486 if (t.isArray() || Collection.class.isAssignableFrom(t))
1487 {
1488 return null;
1489 }
1490
1491 try
1492 {
1493 final ByteArrayInputStream bais =
1494 new ByteArrayInputStream(v.getValue());
1495 final ObjectInputStream ois = new ObjectInputStream(bais);
1496 final Object o = ois.readObject();
1497 ois.close();
1498 return o;
1499 }
1500 catch (final Exception e)
1501 {
1502 debugException(e);
1503 throw new LDAPPersistException(
1504 ERR_DEFAULT_ENCODER_CANNOT_DESERIALIZE.get(a.getName(),
1505 getExceptionMessage(e)),
1506 e);
1507 }
1508 }
1509
1510 return null;
1511 }
1512
1513
1514
1515 /**
1516 * Invokes the {@code add} method on the provided {@code List} or {@code Set}
1517 * object.
1518 *
1519 * @param l The list or set on which to invoke the {@code add} method.
1520 * @param o The object to add to the {@code List} or {@code Set} object.
1521 *
1522 * @throws LDAPPersistException If a problem occurs while attempting to
1523 * invoke the {@code add} method.
1524 */
1525 private static void invokeAdd(final Object l, final Object o)
1526 throws LDAPPersistException
1527 {
1528 final Class<?> c = l.getClass();
1529
1530 for (final Method m : c.getMethods())
1531 {
1532 if (m.getName().equals("add") &&
1533 (m.getGenericParameterTypes().length == 1))
1534 {
1535 try
1536 {
1537 m.invoke(l, o);
1538 return;
1539 }
1540 catch (final Exception e)
1541 {
1542 debugException(e);
1543 throw new LDAPPersistException(
1544 ERR_DEFAULT_ENCODER_CANNOT_ADD.get(getExceptionMessage(e)), e);
1545 }
1546 }
1547 }
1548
1549 throw new LDAPPersistException(
1550 ERR_DEFAULT_ENCODER_CANNOT_FIND_ADD_METHOD.get());
1551 }
1552 }