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.StringEscapeUtils;
28 import org.apache.commons.lang3.StringUtils;
29
30 /**
31  * <p>Controls {@code String} formatting for {@link ToStringBuilder}.
32  * The main public interface is always via {@code ToStringBuilder}.</p>
33  *
34  * <p>These classes are intended to be used as {@code Singletons}.
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}
43  * to {@code long} to {@code Object} to {@code int[]}) 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  * </p>
53  * <pre>
54  * public class MyStyle extends ToStringStyle {
55  *   protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
56  *     if (value instanceof Date) {
57  *       value = new SimpleDateFormat("yyyy-MM-dd").format(value);
58  *     }
59  *     buffer.append(value);
60  *   }
61  * }
62  * </pre>
63  *
64  * @since 1.0
65  */

66 @SuppressWarnings("deprecation"// StringEscapeUtils
67 public abstract class ToStringStyle implements Serializable {
68
69     /**
70      * Serialization version ID.
71      */

72     private static final long serialVersionUID = -2587890625525655916L;
73
74     /**
75      * The default toString style. Using the {@code Person}
76      * example from {@link ToStringBuilder}, the output would look like this:
77      *
78      * <pre>
79      * Person@182f0db[name=John Doe,age=33,smoker=false]
80      * </pre>
81      */

82     public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
83
84     /**
85      * The multi line toString style. Using the {@code Person}
86      * example from {@link ToStringBuilder}, the output would look like this:
87      *
88      * <pre>
89      * Person@182f0db[
90      *   name=John Doe
91      *   age=33
92      *   smoker=false
93      * ]
94      * </pre>
95      */

96     public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
97
98     /**
99      * The no field names toString style. Using the
100      * {@code Person} example from {@link ToStringBuilder}, the output
101      * would look like this:
102      *
103      * <pre>
104      * Person@182f0db[John Doe,33,false]
105      * </pre>
106      */

107     public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
108
109     /**
110      * The short prefix toString style. Using the {@code Person} example
111      * from {@link ToStringBuilder}, the output would look like this:
112      *
113      * <pre>
114      * Person[name=John Doe,age=33,smoker=false]
115      * </pre>
116      *
117      * @since 2.1
118      */

119     public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
120
121     /**
122      * The simple toString style. Using the {@code Person}
123      * example from {@link ToStringBuilder}, the output would look like this:
124      *
125      * <pre>
126      * John Doe,33,false
127      * </pre>
128      */

129     public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
130
131     /**
132      * The no class name toString style. Using the {@code Person}
133      * example from {@link ToStringBuilder}, the output would look like this:
134      *
135      * <pre>
136      * [name=John Doe,age=33,smoker=false]
137      * </pre>
138      *
139      * @since 3.4
140      */

141     public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();
142
143     /**
144      * The JSON toString style. Using the {@code Person} example from
145      * {@link ToStringBuilder}, the output would look like this:
146      *
147      * <pre>
148      * {"name""John Doe""age": 33, "smoker"true}
149      * </pre>
150      *
151      * <strong>Note:</strong> Since field names are mandatory in JSON, this
152      * ToStringStyle will throw an {@link UnsupportedOperationException} if no
153      * field name is passed in while appending. Furthermore This ToStringStyle
154      * will only generate valid JSON if referenced objects also produce JSON
155      * when calling {@code toString()} on them.
156      *
157      * @since 3.4
158      * @see <a href="http://json.org">json.org</a>
159      */

160     public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
161
162     /**
163      * <p>
164      * A registry of objects used by {@code reflectionToString} methods
165      * to detect cyclical object references and avoid infinite loops.
166      * </p>
167      */

168     private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY =
169         new ThreadLocal<>();
170     /*
171      * Note that objects of this class are generally shared between threads, so
172      * an instance variable would not be suitable here.
173      *
174      * In normal use the registry should always be left empty, because the caller
175      * should call toString() which will clean up.
176      *
177      * See LANG-792
178      */

179
180     /**
181      * <p>
182      * Returns the registry of objects being traversed by the {@code reflectionToString}
183      * methods in the current thread.
184      * </p>
185      *
186      * @return Set the registry of objects being traversed
187      */

188     static Map<Object, Object> getRegistry() {
189         return REGISTRY.get();
190     }
191
192     /**
193      * <p>
194      * Returns {@code trueif the registry contains the given object.
195      * Used by the reflection methods to avoid infinite loops.
196      * </p>
197      *
198      * @param value
199      *                  The object to lookup in the registry.
200      * @return boolean {@code trueif the registry contains the given
201      *             object.
202      */

203     static boolean isRegistered(final Object value) {
204         final Map<Object, Object> m = getRegistry();
205         return m != null && m.containsKey(value);
206     }
207
208     /**
209      * <p>
210      * Registers the given object. Used by the reflection methods to avoid
211      * infinite loops.
212      * </p>
213      *
214      * @param value
215      *                  The object to register.
216      */

217     static void register(final Object value) {
218         if (value != null) {
219             final Map<Object, Object> m = getRegistry();
220             if (m == null) {
221                 REGISTRY.set(new WeakHashMap<>());
222             }
223             getRegistry().put(value, null);
224         }
225     }
226
227     /**
228      * <p>
229      * Unregisters the given object.
230      * </p>
231      *
232      * <p>
233      * Used by the reflection methods to avoid infinite loops.
234      * </p>
235      *
236      * @param value
237      *                  The object to unregister.
238      */

239     static void unregister(final Object value) {
240         if (value != null) {
241             final Map<Object, Object> m = getRegistry();
242             if (m != null) {
243                 m.remove(value);
244                 if (m.isEmpty()) {
245                     REGISTRY.remove();
246                 }
247             }
248         }
249     }
250
251     /**
252      * Whether to use the field names, the default is {@code true}.
253      */

254     private boolean useFieldNames = true;
255
256     /**
257      * Whether to use the class name, the default is {@code true}.
258      */

259     private boolean useClassName = true;
260
261     /**
262      * Whether to use short class names, the default is {@code false}.
263      */

264     private boolean useShortClassName = false;
265
266     /**
267      * Whether to use the identity hash code, the default is {@code true}.
268      */

269     private boolean useIdentityHashCode = true;
270
271     /**
272      * The content start {@code '['}.
273      */

274     private String contentStart = "[";
275
276     /**
277      * The content end {@code ']'}.
278      */

279     private String contentEnd = "]";
280
281     /**
282      * The field name value separator {@code '='}.
283      */

284     private String fieldNameValueSeparator = "=";
285
286     /**
287      * Whether the field separator should be added before any other fields.
288      */

289     private boolean fieldSeparatorAtStart = false;
290
291     /**
292      * Whether the field separator should be added after any other fields.
293      */

294     private boolean fieldSeparatorAtEnd = false;
295
296     /**
297      * The field separator {@code ','}.
298      */

299     private String fieldSeparator = ",";
300
301     /**
302      * The array start <code>'{'</code>.
303      */

304     private String arrayStart = "{";
305
306     /**
307      * The array separator {@code ','}.
308      */

309     private String arraySeparator = ",";
310
311     /**
312      * The detail for array content.
313      */

314     private boolean arrayContentDetail = true;
315
316     /**
317      * The array end {@code '}'}.
318      */

319     private String arrayEnd = "}";
320
321     /**
322      * The value to use when fullDetail is {@code null},
323      * the default value is {@code true}.
324      */

325     private boolean defaultFullDetail = true;
326
327     /**
328      * The {@code null} text {@code '&lt;null&gt;'}.
329      */

330     private String nullText = "<null>";
331
332     /**
333      * The summary size text start {@code '&lt;size'}.
334      */

335     private String sizeStartText = "<size=";
336
337     /**
338      * The summary size text start {@code '&gt;'}.
339      */

340     private String sizeEndText = ">";
341
342     /**
343      * The summary object text start {@code '&lt;'}.
344      */

345     private String summaryObjectStartText = "<";
346
347     /**
348      * The summary object text start {@code '&gt;'}.
349      */

350     private String summaryObjectEndText = ">";
351
352     //----------------------------------------------------------------------------
353
354     /**
355      * <p>Constructor.</p>
356      */

357     protected ToStringStyle() {
358         super();
359     }
360
361     //----------------------------------------------------------------------------
362
363     /**
364      * <p>Append to the {@code toString} the superclass toString.</p>
365      * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
366      *
367      * <p>A {@code null} {@code superToString} is ignored.</p>
368      *
369      * @param buffer  the {@code StringBuffer} to populate
370      * @param superToString  the {@code super.toString()}
371      * @since 2.0
372      */

373     public void appendSuper(final StringBuffer buffer, final String superToString) {
374         appendToString(buffer, superToString);
375     }
376
377     /**
378      * <p>Append to the {@code toString} another toString.</p>
379      * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
380      *
381      * <p>A {@code null} {@code toString} is ignored.</p>
382      *
383      * @param buffer  the {@code StringBuffer} to populate
384      * @param toString  the additional {@code toString}
385      * @since 2.0
386      */

387     public void appendToString(final StringBuffer buffer, final String toString) {
388         if (toString != null) {
389             final int pos1 = toString.indexOf(contentStart) + contentStart.length();
390             final int pos2 = toString.lastIndexOf(contentEnd);
391             if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
392                 if (fieldSeparatorAtStart) {
393                     removeLastFieldSeparator(buffer);
394                 }
395                 buffer.append(toString, pos1, pos2);
396                 appendFieldSeparator(buffer);
397             }
398         }
399     }
400
401     /**
402      * <p>Append to the {@code toString} the start of data indicator.</p>
403      *
404      * @param buffer  the {@code StringBuffer} to populate
405      * @param object  the {@code Object} to build a {@code toString} for
406      */

407     public void appendStart(final StringBuffer buffer, final Object object) {
408         if (object != null) {
409             appendClassName(buffer, object);
410             appendIdentityHashCode(buffer, object);
411             appendContentStart(buffer);
412             if (fieldSeparatorAtStart) {
413                 appendFieldSeparator(buffer);
414             }
415         }
416     }
417
418     /**
419      * <p>Append to the {@code toString} the end of data indicator.</p>
420      *
421      * @param buffer  the {@code StringBuffer} to populate
422      * @param object  the {@code Object} to build a
423      *  {@code toString} for.
424      */

425     public void appendEnd(final StringBuffer buffer, final Object object) {
426         if (!this.fieldSeparatorAtEnd) {
427             removeLastFieldSeparator(buffer);
428         }
429         appendContentEnd(buffer);
430         unregister(object);
431     }
432
433     /**
434      * <p>Remove the last field separator from the buffer.</p>
435      *
436      * @param buffer  the {@code StringBuffer} to populate
437      * @since 2.0
438      */

439     protected void removeLastFieldSeparator(final StringBuffer buffer) {
440         final int len = buffer.length();
441         final int sepLen = fieldSeparator.length();
442         if (len > 0 && sepLen > 0 && len >= sepLen) {
443             boolean match = true;
444             for (int i = 0; i < sepLen; i++) {
445                 if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
446                     match = false;
447                     break;
448                 }
449             }
450             if (match) {
451                 buffer.setLength(len - sepLen);
452             }
453         }
454     }
455
456     //----------------------------------------------------------------------------
457
458     /**
459      * <p>Append to the {@code toString} an {@code Object}
460      * value, printing the full {@code toString} of the
461      * {@code Object} passed in.</p>
462      *
463      * @param buffer  the {@code StringBuffer} to populate
464      * @param fieldName  the field name
465      * @param value  the value to add to the {@code toString}
466      * @param fullDetail  {@code truefor detail, {@code false}
467      *  for summary info, {@code nullfor style decides
468      */

