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