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