469     public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
470         appendFieldStart(buffer, fieldName);
471
472         if (value == null) {
473             appendNullText(buffer, fieldName);
474
475         } else {
476             appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
477         }
478
479         appendFieldEnd(buffer, fieldName);
480     }
481
482     /**
483      * <p>Append to the {@code toString} an {@code Object},
484      * correctly interpreting its type.</p>
485      *
486      * <p>This method performs the main lookup by Class type to correctly
487      * route arrays, {@code Collections}, {@code Maps} and
488      * {@code Objects} to the appropriate method.</p>
489      *
490      * <p>Either detail or summary views can be specified.</p>
491      *
492      * <p>If a cycle is detected, an object will be appended with the
493      * {@code Object.toString()} format.</p>
494      *
495      * @param buffer  the {@code StringBuffer} to populate
496      * @param fieldName  the field name, typically not used as already appended
497      * @param value  the value to add to the {@code toString},
498      *  not {@code null}
499      * @param detail  output detail or not
500      */

501     protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
502         if (isRegistered(value)
503             && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
504            appendCyclicObject(buffer, fieldName, value);
505            return;
506         }
507
508         register(value);
509
510         try {
511             if (value instanceof Collection<?>) {
512                 if (detail) {
513                     appendDetail(buffer, fieldName, (Collection<?>) value);
514                 } else {
515                     appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
516                 }
517
518             } else if (value instanceof Map<?, ?>) {
519                 if (detail) {
520                     appendDetail(buffer, fieldName, (Map<?, ?>) value);
521                 } else {
522                     appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
523                 }
524
525             } else if (value instanceof long[]) {
526                 if (detail) {
527                     appendDetail(buffer, fieldName, (long[]) value);
528                 } else {
529                     appendSummary(buffer, fieldName, (long[]) value);
530                 }
531
532             } else if (value instanceof int[]) {
533                 if (detail) {
534                     appendDetail(buffer, fieldName, (int[]) value);
535                 } else {
536                     appendSummary(buffer, fieldName, (int[]) value);
537                 }
538
539             } else if (value instanceof short[]) {
540                 if (detail) {
541                     appendDetail(buffer, fieldName, (short[]) value);
542                 } else {
543                     appendSummary(buffer, fieldName, (short[]) value);
544                 }
545
546             } else if (value instanceof byte[]) {
547                 if (detail) {
548                     appendDetail(buffer, fieldName, (byte[]) value);
549                 } else {
550                     appendSummary(buffer, fieldName, (byte[]) value);
551                 }
552
553             } else if (value instanceof char[]) {
554                 if (detail) {
555                     appendDetail(buffer, fieldName, (char[]) value);
556                 } else {
557                     appendSummary(buffer, fieldName, (char[]) value);
558                 }
559
560             } else if (value instanceof double[]) {
561                 if (detail) {
562                     appendDetail(buffer, fieldName, (double[]) value);
563                 } else {
564                     appendSummary(buffer, fieldName, (double[]) value);
565                 }
566
567             } else if (value instanceof float[]) {
568                 if (detail) {
569                     appendDetail(buffer, fieldName, (float[]) value);
570                 } else {
571                     appendSummary(buffer, fieldName, (float[]) value);
572                 }
573
574             } else if (value instanceof boolean[]) {
575                 if (detail) {
576                     appendDetail(buffer, fieldName, (boolean[]) value);
577                 } else {
578                     appendSummary(buffer, fieldName, (boolean[]) value);
579                 }
580
581             } else if (value.getClass().isArray()) {
582                 if (detail) {
583                     appendDetail(buffer, fieldName, (Object[]) value);
584                 } else {
585                     appendSummary(buffer, fieldName, (Object[]) value);
586                 }
587
588             } else {
589                 if (detail) {
590                     appendDetail(buffer, fieldName, value);
591                 } else {
592                     appendSummary(buffer, fieldName, value);
593                 }
594             }
595         } finally {
596             unregister(value);
597         }
598     }
599
600     /**
601      * <p>Append to the {@code toString} an {@code Object}
602      * value that has been detected to participate in a cycle. This
603      * implementation will print the standard string value of the value.</p>
604      *
605      * @param buffer  the {@code StringBuffer} to populate
606      * @param fieldName  the field name, typically not used as already appended
607      * @param value  the value to add to the {@code toString},
608      *  not {@code null}
609      *
610      * @since 2.2
611      */

612     protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
613        ObjectUtils.identityToString(buffer, value);
614     }
615
616     /**
617      * <p>Append to the {@code toString} an {@code Object}
618      * value, printing the full detail of the {@code Object}.</p>
619      *
620      * @param buffer  the {@code StringBuffer} to populate
621      * @param fieldName  the field name, typically not used as already appended
622      * @param value  the value to add to the {@code toString},
623      *  not {@code null}
624      */

625     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
626         buffer.append(value);
627     }
628
629     /**
630      * <p>Append to the {@code toString} a {@code Collection}.</p>
631      *
632      * @param buffer  the {@code StringBuffer} to populate
633      * @param fieldName  the field name, typically not used as already appended
634      * @param coll  the {@code Collection} to add to the
635      *  {@code toString}, not {@code null}
636      */

637     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
638         buffer.append(coll);
639     }
640
641     /**
642      * <p>Append to the {@code toString} a {@code Map}.</p>
643      *
644      * @param buffer  the {@code StringBuffer} to populate
645      * @param fieldName  the field name, typically not used as already appended
646      * @param map  the {@code Map} to add to the {@code toString},
647      *  not {@code null}
648      */

649     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
650         buffer.append(map);
651     }
652
653     /**
654      * <p>Append to the {@code toString} an {@code Object}
655      * value, printing a summary of the {@code Object}.</P>
656      *
657      * @param buffer  the {@code StringBuffer} to populate
658      * @param fieldName  the field name, typically not used as already appended
659      * @param value  the value to add to the {@code toString},
660      *  not {@code null}
661      */

662     protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
663         buffer.append(summaryObjectStartText);
664         buffer.append(getShortClassName(value.getClass()));
665         buffer.append(summaryObjectEndText);
666     }
667
668     //----------------------------------------------------------------------------
669
670     /**
671      * <p>Append to the {@code toString} a {@code long}
672      * value.</p>
673      *
674      * @param buffer  the {@code StringBuffer} to populate
675      * @param fieldName  the field name
676      * @param value  the value to add to the {@code toString}
677      */

678     public void append(final StringBuffer buffer, final String fieldName, final long value) {
679         appendFieldStart(buffer, fieldName);
680         appendDetail(buffer, fieldName, value);
681         appendFieldEnd(buffer, fieldName);
682     }
683
684     /**
685      * <p>Append to the {@code toString} a {@code long}
686      * value.</p>
687      *
688      * @param buffer  the {@code StringBuffer} to populate
689      * @param fieldName  the field name, typically not used as already appended
690      * @param value  the value to add to the {@code toString}
691      */

692     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
693         buffer.append(value);
694     }
695
696     //----------------------------------------------------------------------------
697
698     /**
699      * <p>Append to the {@code toString} an {@code int}
700      * value.</p>
701      *
702      * @param buffer  the {@code StringBuffer} to populate
703      * @param fieldName  the field name
704      * @param value  the value to add to the {@code toString}
705      */

706     public void append(final StringBuffer buffer, final String fieldName, final int value) {
707         appendFieldStart(buffer, fieldName);
708         appendDetail(buffer, fieldName, value);
709         appendFieldEnd(buffer, fieldName);
710     }
711
712     /**
713      * <p>Append to the {@code toString} an {@code int}
714      * value.</p>
715      *
716      * @param buffer  the {@code StringBuffer} to populate
717      * @param fieldName  the field name, typically not used as already appended
718      * @param value  the value to add to the {@code toString}
719      */

720     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
721         buffer.append(value);
722     }
723
724     //----------------------------------------------------------------------------
725
726     /**
727      * <p>Append to the {@code toString} a {@code short}
728      * value.</p>
729      *
730      * @param buffer  the {@code StringBuffer} to populate
731      * @param fieldName  the field name
732      * @param value  the value to add to the {@code toString}
733      */

