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