001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.shared.ldap.util; 021 022 023 import java.lang.reflect.AccessibleObject; 024 import java.lang.reflect.Field; 025 import java.lang.reflect.Modifier; 026 import java.util.HashSet; 027 import java.util.Set; 028 029 import org.apache.directory.shared.i18n.I18n; 030 031 032 /** 033 * <p> 034 * Assists in implementing {@link Object#toString()}methods using reflection. 035 * </p> 036 * <p> 037 * This class uses reflection to determine the fields to append. Because these 038 * fields are usually private, the class uses 039 * {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} 040 * to change the visibility of the fields. This will fail under a security 041 * manager, unless the appropriate permissions are set up correctly. 042 * </p> 043 * <p> 044 * A typical invocation for this method would look like: 045 * </p> 046 * 047 * <pre> 048 * public String toString() 049 * { 050 * return ReflectionToStringBuilder.toString( this ); 051 * } 052 * </pre> 053 * 054 * <p> 055 * You can also use the builder to debug 3rd party objects: 056 * </p> 057 * 058 * <pre> 059 * System.out.println( "An object: " + ReflectionToStringBuilder.toString( anObject ) ); 060 * </pre> 061 * 062 * <p> 063 * A subclass can control field output by overriding the methods: 064 * <ul> 065 * <li>{@link #accept(java.lang.reflect.Field)}</li> 066 * <li>{@link #getValue(java.lang.reflect.Field)}</li> 067 * </ul> 068 * </p> 069 * <p> 070 * For example, this method does <i>not</i> include the <code>password</code> 071 * field in the returned <code>String</code>: 072 * </p> 073 * 074 * <pre> 075 * public String toString() 076 * { 077 * return ( new ReflectionToStringBuilder( this ) 078 * { 079 * protected boolean accept( Field f ) 080 * { 081 * return super.accept( f ) && !f.getName().equals( "password" ); 082 * } 083 * } ).toString(); 084 * } 085 * </pre> 086 * 087 * <p> 088 * The exact format of the <code>toString</code> is determined by the 089 * {@link ToStringStyle} passed into the constructor. 090 * </p> 091 * 092 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 093 */ 094 public class ReflectionToStringBuilder extends ToStringBuilder 095 { 096 /** 097 * <p> 098 * A registry of objects used by <code>reflectionToString</code> methods 099 * to detect cyclical object references and avoid infinite loops. 100 * </p> 101 */ 102 private static ThreadLocal registry = new ThreadLocal() 103 { 104 protected synchronized Object initialValue() 105 { 106 // The HashSet implementation is not synchronized, 107 // which is just what we need here. 108 return new HashSet(); 109 } 110 }; 111 112 113 /** 114 * <p> 115 * Returns the registry of objects being traversed by the 116 * <code>reflectionToString</code> methods in the current thread. 117 * </p> 118 * 119 * @return Set the registry of objects being traversed 120 */ 121 static Set getRegistry() 122 { 123 return ( Set ) registry.get(); 124 } 125 126 127 /** 128 * <p> 129 * Returns <code>true</code> if the registry contains the given object. 130 * Used by the reflection methods to avoid infinite loops. 131 * </p> 132 * 133 * @param value 134 * The object to lookup in the registry. 135 * @return boolean <code>true</code> if the registry contains the given 136 * object. 137 */ 138 static boolean isRegistered( Object value ) 139 { 140 return getRegistry().contains( value ); 141 } 142 143 144 /** 145 * <p> 146 * Registers the given object. Used by the reflection methods to avoid 147 * infinite loops. 148 * </p> 149 * 150 * @param value 151 * The object to register. 152 */ 153 static void register( Object value ) 154 { 155 getRegistry().add( value ); 156 } 157 158 159 /** 160 * <p> 161 * This method uses reflection to build a suitable <code>toString</code> 162 * using the default <code>ToStringStyle</code>. 163 * <p> 164 * It uses <code>AccessibleObject.setAccessible</code> to gain access to 165 * private fields. This means that it will throw a security exception if run 166 * under a security manager, if the permissions are not set up correctly. It 167 * is also not as efficient as testing explicitly. 168 * </p> 169 * <p> 170 * Transient members will be not be included, as they are likely derived. 171 * Static fields will not be included. Superclass fields will be appended. 172 * </p> 173 * 174 * @param object 175 * the Object to be output 176 * @return the String result 177 * @throws IllegalArgumentException 178 * if the Object is <code>null</code> 179 */ 180 public static String toString( Object object ) 181 { 182 return toString( object, null, false, false, null ); 183 } 184 185 186 /** 187 * <p> 188 * This method uses reflection to build a suitable <code>toString</code>. 189 * </p> 190 * <p> 191 * It uses <code>AccessibleObject.setAccessible</code> to gain access to 192 * private fields. This means that it will throw a security exception if run 193 * under a security manager, if the permissions are not set up correctly. It 194 * is also not as efficient as testing explicitly. 195 * </p> 196 * <p> 197 * Transient members will be not be included, as they are likely derived. 198 * Static fields will not be included. Superclass fields will be appended. 199 * </p> 200 * <p> 201 * If the style is <code>null</code>, the default 202 * <code>ToStringStyle</code> is used. 203 * </p> 204 * 205 * @param object 206 * the Object to be output 207 * @param style 208 * the style of the <code>toString</code> to create, may be 209 * <code>null</code> 210 * @return the String result 211 * @throws IllegalArgumentException 212 * if the Object or <code>ToStringStyle</code> is 213 * <code>null</code> 214 */ 215 public static String toString( Object object, ToStringStyle style ) 216 { 217 return toString( object, style, false, false, null ); 218 } 219 220 221 /** 222 * <p> 223 * This method uses reflection to build a suitable <code>toString</code>. 224 * </p> 225 * <p> 226 * It uses <code>AccessibleObject.setAccessible</code> to gain access to 227 * private fields. This means that it will throw a security exception if run 228 * under a security manager, if the permissions are not set up correctly. It 229 * is also not as efficient as testing explicitly. 230 * </p> 231 * <p> 232 * If the <code>outputTransients</code> is <code>true</code>, transient 233 * members will be output, otherwise they are ignored, as they are likely 234 * derived fields, and not part of the value of the Object. 235 * </p> 236 * <p> 237 * Static fields will not be included. Superclass fields will be appended. 238 * </p> 239 * <p> 240 * If the style is <code>null</code>, the default 241 * <code>ToStringStyle</code> is used. 242 * </p> 243 * 244 * @param object 245 * the Object to be output 246 * @param style 247 * the style of the <code>toString</code> to create, may be 248 * <code>null</code> 249 * @param outputTransients 250 * whether to include transient fields 251 * @return the String result 252 * @throws IllegalArgumentException 253 * if the Object is <code>null</code> 254 */ 255 public static String toString( Object object, ToStringStyle style, boolean outputTransients ) 256 { 257 return toString( object, style, outputTransients, false, null ); 258 } 259 260 261 /** 262 * <p> 263 * This method uses reflection to build a suitable <code>toString</code>. 264 * </p> 265 * <p> 266 * It uses <code>AccessibleObject.setAccessible</code> to gain access to 267 * private fields. This means that it will throw a security exception if run 268 * under a security manager, if the permissions are not set up correctly. It 269 * is also not as efficient as testing explicitly. 270 * </p> 271 * <p> 272 * If the <code>outputTransients</code> is <code>true</code>, transient 273 * fields will be output, otherwise they are ignored, as they are likely 274 * derived fields, and not part of the value of the Object. 275 * </p> 276 * <p> 277 * If the <code>outputStatics</code> is <code>true</code>, static 278 * fields will be output, otherwise they are ignored. 279 * </p> 280 * <p> 281 * Static fields will not be included. Superclass fields will be appended. 282 * </p> 283 * <p> 284 * If the style is <code>null</code>, the default 285 * <code>ToStringStyle</code> is used. 286 * </p> 287 * 288 * @param object 289 * the Object to be output 290 * @param style 291 * the style of the <code>toString</code> to create, may be 292 * <code>null</code> 293 * @param outputTransients 294 * whether to include transient fields 295 * @param outputStatics 296 * whether to include transient fields 297 * @return the String result 298 * @throws IllegalArgumentException 299 * if the Object is <code>null</code> 300 */ 301 public static String toString( Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics ) 302 { 303 return toString( object, style, outputTransients, outputStatics, null ); 304 } 305 306 307 /** 308 * <p> 309 * This method uses reflection to build a suitable <code>toString</code>. 310 * </p> 311 * <p> 312 * It uses <code>AccessibleObject.setAccessible</code> to gain access to 313 * private fields. This means that it will throw a security exception if run 314 * under a security manager, if the permissions are not set up correctly. It 315 * is also not as efficient as testing explicitly. 316 * </p> 317 * <p> 318 * If the <code>outputTransients</code> is <code>true</code>, transient 319 * fields will be output, otherwise they are ignored, as they are likely 320 * derived fields, and not part of the value of the Object. 321 * </p> 322 * <p> 323 * If the <code>outputStatics</code> is <code>true</code>, static 324 * fields will be output, otherwise they are ignored. 325 * </p> 326 * <p> 327 * Superclass fields will be appended up to and including the specified 328 * superclass. A null superclass is treated as <code>java.lang.Object</code>. 329 * </p> 330 * <p> 331 * If the style is <code>null</code>, the default 332 * <code>ToStringStyle</code> is used. 333 * </p> 334 * 335 * @param object 336 * the Object to be output 337 * @param style 338 * the style of the <code>toString</code> to create, may be 339 * <code>null</code> 340 * @param outputTransients 341 * whether to include transient fields 342 * @param outputStatics 343 * whether to include static fields 344 * @param reflectUpToClass 345 * the superclass to reflect up to (inclusive), may be 346 * <code>null</code> 347 * @return the String result 348 * @throws IllegalArgumentException 349 * if the Object is <code>null</code> 350 */ 351 public static String toString( Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics, 352 Class reflectUpToClass ) 353 { 354 return new ReflectionToStringBuilder( object, style, null, reflectUpToClass, outputTransients, outputStatics ) 355 .toString(); 356 } 357 358 359 /** 360 * <p> 361 * This method uses reflection to build a suitable <code>toString</code>. 362 * </p> 363 * <p> 364 * It uses <code>AccessibleObject.setAccessible</code> to gain access to 365 * private fields. This means that it will throw a security exception if run 366 * under a security manager, if the permissions are not set up correctly. It 367 * is also not as efficient as testing explicitly. 368 * </p> 369 * <p> 370 * If the <code>outputTransients</code> is <code>true</code>, transient 371 * members will be output, otherwise they are ignored, as they are likely 372 * derived fields, and not part of the value of the Object. 373 * </p> 374 * <p> 375 * Static fields will not be included. Superclass fields will be appended up 376 * to and including the specified superclass. A null superclass is treated 377 * as <code>java.lang.Object</code>. 378 * </p> 379 * <p> 380 * If the style is <code>null</code>, the default 381 * <code>ToStringStyle</code> is used. 382 * </p> 383 * 384 * @deprecated Use 385 * {@link #toString(Object,ToStringStyle,boolean,boolean,Class)} 386 * @param object 387 * the Object to be output 388 * @param style 389 * the style of the <code>toString</code> to create, may be 390 * <code>null</code> 391 * @param outputTransients 392 * whether to include transient fields 393 * @param reflectUpToClass 394 * the superclass to reflect up to (inclusive), may be 395 * <code>null</code> 396 * @return the String result 397 * @throws IllegalArgumentException 398 * if the Object is <code>null</code> 399 */ 400 public static String toString( Object object, ToStringStyle style, boolean outputTransients, Class reflectUpToClass ) 401 { 402 return new ReflectionToStringBuilder( object, style, null, reflectUpToClass, outputTransients ).toString(); 403 } 404 405 406 /** 407 * <p> 408 * Unregisters the given object. 409 * </p> 410 * <p> 411 * Used by the reflection methods to avoid infinite loops. 412 * </p> 413 * 414 * @param value 415 * The object to unregister. 416 */ 417 static void unregister( Object value ) 418 { 419 getRegistry().remove( value ); 420 } 421 422 /** 423 * Whether or not to append static fields. 424 */ 425 private boolean appendStatics = false; 426 427 /** 428 * Whether or not to append transient fields. 429 */ 430 private boolean appendTransients = false; 431 432 /** 433 * The last super class to stop appending fields for. 434 */ 435 private Class upToClass = null; 436 437 438 /** 439 * <p> 440 * Constructor. 441 * </p> 442 * <p> 443 * This constructor outputs using the default style set with 444 * <code>setDefaultStyle</code>. 445 * </p> 446 * 447 * @param object 448 * the Object to build a <code>toString</code> for, must not be 449 * <code>null</code> 450 * @throws IllegalArgumentException 451 * if the Object passed in is <code>null</code> 452 */ 453 public ReflectionToStringBuilder(Object object) 454 { 455 super( object ); 456 } 457 458 459 /** 460 * <p> 461 * Constructor. 462 * </p> 463 * <p> 464 * If the style is <code>null</code>, the default style is used. 465 * </p> 466 * 467 * @param object 468 * the Object to build a <code>toString</code> for, must not be 469 * <code>null</code> 470 * @param style 471 * the style of the <code>toString</code> to create, may be 472 * <code>null</code> 473 * @throws IllegalArgumentException 474 * if the Object passed in is <code>null</code> 475 */ 476 public ReflectionToStringBuilder(Object object, ToStringStyle style) 477 { 478 super( object, style ); 479 } 480 481 482 /** 483 * <p> 484 * Constructor. 485 * </p> 486 * <p> 487 * If the style is <code>null</code>, the default style is used. 488 * </p> 489 * <p> 490 * If the buffer is <code>null</code>, a new one is created. 491 * </p> 492 * 493 * @param object 494 * the Object to build a <code>toString</code> for 495 * @param style 496 * the style of the <code>toString</code> to create, may be 497 * <code>null</code> 498 * @param buffer 499 * the <code>StringBuffer</code> to populate, may be 500 * <code>null</code> 501 * @throws IllegalArgumentException 502 * if the Object passed in is <code>null</code> 503 */ 504 public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) 505 { 506 super( object, style, buffer ); 507 } 508 509 510 /** 511 * Constructor. 512 * 513 * @deprecated Use 514 * {@link #ReflectionToStringBuilder(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)}. 515 * @param object 516 * the Object to build a <code>toString</code> for 517 * @param style 518 * the style of the <code>toString</code> to create, may be 519 * <code>null</code> 520 * @param buffer 521 * the <code>StringBuffer</code> to populate, may be 522 * <code>null</code> 523 * @param reflectUpToClass 524 * the superclass to reflect up to (inclusive), may be 525 * <code>null</code> 526 * @param outputTransients 527 * whether to include transient fields 528 */ 529 public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, 530 boolean outputTransients) 531 { 532 super( object, style, buffer ); 533 this.setUpToClass( reflectUpToClass ); 534 this.setAppendTransients( outputTransients ); 535 } 536 537 538 /** 539 * Constructor. 540 * 541 * @param object 542 * the Object to build a <code>toString</code> for 543 * @param style 544 * the style of the <code>toString</code> to create, may be 545 * <code>null</code> 546 * @param buffer 547 * the <code>StringBuffer</code> to populate, may be 548 * <code>null</code> 549 * @param reflectUpToClass 550 * the superclass to reflect up to (inclusive), may be 551 * <code>null</code> 552 * @param outputTransients 553 * whether to include transient fields 554 * @param outputStatics 555 * whether to include static fields 556 */ 557 public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass, 558 boolean outputTransients, boolean outputStatics) 559 { 560 super( object, style, buffer ); 561 this.setUpToClass( reflectUpToClass ); 562 this.setAppendTransients( outputTransients ); 563 this.setAppendStatics( outputStatics ); 564 } 565 566 567 /** 568 * Returns whether or not to append the given <code>Field</code>. 569 * <ul> 570 * <li>Transient fields are appended only if {@link #isAppendTransients()} 571 * returns <code>true</code>. 572 * <li>Static fields are appended only if {@link #isAppendStatics()} 573 * returns <code>true</code>. 574 * <li>Inner class fields are not appened.</li> 575 * </ul> 576 * 577 * @param field 578 * The Field to test. 579 * @return Whether or not to append the given <code>Field</code>. 580 */ 581 protected boolean accept( Field field ) 582 { 583 if ( field.getName().indexOf( '$' ) != -1 ) 584 { 585 // Reject field from inner class. 586 return false; 587 } 588 if ( Modifier.isTransient( field.getModifiers() ) && !this.isAppendTransients() ) 589 { 590 // transients. 591 return false; 592 } 593 if ( Modifier.isStatic( field.getModifiers() ) && !this.isAppendStatics() ) 594 { 595 // transients. 596 return false; 597 } 598 return true; 599 } 600 601 602 /** 603 * <p> 604 * Appends the fields and values defined by the given object of the given 605 * Class. 606 * </p> 607 * <p> 608 * If a cycle is detected as an object is "toString()'ed", such an 609 * object is rendered as if <code>Object.toString()</code> had been called 610 * and not implemented by the object. 611 * </p> 612 * 613 * @param clazz 614 * The class of object parameter 615 */ 616 protected void appendFieldsIn( Class clazz ) 617 { 618 if ( isRegistered( this.getObject() ) ) 619 { 620 // The object has already been appended, therefore we have an 621 // object cycle. 622 // Append a simple Object.toString style string. The field name is 623 // already appended at this point. 624 this.appendAsObjectToString( this.getObject() ); 625 return; 626 } 627 try 628 { 629 this.registerObject(); 630 if ( clazz.isArray() ) 631 { 632 this.reflectionAppendArray( this.getObject() ); 633 return; 634 } 635 Field[] fields = clazz.getDeclaredFields(); 636 AccessibleObject.setAccessible( fields, true ); 637 for ( int i = 0; i < fields.length; i++ ) 638 { 639 Field field = fields[i]; 640 String fieldName = field.getName(); 641 if ( this.accept( field ) ) 642 { 643 try 644 { 645 // Warning: Field.get(Object) creates wrappers objects 646 // for primitive types. 647 Object fieldValue = this.getValue( field ); 648 if ( isRegistered( fieldValue ) && !field.getType().isPrimitive() ) 649 { 650 // A known field value has already been appended, 651 // therefore we have an object cycle, 652 // append a simple Object.toString style string. 653 this.getStyle().appendFieldStart( this.getStringBuffer(), fieldName ); 654 this.appendAsObjectToString( fieldValue ); 655 // The recursion out of 656 // builder.append(fieldName, fieldValue); 657 // below will append the field 658 // end marker. 659 } 660 else 661 { 662 try 663 { 664 this.registerObject(); 665 this.append( fieldName, fieldValue ); 666 } 667 finally 668 { 669 this.unregisterObject(); 670 } 671 } 672 } 673 catch ( IllegalAccessException ex ) 674 { 675 // this can't happen. Would get a Security exception 676 // instead 677 // throw a runtime exception in case the impossible 678 // happens. 679 throw new InternalError( I18n.err( I18n.ERR_04424, ex.getLocalizedMessage() ) ); 680 } 681 } 682 } 683 } 684 finally 685 { 686 this.unregisterObject(); 687 } 688 } 689 690 691 /** 692 * <p> 693 * Gets the last super class to stop appending fields for. 694 * </p> 695 * 696 * @return The last super class to stop appending fields for. 697 */ 698 public Class getUpToClass() 699 { 700 return this.upToClass; 701 } 702 703 704 /** 705 * <p> 706 * Calls <code>java.lang.reflect.Field.get(Object)</code>. 707 * </p> 708 * 709 * @param field 710 * The Field to query. 711 * @return The Object from the given Field. 712 * @throws IllegalArgumentException 713 * see {@link java.lang.reflect.Field#get(Object)} 714 * @throws IllegalAccessException 715 * see {@link java.lang.reflect.Field#get(Object)} 716 * @see java.lang.reflect.Field#get(Object) 717 */ 718 protected Object getValue( Field field ) throws IllegalArgumentException, IllegalAccessException 719 { 720 return field.get( this.getObject() ); 721 } 722 723 724 /** 725 * <p> 726 * Gets whether or not to append static fields. 727 * </p> 728 * 729 * @return Whether or not to append static fields. 730 */ 731 public boolean isAppendStatics() 732 { 733 return this.appendStatics; 734 } 735 736 737 /** 738 * <p> 739 * Gets whether or not to append transient fields. 740 * </p> 741 * 742 * @return Whether or not to append transient fields. 743 */ 744 public boolean isAppendTransients() 745 { 746 return this.appendTransients; 747 } 748 749 750 /** 751 * <p> 752 * Append to the <code>toString</code> an <code>Object</code> array. 753 * </p> 754 * 755 * @param array 756 * the array to add to the <code>toString</code> 757 * @return this 758 */ 759 public ToStringBuilder reflectionAppendArray( Object array ) 760 { 761 this.getStyle().reflectionAppendArrayDetail( this.getStringBuffer(), null, array ); 762 return this; 763 } 764 765 766 /** 767 * <p> 768 * Registers this builder's source object to avoid infinite loops when 769 * processing circular object references. 770 * </p> 771 */ 772 void registerObject() 773 { 774 register( this.getObject() ); 775 } 776 777 778 /** 779 * <p> 780 * Sets whether or not to append static fields. 781 * </p> 782 * 783 * @param appendStatics 784 * Whether or not to append static fields. 785 */ 786 public void setAppendStatics( boolean appendStatics ) 787 { 788 this.appendStatics = appendStatics; 789 } 790 791 792 /** 793 * <p> 794 * Sets whether or not to append transient fields. 795 * </p> 796 * 797 * @param appendTransients 798 * Whether or not to append transient fields. 799 */ 800 public void setAppendTransients( boolean appendTransients ) 801 { 802 this.appendTransients = appendTransients; 803 } 804 805 806 /** 807 * <p> 808 * Sets the last super class to stop appending fields for. 809 * </p> 810 * 811 * @param clazz 812 * The last super class to stop appending fields for. 813 */ 814 public void setUpToClass( Class clazz ) 815 { 816 this.upToClass = clazz; 817 } 818 819 820 /** 821 * <p> 822 * Gets the String built by this builder. 823 * </p> 824 * 825 * @return the built string 826 */ 827 public String toString() 828 { 829 if ( this.getObject() == null ) 830 { 831 return this.getStyle().getNullText(); 832 } 833 Class clazz = this.getObject().getClass(); 834 this.appendFieldsIn( clazz ); 835 while ( clazz.getSuperclass() != null && clazz != this.getUpToClass() ) 836 { 837 clazz = clazz.getSuperclass(); 838 this.appendFieldsIn( clazz ); 839 } 840 return super.toString(); 841 } 842 843 844 /** 845 * <p> 846 * Unregisters this builder's source object to avoid infinite loops when 847 * processing circular object references. 848 * </p> 849 */ 850 void unregisterObject() 851 { 852 unregister( this.getObject() ); 853 } 854 }