    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    import java.util.ArrayList;
    024    import java.util.Arrays;
    025    import java.util.Collection;
    026    import java.util.List;
    027
    028    import org.apache.commons.lang3.ArrayUtils;
    029    import 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 1200177 2011-11-10 06:14:33Z ggregory $
    087     */
    088    public 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(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(Object object, 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(Object object, ToStringStyle style, 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(Object object, ToStringStyle style, boolean outputTransients, 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                T object, ToStringStyle style, boolean outputTransients,
    281                boolean outputStatics, 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(Object object, 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(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(Object[] array) {
    325            List<String> list = new ArrayList<String>(array.length);
    326            for (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(Object object, 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(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(Object object, 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(Object object, ToStringStyle style, 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                T object, ToStringStyle style, StringBuffer buffer,
    455                Class<? super T> reflectUpToClass, boolean outputTransients, 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 appened.</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(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(Class<?> clazz) {
    509            if (clazz.isArray()) {
    510                this.reflectionAppendArray(this.getObject());
    511                return;
    512            }
    513            Field[] fields = clazz.getDeclaredFields();
    514            AccessibleObject.setAccessible(fields, true);
    515            for (Field field : fields) {
    516                String fieldName = field.getName();
    517                if (this.accept(field)) {
    518                    try {
    519                        // Warning: Field.get(Object) creates wrappers objects
    520                        // for primitive types.
    521                        Object fieldValue = this.getValue(field);
    522                        this.append(fieldName, fieldValue);
    523                    } catch (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(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(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(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(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(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(Class<?> clazz) {
    661            if (clazz != null) {
    662                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    }