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