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