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.Map;
24  import java.util.Map.Entry;
25  import java.util.Objects;
26  import java.util.WeakHashMap;
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</ode> 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      * The default toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
501      *
502      * <pre>
503      * Person@182f0db[name=John Doe,age=33,smoker=false]
504      * </pre>
505      */
506     public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
507     /**
508      * The multi line toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
509      *
510      * <pre>
511      * Person@182f0db[
512      *   name=John Doe
513      *   age=33
514      *   smoker=false
515      * ]
516      * </pre>
517      */
518     public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
519     /**
520      * The no field names toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
521      *
522      * <pre>
523      * Person@182f0db[John Doe,33,false]
524      * </pre>
525      */
526     public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
527     /**
528      * The short prefix toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
529      *
530      * <pre>
531      * Person[name=John Doe,age=33,smoker=false]
532      * </pre>
533      *
534      * @since 2.1
535      */
536     public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
537     /**
538      * The simple toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
539      *
540      * <pre>
541      * John Doe,33,false
542      * </pre>
543      */
544     public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
545     /**
546      * The no class name toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
547      *
548      * <pre>
549      * [name=John Doe,age=33,smoker=false]
550      * </pre>
551      *
552      * @since 3.4
553      */
554     public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
555     /**
556      * The JSON toString style. Using the {@code Person} example from {@link ToStringBuilder}, the output would look like this:
557      *
558      * <pre>
559      * {"name": "John Doe", "age": 33, "smoker": true}
560      * </pre>
561      *
562      * <strong>Note:</strong> Since field names are mandatory in JSON, this ToStringStyle will throw an {@link UnsupportedOperationException} if no field name
563      * is passed in while appending. Furthermore This ToStringStyle will only generate valid JSON if referenced objects also produce JSON when calling
564      * {@code toString()} on them.
565      *
566      * @since 3.4
567      * @see <a href="https://www.json.org/">json.org</a>
568      */
569     public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
570     /**
571      * A registry of objects used by {@code reflectionToString} methods to detect cyclical object references and avoid infinite loops.
572      */
573     private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY = ThreadLocal.withInitial(WeakHashMap::new);
574     /*
575      * Note that objects of this class are generally shared between threads, so an instance variable would not be suitable here.
576      *
577      * In normal use the registry should always be left empty, because the caller should call toString() which will clean up.
578      *
579      * See LANG-792
580      */
581 
582     /**
583      * Gets the registry of objects being traversed by the {@code reflectionToString} methods in the current thread.
584      *
585      * @return Set the registry of objects being traversed.
586      */
587     public static Map<Object, Object> getRegistry() {
588         return REGISTRY.get();
589     }
590 
591     /**
592      * Tests whether the registry contains the given object. Used by the reflection methods to avoid infinite loops.
593      *
594      * @param value The object to lookup in the registry.
595      * @return boolean {@code true} if the registry contains the given object.
596      */
597     static boolean isRegistered(final Object value) {
598         return getRegistry().containsKey(value);
599     }
600 
601     /**
602      * Registers the given object. Used by the reflection methods to avoid infinite loops.
603      *
604      * @param value The object to register.
605      */
606     static void register(final Object value) {
607         if (value != null) {
608             getRegistry().put(value, null);
609         }
610     }
611 
612     /**
613      * Unregisters the given object.
614      *
615      * <p>
616      * Used by the reflection methods to avoid infinite loops.
617      * </p>
618      *
619      * @param value The object to unregister.
620      */
621     static void unregister(final Object value) {
622         if (value != null) {
623             final Map<Object, Object> m = getRegistry();
624             m.remove(value);
625             if (m.isEmpty()) {
626                 REGISTRY.remove();
627             }
628         }
629     }
630 
631     /**
632      * Whether to use the field names, the default is {@code true}.
633      */
634     private boolean useFieldNames = true;
635     /**
636      * Whether to use the class name, the default is {@code true}.
637      */
638     private boolean useClassName = true;
639     /**
640      * Whether to use short class names, the default is {@code false}.
641      */
642     private boolean useShortClassName;
643     /**
644      * Whether to use the identity hash code, the default is {@code true}.
645      */
646     private boolean useIdentityHashCode = true;
647     /**
648      * The content start {@code '['}.
649      */
650     private String contentStart = "[";
651     /**
652      * The content end {@code ']'}.
653      */
654     private String contentEnd = "]";
655     /**
656      * The field name value separator {@code '='}.
657      */
658     private String fieldNameValueSeparator = "=";
659     /**
660      * Whether the field separator should be added before any other fields.
661      */
662     private boolean fieldSeparatorAtStart;
663     /**
664      * Whether the field separator should be added after any other fields.
665      */
666     private boolean fieldSeparatorAtEnd;
667     /**
668      * The field separator {@code ','}.
669      */
670     private String fieldSeparator = ",";
671     /**
672      * The array start <code>'{'</code>.
673      */
674     private String arrayStart = "{";
675     /**
676      * The array separator {@code ','}.
677      */
678     private String arraySeparator = ",";
679     /**
680      * The detail for array content.
681      */
682     private boolean arrayContentDetail = true;
683     /**
684      * The array end {@code '}'}.
685      */
686     private String arrayEnd = "}";
687     /**
688      * The value to use when fullDetail is {@code null}, the default value is {@code true}.
689      */
690     private boolean defaultFullDetail = true;
691     /**
692      * The {@code null} text {@code "<null>"}.
693      */
694     private String nullText = "<null>";
695     /**
696      * The summary size text start {@code "<size="}.
697      */
698     private String sizeStartText = "<size=";
699     /**
700      * The summary size text start {@code ">"}.
701      */
702     private String sizeEndText = ">";
703     /**
704      * The summary object text start {@code "<"}.
705      */
706     private String summaryObjectStartText = "<";
707     /**
708      * The summary object text start {@code ">"}.
709      */
710     private String summaryObjectEndText = ">";
711 
712     /**
713      * Constructs a new instance.
714      */
715     protected ToStringStyle() {
716     }
717 
718     /**
719      * Appends to the {@code toString} a {@code boolean} value.
720      *
721      * @param buffer    the {@link StringBuffer} to populate.
722      * @param fieldName the field name.
723      * @param value     the value to add to the {@code toString}.
724      */
725     public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
726         appendFieldStart(buffer, fieldName);
727         appendDetail(buffer, fieldName, value);
728         appendFieldEnd(buffer, fieldName);
729     }
730 
731     /**
732      * Appends to the {@code toString} a {@code boolean} array.
733      *
734      * @param buffer     the {@link StringBuffer} to populate.
735      * @param fieldName  the field name.
736      * @param array      the array to add to the toString.
737      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
738      */
739     public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
740         appendFieldStart(buffer, fieldName);
741         if (array == null) {
742             appendNullText(buffer, fieldName);
743         } else if (isFullDetail(fullDetail)) {
744             appendDetail(buffer, fieldName, array);
745         } else {
746             appendSummary(buffer, fieldName, array);
747         }
748         appendFieldEnd(buffer, fieldName);
749     }
750 
751     /**
752      * Appends to the {@code toString} a {@code byte} value.
753      *
754      * @param buffer    the {@link StringBuffer} to populate.
755      * @param fieldName the field name.
756      * @param value     the value to add to the {@code toString}.
757      */
758     public void append(final StringBuffer buffer, final String fieldName, final byte value) {
759         appendFieldStart(buffer, fieldName);
760         appendDetail(buffer, fieldName, value);
761         appendFieldEnd(buffer, fieldName);
762     }
763 
764     /**
765      * Appends to the {@code toString} a {@code byte} array.
766      *
767      * @param buffer     the {@link StringBuffer} to populate.
768      * @param fieldName  the field name.
769      * @param array      the array to add to the {@code toString}.
770      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
771      */
772     public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
773         appendFieldStart(buffer, fieldName);
774         if (array == null) {
775             appendNullText(buffer, fieldName);
776         } else if (isFullDetail(fullDetail)) {
777             appendDetail(buffer, fieldName, array);
778         } else {
779             appendSummary(buffer, fieldName, array);
780         }
781         appendFieldEnd(buffer, fieldName);
782     }
783 
784     /**
785      * Appends to the {@code toString} a {@code char} value.
786      *
787      * @param buffer    the {@link StringBuffer} to populate.
788      * @param fieldName the field name.
789      * @param value     the value to add to the {@code toString}.
790      */
791     public void append(final StringBuffer buffer, final String fieldName, final char value) {
792         appendFieldStart(buffer, fieldName);
793         appendDetail(buffer, fieldName, value);
794         appendFieldEnd(buffer, fieldName);
795     }
796 
797     /**
798      * Appends to the {@code toString} a {@code char} array.
799      *
800      * @param buffer     the {@link StringBuffer} to populate.
801      * @param fieldName  the field name.
802      * @param array      the array to add to the {@code toString}.
803      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
804      */
805     public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
806         appendFieldStart(buffer, fieldName);
807         if (array == null) {
808             appendNullText(buffer, fieldName);
809         } else if (isFullDetail(fullDetail)) {
810             appendDetail(buffer, fieldName, array);
811         } else {
812             appendSummary(buffer, fieldName, array);
813         }
814         appendFieldEnd(buffer, fieldName);
815     }
816 
817     /**
818      * Appends to the {@code toString} a {@code double} value.
819      *
820      * @param buffer    the {@link StringBuffer} to populate.
821      * @param fieldName the field name.
822      * @param value     the value to add to the {@code toString}.
823      */
824     public void append(final StringBuffer buffer, final String fieldName, final double value) {
825         appendFieldStart(buffer, fieldName);
826         appendDetail(buffer, fieldName, value);
827         appendFieldEnd(buffer, fieldName);
828     }
829 
830     /**
831      * Appends to the {@code toString} a {@code double} array.
832      *
833      * @param buffer     the {@link StringBuffer} to populate.
834      * @param fieldName  the field name.
835      * @param array      the array to add to the toString.
836      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
837      */
838     public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
839         appendFieldStart(buffer, fieldName);
840         if (array == null) {
841             appendNullText(buffer, fieldName);
842         } else if (isFullDetail(fullDetail)) {
843             appendDetail(buffer, fieldName, array);
844         } else {
845             appendSummary(buffer, fieldName, array);
846         }
847         appendFieldEnd(buffer, fieldName);
848     }
849 
850     /**
851      * Appends to the {@code toString} a {@code float} value.
852      *
853      * @param buffer    the {@link StringBuffer} to populate.
854      * @param fieldName the field name.
855      * @param value     the value to add to the {@code toString}.
856      */
857     public void append(final StringBuffer buffer, final String fieldName, final float value) {
858         appendFieldStart(buffer, fieldName);
859         appendDetail(buffer, fieldName, value);
860         appendFieldEnd(buffer, fieldName);
861     }
862 
863     /**
864      * Appends to the {@code toString} a {@code float} array.
865      *
866      * @param buffer     the {@link StringBuffer} to populate.
867      * @param fieldName  the field name.
868      * @param array      the array to add to the toString.
869      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
870      */
871     public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
872         appendFieldStart(buffer, fieldName);
873         if (array == null) {
874             appendNullText(buffer, fieldName);
875         } else if (isFullDetail(fullDetail)) {
876             appendDetail(buffer, fieldName, array);
877         } else {
878             appendSummary(buffer, fieldName, array);
879         }
880         appendFieldEnd(buffer, fieldName);
881     }
882 
883     /**
884      * Appends to the {@code toString} an {@code int} value.
885      *
886      * @param buffer    the {@link StringBuffer} to populate.
887      * @param fieldName the field name.
888      * @param value     the value to add to the {@code toString}.
889      */
890     public void append(final StringBuffer buffer, final String fieldName, final int value) {
891         appendFieldStart(buffer, fieldName);
892         appendDetail(buffer, fieldName, value);
893         appendFieldEnd(buffer, fieldName);
894     }
895 
896     /**
897      * Appends to the {@code toString} an {@code int} array.
898      *
899      * @param buffer     the {@link StringBuffer} to populate.
900      * @param fieldName  the field name.
901      * @param array      the array to add to the {@code toString}.
902      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
903      */
904     public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
905         appendFieldStart(buffer, fieldName);
906         if (array == null) {
907             appendNullText(buffer, fieldName);
908         } else if (isFullDetail(fullDetail)) {
909             appendDetail(buffer, fieldName, array);
910         } else {
911             appendSummary(buffer, fieldName, array);
912         }
913         appendFieldEnd(buffer, fieldName);
914     }
915 
916     /**
917      * Appends to the {@code toString} a {@code long} value.
918      *
919      * @param buffer    the {@link StringBuffer} to populate.
920      * @param fieldName the field name.
921      * @param value     the value to add to the {@code toString}.
922      */
923     public void append(final StringBuffer buffer, final String fieldName, final long value) {
924         appendFieldStart(buffer, fieldName);
925         appendDetail(buffer, fieldName, value);
926         appendFieldEnd(buffer, fieldName);
927     }
928 
929     /**
930      * Appends to the {@code toString} a {@code long} array.
931      *
932      * @param buffer     the {@link StringBuffer} to populate.
933      * @param fieldName  the field name.
934      * @param array      the array to add to the {@code toString}.
935      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
936      */
937     public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
938         appendFieldStart(buffer, fieldName);
939         if (array == null) {
940             appendNullText(buffer, fieldName);
941         } else if (isFullDetail(fullDetail)) {
942             appendDetail(buffer, fieldName, array);
943         } else {
944             appendSummary(buffer, fieldName, array);
945         }
946         appendFieldEnd(buffer, fieldName);
947     }
948 
949     /**
950      * Appends to the {@code toString} an {@link Object} value, printing the full {@code toString} of the {@link Object} passed in.
951      *
952      * @param buffer     the {@link StringBuffer} to populate.
953      * @param fieldName  the field name.
954      * @param value      the value to add to the {@code toString}.
955      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
956      */
957     public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
958         appendFieldStart(buffer, fieldName);
959         if (value == null) {
960             appendNullText(buffer, fieldName);
961         } else {
962             appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
963         }
964         appendFieldEnd(buffer, fieldName);
965     }
966 
967     /**
968      * Appends to the {@code toString} an {@link Object} array.
969      *
970      * @param buffer     the {@link StringBuffer} to populate.
971      * @param fieldName  the field name.
972      * @param array      the array to add to the toString.
973      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
974      */
975     public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
976         appendFieldStart(buffer, fieldName);
977         if (array == null) {
978             appendNullText(buffer, fieldName);
979         } else if (isFullDetail(fullDetail)) {
980             appendDetail(buffer, fieldName, array);
981         } else {
982             appendSummary(buffer, fieldName, array);
983         }
984         appendFieldEnd(buffer, fieldName);
985     }
986 
987     /**
988      * Appends to the {@code toString} a {@code short} value.
989      *
990      * @param buffer    the {@link StringBuffer} to populate.
991      * @param fieldName the field name.
992      * @param value     the value to add to the {@code toString}.
993      */
994     public void append(final StringBuffer buffer, final String fieldName, final short value) {
995         appendFieldStart(buffer, fieldName);
996         appendDetail(buffer, fieldName, value);
997         appendFieldEnd(buffer, fieldName);
998     }
999 
1000     /**
1001      * Appends to the {@code toString} a {@code short} array.
1002      *
1003      * @param buffer     the {@link StringBuffer} to populate.
1004      * @param fieldName  the field name.
1005      * @param array      the array to add to the {@code toString}.
1006      * @param fullDetail {@code true} for detail, {@code false} for summary info, {@code null} for style decides.
1007      */
1008     public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
1009         appendFieldStart(buffer, fieldName);
1010         if (array == null) {
1011             appendNullText(buffer, fieldName);
1012         } else if (isFullDetail(fullDetail)) {
1013             appendDetail(buffer, fieldName, array);
1014         } else {
1015             appendSummary(buffer, fieldName, array);
1016         }
1017         appendFieldEnd(buffer, fieldName);
1018     }
1019 
1020     /**
1021      * Appends to the {@code toString} the class name.
1022      *
1023      * @param buffer the {@link StringBuffer} to populate.
1024      * @param object the {@link Object} whose name to output.
1025      */
1026     protected void appendClassName(final StringBuffer buffer, final Object object) {
1027         if (isUseClassName() && object != null) {
1028             register(object);
1029             if (isUseShortClassName()) {
1030                 buffer.append(getShortClassName(object.getClass()));
1031             } else {
1032                 buffer.append(object.getClass().getName());
1033             }
1034         }
1035     }
1036 
1037     /**
1038      * Appends to the {@code toString} the content end.
1039      *
1040      * @param buffer the {@link StringBuffer} to populate.
1041      */
1042     protected void appendContentEnd(final StringBuffer buffer) {
1043         buffer.append(getContentEnd());
1044     }
1045 
1046     /**
1047      * Appends to the {@code toString} the content start.
1048      *
1049      * @param buffer the {@link StringBuffer} to populate.
1050      */
1051     protected void appendContentStart(final StringBuffer buffer) {
1052         buffer.append(getContentStart());
1053     }
1054 
1055     /**
1056      * Appends to the {@code toString} an {@link Object} value that has been detected to participate in a cycle. This implementation will print the standard
1057      * string value of the value.
1058      *
1059      * @param buffer    the {@link StringBuffer} to populate.
1060      * @param fieldName the field name, typically not used as already appended
1061      * @param value     the value to add to the {@code toString}, not {@code null}.
1062      * @since 2.2
1063      */
1064     protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
1065         ObjectUtils.identityToString(buffer, value);
1066     }
1067 
1068     /**
1069      * Appends to the {@code toString} a {@code boolean} value.
1070      *
1071      * @param buffer    the {@link StringBuffer} to populate.
1072      * @param fieldName the field name, typically not used as already appended.
1073      * @param value     the value to add to the {@code toString}.
1074      */
1075     protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
1076         buffer.append(value);
1077     }
1078 
1079     /**
1080      * Appends to the {@code toString} the detail of a {@code boolean} array.
1081      *
1082      * @param buffer    the {@link StringBuffer} to populate.
1083      * @param fieldName the field name, typically not used as already appended.
1084      * @param array     the array to add to the {@code toString}, not {@code null}.
1085      */
1086     protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1087         buffer.append(getArrayStart());
1088         for (int i = 0; i < array.length; i++) {
1089             if (i > 0) {
1090                 buffer.append(getArraySeparator());
1091             }
1092             appendDetail(buffer, fieldName, array[i]);
1093         }
1094         buffer.append(getArrayEnd());
1095     }
1096 
1097     /**
1098      * Appends to the {@code toString} a {@code byte} value.
1099      *
1100      * @param buffer    the {@link StringBuffer} to populate.
1101      * @param fieldName the field name, typically not used as already appended.
1102      * @param value     the value to add to the {@code toString}.
1103      */
1104     protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
1105         buffer.append(value);
1106     }
1107 
1108     /**
1109      * Appends to the {@code toString} the detail of a {@code byte} array.
1110      *
1111      * @param buffer    the {@link StringBuffer} to populate.
1112      * @param fieldName the field name, typically not used as already appended.
1113      * @param array     the array to add to the {@code toString}, not {@code null}.
1114      */
1115     protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
1116         buffer.append(getArrayStart());
1117         for (int i = 0; i < array.length; i++) {
1118             if (i > 0) {
1119                 buffer.append(getArraySeparator());
1120             }
1121             appendDetail(buffer, fieldName, array[i]);
1122         }
1123         buffer.append(getArrayEnd());
1124     }
1125 
1126     /**
1127      * Appends to the {@code toString} a {@code char} value.
1128      *
1129      * @param buffer    the {@link StringBuffer} to populate.
1130      * @param fieldName the field name, typically not used as already appended.
1131      * @param value     the value to add to the {@code toString}.
1132      */
1133     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
1134         buffer.append(value);
1135     }
1136 
1137     /**
1138      * Appends to the {@code toString} the detail of a {@code char} array.
1139      *
1140      * @param buffer    the {@link StringBuffer} to populate.
1141      * @param fieldName the field name, typically not used as already appended.
1142      * @param array     the array to add to the {@code toString}, not {@code null}.
1143      */
1144     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
1145         buffer.append(getArrayStart());
1146         for (int i = 0; i < array.length; i++) {
1147             if (i > 0) {
1148                 buffer.append(getArraySeparator());
1149             }
1150             appendDetail(buffer, fieldName, array[i]);
1151         }
1152         buffer.append(getArrayEnd());
1153     }
1154 
1155     /**
1156      * Appends to the {@code toString} a {@link Collection}.
1157      *
1158      * @param buffer    the {@link StringBuffer} to populate.
1159      * @param fieldName the field name, typically not used as already appended.
1160      * @param coll      the {@link Collection} to add to the {@code toString}, not {@code null}.
1161      */
1162     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
1163         buffer.append(coll);
1164     }
1165 
1166     /**
1167      * Appends to the {@code toString} a {@code double} value.
1168      *
1169      * @param buffer    the {@link StringBuffer} to populate.
1170      * @param fieldName the field name, typically not used as already appended.
1171      * @param value     the value to add to the {@code toString}.
1172      */
1173     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
1174         buffer.append(value);
1175     }
1176 
1177     /**
1178      * Appends to the {@code toString} the detail of a {@code double} array.
1179      *
1180      * @param buffer    the {@link StringBuffer} to populate.
1181      * @param fieldName the field name, typically not used as already appended
1182      * @param array     the array to add to the {@code toString}, not {@code null}.
1183      */
1184     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
1185         buffer.append(getArrayStart());
1186         for (int i = 0; i < array.length; i++) {
1187             if (i > 0) {
1188                 buffer.append(getArraySeparator());
1189             }
1190             appendDetail(buffer, fieldName, array[i]);
1191         }
1192         buffer.append(getArrayEnd());
1193     }
1194 
1195     /**
1196      * Appends to the {@code toString} a {@code float} value.
1197      *
1198      * @param buffer    the {@link StringBuffer} to populate.
1199      * @param fieldName the field name, typically not used as already appended.
1200      * @param value     the value to add to the {@code toString}.
1201      */
1202     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
1203         buffer.append(value);
1204     }
1205 
1206     /**
1207      * Appends to the {@code toString} the detail of a {@code float} array.
1208      *
1209      * @param buffer    the {@link StringBuffer} to populate.
1210      * @param fieldName the field name, typically not used as already appended.
1211      * @param array     the array to add to the {@code toString}, not {@code null}.
1212      */
1213     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
1214         buffer.append(getArrayStart());
1215         for (int i = 0; i < array.length; i++) {
1216             if (i > 0) {
1217                 buffer.append(getArraySeparator());
1218             }
1219             appendDetail(buffer, fieldName, array[i]);
1220         }
1221         buffer.append(getArrayEnd());
1222     }
1223 
1224     /**
1225      * Appends to the {@code toString} an {@code int} value.
1226      *
1227      * @param buffer    the {@link StringBuffer} to populate.
1228      * @param fieldName the field name, typically not used as already appended.
1229      * @param value     the value to add to the {@code toString}.
1230      */
1231     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
1232         buffer.append(value);
1233     }
1234 
1235     /**
1236      * Appends to the {@code toString} the detail of an {@link Object} array item.
1237      *
1238      * @param buffer    the {@link StringBuffer} to populate.
1239      * @param fieldName the field name, typically not used as already appended.
1240      * @param i         the array item index to add.
1241      * @param item      the array item to add.
1242      * @since 3.11
1243      */
1244     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int i, final Object item) {
1245         if (i > 0) {
1246             buffer.append(getArraySeparator());
1247         }
1248         if (item == null) {
1249             appendNullText(buffer, fieldName);
1250         } else {
1251             appendInternal(buffer, fieldName, item, isArrayContentDetail());
1252         }
1253     }
1254 
1255     /**
1256      * Appends to the {@code toString} the detail of an {@code int} array.
1257      *
1258      * @param buffer    the {@link StringBuffer} to populate.
1259      * @param fieldName the field name, typically not used as already appended.
1260      * @param array     the array to add to the {@code toString}, not {@code null}.
1261      */
1262     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
1263         buffer.append(getArrayStart());
1264         for (int i = 0; i < array.length; i++) {
1265             if (i > 0) {
1266                 buffer.append(getArraySeparator());
1267             }
1268             appendDetail(buffer, fieldName, array[i]);
1269         }
1270         buffer.append(getArrayEnd());
1271     }
1272 
1273     /**
1274      * Appends to the {@code toString} a {@code long} value.
1275      *
1276      * @param buffer    the {@link StringBuffer} to populate.
1277      * @param fieldName the field name, typically not used as already appended.
1278      * @param value     the value to add to the {@code toString}.
1279      */
1280     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
1281         buffer.append(value);
1282     }
1283 
1284     /**
1285      * Appends to the {@code toString} the detail of a {@code long} array.
1286      *
1287      * @param buffer    the {@link StringBuffer} to populate.
1288      * @param fieldName the field name, typically not used as already appended.
1289      * @param array     the array to add to the {@code toString}, not {@code null}.
1290      */
1291     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
1292         buffer.append(getArrayStart());
1293         for (int i = 0; i < array.length; i++) {
1294             if (i > 0) {
1295                 buffer.append(getArraySeparator());
1296             }
1297             appendDetail(buffer, fieldName, array[i]);
1298         }
1299         buffer.append(getArrayEnd());
1300     }
1301 
1302     /**
1303      * Appends to the {@code toString} a {@link Map}.
1304      *
1305      * @param buffer    the {@link StringBuffer} to populate.
1306      * @param fieldName the field name, typically not used as already appended.
1307      * @param map       the {@link Map} to add to the {@code toString}, not {@code null}.
1308      */
1309     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
1310         buffer.append(map);
1311     }
1312 
1313     /**
1314      * Appends to the {@code toString} an {@link Object} value, printing the full detail of the {@link Object}.
1315      *
1316      * @param buffer    the {@link StringBuffer} to populate.
1317      * @param fieldName the field name, typically not used as already appended.
1318      * @param value     the value to add to the {@code toString}, not {@code null}.
1319      */
1320     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
1321         buffer.append(value);
1322     }
1323 
1324     /**
1325      * Appends to the {@code toString} the detail of an {@link Object} array.
1326      *
1327      * @param buffer    the {@link StringBuffer} to populate.
1328      * @param fieldName the field name, typically not used as already appended.
1329      * @param array     the array to add to the {@code toString}, not {@code null}.
1330      */
1331     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
1332         buffer.append(getArrayStart());
1333         for (int i = 0; i < array.length; i++) {
1334             appendDetail(buffer, fieldName, i, array[i]);
1335         }
1336         buffer.append(getArrayEnd());
1337     }
1338 
1339     /**
1340      * Appends to the {@code toString} a {@code short} value.
1341      *
1342      * @param buffer    the {@link StringBuffer} to populate.
1343      * @param fieldName the field name, typically not used as already appended.
1344      * @param value     the value to add to the {@code toString}.
1345      */
1346     protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
1347         buffer.append(value);
1348     }
1349 
1350     /**
1351      * Appends to the {@code toString} the detail of a {@code short} array.
1352      *
1353      * @param buffer    the {@link StringBuffer} to populate.
1354      * @param fieldName the field name, typically not used as already appended.
1355      * @param array     the array to add to the {@code toString}, not {@code null}.
1356      */
1357     protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
1358         buffer.append(getArrayStart());
1359         for (int i = 0; i < array.length; i++) {
1360             if (i > 0) {
1361                 buffer.append(getArraySeparator());
1362             }
1363             appendDetail(buffer, fieldName, array[i]);
1364         }
1365         buffer.append(getArrayEnd());
1366     }
1367 
1368     /**
1369      * Appends to the {@code toString} the end of data indicator.
1370      *
1371      * @param buffer the {@link StringBuffer} to populate.
1372      * @param object the {@link Object} to build a {@code toString} for.
1373      */
1374     public void appendEnd(final StringBuffer buffer, final Object object) {
1375         if (!isFieldSeparatorAtEnd()) {
1376             removeLastFieldSeparator(buffer);
1377         }
1378         appendContentEnd(buffer);
1379         unregister(object);
1380     }
1381 
1382     /**
1383      * Appends to the {@code toString} the field end.
1384      *
1385      * @param buffer    the {@link StringBuffer} to populate.
1386      * @param fieldName the field name, typically not used as already appended.
1387      */
1388     protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
1389         appendFieldSeparator(buffer);
1390     }
1391 
1392     /**
1393      * Appends to the {@code toString} the field separator.
1394      *
1395      * @param buffer the {@link StringBuffer} to populate.
1396      */
1397     protected void appendFieldSeparator(final StringBuffer buffer) {
1398         buffer.append(getFieldSeparator());
1399     }
1400 
1401     /**
1402      * Appends to the {@code toString} the field start.
1403      *
1404      * @param buffer    the {@link StringBuffer} to populate.
1405      * @param fieldName the field name.
1406      */
1407     protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
1408         if (isUseFieldNames() && fieldName != null) {
1409             buffer.append(fieldName);
1410             buffer.append(getFieldNameValueSeparator());
1411         }
1412     }
1413 
1414     /**
1415      * Appends the {@link System#identityHashCode(java.lang.Object)}.
1416      *
1417      * @param buffer the {@link StringBuffer} to populate.
1418      * @param object the {@link Object} whose id to output.
1419      */
1420     protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
1421         if (isUseIdentityHashCode() && object != null) {
1422             register(object);
1423             buffer.append('@');
1424             buffer.append(ObjectUtils.identityHashCodeHex(object));
1425         }
1426     }
1427 
1428     /**
1429      * Appends to the {@code toString} an {@link Object}, correctly interpreting its type.
1430      *
1431      * <p>
1432      * 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
1433      * method.
1434      * </p>
1435      *
1436      * <p>
1437      * Either detail or summary views can be specified.
1438      * </p>
1439      *
1440      * <p>
1441      * If a cycle is detected, an object will be appended with the {@code Object.toString()} format.
1442      * </p>
1443      *
1444      * @param buffer    the {@link StringBuffer} to populate.
1445      * @param fieldName the field name, typically not used as already appended.
1446      * @param value     the value to add to the {@code toString}, not {@code null}.
1447      * @param detail    output detail or not.
1448      */
1449     protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
1450         if (isRegistered(value) && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
1451             appendCyclicObject(buffer, fieldName, value);
1452             return;
1453         }
1454         register(value);
1455         try {
1456             if (value instanceof Collection<?>) {
1457                 if (detail) {
1458                     appendDetail(buffer, fieldName, (Collection<?>) value);
1459                 } else {
1460                     appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
1461                 }
1462             } else if (value instanceof Map<?, ?>) {
1463                 if (detail) {
1464                     appendDetail(buffer, fieldName, (Map<?, ?>) value);
1465                 } else {
1466                     appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
1467                 }
1468             } else if (value instanceof long[]) {
1469                 if (detail) {
1470                     appendDetail(buffer, fieldName, (long[]) value);
1471                 } else {
1472                     appendSummary(buffer, fieldName, (long[]) value);
1473                 }
1474             } else if (value instanceof int[]) {
1475                 if (detail) {
1476                     appendDetail(buffer, fieldName, (int[]) value);
1477                 } else {
1478                     appendSummary(buffer, fieldName, (int[]) value);
1479                 }
1480             } else if (value instanceof short[]) {
1481                 if (detail) {
1482                     appendDetail(buffer, fieldName, (short[]) value);
1483                 } else {
1484                     appendSummary(buffer, fieldName, (short[]) value);
1485                 }
1486             } else if (value instanceof byte[]) {
1487                 if (detail) {
1488                     appendDetail(buffer, fieldName, (byte[]) value);
1489                 } else {
1490                     appendSummary(buffer, fieldName, (byte[]) value);
1491                 }
1492             } else if (value instanceof char[]) {
1493                 if (detail) {
1494                     appendDetail(buffer, fieldName, (char[]) value);
1495                 } else {
1496                     appendSummary(buffer, fieldName, (char[]) value);
1497                 }
1498             } else if (value instanceof double[]) {
1499                 if (detail) {
1500                     appendDetail(buffer, fieldName, (double[]) value);
1501                 } else {
1502                     appendSummary(buffer, fieldName, (double[]) value);
1503                 }
1504             } else if (value instanceof float[]) {
1505                 if (detail) {
1506                     appendDetail(buffer, fieldName, (float[]) value);
1507                 } else {
1508                     appendSummary(buffer, fieldName, (float[]) value);
1509                 }
1510             } else if (value instanceof boolean[]) {
1511                 if (detail) {
1512                     appendDetail(buffer, fieldName, (boolean[]) value);
1513                 } else {
1514                     appendSummary(buffer, fieldName, (boolean[]) value);
1515                 }
1516             } else if (ObjectUtils.isArray(value)) {
1517                 if (detail) {
1518                     appendDetail(buffer, fieldName, (Object[]) value);
1519                 } else {
1520                     appendSummary(buffer, fieldName, (Object[]) value);
1521                 }
1522             } else if (detail) {
1523                 appendDetail(buffer, fieldName, value);
1524             } else {
1525                 appendSummary(buffer, fieldName, value);
1526             }
1527         } finally {
1528             unregister(value);
1529         }
1530     }
1531 
1532     /**
1533      * Appends to the {@code toString} an indicator for {@code null}.
1534      *
1535      * <p>
1536      * The default indicator is {@code "<null>"}.
1537      * </p>
1538      *
1539      * @param buffer    the {@link StringBuffer} to populate.
1540      * @param fieldName the field name, typically not used as already appended.
1541      */
1542     protected void appendNullText(final StringBuffer buffer, final String fieldName) {
1543         buffer.append(getNullText());
1544     }
1545 
1546     /**
1547      * Appends to the {@code toString} the start of data indicator.
1548      *
1549      * @param buffer the {@link StringBuffer} to populate.
1550      * @param object the {@link Object} to build a {@code toString} for.
1551      */
1552     public void appendStart(final StringBuffer buffer, final Object object) {
1553         if (object != null) {
1554             appendClassName(buffer, object);
1555             appendIdentityHashCode(buffer, object);
1556             appendContentStart(buffer);
1557             if (isFieldSeparatorAtStart()) {
1558                 appendFieldSeparator(buffer);
1559             }
1560         }
1561     }
1562 
1563     /**
1564      * Appends to the {@code toString} a summary of a {@code boolean} array.
1565      *
1566      * @param buffer    the {@link StringBuffer} to populate.
1567      * @param fieldName the field name, typically not used as already appended.
1568      * @param array     the array to add to the {@code toString}, not {@code null}.
1569      */
1570     protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1571         appendSummarySize(buffer, fieldName, array.length);
1572     }
1573 
1574     /**
1575      * Appends to the {@code toString} a summary of a {@code byte} array.
1576      *
1577      * @param buffer    the {@link StringBuffer} to populate.
1578      * @param fieldName the field name, typically not used as already appended.
1579      * @param array     the array to add to the {@code toString}, not {@code null}.
1580      */
1581     protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
1582         appendSummarySize(buffer, fieldName, array.length);
1583     }
1584 
1585     /**
1586      * Appends to the {@code toString} a summary of a {@code char} array.
1587      *
1588      * @param buffer    the {@link StringBuffer} to populate.
1589      * @param fieldName the field name, typically not used as already appended.
1590      * @param array     the array to add to the {@code toString}, not {@code null}.
1591      */
1592     protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
1593         appendSummarySize(buffer, fieldName, array.length);
1594     }
1595 
1596     /**
1597      * Appends to the {@code toString} a summary of a {@code double} array.
1598      *
1599      * @param buffer    the {@link StringBuffer} to populate
1600      * @param fieldName the field name, typically not used as already appended
1601      * @param array     the array to add to the {@code toString}, not {@code null}
1602      */
1603     protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
1604         appendSummarySize(buffer, fieldName, array.length);
1605     }
1606 
1607     /**
1608      * Appends to the {@code toString} a summary of a {@code float} array.
1609      *
1610      * @param buffer    the {@link StringBuffer} to populate.
1611      * @param fieldName the field name, typically not used as already appended.
1612      * @param array     the array to add to the {@code toString}, not {@code null}.
1613      */
1614     protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
1615         appendSummarySize(buffer, fieldName, array.length);
1616     }
1617 
1618     /**
1619      * Appends to the {@code toString} a summary of an {@code int} array.
1620      *
1621      * @param buffer    the {@link StringBuffer} to populate.
1622      * @param fieldName the field name, typically not used as already appended.
1623      * @param array     the array to add to the {@code toString}, not {@code null}.
1624      */
1625     protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
1626         appendSummarySize(buffer, fieldName, array.length);
1627     }
1628 
1629     /**
1630      * Appends to the {@code toString} a summary of a {@code long} array.
1631      *
1632      * @param buffer    the {@link StringBuffer} to populate.
1633      * @param fieldName the field name, typically not used as already appended.
1634      * @param array     the array to add to the {@code toString}, not {@code null}.
1635      */
1636     protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
1637         appendSummarySize(buffer, fieldName, array.length);
1638     }
1639 
1640     /**
1641      * Appends to the {@code toString} an {@link Object} value, printing a summary of the {@link Object}.
1642      *
1643      * @param buffer    the {@link StringBuffer} to populate.
1644      * @param fieldName the field name, typically not used as already appended.
1645      * @param value     the value to add to the {@code toString}, not {@code null}.
1646      */
1647     protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
1648         buffer.append(getSummaryObjectStartText());
1649         buffer.append(getShortClassName(value.getClass()));
1650         buffer.append(getSummaryObjectEndText());
1651     }
1652 
1653     /**
1654      * Appends to the {@code toString} a summary of an {@link Object} array.
1655      *
1656      * @param buffer    the {@link StringBuffer} to populate.
1657      * @param fieldName the field name, typically not used as already appended.
1658      * @param array     the array to add to the {@code toString}, not {@code null}.
1659      */
1660     protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
1661         appendSummarySize(buffer, fieldName, array.length);
1662     }
1663 
1664     /**
1665      * Appends to the {@code toString} a summary of a {@code short} array.
1666      *
1667      * @param buffer    the {@link StringBuffer} to populate.
1668      * @param fieldName the field name, typically not used as already appended.
1669      * @param array     the array to add to the {@code toString}, not {@code null}.
1670      */
1671     protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
1672         appendSummarySize(buffer, fieldName, array.length);
1673     }
1674 
1675     /**
1676      * Appends to the {@code toString} a size summary.
1677      *
1678      * <p>
1679      * The size summary is used to summarize the contents of {@link Collection}s, {@link Map}s and arrays.
1680      * </p>
1681      *
1682      * <p>
1683      * The output consists of a prefix, the passed in size and a suffix.
1684      * </p>
1685      *
1686      * <p>
1687      * The default format is {@code "<size=n>"}.
1688      * </p>
1689      *
1690      * @param buffer    the {@link StringBuffer} to populate.
1691      * @param fieldName the field name, typically not used as already appended.
1692      * @param size      the size to append.
1693      */
1694     protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
1695         buffer.append(getSizeStartText());
1696         buffer.append(size);
1697         buffer.append(getSizeEndText());
1698     }
1699 
1700     /**
1701      * Appends to the {@code toString} the superclass toString.
1702      * <p>
1703      * NOTE: It assumes that the toString has been created from the same ToStringStyle.
1704      * </p>
1705      *
1706      * <p>
1707      * A {@code null} {@code superToString} is ignored.
1708      * </p>
1709      *
1710      * @param buffer        the {@link StringBuffer} to populate.
1711      * @param superToString the {@code super.toString()}.
1712      * @since 2.0
1713      */
1714     public void appendSuper(final StringBuffer buffer, final String superToString) {
1715         appendToString(buffer, superToString);
1716     }
1717 
1718     /**
1719      * Appends to the {@code toString} another toString.
1720      * <p>
1721      * NOTE: It assumes that the toString has been created from the same ToStringStyle.
1722      * </p>
1723      *
1724      * <p>
1725      * A {@code null} {@code toString} is ignored.
1726      * </p>
1727      *
1728      * @param buffer   the {@link StringBuffer} to populate.
1729      * @param toString the additional {@code toString}.
1730      * @since 2.0
1731      */
1732     public void appendToString(final StringBuffer buffer, final String toString) {
1733         if (toString != null) {
1734             final int pos1 = toString.indexOf(getContentStart()) + getContentStart().length();
1735             final int pos2 = toString.lastIndexOf(getContentEnd());
1736             if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
1737                 if (isFieldSeparatorAtStart()) {
1738                     removeLastFieldSeparator(buffer);
1739                 }
1740                 buffer.append(toString, pos1, pos2);
1741                 appendFieldSeparator(buffer);
1742             }
1743         }
1744     }
1745 
1746     /**
1747      * Gets the array end text.
1748      *
1749      * @return the current array end text.
1750      */
1751     protected String getArrayEnd() {
1752         return arrayEnd;
1753     }
1754 
1755     /**
1756      * Gets the array separator text.
1757      *
1758      * @return the current array separator text.
1759      */
1760     protected String getArraySeparator() {
1761         return arraySeparator;
1762     }
1763 
1764     /**
1765      * Gets the array start text.
1766      *
1767      * @return the current array start text.
1768      */
1769     protected String getArrayStart() {
1770         return arrayStart;
1771     }
1772 
1773     /**
1774      * Gets the content end text.
1775      *
1776      * @return the current content end text.
1777      */
1778     protected String getContentEnd() {
1779         return contentEnd;
1780     }
1781 
1782     /**
1783      * Gets the content start text.
1784      *
1785      * @return the current content start text.
1786      */
1787     protected String getContentStart() {
1788         return contentStart;
1789     }
1790 
1791     /**
1792      * Gets the field name value separator text.
1793      *
1794      * @return the current field name value separator text.
1795      */
1796     protected String getFieldNameValueSeparator() {
1797         return fieldNameValueSeparator;
1798     }
1799 
1800     /**
1801      * Gets the field separator text.
1802      *
1803      * @return the current field separator text.
1804      */
1805     protected String getFieldSeparator() {
1806         return fieldSeparator;
1807     }
1808 
1809     /**
1810      * Gets the text to output when {@code null} found.
1811      *
1812      * @return the current text to output when null found.
1813      */
1814     protected String getNullText() {
1815         return nullText;
1816     }
1817 
1818     /**
1819      * Gets the short class name for a class.
1820      *
1821      * <p>
1822      * The short class name is the class name excluding the package name.
1823      * </p>
1824      *
1825      * @param cls the {@link Class} to get the short name of.
1826      * @return the short name.
1827      */
1828     protected String getShortClassName(final Class<?> cls) {
1829         return ClassUtils.getShortClassName(cls);
1830     }
1831 
1832     /**
1833      * Gets the end text to output when a {@link Collection}, {@link Map} or array size is output.
1834      *
1835      * <p>
1836      * This is output after the size value.
1837      * </p>
1838      *
1839      * @return the current end of size text.
1840      */
1841     protected String getSizeEndText() {
1842         return sizeEndText;
1843     }
1844 
1845     /**
1846      * Gets the start text to output when a {@link Collection}, {@link Map} or array size is output.
1847      *
1848      * <p>
1849      * This is output before the size value.
1850      * </p>
1851      *
1852      * @return the current start of size text.
1853      */
1854     protected String getSizeStartText() {
1855         return sizeStartText;
1856     }
1857 
1858     /**
1859      * Gets the end text to output when an {@link Object} is output in summary mode.
1860      *
1861      * <p>
1862      * This is output after the size value.
1863      * </p>
1864      *
1865      * @return the current end of summary text.
1866      */
1867     protected String getSummaryObjectEndText() {
1868         return summaryObjectEndText;
1869     }
1870 
1871     /**
1872      * Gets the start text to output when an {@link Object} is output in summary mode.
1873      *
1874      * <p>
1875      * This is output before the size value.
1876      * </p>
1877      *
1878      * @return the current start of summary text.
1879      */
1880     protected String getSummaryObjectStartText() {
1881         return summaryObjectStartText;
1882     }
1883 
1884     /**
1885      * Gets whether to output array content detail.
1886      *
1887      * @return the current array content detail setting.
1888      */
1889     protected boolean isArrayContentDetail() {
1890         return arrayContentDetail;
1891     }
1892 
1893     /**
1894      * Gets whether to use full detail when the caller doesn't specify.
1895      *
1896      * @return the current defaultFullDetail flag.
1897      */
1898     protected boolean isDefaultFullDetail() {
1899         return defaultFullDetail;
1900     }
1901 
1902     /**
1903      * Gets whether the field separator should be added at the end of each buffer.
1904      *
1905      * @return fieldSeparatorAtEnd flag.
1906      * @since 2.0
1907      */
1908     protected boolean isFieldSeparatorAtEnd() {
1909         return fieldSeparatorAtEnd;
1910     }
1911 
1912     /**
1913      * Gets whether the field separator should be added at the start of each buffer.
1914      *
1915      * @return the fieldSeparatorAtStart flag.
1916      * @since 2.0
1917      */
1918     protected boolean isFieldSeparatorAtStart() {
1919         return fieldSeparatorAtStart;
1920     }
1921 
1922     /**
1923      * Is this field to be output in full detail.
1924      *
1925      * <p>
1926      * 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
1927      * 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
1928      * detail level is used.
1929      * </p>
1930      *
1931      * @param fullDetailRequest the detail level requested.
1932      * @return whether full detail is to be shown.
1933      */
1934     protected boolean isFullDetail(final Boolean fullDetailRequest) {
1935         if (fullDetailRequest == null) {
1936             return isDefaultFullDetail();
1937         }
1938         return fullDetailRequest.booleanValue();
1939     }
1940 
1941     // Setters and getters for the customizable parts of the style
1942     // These methods are not expected to be overridden, except to make public
1943     // (They are not public so that immutable subclasses can be written)
1944     /**
1945      * Gets whether to use the class name.
1946      *
1947      * @return the current useClassName flag.
1948      */
1949     protected boolean isUseClassName() {
1950         return useClassName;
1951     }
1952 
1953     /**
1954      * Gets whether to use the field names passed in.
1955      *
1956      * @return the current useFieldNames flag.
1957      */
1958     protected boolean isUseFieldNames() {
1959         return useFieldNames;
1960     }
1961 
1962     /**
1963      * Gets whether to use the identity hash code.
1964      *
1965      * @return the current useIdentityHashCode flag.
1966      */
1967     protected boolean isUseIdentityHashCode() {
1968         return useIdentityHashCode;
1969     }
1970 
1971     /**
1972      * Gets whether to output short or long class names.
1973      *
1974      * @return the current useShortClassName flag.
1975      * @since 2.0
1976      */
1977     protected boolean isUseShortClassName() {
1978         return useShortClassName;
1979     }
1980 
1981     /**
1982      * Appends to the {@code toString} the detail of an array type.
1983      *
1984      * @param buffer    the {@link StringBuffer} to populate.
1985      * @param fieldName the field name, typically not used as already appended.
1986      * @param array     the array to add to the {@code toString}, not {@code null}.
1987      * @since 2.0
1988      */
1989     protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
1990         buffer.append(getArrayStart());
1991         final int length = Array.getLength(array);
1992         for (int i = 0; i < length; i++) {
1993             appendDetail(buffer, fieldName, i, Array.get(array, i));
1994         }
1995         buffer.append(getArrayEnd());
1996     }
1997 
1998     /**
1999      * Remove the last field separator from the buffer.
2000      *
2001      * @param buffer the {@link StringBuffer} to populate.
2002      * @since 2.0
2003      */
2004     protected void removeLastFieldSeparator(final StringBuffer buffer) {
2005         if (Strings.CS.endsWith(buffer, getFieldSeparator())) {
2006             buffer.setLength(buffer.length() - getFieldSeparator().length());
2007         }
2008     }
2009 
2010     /**
2011      * Sets whether to output array content detail.
2012      *
2013      * @param arrayContentDetail the new arrayContentDetail flag.
2014      */
2015     protected void setArrayContentDetail(final boolean arrayContentDetail) {
2016         this.arrayContentDetail = arrayContentDetail;
2017     }
2018 
2019     /**
2020      * Sets the array end text.
2021      *
2022      * <p>
2023      * {@code null} is accepted, but will be converted to an empty String.
2024      * </p>
2025      *
2026      * @param arrayEnd the new array end text.
2027      */
2028     protected void setArrayEnd(final String arrayEnd) {
2029         this.arrayEnd = ObjectUtils.toString(arrayEnd);
2030     }
2031 
2032     /**
2033      * Sets the array separator text.
2034      *
2035      * <p>
2036      * {@code null} is accepted, but will be converted to an empty String.
2037      * </p>
2038      *
2039      * @param arraySeparator the new array separator text.
2040      */
2041     protected void setArraySeparator(final String arraySeparator) {
2042         this.arraySeparator = ObjectUtils.toString(arraySeparator);
2043     }
2044 
2045     /**
2046      * Sets the array start text.
2047      *
2048      * <p>
2049      * {@code null} is accepted, but will be converted to an empty String.
2050      * </p>
2051      *
2052      * @param arrayStart the new array start text.
2053      */
2054     protected void setArrayStart(final String arrayStart) {
2055         this.arrayStart = ObjectUtils.toString(arrayStart);
2056     }
2057 
2058     /**
2059      * Sets the content end text.
2060      *
2061      * <p>
2062      * {@code null} is accepted, but will be converted to an empty String.
2063      * </p>
2064      *
2065      * @param contentEnd the new content end text.
2066      */
2067     protected void setContentEnd(final String contentEnd) {
2068         this.contentEnd = ObjectUtils.toString(contentEnd);
2069     }
2070 
2071     /**
2072      * Sets the content start text.
2073      *
2074      * <p>
2075      * {@code null} is accepted, but will be converted to an empty String.
2076      * </p>
2077      *
2078      * @param contentStart the new content start text.
2079      */
2080     protected void setContentStart(final String contentStart) {
2081         this.contentStart = ObjectUtils.toString(contentStart);
2082     }
2083 
2084     /**
2085      * Sets whether to use full detail when the caller doesn't specify.
2086      *
2087      * @param defaultFullDetail the new defaultFullDetail flag.
2088      */
2089     protected void setDefaultFullDetail(final boolean defaultFullDetail) {
2090         this.defaultFullDetail = defaultFullDetail;
2091     }
2092 
2093     /**
2094      * Sets the field name value separator text.
2095      *
2096      * <p>
2097      * {@code null} is accepted, but will be converted to an empty String.
2098      * </p>
2099      *
2100      * @param fieldNameValueSeparator the new field name value separator text.
2101      */
2102     protected void setFieldNameValueSeparator(final String fieldNameValueSeparator) {
2103         this.fieldNameValueSeparator = ObjectUtils.toString(fieldNameValueSeparator);
2104     }
2105 
2106     /**
2107      * Sets the field separator text.
2108      *
2109      * <p>
2110      * {@code null} is accepted, but will be converted to an empty String.
2111      * </p>
2112      *
2113      * @param fieldSeparator the new field separator text.
2114      */
2115     protected void setFieldSeparator(final String fieldSeparator) {
2116         this.fieldSeparator = ObjectUtils.toString(fieldSeparator);
2117     }
2118 
2119     /**
2120      * Sets whether the field separator should be added at the end of each buffer.
2121      *
2122      * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag.
2123      * @since 2.0
2124      */
2125     protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
2126         this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
2127     }
2128 
2129     /**
2130      * Sets whether the field separator should be added at the start of each buffer.
2131      *
2132      * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag.
2133      * @since 2.0
2134      */
2135     protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
2136         this.fieldSeparatorAtStart = fieldSeparatorAtStart;
2137     }
2138 
2139     /**
2140      * Sets the text to output when {@code null} found.
2141      *
2142      * <p>
2143      * {@code null} is accepted, but will be converted to an empty String.
2144      * </p>
2145      *
2146      * @param nullText the new text to output when null found.
2147      */
2148     protected void setNullText(final String nullText) {
2149         this.nullText = ObjectUtils.toString(nullText);
2150     }
2151 
2152     /**
2153      * Sets the end text to output when a {@link Collection}, {@link Map} or array size is output.
2154      *
2155      * <p>
2156      * This is output after the size value.
2157      * </p>
2158      *
2159      * <p>
2160      * {@code null} is accepted, but will be converted to an empty String.
2161      * </p>
2162      *
2163      * @param sizeEndText the new end of size text.
2164      */
2165     protected void setSizeEndText(final String sizeEndText) {
2166         this.sizeEndText = ObjectUtils.toString(sizeEndText);
2167     }
2168 
2169     /**
2170      * Sets the start text to output when a {@link Collection}, {@link Map} or array size is output.
2171      *
2172      * <p>
2173      * This is output before the size value.
2174      * </p>
2175      *
2176      * <p>
2177      * {@code null} is accepted, but will be converted to an empty String.
2178      * </p>
2179      *
2180      * @param sizeStartText the new start of size text.
2181      */
2182     protected void setSizeStartText(final String sizeStartText) {
2183         this.sizeStartText = ObjectUtils.toString(sizeStartText);
2184     }
2185 
2186     /**
2187      * Sets the end text to output when an {@link Object} is output in summary mode.
2188      *
2189      * <p>
2190      * This is output after the size value.
2191      * </p>
2192      *
2193      * <p>
2194      * {@code null} is accepted, but will be converted to an empty String.
2195      * </p>
2196      *
2197      * @param summaryObjectEndText the new end of summary text.
2198      */
2199     protected void setSummaryObjectEndText(final String summaryObjectEndText) {
2200         this.summaryObjectEndText = ObjectUtils.toString(summaryObjectEndText);
2201     }
2202 
2203     /**
2204      * Sets the start text to output when an {@link Object} is output in summary mode.
2205      *
2206      * <p>
2207      * This is output before the size value.
2208      * </p>
2209      *
2210      * <p>
2211      * {@code null} is accepted, but will be converted to an empty String.
2212      * </p>
2213      *
2214      * @param summaryObjectStartText the new start of summary text.
2215      */
2216     protected void setSummaryObjectStartText(final String summaryObjectStartText) {
2217         this.summaryObjectStartText = ObjectUtils.toString(summaryObjectStartText);
2218     }
2219 
2220     /**
2221      * Sets whether to use the class name.
2222      *
2223      * @param useClassName the new useClassName flag.
2224      */
2225     protected void setUseClassName(final boolean useClassName) {
2226         this.useClassName = useClassName;
2227     }
2228 
2229     /**
2230      * Sets whether to use the field names passed in.
2231      *
2232      * @param useFieldNames the new useFieldNames flag.
2233      */
2234     protected void setUseFieldNames(final boolean useFieldNames) {
2235         this.useFieldNames = useFieldNames;
2236     }
2237 
2238     /**
2239      * Sets whether to use the identity hash code.
2240      *
2241      * @param useIdentityHashCode the new useIdentityHashCode flag.
2242      */
2243     protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
2244         this.useIdentityHashCode = useIdentityHashCode;
2245     }
2246 
2247     /**
2248      * Sets whether to output short or long class names.
2249      *
2250      * @param useShortClassName the new useShortClassName flag.
2251      * @since 2.0
2252      */
2253     protected void setUseShortClassName(final boolean useShortClassName) {
2254         this.useShortClassName = useShortClassName;
2255     }
2256 }