001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.builder;
018
019import java.io.Serializable;
020import java.lang.reflect.Array;
021import java.util.Collection;
022import java.util.Map;
023import java.util.WeakHashMap;
024
025import org.apache.commons.lang3.ClassUtils;
026import org.apache.commons.lang3.ObjectUtils;
027import org.apache.commons.lang3.StringUtils;
028import org.apache.commons.lang3.SystemUtils;
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 */
066public abstract class ToStringStyle implements Serializable {
067
068    /**
069     * Serialization version ID.
070     */
071    private static final long serialVersionUID = -2587890625525655916L;
072
073    /**
074     * The default toString style. Using the <code>Person</code>
075     * example from {@link ToStringBuilder}, the output would look like this:
076     *
077     * <pre>
078     * Person@182f0db[name=John Doe,age=33,smoker=false]
079     * </pre>
080     */
081    public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
082
083    /**
084     * The multi line toString style. Using the <code>Person</code>
085     * example from {@link ToStringBuilder}, the output would look like this:
086     *
087     * <pre>
088     * Person@182f0db[
089     *   name=John Doe
090     *   age=33
091     *   smoker=false
092     * ]
093     * </pre>
094     */
095    public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
096
097    /**
098     * The no field names toString style. Using the
099     * <code>Person</code> example from {@link ToStringBuilder}, the output
100     * would look like this:
101     *
102     * <pre>
103     * Person@182f0db[John Doe,33,false]
104     * </pre>
105     */
106    public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
107
108    /**
109     * The short prefix toString style. Using the <code>Person</code> example
110     * from {@link ToStringBuilder}, the output would look like this:
111     *
112     * <pre>
113     * Person[name=John Doe,age=33,smoker=false]
114     * </pre>
115     *
116     * @since 2.1
117     */
118    public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
119
120    /**
121     * The simple toString style. Using the <code>Person</code>
122     * example from {@link ToStringBuilder}, the output would look like this:
123     *
124     * <pre>
125     * John Doe,33,false
126     * </pre>
127     */
128    public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
129
130    /**
131     * The no class name toString style. Using the <code>Person</code>
132     * example from {@link ToStringBuilder}, the output would look like this:
133     *
134     * <pre>
135     * [name=John Doe,age=33,smoker=false]
136     * </pre>
137     *
138     * @since 3.4
139     */
140    public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
141
142    /**
143     * The JSON toString style. Using the <code>Person</code> example from
144     * {@link ToStringBuilder}, the output would look like this:
145     *
146     * <pre>
147     * {"name": "John Doe", "age": 33, "smoker": true}
148     * </pre>
149     *
150     * <strong>Note:</strong> Since field names are mandatory in JSON, this
151     * ToStringStyle will throw an {@link UnsupportedOperationException} if no
152     * field name is passed in while appending. Furthermore This ToStringStyle
153     * will only generate valid JSON if referenced objects also produce JSON
154     * when calling {@code toString()} on them.
155     *
156     * @since 3.4
157     * @see <a href="http://json.org">json.org</a>
158     */
159    public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
160
161    /**
162     * <p>
163     * A registry of objects used by <code>reflectionToString</code> methods
164     * to detect cyclical object references and avoid infinite loops.
165     * </p>
166     */
167    private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY =
168        new ThreadLocal<WeakHashMap<Object,Object>>();
169    /*
170     * Note that objects of this class are generally shared between threads, so
171     * an instance variable would not be suitable here.
172     * 
173     * In normal use the registry should always be left empty, because the caller
174     * should call toString() which will clean up.
175     * 
176     * See LANG-792
177     */
178
179    /**
180     * <p>
181     * Returns the registry of objects being traversed by the <code>reflectionToString</code>
182     * methods in the current thread.
183     * </p>
184     *
185     * @return Set the registry of objects being traversed
186     */
187    static Map<Object, Object> getRegistry() {
188        return REGISTRY.get();
189    }
190
191    /**
192     * <p>
193     * Returns <code>true</code> if the registry contains the given object.
194     * Used by the reflection methods to avoid infinite loops.
195     * </p>
196     *
197     * @param value
198     *                  The object to lookup in the registry.
199     * @return boolean <code>true</code> if the registry contains the given
200     *             object.
201     */
202    static boolean isRegistered(final Object value) {
203        final Map<Object, Object> m = getRegistry();
204        return m != null && m.containsKey(value);
205    }
206
207    /**
208     * <p>
209     * Registers the given object. Used by the reflection methods to avoid
210     * infinite loops.
211     * </p>
212     *
213     * @param value
214     *                  The object to register.
215     */
216    static void register(final Object value) {
217        if (value != null) {
218            final Map<Object, Object> m = getRegistry();
219            if (m == null) {
220                REGISTRY.set(new WeakHashMap<Object, Object>());
221            }
222            getRegistry().put(value, null);
223        }
224    }
225
226    /**
227     * <p>
228     * Unregisters the given object.
229     * </p>
230     *
231     * <p>
232     * Used by the reflection methods to avoid infinite loops.
233     * </p>
234     *
235     * @param value
236     *                  The object to unregister.
237     */
238    static void unregister(final Object value) {
239        if (value != null) {
240            final Map<Object, Object> m = getRegistry();
241            if (m != null) {
242                m.remove(value);
243                if (m.isEmpty()) {
244                    REGISTRY.remove();
245                }
246            }
247        }
248    }
249
250    /**
251     * Whether to use the field names, the default is <code>true</code>.
252     */
253    private boolean useFieldNames = true;
254
255    /**
256     * Whether to use the class name, the default is <code>true</code>.
257     */
258    private boolean useClassName = true;
259
260    /**
261     * Whether to use short class names, the default is <code>false</code>.
262     */
263    private boolean useShortClassName = false;
264
265    /**
266     * Whether to use the identity hash code, the default is <code>true</code>.
267     */
268    private boolean useIdentityHashCode = true;
269
270    /**
271     * The content start <code>'['</code>.
272     */
273    private String contentStart = "[";
274
275    /**
276     * The content end <code>']'</code>.
277     */
278    private String contentEnd = "]";
279
280    /**
281     * The field name value separator <code>'='</code>.
282     */
283    private String fieldNameValueSeparator = "=";
284
285    /**
286     * Whether the field separator should be added before any other fields.
287     */
288    private boolean fieldSeparatorAtStart = false;
289
290    /**
291     * Whether the field separator should be added after any other fields.
292     */
293    private boolean fieldSeparatorAtEnd = false;
294
295    /**
296     * The field separator <code>','</code>.
297     */
298    private String fieldSeparator = ",";
299
300    /**
301     * The array start <code>'{'</code>.
302     */
303    private String arrayStart = "{";
304
305    /**
306     * The array separator <code>','</code>.
307     */
308    private String arraySeparator = ",";
309
310    /**
311     * The detail for array content.
312     */
313    private boolean arrayContentDetail = true;
314
315    /**
316     * The array end <code>'}'</code>.
317     */
318    private String arrayEnd = "}";
319
320    /**
321     * The value to use when fullDetail is <code>null</code>,
322     * the default value is <code>true</code>.
323     */
324    private boolean defaultFullDetail = true;
325
326    /**
327     * The <code>null</code> text <code>'&lt;null&gt;'</code>.
328     */
329    private String nullText = "<null>";
330
331    /**
332     * The summary size text start <code>'&lt;size'</code>.
333     */
334    private String sizeStartText = "<size=";
335
336    /**
337     * The summary size text start <code>'&gt;'</code>.
338     */
339    private String sizeEndText = ">";
340
341    /**
342     * The summary object text start <code>'&lt;'</code>.
343     */
344    private String summaryObjectStartText = "<";
345
346    /**
347     * The summary object text start <code>'&gt;'</code>.
348     */
349    private String summaryObjectEndText = ">";
350
351    //----------------------------------------------------------------------------
352
353    /**
354     * <p>Constructor.</p>
355     */
356    protected ToStringStyle() {
357        super();
358    }
359
360    //----------------------------------------------------------------------------
361
362    /**
363     * <p>Append to the <code>toString</code> the superclass toString.</p>
364     * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
365     *
366     * <p>A <code>null</code> <code>superToString</code> is ignored.</p>
367     *
368     * @param buffer  the <code>StringBuffer</code> to populate
369     * @param superToString  the <code>super.toString()</code>
370     * @since 2.0
371     */
372    public void appendSuper(final StringBuffer buffer, final String superToString) {
373        appendToString(buffer, superToString);
374    }
375
376    /**
377     * <p>Append to the <code>toString</code> another toString.</p>
378     * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
379     *
380     * <p>A <code>null</code> <code>toString</code> is ignored.</p>
381     *
382     * @param buffer  the <code>StringBuffer</code> to populate
383     * @param toString  the additional <code>toString</code>
384     * @since 2.0
385     */
386    public void appendToString(final StringBuffer buffer, final String toString) {
387        if (toString != null) {
388            final int pos1 = toString.indexOf(contentStart) + contentStart.length();
389            final int pos2 = toString.lastIndexOf(contentEnd);
390            if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
391                final String data = toString.substring(pos1, pos2);
392                if (fieldSeparatorAtStart) {
393                    removeLastFieldSeparator(buffer);
394                }
395                buffer.append(data);
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 == false) {
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 ToStringStyle.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 ToStringStyle.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 ToStringStyle.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 ToStringStyle.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(SystemUtils.LINE_SEPARATOR + "  ");
2298            this.setFieldSeparatorAtStart(true);
2299            this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
2300        }
2301
2302        /**
2303         * <p>Ensure <code>Singleton</code> after serialization.</p>
2304         *
2305         * @return the singleton
2306         */
2307        private Object readResolve() {
2308            return ToStringStyle.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 hashcode 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 ToStringStyle.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        /**
2368         * The summary size text start <code>'&gt;'</code>.
2369         */
2370        private String FIELD_NAME_PREFIX = "\"";
2371
2372        /**
2373         * <p>
2374         * Constructor.
2375         * </p>
2376         *
2377         * <p>
2378         * Use the static constant rather than instantiating.
2379         * </p>
2380         */
2381        JsonToStringStyle() {
2382            super();
2383
2384            this.setUseClassName(false);
2385            this.setUseIdentityHashCode(false);
2386
2387            this.setContentStart("{");
2388            this.setContentEnd("}");
2389
2390            this.setArrayStart("[");
2391            this.setArrayEnd("]");
2392
2393            this.setFieldSeparator(",");
2394            this.setFieldNameValueSeparator(":");
2395
2396            this.setNullText("null");
2397
2398            this.setSummaryObjectStartText("\"<");
2399            this.setSummaryObjectEndText(">\"");
2400
2401            this.setSizeStartText("\"<size=");
2402            this.setSizeEndText(">\"");
2403        }
2404
2405        @Override
2406        public void append(StringBuffer buffer, String fieldName,
2407                           Object[] array, Boolean fullDetail) {
2408
2409            if (fieldName == null) {
2410                throw new UnsupportedOperationException(
2411                        "Field names are mandatory when using JsonToStringStyle");
2412            }
2413            if (!isFullDetail(fullDetail)){
2414                throw new UnsupportedOperationException(
2415                        "FullDetail must be true when using JsonToStringStyle");
2416            }
2417
2418            super.append(buffer, fieldName, array, fullDetail);
2419        }
2420
2421        @Override
2422        public void append(StringBuffer buffer, String fieldName, long[] array,
2423                           Boolean fullDetail) {
2424
2425            if (fieldName == null) {
2426                throw new UnsupportedOperationException(
2427                        "Field names are mandatory when using JsonToStringStyle");
2428            }
2429            if (!isFullDetail(fullDetail)){
2430                throw new UnsupportedOperationException(
2431                        "FullDetail must be true when using JsonToStringStyle");
2432            }
2433
2434            super.append(buffer, fieldName, array, fullDetail);
2435        }
2436
2437        @Override
2438        public void append(StringBuffer buffer, String fieldName, int[] array,
2439                           Boolean fullDetail) {
2440
2441            if (fieldName == null) {
2442                throw new UnsupportedOperationException(
2443                        "Field names are mandatory when using JsonToStringStyle");
2444            }
2445            if (!isFullDetail(fullDetail)){
2446                throw new UnsupportedOperationException(
2447                        "FullDetail must be true when using JsonToStringStyle");
2448            }
2449
2450            super.append(buffer, fieldName, array, fullDetail);
2451        }
2452
2453        @Override
2454        public void append(StringBuffer buffer, String fieldName,
2455                           short[] array, Boolean fullDetail) {
2456
2457            if (fieldName == null) {
2458                throw new UnsupportedOperationException(
2459                        "Field names are mandatory when using JsonToStringStyle");
2460            }
2461            if (!isFullDetail(fullDetail)){
2462                throw new UnsupportedOperationException(
2463                        "FullDetail must be true when using JsonToStringStyle");
2464            }
2465
2466            super.append(buffer, fieldName, array, fullDetail);
2467        }
2468
2469        @Override
2470        public void append(StringBuffer buffer, String fieldName, byte[] array,
2471                           Boolean fullDetail) {
2472
2473            if (fieldName == null) {
2474                throw new UnsupportedOperationException(
2475                        "Field names are mandatory when using JsonToStringStyle");
2476            }
2477            if (!isFullDetail(fullDetail)){
2478                throw new UnsupportedOperationException(
2479                        "FullDetail must be true when using JsonToStringStyle");
2480            }
2481
2482            super.append(buffer, fieldName, array, fullDetail);
2483        }
2484
2485        @Override
2486        public void append(StringBuffer buffer, String fieldName, char[] array,
2487                           Boolean fullDetail) {
2488
2489            if (fieldName == null) {
2490                throw new UnsupportedOperationException(
2491                        "Field names are mandatory when using JsonToStringStyle");
2492            }
2493            if (!isFullDetail(fullDetail)){
2494                throw new UnsupportedOperationException(
2495                        "FullDetail must be true when using JsonToStringStyle");
2496            }
2497
2498            super.append(buffer, fieldName, array, fullDetail);
2499        }
2500
2501        @Override
2502        public void append(StringBuffer buffer, String fieldName,
2503                           double[] array, Boolean fullDetail) {
2504
2505            if (fieldName == null) {
2506                throw new UnsupportedOperationException(
2507                        "Field names are mandatory when using JsonToStringStyle");
2508            }
2509            if (!isFullDetail(fullDetail)){
2510                throw new UnsupportedOperationException(
2511                        "FullDetail must be true when using JsonToStringStyle");
2512            }
2513
2514            super.append(buffer, fieldName, array, fullDetail);
2515        }
2516
2517        @Override
2518        public void append(StringBuffer buffer, String fieldName,
2519                           float[] array, Boolean fullDetail) {
2520
2521            if (fieldName == null) {
2522                throw new UnsupportedOperationException(
2523                        "Field names are mandatory when using JsonToStringStyle");
2524            }
2525            if (!isFullDetail(fullDetail)){
2526                throw new UnsupportedOperationException(
2527                        "FullDetail must be true when using JsonToStringStyle");
2528            }
2529
2530            super.append(buffer, fieldName, array, fullDetail);
2531        }
2532
2533        @Override
2534        public void append(StringBuffer buffer, String fieldName,
2535                           boolean[] array, Boolean fullDetail) {
2536
2537            if (fieldName == null) {
2538                throw new UnsupportedOperationException(
2539                        "Field names are mandatory when using JsonToStringStyle");
2540            }
2541            if (!isFullDetail(fullDetail)){
2542                throw new UnsupportedOperationException(
2543                        "FullDetail must be true when using JsonToStringStyle");
2544            }
2545
2546            super.append(buffer, fieldName, array, fullDetail);
2547        }
2548
2549        @Override
2550        public void append(StringBuffer buffer, String fieldName, Object value,
2551                           Boolean fullDetail) {
2552
2553            if (fieldName == null) {
2554                throw new UnsupportedOperationException(
2555                        "Field names are mandatory when using JsonToStringStyle");
2556            }
2557            if (!isFullDetail(fullDetail)){
2558                throw new UnsupportedOperationException(
2559                        "FullDetail must be true when using JsonToStringStyle");
2560            }
2561
2562            super.append(buffer, fieldName, value, fullDetail);
2563        }
2564
2565        @Override
2566        protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
2567            appendValueAsString(buffer, String.valueOf(value));
2568        }
2569
2570        @Override
2571        protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
2572
2573            if (value == null) {
2574                appendNullText(buffer, fieldName);
2575                return;
2576            }
2577
2578            if (value instanceof String || value instanceof Character) {
2579                appendValueAsString(buffer, value.toString());
2580                return;
2581            }
2582
2583            if (value instanceof Number || value instanceof Boolean) {
2584                buffer.append(value);
2585                return;
2586            }
2587
2588            final String valueAsString = value.toString();
2589            if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
2590                buffer.append(value);
2591                return;
2592            }
2593
2594            appendDetail(buffer, fieldName, valueAsString);
2595        }
2596
2597        private boolean isJsonArray(String valueAsString) {
2598            return valueAsString.startsWith(getArrayStart())
2599                    && valueAsString.startsWith(getArrayEnd());
2600        }
2601
2602        private boolean isJsonObject(String valueAsString) {
2603            return valueAsString.startsWith(getContentStart())
2604                    && valueAsString.endsWith(getContentEnd());
2605        }
2606
2607        /**
2608         * Appends the given String in parenthesis to the given StringBuffer.
2609         * 
2610         * @param buffer the StringBuffer to append the value to.
2611         * @param value the value to append.
2612         */
2613        private void appendValueAsString(StringBuffer buffer, String value) {
2614            buffer.append("\"" + value + "\"");
2615        }
2616
2617        @Override
2618        protected void appendFieldStart(StringBuffer buffer, String fieldName) {
2619
2620            if (fieldName == null) {
2621                throw new UnsupportedOperationException(
2622                        "Field names are mandatory when using JsonToStringStyle");
2623            }
2624
2625            super.appendFieldStart(buffer, FIELD_NAME_PREFIX + fieldName
2626                    + FIELD_NAME_PREFIX);
2627        }
2628
2629        /**
2630         * <p>
2631         * Ensure <code>Singleton</code> after serialization.
2632         * </p>
2633         *
2634         * @return the singleton
2635         */
2636        private Object readResolve() {
2637            return ToStringStyle.JSON_STYLE;
2638        }
2639
2640    }
2641}