001/* 002 * Copyright 2009-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2009-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.persist; 037 038 039 040import java.io.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}