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