734     public void append(final StringBuffer buffer, final String fieldName, final short value) {
735         appendFieldStart(buffer, fieldName);
736         appendDetail(buffer, fieldName, value);
737         appendFieldEnd(buffer, fieldName);
738     }
739
740     /**
741      * <p>Append to the {@code toString} a {@code short}
742      * value.</p>
743      *
744      * @param buffer  the {@code StringBuffer} to populate
745      * @param fieldName  the field name, typically not used as already appended
746      * @param value  the value to add to the {@code toString}
747      */

748     protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
749         buffer.append(value);
750     }
751
752     //----------------------------------------------------------------------------
753
754     /**
755      * <p>Append to the {@code toString} a {@code byte}
756      * value.</p>
757      *
758      * @param buffer  the {@code StringBuffer} to populate
759      * @param fieldName  the field name
760      * @param value  the value to add to the {@code toString}
761      */

762     public void append(final StringBuffer buffer, final String fieldName, final byte value) {
763         appendFieldStart(buffer, fieldName);
764         appendDetail(buffer, fieldName, value);
765         appendFieldEnd(buffer, fieldName);
766     }
767
768     /**
769      * <p>Append to the {@code toString} a {@code byte}
770      * value.</p>
771      *
772      * @param buffer  the {@code StringBuffer} to populate
773      * @param fieldName  the field name, typically not used as already appended
774      * @param value  the value to add to the {@code toString}
775      */

776     protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
777         buffer.append(value);
778     }
779
780     //----------------------------------------------------------------------------
781
782     /**
783      * <p>Append to the {@code toString} a {@code char}
784      * value.</p>
785      *
786      * @param buffer  the {@code StringBuffer} to populate
787      * @param fieldName  the field name
788      * @param value  the value to add to the {@code toString}
789      */

790     public void append(final StringBuffer buffer, final String fieldName, final char value) {
791         appendFieldStart(buffer, fieldName);
792         appendDetail(buffer, fieldName, value);
793         appendFieldEnd(buffer, fieldName);
794     }
795
796     /**
797      * <p>Append to the {@code toString} a {@code char}
798      * value.</p>
799      *
800      * @param buffer  the {@code StringBuffer} to populate
801      * @param fieldName  the field name, typically not used as already appended
802      * @param value  the value to add to the {@code toString}
803      */

804     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
805         buffer.append(value);
806     }
807
808     //----------------------------------------------------------------------------
809
810     /**
811      * <p>Append to the {@code toString} a {@code double}
812      * value.</p>
813      *
814      * @param buffer  the {@code StringBuffer} to populate
815      * @param fieldName  the field name
816      * @param value  the value to add to the {@code toString}
817      */

818     public void append(final StringBuffer buffer, final String fieldName, final double value) {
819         appendFieldStart(buffer, fieldName);
820         appendDetail(buffer, fieldName, value);
821         appendFieldEnd(buffer, fieldName);
822     }
823
824     /**
825      * <p>Append to the {@code toString} a {@code double}
826      * value.</p>
827      *
828      * @param buffer  the {@code StringBuffer} to populate
829      * @param fieldName  the field name, typically not used as already appended
830      * @param value  the value to add to the {@code toString}
831      */

832     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
833         buffer.append(value);
834     }
835
836     //----------------------------------------------------------------------------
837
838     /**
839      * <p>Append to the {@code toString} a {@code float}
840      * value.</p>
841      *
842      * @param buffer  the {@code StringBuffer} to populate
843      * @param fieldName  the field name
844      * @param value  the value to add to the {@code toString}
845      */

846     public void append(final StringBuffer buffer, final String fieldName, final float value) {
847         appendFieldStart(buffer, fieldName);
848         appendDetail(buffer, fieldName, value);
849         appendFieldEnd(buffer, fieldName);
850     }
851
852     /**
853      * <p>Append to the {@code toString} a {@code float}
854      * value.</p>
855      *
856      * @param buffer  the {@code StringBuffer} to populate
857      * @param fieldName  the field name, typically not used as already appended
858      * @param value  the value to add to the {@code toString}
859      */

860     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
861         buffer.append(value);
862     }
863
864     //----------------------------------------------------------------------------
865
866     /**
867      * <p>Append to the {@code toString} a {@code boolean}
868      * value.</p>
869      *
870      * @param buffer  the {@code StringBuffer} to populate
871      * @param fieldName  the field name
872      * @param value  the value to add to the {@code toString}
873      */

874     public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
875         appendFieldStart(buffer, fieldName);
876         appendDetail(buffer, fieldName, value);
877         appendFieldEnd(buffer, fieldName);
878     }
879
880     /**
881      * <p>Append to the {@code toString} a {@code boolean}
882      * value.</p>
883      *
884      * @param buffer  the {@code StringBuffer} to populate
885      * @param fieldName  the field name, typically not used as already appended
886      * @param value  the value to add to the {@code toString}
887      */

888     protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
889         buffer.append(value);
890     }
891
892     /**
893      * <p>Append to the {@code toString} an {@code Object}
894      * array.</p>
895      *
896      * @param buffer  the {@code StringBuffer} to populate
897      * @param fieldName  the field name
898      * @param array  the array to add to the toString
899      * @param fullDetail  {@code truefor detail, {@code false}
900      *  for summary info, {@code nullfor style decides
901      */

902     public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
903         appendFieldStart(buffer, fieldName);
904
905         if (array == null) {
906             appendNullText(buffer, fieldName);
907
908         } else if (isFullDetail(fullDetail)) {
909             appendDetail(buffer, fieldName, array);
910
911         } else {
912             appendSummary(buffer, fieldName, array);
913         }
914
915         appendFieldEnd(buffer, fieldName);
916     }
917
918     //----------------------------------------------------------------------------
919
920     /**
921      * <p>Append to the {@code toString} the detail of an
922      * {@code Object} array.</p>
923      *
924      * @param buffer  the {@code StringBuffer} to populate
925      * @param fieldName  the field name, typically not used as already appended
926      * @param array  the array to add to the {@code toString},
927      *  not {@code null}
928      */

929     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
930         buffer.append(arrayStart);
931         for (int i = 0; i < array.length; i++) {
932             final Object item = array[i];
933             if (i > 0) {
934                 buffer.append(arraySeparator);
935             }
936             if (item == null) {
937                 appendNullText(buffer, fieldName);
938
939             } else {
940                 appendInternal(buffer, fieldName, item, arrayContentDetail);
941             }
942         }
943         buffer.append(arrayEnd);
944     }
945
946     /**
947      * <p>Append to the {@code toString} the detail of an array type.</p>
948      *
949      * @param buffer  the {@code StringBuffer} to populate
950      * @param fieldName  the field name, typically not used as already appended
951      * @param array  the array to add to the {@code toString},
952      *  not {@code null}
953      * @since 2.0
954      */

955     protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
956         buffer.append(arrayStart);
957         final int length = Array.getLength(array);
958         for (int i = 0; i < length; i++) {
959             final Object item = Array.get(array, i);
960             if (i > 0) {
961                 buffer.append(arraySeparator);
962             }
963             if (item == null) {
964                 appendNullText(buffer, fieldName);
965
966             } else {
967                 appendInternal(buffer, fieldName, item, arrayContentDetail);
968             }
969         }
970         buffer.append(arrayEnd);
971     }
972
973     /**
974      * <p>Append to the {@code toString} a summary of an
975      * {@code Object} array.</p>
976      *
977      * @param buffer  the {@code StringBuffer} to populate
978      * @param fieldName  the field name, typically not used as already appended
979      * @param array  the array to add to the {@code toString},
980      *  not {@code null}
981      */

982     protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
983         appendSummarySize(buffer, fieldName, array.length);
984     }
985
986     //----------------------------------------------------------------------------
987
988     /**
989      * <p>Append to the {@code toString} a {@code long}
990      * array.</p>
991      *
992      * @param buffer  the {@code StringBuffer} to populate
993      * @param fieldName  the field name
994      * @param array  the array to add to the {@code toString}
995      * @param fullDetail  {@code truefor detail, {@code false}
996      *  for summary info, {@code nullfor style decides
997      */

998     public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
999         appendFieldStart(buffer, fieldName);
1000
1001         if (array == null) {
1002             appendNullText(buffer, fieldName);
1003
1004         } else if (isFullDetail(fullDetail)) {
1005             appendDetail(buffer, fieldName, array);
1006
1007         } else {
1008             appendSummary(buffer, fieldName, array);
1009         }
1010
1011         appendFieldEnd(buffer, fieldName);
1012     }
1013
1014     /**
1015      * <p>Append to the {@code toString} the detail of a
1016      * {@code long} array.</p>
1017      *
1018      * @param buffer  the {@code StringBuffer} to populate
1019      * @param fieldName  the field name, typically not used as already appended
1020      * @param array  the array to add to the {@code toString},
1021      *  not {@code null}
1022      */

1023     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
1024         buffer.append(arrayStart);
1025         for (int i = 0; i < array.length; i++) {
1026             if (i > 0) {
1027                 buffer.append(arraySeparator);
1028             }
1029             appendDetail(buffer, fieldName, array[i]);
1030         }
1031         buffer.append(arrayEnd);
1032     }
1033
1034     /**
1035      * <p>Append to the {@code toString} a summary of a
1036      * {@code long} array.</p>
1037      *
1038      * @param buffer  the {@code StringBuffer} to populate
1039      * @param fieldName  the field name, typically not used as already appended
1040      * @param array  the array to add to the {@code toString},
1041      *  not {@code null}
1042      */

1043     protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
1044         appendSummarySize(buffer, fieldName, array.length);
1045     }
1046
1047     //----------------------------------------------------------------------------
1048
1049     /**
1050      * <p>Append to the {@code toString} an {@code int}
1051      * array.</p>
1052      *
1053      * @param buffer  the {@code StringBuffer} to populate
1054      * @param fieldName  the field name
1055      * @param array  the array to add to the {@code toString}
1056      * @param fullDetail  {@code truefor detail, {@code false}
1057      *  for summary info, {@code nullfor style decides
1058      */

