View Javadoc
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 }