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