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