001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.lang3.builder;
019
020import java.lang.reflect.AccessibleObject;
021import java.lang.reflect.Field;
022import java.lang.reflect.Modifier;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Collection;
026import java.util.List;
027
028import org.apache.commons.lang3.ArrayUtils;
029import org.apache.commons.lang3.ClassUtils;
030
031/**
032 * <p>
033 * Assists in implementing {@link Object#toString()} methods using reflection.
034 * </p>
035 * <p>
036 * This class uses reflection to determine the fields to append. Because these fields are usually private, the class
037 * uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to
038 * change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are
039 * set up correctly.
040 * </p>
041 * <p>
042 * Using reflection to access (private) fields circumvents any synchronization protection guarding access to these
043 * fields. If a toString method cannot safely read a field, you should exclude it from the toString method, or use
044 * synchronization consistent with the class' lock management around the invocation of the method. Take special care to
045 * exclude non-thread-safe collection classes, because these classes may throw ConcurrentModificationException if
046 * modified while the toString method is executing.
047 * </p>
048 * <p>
049 * A typical invocation for this method would look like:
050 * </p>
051 * <pre>
052 * public String toString() {
053 *     return ReflectionToStringBuilder.toString(this);
054 * }
055 * </pre>
056 * <p>
057 * You can also use the builder to debug 3rd party objects:
058 * </p>
059 * <pre>
060 * System.out.println(&quot;An object: &quot; + ReflectionToStringBuilder.toString(anObject));
061 * </pre>
062 * <p>
063 * A subclass can control field output by overriding the methods:
064 * <ul>
065 * <li>{@link #accept(java.lang.reflect.Field)}</li>
066 * <li>{@link #getValue(java.lang.reflect.Field)}</li>
067 * </ul>
068 * </p>
069 * <p>
070 * For example, this method does <i>not</i> include the <code>password</code> field in the returned <code>String</code>:
071 * </p>
072 * <pre>
073 * public String toString() {
074 *     return (new ReflectionToStringBuilder(this) {
075 *         protected boolean accept(Field f) {
076 *             return super.accept(f) &amp;&amp; !f.getName().equals(&quot;password&quot;);
077 *         }
078 *     }).toString();
079 * }
080 * </pre>
081 * <p>
082 * The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the constructor.
083 * </p>
084 * 
085 * @since 2.0
086 * @version $Id: ReflectionToStringBuilder.java 1448370 2013-02-20 19:54:19Z tn $
087 */
088public class ReflectionToStringBuilder extends ToStringBuilder {
089
090    /**
091     * <p>
092     * Builds a <code>toString</code> value using the default <code>ToStringStyle</code> through reflection.
093     * </p>
094     *
095     * <p>
096     * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
097     * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
098     * also not as efficient as testing explicitly.
099     * </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}