1059     public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
1060         appendFieldStart(buffer, fieldName);
1061
1062         if (array == null) {
1063             appendNullText(buffer, fieldName);
1064
1065         } else if (isFullDetail(fullDetail)) {
1066             appendDetail(buffer, fieldName, array);
1067
1068         } else {
1069             appendSummary(buffer, fieldName, array);
1070         }
1071
1072         appendFieldEnd(buffer, fieldName);
1073     }
1074
1075     /**
1076      * <p>Append to the {@code toString} the detail of an
1077      * {@code int} array.</p>
1078      *
1079      * @param buffer  the {@code StringBuffer} to populate
1080      * @param fieldName  the field name, typically not used as already appended
1081      * @param array  the array to add to the {@code toString},
1082      *  not {@code null}
1083      */

1084     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
1085         buffer.append(arrayStart);
1086         for (int i = 0; i < array.length; i++) {
1087             if (i > 0) {
1088                 buffer.append(arraySeparator);
1089             }
1090             appendDetail(buffer, fieldName, array[i]);
1091         }
1092         buffer.append(arrayEnd);
1093     }
1094
1095     /**
1096      * <p>Append to the {@code toString} a summary of an
1097      * {@code int} array.</p>
1098      *
1099      * @param buffer  the {@code StringBuffer} to populate
1100      * @param fieldName  the field name, typically not used as already appended
1101      * @param array  the array to add to the {@code toString},
1102      *  not {@code null}
1103      */

1104     protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
1105         appendSummarySize(buffer, fieldName, array.length);
1106     }
1107
1108     //----------------------------------------------------------------------------
1109
1110     /**
1111      * <p>Append to the {@code toString} a {@code short}
1112      * array.</p>
1113      *
1114      * @param buffer  the {@code StringBuffer} to populate
1115      * @param fieldName  the field name
1116      * @param array  the array to add to the {@code toString}
1117      * @param fullDetail  {@code truefor detail, {@code false}
1118      *  for summary info, {@code nullfor style decides
1119      */

1120     public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
1121         appendFieldStart(buffer, fieldName);
1122
1123         if (array == null) {
1124             appendNullText(buffer, fieldName);
1125
1126         } else if (isFullDetail(fullDetail)) {
1127             appendDetail(buffer, fieldName, array);
1128
1129         } else {
1130             appendSummary(buffer, fieldName, array);
1131         }
1132
1133         appendFieldEnd(buffer, fieldName);
1134     }
1135
1136     /**
1137      * <p>Append to the {@code toString} the detail of a
1138      * {@code short} array.</p>
1139      *
1140      * @param buffer  the {@code StringBuffer} to populate
1141      * @param fieldName  the field name, typically not used as already appended
1142      * @param array  the array to add to the {@code toString},
1143      *  not {@code null}
1144      */

1145     protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
1146         buffer.append(arrayStart);
1147         for (int i = 0; i < array.length; i++) {
1148             if (i > 0) {
1149                 buffer.append(arraySeparator);
1150             }
1151             appendDetail(buffer, fieldName, array[i]);
1152         }
1153         buffer.append(arrayEnd);
1154     }
1155
1156     /**
1157      * <p>Append to the {@code toString} a summary of a
1158      * {@code short} array.</p>
1159      *
1160      * @param buffer  the {@code StringBuffer} to populate
1161      * @param fieldName  the field name, typically not used as already appended
1162      * @param array  the array to add to the {@code toString},
1163      *  not {@code null}
1164      */

1165     protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
1166         appendSummarySize(buffer, fieldName, array.length);
1167     }
1168
1169     //----------------------------------------------------------------------------
1170
1171     /**
1172      * <p>Append to the {@code toString} a {@code byte}
1173      * array.</p>
1174      *
1175      * @param buffer  the {@code StringBuffer} to populate
1176      * @param fieldName  the field name
1177      * @param array  the array to add to the {@code toString}
1178      * @param fullDetail  {@code truefor detail, {@code false}
1179      *  for summary info, {@code nullfor style decides
1180      */

1181     public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
1182         appendFieldStart(buffer, fieldName);
1183
1184         if (array == null) {
1185             appendNullText(buffer, fieldName);
1186
1187         } else if (isFullDetail(fullDetail)) {
1188             appendDetail(buffer, fieldName, array);
1189
1190         } else {
1191             appendSummary(buffer, fieldName, array);
1192         }
1193
1194         appendFieldEnd(buffer, fieldName);
1195     }
1196
1197     /**
1198      * <p>Append to the {@code toString} the detail of a
1199      * {@code byte} array.</p>
1200      *
1201      * @param buffer  the {@code StringBuffer} to populate
1202      * @param fieldName  the field name, typically not used as already appended
1203      * @param array  the array to add to the {@code toString},
1204      *  not {@code null}
1205      */

1206     protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
1207         buffer.append(arrayStart);
1208         for (int i = 0; i < array.length; i++) {
1209             if (i > 0) {
1210                 buffer.append(arraySeparator);
1211             }
1212             appendDetail(buffer, fieldName, array[i]);
1213         }
1214         buffer.append(arrayEnd);
1215     }
1216
1217     /**
1218      * <p>Append to the {@code toString} a summary of a
1219      * {@code byte} array.</p>
1220      *
1221      * @param buffer  the {@code StringBuffer} to populate
1222      * @param fieldName  the field name, typically not used as already appended
1223      * @param array  the array to add to the {@code toString},
1224      *  not {@code null}
1225      */

1226     protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
1227         appendSummarySize(buffer, fieldName, array.length);
1228     }
1229
1230     //----------------------------------------------------------------------------
1231
1232     /**
1233      * <p>Append to the {@code toString} a {@code char}
1234      * array.</p>
1235      *
1236      * @param buffer  the {@code StringBuffer} to populate
1237      * @param fieldName  the field name
1238      * @param array  the array to add to the {@code toString}
1239      * @param fullDetail  {@code truefor detail, {@code false}
1240      *  for summary info, {@code nullfor style decides
1241      */

1242     public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
1243         appendFieldStart(buffer, fieldName);
1244
1245         if (array == null) {
1246             appendNullText(buffer, fieldName);
1247
1248         } else if (isFullDetail(fullDetail)) {
1249             appendDetail(buffer, fieldName, array);
1250
1251         } else {
1252             appendSummary(buffer, fieldName, array);
1253         }
1254
1255         appendFieldEnd(buffer, fieldName);
1256     }
1257
1258     /**
1259      * <p>Append to the {@code toString} the detail of a
1260      * {@code char} array.</p>
1261      *
1262      * @param buffer  the {@code StringBuffer} to populate
1263      * @param fieldName  the field name, typically not used as already appended
1264      * @param array  the array to add to the {@code toString},
1265      *  not {@code null}
1266      */

1267     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
1268         buffer.append(arrayStart);
1269         for (int i = 0; i < array.length; i++) {
1270             if (i > 0) {
1271                 buffer.append(arraySeparator);
1272             }
1273             appendDetail(buffer, fieldName, array[i]);
1274         }
1275         buffer.append(arrayEnd);
1276     }
1277
1278     /**
1279      * <p>Append to the {@code toString} a summary of a
1280      * {@code char} array.</p>
1281      *
1282      * @param buffer  the {@code StringBuffer} to populate
1283      * @param fieldName  the field name, typically not used as already appended
1284      * @param array  the array to add to the {@code toString},
1285      *  not {@code null}
1286      */

1287     protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
1288         appendSummarySize(buffer, fieldName, array.length);
1289     }
1290
1291     //----------------------------------------------------------------------------
1292
1293     /**
1294      * <p>Append to the {@code toString} a {@code double}
1295      * array.</p>
1296      *
1297      * @param buffer  the {@code StringBuffer} to populate
1298      * @param fieldName  the field name
1299      * @param array  the array to add to the toString
1300      * @param fullDetail  {@code truefor detail, {@code false}
1301      *  for summary info, {@code nullfor style decides
1302      */

1303     public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
1304         appendFieldStart(buffer, fieldName);
1305
1306         if (array == null) {
1307             appendNullText(buffer, fieldName);
1308
1309         } else if (isFullDetail(fullDetail)) {
1310             appendDetail(buffer, fieldName, array);
1311
1312         } else {
1313             appendSummary(buffer, fieldName, array);
1314         }
1315
1316         appendFieldEnd(buffer, fieldName);
1317     }
1318
1319     /**
1320      * <p>Append to the {@code toString} the detail of a
1321      * {@code double} array.</p>
1322      *
1323      * @param buffer  the {@code StringBuffer} to populate
1324      * @param fieldName  the field name, typically not used as already appended
1325      * @param array  the array to add to the {@code toString},
1326      *  not {@code null}
1327      */

1328     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
1329         buffer.append(arrayStart);
1330         for (int i = 0; i < array.length; i++) {
1331             if (i > 0) {
1332                 buffer.append(arraySeparator);
1333             }
1334             appendDetail(buffer, fieldName, array[i]);
1335         }
1336         buffer.append(arrayEnd);
1337     }
1338
1339     /**
1340      * <p>Append to the {@code toString} a summary of a
1341      * {@code double} array.</p>
1342      *
1343      * @param buffer  the {@code StringBuffer} to populate
1344      * @param fieldName  the field name, typically not used as already appended
1345      * @param array  the array to add to the {@code toString},
1346      *  not {@code null}
1347      */

1348     protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
1349         appendSummarySize(buffer, fieldName, array.length);
1350     }
1351
1352     //----------------------------------------------------------------------------
1353
1354     /**
1355      * <p>Append to the {@code toString} a {@code float}
1356      * array.</p>
1357      *
1358      * @param buffer  the {@code StringBuffer} to populate
1359      * @param fieldName  the field name
1360      * @param array  the array to add to the toString
1361      * @param fullDetail  {@code truefor detail, {@code false}
1362      *  for summary info, {@code nullfor style decides
1363      */

