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