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