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