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 "toString()'ed", 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 }