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