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 */ 017package org.apache.commons.lang3.builder; 018 019import java.io.Serializable; 020import java.lang.reflect.Array; 021import java.util.Collection; 022import java.util.Map; 023import java.util.Map.Entry; 024import java.util.Objects; 025import java.util.WeakHashMap; 026 027import org.apache.commons.lang3.ClassUtils; 028import org.apache.commons.lang3.ObjectUtils; 029import org.apache.commons.lang3.StringEscapeUtils; 030import org.apache.commons.lang3.StringUtils; 031 032/** 033 * <p>Controls {@code String} formatting for {@link ToStringBuilder}. 034 * The main public interface is always via {@code ToStringBuilder}.</p> 035 * 036 * <p>These classes are intended to be used as {@code Singletons}. 037 * There is no need to instantiate a new style each time. A program 038 * will generally use one of the predefined constants on this class. 039 * Alternatively, the {@link StandardToStringStyle} class can be used 040 * to set the individual settings. Thus most styles can be achieved 041 * without subclassing.</p> 042 * 043 * <p>If required, a subclass can override as many or as few of the 044 * methods as it requires. Each object type (from {@code boolean} 045 * to {@code long} to {@code Object} to {@code int[]}) has 046 * its own methods to output it. Most have two versions, detail and summary. 047 * 048 * <p>For example, the detail version of the array based methods will 049 * output the whole array, whereas the summary method will just output 050 * the array length.</p> 051 * 052 * <p>If you want to format the output of certain objects, such as dates, you 053 * must create a subclass and override a method. 054 * </p> 055 * <pre> 056 * public class MyStyle extends ToStringStyle { 057 * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { 058 * if (value instanceof Date) { 059 * value = new SimpleDateFormat("yyyy-MM-dd").format(value); 060 * } 061 * buffer.append(value); 062 * } 063 * } 064 * </pre> 065 * 066 * @since 1.0 067 */ 068@SuppressWarnings("deprecation") // StringEscapeUtils 069public abstract class ToStringStyle implements Serializable { 070 071 /** 072 * Serialization version ID. 073 */ 074 private static final long serialVersionUID = -2587890625525655916L; 075 076 /** 077 * The default toString style. Using the {@code Person} 078 * example from {@link ToStringBuilder}, the output would look like this: 079 * 080 * <pre> 081 * Person@182f0db[name=John Doe,age=33,smoker=false] 082 * </pre> 083 */ 084 public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle(); 085 086 /** 087 * The multi line toString style. Using the {@code Person} 088 * example from {@link ToStringBuilder}, the output would look like this: 089 * 090 * <pre> 091 * Person@182f0db[ 092 * name=John Doe 093 * age=33 094 * smoker=false 095 * ] 096 * </pre> 097 */ 098 public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle(); 099 100 /** 101 * The no field names toString style. Using the 102 * {@code Person} example from {@link ToStringBuilder}, the output 103 * would look like this: 104 * 105 * <pre> 106 * Person@182f0db[John Doe,33,false] 107 * </pre> 108 */ 109 public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle(); 110 111 /** 112 * The short prefix toString style. Using the {@code Person} example 113 * from {@link ToStringBuilder}, the output would look like this: 114 * 115 * <pre> 116 * Person[name=John Doe,age=33,smoker=false] 117 * </pre> 118 * 119 * @since 2.1 120 */ 121 public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle(); 122 123 /** 124 * The simple toString style. Using the {@code Person} 125 * example from {@link ToStringBuilder}, the output would look like this: 126 * 127 * <pre> 128 * John Doe,33,false 129 * </pre> 130 */ 131 public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle(); 132 133 /** 134 * The no class name toString style. Using the {@code Person} 135 * example from {@link ToStringBuilder}, the output would look like this: 136 * 137 * <pre> 138 * [name=John Doe,age=33,smoker=false] 139 * </pre> 140 * 141 * @since 3.4 142 */ 143 public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle(); 144 145 /** 146 * The JSON toString style. Using the {@code Person} example from 147 * {@link ToStringBuilder}, the output would look like this: 148 * 149 * <pre> 150 * {"name": "John Doe", "age": 33, "smoker": true} 151 * </pre> 152 * 153 * <strong>Note:</strong> Since field names are mandatory in JSON, this 154 * ToStringStyle will throw an {@link UnsupportedOperationException} if no 155 * field name is passed in while appending. Furthermore This ToStringStyle 156 * will only generate valid JSON if referenced objects also produce JSON 157 * when calling {@code toString()} on them. 158 * 159 * @since 3.4 160 * @see <a href="http://json.org">json.org</a> 161 */ 162 public static final ToStringStyle JSON_STYLE = new JsonToStringStyle(); 163 164 /** 165 * <p> 166 * A registry of objects used by {@code reflectionToString} methods 167 * to detect cyclical object references and avoid infinite loops. 168 * </p> 169 */ 170 private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY = 171 new ThreadLocal<>(); 172 /* 173 * Note that objects of this class are generally shared between threads, so 174 * an instance variable would not be suitable here. 175 * 176 * In normal use the registry should always be left empty, because the caller 177 * should call toString() which will clean up. 178 * 179 * See LANG-792 180 */ 181 182 /** 183 * <p> 184 * Returns the registry of objects being traversed by the {@code reflectionToString} 185 * methods in the current thread. 186 * </p> 187 * 188 * @return Set the registry of objects being traversed 189 */ 190 static Map<Object, Object> getRegistry() { 191 return REGISTRY.get(); 192 } 193 194 /** 195 * <p> 196 * Returns {@code true} if the registry contains the given object. 197 * Used by the reflection methods to avoid infinite loops. 198 * </p> 199 * 200 * @param value 201 * The object to lookup in the registry. 202 * @return boolean {@code true} if the registry contains the given 203 * object. 204 */ 205 static boolean isRegistered(final Object value) { 206 final Map<Object, Object> m = getRegistry(); 207 return m != null && m.containsKey(value); 208 } 209 210 /** 211 * <p> 212 * Registers the given object. Used by the reflection methods to avoid 213 * infinite loops. 214 * </p> 215 * 216 * @param value 217 * The object to register. 218 */ 219 static void register(final Object value) { 220 if (value != null) { 221 final Map<Object, Object> m = getRegistry(); 222 if (m == null) { 223 REGISTRY.set(new WeakHashMap<>()); 224 } 225 getRegistry().put(value, null); 226 } 227 } 228 229 /** 230 * <p> 231 * Unregisters the given object. 232 * </p> 233 * 234 * <p> 235 * Used by the reflection methods to avoid infinite loops. 236 * </p> 237 * 238 * @param value 239 * The object to unregister. 240 */ 241 static void unregister(final Object value) { 242 if (value != null) { 243 final Map<Object, Object> m = getRegistry(); 244 if (m != null) { 245 m.remove(value); 246 if (m.isEmpty()) { 247 REGISTRY.remove(); 248 } 249 } 250 } 251 } 252 253 /** 254 * Whether to use the field names, the default is {@code true}. 255 */ 256 private boolean useFieldNames = true; 257 258 /** 259 * Whether to use the class name, the default is {@code true}. 260 */ 261 private boolean useClassName = true; 262 263 /** 264 * Whether to use short class names, the default is {@code false}. 265 */ 266 private boolean useShortClassName = false; 267 268 /** 269 * Whether to use the identity hash code, the default is {@code true}. 270 */ 271 private boolean useIdentityHashCode = true; 272 273 /** 274 * The content start {@code '['}. 275 */ 276 private String contentStart = "["; 277 278 /** 279 * The content end {@code ']'}. 280 */ 281 private String contentEnd = "]"; 282 283 /** 284 * The field name value separator {@code '='}. 285 */ 286 private String fieldNameValueSeparator = "="; 287 288 /** 289 * Whether the field separator should be added before any other fields. 290 */ 291 private boolean fieldSeparatorAtStart = false; 292 293 /** 294 * Whether the field separator should be added after any other fields. 295 */ 296 private boolean fieldSeparatorAtEnd = false; 297 298 /** 299 * The field separator {@code ','}. 300 */ 301 private String fieldSeparator = ","; 302 303 /** 304 * The array start <code>'{'</code>. 305 */ 306 private String arrayStart = "{"; 307 308 /** 309 * The array separator {@code ','}. 310 */ 311 private String arraySeparator = ","; 312 313 /** 314 * The detail for array content. 315 */ 316 private boolean arrayContentDetail = true; 317 318 /** 319 * The array end {@code '}'}. 320 */ 321 private String arrayEnd = "}"; 322 323 /** 324 * The value to use when fullDetail is {@code null}, 325 * the default value is {@code true}. 326 */ 327 private boolean defaultFullDetail = true; 328 329 /** 330 * The {@code null} text {@code '<null>'}. 331 */ 332 private String nullText = "<null>"; 333 334 /** 335 * The summary size text start {@code '<size'}. 336 */ 337 private String sizeStartText = "<size="; 338 339 /** 340 * The summary size text start {@code '>'}. 341 */ 342 private String sizeEndText = ">"; 343 344 /** 345 * The summary object text start {@code '<'}. 346 */ 347 private String summaryObjectStartText = "<"; 348 349 /** 350 * The summary object text start {@code '>'}. 351 */ 352 private String summaryObjectEndText = ">"; 353 354 //---------------------------------------------------------------------------- 355 356 /** 357 * <p>Constructor.</p> 358 */ 359 protected ToStringStyle() { 360 super(); 361 } 362 363 //---------------------------------------------------------------------------- 364 365 /** 366 * <p>Append to the {@code toString} the superclass toString.</p> 367 * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p> 368 * 369 * <p>A {@code null} {@code superToString} is ignored.</p> 370 * 371 * @param buffer the {@code StringBuffer} to populate 372 * @param superToString the {@code super.toString()} 373 * @since 2.0 374 */ 375 public void appendSuper(final StringBuffer buffer, final String superToString) { 376 appendToString(buffer, superToString); 377 } 378 379 /** 380 * <p>Append to the {@code toString} another toString.</p> 381 * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p> 382 * 383 * <p>A {@code null} {@code toString} is ignored.</p> 384 * 385 * @param buffer the {@code StringBuffer} to populate 386 * @param toString the additional {@code toString} 387 * @since 2.0 388 */ 389 public void appendToString(final StringBuffer buffer, final String toString) { 390 if (toString != null) { 391 final int pos1 = toString.indexOf(contentStart) + contentStart.length(); 392 final int pos2 = toString.lastIndexOf(contentEnd); 393 if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) { 394 if (fieldSeparatorAtStart) { 395 removeLastFieldSeparator(buffer); 396 } 397 buffer.append(toString, pos1, pos2); 398 appendFieldSeparator(buffer); 399 } 400 } 401 } 402 403 /** 404 * <p>Append to the {@code toString} the start of data indicator.</p> 405 * 406 * @param buffer the {@code StringBuffer} to populate 407 * @param object the {@code Object} to build a {@code toString} for 408 */ 409 public void appendStart(final StringBuffer buffer, final Object object) { 410 if (object != null) { 411 appendClassName(buffer, object); 412 appendIdentityHashCode(buffer, object); 413 appendContentStart(buffer); 414 if (fieldSeparatorAtStart) { 415 appendFieldSeparator(buffer); 416 } 417 } 418 } 419 420 /** 421 * <p>Append to the {@code toString} the end of data indicator.</p> 422 * 423 * @param buffer the {@code StringBuffer} to populate 424 * @param object the {@code Object} to build a 425 * {@code toString} for. 426 */ 427 public void appendEnd(final StringBuffer buffer, final Object object) { 428 if (!this.fieldSeparatorAtEnd) { 429 removeLastFieldSeparator(buffer); 430 } 431 appendContentEnd(buffer); 432 unregister(object); 433 } 434 435 /** 436 * <p>Remove the last field separator from the buffer.</p> 437 * 438 * @param buffer the {@code StringBuffer} to populate 439 * @since 2.0 440 */ 441 protected void removeLastFieldSeparator(final StringBuffer buffer) { 442 if (StringUtils.endsWith(buffer, fieldSeparator)) { 443 buffer.setLength(buffer.length() - fieldSeparator.length()); 444 } 445 } 446 447 //---------------------------------------------------------------------------- 448 449 /** 450 * <p>Append to the {@code toString} an {@code Object} 451 * value, printing the full {@code toString} of the 452 * {@code Object} passed in.</p> 453 * 454 * @param buffer the {@code StringBuffer} to populate 455 * @param fieldName the field name 456 * @param value the value to add to the {@code toString} 457 * @param fullDetail {@code true} for detail, {@code false} 458 * for summary info, {@code null} for style decides 459 */ 460 public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) { 461 appendFieldStart(buffer, fieldName); 462 463 if (value == null) { 464 appendNullText(buffer, fieldName); 465 466 } else { 467 appendInternal(buffer, fieldName, value, isFullDetail(fullDetail)); 468 } 469 470 appendFieldEnd(buffer, fieldName); 471 } 472 473 /** 474 * <p>Append to the {@code toString} an {@code Object}, 475 * correctly interpreting its type.</p> 476 * 477 * <p>This method performs the main lookup by Class type to correctly 478 * route arrays, {@code Collections}, {@code Maps} and 479 * {@code Objects} to the appropriate method.</p> 480 * 481 * <p>Either detail or summary views can be specified.</p> 482 * 483 * <p>If a cycle is detected, an object will be appended with the 484 * {@code Object.toString()} format.</p> 485 * 486 * @param buffer the {@code StringBuffer} to populate 487 * @param fieldName the field name, typically not used as already appended 488 * @param value the value to add to the {@code toString}, 489 * not {@code null} 490 * @param detail output detail or not 491 */ 492 protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) { 493 if (isRegistered(value) 494 && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) { 495 appendCyclicObject(buffer, fieldName, value); 496 return; 497 } 498 499 register(value); 500 501 try { 502 if (value instanceof Collection<?>) { 503 if (detail) { 504 appendDetail(buffer, fieldName, (Collection<?>) value); 505 } else { 506 appendSummarySize(buffer, fieldName, ((Collection<?>) value).size()); 507 } 508 509 } else if (value instanceof Map<?, ?>) { 510 if (detail) { 511 appendDetail(buffer, fieldName, (Map<?, ?>) value); 512 } else { 513 appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size()); 514 } 515 516 } else if (value instanceof long[]) { 517 if (detail) { 518 appendDetail(buffer, fieldName, (long[]) value); 519 } else { 520 appendSummary(buffer, fieldName, (long[]) value); 521 } 522 523 } else if (value instanceof int[]) { 524 if (detail) { 525 appendDetail(buffer, fieldName, (int[]) value); 526 } else { 527 appendSummary(buffer, fieldName, (int[]) value); 528 } 529 530 } else if (value instanceof short[]) { 531 if (detail) { 532 appendDetail(buffer, fieldName, (short[]) value); 533 } else { 534 appendSummary(buffer, fieldName, (short[]) value); 535 } 536 537 } else if (value instanceof byte[]) { 538 if (detail) { 539 appendDetail(buffer, fieldName, (byte[]) value); 540 } else { 541 appendSummary(buffer, fieldName, (byte[]) value); 542 } 543 544 } else if (value instanceof char[]) { 545 if (detail) { 546 appendDetail(buffer, fieldName, (char[]) value); 547 } else { 548 appendSummary(buffer, fieldName, (char[]) value); 549 } 550 551 } else if (value instanceof double[]) { 552 if (detail) { 553 appendDetail(buffer, fieldName, (double[]) value); 554 } else { 555 appendSummary(buffer, fieldName, (double[]) value); 556 } 557 558 } else if (value instanceof float[]) { 559 if (detail) { 560 appendDetail(buffer, fieldName, (float[]) value); 561 } else { 562 appendSummary(buffer, fieldName, (float[]) value); 563 } 564 565 } else if (value instanceof boolean[]) { 566 if (detail) { 567 appendDetail(buffer, fieldName, (boolean[]) value); 568 } else { 569 appendSummary(buffer, fieldName, (boolean[]) value); 570 } 571 572 } else if (value.getClass().isArray()) { 573 if (detail) { 574 appendDetail(buffer, fieldName, (Object[]) value); 575 } else { 576 appendSummary(buffer, fieldName, (Object[]) value); 577 } 578 579 } else { 580 if (detail) { 581 appendDetail(buffer, fieldName, value); 582 } else { 583 appendSummary(buffer, fieldName, value); 584 } 585 } 586 } finally { 587 unregister(value); 588 } 589 } 590 591 /** 592 * <p>Append to the {@code toString} an {@code Object} 593 * value that has been detected to participate in a cycle. This 594 * implementation will print the standard string value of the value.</p> 595 * 596 * @param buffer the {@code StringBuffer} to populate 597 * @param fieldName the field name, typically not used as already appended 598 * @param value the value to add to the {@code toString}, 599 * not {@code null} 600 * 601 * @since 2.2 602 */ 603 protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) { 604 ObjectUtils.identityToString(buffer, value); 605 } 606 607 /** 608 * <p>Append to the {@code toString} an {@code Object} 609 * value, printing the full detail of the {@code Object}.</p> 610 * 611 * @param buffer the {@code StringBuffer} to populate 612 * @param fieldName the field name, typically not used as already appended 613 * @param value the value to add to the {@code toString}, 614 * not {@code null} 615 */ 616 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) { 617 buffer.append(value); 618 } 619 620 /** 621 * <p>Append to the {@code toString} a {@code Collection}.</p> 622 * 623 * @param buffer the {@code StringBuffer} to populate 624 * @param fieldName the field name, typically not used as already appended 625 * @param coll the {@code Collection} to add to the 626 * {@code toString}, not {@code null} 627 */ 628 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) { 629 if (coll != null && !coll.isEmpty()) { 630 buffer.append(arrayStart); 631 int i = 0; 632 for (final Object item : coll) { 633 appendDetail(buffer, fieldName, i++, item); 634 } 635 buffer.append(arrayEnd); 636 return; 637 } 638 639 buffer.append(coll); 640 } 641 642 /** 643 * <p>Append to the {@code toString} a {@code Map}.</p> 644 * 645 * @param buffer the {@code StringBuffer} to populate 646 * @param fieldName the field name, typically not used as already appended 647 * @param map the {@code Map} to add to the {@code toString}, 648 * not {@code null} 649 */ 650 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) { 651 buffer.append(map); 652 } 653 654 /** 655 * <p>Append to the {@code toString} an {@code Object} 656 * value, printing a summary of the {@code Object}.</P> 657 * 658 * @param buffer the {@code StringBuffer} to populate 659 * @param fieldName the field name, typically not used as already appended 660 * @param value the value to add to the {@code toString}, 661 * not {@code null} 662 */ 663 protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) { 664 buffer.append(summaryObjectStartText); 665 buffer.append(getShortClassName(value.getClass())); 666 buffer.append(summaryObjectEndText); 667 } 668 669 //---------------------------------------------------------------------------- 670 671 /** 672 * <p>Append to the {@code toString} a {@code long} 673 * value.</p> 674 * 675 * @param buffer the {@code StringBuffer} to populate 676 * @param fieldName the field name 677 * @param value the value to add to the {@code toString} 678 */ 679 public void append(final StringBuffer buffer, final String fieldName, final long value) { 680 appendFieldStart(buffer, fieldName); 681 appendDetail(buffer, fieldName, value); 682 appendFieldEnd(buffer, fieldName); 683 } 684 685 /** 686 * <p>Append to the {@code toString} a {@code long} 687 * value.</p> 688 * 689 * @param buffer the {@code StringBuffer} to populate 690 * @param fieldName the field name, typically not used as already appended 691 * @param value the value to add to the {@code toString} 692 */ 693 protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) { 694 buffer.append(value); 695 } 696 697 //---------------------------------------------------------------------------- 698 699 /** 700 * <p>Append to the {@code toString} an {@code int} 701 * value.</p> 702 * 703 * @param buffer the {@code StringBuffer} to populate 704 * @param fieldName the field name 705 * @param value the value to add to the {@code toString} 706 */ 707 public void append(final StringBuffer buffer, final String fieldName, final int value) { 708 appendFieldStart(buffer, fieldName); 709 appendDetail(buffer, fieldName, value); 710 appendFieldEnd(buffer, fieldName); 711 } 712 713 /** 714 * <p>Append to the {@code toString} an {@code int} 715 * value.</p> 716 * 717 * @param buffer the {@code StringBuffer} to populate 718 * @param fieldName the field name, typically not used as already appended 719 * @param value the value to add to the {@code toString} 720 */ 721 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) { 722 buffer.append(value); 723 } 724 725 //---------------------------------------------------------------------------- 726 727 /** 728 * <p>Append to the {@code toString} a {@code short} 729 * value.</p> 730 * 731 * @param buffer the {@code StringBuffer} to populate 732 * @param fieldName the field name 733 * @param value the value to add to the {@code toString} 734 */ 735 public void append(final StringBuffer buffer, final String fieldName, final short value) { 736 appendFieldStart(buffer, fieldName); 737 appendDetail(buffer, fieldName, value); 738 appendFieldEnd(buffer, fieldName); 739 } 740 741 /** 742 * <p>Append to the {@code toString} a {@code short} 743 * value.</p> 744 * 745 * @param buffer the {@code StringBuffer} to populate 746 * @param fieldName the field name, typically not used as already appended 747 * @param value the value to add to the {@code toString} 748 */ 749 protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) { 750 buffer.append(value); 751 } 752 753 //---------------------------------------------------------------------------- 754 755 /** 756 * <p>Append to the {@code toString} a {@code byte} 757 * value.</p> 758 * 759 * @param buffer the {@code StringBuffer} to populate 760 * @param fieldName the field name 761 * @param value the value to add to the {@code toString} 762 */ 763 public void append(final StringBuffer buffer, final String fieldName, final byte value) { 764 appendFieldStart(buffer, fieldName); 765 appendDetail(buffer, fieldName, value); 766 appendFieldEnd(buffer, fieldName); 767 } 768 769 /** 770 * <p>Append to the {@code toString} a {@code byte} 771 * value.</p> 772 * 773 * @param buffer the {@code StringBuffer} to populate 774 * @param fieldName the field name, typically not used as already appended 775 * @param value the value to add to the {@code toString} 776 */ 777 protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) { 778 buffer.append(value); 779 } 780 781 //---------------------------------------------------------------------------- 782 783 /** 784 * <p>Append to the {@code toString} a {@code char} 785 * value.</p> 786 * 787 * @param buffer the {@code StringBuffer} to populate 788 * @param fieldName the field name 789 * @param value the value to add to the {@code toString} 790 */ 791 public void append(final StringBuffer buffer, final String fieldName, final char value) { 792 appendFieldStart(buffer, fieldName); 793 appendDetail(buffer, fieldName, value); 794 appendFieldEnd(buffer, fieldName); 795 } 796 797 /** 798 * <p>Append to the {@code toString} a {@code char} 799 * value.</p> 800 * 801 * @param buffer the {@code StringBuffer} to populate 802 * @param fieldName the field name, typically not used as already appended 803 * @param value the value to add to the {@code toString} 804 */ 805 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) { 806 buffer.append(value); 807 } 808 809 //---------------------------------------------------------------------------- 810 811 /** 812 * <p>Append to the {@code toString} a {@code double} 813 * value.</p> 814 * 815 * @param buffer the {@code StringBuffer} to populate 816 * @param fieldName the field name 817 * @param value the value to add to the {@code toString} 818 */ 819 public void append(final StringBuffer buffer, final String fieldName, final double value) { 820 appendFieldStart(buffer, fieldName); 821 appendDetail(buffer, fieldName, value); 822 appendFieldEnd(buffer, fieldName); 823 } 824 825 /** 826 * <p>Append to the {@code toString} a {@code double} 827 * value.</p> 828 * 829 * @param buffer the {@code StringBuffer} to populate 830 * @param fieldName the field name, typically not used as already appended 831 * @param value the value to add to the {@code toString} 832 */ 833 protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) { 834 buffer.append(value); 835 } 836 837 //---------------------------------------------------------------------------- 838 839 /** 840 * <p>Append to the {@code toString} a {@code float} 841 * value.</p> 842 * 843 * @param buffer the {@code StringBuffer} to populate 844 * @param fieldName the field name 845 * @param value the value to add to the {@code toString} 846 */ 847 public void append(final StringBuffer buffer, final String fieldName, final float value) { 848 appendFieldStart(buffer, fieldName); 849 appendDetail(buffer, fieldName, value); 850 appendFieldEnd(buffer, fieldName); 851 } 852 853 /** 854 * <p>Append to the {@code toString} a {@code float} 855 * value.</p> 856 * 857 * @param buffer the {@code StringBuffer} to populate 858 * @param fieldName the field name, typically not used as already appended 859 * @param value the value to add to the {@code toString} 860 */ 861 protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) { 862 buffer.append(value); 863 } 864 865 //---------------------------------------------------------------------------- 866 867 /** 868 * <p>Append to the {@code toString} a {@code boolean} 869 * value.</p> 870 * 871 * @param buffer the {@code StringBuffer} to populate 872 * @param fieldName the field name 873 * @param value the value to add to the {@code toString} 874 */ 875 public void append(final StringBuffer buffer, final String fieldName, final boolean value) { 876 appendFieldStart(buffer, fieldName); 877 appendDetail(buffer, fieldName, value); 878 appendFieldEnd(buffer, fieldName); 879 } 880 881 /** 882 * <p>Append to the {@code toString} a {@code boolean} 883 * value.</p> 884 * 885 * @param buffer the {@code StringBuffer} to populate 886 * @param fieldName the field name, typically not used as already appended 887 * @param value the value to add to the {@code toString} 888 */ 889 protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) { 890 buffer.append(value); 891 } 892 893 /** 894 * <p>Append to the {@code toString} an {@code Object} 895 * array.</p> 896 * 897 * @param buffer the {@code StringBuffer} to populate 898 * @param fieldName the field name 899 * @param array the array to add to the toString 900 * @param fullDetail {@code true} for detail, {@code false} 901 * for summary info, {@code null} for style decides 902 */ 903 public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) { 904 appendFieldStart(buffer, fieldName); 905 906 if (array == null) { 907 appendNullText(buffer, fieldName); 908 909 } else if (isFullDetail(fullDetail)) { 910 appendDetail(buffer, fieldName, array); 911 912 } else { 913 appendSummary(buffer, fieldName, array); 914 } 915 916 appendFieldEnd(buffer, fieldName); 917 } 918 919 //---------------------------------------------------------------------------- 920 921 /** 922 * <p>Append to the {@code toString} the detail of an 923 * {@code Object} array.</p> 924 * 925 * @param buffer the {@code StringBuffer} to populate 926 * @param fieldName the field name, typically not used as already appended 927 * @param array the array to add to the {@code toString}, 928 * not {@code null} 929 */ 930 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) { 931 buffer.append(arrayStart); 932 for (int i = 0; i < array.length; i++) { 933 final Object item = array[i]; 934 appendDetail(buffer, fieldName, i, item); 935 } 936 buffer.append(arrayEnd); 937 } 938 939 /** 940 * <p>Append to the {@code toString} the detail of an 941 * {@code Object} array item.</p> 942 * 943 * @param buffer the {@code StringBuffer} to populate 944 * @param fieldName the field name, typically not used as already appended 945 * @param i the array item index to add 946 * @param item the array item to add 947 * @since 3.11 948 */ 949 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int i, final Object item) { 950 if (i > 0) { 951 buffer.append(arraySeparator); 952 } 953 if (item == null) { 954 appendNullText(buffer, fieldName); 955 } else { 956 appendInternal(buffer, fieldName, item, arrayContentDetail); 957 } 958 } 959 960 /** 961 * <p>Append to the {@code toString} the detail of an array type.</p> 962 * 963 * @param buffer the {@code StringBuffer} to populate 964 * @param fieldName the field name, typically not used as already appended 965 * @param array the array to add to the {@code toString}, 966 * not {@code null} 967 * @since 2.0 968 */ 969 protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) { 970 buffer.append(arrayStart); 971 final int length = Array.getLength(array); 972 for (int i = 0; i < length; i++) { 973 final Object item = Array.get(array, i); 974 appendDetail(buffer, fieldName, i, item); 975 } 976 buffer.append(arrayEnd); 977 } 978 979 /** 980 * <p>Append to the {@code toString} a summary of an 981 * {@code Object} array.</p> 982 * 983 * @param buffer the {@code StringBuffer} to populate 984 * @param fieldName the field name, typically not used as already appended 985 * @param array the array to add to the {@code toString}, 986 * not {@code null} 987 */ 988 protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) { 989 appendSummarySize(buffer, fieldName, array.length); 990 } 991 992 //---------------------------------------------------------------------------- 993 994 /** 995 * <p>Append to the {@code toString} a {@code long} 996 * array.</p> 997 * 998 * @param buffer the {@code StringBuffer} to populate 999 * @param fieldName the field name 1000 * @param array the array to add to the {@code toString} 1001 * @param fullDetail {@code true} for detail, {@code false} 1002 * for summary info, {@code null} for style decides 1003 */ 1004 public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) { 1005 appendFieldStart(buffer, fieldName); 1006 1007 if (array == null) { 1008 appendNullText(buffer, fieldName); 1009 1010 } else if (isFullDetail(fullDetail)) { 1011 appendDetail(buffer, fieldName, array); 1012 1013 } else { 1014 appendSummary(buffer, fieldName, array); 1015 } 1016 1017 appendFieldEnd(buffer, fieldName); 1018 } 1019 1020 /** 1021 * <p>Append to the {@code toString} the detail of a 1022 * {@code long} array.</p> 1023 * 1024 * @param buffer the {@code StringBuffer} to populate 1025 * @param fieldName the field name, typically not used as already appended 1026 * @param array the array to add to the {@code toString}, 1027 * not {@code null} 1028 */ 1029 protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) { 1030 buffer.append(arrayStart); 1031 for (int i = 0; i < array.length; i++) { 1032 if (i > 0) { 1033 buffer.append(arraySeparator); 1034 } 1035 appendDetail(buffer, fieldName, array[i]); 1036 } 1037 buffer.append(arrayEnd); 1038 } 1039 1040 /** 1041 * <p>Append to the {@code toString} a summary of a 1042 * {@code long} array.</p> 1043 * 1044 * @param buffer the {@code StringBuffer} to populate 1045 * @param fieldName the field name, typically not used as already appended 1046 * @param array the array to add to the {@code toString}, 1047 * not {@code null} 1048 */ 1049 protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) { 1050 appendSummarySize(buffer, fieldName, array.length); 1051 } 1052 1053 //---------------------------------------------------------------------------- 1054 1055 /** 1056 * <p>Append to the {@code toString} an {@code int} 1057 * array.</p> 1058 * 1059 * @param buffer the {@code StringBuffer} to populate 1060 * @param fieldName the field name 1061 * @param array the array to add to the {@code toString} 1062 * @param fullDetail {@code true} for detail, {@code false} 1063 * for summary info, {@code null} for style decides 1064 */ 1065 public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) { 1066 appendFieldStart(buffer, fieldName); 1067 1068 if (array == null) { 1069 appendNullText(buffer, fieldName); 1070 1071 } else if (isFullDetail(fullDetail)) { 1072 appendDetail(buffer, fieldName, array); 1073 1074 } else { 1075 appendSummary(buffer, fieldName, array); 1076 } 1077 1078 appendFieldEnd(buffer, fieldName); 1079 } 1080 1081 /** 1082 * <p>Append to the {@code toString} the detail of an 1083 * {@code int} array.</p> 1084 * 1085 * @param buffer the {@code StringBuffer} to populate 1086 * @param fieldName the field name, typically not used as already appended 1087 * @param array the array to add to the {@code toString}, 1088 * not {@code null} 1089 */ 1090 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) { 1091 buffer.append(arrayStart); 1092 for (int i = 0; i < array.length; i++) { 1093 if (i > 0) { 1094 buffer.append(arraySeparator); 1095 } 1096 appendDetail(buffer, fieldName, array[i]); 1097 } 1098 buffer.append(arrayEnd); 1099 } 1100 1101 /** 1102 * <p>Append to the {@code toString} a summary of an 1103 * {@code int} array.</p> 1104 * 1105 * @param buffer the {@code StringBuffer} to populate 1106 * @param fieldName the field name, typically not used as already appended 1107 * @param array the array to add to the {@code toString}, 1108 * not {@code null} 1109 */ 1110 protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) { 1111 appendSummarySize(buffer, fieldName, array.length); 1112 } 1113 1114 //---------------------------------------------------------------------------- 1115 1116 /** 1117 * <p>Append to the {@code toString} a {@code short} 1118 * array.</p> 1119 * 1120 * @param buffer the {@code StringBuffer} to populate 1121 * @param fieldName the field name 1122 * @param array the array to add to the {@code toString} 1123 * @param fullDetail {@code true} for detail, {@code false} 1124 * for summary info, {@code null} for style decides 1125 */ 1126 public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) { 1127 appendFieldStart(buffer, fieldName); 1128 1129 if (array == null) { 1130 appendNullText(buffer, fieldName); 1131 1132 } else if (isFullDetail(fullDetail)) { 1133 appendDetail(buffer, fieldName, array); 1134 1135 } else { 1136 appendSummary(buffer, fieldName, array); 1137 } 1138 1139 appendFieldEnd(buffer, fieldName); 1140 } 1141 1142 /** 1143 * <p>Append to the {@code toString} the detail of a 1144 * {@code short} array.</p> 1145 * 1146 * @param buffer the {@code StringBuffer} to populate 1147 * @param fieldName the field name, typically not used as already appended 1148 * @param array the array to add to the {@code toString}, 1149 * not {@code null} 1150 */ 1151 protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) { 1152 buffer.append(arrayStart); 1153 for (int i = 0; i < array.length; i++) { 1154 if (i > 0) { 1155 buffer.append(arraySeparator); 1156 } 1157 appendDetail(buffer, fieldName, array[i]); 1158 } 1159 buffer.append(arrayEnd); 1160 } 1161 1162 /** 1163 * <p>Append to the {@code toString} a summary of a 1164 * {@code short} array.</p> 1165 * 1166 * @param buffer the {@code StringBuffer} to populate 1167 * @param fieldName the field name, typically not used as already appended 1168 * @param array the array to add to the {@code toString}, 1169 * not {@code null} 1170 */ 1171 protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) { 1172 appendSummarySize(buffer, fieldName, array.length); 1173 } 1174 1175 //---------------------------------------------------------------------------- 1176 1177 /** 1178 * <p>Append to the {@code toString} a {@code byte} 1179 * array.</p> 1180 * 1181 * @param buffer the {@code StringBuffer} to populate 1182 * @param fieldName the field name 1183 * @param array the array to add to the {@code toString} 1184 * @param fullDetail {@code true} for detail, {@code false} 1185 * for summary info, {@code null} for style decides 1186 */ 1187 public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) { 1188 appendFieldStart(buffer, fieldName); 1189 1190 if (array == null) { 1191 appendNullText(buffer, fieldName); 1192 1193 } else if (isFullDetail(fullDetail)) { 1194 appendDetail(buffer, fieldName, array); 1195 1196 } else { 1197 appendSummary(buffer, fieldName, array); 1198 } 1199 1200 appendFieldEnd(buffer, fieldName); 1201 } 1202 1203 /** 1204 * <p>Append to the {@code toString} the detail of a 1205 * {@code byte} array.</p> 1206 * 1207 * @param buffer the {@code StringBuffer} to populate 1208 * @param fieldName the field name, typically not used as already appended 1209 * @param array the array to add to the {@code toString}, 1210 * not {@code null} 1211 */ 1212 protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) { 1213 buffer.append(arrayStart); 1214 for (int i = 0; i < array.length; i++) { 1215 if (i > 0) { 1216 buffer.append(arraySeparator); 1217 } 1218 appendDetail(buffer, fieldName, array[i]); 1219 } 1220 buffer.append(arrayEnd); 1221 } 1222 1223 /** 1224 * <p>Append to the {@code toString} a summary of a 1225 * {@code byte} array.</p> 1226 * 1227 * @param buffer the {@code StringBuffer} to populate 1228 * @param fieldName the field name, typically not used as already appended 1229 * @param array the array to add to the {@code toString}, 1230 * not {@code null} 1231 */ 1232 protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) { 1233 appendSummarySize(buffer, fieldName, array.length); 1234 } 1235 1236 //---------------------------------------------------------------------------- 1237 1238 /** 1239 * <p>Append to the {@code toString} a {@code char} 1240 * array.</p> 1241 * 1242 * @param buffer the {@code StringBuffer} to populate 1243 * @param fieldName the field name 1244 * @param array the array to add to the {@code toString} 1245 * @param fullDetail {@code true} for detail, {@code false} 1246 * for summary info, {@code null} for style decides 1247 */ 1248 public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) { 1249 appendFieldStart(buffer, fieldName); 1250 1251 if (array == null) { 1252 appendNullText(buffer, fieldName); 1253 1254 } else if (isFullDetail(fullDetail)) { 1255 appendDetail(buffer, fieldName, array); 1256 1257 } else { 1258 appendSummary(buffer, fieldName, array); 1259 } 1260 1261 appendFieldEnd(buffer, fieldName); 1262 } 1263 1264 /** 1265 * <p>Append to the {@code toString} the detail of a 1266 * {@code char} array.</p> 1267 * 1268 * @param buffer the {@code StringBuffer} to populate 1269 * @param fieldName the field name, typically not used as already appended 1270 * @param array the array to add to the {@code toString}, 1271 * not {@code null} 1272 */ 1273 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) { 1274 buffer.append(arrayStart); 1275 for (int i = 0; i < array.length; i++) { 1276 if (i > 0) { 1277 buffer.append(arraySeparator); 1278 } 1279 appendDetail(buffer, fieldName, array[i]); 1280 } 1281 buffer.append(arrayEnd); 1282 } 1283 1284 /** 1285 * <p>Append to the {@code toString} a summary of a 1286 * {@code char} array.</p> 1287 * 1288 * @param buffer the {@code StringBuffer} to populate 1289 * @param fieldName the field name, typically not used as already appended 1290 * @param array the array to add to the {@code toString}, 1291 * not {@code null} 1292 */ 1293 protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) { 1294 appendSummarySize(buffer, fieldName, array.length); 1295 } 1296 1297 //---------------------------------------------------------------------------- 1298 1299 /** 1300 * <p>Append to the {@code toString} a {@code double} 1301 * array.</p> 1302 * 1303 * @param buffer the {@code StringBuffer} to populate 1304 * @param fieldName the field name 1305 * @param array the array to add to the toString 1306 * @param fullDetail {@code true} for detail, {@code false} 1307 * for summary info, {@code null} for style decides 1308 */ 1309 public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) { 1310 appendFieldStart(buffer, fieldName); 1311 1312 if (array == null) { 1313 appendNullText(buffer, fieldName); 1314 1315 } else if (isFullDetail(fullDetail)) { 1316 appendDetail(buffer, fieldName, array); 1317 1318 } else { 1319 appendSummary(buffer, fieldName, array); 1320 } 1321 1322 appendFieldEnd(buffer, fieldName); 1323 } 1324 1325 /** 1326 * <p>Append to the {@code toString} the detail of a 1327 * {@code double} array.</p> 1328 * 1329 * @param buffer the {@code StringBuffer} to populate 1330 * @param fieldName the field name, typically not used as already appended 1331 * @param array the array to add to the {@code toString}, 1332 * not {@code null} 1333 */ 1334 protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) { 1335 buffer.append(arrayStart); 1336 for (int i = 0; i < array.length; i++) { 1337 if (i > 0) { 1338 buffer.append(arraySeparator); 1339 } 1340 appendDetail(buffer, fieldName, array[i]); 1341 } 1342 buffer.append(arrayEnd); 1343 } 1344 1345 /** 1346 * <p>Append to the {@code toString} a summary of a 1347 * {@code double} array.</p> 1348 * 1349 * @param buffer the {@code StringBuffer} to populate 1350 * @param fieldName the field name, typically not used as already appended 1351 * @param array the array to add to the {@code toString}, 1352 * not {@code null} 1353 */ 1354 protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) { 1355 appendSummarySize(buffer, fieldName, array.length); 1356 } 1357 1358 //---------------------------------------------------------------------------- 1359 1360 /** 1361 * <p>Append to the {@code toString} a {@code float} 1362 * array.</p> 1363 * 1364 * @param buffer the {@code StringBuffer} to populate 1365 * @param fieldName the field name 1366 * @param array the array to add to the toString 1367 * @param fullDetail {@code true} for detail, {@code false} 1368 * for summary info, {@code null} for style decides 1369 */ 1370 public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) { 1371 appendFieldStart(buffer, fieldName); 1372 1373 if (array == null) { 1374 appendNullText(buffer, fieldName); 1375 1376 } else if (isFullDetail(fullDetail)) { 1377 appendDetail(buffer, fieldName, array); 1378 1379 } else { 1380 appendSummary(buffer, fieldName, array); 1381 } 1382 1383 appendFieldEnd(buffer, fieldName); 1384 } 1385 1386 /** 1387 * <p>Append to the {@code toString} the detail of a 1388 * {@code float} array.</p> 1389 * 1390 * @param buffer the {@code StringBuffer} to populate 1391 * @param fieldName the field name, typically not used as already appended 1392 * @param array the array to add to the {@code toString}, 1393 * not {@code null} 1394 */ 1395 protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) { 1396 buffer.append(arrayStart); 1397 for (int i = 0; i < array.length; i++) { 1398 if (i > 0) { 1399 buffer.append(arraySeparator); 1400 } 1401 appendDetail(buffer, fieldName, array[i]); 1402 } 1403 buffer.append(arrayEnd); 1404 } 1405 1406 /** 1407 * <p>Append to the {@code toString} a summary of a 1408 * {@code float} array.</p> 1409 * 1410 * @param buffer the {@code StringBuffer} to populate 1411 * @param fieldName the field name, typically not used as already appended 1412 * @param array the array to add to the {@code toString}, 1413 * not {@code null} 1414 */ 1415 protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) { 1416 appendSummarySize(buffer, fieldName, array.length); 1417 } 1418 1419 //---------------------------------------------------------------------------- 1420 1421 /** 1422 * <p>Append to the {@code toString} a {@code boolean} 1423 * array.</p> 1424 * 1425 * @param buffer the {@code StringBuffer} to populate 1426 * @param fieldName the field name 1427 * @param array the array to add to the toString 1428 * @param fullDetail {@code true} for detail, {@code false} 1429 * for summary info, {@code null} for style decides 1430 */ 1431 public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) { 1432 appendFieldStart(buffer, fieldName); 1433 1434 if (array == null) { 1435 appendNullText(buffer, fieldName); 1436 1437 } else if (isFullDetail(fullDetail)) { 1438 appendDetail(buffer, fieldName, array); 1439 1440 } else { 1441 appendSummary(buffer, fieldName, array); 1442 } 1443 1444 appendFieldEnd(buffer, fieldName); 1445 } 1446 1447 /** 1448 * <p>Append to the {@code toString} the detail of a 1449 * {@code boolean} array.</p> 1450 * 1451 * @param buffer the {@code StringBuffer} to populate 1452 * @param fieldName the field name, typically not used as already appended 1453 * @param array the array to add to the {@code toString}, 1454 * not {@code null} 1455 */ 1456 protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) { 1457 buffer.append(arrayStart); 1458 for (int i = 0; i < array.length; i++) { 1459 if (i > 0) { 1460 buffer.append(arraySeparator); 1461 } 1462 appendDetail(buffer, fieldName, array[i]); 1463 } 1464 buffer.append(arrayEnd); 1465 } 1466 1467 /** 1468 * <p>Append to the {@code toString} a summary of a 1469 * {@code boolean} array.</p> 1470 * 1471 * @param buffer the {@code StringBuffer} to populate 1472 * @param fieldName the field name, typically not used as already appended 1473 * @param array the array to add to the {@code toString}, 1474 * not {@code null} 1475 */ 1476 protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) { 1477 appendSummarySize(buffer, fieldName, array.length); 1478 } 1479 1480 //---------------------------------------------------------------------------- 1481 1482 /** 1483 * <p>Append to the {@code toString} the class name.</p> 1484 * 1485 * @param buffer the {@code StringBuffer} to populate 1486 * @param object the {@code Object} whose name to output 1487 */ 1488 protected void appendClassName(final StringBuffer buffer, final Object object) { 1489 if (useClassName && object != null) { 1490 register(object); 1491 if (useShortClassName) { 1492 buffer.append(getShortClassName(object.getClass())); 1493 } else { 1494 buffer.append(object.getClass().getName()); 1495 } 1496 } 1497 } 1498 1499 /** 1500 * <p>Append the {@link System#identityHashCode(java.lang.Object)}.</p> 1501 * 1502 * @param buffer the {@code StringBuffer} to populate 1503 * @param object the {@code Object} whose id to output 1504 */ 1505 protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) { 1506 if (this.isUseIdentityHashCode() && object!=null) { 1507 register(object); 1508 buffer.append('@'); 1509 buffer.append(Integer.toHexString(System.identityHashCode(object))); 1510 } 1511 } 1512 1513 /** 1514 * <p>Append to the {@code toString} the content start.</p> 1515 * 1516 * @param buffer the {@code StringBuffer} to populate 1517 */ 1518 protected void appendContentStart(final StringBuffer buffer) { 1519 buffer.append(contentStart); 1520 } 1521 1522 /** 1523 * <p>Append to the {@code toString} the content end.</p> 1524 * 1525 * @param buffer the {@code StringBuffer} to populate 1526 */ 1527 protected void appendContentEnd(final StringBuffer buffer) { 1528 buffer.append(contentEnd); 1529 } 1530 1531 /** 1532 * <p>Append to the {@code toString} an indicator for {@code null}.</p> 1533 * 1534 * <p>The default indicator is {@code '<null>'}.</p> 1535 * 1536 * @param buffer the {@code StringBuffer} to populate 1537 * @param fieldName the field name, typically not used as already appended 1538 */ 1539 protected void appendNullText(final StringBuffer buffer, final String fieldName) { 1540 buffer.append(nullText); 1541 } 1542 1543 /** 1544 * <p>Append to the {@code toString} the field separator.</p> 1545 * 1546 * @param buffer the {@code StringBuffer} to populate 1547 */ 1548 protected void appendFieldSeparator(final StringBuffer buffer) { 1549 buffer.append(fieldSeparator); 1550 } 1551 1552 /** 1553 * <p>Append to the {@code toString} the field start.</p> 1554 * 1555 * @param buffer the {@code StringBuffer} to populate 1556 * @param fieldName the field name 1557 */ 1558 protected void appendFieldStart(final StringBuffer buffer, final String fieldName) { 1559 if (useFieldNames && fieldName != null) { 1560 buffer.append(fieldName); 1561 buffer.append(fieldNameValueSeparator); 1562 } 1563 } 1564 1565 /** 1566 * <p>Append to the {@code toString} the field end.</p> 1567 * 1568 * @param buffer the {@code StringBuffer} to populate 1569 * @param fieldName the field name, typically not used as already appended 1570 */ 1571 protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) { 1572 appendFieldSeparator(buffer); 1573 } 1574 1575 /** 1576 * <p>Append to the {@code toString} a size summary.</p> 1577 * 1578 * <p>The size summary is used to summarize the contents of 1579 * {@code Collections}, {@code Maps} and arrays.</p> 1580 * 1581 * <p>The output consists of a prefix, the passed in size 1582 * and a suffix.</p> 1583 * 1584 * <p>The default format is {@code '<size=n>'}.</p> 1585 * 1586 * @param buffer the {@code StringBuffer} to populate 1587 * @param fieldName the field name, typically not used as already appended 1588 * @param size the size to append 1589 */ 1590 protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) { 1591 buffer.append(sizeStartText); 1592 buffer.append(size); 1593 buffer.append(sizeEndText); 1594 } 1595 1596 /** 1597 * <p>Is this field to be output in full detail.</p> 1598 * 1599 * <p>This method converts a detail request into a detail level. 1600 * The calling code may request full detail ({@code true}), 1601 * but a subclass might ignore that and always return 1602 * {@code false}. The calling code may pass in 1603 * {@code null} indicating that it doesn't care about 1604 * the detail level. In this case the default detail level is 1605 * used.</p> 1606 * 1607 * @param fullDetailRequest the detail level requested 1608 * @return whether full detail is to be shown 1609 */ 1610 protected boolean isFullDetail(final Boolean fullDetailRequest) { 1611 if (fullDetailRequest == null) { 1612 return defaultFullDetail; 1613 } 1614 return fullDetailRequest.booleanValue(); 1615 } 1616 1617 /** 1618 * <p>Gets the short class name for a class.</p> 1619 * 1620 * <p>The short class name is the classname excluding 1621 * the package name.</p> 1622 * 1623 * @param cls the {@code Class} to get the short name of 1624 * @return the short name 1625 */ 1626 protected String getShortClassName(final Class<?> cls) { 1627 return ClassUtils.getShortClassName(cls); 1628 } 1629 1630 // Setters and getters for the customizable parts of the style 1631 // These methods are not expected to be overridden, except to make public 1632 // (They are not public so that immutable subclasses can be written) 1633 //--------------------------------------------------------------------- 1634 1635 /** 1636 * <p>Gets whether to use the class name.</p> 1637 * 1638 * @return the current useClassName flag 1639 */ 1640 protected boolean isUseClassName() { 1641 return useClassName; 1642 } 1643 1644 /** 1645 * <p>Sets whether to use the class name.</p> 1646 * 1647 * @param useClassName the new useClassName flag 1648 */ 1649 protected void setUseClassName(final boolean useClassName) { 1650 this.useClassName = useClassName; 1651 } 1652 1653 //--------------------------------------------------------------------- 1654 1655 /** 1656 * <p>Gets whether to output short or long class names.</p> 1657 * 1658 * @return the current useShortClassName flag 1659 * @since 2.0 1660 */ 1661 protected boolean isUseShortClassName() { 1662 return useShortClassName; 1663 } 1664 1665 /** 1666 * <p>Sets whether to output short or long class names.</p> 1667 * 1668 * @param useShortClassName the new useShortClassName flag 1669 * @since 2.0 1670 */ 1671 protected void setUseShortClassName(final boolean useShortClassName) { 1672 this.useShortClassName = useShortClassName; 1673 } 1674 1675 //--------------------------------------------------------------------- 1676 1677 /** 1678 * <p>Gets whether to use the identity hash code.</p> 1679 * 1680 * @return the current useIdentityHashCode flag 1681 */ 1682 protected boolean isUseIdentityHashCode() { 1683 return useIdentityHashCode; 1684 } 1685 1686 /** 1687 * <p>Sets whether to use the identity hash code.</p> 1688 * 1689 * @param useIdentityHashCode the new useIdentityHashCode flag 1690 */ 1691 protected void setUseIdentityHashCode(final boolean useIdentityHashCode) { 1692 this.useIdentityHashCode = useIdentityHashCode; 1693 } 1694 1695 //--------------------------------------------------------------------- 1696 1697 /** 1698 * <p>Gets whether to use the field names passed in.</p> 1699 * 1700 * @return the current useFieldNames flag 1701 */ 1702 protected boolean isUseFieldNames() { 1703 return useFieldNames; 1704 } 1705 1706 /** 1707 * <p>Sets whether to use the field names passed in.</p> 1708 * 1709 * @param useFieldNames the new useFieldNames flag 1710 */ 1711 protected void setUseFieldNames(final boolean useFieldNames) { 1712 this.useFieldNames = useFieldNames; 1713 } 1714 1715 //--------------------------------------------------------------------- 1716 1717 /** 1718 * <p>Gets whether to use full detail when the caller doesn't 1719 * specify.</p> 1720 * 1721 * @return the current defaultFullDetail flag 1722 */ 1723 protected boolean isDefaultFullDetail() { 1724 return defaultFullDetail; 1725 } 1726 1727 /** 1728 * <p>Sets whether to use full detail when the caller doesn't 1729 * specify.</p> 1730 * 1731 * @param defaultFullDetail the new defaultFullDetail flag 1732 */ 1733 protected void setDefaultFullDetail(final boolean defaultFullDetail) { 1734 this.defaultFullDetail = defaultFullDetail; 1735 } 1736 1737 //--------------------------------------------------------------------- 1738 1739 /** 1740 * <p>Gets whether to output array content detail.</p> 1741 * 1742 * @return the current array content detail setting 1743 */ 1744 protected boolean isArrayContentDetail() { 1745 return arrayContentDetail; 1746 } 1747 1748 /** 1749 * <p>Sets whether to output array content detail.</p> 1750 * 1751 * @param arrayContentDetail the new arrayContentDetail flag 1752 */ 1753 protected void setArrayContentDetail(final boolean arrayContentDetail) { 1754 this.arrayContentDetail = arrayContentDetail; 1755 } 1756 1757 //--------------------------------------------------------------------- 1758 1759 /** 1760 * <p>Gets the array start text.</p> 1761 * 1762 * @return the current array start text 1763 */ 1764 protected String getArrayStart() { 1765 return arrayStart; 1766 } 1767 1768 /** 1769 * <p>Sets the array start text.</p> 1770 * 1771 * <p>{@code null} is accepted, but will be converted to 1772 * an empty String.</p> 1773 * 1774 * @param arrayStart the new array start text 1775 */ 1776 protected void setArrayStart(String arrayStart) { 1777 if (arrayStart == null) { 1778 arrayStart = StringUtils.EMPTY; 1779 } 1780 this.arrayStart = arrayStart; 1781 } 1782 1783 //--------------------------------------------------------------------- 1784 1785 /** 1786 * <p>Gets the array end text.</p> 1787 * 1788 * @return the current array end text 1789 */ 1790 protected String getArrayEnd() { 1791 return arrayEnd; 1792 } 1793 1794 /** 1795 * <p>Sets the array end text.</p> 1796 * 1797 * <p>{@code null} is accepted, but will be converted to 1798 * an empty String.</p> 1799 * 1800 * @param arrayEnd the new array end text 1801 */ 1802 protected void setArrayEnd(String arrayEnd) { 1803 if (arrayEnd == null) { 1804 arrayEnd = StringUtils.EMPTY; 1805 } 1806 this.arrayEnd = arrayEnd; 1807 } 1808 1809 //--------------------------------------------------------------------- 1810 1811 /** 1812 * <p>Gets the array separator text.</p> 1813 * 1814 * @return the current array separator text 1815 */ 1816 protected String getArraySeparator() { 1817 return arraySeparator; 1818 } 1819 1820 /** 1821 * <p>Sets the array separator text.</p> 1822 * 1823 * <p>{@code null} is accepted, but will be converted to 1824 * an empty String.</p> 1825 * 1826 * @param arraySeparator the new array separator text 1827 */ 1828 protected void setArraySeparator(String arraySeparator) { 1829 if (arraySeparator == null) { 1830 arraySeparator = StringUtils.EMPTY; 1831 } 1832 this.arraySeparator = arraySeparator; 1833 } 1834 1835 //--------------------------------------------------------------------- 1836 1837 /** 1838 * <p>Gets the content start text.</p> 1839 * 1840 * @return the current content start text 1841 */ 1842 protected String getContentStart() { 1843 return contentStart; 1844 } 1845 1846 /** 1847 * <p>Sets the content start text.</p> 1848 * 1849 * <p>{@code null} is accepted, but will be converted to 1850 * an empty String.</p> 1851 * 1852 * @param contentStart the new content start text 1853 */ 1854 protected void setContentStart(String contentStart) { 1855 if (contentStart == null) { 1856 contentStart = StringUtils.EMPTY; 1857 } 1858 this.contentStart = contentStart; 1859 } 1860 1861 //--------------------------------------------------------------------- 1862 1863 /** 1864 * <p>Gets the content end text.</p> 1865 * 1866 * @return the current content end text 1867 */ 1868 protected String getContentEnd() { 1869 return contentEnd; 1870 } 1871 1872 /** 1873 * <p>Sets the content end text.</p> 1874 * 1875 * <p>{@code null} is accepted, but will be converted to 1876 * an empty String.</p> 1877 * 1878 * @param contentEnd the new content end text 1879 */ 1880 protected void setContentEnd(String contentEnd) { 1881 if (contentEnd == null) { 1882 contentEnd = StringUtils.EMPTY; 1883 } 1884 this.contentEnd = contentEnd; 1885 } 1886 1887 //--------------------------------------------------------------------- 1888 1889 /** 1890 * <p>Gets the field name value separator text.</p> 1891 * 1892 * @return the current field name value separator text 1893 */ 1894 protected String getFieldNameValueSeparator() { 1895 return fieldNameValueSeparator; 1896 } 1897 1898 /** 1899 * <p>Sets the field name value separator text.</p> 1900 * 1901 * <p>{@code null} is accepted, but will be converted to 1902 * an empty String.</p> 1903 * 1904 * @param fieldNameValueSeparator the new field name value separator text 1905 */ 1906 protected void setFieldNameValueSeparator(String fieldNameValueSeparator) { 1907 if (fieldNameValueSeparator == null) { 1908 fieldNameValueSeparator = StringUtils.EMPTY; 1909 } 1910 this.fieldNameValueSeparator = fieldNameValueSeparator; 1911 } 1912 1913 //--------------------------------------------------------------------- 1914 1915 /** 1916 * <p>Gets the field separator text.</p> 1917 * 1918 * @return the current field separator text 1919 */ 1920 protected String getFieldSeparator() { 1921 return fieldSeparator; 1922 } 1923 1924 /** 1925 * <p>Sets the field separator text.</p> 1926 * 1927 * <p>{@code null} is accepted, but will be converted to 1928 * an empty String.</p> 1929 * 1930 * @param fieldSeparator the new field separator text 1931 */ 1932 protected void setFieldSeparator(String fieldSeparator) { 1933 if (fieldSeparator == null) { 1934 fieldSeparator = StringUtils.EMPTY; 1935 } 1936 this.fieldSeparator = fieldSeparator; 1937 } 1938 1939 //--------------------------------------------------------------------- 1940 1941 /** 1942 * <p>Gets whether the field separator should be added at the start 1943 * of each buffer.</p> 1944 * 1945 * @return the fieldSeparatorAtStart flag 1946 * @since 2.0 1947 */ 1948 protected boolean isFieldSeparatorAtStart() { 1949 return fieldSeparatorAtStart; 1950 } 1951 1952 /** 1953 * <p>Sets whether the field separator should be added at the start 1954 * of each buffer.</p> 1955 * 1956 * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag 1957 * @since 2.0 1958 */ 1959 protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) { 1960 this.fieldSeparatorAtStart = fieldSeparatorAtStart; 1961 } 1962 1963 //--------------------------------------------------------------------- 1964 1965 /** 1966 * <p>Gets whether the field separator should be added at the end 1967 * of each buffer.</p> 1968 * 1969 * @return fieldSeparatorAtEnd flag 1970 * @since 2.0 1971 */ 1972 protected boolean isFieldSeparatorAtEnd() { 1973 return fieldSeparatorAtEnd; 1974 } 1975 1976 /** 1977 * <p>Sets whether the field separator should be added at the end 1978 * of each buffer.</p> 1979 * 1980 * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag 1981 * @since 2.0 1982 */ 1983 protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) { 1984 this.fieldSeparatorAtEnd = fieldSeparatorAtEnd; 1985 } 1986 1987 //--------------------------------------------------------------------- 1988 1989 /** 1990 * <p>Gets the text to output when {@code null} found.</p> 1991 * 1992 * @return the current text to output when null found 1993 */ 1994 protected String getNullText() { 1995 return nullText; 1996 } 1997 1998 /** 1999 * <p>Sets the text to output when {@code null} found.</p> 2000 * 2001 * <p>{@code null} is accepted, but will be converted to 2002 * an empty String.</p> 2003 * 2004 * @param nullText the new text to output when null found 2005 */ 2006 protected void setNullText(String nullText) { 2007 if (nullText == null) { 2008 nullText = StringUtils.EMPTY; 2009 } 2010 this.nullText = nullText; 2011 } 2012 2013 //--------------------------------------------------------------------- 2014 2015 /** 2016 * <p>Gets the start text to output when a {@code Collection}, 2017 * {@code Map} or array size is output.</p> 2018 * 2019 * <p>This is output before the size value.</p> 2020 * 2021 * @return the current start of size text 2022 */ 2023 protected String getSizeStartText() { 2024 return sizeStartText; 2025 } 2026 2027 /** 2028 * <p>Sets the start text to output when a {@code Collection}, 2029 * {@code Map} or array size is output.</p> 2030 * 2031 * <p>This is output before the size value.</p> 2032 * 2033 * <p>{@code null} is accepted, but will be converted to 2034 * an empty String.</p> 2035 * 2036 * @param sizeStartText the new start of size text 2037 */ 2038 protected void setSizeStartText(String sizeStartText) { 2039 if (sizeStartText == null) { 2040 sizeStartText = StringUtils.EMPTY; 2041 } 2042 this.sizeStartText = sizeStartText; 2043 } 2044 2045 //--------------------------------------------------------------------- 2046 2047 /** 2048 * <p>Gets the end text to output when a {@code Collection}, 2049 * {@code Map} or array size is output.</p> 2050 * 2051 * <p>This is output after the size value.</p> 2052 * 2053 * @return the current end of size text 2054 */ 2055 protected String getSizeEndText() { 2056 return sizeEndText; 2057 } 2058 2059 /** 2060 * <p>Sets the end text to output when a {@code Collection}, 2061 * {@code Map} or array size is output.</p> 2062 * 2063 * <p>This is output after the size value.</p> 2064 * 2065 * <p>{@code null} is accepted, but will be converted to 2066 * an empty String.</p> 2067 * 2068 * @param sizeEndText the new end of size text 2069 */ 2070 protected void setSizeEndText(String sizeEndText) { 2071 if (sizeEndText == null) { 2072 sizeEndText = StringUtils.EMPTY; 2073 } 2074 this.sizeEndText = sizeEndText; 2075 } 2076 2077 //--------------------------------------------------------------------- 2078 2079 /** 2080 * <p>Gets the start text to output when an {@code Object} is 2081 * output in summary mode.</p> 2082 * 2083 * <p>This is output before the size value.</p> 2084 * 2085 * @return the current start of summary text 2086 */ 2087 protected String getSummaryObjectStartText() { 2088 return summaryObjectStartText; 2089 } 2090 2091 /** 2092 * <p>Sets the start text to output when an {@code Object} is 2093 * output in summary mode.</p> 2094 * 2095 * <p>This is output before the size value.</p> 2096 * 2097 * <p>{@code null} is accepted, but will be converted to 2098 * an empty String.</p> 2099 * 2100 * @param summaryObjectStartText the new start of summary text 2101 */ 2102 protected void setSummaryObjectStartText(String summaryObjectStartText) { 2103 if (summaryObjectStartText == null) { 2104 summaryObjectStartText = StringUtils.EMPTY; 2105 } 2106 this.summaryObjectStartText = summaryObjectStartText; 2107 } 2108 2109 //--------------------------------------------------------------------- 2110 2111 /** 2112 * <p>Gets the end text to output when an {@code Object} is 2113 * output in summary mode.</p> 2114 * 2115 * <p>This is output after the size value.</p> 2116 * 2117 * @return the current end of summary text 2118 */ 2119 protected String getSummaryObjectEndText() { 2120 return summaryObjectEndText; 2121 } 2122 2123 /** 2124 * <p>Sets the end text to output when an {@code Object} is 2125 * output in summary mode.</p> 2126 * 2127 * <p>This is output after the size value.</p> 2128 * 2129 * <p>{@code null} is accepted, but will be converted to 2130 * an empty String.</p> 2131 * 2132 * @param summaryObjectEndText the new end of summary text 2133 */ 2134 protected void setSummaryObjectEndText(String summaryObjectEndText) { 2135 if (summaryObjectEndText == null) { 2136 summaryObjectEndText = StringUtils.EMPTY; 2137 } 2138 this.summaryObjectEndText = summaryObjectEndText; 2139 } 2140 2141 //---------------------------------------------------------------------------- 2142 2143 /** 2144 * <p>Default {@code ToStringStyle}.</p> 2145 * 2146 * <p>This is an inner class rather than using 2147 * {@code StandardToStringStyle} to ensure its immutability.</p> 2148 */ 2149 private static final class DefaultToStringStyle extends ToStringStyle { 2150 2151 /** 2152 * Required for serialization support. 2153 * 2154 * @see java.io.Serializable 2155 */ 2156 private static final long serialVersionUID = 1L; 2157 2158 /** 2159 * <p>Constructor.</p> 2160 * 2161 * <p>Use the static constant rather than instantiating.</p> 2162 */ 2163 DefaultToStringStyle() { 2164 super(); 2165 } 2166 2167 /** 2168 * <p>Ensure {@code Singleton} after serialization.</p> 2169 * 2170 * @return the singleton 2171 */ 2172 private Object readResolve() { 2173 return DEFAULT_STYLE; 2174 } 2175 2176 } 2177 2178 //---------------------------------------------------------------------------- 2179 2180 /** 2181 * <p>{@code ToStringStyle} that does not print out 2182 * the field names.</p> 2183 * 2184 * <p>This is an inner class rather than using 2185 * {@code StandardToStringStyle} to ensure its immutability. 2186 */ 2187 private static final class NoFieldNameToStringStyle extends ToStringStyle { 2188 2189 private static final long serialVersionUID = 1L; 2190 2191 /** 2192 * <p>Constructor.</p> 2193 * 2194 * <p>Use the static constant rather than instantiating.</p> 2195 */ 2196 NoFieldNameToStringStyle() { 2197 super(); 2198 this.setUseFieldNames(false); 2199 } 2200 2201 /** 2202 * <p>Ensure {@code Singleton} after serialization.</p> 2203 * 2204 * @return the singleton 2205 */ 2206 private Object readResolve() { 2207 return NO_FIELD_NAMES_STYLE; 2208 } 2209 2210 } 2211 2212 //---------------------------------------------------------------------------- 2213 2214 /** 2215 * <p>{@code ToStringStyle} that prints out the short 2216 * class name and no identity hashcode.</p> 2217 * 2218 * <p>This is an inner class rather than using 2219 * {@code StandardToStringStyle} to ensure its immutability.</p> 2220 */ 2221 private static final class ShortPrefixToStringStyle extends ToStringStyle { 2222 2223 private static final long serialVersionUID = 1L; 2224 2225 /** 2226 * <p>Constructor.</p> 2227 * 2228 * <p>Use the static constant rather than instantiating.</p> 2229 */ 2230 ShortPrefixToStringStyle() { 2231 super(); 2232 this.setUseShortClassName(true); 2233 this.setUseIdentityHashCode(false); 2234 } 2235 2236 /** 2237 * <p>Ensure <code>Singleton</ode> after serialization.</p> 2238 * @return the singleton 2239 */ 2240 private Object readResolve() { 2241 return SHORT_PREFIX_STYLE; 2242 } 2243 2244 } 2245 2246 //---------------------------------------------------------------------------- 2247 2248 /** 2249 * <p>{@code ToStringStyle} that does not print out the 2250 * classname, identity hashcode, content start or field name.</p> 2251 * 2252 * <p>This is an inner class rather than using 2253 * {@code StandardToStringStyle} to ensure its immutability.</p> 2254 */ 2255 private static final class SimpleToStringStyle extends ToStringStyle { 2256 2257 private static final long serialVersionUID = 1L; 2258 2259 /** 2260 * <p>Constructor.</p> 2261 * 2262 * <p>Use the static constant rather than instantiating.</p> 2263 */ 2264 SimpleToStringStyle() { 2265 super(); 2266 this.setUseClassName(false); 2267 this.setUseIdentityHashCode(false); 2268 this.setUseFieldNames(false); 2269 this.setContentStart(StringUtils.EMPTY); 2270 this.setContentEnd(StringUtils.EMPTY); 2271 } 2272 2273 /** 2274 * <p>Ensure <code>Singleton</ode> after serialization.</p> 2275 * @return the singleton 2276 */ 2277 private Object readResolve() { 2278 return SIMPLE_STYLE; 2279 } 2280 2281 } 2282 2283 //---------------------------------------------------------------------------- 2284 2285 /** 2286 * <p>{@code ToStringStyle} that outputs on multiple lines.</p> 2287 * 2288 * <p>This is an inner class rather than using 2289 * {@code StandardToStringStyle} to ensure its immutability.</p> 2290 */ 2291 private static final class MultiLineToStringStyle extends ToStringStyle { 2292 2293 private static final long serialVersionUID = 1L; 2294 2295 /** 2296 * <p>Constructor.</p> 2297 * 2298 * <p>Use the static constant rather than instantiating.</p> 2299 */ 2300 MultiLineToStringStyle() { 2301 super(); 2302 this.setContentStart("["); 2303 this.setFieldSeparator(System.lineSeparator() + " "); 2304 this.setFieldSeparatorAtStart(true); 2305 this.setContentEnd(System.lineSeparator() + "]"); 2306 } 2307 2308 /** 2309 * <p>Ensure {@code Singleton} after serialization.</p> 2310 * 2311 * @return the singleton 2312 */ 2313 private Object readResolve() { 2314 return MULTI_LINE_STYLE; 2315 } 2316 2317 } 2318 2319 //---------------------------------------------------------------------------- 2320 2321 /** 2322 * <p>{@code ToStringStyle} that does not print out the classname 2323 * and identity hash code but prints content start and field names.</p> 2324 * 2325 * <p>This is an inner class rather than using 2326 * {@code StandardToStringStyle} to ensure its immutability.</p> 2327 */ 2328 private static final class NoClassNameToStringStyle extends ToStringStyle { 2329 2330 private static final long serialVersionUID = 1L; 2331 2332 /** 2333 * <p>Constructor.</p> 2334 * 2335 * <p>Use the static constant rather than instantiating.</p> 2336 */ 2337 NoClassNameToStringStyle() { 2338 super(); 2339 this.setUseClassName(false); 2340 this.setUseIdentityHashCode(false); 2341 } 2342 2343 /** 2344 * <p>Ensure {@code Singleton} after serialization.</p> 2345 * 2346 * @return the singleton 2347 */ 2348 private Object readResolve() { 2349 return NO_CLASS_NAME_STYLE; 2350 } 2351 2352 } 2353 2354 // ---------------------------------------------------------------------------- 2355 2356 /** 2357 * <p> 2358 * {@code ToStringStyle} that outputs with JSON format. 2359 * </p> 2360 * 2361 * <p> 2362 * This is an inner class rather than using 2363 * {@code StandardToStringStyle} to ensure its immutability. 2364 * </p> 2365 * 2366 * @since 3.4 2367 * @see <a href="http://json.org">json.org</a> 2368 */ 2369 private static final class JsonToStringStyle extends ToStringStyle { 2370 2371 private static final long serialVersionUID = 1L; 2372 2373 private static final String FIELD_NAME_QUOTE = "\""; 2374 2375 /** 2376 * <p> 2377 * Constructor. 2378 * </p> 2379 * 2380 * <p> 2381 * Use the static constant rather than instantiating. 2382 * </p> 2383 */ 2384 JsonToStringStyle() { 2385 super(); 2386 2387 this.setUseClassName(false); 2388 this.setUseIdentityHashCode(false); 2389 2390 this.setContentStart("{"); 2391 this.setContentEnd("}"); 2392 2393 this.setArrayStart("["); 2394 this.setArrayEnd("]"); 2395 2396 this.setFieldSeparator(","); 2397 this.setFieldNameValueSeparator(":"); 2398 2399 this.setNullText("null"); 2400 2401 this.setSummaryObjectStartText("\"<"); 2402 this.setSummaryObjectEndText(">\""); 2403 2404 this.setSizeStartText("\"<size="); 2405 this.setSizeEndText(">\""); 2406 } 2407 2408 @Override 2409 public void append(final StringBuffer buffer, final String fieldName, 2410 final Object[] array, final Boolean fullDetail) { 2411 2412 if (fieldName == null) { 2413 throw new UnsupportedOperationException( 2414 "Field names are mandatory when using JsonToStringStyle"); 2415 } 2416 if (!isFullDetail(fullDetail)) { 2417 throw new UnsupportedOperationException( 2418 "FullDetail must be true when using JsonToStringStyle"); 2419 } 2420 2421 super.append(buffer, fieldName, array, fullDetail); 2422 } 2423 2424 @Override 2425 public void append(final StringBuffer buffer, final String fieldName, final long[] array, 2426 final Boolean fullDetail) { 2427 2428 if (fieldName == null) { 2429 throw new UnsupportedOperationException( 2430 "Field names are mandatory when using JsonToStringStyle"); 2431 } 2432 if (!isFullDetail(fullDetail)) { 2433 throw new UnsupportedOperationException( 2434 "FullDetail must be true when using JsonToStringStyle"); 2435 } 2436 2437 super.append(buffer, fieldName, array, fullDetail); 2438 } 2439 2440 @Override 2441 public void append(final StringBuffer buffer, final String fieldName, final int[] array, 2442 final Boolean fullDetail) { 2443 2444 if (fieldName == null) { 2445 throw new UnsupportedOperationException( 2446 "Field names are mandatory when using JsonToStringStyle"); 2447 } 2448 if (!isFullDetail(fullDetail)) { 2449 throw new UnsupportedOperationException( 2450 "FullDetail must be true when using JsonToStringStyle"); 2451 } 2452 2453 super.append(buffer, fieldName, array, fullDetail); 2454 } 2455 2456 @Override 2457 public void append(final StringBuffer buffer, final String fieldName, 2458 final short[] array, final Boolean fullDetail) { 2459 2460 if (fieldName == null) { 2461 throw new UnsupportedOperationException( 2462 "Field names are mandatory when using JsonToStringStyle"); 2463 } 2464 if (!isFullDetail(fullDetail)) { 2465 throw new UnsupportedOperationException( 2466 "FullDetail must be true when using JsonToStringStyle"); 2467 } 2468 2469 super.append(buffer, fieldName, array, fullDetail); 2470 } 2471 2472 @Override 2473 public void append(final StringBuffer buffer, final String fieldName, final byte[] array, 2474 final Boolean fullDetail) { 2475 2476 if (fieldName == null) { 2477 throw new UnsupportedOperationException( 2478 "Field names are mandatory when using JsonToStringStyle"); 2479 } 2480 if (!isFullDetail(fullDetail)) { 2481 throw new UnsupportedOperationException( 2482 "FullDetail must be true when using JsonToStringStyle"); 2483 } 2484 2485 super.append(buffer, fieldName, array, fullDetail); 2486 } 2487 2488 @Override 2489 public void append(final StringBuffer buffer, final String fieldName, final char[] array, 2490 final Boolean fullDetail) { 2491 2492 if (fieldName == null) { 2493 throw new UnsupportedOperationException( 2494 "Field names are mandatory when using JsonToStringStyle"); 2495 } 2496 if (!isFullDetail(fullDetail)) { 2497 throw new UnsupportedOperationException( 2498 "FullDetail must be true when using JsonToStringStyle"); 2499 } 2500 2501 super.append(buffer, fieldName, array, fullDetail); 2502 } 2503 2504 @Override 2505 public void append(final StringBuffer buffer, final String fieldName, 2506 final double[] array, final Boolean fullDetail) { 2507 2508 if (fieldName == null) { 2509 throw new UnsupportedOperationException( 2510 "Field names are mandatory when using JsonToStringStyle"); 2511 } 2512 if (!isFullDetail(fullDetail)) { 2513 throw new UnsupportedOperationException( 2514 "FullDetail must be true when using JsonToStringStyle"); 2515 } 2516 2517 super.append(buffer, fieldName, array, fullDetail); 2518 } 2519 2520 @Override 2521 public void append(final StringBuffer buffer, final String fieldName, 2522 final float[] array, final Boolean fullDetail) { 2523 2524 if (fieldName == null) { 2525 throw new UnsupportedOperationException( 2526 "Field names are mandatory when using JsonToStringStyle"); 2527 } 2528 if (!isFullDetail(fullDetail)) { 2529 throw new UnsupportedOperationException( 2530 "FullDetail must be true when using JsonToStringStyle"); 2531 } 2532 2533 super.append(buffer, fieldName, array, fullDetail); 2534 } 2535 2536 @Override 2537 public void append(final StringBuffer buffer, final String fieldName, 2538 final boolean[] array, final Boolean fullDetail) { 2539 2540 if (fieldName == null) { 2541 throw new UnsupportedOperationException( 2542 "Field names are mandatory when using JsonToStringStyle"); 2543 } 2544 if (!isFullDetail(fullDetail)) { 2545 throw new UnsupportedOperationException( 2546 "FullDetail must be true when using JsonToStringStyle"); 2547 } 2548 2549 super.append(buffer, fieldName, array, fullDetail); 2550 } 2551 2552 @Override 2553 public void append(final StringBuffer buffer, final String fieldName, final Object value, 2554 final Boolean fullDetail) { 2555 2556 if (fieldName == null) { 2557 throw new UnsupportedOperationException( 2558 "Field names are mandatory when using JsonToStringStyle"); 2559 } 2560 if (!isFullDetail(fullDetail)) { 2561 throw new UnsupportedOperationException( 2562 "FullDetail must be true when using JsonToStringStyle"); 2563 } 2564 2565 super.append(buffer, fieldName, value, fullDetail); 2566 } 2567 2568 @Override 2569 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) { 2570 appendValueAsString(buffer, String.valueOf(value)); 2571 } 2572 2573 @Override 2574 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) { 2575 2576 if (value == null) { 2577 appendNullText(buffer, fieldName); 2578 return; 2579 } 2580 2581 if (value instanceof String || value instanceof Character) { 2582 appendValueAsString(buffer, value.toString()); 2583 return; 2584 } 2585 2586 if (value instanceof Number || value instanceof Boolean) { 2587 buffer.append(value); 2588 return; 2589 } 2590 2591 final String valueAsString = value.toString(); 2592 if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) { 2593 buffer.append(value); 2594 return; 2595 } 2596 2597 appendDetail(buffer, fieldName, valueAsString); 2598 } 2599 2600 @Override 2601 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) { 2602 if (map != null && !map.isEmpty()) { 2603 buffer.append(getContentStart()); 2604 2605 boolean firstItem = true; 2606 for (final Entry<?, ?> entry : map.entrySet()) { 2607 final String keyStr = Objects.toString(entry.getKey(), null); 2608 if (keyStr != null) { 2609 if (firstItem) { 2610 firstItem = false; 2611 } else { 2612 appendFieldEnd(buffer, keyStr); 2613 } 2614 appendFieldStart(buffer, keyStr); 2615 final Object value = entry.getValue(); 2616 if (value == null) { 2617 appendNullText(buffer, keyStr); 2618 } else { 2619 appendInternal(buffer, keyStr, value, true); 2620 } 2621 } 2622 } 2623 2624 buffer.append(getContentEnd()); 2625 return; 2626 } 2627 2628 buffer.append(map); 2629 } 2630 2631 private boolean isJsonArray(final String valueAsString) { 2632 return valueAsString.startsWith(getArrayStart()) 2633 && valueAsString.endsWith(getArrayEnd()); 2634 } 2635 2636 private boolean isJsonObject(final String valueAsString) { 2637 return valueAsString.startsWith(getContentStart()) 2638 && valueAsString.endsWith(getContentEnd()); 2639 } 2640 2641 /** 2642 * Appends the given String enclosed in double-quotes to the given StringBuffer. 2643 * 2644 * @param buffer the StringBuffer to append the value to. 2645 * @param value the value to append. 2646 */ 2647 private void appendValueAsString(final StringBuffer buffer, final String value) { 2648 buffer.append('"').append(StringEscapeUtils.escapeJson(value)).append('"'); 2649 } 2650 2651 @Override 2652 protected void appendFieldStart(final StringBuffer buffer, final String fieldName) { 2653 2654 if (fieldName == null) { 2655 throw new UnsupportedOperationException( 2656 "Field names are mandatory when using JsonToStringStyle"); 2657 } 2658 2659 super.appendFieldStart(buffer, FIELD_NAME_QUOTE + StringEscapeUtils.escapeJson(fieldName) 2660 + FIELD_NAME_QUOTE); 2661 } 2662 2663 /** 2664 * <p> 2665 * Ensure {@code Singleton} after serialization. 2666 * </p> 2667 * 2668 * @return the singleton 2669 */ 2670 private Object readResolve() { 2671 return JSON_STYLE; 2672 } 2673 2674 } 2675}