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