1364     public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
1365         appendFieldStart(buffer, fieldName);
1366
1367         if (array == null) {
1368             appendNullText(buffer, fieldName);
1369
1370         } else if (isFullDetail(fullDetail)) {
1371             appendDetail(buffer, fieldName, array);
1372
1373         } else {
1374             appendSummary(buffer, fieldName, array);
1375         }
1376
1377         appendFieldEnd(buffer, fieldName);
1378     }
1379
1380     /**
1381      * <p>Append to the {@code toString} the detail of a
1382      * {@code float} array.</p>
1383      *
1384      * @param buffer  the {@code StringBuffer} to populate
1385      * @param fieldName  the field name, typically not used as already appended
1386      * @param array  the array to add to the {@code toString},
1387      *  not {@code null}
1388      */

1389     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
1390         buffer.append(arrayStart);
1391         for (int i = 0; i < array.length; i++) {
1392             if (i > 0) {
1393                 buffer.append(arraySeparator);
1394             }
1395             appendDetail(buffer, fieldName, array[i]);
1396         }
1397         buffer.append(arrayEnd);
1398     }
1399
1400     /**
1401      * <p>Append to the {@code toString} a summary of a
1402      * {@code float} array.</p>
1403      *
1404      * @param buffer  the {@code StringBuffer} to populate
1405      * @param fieldName  the field name, typically not used as already appended
1406      * @param array  the array to add to the {@code toString},
1407      *  not {@code null}
1408      */

1409     protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
1410         appendSummarySize(buffer, fieldName, array.length);
1411     }
1412
1413     //----------------------------------------------------------------------------
1414
1415     /**
1416      * <p>Append to the {@code toString} a {@code boolean}
1417      * array.</p>
1418      *
1419      * @param buffer  the {@code StringBuffer} to populate
1420      * @param fieldName  the field name
1421      * @param array  the array to add to the toString
1422      * @param fullDetail  {@code truefor detail, {@code false}
1423      *  for summary info, {@code nullfor style decides
1424      */

1425     public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
1426         appendFieldStart(buffer, fieldName);
1427
1428         if (array == null) {
1429             appendNullText(buffer, fieldName);
1430
1431         } else if (isFullDetail(fullDetail)) {
1432             appendDetail(buffer, fieldName, array);
1433
1434         } else {
1435             appendSummary(buffer, fieldName, array);
1436         }
1437
1438         appendFieldEnd(buffer, fieldName);
1439     }
1440
1441     /**
1442      * <p>Append to the {@code toString} the detail of a
1443      * {@code boolean} array.</p>
1444      *
1445      * @param buffer  the {@code StringBuffer} to populate
1446      * @param fieldName  the field name, typically not used as already appended
1447      * @param array  the array to add to the {@code toString},
1448      *  not {@code null}
1449      */

1450     protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1451         buffer.append(arrayStart);
1452         for (int i = 0; i < array.length; i++) {
1453             if (i > 0) {
1454                 buffer.append(arraySeparator);
1455             }
1456             appendDetail(buffer, fieldName, array[i]);
1457         }
1458         buffer.append(arrayEnd);
1459     }
1460
1461     /**
1462      * <p>Append to the {@code toString} a summary of a
1463      * {@code boolean} array.</p>
1464      *
1465      * @param buffer  the {@code StringBuffer} to populate
1466      * @param fieldName  the field name, typically not used as already appended
1467      * @param array  the array to add to the {@code toString},
1468      *  not {@code null}
1469      */

1470     protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1471         appendSummarySize(buffer, fieldName, array.length);
1472     }
1473
1474     //----------------------------------------------------------------------------
1475
1476     /**
1477      * <p>Append to the {@code toString} the class name.</p>
1478      *
1479      * @param buffer  the {@code StringBuffer} to populate
1480      * @param object  the {@code Object} whose name to output
1481      */

1482     protected void appendClassName(final StringBuffer buffer, final Object object) {
1483         if (useClassName && object != null) {
1484             register(object);
1485             if (useShortClassName) {
1486                 buffer.append(getShortClassName(object.getClass()));
1487             } else {
1488                 buffer.append(object.getClass().getName());
1489             }
1490         }
1491     }
1492
1493     /**
1494      * <p>Append the {@link System#identityHashCode(java.lang.Object)}.</p>
1495      *
1496      * @param buffer  the {@code StringBuffer} to populate
1497      * @param object  the {@code Object} whose id to output
1498      */

1499     protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
1500         if (this.isUseIdentityHashCode() && object!=null) {
1501             register(object);
1502             buffer.append('@');
1503             buffer.append(Integer.toHexString(System.identityHashCode(object)));
1504         }
1505     }
1506
1507     /**
1508      * <p>Append to the {@code toString} the content start.</p>
1509      *
1510      * @param buffer  the {@code StringBuffer} to populate
1511      */

1512     protected void appendContentStart(final StringBuffer buffer) {
1513         buffer.append(contentStart);
1514     }
1515
1516     /**
1517      * <p>Append to the {@code toString} the content end.</p>
1518      *
1519      * @param buffer  the {@code StringBuffer} to populate
1520      */

1521     protected void appendContentEnd(final StringBuffer buffer) {
1522         buffer.append(contentEnd);
1523     }
1524
1525     /**
1526      * <p>Append to the {@code toString} an indicator for {@code null}.</p>
1527      *
1528      * <p>The default indicator is {@code '&lt;null&gt;'}.</p>
1529      *
1530      * @param buffer  the {@code StringBuffer} to populate
1531      * @param fieldName  the field name, typically not used as already appended
1532      */

1533     protected void appendNullText(final StringBuffer buffer, final String fieldName) {
1534         buffer.append(nullText);
1535     }
1536
1537     /**
1538      * <p>Append to the {@code toString} the field separator.</p>
1539      *
1540      * @param buffer  the {@code StringBuffer} to populate
1541      */

1542     protected void appendFieldSeparator(final StringBuffer buffer) {
1543         buffer.append(fieldSeparator);
1544     }
1545
1546     /**
1547      * <p>Append to the {@code toString} the field start.</p>
1548      *
1549      * @param buffer  the {@code StringBuffer} to populate
1550      * @param fieldName  the field name
1551      */

1552     protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
1553         if (useFieldNames && fieldName != null) {
1554             buffer.append(fieldName);
1555             buffer.append(fieldNameValueSeparator);
1556         }
1557     }
1558
1559     /**
1560      * <p>Append to the {@code toString} the field end.</p>
1561      *
1562      * @param buffer  the {@code StringBuffer} to populate
1563      * @param fieldName  the field name, typically not used as already appended
1564      */

1565     protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
1566         appendFieldSeparator(buffer);
1567     }
1568
1569     /**
1570      * <p>Append to the {@code toString} a size summary.</p>
1571      *
1572      * <p>The size summary is used to summarize the contents of
1573      * {@code Collections}, {@code Maps} and arrays.</p>
1574      *
1575      * <p>The output consists of a prefix, the passed in size
1576      * and a suffix.</p>
1577      *
1578      * <p>The default format is {@code '&lt;size=n&gt;'}.</p>
1579      *
1580      * @param buffer  the {@code StringBuffer} to populate
1581      * @param fieldName  the field name, typically not used as already appended
1582      * @param size  the size to append
1583      */

1584     protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
1585         buffer.append(sizeStartText);
1586         buffer.append(size);
1587         buffer.append(sizeEndText);
1588     }
1589
1590     /**
1591      * <p>Is this field to be output in full detail.</p>
1592      *
1593      * <p>This method converts a detail request into a detail level.
1594      * The calling code may request full detail ({@code true}),
1595      * but a subclass might ignore that and always return
1596      * {@code false}. The calling code may pass in
1597      * {@code null} indicating that it doesn't care about
1598      * the detail level. In this case the default detail level is
1599      * used.</p>
1600      *
1601      * @param fullDetailRequest  the detail level requested
1602      * @return whether full detail is to be shown
1603      */

1604     protected boolean isFullDetail(final Boolean fullDetailRequest) {
1605         if (fullDetailRequest == null) {
1606             return defaultFullDetail;
1607         }
1608         return fullDetailRequest.booleanValue();
1609     }
1610
1611     /**
1612      * <p>Gets the short class name for a class.</p>
1613      *
1614      * <p>The short class name is the classname excluding
1615      * the package name.</p>
1616      *
1617      * @param cls  the {@code Class} to get the short name of
1618      * @return the short name
1619      */

1620     protected String getShortClassName(final Class<?> cls) {
1621         return ClassUtils.getShortClassName(cls);
1622     }
1623
1624     // Setters and getters for the customizable parts of the style
1625     // These methods are not expected to be overridden, except to make public
1626     // (They are not public so that immutable subclasses can be written)
1627     //---------------------------------------------------------------------
1628
1629     /**
1630      * <p>Gets whether to use the class name.</p>
1631      *
1632      * @return the current useClassName flag
1633      */

1634     protected boolean isUseClassName() {
1635         return useClassName;
1636     }
1637
1638     /**
1639      * <p>Sets whether to use the class name.</p>
1640      *
1641      * @param useClassName  the new useClassName flag
1642      */

1643     protected void setUseClassName(final boolean useClassName) {
1644         this.useClassName = useClassName;
1645     }
1646
1647     //---------------------------------------------------------------------
1648
1649     /**
1650      * <p>Gets whether to output short or long class names.</p>
1651      *
1652      * @return the current useShortClassName flag
1653      * @since 2.0
1654      */

1655     protected boolean isUseShortClassName() {
1656         return useShortClassName;
1657     }
1658
1659     /**
1660      * <p>Sets whether to output short or long class names.</p>
1661      *
1662      * @param useShortClassName  the new useShortClassName flag
1663      * @since 2.0
1664      */

