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