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 * </p>
065 * <ul>
066 * <li>{@link #accept(java.lang.reflect.Field)}</li>
067 * <li>{@link #getValue(java.lang.reflect.Field)}</li>
068 * </ul>
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 * Alternatively the {@link ToStringExclude} annotation can be used to exclude fields from being incorporated in the 
083 * result.
084 * </p>
085 * <p>
086 * The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the constructor.
087 * </p>
088 *
089 * <p>
090 * <b>Note:</b> the default {@link ToStringStyle} will only do a "shallow" formatting, i.e. composed objects are not
091 * further traversed. To get "deep" formatting, use an instance of {@link RecursiveToStringStyle}.
092 * </p>
093 *
094 * @since 2.0
095 */
096public class ReflectionToStringBuilder extends ToStringBuilder {
097
098    /**
099     * <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}