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