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