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