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