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 }