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