1665     protected void setUseShortClassName(final boolean useShortClassName) {
1666         this.useShortClassName = useShortClassName;
1667     }
1668
1669     //---------------------------------------------------------------------
1670
1671     /**
1672      * <p>Gets whether to use the identity hash code.</p>
1673      *
1674      * @return the current useIdentityHashCode flag
1675      */

1676     protected boolean isUseIdentityHashCode() {
1677         return useIdentityHashCode;
1678     }
1679
1680     /**
1681      * <p>Sets whether to use the identity hash code.</p>
1682      *
1683      * @param useIdentityHashCode  the new useIdentityHashCode flag
1684      */

1685     protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
1686         this.useIdentityHashCode = useIdentityHashCode;
1687     }
1688
1689     //---------------------------------------------------------------------
1690
1691     /**
1692      * <p>Gets whether to use the field names passed in.</p>
1693      *
1694      * @return the current useFieldNames flag
1695      */

1696     protected boolean isUseFieldNames() {
1697         return useFieldNames;
1698     }
1699
1700     /**
1701      * <p>Sets whether to use the field names passed in.</p>
1702      *
1703      * @param useFieldNames  the new useFieldNames flag
1704      */

1705     protected void setUseFieldNames(final boolean useFieldNames) {
1706         this.useFieldNames = useFieldNames;
1707     }
1708
1709     //---------------------------------------------------------------------
1710
1711     /**
1712      * <p>Gets whether to use full detail when the caller doesn't
1713      * specify.</p>
1714      *
1715      * @return the current defaultFullDetail flag
1716      */

1717     protected boolean isDefaultFullDetail() {
1718         return defaultFullDetail;
1719     }
1720
1721     /**
1722      * <p>Sets whether to use full detail when the caller doesn't
1723      * specify.</p>
1724      *
1725      * @param defaultFullDetail  the new defaultFullDetail flag
1726      */

1727     protected void setDefaultFullDetail(final boolean defaultFullDetail) {
1728         this.defaultFullDetail = defaultFullDetail;
1729     }
1730
1731     //---------------------------------------------------------------------
1732
1733     /**
1734      * <p>Gets whether to output array content detail.</p>
1735      *
1736      * @return the current array content detail setting
1737      */

1738     protected boolean isArrayContentDetail() {
1739         return arrayContentDetail;
1740     }
1741
1742     /**
1743      * <p>Sets whether to output array content detail.</p>
1744      *
1745      * @param arrayContentDetail  the new arrayContentDetail flag
1746      */

1747     protected void setArrayContentDetail(final boolean arrayContentDetail) {
1748         this.arrayContentDetail = arrayContentDetail;
1749     }
1750
1751     //---------------------------------------------------------------------
1752
1753     /**
1754      * <p>Gets the array start text.</p>
1755      *
1756      * @return the current array start text
1757      */

1758     protected String getArrayStart() {
1759         return arrayStart;
1760     }
1761
1762     /**
1763      * <p>Sets the array start text.</p>
1764      *
1765      * <p>{@code null} is accepted, but will be converted to
1766      * an empty String.</p>
1767      *
1768      * @param arrayStart  the new array start text
1769      */

1770     protected void setArrayStart(String arrayStart) {
1771         if (arrayStart == null) {
1772             arrayStart = StringUtils.EMPTY;
1773         }
1774         this.arrayStart = arrayStart;
1775     }
1776
1777     //---------------------------------------------------------------------
1778
1779     /**
1780      * <p>Gets the array end text.</p>
1781      *
1782      * @return the current array end text
1783      */

1784     protected String getArrayEnd() {
1785         return arrayEnd;
1786     }
1787
1788     /**
1789      * <p>Sets the array end text.</p>
1790      *
1791      * <p>{@code null} is accepted, but will be converted to
1792      * an empty String.</p>
1793      *
1794      * @param arrayEnd  the new array end text
1795      */

1796     protected void setArrayEnd(String arrayEnd) {
1797         if (arrayEnd == null) {
1798             arrayEnd = StringUtils.EMPTY;
1799         }
1800         this.arrayEnd = arrayEnd;
1801     }
1802
1803     //---------------------------------------------------------------------
1804
1805     /**
1806      * <p>Gets the array separator text.</p>
1807      *
1808      * @return the current array separator text
1809      */

1810     protected String getArraySeparator() {
1811         return arraySeparator;
1812     }
1813
1814     /**
1815      * <p>Sets the array separator text.</p>
1816      *
1817      * <p>{@code null} is accepted, but will be converted to
1818      * an empty String.</p>
1819      *
1820      * @param arraySeparator  the new array separator text
1821      */

1822     protected void setArraySeparator(String arraySeparator) {
1823         if (arraySeparator == null) {
1824             arraySeparator = StringUtils.EMPTY;
1825         }
1826         this.arraySeparator = arraySeparator;
1827     }
1828
1829     //---------------------------------------------------------------------
1830
1831     /**
1832      * <p>Gets the content start text.</p>
1833      *
1834      * @return the current content start text
1835      */

1836     protected String getContentStart() {
1837         return contentStart;
1838     }
1839
1840     /**
1841      * <p>Sets the content start text.</p>
1842      *
1843      * <p>{@code null} is accepted, but will be converted to
1844      * an empty String.</p>
1845      *
1846      * @param contentStart  the new content start text
1847      */

1848     protected void setContentStart(String contentStart) {
1849         if (contentStart == null) {
1850             contentStart = StringUtils.EMPTY;
1851         }
1852         this.contentStart = contentStart;
1853     }
1854
1855     //---------------------------------------------------------------------
1856
1857     /**
1858      * <p>Gets the content end text.</p>
1859      *
1860      * @return the current content end text
1861      */

1862     protected String getContentEnd() {
1863         return contentEnd;
1864     }
1865
1866     /**
1867      * <p>Sets the content end text.</p>
1868      *
1869      * <p>{@code null} is accepted, but will be converted to
1870      * an empty String.</p>
1871      *
1872      * @param contentEnd  the new content end text
1873      */

1874     protected void setContentEnd(String contentEnd) {
1875         if (contentEnd == null) {
1876             contentEnd = StringUtils.EMPTY;
1877         }
1878         this.contentEnd = contentEnd;
1879     }
1880
1881     //---------------------------------------------------------------------
1882
1883     /**
1884      * <p>Gets the field name value separator text.</p>
1885      *
1886      * @return the current field name value separator text
1887      */

1888     protected String getFieldNameValueSeparator() {
1889         return fieldNameValueSeparator;
1890     }
1891
1892     /**
1893      * <p>Sets the field name value separator text.</p>
1894      *
1895      * <p>{@code null} is accepted, but will be converted to
1896      * an empty String.</p>
1897      *
1898      * @param fieldNameValueSeparator  the new field name value separator text
1899      */

1900     protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
1901         if (fieldNameValueSeparator == null) {
1902             fieldNameValueSeparator = StringUtils.EMPTY;
1903         }
1904         this.fieldNameValueSeparator = fieldNameValueSeparator;
1905     }
1906
1907     //---------------------------------------------------------------------
1908
1909     /**
1910      * <p>Gets the field separator text.</p>
1911      *
1912      * @return the current field separator text
1913      */

1914     protected String getFieldSeparator() {
1915         return fieldSeparator;
1916     }
1917
1918     /**
1919      * <p>Sets the field separator text.</p>
1920      *
1921      * <p>{@code null} is accepted, but will be converted to
1922      * an empty String.</p>
1923      *
1924      * @param fieldSeparator  the new field separator text
1925      */

1926     protected void setFieldSeparator(String fieldSeparator) {
1927         if (fieldSeparator == null) {
1928             fieldSeparator = StringUtils.EMPTY;
1929         }
1930         this.fieldSeparator = fieldSeparator;
1931     }
1932
1933     //---------------------------------------------------------------------
1934
1935     /**
1936      * <p>Gets whether the field separator should be added at the start
1937      * of each buffer.</p>
1938      *
1939      * @return the fieldSeparatorAtStart flag
1940      * @since 2.0
1941      */

1942     protected boolean isFieldSeparatorAtStart() {
1943         return fieldSeparatorAtStart;
1944     }
1945
1946     /**
1947      * <p>Sets whether the field separator should be added at the start
1948      * of each buffer.</p>
1949      *
1950      * @param fieldSeparatorAtStart  the fieldSeparatorAtStart flag
1951      * @since 2.0
1952      */

1953     protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
1954         this.fieldSeparatorAtStart = fieldSeparatorAtStart;
1955     }
1956
1957     //---------------------------------------------------------------------
1958
1959     /**
1960      * <p>Gets whether the field separator should be added at the end
1961      * of each buffer.</p>
1962      *
1963      * @return fieldSeparatorAtEnd flag
1964      * @since 2.0
1965      */

1966     protected boolean isFieldSeparatorAtEnd() {
1967         return fieldSeparatorAtEnd;
1968     }
1969
1970     /**
1971      * <p>Sets whether the field separator should be added at the end
1972      * of each buffer.</p>
1973      *
1974      * @param fieldSeparatorAtEnd  the fieldSeparatorAtEnd flag
1975      * @since 2.0
1976      */

1977     protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
1978         this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
1979     }
1980
1981     //---------------------------------------------------------------------
1982
1983     /**
1984      * <p>Gets the text to output when {@code null} found.</p>
1985      *
1986      * @return the current text to output when null found
1987      */

1988     protected String getNullText() {
1989         return nullText;
1990     }
1991
1992     /**
1993      * <p>Sets the text to output when {@code null} found.</p>
1994      *
1995      * <p>{@code null} is accepted, but will be converted to
1996      * an empty String.</p>
1997      *
1998      * @param nullText  the new text to output when null found
1999      */

