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