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