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.lang.builder;
18  
19  import java.io.Serializable;
20  import java.lang.reflect.Array;
21  import java.util.Collection;
22  import java.util.HashSet;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import org.apache.commons.lang.ClassUtils;
27  import org.apache.commons.lang.ObjectUtils;
28  import org.apache.commons.lang.SystemUtils;
29  
30  /**
31   * <p>Controls <code>String</code> formatting for {@link ToStringBuilder}.
32   * The main public interface is always via <code>ToStringBuilder</code>.</p>
33   *
34   * <p>These classes are intended to be used as <code>Singletons</code>.
35   * There is no need to instantiate a new style each time. A program
36   * will generally use one of the predefined constants on this class.
37   * Alternatively, the {@link StandardToStringStyle} class can be used
38   * to set the individual settings. Thus most styles can be achieved
39   * without subclassing.</p>
40   *
41   * <p>If required, a subclass can override as many or as few of the
42   * methods as it requires. Each object type (from <code>boolean</code>
43   * to <code>long</code> to <code>Object</code> to <code>int[]</code>) has
44   * its own methods to output it. Most have two versions, detail and summary.
45   *
46   * <p>For example, the detail version of the array based methods will
47   * output the whole array, whereas the summary method will just output
48   * the array length.</p>
49   * 
50   * <p>If you want to format the output of certain objects, such as dates, you
51   * must create a subclass and override a method.
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   * </p>
63   *
64   * @author Stephen Colebourne
65   * @author Gary Gregory
66   * @author Pete Gieser
67   * @author Masato Tezuka
68   * @since 1.0
69   * @version $Id: ToStringStyle.java 594386 2007-11-13 01:22:21Z bayard $
70   */
71  public abstract class ToStringStyle implements Serializable {
72  
73      /**
74       * The default toString style. Using the Using the <code>Person</code>
75       * example from {@link ToStringBuilder}, the output would look like this:
76       * 
77       * <pre>
78       * Person@182f0db[name=John Doe,age=33,smoker=false]
79       * </pre>
80       */
81      public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
82      
83      /**
84       * The multi line toString style. Using the Using the <code>Person</code>
85       * example from {@link ToStringBuilder}, the output would look like this:
86       * 
87       * <pre>
88       * Person@182f0db[
89       *   name=John Doe
90       *   age=33
91       *   smoker=false
92       * ]
93       * </pre>
94       */
95      public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
96      
97      /**
98       * The no field names toString style. Using the Using the
99       * <code>Person</code> example from {@link ToStringBuilder}, the output
100      * would look like this:
101      * 
102      * <pre>
103      * Person@182f0db[John Doe,33,false]
104      * </pre>
105      */
106     public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
107     
108     /**
109      * The short prefix toString style. Using the <code>Person</code> example
110      * from {@link ToStringBuilder}, the output would look like this:
111      * 
112      * <pre>
113      * Person[name=John Doe,age=33,smoker=false]
114      * </pre>
115      * 
116      * @since 2.1
117      */
118     public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
119 
120     /**
121      * The simple toString style. Using the Using the <code>Person</code>
122      * example from {@link ToStringBuilder}, the output would look like this:
123      * 
124      * <pre>
125      * John Doe,33,false
126      * </pre>
127      */
128     public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
129     
130     /**
131      * <p>
132      * A registry of objects used by <code>reflectionToString</code> methods
133      * to detect cyclical object references and avoid infinite loops.
134      * </p>
135      */
136     private static ThreadLocal registry = new ThreadLocal() {
137         protected Object initialValue() {
138             // The HashSet implementation is not synchronized,
139             // which is just what we need here.
140             return new HashSet();
141         }
142     };
143 
144     /**
145      * <p>
146      * Returns the registry of objects being traversed by the <code>reflectionToString</code>
147      * methods in the current thread.
148      * </p>
149      * 
150      * @return Set the registry of objects being traversed
151      */
152     static Set getRegistry() {
153         return (Set) registry.get();
154     }
155 
156     /**
157      * <p>
158      * Returns <code>true</code> if the registry contains the given object.
159      * Used by the reflection methods to avoid infinite loops.
160      * </p>
161      * 
162      * @param value
163      *                  The object to lookup in the registry.
164      * @return boolean <code>true</code> if the registry contains the given
165      *             object.
166      */
167     static boolean isRegistered(Object value) {
168         return getRegistry().contains(value);
169     }
170 
171     /**
172      * <p>
173      * Registers the given object. Used by the reflection methods to avoid
174      * infinite loops.
175      * </p>
176      * 
177      * @param value
178      *                  The object to register.
179      */
180     static void register(Object value) {
181         if (value != null) {
182             getRegistry().add(value);
183         }
184     }
185 
186     /**
187      * <p>
188      * Unregisters the given object.
189      * </p>
190      * 
191      * <p>
192      * Used by the reflection methods to avoid infinite loops.
193      * </p>
194      * 
195      * @param value
196      *                  The object to unregister.
197      */
198     static void unregister(Object value) {
199         getRegistry().remove(value);
200     }
201 
202     /**
203      * Whether to use the field names, the default is <code>true</code>.
204      */
205     private boolean useFieldNames = true;
206     
207     /**
208      * Whether to use the class name, the default is <code>true</code>.
209      */
210     private boolean useClassName = true;
211     
212     /**
213      * Whether to use short class names, the default is <code>false</code>.
214      */
215     private boolean useShortClassName = false;
216     
217     /**
218      * Whether to use the identity hash code, the default is <code>true</code>.
219      */
220     private boolean useIdentityHashCode = true;
221 
222     /**
223      * The content start <code>'['</code>.
224      */
225     private String contentStart = "[";
226     
227     /**
228      * The content end <code>']'</code>.
229      */
230     private String contentEnd = "]";
231     
232     /**
233      * The field name value separator <code>'='</code>.
234      */
235     private String fieldNameValueSeparator = "=";
236     
237     /**
238      * Whether the field separator should be added before any other fields.
239      */
240     private boolean fieldSeparatorAtStart = false;
241     
242     /**
243      * Whether the field separator should be added after any other fields.
244      */
245     private boolean fieldSeparatorAtEnd = false;
246     
247     /**
248      * The field separator <code>','</code>.
249      */
250     private String fieldSeparator = ",";
251     
252     /**
253      * The array start <code>'{'</code>.
254      */
255     private String arrayStart = "{";
256     
257     /**
258      * The array separator <code>','</code>.
259      */
260     private String arraySeparator = ",";
261     
262     /**
263      * The detail for array content.
264      */
265     private boolean arrayContentDetail = true;
266     
267     /**
268      * The array end <code>'}'</code>.
269      */
270     private String arrayEnd = "}";
271     
272     /**
273      * The value to use when fullDetail is <code>null</code>,
274      * the default value is <code>true</code>.
275      */
276     private boolean defaultFullDetail = true;
277     
278     /**
279      * The <code>null</code> text <code>'&lt;null&gt;'</code>.
280      */
281     private String nullText = "<null>";
282     
283     /**
284      * The summary size text start <code>'<size'</code>.
285      */
286     private String sizeStartText = "<size=";
287     
288     /**
289      * The summary size text start <code>'&gt;'</code>.
290      */
291     private String sizeEndText = ">";
292     
293     /**
294      * The summary object text start <code>'&lt;'</code>.
295      */
296     private String summaryObjectStartText = "<";
297     
298     /**
299      * The summary object text start <code>'&gt;'</code>.
300      */
301     private String summaryObjectEndText = ">";
302 
303     //----------------------------------------------------------------------------
304 
305     /**
306      * <p>Constructor.</p>
307      */
308     protected ToStringStyle() {
309         super();
310     }
311 
312     //----------------------------------------------------------------------------
313 
314     /**
315      * <p>Append to the <code>toString</code> the superclass toString.</p>
316      * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
317      * 
318      * <p>A <code>null</code> <code>superToString</code> is ignored.</p>
319      * 
320      * @param buffer  the <code>StringBuffer</code> to populate
321      * @param superToString  the <code>super.toString()</code>
322      * @since 2.0
323      */
324     public void appendSuper(StringBuffer buffer, String superToString) {
325         appendToString(buffer, superToString);
326     }
327 
328     /**
329      * <p>Append to the <code>toString</code> another toString.</p>
330      * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
331      * 
332      * <p>A <code>null</code> <code>toString</code> is ignored.</p>
333      * 
334      * @param buffer  the <code>StringBuffer</code> to populate
335      * @param toString  the additional <code>toString</code>
336      * @since 2.0
337      */
338     public void appendToString(StringBuffer buffer, String toString) {
339         if (toString != null) {
340             int pos1 = toString.indexOf(contentStart) + contentStart.length();
341             int pos2 = toString.lastIndexOf(contentEnd);
342             if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
343                 String data = toString.substring(pos1, pos2);
344                 if (fieldSeparatorAtStart) {
345                     removeLastFieldSeparator(buffer);
346                 }
347                 buffer.append(data);
348                 appendFieldSeparator(buffer);
349             }
350         }
351     }
352 
353     /**
354      * <p>Append to the <code>toString</code> the start of data indicator.</p>
355      * 
356      * @param buffer  the <code>StringBuffer</code> to populate
357      * @param object  the <code>Object</code> to build a <code>toString</code> for
358      */
359     public void appendStart(StringBuffer buffer, Object object) {
360         if (object != null) {
361             appendClassName(buffer, object);
362             appendIdentityHashCode(buffer, object);
363             appendContentStart(buffer);
364             if (fieldSeparatorAtStart) {
365                 appendFieldSeparator(buffer);
366             }
367         }
368     }
369 
370     /**
371      * <p>Append to the <code>toString</code> the end of data indicator.</p>
372      * 
373      * @param buffer  the <code>StringBuffer</code> to populate
374      * @param object  the <code>Object</code> to build a
375      *  <code>toString</code> for.
376      */
377     public void appendEnd(StringBuffer buffer, Object object) {
378         if (this.fieldSeparatorAtEnd == false) {
379             removeLastFieldSeparator(buffer);
380         }
381         appendContentEnd(buffer);
382         unregister(object);
383     }
384 
385     /**
386      * <p>Remove the last field separator from the buffer.</p>
387      * 
388      * @param buffer  the <code>StringBuffer</code> to populate
389      * @since 2.0
390      */
391     protected void removeLastFieldSeparator(StringBuffer buffer) {
392         int len = buffer.length();
393         int sepLen = fieldSeparator.length();
394         if (len > 0 && sepLen > 0 && len >= sepLen) {
395             boolean match = true;
396             for (int i = 0; i < sepLen; i++) {
397                 if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
398                     match = false;
399                     break;
400                 }
401             }
402             if (match) {
403                 buffer.setLength(len - sepLen);
404             }
405         }
406     }
407 
408     //----------------------------------------------------------------------------
409 
410     /**
411      * <p>Append to the <code>toString</code> an <code>Object</code>
412      * value, printing the full <code>toString</code> of the
413      * <code>Object</code> passed in.</p>
414      *
415      * @param buffer  the <code>StringBuffer</code> to populate
416      * @param fieldName  the field name
417      * @param value  the value to add to the <code>toString</code>
418      * @param fullDetail  <code>true</code> for detail, <code>false</code>
419      *  for summary info, <code>null</code> for style decides
420      */
421     public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) {
422         appendFieldStart(buffer, fieldName);
423 
424         if (value == null) {
425             appendNullText(buffer, fieldName);
426 
427         } else {
428             appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
429         }
430 
431         appendFieldEnd(buffer, fieldName);
432     }
433 
434     /**
435      * <p>Append to the <code>toString</code> an <code>Object</code>,
436      * correctly interpreting its type.</p>
437      *
438      * <p>This method performs the main lookup by Class type to correctly
439      * route arrays, <code>Collections</code>, <code>Maps</code> and
440      * <code>Objects</code> to the appropriate method.</p>
441      *
442      * <p>Either detail or summary views can be specified.</p>
443      *
444      * <p>If a cycle is detected, an object will be appended with the
445      * <code>Object.toString()</code> format.</p>
446      *
447      * @param buffer  the <code>StringBuffer</code> to populate
448      * @param fieldName  the field name, typically not used as already appended
449      * @param value  the value to add to the <code>toString</code>,
450      *  not <code>null</code>
451      * @param detail  output detail or not
452      */
453     protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) {
454         if (isRegistered(value)
455             && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
456            appendCyclicObject(buffer, fieldName, value);
457            return;
458         }   
459 
460         register(value);
461 
462         try {
463             if (value instanceof Collection) {
464                 if (detail) {
465                     appendDetail(buffer, fieldName, (Collection) value);
466                 } else {
467                     appendSummarySize(buffer, fieldName, ((Collection) value).size());
468                 }
469     
470             } else if (value instanceof Map) {
471                 if (detail) {
472                     appendDetail(buffer, fieldName, (Map) value);
473                 } else {
474                     appendSummarySize(buffer, fieldName, ((Map) value).size());
475                 }
476     
477             } else if (value instanceof long[]) {
478                 if (detail) {
479                     appendDetail(buffer, fieldName, (long[]) value);
480                 } else {
481                     appendSummary(buffer, fieldName, (long[]) value);
482                 }
483     
484             } else if (value instanceof int[]) {
485                 if (detail) {
486                     appendDetail(buffer, fieldName, (int[]) value);
487                 } else {
488                     appendSummary(buffer, fieldName, (int[]) value);
489                 }
490     
491             } else if (value instanceof short[]) {
492                 if (detail) {
493                     appendDetail(buffer, fieldName, (short[]) value);
494                 } else {
495                     appendSummary(buffer, fieldName, (short[]) value);
496                 }
497     
498             } else if (value instanceof byte[]) {
499                 if (detail) {
500                     appendDetail(buffer, fieldName, (byte[]) value);
501                 } else {
502                     appendSummary(buffer, fieldName, (byte[]) value);
503                 }
504     
505             } else if (value instanceof char[]) {
506                 if (detail) {
507                     appendDetail(buffer, fieldName, (char[]) value);
508                 } else {
509                     appendSummary(buffer, fieldName, (char[]) value);
510                 }
511     
512             } else if (value instanceof double[]) {
513                 if (detail) {
514                     appendDetail(buffer, fieldName, (double[]) value);
515                 } else {
516                     appendSummary(buffer, fieldName, (double[]) value);
517                 }
518     
519             } else if (value instanceof float[]) {
520                 if (detail) {
521                     appendDetail(buffer, fieldName, (float[]) value);
522                 } else {
523                     appendSummary(buffer, fieldName, (float[]) value);
524                 }
525     
526             } else if (value instanceof boolean[]) {
527                 if (detail) {
528                     appendDetail(buffer, fieldName, (boolean[]) value);
529                 } else {
530                     appendSummary(buffer, fieldName, (boolean[]) value);
531                 }
532     
533             } else if (value.getClass().isArray()) {
534                 if (detail) {
535                     appendDetail(buffer, fieldName, (Object[]) value);
536                 } else {
537                     appendSummary(buffer, fieldName, (Object[]) value);
538                 }
539     
540             } else {
541                     if (detail) {
542                         appendDetail(buffer, fieldName, value);
543                     } else {
544                         appendSummary(buffer, fieldName, value);
545                     }
546             }
547         } finally {
548             unregister(value);
549         }
550     }
551     
552     /**
553      * <p>Append to the <code>toString</code> an <code>Object</code>
554      * value that has been detected to participate in a cycle. This
555      * implementation will print the standard string value of the value.</p>
556      * 
557      * @param buffer  the <code>StringBuffer</code> to populate
558      * @param fieldName  the field name, typically not used as already appended
559      * @param value  the value to add to the <code>toString</code>,
560      *  not <code>null</code>
561      *  
562      * @since 2.2
563      */
564     protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) {
565        ObjectUtils.appendIdentityToString(buffer, value);
566     }
567 
568     /**
569      * <p>Append to the <code>toString</code> an <code>Object</code>
570      * value, printing the full detail of the <code>Object</code>.</p>
571      *
572      * @param buffer  the <code>StringBuffer</code> to populate
573      * @param fieldName  the field name, typically not used as already appended
574      * @param value  the value to add to the <code>toString</code>,
575      *  not <code>null</code>
576      */
577     protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
578         buffer.append(value);
579     }
580 
581     /**
582      * <p>Append to the <code>toString</code> a <code>Collection</code>.</p>
583      *
584      * @param buffer  the <code>StringBuffer</code> to populate
585      * @param fieldName  the field name, typically not used as already appended
586      * @param coll  the <code>Collection</code> to add to the
587      *  <code>toString</code>, not <code>null</code>
588      */
589     protected void appendDetail(StringBuffer buffer, String fieldName, Collection coll) {
590         buffer.append(coll);
591     }
592 
593     /**
594      * <p>Append to the <code>toString</code> a <code>Map<code>.</p>
595      *
596      * @param buffer  the <code>StringBuffer</code> to populate
597      * @param fieldName  the field name, typically not used as already appended
598      * @param map  the <code>Map</code> to add to the <code>toString</code>,
599      *  not <code>null</code>
600      */
601     protected void appendDetail(StringBuffer buffer, String fieldName, Map map) {
602         buffer.append(map);
603     }
604 
605     /**
606      * <p>Append to the <code>toString</code> an <code>Object</code>
607      * value, printing a summary of the <code>Object</code>.</P>
608      *
609      * @param buffer  the <code>StringBuffer</code> to populate
610      * @param fieldName  the field name, typically not used as already appended
611      * @param value  the value to add to the <code>toString</code>,
612      *  not <code>null</code>
613      */
614     protected void appendSummary(StringBuffer buffer, String fieldName, Object value) {
615         buffer.append(summaryObjectStartText);
616         buffer.append(getShortClassName(value.getClass()));
617         buffer.append(summaryObjectEndText);
618     }
619 
620     //----------------------------------------------------------------------------
621 
622     /**
623      * <p>Append to the <code>toString</code> a <code>long</code>
624      * value.</p>
625      *
626      * @param buffer  the <code>StringBuffer</code> to populate
627      * @param fieldName  the field name
628      * @param value  the value to add to the <code>toString</code>
629      */
630     public void append(StringBuffer buffer, String fieldName, long value) {
631         appendFieldStart(buffer, fieldName);
632         appendDetail(buffer, fieldName, value);
633         appendFieldEnd(buffer, fieldName);
634     }
635 
636     /**
637      * <p>Append to the <code>toString</code> a <code>long</code>
638      * value.</p>
639      *
640      * @param buffer  the <code>StringBuffer</code> to populate
641      * @param fieldName  the field name, typically not used as already appended
642      * @param value  the value to add to the <code>toString</code>
643      */
644     protected void appendDetail(StringBuffer buffer, String fieldName, long value) {
645         buffer.append(value);
646     }
647 
648     //----------------------------------------------------------------------------
649 
650     /**
651      * <p>Append to the <code>toString</code> an <code>int</code>
652      * value.</p>
653      *
654      * @param buffer  the <code>StringBuffer</code> to populate
655      * @param fieldName  the field name
656      * @param value  the value to add to the <code>toString</code>
657      */
658     public void append(StringBuffer buffer, String fieldName, int value) {
659         appendFieldStart(buffer, fieldName);
660         appendDetail(buffer, fieldName, value);
661         appendFieldEnd(buffer, fieldName);
662     }
663 
664     /**
665      * <p>Append to the <code>toString</code> an <code>int</code>
666      * value.</p>
667      *
668      * @param buffer  the <code>StringBuffer</code> to populate
669      * @param fieldName  the field name, typically not used as already appended
670      * @param value  the value to add to the <code>toString</code>
671      */
672     protected void appendDetail(StringBuffer buffer, String fieldName, int value) {
673         buffer.append(value);
674     }
675 
676     //----------------------------------------------------------------------------
677 
678     /**
679      * <p>Append to the <code>toString</code> a <code>short</code>
680      * value.</p>
681      *
682      * @param buffer  the <code>StringBuffer</code> to populate
683      * @param fieldName  the field name
684      * @param value  the value to add to the <code>toString</code>
685      */
686     public void append(StringBuffer buffer, String fieldName, short value) {
687         appendFieldStart(buffer, fieldName);
688         appendDetail(buffer, fieldName, value);
689         appendFieldEnd(buffer, fieldName);
690     }
691 
692     /**
693      * <p>Append to the <code>toString</code> a <code>short</code>
694      * value.</p>
695      *
696      * @param buffer  the <code>StringBuffer</code> to populate
697      * @param fieldName  the field name, typically not used as already appended
698      * @param value  the value to add to the <code>toString</code>
699      */
700     protected void appendDetail(StringBuffer buffer, String fieldName, short value) {
701         buffer.append(value);
702     }
703 
704     //----------------------------------------------------------------------------
705 
706     /**
707      * <p>Append to the <code>toString</code> a <code>byte</code>
708      * value.</p>
709      *
710      * @param buffer  the <code>StringBuffer</code> to populate
711      * @param fieldName  the field name
712      * @param value  the value to add to the <code>toString</code>
713      */
714     public void append(StringBuffer buffer, String fieldName, byte value) {
715         appendFieldStart(buffer, fieldName);
716         appendDetail(buffer, fieldName, value);
717         appendFieldEnd(buffer, fieldName);
718     }
719 
720     /**
721      * <p>Append to the <code>toString</code> a <code>byte</code>
722      * value.</p>
723      *
724      * @param buffer  the <code>StringBuffer</code> to populate
725      * @param fieldName  the field name, typically not used as already appended
726      * @param value  the value to add to the <code>toString</code>
727      */
728     protected void appendDetail(StringBuffer buffer, String fieldName, byte value) {
729         buffer.append(value);
730     }
731 
732     //----------------------------------------------------------------------------
733 
734     /**
735      * <p>Append to the <code>toString</code> a <code>char</code>
736      * value.</p>
737      *
738      * @param buffer  the <code>StringBuffer</code> to populate
739      * @param fieldName  the field name
740      * @param value  the value to add to the <code>toString</code>
741      */
742     public void append(StringBuffer buffer, String fieldName, char value) {
743         appendFieldStart(buffer, fieldName);
744         appendDetail(buffer, fieldName, value);
745         appendFieldEnd(buffer, fieldName);
746     }
747 
748     /**
749      * <p>Append to the <code>toString</code> a <code>char</code>
750      * value.</p>
751      *
752      * @param buffer  the <code>StringBuffer</code> to populate
753      * @param fieldName  the field name, typically not used as already appended
754      * @param value  the value to add to the <code>toString</code>
755      */
756     protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
757         buffer.append(value);
758     }
759 
760     //----------------------------------------------------------------------------
761 
762     /**
763      * <p>Append to the <code>toString</code> a <code>double</code>
764      * value.</p>
765      *
766      * @param buffer  the <code>StringBuffer</code> to populate
767      * @param fieldName  the field name
768      * @param value  the value to add to the <code>toString</code>
769      */
770     public void append(StringBuffer buffer, String fieldName, double value) {
771         appendFieldStart(buffer, fieldName);
772         appendDetail(buffer, fieldName, value);
773         appendFieldEnd(buffer, fieldName);
774     }
775 
776     /**
777      * <p>Append to the <code>toString</code> a <code>double</code>
778      * value.</p>
779      *
780      * @param buffer  the <code>StringBuffer</code> to populate
781      * @param fieldName  the field name, typically not used as already appended
782      * @param value  the value to add to the <code>toString</code>
783      */
784     protected void appendDetail(StringBuffer buffer, String fieldName, double value) {
785         buffer.append(value);
786     }
787 
788     //----------------------------------------------------------------------------
789 
790     /**
791      * <p>Append to the <code>toString</code> a <code>float</code>
792      * value.</p>
793      *
794      * @param buffer  the <code>StringBuffer</code> to populate
795      * @param fieldName  the field name
796      * @param value  the value to add to the <code>toString</code>
797      */
798     public void append(StringBuffer buffer, String fieldName, float value) {
799         appendFieldStart(buffer, fieldName);
800         appendDetail(buffer, fieldName, value);
801         appendFieldEnd(buffer, fieldName);
802     }
803 
804     /**
805      * <p>Append to the <code>toString</code> a <code>float</code>
806      * value.</p>
807      *
808      * @param buffer  the <code>StringBuffer</code> to populate
809      * @param fieldName  the field name, typically not used as already appended
810      * @param value  the value to add to the <code>toString</code>
811      */
812     protected void appendDetail(StringBuffer buffer, String fieldName, float value) {
813         buffer.append(value);
814     }
815 
816     //----------------------------------------------------------------------------
817 
818     /**
819      * <p>Append to the <code>toString</code> a <code>boolean</code>
820      * value.</p>
821      *
822      * @param buffer  the <code>StringBuffer</code> to populate
823      * @param fieldName  the field name
824      * @param value  the value to add to the <code>toString</code>
825      */
826     public void append(StringBuffer buffer, String fieldName, boolean value) {
827         appendFieldStart(buffer, fieldName);
828         appendDetail(buffer, fieldName, value);
829         appendFieldEnd(buffer, fieldName);
830     }
831 
832     /**
833      * <p>Append to the <code>toString</code> a <code>boolean</code>
834      * value.</p>
835      *
836      * @param buffer  the <code>StringBuffer</code> to populate
837      * @param fieldName  the field name, typically not used as already appended
838      * @param value  the value to add to the <code>toString</code>
839      */
840     protected void appendDetail(StringBuffer buffer, String fieldName, boolean value) {
841         buffer.append(value);
842     }
843 
844     /**
845      * <p>Append to the <code>toString</code> an <code>Object</code>
846      * array.</p>
847      *
848      * @param buffer  the <code>StringBuffer</code> to populate
849      * @param fieldName  the field name
850      * @param array  the array to add to the toString
851      * @param fullDetail  <code>true</code> for detail, <code>false</code>
852      *  for summary info, <code>null</code> for style decides
853      */
854     public void append(StringBuffer buffer, String fieldName, Object[] array, Boolean fullDetail) {
855         appendFieldStart(buffer, fieldName);
856 
857         if (array == null) {
858             appendNullText(buffer, fieldName);
859 
860         } else if (isFullDetail(fullDetail)) {
861             appendDetail(buffer, fieldName, array);
862 
863         } else {
864             appendSummary(buffer, fieldName, array);
865         }
866 
867         appendFieldEnd(buffer, fieldName);
868     }
869 
870     //----------------------------------------------------------------------------
871 
872     /**
873      * <p>Append to the <code>toString</code> the detail of an
874      * <code>Object</code> array.</p>
875      *
876      * @param buffer  the <code>StringBuffer</code> to populate
877      * @param fieldName  the field name, typically not used as already appended
878      * @param array  the array to add to the <code>toString</code>,
879      *  not <code>null</code>
880      */
881     protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) {
882         buffer.append(arrayStart);
883         for (int i = 0; i < array.length; i++) {
884             Object item = array[i];
885             if (i > 0) {
886                 buffer.append(arraySeparator);
887             }
888             if (item == null) {
889                 appendNullText(buffer, fieldName);
890 
891             } else {
892                 appendInternal(buffer, fieldName, item, arrayContentDetail);
893             }
894         }
895         buffer.append(arrayEnd);
896     }
897 
898     /**
899      * <p>Append to the <code>toString</code> the detail of an array type.</p>
900      *
901      * @param buffer  the <code>StringBuffer</code> to populate
902      * @param fieldName  the field name, typically not used as already appended
903      * @param array  the array to add to the <code>toString</code>,
904      *  not <code>null</code>
905      * @since 2.0
906      */
907     protected void reflectionAppendArrayDetail(StringBuffer buffer, String fieldName, Object array) {
908         buffer.append(arrayStart);
909         int length = Array.getLength(array);
910         for (int i = 0; i < length; i++) {
911             Object item = Array.get(array, i);
912             if (i > 0) {
913                 buffer.append(arraySeparator);
914             }
915             if (item == null) {
916                 appendNullText(buffer, fieldName);
917 
918             } else {
919                 appendInternal(buffer, fieldName, item, arrayContentDetail);
920             }
921         }
922         buffer.append(arrayEnd);
923     }
924 
925     /**
926      * <p>Append to the <code>toString</code> a summary of an
927      * <code>Object</code> array.</p>
928      *
929      * @param buffer  the <code>StringBuffer</code> to populate
930      * @param fieldName  the field name, typically not used as already appended
931      * @param array  the array to add to the <code>toString</code>,
932      *  not <code>null</code>
933      */
934     protected void appendSummary(StringBuffer buffer, String fieldName, Object[] array) {
935         appendSummarySize(buffer, fieldName, array.length);
936     }
937 
938     //----------------------------------------------------------------------------
939 
940     /**
941      * <p>Append to the <code>toString</code> a <code>long</code>
942      * array.</p>
943      *
944      * @param buffer  the <code>StringBuffer</code> to populate
945      * @param fieldName  the field name
946      * @param array  the array to add to the <code>toString</code>
947      * @param fullDetail  <code>true</code> for detail, <code>false</code>
948      *  for summary info, <code>null</code> for style decides
949      */
950     public void append(StringBuffer buffer, String fieldName, long[] array, Boolean fullDetail) {
951         appendFieldStart(buffer, fieldName);
952 
953         if (array == null) {
954             appendNullText(buffer, fieldName);
955 
956         } else if (isFullDetail(fullDetail)) {
957             appendDetail(buffer, fieldName, array);
958 
959         } else {
960             appendSummary(buffer, fieldName, array);
961         }
962 
963         appendFieldEnd(buffer, fieldName);
964     }
965 
966     /**
967      * <p>Append to the <code>toString</code> the detail of a
968      * <code>long</code> array.</p>
969      *
970      * @param buffer  the <code>StringBuffer</code> to populate
971      * @param fieldName  the field name, typically not used as already appended
972      * @param array  the array to add to the <code>toString</code>,
973      *  not <code>null</code>
974      */
975     protected void appendDetail(StringBuffer buffer, String fieldName, long[] array) {
976         buffer.append(arrayStart);
977         for (int i = 0; i < array.length; i++) {
978             if (i > 0) {
979                 buffer.append(arraySeparator);
980             }
981             appendDetail(buffer, fieldName, array[i]);
982         }
983         buffer.append(arrayEnd);
984     }
985 
986     /**
987      * <p>Append to the <code>toString</code> a summary of a
988      * <code>long</code> array.</p>
989      *
990      * @param buffer  the <code>StringBuffer</code> to populate
991      * @param fieldName  the field name, typically not used as already appended
992      * @param array  the array to add to the <code>toString</code>,
993      *  not <code>null</code>
994      */
995     protected void appendSummary(StringBuffer buffer, String fieldName, long[] array) {
996         appendSummarySize(buffer, fieldName, array.length);
997     }
998 
999     //----------------------------------------------------------------------------
1000 
1001     /**
1002      * <p>Append to the <code>toString</code> an <code>int</code>
1003      * array.</p>
1004      *
1005      * @param buffer  the <code>StringBuffer</code> to populate
1006      * @param fieldName  the field name
1007      * @param array  the array to add to the <code>toString</code>
1008      * @param fullDetail  <code>true</code> for detail, <code>false</code>
1009      *  for summary info, <code>null</code> for style decides
1010      */
1011     public void append(StringBuffer buffer, String fieldName, int[] array, Boolean fullDetail) {
1012         appendFieldStart(buffer, fieldName);
1013 
1014         if (array == null) {
1015             appendNullText(buffer, fieldName);
1016 
1017         } else if (isFullDetail(fullDetail)) {
1018             appendDetail(buffer, fieldName, array);
1019 
1020         } else {
1021             appendSummary(buffer, fieldName, array);
1022         }
1023 
1024         appendFieldEnd(buffer, fieldName);
1025     }
1026 
1027     /**
1028      * <p>Append to the <code>toString</code> the detail of an
1029      * <code>int</code> array.</p>
1030      *
1031      * @param buffer  the <code>StringBuffer</code> to populate
1032      * @param fieldName  the field name, typically not used as already appended
1033      * @param array  the array to add to the <code>toString</code>,
1034      *  not <code>null</code>
1035      */
1036     protected void appendDetail(StringBuffer buffer, String fieldName, int[] array) {
1037         buffer.append(arrayStart);
1038         for (int i = 0; i < array.length; i++) {
1039             if (i > 0) {
1040                 buffer.append(arraySeparator);
1041             }
1042             appendDetail(buffer, fieldName, array[i]);
1043         }
1044         buffer.append(arrayEnd);
1045     }
1046 
1047     /**
1048      * <p>Append to the <code>toString</code> a summary of an
1049      * <code>int</code> array.</p>
1050      *
1051      * @param buffer  the <code>StringBuffer</code> to populate
1052      * @param fieldName  the field name, typically not used as already appended
1053      * @param array  the array to add to the <code>toString</code>,
1054      *  not <code>null</code>
1055      */
1056     protected void appendSummary(StringBuffer buffer, String fieldName, int[] array) {
1057         appendSummarySize(buffer, fieldName, array.length);
1058     }
1059 
1060     //----------------------------------------------------------------------------
1061 
1062     /**
1063      * <p>Append to the <code>toString</code> a <code>short</code>
1064      * array.</p>
1065      *
1066      * @param buffer  the <code>StringBuffer</code> to populate
1067      * @param fieldName  the field name
1068      * @param array  the array to add to the <code>toString</code>
1069      * @param fullDetail  <code>true</code> for detail, <code>false</code>
1070      *  for summary info, <code>null</code> for style decides
1071      */
1072     public void append(StringBuffer buffer, String fieldName, short[] array, Boolean fullDetail) {
1073         appendFieldStart(buffer, fieldName);
1074 
1075         if (array == null) {
1076             appendNullText(buffer, fieldName);
1077 
1078         } else if (isFullDetail(fullDetail)) {
1079             appendDetail(buffer, fieldName, array);
1080 
1081         } else {
1082             appendSummary(buffer, fieldName, array);
1083         }
1084 
1085         appendFieldEnd(buffer, fieldName);
1086     }
1087 
1088     /**
1089      * <p>Append to the <code>toString</code> the detail of a
1090      * <code>short</code> array.</p>
1091      *
1092      * @param buffer  the <code>StringBuffer</code> to populate
1093      * @param fieldName  the field name, typically not used as already appended
1094      * @param array  the array to add to the <code>toString</code>,
1095      *  not <code>null</code>
1096      */
1097     protected void appendDetail(StringBuffer buffer, String fieldName, short[] array) {
1098         buffer.append(arrayStart);
1099         for (int i = 0; i < array.length; i++) {
1100             if (i > 0) {
1101                 buffer.append(arraySeparator);
1102             }
1103             appendDetail(buffer, fieldName, array[i]);
1104         }
1105         buffer.append(arrayEnd);
1106     }
1107 
1108     /**
1109      * <p>Append to the <code>toString</code> a summary of a
1110      * <code>short</code> array.</p>
1111      *
1112      * @param buffer  the <code>StringBuffer</code> to populate
1113      * @param fieldName  the field name, typically not used as already appended
1114      * @param array  the array to add to the <code>toString</code>,
1115      *  not <code>null</code>
1116      */
1117     protected void appendSummary(StringBuffer buffer, String fieldName, short[] array) {
1118         appendSummarySize(buffer, fieldName, array.length);
1119     }
1120 
1121     //----------------------------------------------------------------------------
1122 
1123     /**
1124      * <p>Append to the <code>toString</code> a <code>byte</code>
1125      * array.</p>
1126      *
1127      * @param buffer  the <code>StringBuffer</code> to populate
1128      * @param fieldName  the field name
1129      * @param array  the array to add to the <code>toString</code>
1130      * @param fullDetail  <code>true</code> for detail, <code>false</code>
1131      *  for summary info, <code>null</code> for style decides
1132      */
1133     public void append(StringBuffer buffer, String fieldName, byte[] array, Boolean fullDetail) {
1134         appendFieldStart(buffer, fieldName);
1135 
1136         if (array == null) {
1137             appendNullText(buffer, fieldName);
1138 
1139         } else if (isFullDetail(fullDetail)) {
1140             appendDetail(buffer, fieldName, array);
1141 
1142         } else {
1143             appendSummary(buffer, fieldName, array);
1144         }
1145 
1146         appendFieldEnd(buffer, fieldName);
1147     }
1148 
1149     /**
1150      * <p>Append to the <code>toString</code> the detail of a
1151      * <code>byte</code> array.</p>
1152      *
1153      * @param buffer  the <code>StringBuffer</code> to populate
1154      * @param fieldName  the field name, typically not used as already appended
1155      * @param array  the array to add to the <code>toString</code>,
1156      *  not <code>null</code>
1157      */
1158     protected void appendDetail(StringBuffer buffer, String fieldName, byte[] array) {
1159         buffer.append(arrayStart);
1160         for (int i = 0; i < array.length; i++) {
1161             if (i > 0) {
1162                 buffer.append(arraySeparator);
1163             }
1164             appendDetail(buffer, fieldName, array[i]);
1165         }
1166         buffer.append(arrayEnd);
1167     }
1168 
1169     /**
1170      * <p>Append to the <code>toString</code> a summary of a
1171      * <code>byte</code> array.</p>
1172      *
1173      * @param buffer  the <code>StringBuffer</code> to populate
1174      * @param fieldName  the field name, typically not used as already appended
1175      * @param array  the array to add to the <code>toString</code>,
1176      *  not <code>null</code>
1177      */
1178     protected void appendSummary(StringBuffer buffer, String fieldName, byte[] array) {
1179         appendSummarySize(buffer, fieldName, array.length);
1180     }
1181 
1182     //----------------------------------------------------------------------------
1183 
1184     /**
1185      * <p>Append to the <code>toString</code> a <code>char</code>
1186      * array.</p>
1187      *
1188      * @param buffer  the <code>StringBuffer</code> to populate
1189      * @param fieldName  the field name
1190      * @param array  the array to add to the <code>toString</code>
1191      * @param fullDetail  <code>true</code> for detail, <code>false</code>
1192      *  for summary info, <code>null</code> for style decides
1193      */
1194     public void append(StringBuffer buffer, String fieldName, char[] array, Boolean fullDetail) {
1195         appendFieldStart(buffer, fieldName);
1196 
1197         if (array == null) {
1198             appendNullText(buffer, fieldName);
1199 
1200         } else if (isFullDetail(fullDetail)) {
1201             appendDetail(buffer, fieldName, array);
1202 
1203         } else {
1204             appendSummary(buffer, fieldName, array);
1205         }
1206 
1207         appendFieldEnd(buffer, fieldName);
1208     }
1209 
1210     /**
1211      * <p>Append to the <code>toString</code> the detail of a
1212      * <code>char</code> array.</p>
1213      *
1214      * @param buffer  the <code>StringBuffer</code> to populate
1215      * @param fieldName  the field name, typically not used as already appended
1216      * @param array  the array to add to the <code>toString</code>,
1217      *  not <code>null</code>
1218      */
1219     protected void appendDetail(StringBuffer buffer, String fieldName, char[] array) {
1220         buffer.append(arrayStart);
1221         for (int i = 0; i < array.length; i++) {
1222             if (i > 0) {
1223                 buffer.append(arraySeparator);
1224             }
1225             appendDetail(buffer, fieldName, array[i]);
1226         }
1227         buffer.append(arrayEnd);
1228     }
1229 
1230     /**
1231      * <p>Append to the <code>toString</code> a summary of a
1232      * <code>char</code> array.</p>
1233      *
1234      * @param buffer  the <code>StringBuffer</code> to populate
1235      * @param fieldName  the field name, typically not used as already appended
1236      * @param array  the array to add to the <code>toString</code>,
1237      *  not <code>null</code>
1238      */
1239     protected void appendSummary(StringBuffer buffer, String fieldName, char[] array) {
1240         appendSummarySize(buffer, fieldName, array.length);
1241     }
1242 
1243     //----------------------------------------------------------------------------
1244 
1245     /**
1246      * <p>Append to the <code>toString</code> a <code>double</code>
1247      * array.</p>
1248      *
1249      * @param buffer  the <code>StringBuffer</code> to populate
1250      * @param fieldName  the field name
1251      * @param array  the array to add to the toString
1252      * @param fullDetail  <code>true</code> for detail, <code>false</code>
1253      *  for summary info, <code>null</code> for style decides
1254      */
1255     public void append(StringBuffer buffer, String fieldName, double[] array, Boolean fullDetail) {
1256         appendFieldStart(buffer, fieldName);
1257 
1258         if (array == null) {
1259             appendNullText(buffer, fieldName);
1260 
1261         } else if (isFullD