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 * @since 2.0 086 * @version $Id: ReflectionToStringBuilder.java 1448370 2013-02-20 19:54:19Z tn $ 087 */ 088public 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(final 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(final Object object, final 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(final Object object, final ToStringStyle style, final 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(final Object object, final ToStringStyle style, final boolean outputTransients, final 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 final T object, final ToStringStyle style, final boolean outputTransients, 281 final boolean outputStatics, final 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(final Object object, final 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(final 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(final Object[] array) { 325 final List<String> list = new ArrayList<String>(array.length); 326 for (final 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(final Object object, final 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(final 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(final Object object, final 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(final Object object, final ToStringStyle style, final 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 final T object, final ToStringStyle style, final StringBuffer buffer, 455 final Class<? super T> reflectUpToClass, final boolean outputTransients, final 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 appended.</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(final 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(final Class<?> clazz) { 509 if (clazz.isArray()) { 510 this.reflectionAppendArray(this.getObject()); 511 return; 512 } 513 final Field[] fields = clazz.getDeclaredFields(); 514 AccessibleObject.setAccessible(fields, true); 515 for (final Field field : fields) { 516 final String fieldName = field.getName(); 517 if (this.accept(field)) { 518 try { 519 // Warning: Field.get(Object) creates wrappers objects 520 // for primitive types. 521 final Object fieldValue = this.getValue(field); 522 this.append(fieldName, fieldValue); 523 } catch (final 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(final 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(final 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(final 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(final 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(final 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(final Class<?> clazz) { 661 if (clazz != null) { 662 final 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}