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