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