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