1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.lang3.builder;
19
20 import java.io.Serializable;
21 import java.lang.reflect.Array;
22 import java.util.Collection;
23 import java.util.IdentityHashMap;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Objects;
27
28 import org.apache.commons.lang3.ClassUtils;
29 import org.apache.commons.lang3.ObjectUtils;
30 import org.apache.commons.lang3.StringEscapeUtils;
31 import org.apache.commons.lang3.StringUtils;
32 import org.apache.commons.lang3.Strings;
33
34 /**
35 * Controls {@link String} formatting for {@link ToStringBuilder}. The main public interface is always via {@link ToStringBuilder}.
36 *
37 * <p>
38 * 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
39 * predefined constants on this class. Alternatively, the {@link StandardToStringStyle} class can be used to set the individual settings. Thus most styles can
40 * be achieved without subclassing.
41 * </p>
42 *
43 * <p>
44 * 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
45 * {@link Object} to {@code int[]}) has its own methods to output it. Most have two versions, detail and summary.
46 *
47 * <p>
48 * 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.
49 * </p>
50 *
51 * <p>
52 * If you want to format the output of certain objects, such as dates, you must create a subclass and override a method.
53 * </p>
54 *
55 * <pre>
56 * public class MyStyle extends ToStringStyle {
57 *
58 * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
59 * if (value instanceof Date) {
60 * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
61 * }
62 * buffer.append(value);
63 * }
64 * }
65 * </pre>
66 *
67 * @since 1.0
68 */
69 @SuppressWarnings("deprecation") // StringEscapeUtils
70 public abstract class ToStringStyle implements Serializable {
71
72 /**
73 * Default {@link ToStringStyle}.
74 *
75 * <p>
76 * This is an inner class rather than using {@link StandardToStringStyle} to ensure its immutability.
77 * </p>
78 */
79 private static final class DefaultToStringStyle extends ToStringStyle {
80
81 /**
82 * Required for serialization support.
83 *
84 * @see Serializable
85 */
86 private static final long serialVersionUID = 1L;
87
88 /**
89 * Constructs a new instance.
90 *
91 * <p>
92 * Use the static constant rather than instantiating.
93 * </p>
94 */
95 DefaultToStringStyle() {
96 }
97
98 /**
99 * Ensure Singleton after serialization.
100 *
101 * @return The singleton.
102 */
103 private Object readResolve() {
104 return DEFAULT_STYLE;
105 }
106 }
107
108 /**
109 * {@link ToStringStyle} that outputs with JSON format.
110 *
111 * <p>
112 * This is an inner class rather than using {@link StandardToStringStyle} to ensure its immutability.
113 * </p>
114 *
115 * @since 3.4
116 * @see <a href="https://www.json.org/">json.org</a>
117 */
118 private static final class JsonToStringStyle extends ToStringStyle {
119
120 private static final long serialVersionUID = 1L;
121 private static final String FIELD_NAME_QUOTE = "\"";
122
123 /**
124 * Constructs a new instance.
125 *
126 * <p>
127 * Use the static constant rather than instantiating.
128 * </p>
129 */
130 JsonToStringStyle() {
131 setUseClassName(false);
132 setUseIdentityHashCode(false);
133 setContentStart("{");
134 setContentEnd("}");
135 setArrayStart("[");
136 setArrayEnd("]");
137 setFieldSeparator(",");
138 setFieldNameValueSeparator(":");
139 setNullText("null");
140 setSummaryObjectStartText("\"<");
141 setSummaryObjectEndText(">\"");
142 setSizeStartText("\"<size=");
143 setSizeEndText(">\"");
144 }
145
146 @Override
147 public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
148 checkAppendInput(fieldName, fullDetail);
149 super.append(buffer, fieldName, array, fullDetail);
150 }
151
152 @Override
153 public void append(final StringBuffer buffer, final String fieldName, final byte[] 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 char[] 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 double[] 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 float[] 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 int[] 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 long[] 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 Object value, final Boolean fullDetail) {
190 checkAppendInput(fieldName, fullDetail);
191 super.append(buffer, fieldName, value, fullDetail);
192 }
193
194 @Override
195 public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
196 checkAppendInput(fieldName, fullDetail);
197 super.append(buffer, fieldName, array, fullDetail);
198 }
199
200 @Override
201 public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
202 checkAppendInput(fieldName, fullDetail);
203 super.append(buffer, fieldName, array, fullDetail);
204 }
205
206 @Override
207 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
208 appendValueAsString(buffer, String.valueOf(value));
209 }
210
211 @Override
212 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
213 if (coll != null && !coll.isEmpty()) {
214 buffer.append(getArrayStart());
215 int i = 0;
216 for (final Object item : coll) {
217 appendDetail(buffer, fieldName, i++, item);
218 }
219 buffer.append(getArrayEnd());
220 return;
221 }
222 buffer.append(coll);
223 }
224
225 @Override
226 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
227 if (map != null && !map.isEmpty()) {
228 buffer.append(getContentStart());
229 boolean firstItem = true;
230 for (final Entry<?, ?> entry : map.entrySet()) {
231 final String keyStr = Objects.toString(entry.getKey(), null);
232 if (keyStr != null) {
233 if (firstItem) {
234 firstItem = false;
235 } else {
236 appendFieldEnd(buffer, keyStr);
237 }
238 appendFieldStart(buffer, keyStr);
239 final Object value = entry.getValue();
240 if (value == null) {
241 appendNullText(buffer, keyStr);
242 } else {
243 appendInternal(buffer, keyStr, value, true);
244 }
245 }
246 }
247 buffer.append(getContentEnd());
248 return;
249 }
250 buffer.append(map);
251 }
252
253 @Override
254 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
255 if (value == null) {
256 appendNullText(buffer, fieldName);
257 return;
258 }
259 if (value instanceof String || value instanceof Character) {
260 appendValueAsString(buffer, value.toString());
261 return;
262 }
263 if (value instanceof Number || value instanceof Boolean) {
264 buffer.append(value);
265 return;
266 }
267 final String valueAsString = value.toString();
268 if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
269 buffer.append(value);
270 return;
271 }
272 appendDetail(buffer, fieldName, valueAsString);
273 }
274
275 @Override
276 protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
277 checkFieldName(fieldName);
278 super.appendFieldStart(buffer, FIELD_NAME_QUOTE + StringEscapeUtils.escapeJson(fieldName) + FIELD_NAME_QUOTE);
279 }
280
281 /**
282 * Appends the given String enclosed in double-quotes to the given StringBuffer.
283 *
284 * @param buffer The StringBuffer to append the value to.
285 * @param value The value to append.
286 */
287 private void appendValueAsString(final StringBuffer buffer, final String value) {
288 buffer.append('"').append(StringEscapeUtils.escapeJson(value)).append('"');
289 }
290
291 private void checkAppendInput(final String fieldName, final Boolean fullDetail) {
292 checkFieldName(fieldName);
293 checkIsFullDetail(fullDetail);
294 }
295
296 private void checkFieldName(final String fieldName) {
297 if (fieldName == null) {
298 throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle");
299 }
300 }
301
302 private void checkIsFullDetail(final Boolean fullDetail) {
303 if (!isFullDetail(fullDetail)) {
304 throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle");
305 }
306 }
307
308 private boolean isJsonArray(final String valueAsString) {
309 return valueAsString.startsWith(getArrayStart()) && valueAsString.endsWith(getArrayEnd());
310 }
311
312 private boolean isJsonObject(final String valueAsString) {
313 return valueAsString.startsWith(getContentStart()) && valueAsString.endsWith(getContentEnd());
314 }
315
316 /**
317 * Ensure Singleton after serialization.
318 *
319 * @return The singleton
320 */
321 private Object readResolve() {
322 return JSON_STYLE;
323 }
324 }
325
326 /**
327 * {@link ToStringStyle} that outputs on multiple lines.
328 *
329 * <p>
330 * This is an inner class rather than using {@link StandardToStringStyle} to ensure its immutability.
331 * </p>
332 */
333 private static final class MultiLineToStringStyle extends ToStringStyle {
334
335 private static final long serialVersionUID = 1L;
336
337 /**
338 * Constructs a new instance.
339 *
340 * <p>
341 * Use the static constant rather than instantiating.
342 * </p>
343 */
344 MultiLineToStringStyle() {
345 setContentStart("[");
346 setFieldSeparator(System.lineSeparator() + " ");
347 setFieldSeparatorAtStart(true);
348 setContentEnd(System.lineSeparator() + "]");
349 }
350
351 /**
352 * Ensure Singleton after serialization.
353 *
354 * @return The singleton.
355 */
356 private Object readResolve() {
357 return MULTI_LINE_STYLE;
358 }
359 }
360
361 /**
362 * {@link ToStringStyle} that does not print out the class name and identity hash code but prints content start and field names.
363 *
364 * <p>
365 * This is an inner class rather than using {@link StandardToStringStyle} to ensure its immutability.
366 * </p>
367 */
368 private static final class NoClassNameToStringStyle extends ToStringStyle {
369
370 private static final long serialVersionUID = 1L;
371
372 /**
373 * Constructs a new instance.
374 *
375 * <p>
376 * Use the static constant rather than instantiating.
377 * </p>
378 */
379 NoClassNameToStringStyle() {
380 setUseClassName(false);
381 setUseIdentityHashCode(false);
382 }
383
384 /**
385 * Ensure Singleton after serialization.
386 *
387 * @return The singleton
388 */
389 private Object readResolve() {
390 return NO_CLASS_NAME_STYLE;
391 }
392 }
393
394 /**
395 * {@link ToStringStyle} that does not print out the field names.
396 *
397 * <p>
398 * This is an inner class rather than using {@link StandardToStringStyle} to ensure its immutability.
399 * </p>
400 */
401 private static final class NoFieldNameToStringStyle extends ToStringStyle {
402
403 private static final long serialVersionUID = 1L;
404
405 /**
406 * Constructs a new instance.
407 *
408 * <p>
409 * Use the static constant rather than instantiating.
410 * </p>
411 */
412 NoFieldNameToStringStyle() {
413 setUseFieldNames(false);
414 }
415
416 /**
417 * Ensure Singleton after serialization.
418 *
419 * @return The singleton
420 */
421 private Object readResolve() {
422 return NO_FIELD_NAMES_STYLE;
423 }
424 }
425
426 /**
427 * {@link ToStringStyle} that prints out the short class name and no identity hash code.
428 *
429 * <p>
430 * This is an inner class rather than using {@link StandardToStringStyle} to ensure its immutability.
431 * </p>
432 */
433 private static final class ShortPrefixToStringStyle extends ToStringStyle {
434
435 private static final long serialVersionUID = 1L;
436
437 /**
438 * Constructs a new instance.
439 *
440 * <p>
441 * Use the static constant rather than instantiating.
442 * </p>
443 */
444 ShortPrefixToStringStyle() {
445 setUseShortClassName(true);
446 setUseIdentityHashCode(false);
447 }
448
449 /**
450 * Ensure {@code Singleton} after serialization.
451 *
452 * @return The singleton.
453 */
454 private Object readResolve() {
455 return SHORT_PREFIX_STYLE;
456 }
457 }
458
459 /**
460 * {@link ToStringStyle} that does not print out the class name, identity hash code, content start or field name.
461 *
462 * <p>
463 * This is an inner class rather than using {@link StandardToStringStyle} to ensure its immutability.
464 * </p>
465 */
466 private static final class SimpleToStringStyle extends ToStringStyle {
467
468 private static final long serialVersionUID = 1L;
469
470 /**
471 * Constructs a new instance.
472 *
473 * <p>
474 * Use the static constant rather than instantiating.
475 * </p>
476 */
477 SimpleToStringStyle() {
478 setUseClassName(false);
479 setUseIdentityHashCode(false);
480 setUseFieldNames(false);
481 setContentStart(StringUtils.EMPTY);
482 setContentEnd(StringUtils.EMPTY);
483 }
484
485 /**
486 * Ensure <code>Singleton</code> after serialization.
487 *
488 * @return The singleton
489 */
490 private Object readResolve() {
491 return SIMPLE_STYLE;
492 }
493 }
494
495 /**
496 * Serialization version ID.
497 */
498 private static final long serialVersionUID = -2587890625525655916L;
499
500 /**
501 * The default toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
502 *
503 * <pre>
504 * Person@182f0db[name=John Doe,age=33,smoker=false]
505 * </pre>
506 */
507 public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
508
509 /**
510 * The multi line toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
511 *
512 * <pre>
513 * Person@182f0db[
514 * name=John Doe
515 * age=33
516 * smoker=false
517 * ]
518 * </pre>
519 */
520 public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
521
522 /**
523 * The no field names toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
524 *
525 * <pre>
526 * Person@182f0db[John Doe,33,false]
527 * </pre>
528 */
529 public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
530
531 /**
532 * The short prefix toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
533 *
534 * <pre>
535 * Person[name=John Doe,age=33,smoker=false]
536 * </pre>
537 *
538 * @since 2.1
539 */
540 public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
541
542 /**
543 * The simple toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
544 *
545 * <pre>
546 * John Doe,33,false
547 * </pre>
548 */
549 public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
550
551 /**
552 * The no class name toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
553 *
554 * <pre>
555 * [name=John Doe,age=33,smoker=false]
556 * </pre>
557 *
558 * @since 3.4
559 */
560 public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
561
562 /**
563 * The JSON toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
564 *
565 * <pre>
566 * {"name": "John Doe", "age": 33, "smoker": true}
567 * </pre>
568 *
569 * <strong>Note:</strong> Since field names are mandatory in JSON, this ToStringStyle will throw an {@link UnsupportedOperationException} if no field name
570 * is passed in while appending. Furthermore This ToStringStyle will only generate valid JSON if referenced objects also produce JSON when calling
571 * {@code toString()} on them.
572 *
573 * @since 3.4
574 * @see <a href="https://www.json.org/">json.org</a>
575 */
576 public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
577
578 /**
579 * A registry of objects used by {@code reflectionToString} methods to detect cyclical object references and avoid infinite loops.
580 * Identity-based comparison is required so that cyclic objects (e.g. an ArrayList whose hashCode() would recurse) can be
581 * registered and looked up without triggering infinite recursion through equals/hashCode.
582 */
583 private static final ThreadLocal<IdentityHashMap<Object, Object>> REGISTRY = ThreadLocal.withInitial(IdentityHashMap::new);
584 /*
585 * Note that objects of this class are generally shared between threads, so an instance variable would not be suitable here.
586 *
587 * In normal use the registry should always be left empty, because the caller should call toString() which will clean up.
588 *
589 * See LANG-792
590 */
591
592 /**
593 * A per-thread set of objects already rendered in detail during the current top-level
594 * {@code reflectionToString} call. Unlike {@link #REGISTRY}, which is a depth-first visit
595 * <em>stack</em> (entries are removed when a visit completes) and therefore only detects
596 * cycles, this set is only cleared when the top-level call completes. Styles that recurse
597 * into arbitrary object graphs (see {@link RecursiveToStringStyle}) consult it so that shared
598 * (acyclic) references are detailed at most once per top-level call, keeping traversal cost
599 * linear in the size of the object graph instead of exponential on reference diamonds.
600 * Identity-based for the same reason as {@link #REGISTRY}. Empty unless such a style is in use.
601 */
602 private static final ThreadLocal<IdentityHashMap<Object, Object>> VISITED = ThreadLocal.withInitial(IdentityHashMap::new);
603
604 /**
605 * Gets the registry of objects being traversed by the {@code reflectionToString} methods in the current thread.
606 *
607 * @return Set the registry of objects being traversed.
608 */
609 public static Map<Object, Object> getRegistry() {
610 return REGISTRY.get();
611 }
612
613 /**
614 * Tests whether the registry contains the given object. Used by the reflection methods to avoid infinite loops.
615 *
616 * @param value The object to lookup in the registry.
617 * @return boolean {@code true} if the registry contains the given object.
618 */
619 static boolean isRegistered(final Object value) {
620 return getRegistry().containsKey(value);
621 }
622
623 /**
624 * Tests whether the given object has already been rendered in detail during the current
625 * top-level {@code reflectionToString} call. Used by graph-recursing styles to avoid
626 * exponential re-traversal of shared (acyclic) references.
627 *
628 * @param value The object to look up in the visited set.
629 * @return {@code true} if the object was already visited in this top-level call.
630 */
631 static boolean isVisited(final Object value) {
632 return VISITED.get().containsKey(value);
633 }
634
635 /**
636 * Marks the given object as rendered in detail for the current top-level
637 * {@code reflectionToString} call. The mark is cleared when the top-level call completes
638 * (when the visit stack in {@link #REGISTRY} empties).
639 *
640 * @param value The object to mark as visited.
641 */
642 static void markVisited(final Object value) {
643 if (value != null) {
644 VISITED.get().put(value, null);
645 }
646 }
647
648 /**
649 * Registers the given object. Used by the reflection methods to avoid infinite loops.
650 *
651 * @param value The object to register.
652 */
653 static void register(final Object value) {
654 if (value != null) {
655 getRegistry().put(value, null);
656 }
657 }
658
659 /**
660 * Unregisters the given object.
661 *
662 * <p>
663 * Used by the reflection methods to avoid infinite loops.
664 * </p>
665 *
666 * @param value The object to unregister.
667 */
668 static void unregister(final Object value) {
669 if (value != null) {
670 final Map<Object, Object> m = getRegistry();
671 m.remove(value);
672 if (m.isEmpty()) {
673 REGISTRY.remove();
674 // The top-level reflectionToString call is complete: clear the visited set as well.
675 VISITED.remove();
676 }
677 }
678 }
679
680 /**
681 * Whether to use the field names, the default is {@code true}.
682 */
683 private boolean useFieldNames = true;
684
685 /**
686 * Whether to use the class name, the default is {@code true}.
687 */
688 private boolean useClassName = true;
689
690 /**
691 * Whether to use short class names, the default is {@code false}.
692 */
693 private boolean useShortClassName;
694
695 /**
696 * Whether to use the identity hash code, the default is {@code true}.
697 */
698 private boolean useIdentityHashCode = true;
699
700 /**
701 * The content start {@code '['}.
702 */
703 private String contentStart = "[";
704
705 /**
706 * The content end {@code ']'}.
707 */
708 private String contentEnd = "]";
709
710 /**
711 * The field name value separator {@code '='}.
712 */
713 private String fieldNameValueSeparator = "=";
714
715 /**
716 * Whether the field separator should be added before any other fields.
717 */
718 private boolean fieldSeparatorAtStart;
719
720 /**
721 * Whether the field separator should be added after any other fields.
722 */
723 private boolean fieldSeparatorAtEnd;
724
725 /**
726 * The field separator {@code ','}.
727 */
728 private String fieldSeparator = ",";
729
730 /**
731 * The array start <code>'{'</code>.
732 */
733 private String arrayStart = "{";
734
735 /**
736 * The array separator {@code ','}.
737 */
738 private String arraySeparator = ",";
739
740 /**
741 * The detail for array content.
742 */
743 private boolean arrayContentDetail = true;
744
745 /**
746 * The array end {@code '}'}.
747 */
748 private String arrayEnd = "}";
749
750 /**
751 * The value to use when fullDetail is {@code null}, the default value is {@code true}.
752 */
753 private boolean defaultFullDetail = true;
754
755 /**
756 * The {@code null} text {@code "<null>"}.
757 */
758 private String nullText = "<null>";
759
760 /**
761 * The summary size text start {@code "<size="}.
762 */
763 private String sizeStartText = "<size=";
764
765 /**
766 * The summary size text start {@code ">"}.
767 */
768 private String sizeEndText = ">";
769
770 /**
771 * The summary object text start {@code "<"}.
772 */
773 private String summaryObjectStartText = "<";
774
775 /**
776 * The summary object text start {@code ">"}.
777 */
778 private String summaryObjectEndText = ">";
779
780 /**
781 * Constructs a new instance.
782 */
783 protected ToStringStyle() {
784 }
785
786 /**
787 * Appends to the {@code toString} a {@code boolean} value.
788 *
789 * @param buffer The {@link StringBuffer} to populate.
790 * @param fieldName The field name.
791 * @param value The value to add to the {@code toString}.
792 */
793 public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
794 appendFieldStart(buffer, fieldName);
795 appendDetail(buffer, fieldName, value);
796 appendFieldEnd(buffer, fieldName);
797 }
798
799 /**
800 * Appends to the {@code toString} a {@code boolean} array.
801 *
802 * @param buffer The {@link StringBuffer} to populate.
803 * @param fieldName The field name.
804 * @param array The array to add to the toString.
805 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
806 */
807 public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
808 appendFieldStart(buffer, fieldName);
809 if (array == null) {
810 appendNullText(buffer, fieldName);
811 } else if (isFullDetail(fullDetail)) {
812 appendDetail(buffer, fieldName, array);
813 } else {
814 appendSummary(buffer, fieldName, array);
815 }
816 appendFieldEnd(buffer, fieldName);
817 }
818
819 /**
820 * Appends to the {@code toString} a {@code byte} value.
821 *
822 * @param buffer The {@link StringBuffer} to populate.
823 * @param fieldName The field name.
824 * @param value The value to add to the {@code toString}.
825 */
826 public void append(final StringBuffer buffer, final String fieldName, final byte value) {
827 appendFieldStart(buffer, fieldName);
828 appendDetail(buffer, fieldName, value);
829 appendFieldEnd(buffer, fieldName);
830 }
831
832 /**
833 * Appends to the {@code toString} a {@code byte} array.
834 *
835 * @param buffer The {@link StringBuffer} to populate.
836 * @param fieldName The field name.
837 * @param array The array to add to the {@code toString}.
838 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
839 */
840 public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
841 appendFieldStart(buffer, fieldName);
842 if (array == null) {
843 appendNullText(buffer, fieldName);
844 } else if (isFullDetail(fullDetail)) {
845 appendDetail(buffer, fieldName, array);
846 } else {
847 appendSummary(buffer, fieldName, array);
848 }
849 appendFieldEnd(buffer, fieldName);
850 }
851
852 /**
853 * Appends to the {@code toString} a {@code char} value.
854 *
855 * @param buffer The {@link StringBuffer} to populate.
856 * @param fieldName The field name.
857 * @param value The value to add to the {@code toString}.
858 */
859 public void append(final StringBuffer buffer, final String fieldName, final char value) {
860 appendFieldStart(buffer, fieldName);
861 appendDetail(buffer, fieldName, value);
862 appendFieldEnd(buffer, fieldName);
863 }
864
865 /**
866 * Appends to the {@code toString} a {@code char} array.
867 *
868 * @param buffer The {@link StringBuffer} to populate.
869 * @param fieldName The field name.
870 * @param array The array to add to the {@code toString}.
871 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
872 */
873 public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
874 appendFieldStart(buffer, fieldName);
875 if (array == null) {
876 appendNullText(buffer, fieldName);
877 } else if (isFullDetail(fullDetail)) {
878 appendDetail(buffer, fieldName, array);
879 } else {
880 appendSummary(buffer, fieldName, array);
881 }
882 appendFieldEnd(buffer, fieldName);
883 }
884
885 /**
886 * Appends to the {@code toString} a {@code double} value.
887 *
888 * @param buffer The {@link StringBuffer} to populate.
889 * @param fieldName The field name.
890 * @param value The value to add to the {@code toString}.
891 */
892 public void append(final StringBuffer buffer, final String fieldName, final double value) {
893 appendFieldStart(buffer, fieldName);
894 appendDetail(buffer, fieldName, value);
895 appendFieldEnd(buffer, fieldName);
896 }
897
898 /**
899 * Appends to the {@code toString} a {@code double} array.
900 *
901 * @param buffer The {@link StringBuffer} to populate.
902 * @param fieldName The field name.
903 * @param array The array to add to the toString.
904 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
905 */
906 public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
907 appendFieldStart(buffer, fieldName);
908 if (array == null) {
909 appendNullText(buffer, fieldName);
910 } else if (isFullDetail(fullDetail)) {
911 appendDetail(buffer, fieldName, array);
912 } else {
913 appendSummary(buffer, fieldName, array);
914 }
915 appendFieldEnd(buffer, fieldName);
916 }
917
918 /**
919 * Appends to the {@code toString} a {@code float} value.
920 *
921 * @param buffer The {@link StringBuffer} to populate.
922 * @param fieldName The field name.
923 * @param value The value to add to the {@code toString}.
924 */
925 public void append(final StringBuffer buffer, final String fieldName, final float value) {
926 appendFieldStart(buffer, fieldName);
927 appendDetail(buffer, fieldName, value);
928 appendFieldEnd(buffer, fieldName);
929 }
930
931 /**
932 * Appends to the {@code toString} a {@code float} array.
933 *
934 * @param buffer The {@link StringBuffer} to populate.
935 * @param fieldName The field name.
936 * @param array The array to add to the toString.
937 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
938 */
939 public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
940 appendFieldStart(buffer, fieldName);
941 if (array == null) {
942 appendNullText(buffer, fieldName);
943 } else if (isFullDetail(fullDetail)) {
944 appendDetail(buffer, fieldName, array);
945 } else {
946 appendSummary(buffer, fieldName, array);
947 }
948 appendFieldEnd(buffer, fieldName);
949 }
950
951 /**
952 * Appends to the {@code toString} an {@code int} value.
953 *
954 * @param buffer The {@link StringBuffer} to populate.
955 * @param fieldName The field name.
956 * @param value The value to add to the {@code toString}.
957 */
958 public void append(final StringBuffer buffer, final String fieldName, final int value) {
959 appendFieldStart(buffer, fieldName);
960 appendDetail(buffer, fieldName, value);
961 appendFieldEnd(buffer, fieldName);
962 }
963
964 /**
965 * Appends to the {@code toString} an {@code int} array.
966 *
967 * @param buffer The {@link StringBuffer} to populate.
968 * @param fieldName The field name.
969 * @param array The array to add to the {@code toString}.
970 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
971 */
972 public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
973 appendFieldStart(buffer, fieldName);
974 if (array == null) {
975 appendNullText(buffer, fieldName);
976 } else if (isFullDetail(fullDetail)) {
977 appendDetail(buffer, fieldName, array);
978 } else {
979 appendSummary(buffer, fieldName, array);
980 }
981 appendFieldEnd(buffer, fieldName);
982 }
983
984 /**
985 * Appends to the {@code toString} a {@code long} value.
986 *
987 * @param buffer The {@link StringBuffer} to populate.
988 * @param fieldName The field name.
989 * @param value The value to add to the {@code toString}.
990 */
991 public void append(final StringBuffer buffer, final String fieldName, final long value) {
992 appendFieldStart(buffer, fieldName);
993 appendDetail(buffer, fieldName, value);
994 appendFieldEnd(buffer, fieldName);
995 }
996
997 /**
998 * Appends to the {@code toString} a {@code long} array.
999 *
1000 * @param buffer The {@link StringBuffer} to populate.
1001 * @param fieldName The field name.
1002 * @param array The array to add to the {@code toString}.
1003 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
1004 */
1005 public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
1006 appendFieldStart(buffer, fieldName);
1007 if (array == null) {
1008 appendNullText(buffer, fieldName);
1009 } else if (isFullDetail(fullDetail)) {
1010 appendDetail(buffer, fieldName, array);
1011 } else {
1012 appendSummary(buffer, fieldName, array);
1013 }
1014 appendFieldEnd(buffer, fieldName);
1015 }
1016
1017 /**
1018 * Appends to the {@code toString} an {@link Object} value, printing the full {@code toString} of the {@link Object} passed in.
1019 *
1020 * @param buffer The {@link StringBuffer} to populate.
1021 * @param fieldName The field name.
1022 * @param value The value to add to the {@code toString}.
1023 * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
1024 */
1025 public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
1026 appendFieldStart(buffer, fieldName);
1027 if (value == null) {
1028 appendNullText(buffer, fieldName);
1029 } else {
1030 appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
1031 }
1032 appendFieldEnd(buffer, fieldName);
1033 }
1034
1035 /**
1036 * Appends to the {@code toString} an {@link Object} 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 Object[] 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 short} 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 short 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 short} 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 short[] 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} the class name.
1090 *
1091 * @param buffer The {@link StringBuffer} to populate.
1092 * @param object The {@link Object} whose name to output.
1093 */
1094 protected void appendClassName(final StringBuffer buffer, final Object object) {
1095 if (isUseClassName() && object != null) {
1096 register(object);
1097 if (isUseShortClassName()) {
1098 buffer.append(getShortClassName(object.getClass()));
1099 } else {
1100 buffer.append(object.getClass().getName());
1101 }
1102 }
1103 }
1104
1105 /**
1106 * Appends to the {@code toString} the content end.
1107 *
1108 * @param buffer The {@link StringBuffer} to populate.
1109 */
1110 protected void appendContentEnd(final StringBuffer buffer) {
1111 buffer.append(getContentEnd());
1112 }
1113
1114 /**
1115 * Appends to the {@code toString} the content start.
1116 *
1117 * @param buffer The {@link StringBuffer} to populate.
1118 */
1119 protected void appendContentStart(final StringBuffer buffer) {
1120 buffer.append(getContentStart());
1121 }
1122
1123 /**
1124 * Appends to the {@code toString} an {@link Object} value that has been detected to participate in a cycle. This implementation will print the standard
1125 * string value of the value.
1126 *
1127 * @param buffer The {@link StringBuffer} to populate.
1128 * @param fieldName The field name, typically not used as already appended
1129 * @param value The value to add to the {@code toString}, not {@code null}.
1130 * @since 2.2
1131 */
1132 protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
1133 ObjectUtils.identityToString(buffer, value);
1134 }
1135
1136 /**
1137 * Appends to the {@code toString} a {@code boolean} value.
1138 *
1139 * @param buffer The {@link StringBuffer} to populate.
1140 * @param fieldName The field name, typically not used as already appended.
1141 * @param value The value to add to the {@code toString}.
1142 */
1143 protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
1144 buffer.append(value);
1145 }
1146
1147 /**
1148 * Appends to the {@code toString} the detail of a {@code boolean} 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}, not {@code null}.
1153 */
1154 protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1155 buffer.append(getArrayStart());
1156 for (int i = 0; i < array.length; i++) {
1157 if (i > 0) {
1158 buffer.append(getArraySeparator());
1159 }
1160 appendDetail(buffer, fieldName, array[i]);
1161 }
1162 buffer.append(getArrayEnd());
1163 }
1164
1165 /**
1166 * Appends to the {@code toString} a {@code byte} value.
1167 *
1168 * @param buffer The {@link StringBuffer} to populate.
1169 * @param fieldName The field name, typically not used as already appended.
1170 * @param value The value to add to the {@code toString}.
1171 */
1172 protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
1173 buffer.append(value);
1174 }
1175
1176 /**
1177 * Appends to the {@code toString} the detail of a {@code byte} array.
1178 *
1179 * @param buffer The {@link StringBuffer} to populate.
1180 * @param fieldName The field name, typically not used as already appended.
1181 * @param array The array to add to the {@code toString}, not {@code null}.
1182 */
1183 protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
1184 buffer.append(getArrayStart());
1185 for (int i = 0; i < array.length; i++) {
1186 if (i > 0) {
1187 buffer.append(getArraySeparator());
1188 }
1189 appendDetail(buffer, fieldName, array[i]);
1190 }
1191 buffer.append(getArrayEnd());
1192 }
1193
1194 /**
1195 * Appends to the {@code toString} a {@code char} value.
1196 *
1197 * @param buffer The {@link StringBuffer} to populate.
1198 * @param fieldName The field name, typically not used as already appended.
1199 * @param value The value to add to the {@code toString}.
1200 */
1201 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
1202 buffer.append(value);
1203 }
1204
1205 /**
1206 * Appends to the {@code toString} the detail of a {@code char} array.
1207 *
1208 * @param buffer The {@link StringBuffer} to populate.
1209 * @param fieldName The field name, typically not used as already appended.
1210 * @param array The array to add to the {@code toString}, not {@code null}.
1211 */
1212 protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
1213 buffer.append(getArrayStart());
1214 for (int i = 0; i < array.length; i++) {
1215 if (i > 0) {
1216 buffer.append(getArraySeparator());
1217 }
1218 appendDetail(buffer, fieldName, array[i]);
1219 }
1220 buffer.append(getArrayEnd());
1221 }
1222
1223 /**
1224 * Appends to the {@code toString} a {@link Collection}.
1225 *
1226 * @param buffer The {@link StringBuffer} to populate.
1227 * @param fieldName The field name, typically not used as already appended.
1228 * @param coll The {@link Collection} to add to the {@code toString}, not {@code null}.
1229 */
1230 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
1231 buffer.append('['); // backward compatibility
1232 boolean first = true;
1233 for (final Object item : coll) {
1234 if (!first) {
1235 buffer.append(", "); // backward compatibility
1236 }
1237 first = false;
1238 if (item == null) {
1239 appendNullText(buffer, fieldName);
1240 } else {
1241 appendInternal(buffer, fieldName, item, true);
1242 }
1243 }
1244 buffer.append(']'); // backward compatibility
1245 }
1246
1247 /**
1248 * Appends to the {@code toString} a {@code double} value.
1249 *
1250 * @param buffer The {@link StringBuffer} to populate.
1251 * @param fieldName The field name, typically not used as already appended.
1252 * @param value The value to add to the {@code toString}.
1253 */
1254 protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
1255 buffer.append(value);
1256 }
1257
1258 /**
1259 * Appends to the {@code toString} the detail of a {@code double} array.
1260 *
1261 * @param buffer The {@link StringBuffer} to populate.
1262 * @param fieldName The field name, typically not used as already appended
1263 * @param array The array to add to the {@code toString}, not {@code null}.
1264 */
1265 protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
1266 buffer.append(getArrayStart());
1267 for (int i = 0; i < array.length; i++) {
1268 if (i > 0) {
1269 buffer.append(getArraySeparator());
1270 }
1271 appendDetail(buffer, fieldName, array[i]);
1272 }
1273 buffer.append(getArrayEnd());
1274 }
1275
1276 /**
1277 * Appends to the {@code toString} a {@code float} value.
1278 *
1279 * @param buffer The {@link StringBuffer} to populate.
1280 * @param fieldName The field name, typically not used as already appended.
1281 * @param value The value to add to the {@code toString}.
1282 */
1283 protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
1284 buffer.append(value);
1285 }
1286
1287 /**
1288 * Appends to the {@code toString} the detail of a {@code float} array.
1289 *
1290 * @param buffer The {@link StringBuffer} to populate.
1291 * @param fieldName The field name, typically not used as already appended.
1292 * @param array The array to add to the {@code toString}, not {@code null}.
1293 */
1294 protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
1295 buffer.append(getArrayStart());
1296 for (int i = 0; i < array.length; i++) {
1297 if (i > 0) {
1298 buffer.append(getArraySeparator());
1299 }
1300 appendDetail(buffer, fieldName, array[i]);
1301 }
1302 buffer.append(getArrayEnd());
1303 }
1304
1305 /**
1306 * Appends to the {@code toString} an {@code int} value.
1307 *
1308 * @param buffer The {@link StringBuffer} to populate.
1309 * @param fieldName The field name, typically not used as already appended.
1310 * @param value The value to add to the {@code toString}.
1311 */
1312 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
1313 buffer.append(value);
1314 }
1315
1316 /**
1317 * Appends to the {@code toString} the detail of an {@link Object} array item.
1318 *
1319 * @param buffer The {@link StringBuffer} to populate.
1320 * @param fieldName The field name, typically not used as already appended.
1321 * @param i The array item index to add.
1322 * @param item The array item to add.
1323 * @since 3.11
1324 */
1325 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int i, final Object item) {
1326 if (i > 0) {
1327 buffer.append(getArraySeparator());
1328 }
1329 if (item == null) {
1330 appendNullText(buffer, fieldName);
1331 } else {
1332 appendInternal(buffer, fieldName, item, isArrayContentDetail());
1333 }
1334 }
1335
1336 /**
1337 * Appends to the {@code toString} the detail of an {@code int} array.
1338 *
1339 * @param buffer The {@link StringBuffer} to populate.
1340 * @param fieldName The field name, typically not used as already appended.
1341 * @param array The array to add to the {@code toString}, not {@code null}.
1342 */
1343 protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
1344 buffer.append(getArrayStart());
1345 for (int i = 0; i < array.length; i++) {
1346 if (i > 0) {
1347 buffer.append(getArraySeparator());
1348 }
1349 appendDetail(buffer, fieldName, array[i]);
1350 }
1351 buffer.append(getArrayEnd());
1352 }
1353
1354 /**
1355 * Appends to the {@code toString} a {@code long} value.
1356 *
1357 * @param buffer The {@link StringBuffer} to populate.
1358 * @param fieldName The field name, typically not used as already appended.
1359 * @param value The value to add to the {@code toString}.
1360 */
1361 protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
1362 buffer.append(value);
1363 }
1364
1365 /**
1366 * Appends to the {@code toString} the detail of a {@code long} array.
1367 *
1368 * @param buffer The {@link StringBuffer} to populate.
1369 * @param fieldName The field name, typically not used as already appended.
1370 * @param array The array to add to the {@code toString}, not {@code null}.
1371 */
1372 protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
1373 buffer.append(getArrayStart());
1374 for (int i = 0; i < array.length; i++) {
1375 if (i > 0) {
1376 buffer.append(getArraySeparator());
1377 }
1378 appendDetail(buffer, fieldName, array[i]);
1379 }
1380 buffer.append(getArrayEnd());
1381 }
1382
1383 /**
1384 * Appends to the {@code toString} a {@link Map}.
1385 *
1386 * @param buffer The {@link StringBuffer} to populate.
1387 * @param fieldName The field name, typically not used as already appended.
1388 * @param map The {@link Map} to add to the {@code toString}, not {@code null}.
1389 */
1390 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
1391 buffer.append('{'); // backward compatibility
1392 boolean first = true;
1393 for (final Map.Entry<?, ?> item : map.entrySet()) {
1394 if (!first) {
1395 buffer.append(getArraySeparator());
1396 buffer.append(' '); // backward compatibility
1397 }
1398 first = false;
1399 if (item == null) {
1400 appendNullText(buffer, fieldName);
1401 } else {
1402 appendInternal(buffer, fieldName, item.getKey(), true);
1403 buffer.append(getFieldNameValueSeparator());
1404 appendInternal(buffer, fieldName, item.getValue(), true);
1405 }
1406 }
1407 buffer.append('}'); // backward compatibility
1408 }
1409
1410 /**
1411 * Appends to the {@code toString} an {@link Object} value, printing the full detail of the {@link Object}.
1412 *
1413 * @param buffer The {@link StringBuffer} to populate.
1414 * @param fieldName The field name, typically not used as already appended.
1415 * @param value The value to add to the {@code toString}, not {@code null}.
1416 */
1417 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
1418 buffer.append(value);
1419 }
1420
1421 /**
1422 * Appends to the {@code toString} the detail of an {@link Object} array.
1423 *
1424 * @param buffer The {@link StringBuffer} to populate.
1425 * @param fieldName The field name, typically not used as already appended.
1426 * @param array The array to add to the {@code toString}, not {@code null}.
1427 */
1428 protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
1429 buffer.append(getArrayStart());
1430 for (int i = 0; i < array.length; i++) {
1431 appendDetail(buffer, fieldName, i, array[i]);
1432 }
1433 buffer.append(getArrayEnd());
1434 }
1435
1436 /**
1437 * Appends to the {@code toString} a {@code short} value.
1438 *
1439 * @param buffer The {@link StringBuffer} to populate.
1440 * @param fieldName The field name, typically not used as already appended.
1441 * @param value The value to add to the {@code toString}.
1442 */
1443 protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
1444 buffer.append(value);
1445 }
1446
1447 /**
1448 * Appends to the {@code toString} the detail of a {@code short} array.
1449 *
1450 * @param buffer The {@link StringBuffer} to populate.
1451 * @param fieldName The field name, typically not used as already appended.
1452 * @param array The array to add to the {@code toString}, not {@code null}.
1453 */
1454 protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
1455 buffer.append(getArrayStart());
1456 for (int i = 0; i < array.length; i++) {
1457 if (i > 0) {
1458 buffer.append(getArraySeparator());
1459 }
1460 appendDetail(buffer, fieldName, array[i]);
1461 }
1462 buffer.append(getArrayEnd());
1463 }
1464
1465 /**
1466 * Appends to the {@code toString} the end of data indicator.
1467 *
1468 * @param buffer The {@link StringBuffer} to populate.
1469 * @param object The {@link Object} to build a {@code toString} for.
1470 */
1471 public void appendEnd(final StringBuffer buffer, final Object object) {
1472 try {
1473 if (!isFieldSeparatorAtEnd()) {
1474 removeLastFieldSeparator(buffer);
1475 }
1476 appendContentEnd(buffer);
1477 } finally {
1478 unregister(object);
1479 }
1480 }
1481
1482 /**
1483 * Appends to the {@code toString} the field end.
1484 *
1485 * @param buffer The {@link StringBuffer} to populate.
1486 * @param fieldName The field name, typically not used as already appended.
1487 */
1488 protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
1489 appendFieldSeparator(buffer);
1490 }
1491
1492 /**
1493 * Appends to the {@code toString} the field separator.
1494 *
1495 * @param buffer The {@link StringBuffer} to populate.
1496 */
1497 protected void appendFieldSeparator(final StringBuffer buffer) {
1498 buffer.append(getFieldSeparator());
1499 }
1500
1501 /**
1502 * Appends to the {@code toString} the field start.
1503 *
1504 * @param buffer The {@link StringBuffer} to populate.
1505 * @param fieldName The field name.
1506 */
1507 protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
1508 if (isUseFieldNames() && fieldName != null) {
1509 buffer.append(fieldName);
1510 buffer.append(getFieldNameValueSeparator());
1511 }
1512 }
1513
1514 /**
1515 * Appends the {@link System#identityHashCode(java.lang.Object)}.
1516 *
1517 * @param buffer The {@link StringBuffer} to populate.
1518 * @param object The {@link Object} whose id to output.
1519 */
1520 protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
1521 if (isUseIdentityHashCode() && object != null) {
1522 register(object);
1523 buffer.append('@');
1524 buffer.append(ObjectUtils.identityHashCodeHex(object));
1525 }
1526 }
1527
1528 /**
1529 * Appends to the {@code toString} an {@link Object}, correctly interpreting its type.
1530 *
1531 * <p>
1532 * 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
1533 * method.
1534 * </p>
1535 *
1536 * <p>
1537 * Either detail or summary views can be specified.
1538 * </p>
1539 *
1540 * <p>
1541 * If a cycle is detected, an object will be appended with the {@code Object.toString()} format.
1542 * </p>
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}, not {@code null}.
1547 * @param detail output detail or not.
1548 */
1549 protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
1550 if (isRegistered(value) && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
1551 appendCyclicObject(buffer, fieldName, value);
1552 return;
1553 }
1554 register(value);
1555 try {
1556 if (value instanceof Collection<?>) {
1557 if (detail) {
1558 appendDetail(buffer, fieldName, (Collection<?>) value);
1559 } else {
1560 appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
1561 }
1562 } else if (value instanceof Map<?, ?>) {
1563 if (detail) {
1564 appendDetail(buffer, fieldName, (Map<?, ?>) value);
1565 } else {
1566 appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
1567 }
1568 } else if (value instanceof long[]) {
1569 if (detail) {
1570 appendDetail(buffer, fieldName, (long[]) value);
1571 } else {
1572 appendSummary(buffer, fieldName, (long[]) value);
1573 }
1574 } else if (value instanceof int[]) {
1575 if (detail) {
1576 appendDetail(buffer, fieldName, (int[]) value);
1577 } else {
1578 appendSummary(buffer, fieldName, (int[]) value);
1579 }
1580 } else if (value instanceof short[]) {
1581 if (detail) {
1582 appendDetail(buffer, fieldName, (short[]) value);
1583 } else {
1584 appendSummary(buffer, fieldName, (short[]) value);
1585 }
1586 } else if (value instanceof byte[]) {
1587 if (detail) {
1588 appendDetail(buffer, fieldName, (byte[]) value);
1589 } else {
1590 appendSummary(buffer, fieldName, (byte[]) value);
1591 }
1592 } else if (value instanceof char[]) {
1593 if (detail) {
1594 appendDetail(buffer, fieldName, (char[]) value);
1595 } else {
1596 appendSummary(buffer, fieldName, (char[]) value);
1597 }
1598 } else if (value instanceof double[]) {
1599 if (detail) {
1600 appendDetail(buffer, fieldName, (double[]) value);
1601 } else {
1602 appendSummary(buffer, fieldName, (double[]) value);
1603 }
1604 } else if (value instanceof float[]) {
1605 if (detail) {
1606 appendDetail(buffer, fieldName, (float[]) value);
1607 } else {
1608 appendSummary(buffer, fieldName, (float[]) value);
1609 }
1610 } else if (value instanceof boolean[]) {
1611 if (detail) {
1612 appendDetail(buffer, fieldName, (boolean[]) value);
1613 } else {
1614 appendSummary(buffer, fieldName, (boolean[]) value);
1615 }
1616 } else if (ObjectUtils.isArray(value)) {
1617 if (detail) {
1618 appendDetail(buffer, fieldName, (Object[]) value);
1619 } else {
1620 appendSummary(buffer, fieldName, (Object[]) value);
1621 }
1622 } else if (detail) {
1623 appendDetail(buffer, fieldName, value);
1624 } else {
1625 appendSummary(buffer, fieldName, value);
1626 }
1627 } finally {
1628 unregister(value);
1629 }
1630 }
1631
1632 /**
1633 * Appends to the {@code toString} an indicator for {@code null}.
1634 *
1635 * <p>
1636 * The default indicator is {@code "<null>"}.
1637 * </p>
1638 *
1639 * @param buffer The {@link StringBuffer} to populate.
1640 * @param fieldName The field name, typically not used as already appended.
1641 */
1642 protected void appendNullText(final StringBuffer buffer, final String fieldName) {
1643 buffer.append(getNullText());
1644 }
1645
1646 /**
1647 * Appends to the {@code toString} the start of data indicator.
1648 *
1649 * @param buffer The {@link StringBuffer} to populate.
1650 * @param object The {@link Object} to build a {@code toString} for.
1651 */
1652 public void appendStart(final StringBuffer buffer, final Object object) {
1653 if (object != null) {
1654 appendClassName(buffer, object);
1655 appendIdentityHashCode(buffer, object);
1656 appendContentStart(buffer);
1657 if (isFieldSeparatorAtStart()) {
1658 appendFieldSeparator(buffer);
1659 }
1660 }
1661 }
1662
1663 /**
1664 * Appends to the {@code toString} a summary of a {@code boolean} array.
1665 *
1666 * @param buffer The {@link StringBuffer} to populate.
1667 * @param fieldName The field name, typically not used as already appended.
1668 * @param array The array to add to the {@code toString}, not {@code null}.
1669 */
1670 protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1671 appendSummarySize(buffer, fieldName, array.length);
1672 }
1673
1674 /**
1675 * Appends to the {@code toString} a summary of a {@code byte} array.
1676 *
1677 * @param buffer The {@link StringBuffer} to populate.
1678 * @param fieldName The field name, typically not used as already appended.
1679 * @param array The array to add to the {@code toString}, not {@code null}.
1680 */
1681 protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
1682 appendSummarySize(buffer, fieldName, array.length);
1683 }
1684
1685 /**
1686 * Appends to the {@code toString} a summary of a {@code char} array.
1687 *
1688 * @param buffer The {@link StringBuffer} to populate.
1689 * @param fieldName The field name, typically not used as already appended.
1690 * @param array The array to add to the {@code toString}, not {@code null}.
1691 */
1692 protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
1693 appendSummarySize(buffer, fieldName, array.length);
1694 }
1695
1696 /**
1697 * Appends to the {@code toString} a summary of a {@code double} array.
1698 *
1699 * @param buffer The {@link StringBuffer} to populate
1700 * @param fieldName The field name, typically not used as already appended
1701 * @param array The array to add to the {@code toString}, not {@code null}
1702 */
1703 protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
1704 appendSummarySize(buffer, fieldName, array.length);
1705 }
1706
1707 /**
1708 * Appends to the {@code toString} a summary of a {@code float} array.
1709 *
1710 * @param buffer The {@link StringBuffer} to populate.
1711 * @param fieldName The field name, typically not used as already appended.
1712 * @param array The array to add to the {@code toString}, not {@code null}.
1713 */
1714 protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
1715 appendSummarySize(buffer, fieldName, array.length);
1716 }
1717
1718 /**
1719 * Appends to the {@code toString} a summary of an {@code int} array.
1720 *
1721 * @param buffer The {@link StringBuffer} to populate.
1722 * @param fieldName The field name, typically not used as already appended.
1723 * @param array The array to add to the {@code toString}, not {@code null}.
1724 */
1725 protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
1726 appendSummarySize(buffer, fieldName, array.length);
1727 }
1728
1729 /**
1730 * Appends to the {@code toString} a summary of a {@code long} array.
1731 *
1732 * @param buffer The {@link StringBuffer} to populate.
1733 * @param fieldName The field name, typically not used as already appended.
1734 * @param array The array to add to the {@code toString}, not {@code null}.
1735 */
1736 protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
1737 appendSummarySize(buffer, fieldName, array.length);
1738 }
1739
1740 /**
1741 * Appends to the {@code toString} an {@link Object} value, printing a summary of the {@link Object}.
1742 *
1743 * @param buffer The {@link StringBuffer} to populate.
1744 * @param fieldName The field name, typically not used as already appended.
1745 * @param value The value to add to the {@code toString}, not {@code null}.
1746 */
1747 protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
1748 buffer.append(getSummaryObjectStartText());
1749 buffer.append(getShortClassName(value.getClass()));
1750 buffer.append(getSummaryObjectEndText());
1751 }
1752
1753 /**
1754 * Appends to the {@code toString} a summary of an {@link Object} array.
1755 *
1756 * @param buffer The {@link StringBuffer} to populate.
1757 * @param fieldName The field name, typically not used as already appended.
1758 * @param array The array to add to the {@code toString}, not {@code null}.
1759 */
1760 protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
1761 appendSummarySize(buffer, fieldName, array.length);
1762 }
1763
1764 /**
1765 * Appends to the {@code toString} a summary of a {@code short} array.
1766 *
1767 * @param buffer The {@link StringBuffer} to populate.
1768 * @param fieldName The field name, typically not used as already appended.
1769 * @param array The array to add to the {@code toString}, not {@code null}.
1770 */
1771 protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
1772 appendSummarySize(buffer, fieldName, array.length);
1773 }
1774
1775 /**
1776 * Appends to the {@code toString} a size summary.
1777 *
1778 * <p>
1779 * The size summary is used to summarize the contents of {@link Collection}s, {@link Map}s and arrays.
1780 * </p>
1781 *
1782 * <p>
1783 * The output consists of a prefix, the passed in size and a suffix.
1784 * </p>
1785 *
1786 * <p>
1787 * The default format is {@code "<size=n>"}.
1788 * </p>
1789 *
1790 * @param buffer The {@link StringBuffer} to populate.
1791 * @param fieldName The field name, typically not used as already appended.
1792 * @param size The size to append.
1793 */
1794 protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
1795 buffer.append(getSizeStartText());
1796 buffer.append(size);
1797 buffer.append(getSizeEndText());
1798 }
1799
1800 /**
1801 * Appends to the {@code toString} the superclass toString.
1802 * <p>
1803 * NOTE: It assumes that the toString has been created from the same ToStringStyle.
1804 * </p>
1805 *
1806 * <p>
1807 * A {@code null} {@code superToString} is ignored.
1808 * </p>
1809 *
1810 * @param buffer The {@link StringBuffer} to populate.
1811 * @param superToString The {@code super.toString()}.
1812 * @since 2.0
1813 */
1814 public void appendSuper(final StringBuffer buffer, final String superToString) {
1815 appendToString(buffer, superToString);
1816 }
1817
1818 /**
1819 * Appends to the {@code toString} another toString.
1820 * <p>
1821 * NOTE: It assumes that the toString has been created from the same ToStringStyle.
1822 * </p>
1823 *
1824 * <p>
1825 * A {@code null} {@code toString} is ignored.
1826 * </p>
1827 *
1828 * @param buffer The {@link StringBuffer} to populate.
1829 * @param toString The additional {@code toString}.
1830 * @since 2.0
1831 */
1832 public void appendToString(final StringBuffer buffer, final String toString) {
1833 if (toString != null) {
1834 final int pos1 = toString.indexOf(getContentStart()) + getContentStart().length();
1835 final int pos2 = toString.lastIndexOf(getContentEnd());
1836 if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
1837 if (isFieldSeparatorAtStart()) {
1838 removeLastFieldSeparator(buffer);
1839 }
1840 buffer.append(toString, pos1, pos2);
1841 appendFieldSeparator(buffer);
1842 }
1843 }
1844 }
1845
1846 /**
1847 * Gets the array end text.
1848 *
1849 * @return The current array end text.
1850 */
1851 protected String getArrayEnd() {
1852 return arrayEnd;
1853 }
1854
1855 /**
1856 * Gets the array separator text.
1857 *
1858 * @return The current array separator text.
1859 */
1860 protected String getArraySeparator() {
1861 return arraySeparator;
1862 }
1863
1864 /**
1865 * Gets the array start text.
1866 *
1867 * @return The current array start text.
1868 */
1869 protected String getArrayStart() {
1870 return arrayStart;
1871 }
1872
1873 /**
1874 * Gets the content end text.
1875 *
1876 * @return The current content end text.
1877 */
1878 protected String getContentEnd() {
1879 return contentEnd;
1880 }
1881
1882 /**
1883 * Gets the content start text.
1884 *
1885 * @return The current content start text.
1886 */
1887 protected String getContentStart() {
1888 return contentStart;
1889 }
1890
1891 /**
1892 * Gets the field name value separator text.
1893 *
1894 * @return The current field name value separator text.
1895 */
1896 protected String getFieldNameValueSeparator() {
1897 return fieldNameValueSeparator;
1898 }
1899
1900 /**
1901 * Gets the field separator text.
1902 *
1903 * @return The current field separator text.
1904 */
1905 protected String getFieldSeparator() {
1906 return fieldSeparator;
1907 }
1908
1909 /**
1910 * Gets the text to output when {@code null} found.
1911 *
1912 * @return The current text to output when null found.
1913 */
1914 protected String getNullText() {
1915 return nullText;
1916 }
1917
1918 /**
1919 * Gets the short class name for a class.
1920 *
1921 * <p>
1922 * The short class name is the class name excluding the package name.
1923 * </p>
1924 *
1925 * @param cls The {@link Class} to get the short name of.
1926 * @return The short name.
1927 */
1928 protected String getShortClassName(final Class<?> cls) {
1929 return ClassUtils.getShortClassName(cls);
1930 }
1931
1932 /**
1933 * Gets the end text to output when a {@link Collection}, {@link Map} or array size is output.
1934 *
1935 * <p>
1936 * This is output after the size value.
1937 * </p>
1938 *
1939 * @return The current end of size text.
1940 */
1941 protected String getSizeEndText() {
1942 return sizeEndText;
1943 }
1944
1945 /**
1946 * Gets the start text to output when a {@link Collection}, {@link Map} or array size is output.
1947 *
1948 * <p>
1949 * This is output before the size value.
1950 * </p>
1951 *
1952 * @return The current start of size text.
1953 */
1954 protected String getSizeStartText() {
1955 return sizeStartText;
1956 }
1957
1958 /**
1959 * Gets the end text to output when an {@link Object} is output in summary mode.
1960 *
1961 * <p>
1962 * This is output after the size value.
1963 * </p>
1964 *
1965 * @return The current end of summary text.
1966 */
1967 protected String getSummaryObjectEndText() {
1968 return summaryObjectEndText;
1969 }
1970
1971 /**
1972 * Gets the start text to output when an {@link Object} is output in summary mode.
1973 *
1974 * <p>
1975 * This is output before the size value.
1976 * </p>
1977 *
1978 * @return The current start of summary text.
1979 */
1980 protected String getSummaryObjectStartText() {
1981 return summaryObjectStartText;
1982 }
1983
1984 /**
1985 * Tests whether to output array content detail.
1986 *
1987 * @return The current array content detail setting.
1988 */
1989 protected boolean isArrayContentDetail() {
1990 return arrayContentDetail;
1991 }
1992
1993 /**
1994 * Tests whether full detail is used when the caller does not specify a detail level.
1995 *
1996 * @return The current defaultFullDetail flag.
1997 */
1998 protected boolean isDefaultFullDetail() {
1999 return defaultFullDetail;
2000 }
2001
2002 /**
2003 * Tests whether the field separator should be added at the end of each buffer.
2004 *
2005 * @return fieldSeparatorAtEnd flag.
2006 * @since 2.0
2007 */
2008 protected boolean isFieldSeparatorAtEnd() {
2009 return fieldSeparatorAtEnd;
2010 }
2011
2012 /**
2013 * Tests whether the field separator should be added at the start of each buffer.
2014 *
2015 * @return The fieldSeparatorAtStart flag.
2016 * @since 2.0
2017 */
2018 protected boolean isFieldSeparatorAtStart() {
2019 return fieldSeparatorAtStart;
2020 }
2021
2022 /**
2023 * Tests whether this field should be output in full detail.
2024 *
2025 * <p>
2026 * 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
2027 * 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
2028 * detail level is used.
2029 * </p>
2030 *
2031 * @param fullDetailRequest The detail level requested.
2032 * @return whether full detail is to be shown.
2033 */
2034 protected boolean isFullDetail(final Boolean fullDetailRequest) {
2035 if (fullDetailRequest == null) {
2036 return isDefaultFullDetail();
2037 }
2038 return fullDetailRequest.booleanValue();
2039 }
2040
2041 // Setters and getters for the customizable parts of the style
2042 // These methods are not expected to be overridden, except to make public
2043 // (They are not public so that immutable subclasses can be written)
2044 /**
2045 * Tests whether to use the class name.
2046 *
2047 * @return The current useClassName flag.
2048 */
2049 protected boolean isUseClassName() {
2050 return useClassName;
2051 }
2052
2053 /**
2054 * Tests whether to use the field names passed in.
2055 *
2056 * @return The current useFieldNames flag.
2057 */
2058 protected boolean isUseFieldNames() {
2059 return useFieldNames;
2060 }
2061
2062 /**
2063 * Tests whether to use the identity hash code.
2064 *
2065 * @return The current useIdentityHashCode flag.
2066 */
2067 protected boolean isUseIdentityHashCode() {
2068 return useIdentityHashCode;
2069 }
2070
2071 /**
2072 * Tests whether short class names should be output.
2073 *
2074 * @return The current useShortClassName flag.
2075 * @since 2.0
2076 */
2077 protected boolean isUseShortClassName() {
2078 return useShortClassName;
2079 }
2080
2081 /**
2082 * Appends to the {@code toString} the detail of an array type.
2083 *
2084 * @param buffer The {@link StringBuffer} to populate.
2085 * @param fieldName The field name, typically not used as already appended.
2086 * @param array The array to add to the {@code toString}, not {@code null}.
2087 * @since 2.0
2088 */
2089 protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
2090 buffer.append(getArrayStart());
2091 final int length = Array.getLength(array);
2092 for (int i = 0; i < length; i++) {
2093 appendDetail(buffer, fieldName, i, Array.get(array, i));
2094 }
2095 buffer.append(getArrayEnd());
2096 }
2097
2098 /**
2099 * Remove the last field separator from the buffer.
2100 *
2101 * @param buffer The {@link StringBuffer} to populate.
2102 * @since 2.0
2103 */
2104 protected void removeLastFieldSeparator(final StringBuffer buffer) {
2105 if (Strings.CS.endsWith(buffer, getFieldSeparator())) {
2106 buffer.setLength(buffer.length() - getFieldSeparator().length());
2107 }
2108 }
2109
2110 /**
2111 * Sets whether to output array content detail.
2112 *
2113 * @param arrayContentDetail The new arrayContentDetail flag.
2114 */
2115 protected void setArrayContentDetail(final boolean arrayContentDetail) {
2116 this.arrayContentDetail = arrayContentDetail;
2117 }
2118
2119 /**
2120 * Sets the array end text.
2121 *
2122 * <p>
2123 * {@code null} is accepted, but will be converted to an empty String.
2124 * </p>
2125 *
2126 * @param arrayEnd The new array end text.
2127 */
2128 protected void setArrayEnd(final String arrayEnd) {
2129 this.arrayEnd = ObjectUtils.toString(arrayEnd);
2130 }
2131
2132 /**
2133 * Sets the array separator text.
2134 *
2135 * <p>
2136 * {@code null} is accepted, but will be converted to an empty String.
2137 * </p>
2138 *
2139 * @param arraySeparator The new array separator text.
2140 */
2141 protected void setArraySeparator(final String arraySeparator) {
2142 this.arraySeparator = ObjectUtils.toString(arraySeparator);
2143 }
2144
2145 /**
2146 * Sets the array start text.
2147 *
2148 * <p>
2149 * {@code null} is accepted, but will be converted to an empty String.
2150 * </p>
2151 *
2152 * @param arrayStart The new array start text.
2153 */
2154 protected void setArrayStart(final String arrayStart) {
2155 this.arrayStart = ObjectUtils.toString(arrayStart);
2156 }
2157
2158 /**
2159 * Sets the content end text.
2160 *
2161 * <p>
2162 * {@code null} is accepted, but will be converted to an empty String.
2163 * </p>
2164 *
2165 * @param contentEnd The new content end text.
2166 */
2167 protected void setContentEnd(final String contentEnd) {
2168 this.contentEnd = ObjectUtils.toString(contentEnd);
2169 }
2170
2171 /**
2172 * Sets the content start text.
2173 *
2174 * <p>
2175 * {@code null} is accepted, but will be converted to an empty String.
2176 * </p>
2177 *
2178 * @param contentStart The new content start text.
2179 */
2180 protected void setContentStart(final String contentStart) {
2181 this.contentStart = ObjectUtils.toString(contentStart);
2182 }
2183
2184 /**
2185 * Sets whether to use full detail when the caller doesn't specify.
2186 *
2187 * @param defaultFullDetail The new defaultFullDetail flag.
2188 */
2189 protected void setDefaultFullDetail(final boolean defaultFullDetail) {
2190 this.defaultFullDetail = defaultFullDetail;
2191 }
2192
2193 /**
2194 * Sets the field name value separator text.
2195 *
2196 * <p>
2197 * {@code null} is accepted, but will be converted to an empty String.
2198 * </p>
2199 *
2200 * @param fieldNameValueSeparator The new field name value separator text.
2201 */
2202 protected void setFieldNameValueSeparator(final String fieldNameValueSeparator) {
2203 this.fieldNameValueSeparator = ObjectUtils.toString(fieldNameValueSeparator);
2204 }
2205
2206 /**
2207 * Sets the field separator text.
2208 *
2209 * <p>
2210 * {@code null} is accepted, but will be converted to an empty String.
2211 * </p>
2212 *
2213 * @param fieldSeparator The new field separator text.
2214 */
2215 protected void setFieldSeparator(final String fieldSeparator) {
2216 this.fieldSeparator = ObjectUtils.toString(fieldSeparator);
2217 }
2218
2219 /**
2220 * Sets whether the field separator should be added at the end of each buffer.
2221 *
2222 * @param fieldSeparatorAtEnd The fieldSeparatorAtEnd flag.
2223 * @since 2.0
2224 */
2225 protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
2226 this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
2227 }
2228
2229 /**
2230 * Sets whether the field separator should be added at the start of each buffer.
2231 *
2232 * @param fieldSeparatorAtStart The fieldSeparatorAtStart flag.
2233 * @since 2.0
2234 */
2235 protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
2236 this.fieldSeparatorAtStart = fieldSeparatorAtStart;
2237 }
2238
2239 /**
2240 * Sets the text to output when {@code null} found.
2241 *
2242 * <p>
2243 * {@code null} is accepted, but will be converted to an empty String.
2244 * </p>
2245 *
2246 * @param nullText The new text to output when null found.
2247 */
2248 protected void setNullText(final String nullText) {
2249 this.nullText = ObjectUtils.toString(nullText);
2250 }
2251
2252 /**
2253 * Sets the end text to output when a {@link Collection}, {@link Map} or array size is output.
2254 *
2255 * <p>
2256 * This is output after the size value.
2257 * </p>
2258 *
2259 * <p>
2260 * {@code null} is accepted, but will be converted to an empty String.
2261 * </p>
2262 *
2263 * @param sizeEndText The new end of size text.
2264 */
2265 protected void setSizeEndText(final String sizeEndText) {
2266 this.sizeEndText = ObjectUtils.toString(sizeEndText);
2267 }
2268
2269 /**
2270 * Sets the start text to output when a {@link Collection}, {@link Map} or array size is output.
2271 *
2272 * <p>
2273 * This is output before the size value.
2274 * </p>
2275 *
2276 * <p>
2277 * {@code null} is accepted, but will be converted to an empty String.
2278 * </p>
2279 *
2280 * @param sizeStartText The new start of size text.
2281 */
2282 protected void setSizeStartText(final String sizeStartText) {
2283 this.sizeStartText = ObjectUtils.toString(sizeStartText);
2284 }
2285
2286 /**
2287 * Sets the end text to output when an {@link Object} is output in summary mode.
2288 *
2289 * <p>
2290 * This is output after the size value.
2291 * </p>
2292 *
2293 * <p>
2294 * {@code null} is accepted, but will be converted to an empty String.
2295 * </p>
2296 *
2297 * @param summaryObjectEndText The new end of summary text.
2298 */
2299 protected void setSummaryObjectEndText(final String summaryObjectEndText) {
2300 this.summaryObjectEndText = ObjectUtils.toString(summaryObjectEndText);
2301 }
2302
2303 /**
2304 * Sets the start text to output when an {@link Object} is output in summary mode.
2305 *
2306 * <p>
2307 * This is output before the size value.
2308 * </p>
2309 *
2310 * <p>
2311 * {@code null} is accepted, but will be converted to an empty String.
2312 * </p>
2313 *
2314 * @param summaryObjectStartText The new start of summary text.
2315 */
2316 protected void setSummaryObjectStartText(final String summaryObjectStartText) {
2317 this.summaryObjectStartText = ObjectUtils.toString(summaryObjectStartText);
2318 }
2319
2320 /**
2321 * Sets whether to use the class name.
2322 *
2323 * @param useClassName The new useClassName flag.
2324 */
2325 protected void setUseClassName(final boolean useClassName) {
2326 this.useClassName = useClassName;
2327 }
2328
2329 /**
2330 * Sets whether to use the field names passed in.
2331 *
2332 * @param useFieldNames The new useFieldNames flag.
2333 */
2334 protected void setUseFieldNames(final boolean useFieldNames) {
2335 this.useFieldNames = useFieldNames;
2336 }
2337
2338 /**
2339 * Sets whether to use the identity hash code.
2340 *
2341 * @param useIdentityHashCode The new useIdentityHashCode flag.
2342 */
2343 protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
2344 this.useIdentityHashCode = useIdentityHashCode;
2345 }
2346
2347 /**
2348 * Sets whether to output short or long class names.
2349 *
2350 * @param useShortClassName The new useShortClassName flag.
2351 * @since 2.0
2352 */
2353 protected void setUseShortClassName(final boolean useShortClassName) {
2354 this.useShortClassName = useShortClassName;
2355 }
2356 }