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