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