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