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