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