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