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