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