2000     protected void setNullText(String nullText) {
2001         if (nullText == null) {
2002             nullText = StringUtils.EMPTY;
2003         }
2004         this.nullText = nullText;
2005     }
2006
2007     //---------------------------------------------------------------------
2008
2009     /**
2010      * <p>Gets the start text to output when a {@code Collection},
2011      * {@code Map} or array size is output.</p>
2012      *
2013      * <p>This is output before the size value.</p>
2014      *
2015      * @return the current start of size text
2016      */

2017     protected String getSizeStartText() {
2018         return sizeStartText;
2019     }
2020
2021     /**
2022      * <p>Sets the start text to output when a {@code Collection},
2023      * {@code Map} or array size is output.</p>
2024      *
2025      * <p>This is output before the size value.</p>
2026      *
2027      * <p>{@code null} is accepted, but will be converted to
2028      * an empty String.</p>
2029      *
2030      * @param sizeStartText  the new start of size text
2031      */

2032     protected void setSizeStartText(String sizeStartText) {
2033         if (sizeStartText == null) {
2034             sizeStartText = StringUtils.EMPTY;
2035         }
2036         this.sizeStartText = sizeStartText;
2037     }
2038
2039     //---------------------------------------------------------------------
2040
2041     /**
2042      * <p>Gets the end text to output when a {@code Collection},
2043      * {@code Map} or array size is output.</p>
2044      *
2045      * <p>This is output after the size value.</p>
2046      *
2047      * @return the current end of size text
2048      */

2049     protected String getSizeEndText() {
2050         return sizeEndText;
2051     }
2052
2053     /**
2054      * <p>Sets the end text to output when a {@code Collection},
2055      * {@code Map} or array size is output.</p>
2056      *
2057      * <p>This is output after the size value.</p>
2058      *
2059      * <p>{@code null} is accepted, but will be converted to
2060      * an empty String.</p>
2061      *
2062      * @param sizeEndText  the new end of size text
2063      */

2064     protected void setSizeEndText(String sizeEndText) {
2065         if (sizeEndText == null) {
2066             sizeEndText = StringUtils.EMPTY;
2067         }
2068         this.sizeEndText = sizeEndText;
2069     }
2070
2071     //---------------------------------------------------------------------
2072
2073     /**
2074      * <p>Gets the start text to output when an {@code Object} is
2075      * output in summary mode.</p>
2076      *
2077      * <p>This is output before the size value.</p>
2078      *
2079      * @return the current start of summary text
2080      */

2081     protected String getSummaryObjectStartText() {
2082         return summaryObjectStartText;
2083     }
2084
2085     /**
2086      * <p>Sets the start text to output when an {@code Object} is
2087      * output in summary mode.</p>
2088      *
2089      * <p>This is output before the size value.</p>
2090      *
2091      * <p>{@code null} is accepted, but will be converted to
2092      * an empty String.</p>
2093      *
2094      * @param summaryObjectStartText  the new start of summary text
2095      */

2096     protected void setSummaryObjectStartText(String summaryObjectStartText) {
2097         if (summaryObjectStartText == null) {
2098             summaryObjectStartText = StringUtils.EMPTY;
2099         }
2100         this.summaryObjectStartText = summaryObjectStartText;
2101     }
2102
2103     //---------------------------------------------------------------------
2104
2105     /**
2106      * <p>Gets the end text to output when an {@code Object} is
2107      * output in summary mode.</p>
2108      *
2109      * <p>This is output after the size value.</p>
2110      *
2111      * @return the current end of summary text
2112      */

2113     protected String getSummaryObjectEndText() {
2114         return summaryObjectEndText;
2115     }
2116
2117     /**
2118      * <p>Sets the end text to output when an {@code Object} is
2119      * output in summary mode.</p>
2120      *
2121      * <p>This is output after the size value.</p>
2122      *
2123      * <p>{@code null} is accepted, but will be converted to
2124      * an empty String.</p>
2125      *
2126      * @param summaryObjectEndText  the new end of summary text
2127      */

2128     protected void setSummaryObjectEndText(String summaryObjectEndText) {
2129         if (summaryObjectEndText == null) {
2130             summaryObjectEndText = StringUtils.EMPTY;
2131         }
2132         this.summaryObjectEndText = summaryObjectEndText;
2133     }
2134
2135     //----------------------------------------------------------------------------
2136
2137     /**
2138      * <p>Default {@code ToStringStyle}.</p>
2139      *
2140      * <p>This is an inner class rather than using
2141      * {@code StandardToStringStyle} to ensure its immutability.</p>
2142      */

2143     private static final class DefaultToStringStyle extends ToStringStyle {
2144
2145         /**
2146          * Required for serialization support.
2147          *
2148          * @see java.io.Serializable
2149          */

2150         private static final long serialVersionUID = 1L;
2151
2152         /**
2153          * <p>Constructor.</p>
2154          *
2155          * <p>Use the static constant rather than instantiating.</p>
2156          */

2157         DefaultToStringStyle() {
2158             super();
2159         }
2160
2161         /**
2162          * <p>Ensure {@code Singleton} after serialization.</p>
2163          *
2164          * @return the singleton
2165          */

2166         private Object readResolve() {
2167             return DEFAULT_STYLE;
2168         }
2169
2170     }
2171
2172     //----------------------------------------------------------------------------
2173
2174     /**
2175      * <p>{@code ToStringStyle} that does not print out
2176      * the field names.</p>
2177      *
2178      * <p>This is an inner class rather than using
2179      * {@code StandardToStringStyle} to ensure its immutability.
2180      */

2181     private static final class NoFieldNameToStringStyle extends ToStringStyle {
2182
2183         private static final long serialVersionUID = 1L;
2184
2185         /**
2186          * <p>Constructor.</p>
2187          *
2188          * <p>Use the static constant rather than instantiating.</p>
2189          */

2190         NoFieldNameToStringStyle() {
2191             super();
2192             this.setUseFieldNames(false);
2193         }
2194
2195         /**
2196          * <p>Ensure {@code Singleton} after serialization.</p>
2197          *
2198          * @return the singleton
2199          */

2200         private Object readResolve() {
2201             return NO_FIELD_NAMES_STYLE;
2202         }
2203
2204     }
2205
2206     //----------------------------------------------------------------------------
2207
2208     /**
2209      * <p>{@code ToStringStyle} that prints out the short
2210      * class name and no identity hashcode.</p>
2211      *
2212      * <p>This is an inner class rather than using
2213      * {@code StandardToStringStyle} to ensure its immutability.</p>
2214      */

2215     private static final class ShortPrefixToStringStyle extends ToStringStyle {
2216
2217         private static final long serialVersionUID = 1L;
2218
2219         /**
2220          * <p>Constructor.</p>
2221          *
2222          * <p>Use the static constant rather than instantiating.</p>
2223          */

2224         ShortPrefixToStringStyle() {
2225             super();
2226             this.setUseShortClassName(true);
2227             this.setUseIdentityHashCode(false);
2228         }
2229
2230         /**
2231          * <p>Ensure <code>Singleton</ode> after serialization.</p>
2232          * @return the singleton
2233          */

2234         private Object readResolve() {
2235             return SHORT_PREFIX_STYLE;
2236         }
2237
2238     }
2239
2240     //----------------------------------------------------------------------------
2241
2242     /**
2243      * <p>{@code ToStringStyle} that does not print out the
2244      * classname, identity hashcode, content start or field name.</p>
2245      *
2246      * <p>This is an inner class rather than using
2247      * {@code StandardToStringStyle} to ensure its immutability.</p>
2248      */

2249     private static final class SimpleToStringStyle extends ToStringStyle {
2250
2251         private static final long serialVersionUID = 1L;
2252
2253         /**
2254          * <p>Constructor.</p>
2255          *
2256          * <p>Use the static constant rather than instantiating.</p>
2257          */

2258         SimpleToStringStyle() {
2259             super();
2260             this.setUseClassName(false);
2261             this.setUseIdentityHashCode(false);
2262             this.setUseFieldNames(false);
2263             this.setContentStart(StringUtils.EMPTY);
2264             this.setContentEnd(StringUtils.EMPTY);
2265         }
2266
2267         /**
2268          * <p>Ensure <code>Singleton</ode> after serialization.</p>
2269          * @return the singleton
2270          */

2271         private Object readResolve() {
2272             return SIMPLE_STYLE;
2273         }
2274
2275     }
2276
2277     //----------------------------------------------------------------------------
2278
2279     /**
2280      * <p>{@code ToStringStyle} that outputs on multiple lines.</p>
2281      *
2282      * <p>This is an inner class rather than using
2283      * {@code StandardToStringStyle} to ensure its immutability.</p>
2284      */

2285     private static final class MultiLineToStringStyle extends ToStringStyle {
2286
2287         private static final long serialVersionUID = 1L;
2288
2289         /**
2290          * <p>Constructor.</p>
2291          *
2292          * <p>Use the static constant rather than instantiating.</p>
2293          */

2294         MultiLineToStringStyle() {
2295             super();
2296             this.setContentStart("[");
2297             this.setFieldSeparator(System.lineSeparator() + "  ");
2298             this.setFieldSeparatorAtStart(true);
2299             this.setContentEnd(System.lineSeparator() + "]");
2300         }
2301
2302         /**
2303          * <p>Ensure {@code Singleton} after serialization.</p>
2304          *
2305          * @return the singleton
2306          */

2307         private Object readResolve() {
2308             return MULTI_LINE_STYLE;
2309         }
2310
2311     }
2312
2313     //----------------------------------------------------------------------------
2314
2315     /**
2316      * <p>{@code ToStringStyle} that does not print out the classname
2317      * and identity hash code but prints content start and field names.</p>
2318      *
2319      * <p>This is an inner class rather than using
2320      * {@code StandardToStringStyle} to ensure its immutability.</p>
2321      */

