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