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         * Constructs a new instance.
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         * Constructs a new instance.
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         * Constructs a new instance.
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         * Constructs a new instance.
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         * Constructs a new instance.
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         * Constructs a new instance.
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         * Constructs a new instance.
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    private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY = ThreadLocal.withInitial(WeakHashMap::new);
691    /*
692     * Note that objects of this class are generally shared between threads, so
693     * an instance variable would not be suitable here.
694     *
695     * In normal use the registry should always be left empty, because the caller
696     * should call toString() which will clean up.
697     *
698     * See LANG-792
699     */
700
701    /**
702     * Returns the registry of objects being traversed by the {@code reflectionToString}
703     * methods in the current thread.
704     *
705     * @return Set the registry of objects being traversed
706     */
707    public static Map<Object, Object> getRegistry() {
708        return REGISTRY.get();
709    }
710
711    /**
712     * Returns {@code true} if the registry contains the given object.
713     * Used by the reflection methods to avoid infinite loops.
714     *
715     * @param value
716     *                  The object to lookup in the registry.
717     * @return boolean {@code true} if the registry contains the given
718     *             object.
719     */
720    static boolean isRegistered(final Object value) {
721        return getRegistry().containsKey(value);
722    }
723
724    /**
725     * Registers the given object. Used by the reflection methods to avoid
726     * infinite loops.
727     *
728     * @param value
729     *                  The object to register.
730     */
731    static void register(final Object value) {
732        if (value != null) {
733            getRegistry().put(value, null);
734        }
735    }
736
737    /**
738     * Unregisters the given object.
739     *
740     * <p>
741     * Used by the reflection methods to avoid infinite loops.
742     * </p>
743     *
744     * @param value
745     *                  The object to unregister.
746     */
747    static void unregister(final Object value) {
748        if (value != null) {
749            final Map<Object, Object> m = getRegistry();
750            m.remove(value);
751            if (m.isEmpty()) {
752                REGISTRY.remove();
753            }
754        }
755    }
756
757    /**
758     * Whether to use the field names, the default is {@code true}.
759     */
760    private boolean useFieldNames = true;
761
762    /**
763     * Whether to use the class name, the default is {@code true}.
764     */
765    private boolean useClassName = true;
766
767    /**
768     * Whether to use short class names, the default is {@code false}.
769     */
770    private boolean useShortClassName;
771
772    /**
773     * Whether to use the identity hash code, the default is {@code true}.
774     */
775    private boolean useIdentityHashCode = true;
776
777    /**
778     * The content start {@code '['}.
779     */
780    private String contentStart = "[";
781
782    /**
783     * The content end {@code ']'}.
784     */
785    private String contentEnd = "]";
786
787    /**
788     * The field name value separator {@code '='}.
789     */
790    private String fieldNameValueSeparator = "=";
791
792    /**
793     * Whether the field separator should be added before any other fields.
794     */
795    private boolean fieldSeparatorAtStart;
796
797    /**
798     * Whether the field separator should be added after any other fields.
799     */
800    private boolean fieldSeparatorAtEnd;
801
802    /**
803     * The field separator {@code ','}.
804     */
805    private String fieldSeparator = ",";
806
807    /**
808     * The array start <code>'{'</code>.
809     */
810    private String arrayStart = "{";
811
812    /**
813     * The array separator {@code ','}.
814     */
815    private String arraySeparator = ",";
816
817    /**
818     * The detail for array content.
819     */
820    private boolean arrayContentDetail = true;
821
822    /**
823     * The array end {@code '}'}.
824     */
825    private String arrayEnd = "}";
826
827    /**
828     * The value to use when fullDetail is {@code null},
829     * the default value is {@code true}.
830     */
831    private boolean defaultFullDetail = true;
832
833    /**
834     * The {@code null} text {@code "<null>"}.
835     */
836    private String nullText = "<null>";
837
838    /**
839     * The summary size text start {@code "<size="}.
840     */
841    private String sizeStartText = "<size=";
842
843    /**
844     * The summary size text start {@code ">"}.
845     */
846    private String sizeEndText = ">";
847
848    /**
849     * The summary object text start {@code "<"}.
850     */
851    private String summaryObjectStartText = "<";
852
853    /**
854     * The summary object text start {@code ">"}.
855     */
856    private String summaryObjectEndText = ">";
857
858    /**
859     * Constructs a new instance.
860     */
861    protected ToStringStyle() {
862    }
863
864    /**
865     * Appends to the {@code toString} a {@code boolean}
866     * value.
867     *
868     * @param buffer  the {@link StringBuffer} to populate
869     * @param fieldName  the field name
870     * @param value  the value to add to the {@code toString}
871     */
872    public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
873        appendFieldStart(buffer, fieldName);
874        appendDetail(buffer, fieldName, value);
875        appendFieldEnd(buffer, fieldName);
876    }
877
878    /**
879     * Appends to the {@code toString} a {@code boolean}
880     * array.
881     *
882     * @param buffer  the {@link StringBuffer} to populate
883     * @param fieldName  the field name
884     * @param array  the array to add to the toString
885     * @param fullDetail  {@code true} for detail, {@code false}
886     *  for summary info, {@code null} for style decides
887     */
888    public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
889        appendFieldStart(buffer, fieldName);
890
891        if (array == null) {
892            appendNullText(buffer, fieldName);
893
894        } else if (isFullDetail(fullDetail)) {
895            appendDetail(buffer, fieldName, array);
896
897        } else {
898            appendSummary(buffer, fieldName, array);
899        }
900
901        appendFieldEnd(buffer, fieldName);
902    }
903
904    /**
905     * Appends to the {@code toString} a {@code byte}
906     * value.
907     *
908     * @param buffer  the {@link StringBuffer} to populate
909     * @param fieldName  the field name
910     * @param value  the value to add to the {@code toString}
911     */
912    public void append(final StringBuffer buffer, final String fieldName, final byte value) {
913        appendFieldStart(buffer, fieldName);
914        appendDetail(buffer, fieldName, value);
915        appendFieldEnd(buffer, fieldName);
916    }
917
918    /**
919     * Appends to the {@code toString} a {@code byte}
920     * array.
921     *
922     * @param buffer  the {@link StringBuffer} to populate
923     * @param fieldName  the field name
924     * @param array  the array to add to the {@code toString}
925     * @param fullDetail  {@code true} for detail, {@code false}
926     *  for summary info, {@code null} for style decides
927     */
928    public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
929        appendFieldStart(buffer, fieldName);
930
931        if (array == null) {
932            appendNullText(buffer, fieldName);
933
934        } else if (isFullDetail(fullDetail)) {
935            appendDetail(buffer, fieldName, array);
936
937        } else {
938            appendSummary(buffer, fieldName, array);
939        }
940
941        appendFieldEnd(buffer, fieldName);
942    }
943
944    /**
945     * Appends to the {@code toString} a {@code char}
946     * value.
947     *
948     * @param buffer  the {@link StringBuffer} to populate
949     * @param fieldName  the field name
950     * @param value  the value to add to the {@code toString}
951     */
952    public void append(final StringBuffer buffer, final String fieldName, final char value) {
953        appendFieldStart(buffer, fieldName);
954        appendDetail(buffer, fieldName, value);
955        appendFieldEnd(buffer, fieldName);
956    }
957
958    /**
959     * Appends to the {@code toString} a {@code char}
960     * array.
961     *
962     * @param buffer  the {@link StringBuffer} to populate
963     * @param fieldName  the field name
964     * @param array  the array to add to the {@code toString}
965     * @param fullDetail  {@code true} for detail, {@code false}
966     *  for summary info, {@code null} for style decides
967     */
968    public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
969        appendFieldStart(buffer, fieldName);
970
971        if (array == null) {
972            appendNullText(buffer, fieldName);
973
974        } else if (isFullDetail(fullDetail)) {
975            appendDetail(buffer, fieldName, array);
976
977        } else {
978            appendSummary(buffer, fieldName, array);
979        }
980
981        appendFieldEnd(buffer, fieldName);
982    }
983
984    /**
985     * Appends to the {@code toString} a {@code double}
986     * value.
987     *
988     * @param buffer  the {@link StringBuffer} to populate
989     * @param fieldName  the field name
990     * @param value  the value to add to the {@code toString}
991     */
992    public void append(final StringBuffer buffer, final String fieldName, final double value) {
993        appendFieldStart(buffer, fieldName);
994        appendDetail(buffer, fieldName, value);
995        appendFieldEnd(buffer, fieldName);
996    }
997
998    /**
999     * Appends to the {@code toString} a {@code double}
1000     * array.
1001     *
1002     * @param buffer  the {@link StringBuffer} to populate
1003     * @param fieldName  the field name
1004     * @param array  the array to add to the toString
1005     * @param fullDetail  {@code true} for detail, {@code false}
1006     *  for summary info, {@code null} for style decides
1007     */
1008    public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
1009        appendFieldStart(buffer, fieldName);
1010
1011        if (array == null) {
1012            appendNullText(buffer, fieldName);
1013
1014        } else if (isFullDetail(fullDetail)) {
1015            appendDetail(buffer, fieldName, array);
1016
1017        } else {
1018            appendSummary(buffer, fieldName, array);
1019        }
1020
1021        appendFieldEnd(buffer, fieldName);
1022    }
1023
1024    /**
1025     * Appends to the {@code toString} a {@code float}
1026     * value.
1027     *
1028     * @param buffer  the {@link StringBuffer} to populate
1029     * @param fieldName  the field name
1030     * @param value  the value to add to the {@code toString}
1031     */
1032    public void append(final StringBuffer buffer, final String fieldName, final float value) {
1033        appendFieldStart(buffer, fieldName);
1034        appendDetail(buffer, fieldName, value);
1035        appendFieldEnd(buffer, fieldName);
1036    }
1037
1038    /**
1039     * Appends to the {@code toString} a {@code float}
1040     * array.
1041     *
1042     * @param buffer  the {@link StringBuffer} to populate
1043     * @param fieldName  the field name
1044     * @param array  the array to add to the toString
1045     * @param fullDetail  {@code true} for detail, {@code false}
1046     *  for summary info, {@code null} for style decides
1047     */
1048    public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
1049        appendFieldStart(buffer, fieldName);
1050
1051        if (array == null) {
1052            appendNullText(buffer, fieldName);
1053
1054        } else if (isFullDetail(fullDetail)) {
1055            appendDetail(buffer, fieldName, array);
1056
1057        } else {
1058            appendSummary(buffer, fieldName, array);
1059        }
1060
1061        appendFieldEnd(buffer, fieldName);
1062    }
1063
1064    /**
1065     * Appends to the {@code toString} an {@code int}
1066     * value.
1067     *
1068     * @param buffer  the {@link StringBuffer} to populate
1069     * @param fieldName  the field name
1070     * @param value  the value to add to the {@code toString}
1071     */
1072    public void append(final StringBuffer buffer, final String fieldName, final int value) {
1073        appendFieldStart(buffer, fieldName);
1074        appendDetail(buffer, fieldName, value);
1075        appendFieldEnd(buffer, fieldName);
1076    }
1077
1078    /**
1079     * Appends to the {@code toString} an {@code int}
1080     * array.
1081     *
1082     * @param buffer  the {@link StringBuffer} to populate
1083     * @param fieldName  the field name
1084     * @param array  the array to add to the {@code toString}
1085     * @param fullDetail  {@code true} for detail, {@code false}
1086     *  for summary info, {@code null} for style decides
1087     */
1088    public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
1089        appendFieldStart(buffer, fieldName);
1090
1091        if (array == null) {
1092            appendNullText(buffer, fieldName);
1093
1094        } else if (isFullDetail(fullDetail)) {
1095            appendDetail(buffer, fieldName, array);
1096
1097        } else {
1098            appendSummary(buffer, fieldName, array);
1099        }
1100
1101        appendFieldEnd(buffer, fieldName);
1102    }
1103
1104    /**
1105     * <p>Appends to the {@code toString} a {@code long}
1106     * value.
1107     *
1108     * @param buffer  the {@link StringBuffer} to populate
1109     * @param fieldName  the field name
1110     * @param value  the value to add to the {@code toString}
1111     */
1112    public void append(final StringBuffer buffer, final String fieldName, final long value) {
1113        appendFieldStart(buffer, fieldName);
1114        appendDetail(buffer, fieldName, value);
1115        appendFieldEnd(buffer, fieldName);
1116    }
1117
1118    /**
1119     * Appends to the {@code toString} a {@code long}
1120     * array.
1121     *
1122     * @param buffer  the {@link StringBuffer} to populate
1123     * @param fieldName  the field name
1124     * @param array  the array to add to the {@code toString}
1125     * @param fullDetail  {@code true} for detail, {@code false}
1126     *  for summary info, {@code null} for style decides
1127     */
1128    public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
1129        appendFieldStart(buffer, fieldName);
1130
1131        if (array == null) {
1132            appendNullText(buffer, fieldName);
1133
1134        } else if (isFullDetail(fullDetail)) {
1135            appendDetail(buffer, fieldName, array);
1136
1137        } else {
1138            appendSummary(buffer, fieldName, array);
1139        }
1140
1141        appendFieldEnd(buffer, fieldName);
1142    }
1143
1144    /**
1145     * Appends to the {@code toString} an {@link Object}
1146     * value, printing the full {@code toString} of the
1147     * {@link Object} passed in.
1148     *
1149     * @param buffer  the {@link StringBuffer} to populate
1150     * @param fieldName  the field name
1151     * @param value  the value to add to the {@code toString}
1152     * @param fullDetail  {@code true} for detail, {@code false}
1153     *  for summary info, {@code null} for style decides
1154     */
1155    public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
1156        appendFieldStart(buffer, fieldName);
1157
1158        if (value == null) {
1159            appendNullText(buffer, fieldName);
1160
1161        } else {
1162            appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
1163        }
1164
1165        appendFieldEnd(buffer, fieldName);
1166    }
1167
1168    /**
1169     * Appends to the {@code toString} an {@link Object}
1170     * array.
1171     *
1172     * @param buffer  the {@link StringBuffer} to populate
1173     * @param fieldName  the field name
1174     * @param array  the array to add to the toString
1175     * @param fullDetail  {@code true} for detail, {@code false}
1176     *  for summary info, {@code null} for style decides
1177     */
1178    public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
1179        appendFieldStart(buffer, fieldName);
1180
1181        if (array == null) {
1182            appendNullText(buffer, fieldName);
1183
1184        } else if (isFullDetail(fullDetail)) {
1185            appendDetail(buffer, fieldName, array);
1186
1187        } else {
1188            appendSummary(buffer, fieldName, array);
1189        }
1190
1191        appendFieldEnd(buffer, fieldName);
1192    }
1193
1194    /**
1195     * Appends to the {@code toString} a {@code short}
1196     * value.
1197     *
1198     * @param buffer  the {@link StringBuffer} to populate
1199     * @param fieldName  the field name
1200     * @param value  the value to add to the {@code toString}
1201     */
1202    public void append(final StringBuffer buffer, final String fieldName, final short value) {
1203        appendFieldStart(buffer, fieldName);
1204        appendDetail(buffer, fieldName, value);
1205        appendFieldEnd(buffer, fieldName);
1206    }
1207
1208    /**
1209     * Appends to the {@code toString} a {@code short}
1210     * array.
1211     *
1212     * @param buffer  the {@link StringBuffer} to populate
1213     * @param fieldName  the field name
1214     * @param array  the array to add to the {@code toString}
1215     * @param fullDetail  {@code true} for detail, {@code false}
1216     *  for summary info, {@code null} for style decides
1217     */
1218    public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
1219        appendFieldStart(buffer, fieldName);
1220
1221        if (array == null) {
1222            appendNullText(buffer, fieldName);
1223
1224        } else if (isFullDetail(fullDetail)) {
1225            appendDetail(buffer, fieldName, array);
1226
1227        } else {
1228            appendSummary(buffer, fieldName, array);
1229        }
1230
1231        appendFieldEnd(buffer, fieldName);
1232    }
1233
1234    /**
1235     * Appends to the {@code toString} the class name.
1236     *
1237     * @param buffer  the {@link StringBuffer} to populate
1238     * @param object  the {@link Object} whose name to output
1239     */
1240    protected void appendClassName(final StringBuffer buffer, final Object object) {
1241        if (useClassName && object != null) {
1242            register(object);
1243            if (useShortClassName) {
1244                buffer.append(getShortClassName(object.getClass()));
1245            } else {
1246                buffer.append(object.getClass().getName());
1247            }
1248        }
1249    }
1250
1251    /**
1252     * Appends to the {@code toString} the content end.
1253     *
1254     * @param buffer  the {@link StringBuffer} to populate
1255     */
1256    protected void appendContentEnd(final StringBuffer buffer) {
1257        buffer.append(contentEnd);
1258    }
1259
1260    /**
1261     * Appends to the {@code toString} the content start.
1262     *
1263     * @param buffer  the {@link StringBuffer} to populate
1264     */
1265    protected void appendContentStart(final StringBuffer buffer) {
1266        buffer.append(contentStart);
1267    }
1268
1269    /**
1270     * Appends to the {@code toString} an {@link Object}
1271     * value that has been detected to participate in a cycle. This
1272     * implementation will print the standard string value of the value.
1273     *
1274     * @param buffer  the {@link StringBuffer} to populate
1275     * @param fieldName  the field name, typically not used as already appended
1276     * @param value  the value to add to the {@code toString},
1277     *  not {@code null}
1278     *
1279     * @since 2.2
1280     */
1281    protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
1282       ObjectUtils.identityToString(buffer, value);
1283    }
1284
1285    /**
1286     * Appends to the {@code toString} a {@code boolean}
1287     * value.
1288     *
1289     * @param buffer  the {@link StringBuffer} to populate
1290     * @param fieldName  the field name, typically not used as already appended
1291     * @param value  the value to add to the {@code toString}
1292     */
1293    protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
1294        buffer.append(value);
1295    }
1296
1297    /**
1298     * Appends to the {@code toString} the detail of a
1299     * {@code boolean} array.
1300     *
1301     * @param buffer  the {@link StringBuffer} to populate
1302     * @param fieldName  the field name, typically not used as already appended
1303     * @param array  the array to add to the {@code toString},
1304     *  not {@code null}
1305     */
1306    protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1307        buffer.append(arrayStart);
1308        for (int i = 0; i < array.length; i++) {
1309            if (i > 0) {
1310                buffer.append(arraySeparator);
1311            }
1312            appendDetail(buffer, fieldName, array[i]);
1313        }
1314        buffer.append(arrayEnd);
1315    }
1316
1317    /**
1318     * Appends to the {@code toString} a {@code byte}
1319     * value.
1320     *
1321     * @param buffer  the {@link StringBuffer} to populate
1322     * @param fieldName  the field name, typically not used as already appended
1323     * @param value  the value to add to the {@code toString}
1324     */
1325    protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
1326        buffer.append(value);
1327    }
1328
1329    /**
1330     * Appends to the {@code toString} the detail of a
1331     * {@code byte} array.
1332     *
1333     * @param buffer  the {@link StringBuffer} to populate
1334     * @param fieldName  the field name, typically not used as already appended
1335     * @param array  the array to add to the {@code toString},
1336     *  not {@code null}
1337     */
1338    protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
1339        buffer.append(arrayStart);
1340        for (int i = 0; i < array.length; i++) {
1341            if (i > 0) {
1342                buffer.append(arraySeparator);
1343            }
1344            appendDetail(buffer, fieldName, array[i]);
1345        }
1346        buffer.append(arrayEnd);
1347    }
1348
1349    /**
1350     * Appends to the {@code toString} a {@code char}
1351     * value.
1352     *
1353     * @param buffer  the {@link StringBuffer} to populate
1354     * @param fieldName  the field name, typically not used as already appended
1355     * @param value  the value to add to the {@code toString}
1356     */
1357    protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
1358        buffer.append(value);
1359    }
1360
1361    /**
1362     * Appends to the {@code toString} the detail of a
1363     * {@code char} array.
1364     *
1365     * @param buffer  the {@link StringBuffer} to populate
1366     * @param fieldName  the field name, typically not used as already appended
1367     * @param array  the array to add to the {@code toString},
1368     *  not {@code null}
1369     */
1370    protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
1371        buffer.append(arrayStart);
1372        for (int i = 0; i < array.length; i++) {
1373            if (i > 0) {
1374                buffer.append(arraySeparator);
1375            }
1376            appendDetail(buffer, fieldName, array[i]);
1377        }
1378        buffer.append(arrayEnd);
1379    }
1380
1381    /**
1382     * Appends to the {@code toString} a {@link Collection}.
1383     *
1384     * @param buffer  the {@link StringBuffer} to populate
1385     * @param fieldName  the field name, typically not used as already appended
1386     * @param coll  the {@link Collection} to add to the
1387     *  {@code toString}, not {@code null}
1388     */
1389    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
1390        buffer.append(coll);
1391    }
1392
1393    /**
1394     * Appends to the {@code toString} a {@code double}
1395     * value.
1396     *
1397     * @param buffer  the {@link StringBuffer} to populate
1398     * @param fieldName  the field name, typically not used as already appended
1399     * @param value  the value to add to the {@code toString}
1400     */
1401    protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
1402        buffer.append(value);
1403    }
1404
1405    /**
1406     * Appends to the {@code toString} the detail of a
1407     * {@code double} array.
1408     *
1409     * @param buffer  the {@link StringBuffer} to populate
1410     * @param fieldName  the field name, typically not used as already appended
1411     * @param array  the array to add to the {@code toString},
1412     *  not {@code null}
1413     */
1414    protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
1415        buffer.append(arrayStart);
1416        for (int i = 0; i < array.length; i++) {
1417            if (i > 0) {
1418                buffer.append(arraySeparator);
1419            }
1420            appendDetail(buffer, fieldName, array[i]);
1421        }
1422        buffer.append(arrayEnd);
1423    }
1424
1425    /**
1426     * Appends to the {@code toString} a {@code float}
1427     * value.
1428     *
1429     * @param buffer  the {@link StringBuffer} to populate
1430     * @param fieldName  the field name, typically not used as already appended
1431     * @param value  the value to add to the {@code toString}
1432     */
1433    protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
1434        buffer.append(value);
1435    }
1436
1437    /**
1438     * Appends to the {@code toString} the detail of a
1439     * {@code float} array.
1440     *
1441     * @param buffer  the {@link StringBuffer} to populate
1442     * @param fieldName  the field name, typically not used as already appended
1443     * @param array  the array to add to the {@code toString},
1444     *  not {@code null}
1445     */
1446    protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
1447        buffer.append(arrayStart);
1448        for (int i = 0; i < array.length; i++) {
1449            if (i > 0) {
1450                buffer.append(arraySeparator);
1451            }
1452            appendDetail(buffer, fieldName, array[i]);
1453        }
1454        buffer.append(arrayEnd);
1455    }
1456
1457    /**
1458     * Appends to the {@code toString} an {@code int}
1459     * value.
1460     *
1461     * @param buffer  the {@link StringBuffer} to populate
1462     * @param fieldName  the field name, typically not used as already appended
1463     * @param value  the value to add to the {@code toString}
1464     */
1465    protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
1466        buffer.append(value);
1467    }
1468
1469    /**
1470     * Appends to the {@code toString} the detail of an
1471     * {@link Object} array item.
1472     *
1473     * @param buffer  the {@link StringBuffer} to populate
1474     * @param fieldName  the field name, typically not used as already appended
1475     * @param i the array item index to add
1476     * @param item the array item to add
1477     * @since 3.11
1478     */
1479    protected void appendDetail(final StringBuffer buffer, final String fieldName, final int i, final Object item) {
1480        if (i > 0) {
1481            buffer.append(arraySeparator);
1482        }
1483        if (item == null) {
1484            appendNullText(buffer, fieldName);
1485        } else {
1486            appendInternal(buffer, fieldName, item, arrayContentDetail);
1487        }
1488    }
1489
1490    /**
1491     * Appends to the {@code toString} the detail of an
1492     * {@code int} array.
1493     *
1494     * @param buffer  the {@link StringBuffer} to populate
1495     * @param fieldName  the field name, typically not used as already appended
1496     * @param array  the array to add to the {@code toString},
1497     *  not {@code null}
1498     */
1499    protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
1500        buffer.append(arrayStart);
1501        for (int i = 0; i < array.length; i++) {
1502            if (i > 0) {
1503                buffer.append(arraySeparator);
1504            }
1505            appendDetail(buffer, fieldName, array[i]);
1506        }
1507        buffer.append(arrayEnd);
1508    }
1509
1510    /**
1511     * Appends to the {@code toString} a {@code long}
1512     * value.
1513     *
1514     * @param buffer  the {@link StringBuffer} to populate
1515     * @param fieldName  the field name, typically not used as already appended
1516     * @param value  the value to add to the {@code toString}
1517     */
1518    protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
1519        buffer.append(value);
1520    }
1521
1522    /**
1523     * Appends to the {@code toString} the detail of a
1524     * {@code long} array.
1525     *
1526     * @param buffer  the {@link StringBuffer} to populate
1527     * @param fieldName  the field name, typically not used as already appended
1528     * @param array  the array to add to the {@code toString},
1529     *  not {@code null}
1530     */
1531    protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
1532        buffer.append(arrayStart);
1533        for (int i = 0; i < array.length; i++) {
1534            if (i > 0) {
1535                buffer.append(arraySeparator);
1536            }
1537            appendDetail(buffer, fieldName, array[i]);
1538        }
1539        buffer.append(arrayEnd);
1540    }
1541
1542    /**
1543     * Appends to the {@code toString} a {@link Map}.
1544     *
1545     * @param buffer  the {@link StringBuffer} to populate
1546     * @param fieldName  the field name, typically not used as already appended
1547     * @param map  the {@link Map} to add to the {@code toString},
1548     *  not {@code null}
1549     */
1550    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
1551        buffer.append(map);
1552    }
1553
1554    /**
1555     * Appends to the {@code toString} an {@link Object}
1556     * value, printing the full detail of the {@link Object}.
1557     *
1558     * @param buffer  the {@link StringBuffer} to populate
1559     * @param fieldName  the field name, typically not used as already appended
1560     * @param value  the value to add to the {@code toString},
1561     *  not {@code null}
1562     */
1563    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
1564        buffer.append(value);
1565    }
1566
1567    /**
1568     * Appends to the {@code toString} the detail of an
1569     * {@link Object} array.
1570     *
1571     * @param buffer  the {@link StringBuffer} to populate
1572     * @param fieldName  the field name, typically not used as already appended
1573     * @param array  the array to add to the {@code toString},
1574     *  not {@code null}
1575     */
1576    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
1577        buffer.append(arrayStart);
1578        for (int i = 0; i < array.length; i++) {
1579            appendDetail(buffer, fieldName, i, array[i]);
1580        }
1581        buffer.append(arrayEnd);
1582    }
1583
1584    /**
1585     * Appends to the {@code toString} a {@code short}
1586     * value.
1587     *
1588     * @param buffer  the {@link StringBuffer} to populate
1589     * @param fieldName  the field name, typically not used as already appended
1590     * @param value  the value to add to the {@code toString}
1591     */
1592    protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
1593        buffer.append(value);
1594    }
1595
1596    /**
1597     * Appends to the {@code toString} the detail of a
1598     * {@code short} array.
1599     *
1600     * @param buffer  the {@link StringBuffer} to populate
1601     * @param fieldName  the field name, typically not used as already appended
1602     * @param array  the array to add to the {@code toString},
1603     *  not {@code null}
1604     */
1605    protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
1606        buffer.append(arrayStart);
1607        for (int i = 0; i < array.length; i++) {
1608            if (i > 0) {
1609                buffer.append(arraySeparator);
1610            }
1611            appendDetail(buffer, fieldName, array[i]);
1612        }
1613        buffer.append(arrayEnd);
1614    }
1615
1616    /**
1617     * Appends to the {@code toString} the end of data indicator.
1618     *
1619     * @param buffer  the {@link StringBuffer} to populate
1620     * @param object  the {@link Object} to build a
1621     *  {@code toString} for.
1622     */
1623    public void appendEnd(final StringBuffer buffer, final Object object) {
1624        if (!this.fieldSeparatorAtEnd) {
1625            removeLastFieldSeparator(buffer);
1626        }
1627        appendContentEnd(buffer);
1628        unregister(object);
1629    }
1630
1631    /**
1632     * Appends to the {@code toString} the field end.
1633     *
1634     * @param buffer  the {@link StringBuffer} to populate
1635     * @param fieldName  the field name, typically not used as already appended
1636     */
1637    protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
1638        appendFieldSeparator(buffer);
1639    }
1640
1641    /**
1642     * Appends to the {@code toString} the field separator.
1643     *
1644     * @param buffer  the {@link StringBuffer} to populate
1645     */
1646    protected void appendFieldSeparator(final StringBuffer buffer) {
1647        buffer.append(fieldSeparator);
1648    }
1649
1650    /**
1651     * Appends to the {@code toString} the field start.
1652     *
1653     * @param buffer  the {@link StringBuffer} to populate
1654     * @param fieldName  the field name
1655     */
1656    protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
1657        if (useFieldNames && fieldName != null) {
1658            buffer.append(fieldName);
1659            buffer.append(fieldNameValueSeparator);
1660        }
1661    }
1662
1663    /**
1664     * Appends the {@link System#identityHashCode(java.lang.Object)}.
1665     *
1666     * @param buffer  the {@link StringBuffer} to populate
1667     * @param object  the {@link Object} whose id to output
1668     */
1669    protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
1670        if (this.isUseIdentityHashCode() && object != null) {
1671            register(object);
1672            buffer.append('@');
1673            buffer.append(ObjectUtils.identityHashCodeHex(object));
1674        }
1675    }
1676
1677    /**
1678     * Appends to the {@code toString} an {@link Object},
1679     * correctly interpreting its type.
1680     *
1681     * <p>This method performs the main lookup by Class type to correctly
1682     * route arrays, {@link Collection}s, {@link Map}s and
1683     * {@link Objects} to the appropriate method.</p>
1684     *
1685     * <p>Either detail or summary views can be specified.</p>
1686     *
1687     * <p>If a cycle is detected, an object will be appended with the
1688     * {@code Object.toString()} format.</p>
1689     *
1690     * @param buffer  the {@link StringBuffer} to populate
1691     * @param fieldName  the field name, typically not used as already appended
1692     * @param value  the value to add to the {@code toString},
1693     *  not {@code null}
1694     * @param detail  output detail or not
1695     */
1696    protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
1697        if (isRegistered(value)
1698            && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
1699           appendCyclicObject(buffer, fieldName, value);
1700           return;
1701        }
1702
1703        register(value);
1704
1705        try {
1706            if (value instanceof Collection<?>) {
1707                if (detail) {
1708                    appendDetail(buffer, fieldName, (Collection<?>) value);
1709                } else {
1710                    appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
1711                }
1712
1713            } else if (value instanceof Map<?, ?>) {
1714                if (detail) {
1715                    appendDetail(buffer, fieldName, (Map<?, ?>) value);
1716                } else {
1717                    appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
1718                }
1719
1720            } else if (value instanceof long[]) {
1721                if (detail) {
1722                    appendDetail(buffer, fieldName, (long[]) value);
1723                } else {
1724                    appendSummary(buffer, fieldName, (long[]) value);
1725                }
1726
1727            } else if (value instanceof int[]) {
1728                if (detail) {
1729                    appendDetail(buffer, fieldName, (int[]) value);
1730                } else {
1731                    appendSummary(buffer, fieldName, (int[]) value);
1732                }
1733
1734            } else if (value instanceof short[]) {
1735                if (detail) {
1736                    appendDetail(buffer, fieldName, (short[]) value);
1737                } else {
1738                    appendSummary(buffer, fieldName, (short[]) value);
1739                }
1740
1741            } else if (value instanceof byte[]) {
1742                if (detail) {
1743                    appendDetail(buffer, fieldName, (byte[]) value);
1744                } else {
1745                    appendSummary(buffer, fieldName, (byte[]) value);
1746                }
1747
1748            } else if (value instanceof char[]) {
1749                if (detail) {
1750                    appendDetail(buffer, fieldName, (char[]) value);
1751                } else {
1752                    appendSummary(buffer, fieldName, (char[]) value);
1753                }
1754
1755            } else if (value instanceof double[]) {
1756                if (detail) {
1757                    appendDetail(buffer, fieldName, (double[]) value);
1758                } else {
1759                    appendSummary(buffer, fieldName, (double[]) value);
1760                }
1761
1762            } else if (value instanceof float[]) {
1763                if (detail) {
1764                    appendDetail(buffer, fieldName, (float[]) value);
1765                } else {
1766                    appendSummary(buffer, fieldName, (float[]) value);
1767                }
1768
1769            } else if (value instanceof boolean[]) {
1770                if (detail) {
1771                    appendDetail(buffer, fieldName, (boolean[]) value);
1772                } else {
1773                    appendSummary(buffer, fieldName, (boolean[]) value);
1774                }
1775
1776            } else if (ObjectUtils.isArray(value)) {
1777                if (detail) {
1778                    appendDetail(buffer, fieldName, (Object[]) value);
1779                } else {
1780                    appendSummary(buffer, fieldName, (Object[]) value);
1781                }
1782
1783            } else if (detail) {
1784                appendDetail(buffer, fieldName, value);
1785            } else {
1786                appendSummary(buffer, fieldName, value);
1787            }
1788        } finally {
1789            unregister(value);
1790        }
1791    }
1792
1793    /**
1794     * Appends to the {@code toString} an indicator for {@code null}.
1795     *
1796     * <p>The default indicator is {@code "<null>"}.</p>
1797     *
1798     * @param buffer  the {@link StringBuffer} to populate
1799     * @param fieldName  the field name, typically not used as already appended
1800     */
1801    protected void appendNullText(final StringBuffer buffer, final String fieldName) {
1802        buffer.append(nullText);
1803    }
1804
1805    /**
1806     * Appends to the {@code toString} the start of data indicator.
1807     *
1808     * @param buffer  the {@link StringBuffer} to populate
1809     * @param object  the {@link Object} to build a {@code toString} for
1810     */
1811    public void appendStart(final StringBuffer buffer, final Object object) {
1812        if (object != null) {
1813            appendClassName(buffer, object);
1814            appendIdentityHashCode(buffer, object);
1815            appendContentStart(buffer);
1816            if (fieldSeparatorAtStart) {
1817                appendFieldSeparator(buffer);
1818            }
1819        }
1820    }
1821
1822    /**
1823     * Appends to the {@code toString} a summary of a
1824     * {@code boolean} array.
1825     *
1826     * @param buffer  the {@link StringBuffer} to populate
1827     * @param fieldName  the field name, typically not used as already appended
1828     * @param array  the array to add to the {@code toString},
1829     *  not {@code null}
1830     */
1831    protected void appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
1832        appendSummarySize(buffer, fieldName, array.length);
1833    }
1834
1835    /**
1836     * Appends to the {@code toString} a summary of a
1837     * {@code byte} array.
1838     *
1839     * @param buffer  the {@link StringBuffer} to populate
1840     * @param fieldName  the field name, typically not used as already appended
1841     * @param array  the array to add to the {@code toString},
1842     *  not {@code null}
1843     */
1844    protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
1845        appendSummarySize(buffer, fieldName, array.length);
1846    }
1847
1848    /**
1849     * Appends to the {@code toString} a summary of a
1850     * {@code char} array.
1851     *
1852     * @param buffer  the {@link StringBuffer} to populate
1853     * @param fieldName  the field name, typically not used as already appended
1854     * @param array  the array to add to the {@code toString},
1855     *  not {@code null}
1856     */
1857    protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
1858        appendSummarySize(buffer, fieldName, array.length);
1859    }
1860
1861    /**
1862     * Appends to the {@code toString} a summary of a
1863     * {@code double} array.
1864     *
1865     * @param buffer  the {@link StringBuffer} to populate
1866     * @param fieldName  the field name, typically not used as already appended
1867     * @param array  the array to add to the {@code toString},
1868     *  not {@code null}
1869     */
1870    protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
1871        appendSummarySize(buffer, fieldName, array.length);
1872    }
1873
1874    /**
1875     * Appends to the {@code toString} a summary of a
1876     * {@code float} array.
1877     *
1878     * @param buffer  the {@link StringBuffer} to populate
1879     * @param fieldName  the field name, typically not used as already appended
1880     * @param array  the array to add to the {@code toString},
1881     *  not {@code null}
1882     */
1883    protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
1884        appendSummarySize(buffer, fieldName, array.length);
1885    }
1886
1887    /**
1888     * Appends to the {@code toString} a summary of an
1889     * {@code int} array.
1890     *
1891     * @param buffer  the {@link StringBuffer} to populate
1892     * @param fieldName  the field name, typically not used as already appended
1893     * @param array  the array to add to the {@code toString},
1894     *  not {@code null}
1895     */
1896    protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
1897        appendSummarySize(buffer, fieldName, array.length);
1898    }
1899
1900    /**
1901     * Appends to the {@code toString} a summary of a
1902     * {@code long} array.
1903     *
1904     * @param buffer  the {@link StringBuffer} to populate
1905     * @param fieldName  the field name, typically not used as already appended
1906     * @param array  the array to add to the {@code toString},
1907     *  not {@code null}
1908     */
1909    protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
1910        appendSummarySize(buffer, fieldName, array.length);
1911    }
1912
1913    /**
1914     * Appends to the {@code toString} an {@link Object}
1915     * value, printing a summary of the {@link Object}.
1916     *
1917     * @param buffer  the {@link StringBuffer} to populate
1918     * @param fieldName  the field name, typically not used as already appended
1919     * @param value  the value to add to the {@code toString},
1920     *  not {@code null}
1921     */
1922    protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
1923        buffer.append(summaryObjectStartText);
1924        buffer.append(getShortClassName(value.getClass()));
1925        buffer.append(summaryObjectEndText);
1926    }
1927
1928    /**
1929     * Appends to the {@code toString} a summary of an
1930     * {@link Object} array.
1931     *
1932     * @param buffer  the {@link StringBuffer} to populate
1933     * @param fieldName  the field name, typically not used as already appended
1934     * @param array  the array to add to the {@code toString},
1935     *  not {@code null}
1936     */
1937    protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
1938        appendSummarySize(buffer, fieldName, array.length);
1939    }
1940
1941    /**
1942     * Appends to the {@code toString} a summary of a
1943     * {@code short} array.
1944     *
1945     * @param buffer  the {@link StringBuffer} to populate
1946     * @param fieldName  the field name, typically not used as already appended
1947     * @param array  the array to add to the {@code toString},
1948     *  not {@code null}
1949     */
1950    protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
1951        appendSummarySize(buffer, fieldName, array.length);
1952    }
1953
1954    /**
1955     * Appends to the {@code toString} a size summary.
1956     *
1957     * <p>The size summary is used to summarize the contents of
1958     * {@link Collection}s, {@link Map}s and arrays.</p>
1959     *
1960     * <p>The output consists of a prefix, the passed in size
1961     * and a suffix.</p>
1962     *
1963     * <p>The default format is {@code "<size=n>"}.</p>
1964     *
1965     * @param buffer  the {@link StringBuffer} to populate
1966     * @param fieldName  the field name, typically not used as already appended
1967     * @param size  the size to append
1968     */
1969    protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
1970        buffer.append(sizeStartText);
1971        buffer.append(size);
1972        buffer.append(sizeEndText);
1973    }
1974
1975    /**
1976     * Appends to the {@code toString} the superclass toString.
1977     * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle.</p>
1978     *
1979     * <p>A {@code null} {@code superToString} is ignored.</p>
1980     *
1981     * @param buffer  the {@link StringBuffer} to populate
1982     * @param superToString  the {@code super.toString()}
1983     * @since 2.0
1984     */
1985    public void appendSuper(final StringBuffer buffer, final String superToString) {
1986        appendToString(buffer, superToString);
1987    }
1988
1989    /**
1990     * Appends to the {@code toString} another toString.
1991     * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle.</p>
1992     *
1993     * <p>A {@code null} {@code toString} is ignored.</p>
1994     *
1995     * @param buffer  the {@link StringBuffer} to populate
1996     * @param toString  the additional {@code toString}
1997     * @since 2.0
1998     */
1999    public void appendToString(final StringBuffer buffer, final String toString) {
2000        if (toString != null) {
2001            final int pos1 = toString.indexOf(contentStart) + contentStart.length();
2002            final int pos2 = toString.lastIndexOf(contentEnd);
2003            if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
2004                if (fieldSeparatorAtStart) {
2005                    removeLastFieldSeparator(buffer);
2006                }
2007                buffer.append(toString, pos1, pos2);
2008                appendFieldSeparator(buffer);
2009            }
2010        }
2011    }
2012
2013    /**
2014     * Gets the array end text.
2015     *
2016     * @return the current array end text
2017     */
2018    protected String getArrayEnd() {
2019        return arrayEnd;
2020    }
2021
2022    /**
2023     * Gets the array separator text.
2024     *
2025     * @return the current array separator text
2026     */
2027    protected String getArraySeparator() {
2028        return arraySeparator;
2029    }
2030
2031    /**
2032     * Gets the array start text.
2033     *
2034     * @return the current array start text
2035     */
2036    protected String getArrayStart() {
2037        return arrayStart;
2038    }
2039
2040    /**
2041     * Gets the content end text.
2042     *
2043     * @return the current content end text
2044     */
2045    protected String getContentEnd() {
2046        return contentEnd;
2047    }
2048
2049    /**
2050     * Gets the content start text.
2051     *
2052     * @return the current content start text
2053     */
2054    protected String getContentStart() {
2055        return contentStart;
2056    }
2057
2058    /**
2059     * Gets the field name value separator text.
2060     *
2061     * @return the current field name value separator text
2062     */
2063    protected String getFieldNameValueSeparator() {
2064        return fieldNameValueSeparator;
2065    }
2066
2067    /**
2068     * Gets the field separator text.
2069     *
2070     * @return the current field separator text
2071     */
2072    protected String getFieldSeparator() {
2073        return fieldSeparator;
2074    }
2075
2076    /**
2077     * Gets the text to output when {@code null} found.
2078     *
2079     * @return the current text to output when null found
2080     */
2081    protected String getNullText() {
2082        return nullText;
2083    }
2084
2085    /**
2086     * Gets the short class name for a class.
2087     *
2088     * <p>The short class name is the class name excluding
2089     * the package name.</p>
2090     *
2091     * @param cls  the {@link Class} to get the short name of
2092     * @return the short name
2093     */
2094    protected String getShortClassName(final Class<?> cls) {
2095        return ClassUtils.getShortClassName(cls);
2096    }
2097
2098    /**
2099     * Gets the end text to output when a {@link Collection},
2100     * {@link Map} or array size is output.
2101     *
2102     * <p>This is output after the size value.</p>
2103     *
2104     * @return the current end of size text
2105     */
2106    protected String getSizeEndText() {
2107        return sizeEndText;
2108    }
2109
2110    /**
2111     * Gets the start text to output when a {@link Collection},
2112     * {@link Map} or array size is output.
2113     *
2114     * <p>This is output before the size value.</p>
2115     *
2116     * @return the current start of size text
2117     */
2118    protected String getSizeStartText() {
2119        return sizeStartText;
2120    }
2121
2122    /**
2123     * Gets the end text to output when an {@link Object} is
2124     * output in summary mode.
2125     *
2126     * <p>This is output after the size value.</p>
2127     *
2128     * @return the current end of summary text
2129     */
2130    protected String getSummaryObjectEndText() {
2131        return summaryObjectEndText;
2132    }
2133
2134    /**
2135     * Gets the start text to output when an {@link Object} is
2136     * output in summary mode.
2137     *
2138     * <p>This is output before the size value.</p>
2139     *
2140     * @return the current start of summary text
2141     */
2142    protected String getSummaryObjectStartText() {
2143        return summaryObjectStartText;
2144    }
2145
2146    /**
2147     * Gets whether to output array content detail.
2148     *
2149     * @return the current array content detail setting
2150     */
2151    protected boolean isArrayContentDetail() {
2152        return arrayContentDetail;
2153    }
2154
2155    /**
2156     * Gets whether to use full detail when the caller doesn't
2157     * specify.
2158     *
2159     * @return the current defaultFullDetail flag
2160     */
2161    protected boolean isDefaultFullDetail() {
2162        return defaultFullDetail;
2163    }
2164
2165    /**
2166     * Gets whether the field separator should be added at the end
2167     * of each buffer.
2168     *
2169     * @return fieldSeparatorAtEnd flag
2170     * @since 2.0
2171     */
2172    protected boolean isFieldSeparatorAtEnd() {
2173        return fieldSeparatorAtEnd;
2174    }
2175
2176    /**
2177     * Gets whether the field separator should be added at the start
2178     * of each buffer.
2179     *
2180     * @return the fieldSeparatorAtStart flag
2181     * @since 2.0
2182     */
2183    protected boolean isFieldSeparatorAtStart() {
2184        return fieldSeparatorAtStart;
2185    }
2186
2187    /**
2188     * Is this field to be output in full detail.
2189     *
2190     * <p>This method converts a detail request into a detail level.
2191     * The calling code may request full detail ({@code true}),
2192     * but a subclass might ignore that and always return
2193     * {@code false}. The calling code may pass in
2194     * {@code null} indicating that it doesn't care about
2195     * the detail level. In this case the default detail level is
2196     * used.</p>
2197     *
2198     * @param fullDetailRequest  the detail level requested
2199     * @return whether full detail is to be shown
2200     */
2201    protected boolean isFullDetail(final Boolean fullDetailRequest) {
2202        if (fullDetailRequest == null) {
2203            return defaultFullDetail;
2204        }
2205        return fullDetailRequest.booleanValue();
2206    }
2207
2208    // Setters and getters for the customizable parts of the style
2209    // These methods are not expected to be overridden, except to make public
2210    // (They are not public so that immutable subclasses can be written)
2211    /**
2212     * Gets whether to use the class name.
2213     *
2214     * @return the current useClassName flag
2215     */
2216    protected boolean isUseClassName() {
2217        return useClassName;
2218    }
2219
2220    /**
2221     * Gets whether to use the field names passed in.
2222     *
2223     * @return the current useFieldNames flag
2224     */
2225    protected boolean isUseFieldNames() {
2226        return useFieldNames;
2227    }
2228
2229    /**
2230     * Gets whether to use the identity hash code.
2231     *
2232     * @return the current useIdentityHashCode flag
2233     */
2234    protected boolean isUseIdentityHashCode() {
2235        return useIdentityHashCode;
2236    }
2237
2238    /**
2239     * Gets whether to output short or long class names.
2240     *
2241     * @return the current useShortClassName flag
2242     * @since 2.0
2243     */
2244    protected boolean isUseShortClassName() {
2245        return useShortClassName;
2246    }
2247
2248    /**
2249     * Appends to the {@code toString} the detail of an array type.
2250     *
2251     * @param buffer  the {@link StringBuffer} to populate
2252     * @param fieldName  the field name, typically not used as already appended
2253     * @param array  the array to add to the {@code toString},
2254     *  not {@code null}
2255     * @since 2.0
2256     */
2257    protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
2258        buffer.append(arrayStart);
2259        final int length = Array.getLength(array);
2260        for (int i = 0; i < length; i++) {
2261            appendDetail(buffer, fieldName, i, Array.get(array, i));
2262        }
2263        buffer.append(arrayEnd);
2264    }
2265
2266    /**
2267     * Remove the last field separator from the buffer.
2268     *
2269     * @param buffer  the {@link StringBuffer} to populate
2270     * @since 2.0
2271     */
2272    protected void removeLastFieldSeparator(final StringBuffer buffer) {
2273        if (StringUtils.endsWith(buffer, fieldSeparator)) {
2274            buffer.setLength(buffer.length() - fieldSeparator.length());
2275        }
2276    }
2277
2278    /**
2279     * Sets whether to output array content detail.
2280     *
2281     * @param arrayContentDetail  the new arrayContentDetail flag
2282     */
2283    protected void setArrayContentDetail(final boolean arrayContentDetail) {
2284        this.arrayContentDetail = arrayContentDetail;
2285    }
2286
2287    /**
2288     * Sets the array end text.
2289     *
2290     * <p>{@code null} is accepted, but will be converted to
2291     * an empty String.</p>
2292     *
2293     * @param arrayEnd  the new array end text
2294     */
2295    protected void setArrayEnd(String arrayEnd) {
2296        if (arrayEnd == null) {
2297            arrayEnd = StringUtils.EMPTY;
2298        }
2299        this.arrayEnd = arrayEnd;
2300    }
2301
2302    /**
2303     * Sets the array separator text.
2304     *
2305     * <p>{@code null} is accepted, but will be converted to
2306     * an empty String.</p>
2307     *
2308     * @param arraySeparator  the new array separator text
2309     */
2310    protected void setArraySeparator(String arraySeparator) {
2311        if (arraySeparator == null) {
2312            arraySeparator = StringUtils.EMPTY;
2313        }
2314        this.arraySeparator = arraySeparator;
2315    }
2316
2317    /**
2318     * Sets the array start text.
2319     *
2320     * <p>{@code null} is accepted, but will be converted to
2321     * an empty String.</p>
2322     *
2323     * @param arrayStart  the new array start text
2324     */
2325    protected void setArrayStart(String arrayStart) {
2326        if (arrayStart == null) {
2327            arrayStart = StringUtils.EMPTY;
2328        }
2329        this.arrayStart = arrayStart;
2330    }
2331
2332    /**
2333     * Sets the content end text.
2334     *
2335     * <p>{@code null} is accepted, but will be converted to
2336     * an empty String.</p>
2337     *
2338     * @param contentEnd  the new content end text
2339     */
2340    protected void setContentEnd(String contentEnd) {
2341        if (contentEnd == null) {
2342            contentEnd = StringUtils.EMPTY;
2343        }
2344        this.contentEnd = contentEnd;
2345    }
2346
2347    /**
2348     * Sets the content start text.
2349     *
2350     * <p>{@code null} is accepted, but will be converted to
2351     * an empty String.</p>
2352     *
2353     * @param contentStart  the new content start text
2354     */
2355    protected void setContentStart(String contentStart) {
2356        if (contentStart == null) {
2357            contentStart = StringUtils.EMPTY;
2358        }
2359        this.contentStart = contentStart;
2360    }
2361
2362    /**
2363     * Sets whether to use full detail when the caller doesn't
2364     * specify.
2365     *
2366     * @param defaultFullDetail  the new defaultFullDetail flag
2367     */
2368    protected void setDefaultFullDetail(final boolean defaultFullDetail) {
2369        this.defaultFullDetail = defaultFullDetail;
2370    }
2371
2372    /**
2373     * Sets the field name value separator text.
2374     *
2375     * <p>{@code null} is accepted, but will be converted to
2376     * an empty String.</p>
2377     *
2378     * @param fieldNameValueSeparator  the new field name value separator text
2379     */
2380    protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
2381        if (fieldNameValueSeparator == null) {
2382            fieldNameValueSeparator = StringUtils.EMPTY;
2383        }
2384        this.fieldNameValueSeparator = fieldNameValueSeparator;
2385    }
2386
2387    /**
2388     * Sets the field separator text.
2389     *
2390     * <p>{@code null} is accepted, but will be converted to
2391     * an empty String.</p>
2392     *
2393     * @param fieldSeparator  the new field separator text
2394     */
2395    protected void setFieldSeparator(String fieldSeparator) {
2396        if (fieldSeparator == null) {
2397            fieldSeparator = StringUtils.EMPTY;
2398        }
2399        this.fieldSeparator = fieldSeparator;
2400    }
2401
2402    /**
2403     * Sets whether the field separator should be added at the end
2404     * of each buffer.
2405     *
2406     * @param fieldSeparatorAtEnd  the fieldSeparatorAtEnd flag
2407     * @since 2.0
2408     */
2409    protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
2410        this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
2411    }
2412
2413    /**
2414     * Sets whether the field separator should be added at the start
2415     * of each buffer.
2416     *
2417     * @param fieldSeparatorAtStart  the fieldSeparatorAtStart flag
2418     * @since 2.0
2419     */
2420    protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
2421        this.fieldSeparatorAtStart = fieldSeparatorAtStart;
2422    }
2423
2424    /**
2425     * Sets the text to output when {@code null} found.
2426     *
2427     * <p>{@code null} is accepted, but will be converted to
2428     * an empty String.</p>
2429     *
2430     * @param nullText  the new text to output when null found
2431     */
2432    protected void setNullText(String nullText) {
2433        if (nullText == null) {
2434            nullText = StringUtils.EMPTY;
2435        }
2436        this.nullText = nullText;
2437    }
2438
2439    /**
2440     * Sets the end text to output when a {@link Collection},
2441     * {@link Map} or array size is output.
2442     *
2443     * <p>This is output after the size value.</p>
2444     *
2445     * <p>{@code null} is accepted, but will be converted to
2446     * an empty String.</p>
2447     *
2448     * @param sizeEndText  the new end of size text
2449     */
2450    protected void setSizeEndText(String sizeEndText) {
2451        if (sizeEndText == null) {
2452            sizeEndText = StringUtils.EMPTY;
2453        }
2454        this.sizeEndText = sizeEndText;
2455    }
2456
2457    /**
2458     * Sets the start text to output when a {@link Collection},
2459     * {@link Map} or array size is output.
2460     *
2461     * <p>This is output before the size value.</p>
2462     *
2463     * <p>{@code null} is accepted, but will be converted to
2464     * an empty String.</p>
2465     *
2466     * @param sizeStartText  the new start of size text
2467     */
2468    protected void setSizeStartText(String sizeStartText) {
2469        if (sizeStartText == null) {
2470            sizeStartText = StringUtils.EMPTY;
2471        }
2472        this.sizeStartText = sizeStartText;
2473    }
2474
2475    /**
2476     * Sets the end text to output when an {@link Object} is
2477     * output in summary mode.
2478     *
2479     * <p>This is output after the size value.</p>
2480     *
2481     * <p>{@code null} is accepted, but will be converted to
2482     * an empty String.</p>
2483     *
2484     * @param summaryObjectEndText  the new end of summary text
2485     */
2486    protected void setSummaryObjectEndText(String summaryObjectEndText) {
2487        if (summaryObjectEndText == null) {
2488            summaryObjectEndText = StringUtils.EMPTY;
2489        }
2490        this.summaryObjectEndText = summaryObjectEndText;
2491    }
2492
2493    /**
2494     * Sets the start text to output when an {@link Object} is
2495     * output in summary mode.
2496     *
2497     * <p>This is output before the size value.</p>
2498     *
2499     * <p>{@code null} is accepted, but will be converted to
2500     * an empty String.</p>
2501     *
2502     * @param summaryObjectStartText  the new start of summary text
2503     */
2504    protected void setSummaryObjectStartText(String summaryObjectStartText) {
2505        if (summaryObjectStartText == null) {
2506            summaryObjectStartText = StringUtils.EMPTY;
2507        }
2508        this.summaryObjectStartText = summaryObjectStartText;
2509    }
2510
2511    /**
2512     * Sets whether to use the class name.
2513     *
2514     * @param useClassName  the new useClassName flag
2515     */
2516    protected void setUseClassName(final boolean useClassName) {
2517        this.useClassName = useClassName;
2518    }
2519
2520    /**
2521     * Sets whether to use the field names passed in.
2522     *
2523     * @param useFieldNames  the new useFieldNames flag
2524     */
2525    protected void setUseFieldNames(final boolean useFieldNames) {
2526        this.useFieldNames = useFieldNames;
2527    }
2528
2529    /**
2530     * Sets whether to use the identity hash code.
2531     *
2532     * @param useIdentityHashCode  the new useIdentityHashCode flag
2533     */
2534    protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
2535        this.useIdentityHashCode = useIdentityHashCode;
2536    }
2537
2538    /**
2539     * Sets whether to output short or long class names.
2540     *
2541     * @param useShortClassName  the new useShortClassName flag
2542     * @since 2.0
2543     */
2544    protected void setUseShortClassName(final boolean useShortClassName) {
2545        this.useShortClassName = useShortClassName;
2546    }
2547}