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    }