View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang.builder;
19  
20  import java.lang.reflect.AccessibleObject;
21  import java.lang.reflect.Field;
22  import java.lang.reflect.Modifier;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  
27  import org.apache.commons.lang.ArrayUtils;
28  import org.apache.commons.lang.ClassUtils;
29  
30  /**
31   * <p>
32   * Assists in implementing {@link Object#toString()} methods using reflection.
33   * </p>
34   * 
35   * <p>
36   * This class uses reflection to determine the fields to append. Because these fields are usually private, the class
37   * uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to
38   * change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are
39   * set up correctly.
40   * </p>
41   * 
42   * <p>
43   * A typical invocation for this method would look like:
44   * </p>
45   * 
46   * <pre>
47   * public String toString() {
48   *   return ReflectionToStringBuilder.toString(this);
49   * }</pre>
50   * 
51   * 
52   * 
53   * <p>
54   * You can also use the builder to debug 3rd party objects:
55   * </p>
56   * 
57   * <pre>
58   * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));</pre>
59   * 
60   * 
61   * 
62   * <p>
63   * A subclass can control field output by overriding the methods:
64   * <ul>
65   * <li>{@link #accept(java.lang.reflect.Field)}</li>
66   * <li>{@link #getValue(java.lang.reflect.Field)}</li>
67   * </ul>
68   * </p>
69   * <p>
70   * For example, this method does <i>not</i> include the <code>password</code> field in the returned
71   * <code>String</code>:
72   * </p>
73   * 
74   * <pre>
75   * public String toString() {
76   *     return (new ReflectionToStringBuilder(this) {
77   *         protected boolean accept(Field f) {
78   *             return super.accept(f) && !f.getName().equals("password");
79   *         }
80   *     }).toString();
81   * }</pre>
82   * 
83   * 
84   * 
85   * <p>
86   * The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the
87   * constructor.
88   * </p>
89   * 
90   * @author Gary Gregory
91   * @author Stephen Colebourne
92   * @author Pete Gieser
93   * @since 2.0
94   * @version $Id: ReflectionToStringBuilder.java 583666 2007-10-11 01:38:13Z ggregory $
95   */
96  public class ReflectionToStringBuilder extends ToStringBuilder {
97  
98      /**
99       * <p>
100      * Builds a <code>toString</code> value using the default <code>ToStringStyle</code> through reflection.
101      * </p>
102      * 
103      * <p>
104      * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
105      * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
106      * also not as efficient as testing explicitly.
107      * </p>
108      * 
109      * <p>
110      * Transient members will be not be included, as they are likely derived. Static fields will not be included.
111      * Superclass fields will be appended.
112      * </p>
113      * 
114      * @param object
115      *            the Object to be output
116      * @return the String result
117      * @throws IllegalArgumentException
118      *             if the Object is <code>null</code>
119      */
120     public static String toString(Object object) {
121         return toString(object, null, false, false, null);
122     }
123 
124     /**
125      * <p>
126      * Builds a <code>toString</code> value through reflection.
127      * </p>
128      * 
129      * <p>
130      * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
131      * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
132      * also not as efficient as testing explicitly.
133      * </p>
134      * 
135      * <p>
136      * Transient members will be not be included, as they are likely derived. Static fields will not be included.
137      * Superclass fields will be appended.
138      * </p>
139      * 
140      * <p>
141      * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
142      * </p>
143      * 
144      * @param object
145      *            the Object to be output
146      * @param style
147      *            the style of the <code>toString</code> to create, may be <code>null</code>
148      * @return the String result
149      * @throws IllegalArgumentException
150      *             if the Object or <code>ToStringStyle</code> is <code>null</code>
151      */
152     public static String toString(Object object, ToStringStyle style) {
153         return toString(object, style, false, false, null);
154     }
155 
156     /**
157      * <p>
158      * Builds a <code>toString</code> value through reflection.
159      * </p>
160      * 
161      * <p>
162      * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
163      * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
164      * also not as efficient as testing explicitly.
165      * </p>
166      * 
167      * <p>
168      * If the <code>outputTransients</code> is <code>true</code>, transient members will be output, otherwise they
169      * are ignored, as they are likely derived fields, and not part of the value of the Object.
170      * </p>
171      * 
172      * <p>
173      * Static fields will not be included. Superclass fields will be appended.
174      * </p>
175      * 
176      * <p>
177      * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
178      * </p>
179      * 
180      * @param object
181      *            the Object to be output
182      * @param style
183      *            the style of the <code>toString</code> to create, may be <code>null</code>
184      * @param outputTransients
185      *            whether to include transient fields
186      * @return the String result
187      * @throws IllegalArgumentException
188      *             if the Object is <code>null</code>
189      */
190     public static String toString(Object object, ToStringStyle style, boolean outputTransients) {
191         return toString(object, style, outputTransients, false, null);
192     }
193 
194     /**
195      * <p>
196      * Builds a <code>toString</code> value through reflection.
197      * </p>
198      * 
199      * <p>
200      * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
201      * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
202      * also not as efficient as testing explicitly.
203      * </p>
204      * 
205      * <p>
206      * If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they
207      * are ignored, as they are likely derived fields, and not part of the value of the Object.
208      * </p>
209      * 
210      * <p>
211      * If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are
212      * ignored.
213      * </p>
214      * 
215      * <p>
216      * Static fields will not be included. Superclass fields will be appended.
217      * </p>
218      * 
219      * <p>
220      * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
221      * </p>
222      * 
223      * @param object
224      *            the Object to be output
225      * @param style
226      *            the style of the <code>toString</code> to create, may be <code>null</code>
227      * @param outputTransients
228      *            whether to include transient fields
229      * @param outputStatics
230      *            whether to include transient fields
231      * @return the String result
232      * @throws IllegalArgumentException
233      *             if the Object is <code>null</code>
234      * @since 2.1
235      */
236     public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) {
237         return toString(object, style, outputTransients, outputStatics, null);
238     }
239 
240     /**
241      * <p>
242      * Builds a <code>toString</code> value through reflection.
243      * </p>
244      * 
245      * <p>
246      * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
247      * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
248      * also not as efficient as testing explicitly.
249      * </p>
250      * 
251      * <p>
252      * If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they
253      * are ignored, as they are likely derived fields, and not part of the value of the Object.
254      * </p>
255      * 
256      * <p>
257      * If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are
258      * ignored.
259      * </p>
260      * 
261      * <p>
262      * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as
263      * <code>java.lang.Object</code>.
264      * </p>
265      * 
266      * <p>
267      * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
268      * </p>
269      * 
270      * @param object
271      *            the Object to be output
272      * @param style
273      *            the style of the <code>toString</code> to create, may be <code>null</code>
274      * @param outputTransients
275      *            whether to include transient fields
276      * @param outputStatics
277      *            whether to include static fields
278      * @param reflectUpToClass
279      *            the superclass to reflect up to (inclusive), may be <code>null</code>
280      * @return the String result
281      * @throws IllegalArgumentException
282      *             if the Object is <code>null</code>
283      * @since 2.1
284      */
285     public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics,
286             Class reflectUpToClass) {
287         return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics)
288                 .toString();
289     }
290 
291     /**
292      * <p>
293      * Builds a <code>toString</code> value through reflection.
294      * </p>
295      * 
296      * <p>
297      * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
298      * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
299      * also not as efficient as testing explicitly.
300      * </p>
301      * 
302      * <p>
303      * If the <code>outputTransients</code> is <code>true</code>, transient members will be output, otherwise they
304      * are ignored, as they are likely derived fields, and not part of the value of the Object.
305      * </p>
306      * 
307      * <p>
308      * Static fields will not be included. Superclass fields will be appended up to and including the specified
309      * superclass. A null superclass is treated as <code>java.lang.Object</code>.
310      * </p>
311      * 
312      * <p>
313      * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
314      * </p>
315      * 
316      * @deprecated Use {@link #toString(Object,ToStringStyle,boolean,boolean,Class)}
317      * 
318      * @param object
319      *            the Object to be output
320      * @param style
321      *            the style of the <code>toString</code> to create, may be <code>null</code>
322      * @param outputTransients
323      *            whether to include transient fields
324      * @param reflectUpToClass
325      *            the superclass to reflect up to (inclusive), may be <code>null</code>
326      * @return the String result
327      * @throws IllegalArgumentException
328      *             if the Object is <code>null</code>
329      * @since 2.0
330      */
331     public static String toString(Object object, ToStringStyle style, 
332                                   boolean outputTransients, Class reflectUpToClass) 
333     {
334         return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients).toString();
335     }
336 
337     /**
338      * Builds a String for a toString method excluding the given field name.
339      * 
340      * @param object
341      *            The object to "toString".
342      * @param excludeFieldName
343      *            The field name to exclude
344      * @return The toString value.
345      */
346     public static String toStringExclude(Object object, final String excludeFieldName) {
347         return toStringExclude(object, new String[]{excludeFieldName});
348     }
349 
350     /**
351      * Builds a String for a toString method excluding the given field names.
352      * 
353      * @param object
354      *            The object to "toString".
355      * @param excludeFieldNames
356      *            The field names to exclude. Null excludes nothing.
357      * @return The toString value.
358      */
359     public static String toStringExclude(Object object, Collection /*String*/ excludeFieldNames) {
360         return toStringExclude(object, toNoNullStringArray(excludeFieldNames));
361     }
362 
363     /**
364      * Converts the given Collection into an array of Strings. The returned array does not contain <code>null</code>
365      * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element 
366      * is <code>null</code>.
367      * 
368      * @param collection
369      *            The collection to convert
370      * @return A new array of Strings.
371      */
372     static String[] toNoNullStringArray(Collection collection) {
373         if (collection == null) {
374             return ArrayUtils.EMPTY_STRING_ARRAY;
375         }
376         return toNoNullStringArray(collection.toArray());
377     }
378 
379     /**
380      * Returns a new array of Strings without null elements. Internal method used to normalize exclude lists
381      * (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} 
382      * if an array element is <code>null</code>.
383      * 
384      * @param array
385      *            The array to check
386      * @return The given array or a new array without null.
387      */
388     static String[] toNoNullStringArray(Object[] array) {
389         ArrayList list = new ArrayList(array.length);
390         for (int i = 0; i < array.length; i++) {
391             Object e = array[i];
392             if (e != null) {
393                 list.add(e.toString());
394             }
395         }
396         return (String[]) list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
397     }
398     
399 
400     /**
401      * Builds a String for a toString method excluding the given field names.
402      * 
403      * @param object
404      *            The object to "toString".
405      * @param excludeFieldNames
406      *            The field names to exclude
407      * @return The toString value.
408      */
409     public static String toStringExclude(Object object, String[] excludeFieldNames) {
410         return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString();
411     }
412 
413     /**
414      * Whether or not to append static fields.
415      */
416     private boolean appendStatics = false;
417 
418     /**
419      * Whether or not to append transient fields.
420      */
421     private boolean appendTransients = false;
422 
423     /**
424      * Which field names to exclude from output. Intended for fields like <code>"password"</code>.
425      */
426     private String[] excludeFieldNames;
427 
428     /**
429      * The last super class to stop appending fields for.
430      */
431     private Class upToClass = null;
432 
433     /**
434      * <p>
435      * Constructor.
436      * </p>
437      * 
438      * <p>
439      * This constructor outputs using the default style set with <code>setDefaultStyle</code>.
440      * </p>
441      * 
442      * @param object
443      *            the Object to build a <code>toString</code> for, must not be <code>null</code>
444      * @throws IllegalArgumentException
445      *             if the Object passed in is <code>null</code>
446      */
447     public ReflectionToStringBuilder(Object object) {
448         super(object);
449     }
450 
451     /**
452      * <p>
453      * Constructor.
454      * </p>
455      * 
456      * <p>
457      * If the style is <code>null</code>, the default style is used.
458      * </p>
459      * 
460      * @param object
461      *            the Object to build a <code>toString</code> for, must not be <code>null</code>
462      * @param style
463      *            the style of the <code>toString</code> to create, may be <code>null</code>
464      * @throws IllegalArgumentException
465      *             if the Object passed in is <code>null</code>
466      */
467     public ReflectionToStringBuilder(Object object, ToStringStyle style) {
468         super(object, style);
469     }
470 
471     /**
472      * <p>
473      * Constructor.
474      * </p>
475      * 
476      * <p>
477      * If the style is <code>null</code>, the default style is used.
478      * </p>
479      * 
480      * <p>
481      * If the buffer is <code>null</code>, a new one is created.
482      * </p>
483      * 
484      * @param object
485      *            the Object to build a <code>toString</code> for
486      * @param style
487      *            the style of the <code>toString</code> to create, may be <code>null</code>
488      * @param buffer
489      *            the <code>StringBuffer</code> to populate, may be <code>null</code>
490      * @throws IllegalArgumentException
491      *             if the Object passed in is <code>null</code>
492      */
493     public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
494         super(object, style, buffer);
495     }
496 
497     /**
498      * Constructor.
499      * 
500      * @deprecated Use {@link #ReflectionToStringBuilder(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)}.
501      * 
502      * @param object
503      *            the Object to build a <code>toString</code> for
504      * @param style
505      *            the style of the <code>toString</code> to create, may be <code>null</code>
506      * @param buffer
507      *            the <code>StringBuffer</code> to populate, may be <code>null</code>
508      * @param reflectUpToClass
509      *            the superclass to reflect up to (inclusive), may be <code>null</code>
510      * @param outputTransients
511      *            whether to include transient fields
512      */
513     public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass,
514             boolean outputTransients) {
515         super(object, style, buffer);
516         this.setUpToClass(reflectUpToClass);
517         this.setAppendTransients(outputTransients);
518     }
519 
520     /**
521      * Constructor.
522      * 
523      * @param object
524      *            the Object to build a <code>toString</code> for
525      * @param style
526      *            the style of the <code>toString</code> to create, may be <code>null</code>
527      * @param buffer
528      *            the <code>StringBuffer</code> to populate, may be <code>null</code>
529      * @param reflectUpToClass
530      *            the superclass to reflect up to (inclusive), may be <code>null</code>
531      * @param outputTransients
532      *            whether to include transient fields
533      * @param outputStatics
534      *            whether to include static fields
535      * @since 2.1
536      */
537     public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer, Class reflectUpToClass,
538             boolean outputTransients, boolean outputStatics) {
539         super(object, style, buffer);
540         this.setUpToClass(reflectUpToClass);
541         this.setAppendTransients(outputTransients);
542         this.setAppendStatics(outputStatics);
543     }
544 
545     /**
546      * Returns whether or not to append the given <code>Field</code>.
547      * <ul>
548      * <li>Transient fields are appended only if {@link #isAppendTransients()} returns <code>true</code>.
549      * <li>Static fields are appended only if {@link #isAppendStatics()} returns <code>true</code>.
550      * <li>Inner class fields are not appened.</li>
551      * </ul>
552      * 
553      * @param field
554      *            The Field to test.
555      * @return Whether or not to append the given <code>Field</code>.
556      */
557     protected boolean accept(Field field) {
558         if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
559             // Reject field from inner class.
560             return false;
561         }
562         if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) {
563             // Reject transient fields.
564             return false;
565         }
566         if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) {
567             // Rject static fields.
568             return false;
569         }
570         if (this.getExcludeFieldNames() != null
571             && Arrays.binarySearch(this.getExcludeFieldNames(), field.getName()) >= 0) {
572             // Reject fields from the getExcludeFieldNames list.
573             return false;
574         }
575         return true;
576     }
577 
578     /**
579      * <p>
580      * Appends the fields and values defined by the given object of the given Class.
581      * </p>
582      * 
583      * <p>
584      * If a cycle is detected as an object is &quot;toString()'ed&quot;, such an object is rendered as if
585      * <code>Object.toString()</code> had been called and not implemented by the object.
586      * </p>
587      * 
588      * @param clazz
589      *            The class of object parameter
590      */
591     protected void appendFieldsIn(Class clazz) {
592         if (clazz.isArray()) {
593             this.reflectionAppendArray(this.getObject());
594             return;
595         }
596         Field[] fields = clazz.getDeclaredFields();
597         AccessibleObject.setAccessible(fields, true);
598         for (int i = 0; i < fields.length; i++) {
599             Field field = fields[i];
600             String fieldName = field.getName();
601             if (this.accept(field)) {
602                 try {
603                     // Warning: Field.get(Object) creates wrappers objects
604                     // for primitive types.
605                     Object fieldValue = this.getValue(field);
606                     this.append(fieldName, fieldValue);
607                 } catch (IllegalAccessException ex) {
608                     //this can't happen. Would get a Security exception
609                     // instead
610                     //throw a runtime exception in case the impossible
611                     // happens.
612                     throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
613                 }
614             }
615         }
616     }
617 
618     /**
619      * @return Returns the excludeFieldNames.
620      */
621     public String[] getExcludeFieldNames() {
622         return this.excludeFieldNames;
623     }
624 
625     /**
626      * <p>
627      * Gets the last super class to stop appending fields for.
628      * </p>
629      * 
630      * @return The last super class to stop appending fields for.
631      */
632     public Class getUpToClass() {
633         return this.upToClass;
634     }
635 
636     /**
637      * <p>
638      * Calls <code>java.lang.reflect.Field.get(Object)</code>.
639      * </p>
640      * 
641      * @param field
642      *            The Field to query.
643      * @return The Object from the given Field.
644      * 
645      * @throws IllegalArgumentException
646      *             see {@link java.lang.reflect.Field#get(Object)}
647      * @throws IllegalAccessException
648      *             see {@link java.lang.reflect.Field#get(Object)}
649      * 
650      * @see java.lang.reflect.Field#get(Object)
651      */
652     protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException {
653         return field.get(this.getObject());
654     }
655 
656     /**
657      * <p>
658      * Gets whether or not to append static fields.
659      * </p>
660      * 
661      * @return Whether or not to append static fields.
662      * @since 2.1
663      */
664     public boolean isAppendStatics() {
665         return this.appendStatics;
666     }
667 
668     /**
669      * <p>
670      * Gets whether or not to append transient fields.
671      * </p>
672      * 
673      * @return Whether or not to append transient fields.
674      */
675     public boolean isAppendTransients() {
676         return this.appendTransients;
677     }
678 
679     /**
680      * <p>
681      * Append to the <code>toString</code> an <code>Object</code> array.
682      * </p>
683      * 
684      * @param array
685      *            the array to add to the <code>toString</code>
686      * @return this
687      */
688     public ToStringBuilder reflectionAppendArray(Object array) {
689         this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
690         return this;
691     }
692 
693     /**
694      * <p>
695      * Sets whether or not to append static fields.
696      * </p>
697      * 
698      * @param appendStatics
699      *            Whether or not to append static fields.
700      * @since 2.1
701      */
702     public void setAppendStatics(boolean appendStatics) {
703         this.appendStatics = appendStatics;
704     }
705 
706     /**
707      * <p>
708      * Sets whether or not to append transient fields.
709      * </p>
710      * 
711      * @param appendTransients
712      *            Whether or not to append transient fields.
713      */
714     public void setAppendTransients(boolean appendTransients) {
715         this.appendTransients = appendTransients;
716     }
717 
718     /**
719      * Sets the field names to exclude.
720      * 
721      * @param excludeFieldNamesParam
722      *            The excludeFieldNames to excluding from toString or <code>null</code>.
723      * @return <code>this</code>
724      */
725     public ReflectionToStringBuilder setExcludeFieldNames(String[] excludeFieldNamesParam) {
726         if (excludeFieldNamesParam == null) {
727             this.excludeFieldNames = null;
728         } else {
729             this.excludeFieldNames = toNoNullStringArray(excludeFieldNamesParam);
730             Arrays.sort(this.excludeFieldNames);
731         }
732         return this;
733     }
734 
735     /**
736      * <p>
737      * Sets the last super class to stop appending fields for.
738      * </p>
739      * 
740      * @param clazz
741      *            The last super class to stop appending fields for.
742      */
743     public void setUpToClass(Class clazz) {
744         this.upToClass = clazz;
745     }
746 
747     /**
748      * <p>
749      * Gets the String built by this builder.
750      * </p>
751      * 
752      * @return the built string
753      */
754     public String toString() {
755         if (this.getObject() == null) {
756             return this.getStyle().getNullText();
757         }
758         Class clazz = this.getObject().getClass();
759         this.appendFieldsIn(clazz);
760         while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
761             clazz = clazz.getSuperclass();
762             this.appendFieldsIn(clazz);
763         }
764         return super.toString();
765     }
766 
767 }