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