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