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    }