    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}