2322     private static final class NoClassNameToStringStyle extends ToStringStyle {
2323
2324         private static final long serialVersionUID = 1L;
2325
2326         /**
2327          * <p>Constructor.</p>
2328          *
2329          * <p>Use the static constant rather than instantiating.</p>
2330          */

2331         NoClassNameToStringStyle() {
2332             super();
2333             this.setUseClassName(false);
2334             this.setUseIdentityHashCode(false);
2335         }
2336
2337         /**
2338          * <p>Ensure {@code Singleton} after serialization.</p>
2339          *
2340          * @return the singleton
2341          */

2342         private Object readResolve() {
2343             return NO_CLASS_NAME_STYLE;
2344         }
2345
2346     }
2347
2348     // ----------------------------------------------------------------------------
2349
2350     /**
2351      * <p>
2352      * {@code ToStringStyle} that outputs with JSON format.
2353      * </p>
2354      *
2355      * <p>
2356      * This is an inner class rather than using
2357      * {@code StandardToStringStyle} to ensure its immutability.
2358      * </p>
2359      *
2360      * @since 3.4
2361      * @see <a href="http://json.org">json.org</a>
2362      */

2363     private static final class JsonToStringStyle extends ToStringStyle {
2364
2365         private static final long serialVersionUID = 1L;
2366
2367         private static final String FIELD_NAME_QUOTE = "\"";
2368
2369         /**
2370          * <p>
2371          * Constructor.
2372          * </p>
2373          *
2374          * <p>
2375          * Use the static constant rather than instantiating.
2376          * </p>
2377          */

2378         JsonToStringStyle() {
2379             super();
2380
2381             this.setUseClassName(false);
2382             this.setUseIdentityHashCode(false);
2383
2384             this.setContentStart("{");
2385             this.setContentEnd("}");
2386
2387             this.setArrayStart("[");
2388             this.setArrayEnd("]");
2389
2390             this.setFieldSeparator(",");
2391             this.setFieldNameValueSeparator(":");
2392
2393             this.setNullText("null");
2394
2395             this.setSummaryObjectStartText("\"<");
2396             this.setSummaryObjectEndText(">\"");
2397
2398             this.setSizeStartText("\"<size=");
2399             this.setSizeEndText(">\"");
2400         }
2401
2402         @Override
2403         public void append(final StringBuffer buffer, final String fieldName,
2404                            final Object[] array, final Boolean fullDetail) {
2405
2406             if (fieldName == null) {
2407                 throw new UnsupportedOperationException(
2408                         "Field names are mandatory when using JsonToStringStyle");
2409             }
2410             if (!isFullDetail(fullDetail)) {
2411                 throw new UnsupportedOperationException(
2412                         "FullDetail must be true when using JsonToStringStyle");
2413             }
2414
2415             super.append(buffer, fieldName, array, fullDetail);
2416         }
2417
2418         @Override
2419         public void append(final StringBuffer buffer, final String fieldName, final long[] array,
2420                            final Boolean fullDetail) {
2421
2422             if (fieldName == null) {
2423                 throw new UnsupportedOperationException(
2424                         "Field names are mandatory when using JsonToStringStyle");
2425             }
2426             if (!isFullDetail(fullDetail)) {
2427                 throw new UnsupportedOperationException(
2428                         "FullDetail must be true when using JsonToStringStyle");
2429             }
2430
2431             super.append(buffer, fieldName, array, fullDetail);
2432         }
2433
2434         @Override
2435         public void append(final StringBuffer buffer, final String fieldName, final int[] array,
2436                            final Boolean fullDetail) {
2437
2438             if (fieldName == null) {
2439                 throw new UnsupportedOperationException(
2440                         "Field names are mandatory when using JsonToStringStyle");
2441             }
2442             if (!isFullDetail(fullDetail)) {
2443                 throw new UnsupportedOperationException(
2444                         "FullDetail must be true when using JsonToStringStyle");
2445             }
2446
2447             super.append(buffer, fieldName, array, fullDetail);
2448         }
2449
2450         @Override
2451         public void append(final StringBuffer buffer, final String fieldName,
2452                            final short[] array, final Boolean fullDetail) {
2453
2454             if (fieldName == null) {
2455                 throw new UnsupportedOperationException(
2456                         "Field names are mandatory when using JsonToStringStyle");
2457             }
2458             if (!isFullDetail(fullDetail)) {
2459                 throw new UnsupportedOperationException(
2460                         "FullDetail must be true when using JsonToStringStyle");
2461             }
2462
2463             super.append(buffer, fieldName, array, fullDetail);
2464         }
2465
2466         @Override
2467         public void append(final StringBuffer buffer, final String fieldName, final byte[] array,
2468                            final Boolean fullDetail) {
2469
2470             if (fieldName == null) {
2471                 throw new UnsupportedOperationException(
2472                         "Field names are mandatory when using JsonToStringStyle");
2473             }
2474             if (!isFullDetail(fullDetail)) {
2475                 throw new UnsupportedOperationException(
2476                         "FullDetail must be true when using JsonToStringStyle");
2477             }
2478
2479             super.append(buffer, fieldName, array, fullDetail);
2480         }
2481
2482         @Override
2483         public void append(final StringBuffer buffer, final String fieldName, final char[] array,
2484                            final Boolean fullDetail) {
2485
2486             if (fieldName == null) {
2487                 throw new UnsupportedOperationException(
2488                         "Field names are mandatory when using JsonToStringStyle");
2489             }
2490             if (!isFullDetail(fullDetail)) {
2491                 throw new UnsupportedOperationException(
2492                         "FullDetail must be true when using JsonToStringStyle");
2493             }
2494
2495             super.append(buffer, fieldName, array, fullDetail);
2496         }
2497
2498         @Override
2499         public void append(final StringBuffer buffer, final String fieldName,
2500                            final double[] array, final Boolean fullDetail) {
2501
2502             if (fieldName == null) {
2503                 throw new UnsupportedOperationException(
2504                         "Field names are mandatory when using JsonToStringStyle");
2505             }
2506             if (!isFullDetail(fullDetail)) {
2507                 throw new UnsupportedOperationException(
2508                         "FullDetail must be true when using JsonToStringStyle");
2509             }
2510
2511             super.append(buffer, fieldName, array, fullDetail);
2512         }
2513
2514         @Override
2515         public void append(final StringBuffer buffer, final String fieldName,
2516                            final float[] array, final Boolean fullDetail) {
2517
2518             if (fieldName == null) {
2519                 throw new UnsupportedOperationException(
2520                         "Field names are mandatory when using JsonToStringStyle");
2521             }
2522             if (!isFullDetail(fullDetail)) {
2523                 throw new UnsupportedOperationException(
2524                         "FullDetail must be true when using JsonToStringStyle");
2525             }
2526
2527             super.append(buffer, fieldName, array, fullDetail);
2528         }
2529
2530         @Override
2531         public void append(final StringBuffer buffer, final String fieldName,
2532                            final boolean[] array, final Boolean fullDetail) {
2533
2534             if (fieldName == null) {
2535                 throw new UnsupportedOperationException(
2536                         "Field names are mandatory when using JsonToStringStyle");
2537             }
2538             if (!isFullDetail(fullDetail)) {
2539                 throw new UnsupportedOperationException(
2540                         "FullDetail must be true when using JsonToStringStyle");
2541             }
2542
2543             super.append(buffer, fieldName, array, fullDetail);
2544         }
2545
2546         @Override
2547         public void append(final StringBuffer buffer, final String fieldName, final Object value,
2548                            final Boolean fullDetail) {
2549
2550             if (fieldName == null) {
2551                 throw new UnsupportedOperationException(
2552                         "Field names are mandatory when using JsonToStringStyle");
2553             }
2554             if (!isFullDetail(fullDetail)) {
2555                 throw new UnsupportedOperationException(
2556                         "FullDetail must be true when using JsonToStringStyle");
2557             }
2558
2559             super.append(buffer, fieldName, value, fullDetail);
2560         }
2561
2562         @Override
2563         protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
2564             appendValueAsString(buffer, String.valueOf(value));
2565         }
2566
2567         @Override
2568         protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
2569
2570             if (value == null) {
2571                 appendNullText(buffer, fieldName);
2572                 return;
2573             }
2574
2575             if (value instanceof String || value instanceof Character) {
2576                 appendValueAsString(buffer, value.toString());
2577                 return;
2578             }
2579
2580             if (value instanceof Number || value instanceof Boolean) {
2581                 buffer.append(value);
2582                 return;
2583             }
2584
2585             final String valueAsString = value.toString();
2586             if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
2587                 buffer.append(value);
2588                 return;
2589             }
2590
2591             appendDetail(buffer, fieldName, valueAsString);
2592         }
2593
2594         private boolean isJsonArray(final String valueAsString) {
2595             return valueAsString.startsWith(getArrayStart())
2596                     && valueAsString.endsWith(getArrayEnd());
2597         }
2598
2599         private boolean isJsonObject(final String valueAsString) {
2600             return valueAsString.startsWith(getContentStart())
2601                     && valueAsString.endsWith(getContentEnd());
2602         }
2603
2604         /**
2605          * Appends the given String enclosed in double-quotes to the given StringBuffer.
2606          *
2607          * @param buffer the StringBuffer to append the value to.
2608          * @param value the value to append.
2609          */

2610         private void appendValueAsString(final StringBuffer buffer, final String value) {
2611             buffer.append('"').append(StringEscapeUtils.escapeJson(value)).append('"');
2612         }
2613
2614         @Override
2615         protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
2616
2617             if (fieldName == null) {
2618                 throw new UnsupportedOperationException(
2619                         "Field names are mandatory when using JsonToStringStyle");
2620             }
2621
2622             super.appendFieldStart(buffer, FIELD_NAME_QUOTE + StringEscapeUtils.escapeJson(fieldName)
2623                     + FIELD_NAME_QUOTE);
2624         }
2625
2626         /**
2627          * <p>
2628          * Ensure {@code Singleton} after serialization.
2629          * </p>
2630          *
2631          * @return the singleton
2632          */

2633         private Object readResolve() {
2634             return JSON_STYLE;
2635         }
2636
2637     }
2638 }
2639