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