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 * <p>
086 * <b>Note:</b> the default {@link ToStringStyle} will only do a "shallow" formatting, i.e. composed objects are not
087 * further traversed. To get "deep" formatting, use an instance of {@link RecursiveToStringStyle}.
088 * </p>
089 *
090 * @since 2.0
091 * @version $Id: ReflectionToStringBuilder.java 1569028 2014-02-17 15:45:17Z kinow $
092 */
093public class ReflectionToStringBuilder extends ToStringBuilder {
094
095    /**
096     * <p>
097     * Builds a <code>toString</code> value using the default <code>ToStringStyle</code> through reflection.
098     * </p>
099     *
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}