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