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