ToStringStyle.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.lang3.builder;

  18. import java.io.Serializable;
  19. import java.lang.reflect.Array;
  20. import java.util.Collection;
  21. import java.util.Map;
  22. import java.util.Map.Entry;
  23. import java.util.Objects;
  24. import java.util.WeakHashMap;

  25. import org.apache.commons.lang3.ClassUtils;
  26. import org.apache.commons.lang3.ObjectUtils;
  27. import org.apache.commons.lang3.StringEscapeUtils;
  28. import org.apache.commons.lang3.StringUtils;
  29. import org.apache.commons.lang3.Strings;

  30. /**
  31.  * Controls {@link String} formatting for {@link ToStringBuilder}.
  32.  * The main public interface is always via {@link ToStringBuilder}.
  33.  *
  34.  * <p>These classes are intended to be used as <em>singletons</em>.
  35.  * There is no need to instantiate a new style each time. A program
  36.  * will generally use one of the predefined constants on this class.
  37.  * Alternatively, the {@link StandardToStringStyle} class can be used
  38.  * to set the individual settings. Thus most styles can be achieved
  39.  * without subclassing.</p>
  40.  *
  41.  * <p>If required, a subclass can override as many or as few of the
  42.  * methods as it requires. Each object type (from {@code boolean}
  43.  * to {@code long} to {@link Object} to {@code int[]}) has
  44.  * its own methods to output it. Most have two versions, detail and summary.
  45.  *
  46.  * <p>For example, the detail version of the array based methods will
  47.  * output the whole array, whereas the summary method will just output
  48.  * the array length.</p>
  49.  *
  50.  * <p>If you want to format the output of certain objects, such as dates, you
  51.  * must create a subclass and override a method.
  52.  * </p>
  53.  * <pre>
  54.  * public class MyStyle extends ToStringStyle {
  55.  *   protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
  56.  *     if (value instanceof Date) {
  57.  *       value = new SimpleDateFormat("yyyy-MM-dd").format(value);
  58.  *     }
  59.  *     buffer.append(value);
  60.  *   }
  61.  * }
  62.  * </pre>
  63.  *
  64.  * @since 1.0
  65.  */
  66. @SuppressWarnings("deprecation") // StringEscapeUtils
  67. public abstract class ToStringStyle implements Serializable {

  68.     /**
  69.      * Default {@link ToStringStyle}.
  70.      *
  71.      * <p>This is an inner class rather than using
  72.      * {@link StandardToStringStyle} to ensure its immutability.</p>
  73.      */
  74.     private static final class DefaultToStringStyle extends ToStringStyle {

  75.         /**
  76.          * Required for serialization support.
  77.          *
  78.          * @see java.io.Serializable
  79.          */
  80.         private static final long serialVersionUID = 1L;

  81.         /**
  82.          * Constructs a new instance.
  83.          *
  84.          * <p>Use the static constant rather than instantiating.</p>
  85.          */
  86.         DefaultToStringStyle() {
  87.         }

  88.         /**
  89.          * Ensure Singleton after serialization.
  90.          *
  91.          * @return the singleton
  92.          */
  93.         private Object readResolve() {
  94.             return DEFAULT_STYLE;
  95.         }

  96.     }

  97.     /**
  98.      * {@link ToStringStyle} that outputs with JSON format.
  99.      *
  100.      * <p>
  101.      * This is an inner class rather than using
  102.      * {@link StandardToStringStyle} to ensure its immutability.
  103.      * </p>
  104.      *
  105.      * @since 3.4
  106.      * @see <a href="https://www.json.org/">json.org</a>
  107.      */
  108.     private static final class JsonToStringStyle extends ToStringStyle {

  109.         private static final long serialVersionUID = 1L;

  110.         private static final String FIELD_NAME_QUOTE = "\"";

  111.         /**
  112.          * Constructs a new instance.
  113.          *
  114.          * <p>
  115.          * Use the static constant rather than instantiating.
  116.          * </p>
  117.          */
  118.         JsonToStringStyle() {
  119.             setUseClassName(false);
  120.             setUseIdentityHashCode(false);

  121.             setContentStart("{");
  122.             setContentEnd("}");

  123.             setArrayStart("[");
  124.             setArrayEnd("]");

  125.             setFieldSeparator(",");
  126.             setFieldNameValueSeparator(":");

  127.             setNullText("null");

  128.             setSummaryObjectStartText("\"<");
  129.             setSummaryObjectEndText(">\"");

  130.             setSizeStartText("\"<size=");
  131.             setSizeEndText(">\"");
  132.         }

  133.         @Override
  134.         public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
  135.             checkAppendInput(fieldName, fullDetail);
  136.             super.append(buffer, fieldName, array, fullDetail);
  137.         }

  138.         @Override
  139.         public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
  140.             checkAppendInput(fieldName, fullDetail);
  141.             super.append(buffer, fieldName, array, fullDetail);
  142.         }

  143.         @Override
  144.         public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
  145.             checkAppendInput(fieldName, fullDetail);
  146.             super.append(buffer, fieldName, array, fullDetail);
  147.         }

  148.         @Override
  149.         public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
  150.             checkAppendInput(fieldName, fullDetail);
  151.             super.append(buffer, fieldName, array, fullDetail);
  152.         }

  153.         @Override
  154.         public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
  155.             checkAppendInput(fieldName, fullDetail);
  156.             super.append(buffer, fieldName, array, fullDetail);
  157.         }

  158.         @Override
  159.         public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
  160.             checkAppendInput(fieldName, fullDetail);
  161.             super.append(buffer, fieldName, array, fullDetail);
  162.         }

  163.         @Override
  164.         public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
  165.             checkAppendInput(fieldName, fullDetail);
  166.             super.append(buffer, fieldName, array, fullDetail);
  167.         }

  168.         @Override
  169.         public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
  170.             checkAppendInput(fieldName, fullDetail);
  171.             super.append(buffer, fieldName, value, fullDetail);
  172.         }

  173.         @Override
  174.         public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
  175.             checkAppendInput(fieldName, fullDetail);
  176.             super.append(buffer, fieldName, array, fullDetail);
  177.         }

  178.         @Override
  179.         public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
  180.             checkAppendInput(fieldName, fullDetail);
  181.             super.append(buffer, fieldName, array, fullDetail);
  182.         }

  183.         @Override
  184.         protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
  185.             appendValueAsString(buffer, String.valueOf(value));
  186.         }

  187.         @Override
  188.         protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
  189.             if (coll != null && !coll.isEmpty()) {
  190.                 buffer.append(getArrayStart());
  191.                 int i = 0;
  192.                 for (final Object item : coll) {
  193.                     appendDetail(buffer, fieldName, i++, item);
  194.                 }
  195.                 buffer.append(getArrayEnd());
  196.                 return;
  197.             }
  198.             buffer.append(coll);
  199.         }

  200.         @Override
  201.         protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
  202.             if (map != null && !map.isEmpty()) {
  203.                 buffer.append(getContentStart());

  204.                 boolean firstItem = true;
  205.                 for (final Entry<?, ?> entry : map.entrySet()) {
  206.                     final String keyStr = Objects.toString(entry.getKey(), null);
  207.                     if (keyStr != null) {
  208.                         if (firstItem) {
  209.                             firstItem = false;
  210.                         } else {
  211.                             appendFieldEnd(buffer, keyStr);
  212.                         }
  213.                         appendFieldStart(buffer, keyStr);
  214.                         final Object value = entry.getValue();
  215.                         if (value == null) {
  216.                             appendNullText(buffer, keyStr);
  217.                         } else {
  218.                             appendInternal(buffer, keyStr, value, true);
  219.                         }
  220.                     }
  221.                 }

  222.                 buffer.append(getContentEnd());
  223.                 return;
  224.             }

  225.             buffer.append(map);
  226.         }

  227.         @Override
  228.         protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {

  229.             if (value == null) {
  230.                 appendNullText(buffer, fieldName);
  231.                 return;
  232.             }

  233.             if (value instanceof String || value instanceof Character) {
  234.                 appendValueAsString(buffer, value.toString());
  235.                 return;
  236.             }

  237.             if (value instanceof Number || value instanceof Boolean) {
  238.                 buffer.append(value);
  239.                 return;
  240.             }

  241.             final String valueAsString = value.toString();
  242.             if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
  243.                 buffer.append(value);
  244.                 return;
  245.             }

  246.             appendDetail(buffer, fieldName, valueAsString);
  247.         }

  248.         @Override
  249.         protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {

  250.             checkFieldName(fieldName);

  251.             super.appendFieldStart(buffer, FIELD_NAME_QUOTE + StringEscapeUtils.escapeJson(fieldName)
  252.                     + FIELD_NAME_QUOTE);
  253.         }

  254.         /**
  255.          * Appends the given String enclosed in double-quotes to the given StringBuffer.
  256.          *
  257.          * @param buffer the StringBuffer to append the value to.
  258.          * @param value the value to append.
  259.          */
  260.         private void appendValueAsString(final StringBuffer buffer, final String value) {
  261.             buffer.append('"').append(StringEscapeUtils.escapeJson(value)).append('"');
  262.         }

  263.         private void checkAppendInput(final String fieldName, final Boolean fullDetail) {
  264.             checkFieldName(fieldName);
  265.             checkIsFullDetail(fullDetail);
  266.         }

  267.         private void checkFieldName(final String fieldName) {
  268.             if (fieldName == null) {
  269.                 throw new UnsupportedOperationException("Field names are mandatory when using JsonToStringStyle");
  270.             }
  271.         }

  272.         private void checkIsFullDetail(final Boolean fullDetail) {
  273.             if (!isFullDetail(fullDetail)) {
  274.                 throw new UnsupportedOperationException("FullDetail must be true when using JsonToStringStyle");
  275.             }
  276.         }

  277.         private boolean isJsonArray(final String valueAsString) {
  278.             return valueAsString.startsWith(getArrayStart())
  279.                     && valueAsString.endsWith(getArrayEnd());
  280.         }

  281.         private boolean isJsonObject(final String valueAsString) {
  282.             return valueAsString.startsWith(getContentStart())
  283.                     && valueAsString.endsWith(getContentEnd());
  284.         }

  285.         /**
  286.          * Ensure Singleton after serialization.
  287.          *
  288.          * @return the singleton
  289.          */
  290.         private Object readResolve() {
  291.             return JSON_STYLE;
  292.         }

  293.     }

  294.     /**
  295.      * {@link ToStringStyle} that outputs on multiple lines.
  296.      *
  297.      * <p>This is an inner class rather than using
  298.      * {@link StandardToStringStyle} to ensure its immutability.</p>
  299.      */
  300.     private static final class MultiLineToStringStyle extends ToStringStyle {

  301.         private static final long serialVersionUID = 1L;

  302.         /**
  303.          * Constructs a new instance.
  304.          *
  305.          * <p>Use the static constant rather than instantiating.</p>
  306.          */
  307.         MultiLineToStringStyle() {
  308.             setContentStart("[");
  309.             setFieldSeparator(System.lineSeparator() + "  ");
  310.             setFieldSeparatorAtStart(true);
  311.             setContentEnd(System.lineSeparator() + "]");
  312.         }

  313.         /**
  314.          * Ensure Singleton after serialization.
  315.          *
  316.          * @return the singleton
  317.          */
  318.         private Object readResolve() {
  319.             return MULTI_LINE_STYLE;
  320.         }

  321.     }

  322.     /**
  323.      * {@link ToStringStyle} that does not print out the class name
  324.      * and identity hash code but prints content start and field names.
  325.      *
  326.      * <p>This is an inner class rather than using
  327.      * {@link StandardToStringStyle} to ensure its immutability.</p>
  328.      */
  329.     private static final class NoClassNameToStringStyle extends ToStringStyle {

  330.         private static final long serialVersionUID = 1L;

  331.         /**
  332.          * Constructs a new instance.
  333.          *
  334.          * <p>Use the static constant rather than instantiating.</p>
  335.          */
  336.         NoClassNameToStringStyle() {
  337.             setUseClassName(false);
  338.             setUseIdentityHashCode(false);
  339.         }

  340.         /**
  341.          * Ensure Singleton after serialization.
  342.          *
  343.          * @return the singleton
  344.          */
  345.         private Object readResolve() {
  346.             return NO_CLASS_NAME_STYLE;
  347.         }

  348.     }

  349.     /**
  350.      * {@link ToStringStyle} that does not print out
  351.      * the field names.
  352.      *
  353.      * <p>This is an inner class rather than using
  354.      * {@link StandardToStringStyle} to ensure its immutability.
  355.      */
  356.     private static final class NoFieldNameToStringStyle extends ToStringStyle {

  357.         private static final long serialVersionUID = 1L;

  358.         /**
  359.          * Constructs a new instance.
  360.          *
  361.          * <p>Use the static constant rather than instantiating.</p>
  362.          */
  363.         NoFieldNameToStringStyle() {
  364.             setUseFieldNames(false);
  365.         }

  366.         /**
  367.          * Ensure Singleton after serialization.
  368.          *
  369.          * @return the singleton
  370.          */
  371.         private Object readResolve() {
  372.             return NO_FIELD_NAMES_STYLE;
  373.         }

  374.     }

  375.     /**
  376.      * {@link ToStringStyle} that prints out the short
  377.      * class name and no identity hash code.
  378.      *
  379.      * <p>This is an inner class rather than using
  380.      * {@link StandardToStringStyle} to ensure its immutability.</p>
  381.      */
  382.     private static final class ShortPrefixToStringStyle extends ToStringStyle {

  383.         private static final long serialVersionUID = 1L;

  384.         /**
  385.          * Constructs a new instance.
  386.          *
  387.          * <p>Use the static constant rather than instantiating.</p>
  388.          */
  389.         ShortPrefixToStringStyle() {
  390.             setUseShortClassName(true);
  391.             setUseIdentityHashCode(false);
  392.         }

  393.         /**
  394.          * Ensure <code>Singleton</ode> after serialization.
  395.          * @return the singleton
  396.          */
  397.         private Object readResolve() {
  398.             return SHORT_PREFIX_STYLE;
  399.         }

  400.     }

  401.     /**
  402.      * {@link ToStringStyle} that does not print out the
  403.      * class name, identity hash code, content start or field name.
  404.      *
  405.      * <p>This is an inner class rather than using
  406.      * {@link StandardToStringStyle} to ensure its immutability.</p>
  407.      */
  408.     private static final class SimpleToStringStyle extends ToStringStyle {

  409.         private static final long serialVersionUID = 1L;

  410.         /**
  411.          * Constructs a new instance.
  412.          *
  413.          * <p>Use the static constant rather than instantiating.</p>
  414.          */
  415.         SimpleToStringStyle() {
  416.             setUseClassName(false);
  417.             setUseIdentityHashCode(false);
  418.             setUseFieldNames(false);
  419.             setContentStart(StringUtils.EMPTY);
  420.             setContentEnd(StringUtils.EMPTY);
  421.         }

  422.         /**
  423.          * Ensure <code>Singleton</ode> after serialization.
  424.          * @return the singleton
  425.          */
  426.         private Object readResolve() {
  427.             return SIMPLE_STYLE;
  428.         }

  429.     }

  430.     /**
  431.      * Serialization version ID.
  432.      */
  433.     private static final long serialVersionUID = -2587890625525655916L;

  434.     /**
  435.      * The default toString style. Using the {@code Person}
  436.      * example from {@link ToStringBuilder}, the output would look like this:
  437.      *
  438.      * <pre>
  439.      * Person@182f0db[name=John Doe,age=33,smoker=false]
  440.      * </pre>
  441.      */
  442.     public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();

  443.     /**
  444.      * The multi line toString style. Using the {@code Person}
  445.      * example from {@link ToStringBuilder}, the output would look like this:
  446.      *
  447.      * <pre>
  448.      * Person@182f0db[
  449.      *   name=John Doe
  450.      *   age=33
  451.      *   smoker=false
  452.      * ]
  453.      * </pre>
  454.      */
  455.     public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();

  456.     /**
  457.      * The no field names toString style. Using the
  458.      * {@code Person} example from {@link ToStringBuilder}, the output
  459.      * would look like this:
  460.      *
  461.      * <pre>
  462.      * Person@182f0db[John Doe,33,false]
  463.      * </pre>
  464.      */
  465.     public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();

  466.     /**
  467.      * The short prefix toString style. Using the {@code Person} example
  468.      * from {@link ToStringBuilder}, the output would look like this:
  469.      *
  470.      * <pre>
  471.      * Person[name=John Doe,age=33,smoker=false]
  472.      * </pre>
  473.      *
  474.      * @since 2.1
  475.      */
  476.     public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();

  477.     /**
  478.      * The simple toString style. Using the {@code Person}
  479.      * example from {@link ToStringBuilder}, the output would look like this:
  480.      *
  481.      * <pre>
  482.      * John Doe,33,false
  483.      * </pre>
  484.      */
  485.     public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();

  486.     /**
  487.      * The no class name toString style. Using the {@code Person}
  488.      * example from {@link ToStringBuilder}, the output would look like this:
  489.      *
  490.      * <pre>
  491.      * [name=John Doe,age=33,smoker=false]
  492.      * </pre>
  493.      *
  494.      * @since 3.4
  495.      */
  496.     public static final ToStringStyle NO_CLASS_NAME_STYLE = new NoClassNameToStringStyle();

  497.     /**
  498.      * The JSON toString style. Using the {@code Person} example from
  499.      * {@link ToStringBuilder}, the output would look like this:
  500.      *
  501.      * <pre>
  502.      * {"name": "John Doe", "age": 33, "smoker": true}
  503.      * </pre>
  504.      *
  505.      * <strong>Note:</strong> Since field names are mandatory in JSON, this
  506.      * ToStringStyle will throw an {@link UnsupportedOperationException} if no
  507.      * field name is passed in while appending. Furthermore This ToStringStyle
  508.      * will only generate valid JSON if referenced objects also produce JSON
  509.      * when calling {@code toString()} on them.
  510.      *
  511.      * @since 3.4
  512.      * @see <a href="https://www.json.org/">json.org</a>
  513.      */
  514.     public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();

  515.     /**
  516.      * A registry of objects used by {@code reflectionToString} methods
  517.      * to detect cyclical object references and avoid infinite loops.
  518.      */
  519.     private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY = ThreadLocal.withInitial(WeakHashMap::new);
  520.     /*
  521.      * Note that objects of this class are generally shared between threads, so
  522.      * an instance variable would not be suitable here.
  523.      *
  524.      * In normal use the registry should always be left empty, because the caller
  525.      * should call toString() which will clean up.
  526.      *
  527.      * See LANG-792
  528.      */

  529.     /**
  530.      * Gets the registry of objects being traversed by the {@code reflectionToString}
  531.      * methods in the current thread.
  532.      *
  533.      * @return Set the registry of objects being traversed
  534.      */
  535.     public static Map<Object, Object> getRegistry() {
  536.         return REGISTRY.get();
  537.     }

  538.     /**
  539.      * Tests whether the registry contains the given object.
  540.      * Used by the reflection methods to avoid infinite loops.
  541.      *
  542.      * @param value
  543.      *                  The object to lookup in the registry.
  544.      * @return boolean {@code true} if the registry contains the given
  545.      *             object.
  546.      */
  547.     static boolean isRegistered(final Object value) {
  548.         return getRegistry().containsKey(value);
  549.     }

  550.     /**
  551.      * Registers the given object. Used by the reflection methods to avoid
  552.      * infinite loops.
  553.      *
  554.      * @param value
  555.      *                  The object to register.
  556.      */
  557.     static void register(final Object value) {
  558.         if (value != null) {
  559.             getRegistry().put(value, null);
  560.         }
  561.     }

  562.     /**
  563.      * Unregisters the given object.
  564.      *
  565.      * <p>
  566.      * Used by the reflection methods to avoid infinite loops.
  567.      * </p>
  568.      *
  569.      * @param value
  570.      *                  The object to unregister.
  571.      */
  572.     static void unregister(final Object value) {
  573.         if (value != null) {
  574.             final Map<Object, Object> m = getRegistry();
  575.             m.remove(value);
  576.             if (m.isEmpty()) {
  577.                 REGISTRY.remove();
  578.             }
  579.         }
  580.     }

  581.     /**
  582.      * Whether to use the field names, the default is {@code true}.
  583.      */
  584.     private boolean useFieldNames = true;

  585.     /**
  586.      * Whether to use the class name, the default is {@code true}.
  587.      */
  588.     private boolean useClassName = true;

  589.     /**
  590.      * Whether to use short class names, the default is {@code false}.
  591.      */
  592.     private boolean useShortClassName;

  593.     /**
  594.      * Whether to use the identity hash code, the default is {@code true}.
  595.      */
  596.     private boolean useIdentityHashCode = true;

  597.     /**
  598.      * The content start {@code '['}.
  599.      */
  600.     private String contentStart = "[";

  601.     /**
  602.      * The content end {@code ']'}.
  603.      */
  604.     private String contentEnd = "]";

  605.     /**
  606.      * The field name value separator {@code '='}.
  607.      */
  608.     private String fieldNameValueSeparator = "=";

  609.     /**
  610.      * Whether the field separator should be added before any other fields.
  611.      */
  612.     private boolean fieldSeparatorAtStart;

  613.     /**
  614.      * Whether the field separator should be added after any other fields.
  615.      */
  616.     private boolean fieldSeparatorAtEnd;

  617.     /**
  618.      * The field separator {@code ','}.
  619.      */
  620.     private String fieldSeparator = ",";

  621.     /**
  622.      * The array start <code>'{'</code>.
  623.      */
  624.     private String arrayStart = "{";

  625.     /**
  626.      * The array separator {@code ','}.
  627.      */
  628.     private String arraySeparator = ",";

  629.     /**
  630.      * The detail for array content.
  631.      */
  632.     private boolean arrayContentDetail = true;

  633.     /**
  634.      * The array end {@code '}'}.
  635.      */
  636.     private String arrayEnd = "}";

  637.     /**
  638.      * The value to use when fullDetail is {@code null},
  639.      * the default value is {@code true}.
  640.      */
  641.     private boolean defaultFullDetail = true;

  642.     /**
  643.      * The {@code null} text {@code "<null>"}.
  644.      */
  645.     private String nullText = "<null>";

  646.     /**
  647.      * The summary size text start {@code "<size="}.
  648.      */
  649.     private String sizeStartText = "<size=";

  650.     /**
  651.      * The summary size text start {@code ">"}.
  652.      */
  653.     private String sizeEndText = ">";

  654.     /**
  655.      * The summary object text start {@code "<"}.
  656.      */
  657.     private String summaryObjectStartText = "<";

  658.     /**
  659.      * The summary object text start {@code ">"}.
  660.      */
  661.     private String summaryObjectEndText = ">";

  662.     /**
  663.      * Constructs a new instance.
  664.      */
  665.     protected ToStringStyle() {
  666.     }

  667.     /**
  668.      * Appends to the {@code toString} a {@code boolean}
  669.      * value.
  670.      *
  671.      * @param buffer  the {@link StringBuffer} to populate
  672.      * @param fieldName  the field name
  673.      * @param value  the value to add to the {@code toString}
  674.      */
  675.     public void append(final StringBuffer buffer, final String fieldName, final boolean value) {
  676.         appendFieldStart(buffer, fieldName);
  677.         appendDetail(buffer, fieldName, value);
  678.         appendFieldEnd(buffer, fieldName);
  679.     }

  680.     /**
  681.      * Appends to the {@code toString} a {@code boolean}
  682.      * array.
  683.      *
  684.      * @param buffer  the {@link StringBuffer} to populate
  685.      * @param fieldName  the field name
  686.      * @param array  the array to add to the toString
  687.      * @param fullDetail  {@code true} for detail, {@code false}
  688.      *  for summary info, {@code null} for style decides
  689.      */
  690.     public void append(final StringBuffer buffer, final String fieldName, final boolean[] array, final Boolean fullDetail) {
  691.         appendFieldStart(buffer, fieldName);

  692.         if (array == null) {
  693.             appendNullText(buffer, fieldName);

  694.         } else if (isFullDetail(fullDetail)) {
  695.             appendDetail(buffer, fieldName, array);

  696.         } else {
  697.             appendSummary(buffer, fieldName, array);
  698.         }

  699.         appendFieldEnd(buffer, fieldName);
  700.     }

  701.     /**
  702.      * Appends to the {@code toString} a {@code byte}
  703.      * value.
  704.      *
  705.      * @param buffer  the {@link StringBuffer} to populate
  706.      * @param fieldName  the field name
  707.      * @param value  the value to add to the {@code toString}
  708.      */
  709.     public void append(final StringBuffer buffer, final String fieldName, final byte value) {
  710.         appendFieldStart(buffer, fieldName);
  711.         appendDetail(buffer, fieldName, value);
  712.         appendFieldEnd(buffer, fieldName);
  713.     }

  714.     /**
  715.      * Appends to the {@code toString} a {@code byte}
  716.      * array.
  717.      *
  718.      * @param buffer  the {@link StringBuffer} to populate
  719.      * @param fieldName  the field name
  720.      * @param array  the array to add to the {@code toString}
  721.      * @param fullDetail  {@code true} for detail, {@code false}
  722.      *  for summary info, {@code null} for style decides
  723.      */
  724.     public void append(final StringBuffer buffer, final String fieldName, final byte[] array, final Boolean fullDetail) {
  725.         appendFieldStart(buffer, fieldName);

  726.         if (array == null) {
  727.             appendNullText(buffer, fieldName);

  728.         } else if (isFullDetail(fullDetail)) {
  729.             appendDetail(buffer, fieldName, array);

  730.         } else {
  731.             appendSummary(buffer, fieldName, array);
  732.         }

  733.         appendFieldEnd(buffer, fieldName);
  734.     }

  735.     /**
  736.      * Appends to the {@code toString} a {@code char}
  737.      * value.
  738.      *
  739.      * @param buffer  the {@link StringBuffer} to populate
  740.      * @param fieldName  the field name
  741.      * @param value  the value to add to the {@code toString}
  742.      */
  743.     public void append(final StringBuffer buffer, final String fieldName, final char value) {
  744.         appendFieldStart(buffer, fieldName);
  745.         appendDetail(buffer, fieldName, value);
  746.         appendFieldEnd(buffer, fieldName);
  747.     }

  748.     /**
  749.      * Appends to the {@code toString} a {@code char}
  750.      * array.
  751.      *
  752.      * @param buffer  the {@link StringBuffer} to populate
  753.      * @param fieldName  the field name
  754.      * @param array  the array to add to the {@code toString}
  755.      * @param fullDetail  {@code true} for detail, {@code false}
  756.      *  for summary info, {@code null} for style decides
  757.      */
  758.     public void append(final StringBuffer buffer, final String fieldName, final char[] array, final Boolean fullDetail) {
  759.         appendFieldStart(buffer, fieldName);

  760.         if (array == null) {
  761.             appendNullText(buffer, fieldName);

  762.         } else if (isFullDetail(fullDetail)) {
  763.             appendDetail(buffer, fieldName, array);

  764.         } else {
  765.             appendSummary(buffer, fieldName, array);
  766.         }

  767.         appendFieldEnd(buffer, fieldName);
  768.     }

  769.     /**
  770.      * Appends to the {@code toString} a {@code double}
  771.      * value.
  772.      *
  773.      * @param buffer  the {@link StringBuffer} to populate
  774.      * @param fieldName  the field name
  775.      * @param value  the value to add to the {@code toString}
  776.      */
  777.     public void append(final StringBuffer buffer, final String fieldName, final double value) {
  778.         appendFieldStart(buffer, fieldName);
  779.         appendDetail(buffer, fieldName, value);
  780.         appendFieldEnd(buffer, fieldName);
  781.     }

  782.     /**
  783.      * Appends to the {@code toString} a {@code double}
  784.      * array.
  785.      *
  786.      * @param buffer  the {@link StringBuffer} to populate
  787.      * @param fieldName  the field name
  788.      * @param array  the array to add to the toString
  789.      * @param fullDetail  {@code true} for detail, {@code false}
  790.      *  for summary info, {@code null} for style decides
  791.      */
  792.     public void append(final StringBuffer buffer, final String fieldName, final double[] array, final Boolean fullDetail) {
  793.         appendFieldStart(buffer, fieldName);

  794.         if (array == null) {
  795.             appendNullText(buffer, fieldName);

  796.         } else if (isFullDetail(fullDetail)) {
  797.             appendDetail(buffer, fieldName, array);

  798.         } else {
  799.             appendSummary(buffer, fieldName, array);
  800.         }

  801.         appendFieldEnd(buffer, fieldName);
  802.     }

  803.     /**
  804.      * Appends to the {@code toString} a {@code float}
  805.      * value.
  806.      *
  807.      * @param buffer  the {@link StringBuffer} to populate
  808.      * @param fieldName  the field name
  809.      * @param value  the value to add to the {@code toString}
  810.      */
  811.     public void append(final StringBuffer buffer, final String fieldName, final float value) {
  812.         appendFieldStart(buffer, fieldName);
  813.         appendDetail(buffer, fieldName, value);
  814.         appendFieldEnd(buffer, fieldName);
  815.     }

  816.     /**
  817.      * Appends to the {@code toString} a {@code float}
  818.      * array.
  819.      *
  820.      * @param buffer  the {@link StringBuffer} to populate
  821.      * @param fieldName  the field name
  822.      * @param array  the array to add to the toString
  823.      * @param fullDetail  {@code true} for detail, {@code false}
  824.      *  for summary info, {@code null} for style decides
  825.      */
  826.     public void append(final StringBuffer buffer, final String fieldName, final float[] array, final Boolean fullDetail) {
  827.         appendFieldStart(buffer, fieldName);

  828.         if (array == null) {
  829.             appendNullText(buffer, fieldName);

  830.         } else if (isFullDetail(fullDetail)) {
  831.             appendDetail(buffer, fieldName, array);

  832.         } else {
  833.             appendSummary(buffer, fieldName, array);
  834.         }

  835.         appendFieldEnd(buffer, fieldName);
  836.     }

  837.     /**
  838.      * Appends to the {@code toString} an {@code int}
  839.      * value.
  840.      *
  841.      * @param buffer  the {@link StringBuffer} to populate
  842.      * @param fieldName  the field name
  843.      * @param value  the value to add to the {@code toString}
  844.      */
  845.     public void append(final StringBuffer buffer, final String fieldName, final int value) {
  846.         appendFieldStart(buffer, fieldName);
  847.         appendDetail(buffer, fieldName, value);
  848.         appendFieldEnd(buffer, fieldName);
  849.     }

  850.     /**
  851.      * Appends to the {@code toString} an {@code int}
  852.      * array.
  853.      *
  854.      * @param buffer  the {@link StringBuffer} to populate
  855.      * @param fieldName  the field name
  856.      * @param array  the array to add to the {@code toString}
  857.      * @param fullDetail  {@code true} for detail, {@code false}
  858.      *  for summary info, {@code null} for style decides
  859.      */
  860.     public void append(final StringBuffer buffer, final String fieldName, final int[] array, final Boolean fullDetail) {
  861.         appendFieldStart(buffer, fieldName);

  862.         if (array == null) {
  863.             appendNullText(buffer, fieldName);

  864.         } else if (isFullDetail(fullDetail)) {
  865.             appendDetail(buffer, fieldName, array);

  866.         } else {
  867.             appendSummary(buffer, fieldName, array);
  868.         }

  869.         appendFieldEnd(buffer, fieldName);
  870.     }

  871.     /**
  872.      * <p>Appends to the {@code toString} a {@code long}
  873.      * value.
  874.      *
  875.      * @param buffer  the {@link StringBuffer} to populate
  876.      * @param fieldName  the field name
  877.      * @param value  the value to add to the {@code toString}
  878.      */
  879.     public void append(final StringBuffer buffer, final String fieldName, final long value) {
  880.         appendFieldStart(buffer, fieldName);
  881.         appendDetail(buffer, fieldName, value);
  882.         appendFieldEnd(buffer, fieldName);
  883.     }

  884.     /**
  885.      * Appends to the {@code toString} a {@code long}
  886.      * array.
  887.      *
  888.      * @param buffer  the {@link StringBuffer} to populate
  889.      * @param fieldName  the field name
  890.      * @param array  the array to add to the {@code toString}
  891.      * @param fullDetail  {@code true} for detail, {@code false}
  892.      *  for summary info, {@code null} for style decides
  893.      */
  894.     public void append(final StringBuffer buffer, final String fieldName, final long[] array, final Boolean fullDetail) {
  895.         appendFieldStart(buffer, fieldName);

  896.         if (array == null) {
  897.             appendNullText(buffer, fieldName);

  898.         } else if (isFullDetail(fullDetail)) {
  899.             appendDetail(buffer, fieldName, array);

  900.         } else {
  901.             appendSummary(buffer, fieldName, array);
  902.         }

  903.         appendFieldEnd(buffer, fieldName);
  904.     }

  905.     /**
  906.      * Appends to the {@code toString} an {@link Object}
  907.      * value, printing the full {@code toString} of the
  908.      * {@link Object} passed in.
  909.      *
  910.      * @param buffer  the {@link StringBuffer} to populate
  911.      * @param fieldName  the field name
  912.      * @param value  the value to add to the {@code toString}
  913.      * @param fullDetail  {@code true} for detail, {@code false}
  914.      *  for summary info, {@code null} for style decides
  915.      */
  916.     public void append(final StringBuffer buffer, final String fieldName, final Object value, final Boolean fullDetail) {
  917.         appendFieldStart(buffer, fieldName);

  918.         if (value == null) {
  919.             appendNullText(buffer, fieldName);

  920.         } else {
  921.             appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
  922.         }

  923.         appendFieldEnd(buffer, fieldName);
  924.     }

  925.     /**
  926.      * Appends to the {@code toString} an {@link Object}
  927.      * array.
  928.      *
  929.      * @param buffer  the {@link StringBuffer} to populate
  930.      * @param fieldName  the field name
  931.      * @param array  the array to add to the toString
  932.      * @param fullDetail  {@code true} for detail, {@code false}
  933.      *  for summary info, {@code null} for style decides
  934.      */
  935.     public void append(final StringBuffer buffer, final String fieldName, final Object[] array, final Boolean fullDetail) {
  936.         appendFieldStart(buffer, fieldName);

  937.         if (array == null) {
  938.             appendNullText(buffer, fieldName);

  939.         } else if (isFullDetail(fullDetail)) {
  940.             appendDetail(buffer, fieldName, array);

  941.         } else {
  942.             appendSummary(buffer, fieldName, array);
  943.         }

  944.         appendFieldEnd(buffer, fieldName);
  945.     }

  946.     /**
  947.      * Appends to the {@code toString} a {@code short}
  948.      * value.
  949.      *
  950.      * @param buffer  the {@link StringBuffer} to populate
  951.      * @param fieldName  the field name
  952.      * @param value  the value to add to the {@code toString}
  953.      */
  954.     public void append(final StringBuffer buffer, final String fieldName, final short value) {
  955.         appendFieldStart(buffer, fieldName);
  956.         appendDetail(buffer, fieldName, value);
  957.         appendFieldEnd(buffer, fieldName);
  958.     }

  959.     /**
  960.      * Appends to the {@code toString} a {@code short}
  961.      * array.
  962.      *
  963.      * @param buffer  the {@link StringBuffer} to populate
  964.      * @param fieldName  the field name
  965.      * @param array  the array to add to the {@code toString}
  966.      * @param fullDetail  {@code true} for detail, {@code false}
  967.      *  for summary info, {@code null} for style decides
  968.      */
  969.     public void append(final StringBuffer buffer, final String fieldName, final short[] array, final Boolean fullDetail) {
  970.         appendFieldStart(buffer, fieldName);

  971.         if (array == null) {
  972.             appendNullText(buffer, fieldName);

  973.         } else if (isFullDetail(fullDetail)) {
  974.             appendDetail(buffer, fieldName, array);

  975.         } else {
  976.             appendSummary(buffer, fieldName, array);
  977.         }

  978.         appendFieldEnd(buffer, fieldName);
  979.     }

  980.     /**
  981.      * Appends to the {@code toString} the class name.
  982.      *
  983.      * @param buffer  the {@link StringBuffer} to populate
  984.      * @param object  the {@link Object} whose name to output
  985.      */
  986.     protected void appendClassName(final StringBuffer buffer, final Object object) {
  987.         if (useClassName && object != null) {
  988.             register(object);
  989.             if (useShortClassName) {
  990.                 buffer.append(getShortClassName(object.getClass()));
  991.             } else {
  992.                 buffer.append(object.getClass().getName());
  993.             }
  994.         }
  995.     }

  996.     /**
  997.      * Appends to the {@code toString} the content end.
  998.      *
  999.      * @param buffer  the {@link StringBuffer} to populate
  1000.      */
  1001.     protected void appendContentEnd(final StringBuffer buffer) {
  1002.         buffer.append(contentEnd);
  1003.     }

  1004.     /**
  1005.      * Appends to the {@code toString} the content start.
  1006.      *
  1007.      * @param buffer  the {@link StringBuffer} to populate
  1008.      */
  1009.     protected void appendContentStart(final StringBuffer buffer) {
  1010.         buffer.append(contentStart);
  1011.     }

  1012.     /**
  1013.      * Appends to the {@code toString} an {@link Object}
  1014.      * value that has been detected to participate in a cycle. This
  1015.      * implementation will print the standard string value of the value.
  1016.      *
  1017.      * @param buffer  the {@link StringBuffer} to populate
  1018.      * @param fieldName  the field name, typically not used as already appended
  1019.      * @param value  the value to add to the {@code toString},
  1020.      *  not {@code null}
  1021.      *
  1022.      * @since 2.2
  1023.      */
  1024.     protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
  1025.        ObjectUtils.identityToString(buffer, value);
  1026.     }

  1027.     /**
  1028.      * Appends to the {@code toString} a {@code boolean}
  1029.      * value.
  1030.      *
  1031.      * @param buffer  the {@link StringBuffer} to populate
  1032.      * @param fieldName  the field name, typically not used as already appended
  1033.      * @param value  the value to add to the {@code toString}
  1034.      */
  1035.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean value) {
  1036.         buffer.append(value);
  1037.     }

  1038.     /**
  1039.      * Appends to the {@code toString} the detail of a
  1040.      * {@code boolean} array.
  1041.      *
  1042.      * @param buffer  the {@link StringBuffer} to populate
  1043.      * @param fieldName  the field name, typically not used as already appended
  1044.      * @param array  the array to add to the {@code toString},
  1045.      *  not {@code null}
  1046.      */
  1047.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
  1048.         buffer.append(arrayStart);
  1049.         for (int i = 0; i < array.length; i++) {
  1050.             if (i > 0) {
  1051.                 buffer.append(arraySeparator);
  1052.             }
  1053.             appendDetail(buffer, fieldName, array[i]);
  1054.         }
  1055.         buffer.append(arrayEnd);
  1056.     }

  1057.     /**
  1058.      * Appends to the {@code toString} a {@code byte}
  1059.      * value.
  1060.      *
  1061.      * @param buffer  the {@link StringBuffer} to populate
  1062.      * @param fieldName  the field name, typically not used as already appended
  1063.      * @param value  the value to add to the {@code toString}
  1064.      */
  1065.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte value) {
  1066.         buffer.append(value);
  1067.     }

  1068.     /**
  1069.      * Appends to the {@code toString} the detail of a
  1070.      * {@code byte} array.
  1071.      *
  1072.      * @param buffer  the {@link StringBuffer} to populate
  1073.      * @param fieldName  the field name, typically not used as already appended
  1074.      * @param array  the array to add to the {@code toString},
  1075.      *  not {@code null}
  1076.      */
  1077.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
  1078.         buffer.append(arrayStart);
  1079.         for (int i = 0; i < array.length; i++) {
  1080.             if (i > 0) {
  1081.                 buffer.append(arraySeparator);
  1082.             }
  1083.             appendDetail(buffer, fieldName, array[i]);
  1084.         }
  1085.         buffer.append(arrayEnd);
  1086.     }

  1087.     /**
  1088.      * Appends to the {@code toString} a {@code char}
  1089.      * value.
  1090.      *
  1091.      * @param buffer  the {@link StringBuffer} to populate
  1092.      * @param fieldName  the field name, typically not used as already appended
  1093.      * @param value  the value to add to the {@code toString}
  1094.      */
  1095.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char value) {
  1096.         buffer.append(value);
  1097.     }

  1098.     /**
  1099.      * Appends to the {@code toString} the detail of a
  1100.      * {@code char} array.
  1101.      *
  1102.      * @param buffer  the {@link StringBuffer} to populate
  1103.      * @param fieldName  the field name, typically not used as already appended
  1104.      * @param array  the array to add to the {@code toString},
  1105.      *  not {@code null}
  1106.      */
  1107.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
  1108.         buffer.append(arrayStart);
  1109.         for (int i = 0; i < array.length; i++) {
  1110.             if (i > 0) {
  1111.                 buffer.append(arraySeparator);
  1112.             }
  1113.             appendDetail(buffer, fieldName, array[i]);
  1114.         }
  1115.         buffer.append(arrayEnd);
  1116.     }

  1117.     /**
  1118.      * Appends to the {@code toString} a {@link Collection}.
  1119.      *
  1120.      * @param buffer  the {@link StringBuffer} to populate
  1121.      * @param fieldName  the field name, typically not used as already appended
  1122.      * @param coll  the {@link Collection} to add to the
  1123.      *  {@code toString}, not {@code null}
  1124.      */
  1125.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Collection<?> coll) {
  1126.         buffer.append(coll);
  1127.     }

  1128.     /**
  1129.      * Appends to the {@code toString} a {@code double}
  1130.      * value.
  1131.      *
  1132.      * @param buffer  the {@link StringBuffer} to populate
  1133.      * @param fieldName  the field name, typically not used as already appended
  1134.      * @param value  the value to add to the {@code toString}
  1135.      */
  1136.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double value) {
  1137.         buffer.append(value);
  1138.     }

  1139.     /**
  1140.      * Appends to the {@code toString} the detail of a
  1141.      * {@code double} array.
  1142.      *
  1143.      * @param buffer  the {@link StringBuffer} to populate
  1144.      * @param fieldName  the field name, typically not used as already appended
  1145.      * @param array  the array to add to the {@code toString},
  1146.      *  not {@code null}
  1147.      */
  1148.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
  1149.         buffer.append(arrayStart);
  1150.         for (int i = 0; i < array.length; i++) {
  1151.             if (i > 0) {
  1152.                 buffer.append(arraySeparator);
  1153.             }
  1154.             appendDetail(buffer, fieldName, array[i]);
  1155.         }
  1156.         buffer.append(arrayEnd);
  1157.     }

  1158.     /**
  1159.      * Appends to the {@code toString} a {@code float}
  1160.      * value.
  1161.      *
  1162.      * @param buffer  the {@link StringBuffer} to populate
  1163.      * @param fieldName  the field name, typically not used as already appended
  1164.      * @param value  the value to add to the {@code toString}
  1165.      */
  1166.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float value) {
  1167.         buffer.append(value);
  1168.     }

  1169.     /**
  1170.      * Appends to the {@code toString} the detail of a
  1171.      * {@code float} array.
  1172.      *
  1173.      * @param buffer  the {@link StringBuffer} to populate
  1174.      * @param fieldName  the field name, typically not used as already appended
  1175.      * @param array  the array to add to the {@code toString},
  1176.      *  not {@code null}
  1177.      */
  1178.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
  1179.         buffer.append(arrayStart);
  1180.         for (int i = 0; i < array.length; i++) {
  1181.             if (i > 0) {
  1182.                 buffer.append(arraySeparator);
  1183.             }
  1184.             appendDetail(buffer, fieldName, array[i]);
  1185.         }
  1186.         buffer.append(arrayEnd);
  1187.     }

  1188.     /**
  1189.      * Appends to the {@code toString} an {@code int}
  1190.      * value.
  1191.      *
  1192.      * @param buffer  the {@link StringBuffer} to populate
  1193.      * @param fieldName  the field name, typically not used as already appended
  1194.      * @param value  the value to add to the {@code toString}
  1195.      */
  1196.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int value) {
  1197.         buffer.append(value);
  1198.     }

  1199.     /**
  1200.      * Appends to the {@code toString} the detail of an
  1201.      * {@link Object} array item.
  1202.      *
  1203.      * @param buffer  the {@link StringBuffer} to populate
  1204.      * @param fieldName  the field name, typically not used as already appended
  1205.      * @param i the array item index to add
  1206.      * @param item the array item to add
  1207.      * @since 3.11
  1208.      */
  1209.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int i, final Object item) {
  1210.         if (i > 0) {
  1211.             buffer.append(arraySeparator);
  1212.         }
  1213.         if (item == null) {
  1214.             appendNullText(buffer, fieldName);
  1215.         } else {
  1216.             appendInternal(buffer, fieldName, item, arrayContentDetail);
  1217.         }
  1218.     }

  1219.     /**
  1220.      * Appends to the {@code toString} the detail of an
  1221.      * {@code int} array.
  1222.      *
  1223.      * @param buffer  the {@link StringBuffer} to populate
  1224.      * @param fieldName  the field name, typically not used as already appended
  1225.      * @param array  the array to add to the {@code toString},
  1226.      *  not {@code null}
  1227.      */
  1228.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
  1229.         buffer.append(arrayStart);
  1230.         for (int i = 0; i < array.length; i++) {
  1231.             if (i > 0) {
  1232.                 buffer.append(arraySeparator);
  1233.             }
  1234.             appendDetail(buffer, fieldName, array[i]);
  1235.         }
  1236.         buffer.append(arrayEnd);
  1237.     }

  1238.     /**
  1239.      * Appends to the {@code toString} a {@code long}
  1240.      * value.
  1241.      *
  1242.      * @param buffer  the {@link StringBuffer} to populate
  1243.      * @param fieldName  the field name, typically not used as already appended
  1244.      * @param value  the value to add to the {@code toString}
  1245.      */
  1246.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long value) {
  1247.         buffer.append(value);
  1248.     }

  1249.     /**
  1250.      * Appends to the {@code toString} the detail of a
  1251.      * {@code long} array.
  1252.      *
  1253.      * @param buffer  the {@link StringBuffer} to populate
  1254.      * @param fieldName  the field name, typically not used as already appended
  1255.      * @param array  the array to add to the {@code toString},
  1256.      *  not {@code null}
  1257.      */
  1258.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
  1259.         buffer.append(arrayStart);
  1260.         for (int i = 0; i < array.length; i++) {
  1261.             if (i > 0) {
  1262.                 buffer.append(arraySeparator);
  1263.             }
  1264.             appendDetail(buffer, fieldName, array[i]);
  1265.         }
  1266.         buffer.append(arrayEnd);
  1267.     }

  1268.     /**
  1269.      * Appends to the {@code toString} a {@link Map}.
  1270.      *
  1271.      * @param buffer  the {@link StringBuffer} to populate
  1272.      * @param fieldName  the field name, typically not used as already appended
  1273.      * @param map  the {@link Map} to add to the {@code toString},
  1274.      *  not {@code null}
  1275.      */
  1276.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Map<?, ?> map) {
  1277.         buffer.append(map);
  1278.     }

  1279.     /**
  1280.      * Appends to the {@code toString} an {@link Object}
  1281.      * value, printing the full detail of the {@link Object}.
  1282.      *
  1283.      * @param buffer  the {@link StringBuffer} to populate
  1284.      * @param fieldName  the field name, typically not used as already appended
  1285.      * @param value  the value to add to the {@code toString},
  1286.      *  not {@code null}
  1287.      */
  1288.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
  1289.         buffer.append(value);
  1290.     }

  1291.     /**
  1292.      * Appends to the {@code toString} the detail of an
  1293.      * {@link Object} array.
  1294.      *
  1295.      * @param buffer  the {@link StringBuffer} to populate
  1296.      * @param fieldName  the field name, typically not used as already appended
  1297.      * @param array  the array to add to the {@code toString},
  1298.      *  not {@code null}
  1299.      */
  1300.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
  1301.         buffer.append(arrayStart);
  1302.         for (int i = 0; i < array.length; i++) {
  1303.             appendDetail(buffer, fieldName, i, array[i]);
  1304.         }
  1305.         buffer.append(arrayEnd);
  1306.     }

  1307.     /**
  1308.      * Appends to the {@code toString} a {@code short}
  1309.      * value.
  1310.      *
  1311.      * @param buffer  the {@link StringBuffer} to populate
  1312.      * @param fieldName  the field name, typically not used as already appended
  1313.      * @param value  the value to add to the {@code toString}
  1314.      */
  1315.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final short value) {
  1316.         buffer.append(value);
  1317.     }

  1318.     /**
  1319.      * Appends to the {@code toString} the detail of a
  1320.      * {@code short} array.
  1321.      *
  1322.      * @param buffer  the {@link StringBuffer} to populate
  1323.      * @param fieldName  the field name, typically not used as already appended
  1324.      * @param array  the array to add to the {@code toString},
  1325.      *  not {@code null}
  1326.      */
  1327.     protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
  1328.         buffer.append(arrayStart);
  1329.         for (int i = 0; i < array.length; i++) {
  1330.             if (i > 0) {
  1331.                 buffer.append(arraySeparator);
  1332.             }
  1333.             appendDetail(buffer, fieldName, array[i]);
  1334.         }
  1335.         buffer.append(arrayEnd);
  1336.     }

  1337.     /**
  1338.      * Appends to the {@code toString} the end of data indicator.
  1339.      *
  1340.      * @param buffer  the {@link StringBuffer} to populate
  1341.      * @param object  the {@link Object} to build a
  1342.      *  {@code toString} for.
  1343.      */
  1344.     public void appendEnd(final StringBuffer buffer, final Object object) {
  1345.         if (!this.fieldSeparatorAtEnd) {
  1346.             removeLastFieldSeparator(buffer);
  1347.         }
  1348.         appendContentEnd(buffer);
  1349.         unregister(object);
  1350.     }

  1351.     /**
  1352.      * Appends to the {@code toString} the field end.
  1353.      *
  1354.      * @param buffer  the {@link StringBuffer} to populate
  1355.      * @param fieldName  the field name, typically not used as already appended
  1356.      */
  1357.     protected void appendFieldEnd(final StringBuffer buffer, final String fieldName) {
  1358.         appendFieldSeparator(buffer);
  1359.     }

  1360.     /**
  1361.      * Appends to the {@code toString} the field separator.
  1362.      *
  1363.      * @param buffer  the {@link StringBuffer} to populate
  1364.      */
  1365.     protected void appendFieldSeparator(final StringBuffer buffer) {
  1366.         buffer.append(fieldSeparator);
  1367.     }

  1368.     /**
  1369.      * Appends to the {@code toString} the field start.
  1370.      *
  1371.      * @param buffer  the {@link StringBuffer} to populate
  1372.      * @param fieldName  the field name
  1373.      */
  1374.     protected void appendFieldStart(final StringBuffer buffer, final String fieldName) {
  1375.         if (useFieldNames && fieldName != null) {
  1376.             buffer.append(fieldName);
  1377.             buffer.append(fieldNameValueSeparator);
  1378.         }
  1379.     }

  1380.     /**
  1381.      * Appends the {@link System#identityHashCode(java.lang.Object)}.
  1382.      *
  1383.      * @param buffer  the {@link StringBuffer} to populate
  1384.      * @param object  the {@link Object} whose id to output
  1385.      */
  1386.     protected void appendIdentityHashCode(final StringBuffer buffer, final Object object) {
  1387.         if (isUseIdentityHashCode() && object != null) {
  1388.             register(object);
  1389.             buffer.append('@');
  1390.             buffer.append(ObjectUtils.identityHashCodeHex(object));
  1391.         }
  1392.     }

  1393.     /**
  1394.      * Appends to the {@code toString} an {@link Object},
  1395.      * correctly interpreting its type.
  1396.      *
  1397.      * <p>This method performs the main lookup by Class type to correctly
  1398.      * route arrays, {@link Collection}s, {@link Map}s and
  1399.      * {@link Objects} to the appropriate method.</p>
  1400.      *
  1401.      * <p>Either detail or summary views can be specified.</p>
  1402.      *
  1403.      * <p>If a cycle is detected, an object will be appended with the
  1404.      * {@code Object.toString()} format.</p>
  1405.      *
  1406.      * @param buffer  the {@link StringBuffer} to populate
  1407.      * @param fieldName  the field name, typically not used as already appended
  1408.      * @param value  the value to add to the {@code toString},
  1409.      *  not {@code null}
  1410.      * @param detail  output detail or not
  1411.      */
  1412.     protected void appendInternal(final StringBuffer buffer, final String fieldName, final Object value, final boolean detail) {
  1413.         if (isRegistered(value)
  1414.             && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
  1415.            appendCyclicObject(buffer, fieldName, value);
  1416.            return;
  1417.         }

  1418.         register(value);

  1419.         try {
  1420.             if (value instanceof Collection<?>) {
  1421.                 if (detail) {
  1422.                     appendDetail(buffer, fieldName, (Collection<?>) value);
  1423.                 } else {
  1424.                     appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
  1425.                 }

  1426.             } else if (value instanceof Map<?, ?>) {
  1427.                 if (detail) {
  1428.                     appendDetail(buffer, fieldName, (Map<?, ?>) value);
  1429.                 } else {
  1430.                     appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
  1431.                 }

  1432.             } else if (value instanceof long[]) {
  1433.                 if (detail) {
  1434.                     appendDetail(buffer, fieldName, (long[]) value);
  1435.                 } else {
  1436.                     appendSummary(buffer, fieldName, (long[]) value);
  1437.                 }

  1438.             } else if (value instanceof int[]) {
  1439.                 if (detail) {
  1440.                     appendDetail(buffer, fieldName, (int[]) value);
  1441.                 } else {
  1442.                     appendSummary(buffer, fieldName, (int[]) value);
  1443.                 }

  1444.             } else if (value instanceof short[]) {
  1445.                 if (detail) {
  1446.                     appendDetail(buffer, fieldName, (short[]) value);
  1447.                 } else {
  1448.                     appendSummary(buffer, fieldName, (short[]) value);
  1449.                 }

  1450.             } else if (value instanceof byte[]) {
  1451.                 if (detail) {
  1452.                     appendDetail(buffer, fieldName, (byte[]) value);
  1453.                 } else {
  1454.                     appendSummary(buffer, fieldName, (byte[]) value);
  1455.                 }

  1456.             } else if (value instanceof char[]) {
  1457.                 if (detail) {
  1458.                     appendDetail(buffer, fieldName, (char[]) value);
  1459.                 } else {
  1460.                     appendSummary(buffer, fieldName, (char[]) value);
  1461.                 }

  1462.             } else if (value instanceof double[]) {
  1463.                 if (detail) {
  1464.                     appendDetail(buffer, fieldName, (double[]) value);
  1465.                 } else {
  1466.                     appendSummary(buffer, fieldName, (double[]) value);
  1467.                 }

  1468.             } else if (value instanceof float[]) {
  1469.                 if (detail) {
  1470.                     appendDetail(buffer, fieldName, (float[]) value);
  1471.                 } else {
  1472.                     appendSummary(buffer, fieldName, (float[]) value);
  1473.                 }

  1474.             } else if (value instanceof boolean[]) {
  1475.                 if (detail) {
  1476.                     appendDetail(buffer, fieldName, (boolean[]) value);
  1477.                 } else {
  1478.                     appendSummary(buffer, fieldName, (boolean[]) value);
  1479.                 }

  1480.             } else if (ObjectUtils.isArray(value)) {
  1481.                 if (detail) {
  1482.                     appendDetail(buffer, fieldName, (Object[]) value);
  1483.                 } else {
  1484.                     appendSummary(buffer, fieldName, (Object[]) value);
  1485.                 }

  1486.             } else if (detail) {
  1487.                 appendDetail(buffer, fieldName, value);
  1488.             } else {
  1489.                 appendSummary(buffer, fieldName, value);
  1490.             }
  1491.         } finally {
  1492.             unregister(value);
  1493.         }
  1494.     }

  1495.     /**
  1496.      * Appends to the {@code toString} an indicator for {@code null}.
  1497.      *
  1498.      * <p>The default indicator is {@code "<null>"}.</p>
  1499.      *
  1500.      * @param buffer  the {@link StringBuffer} to populate
  1501.      * @param fieldName  the field name, typically not used as already appended
  1502.      */
  1503.     protected void appendNullText(final StringBuffer buffer, final String fieldName) {
  1504.         buffer.append(nullText);
  1505.     }

  1506.     /**
  1507.      * Appends to the {@code toString} the start of data indicator.
  1508.      *
  1509.      * @param buffer  the {@link StringBuffer} to populate
  1510.      * @param object  the {@link Object} to build a {@code toString} for
  1511.      */
  1512.     public void appendStart(final StringBuffer buffer, final Object object) {
  1513.         if (object != null) {
  1514.             appendClassName(buffer, object);
  1515.             appendIdentityHashCode(buffer, object);
  1516.             appendContentStart(buffer);
  1517.             if (fieldSeparatorAtStart) {
  1518.                 appendFieldSeparator(buffer);
  1519.             }
  1520.         }
  1521.     }

  1522.     /**
  1523.      * Appends to the {@code toString} a summary of a
  1524.      * {@code boolean} 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 appendSummary(final StringBuffer buffer, final String fieldName, final boolean[] array) {
  1532.         appendSummarySize(buffer, fieldName, array.length);
  1533.     }

  1534.     /**
  1535.      * Appends to the {@code toString} a summary of a
  1536.      * {@code byte} array.
  1537.      *
  1538.      * @param buffer  the {@link StringBuffer} to populate
  1539.      * @param fieldName  the field name, typically not used as already appended
  1540.      * @param array  the array to add to the {@code toString},
  1541.      *  not {@code null}
  1542.      */
  1543.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final byte[] array) {
  1544.         appendSummarySize(buffer, fieldName, array.length);
  1545.     }

  1546.     /**
  1547.      * Appends to the {@code toString} a summary of a
  1548.      * {@code char} array.
  1549.      *
  1550.      * @param buffer  the {@link StringBuffer} to populate
  1551.      * @param fieldName  the field name, typically not used as already appended
  1552.      * @param array  the array to add to the {@code toString},
  1553.      *  not {@code null}
  1554.      */
  1555.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final char[] array) {
  1556.         appendSummarySize(buffer, fieldName, array.length);
  1557.     }

  1558.     /**
  1559.      * Appends to the {@code toString} a summary of a
  1560.      * {@code double} array.
  1561.      *
  1562.      * @param buffer  the {@link StringBuffer} to populate
  1563.      * @param fieldName  the field name, typically not used as already appended
  1564.      * @param array  the array to add to the {@code toString},
  1565.      *  not {@code null}
  1566.      */
  1567.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final double[] array) {
  1568.         appendSummarySize(buffer, fieldName, array.length);
  1569.     }

  1570.     /**
  1571.      * Appends to the {@code toString} a summary of a
  1572.      * {@code float} array.
  1573.      *
  1574.      * @param buffer  the {@link StringBuffer} to populate
  1575.      * @param fieldName  the field name, typically not used as already appended
  1576.      * @param array  the array to add to the {@code toString},
  1577.      *  not {@code null}
  1578.      */
  1579.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final float[] array) {
  1580.         appendSummarySize(buffer, fieldName, array.length);
  1581.     }

  1582.     /**
  1583.      * Appends to the {@code toString} a summary of an
  1584.      * {@code int} array.
  1585.      *
  1586.      * @param buffer  the {@link StringBuffer} to populate
  1587.      * @param fieldName  the field name, typically not used as already appended
  1588.      * @param array  the array to add to the {@code toString},
  1589.      *  not {@code null}
  1590.      */
  1591.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final int[] array) {
  1592.         appendSummarySize(buffer, fieldName, array.length);
  1593.     }

  1594.     /**
  1595.      * Appends to the {@code toString} a summary of a
  1596.      * {@code long} array.
  1597.      *
  1598.      * @param buffer  the {@link StringBuffer} to populate
  1599.      * @param fieldName  the field name, typically not used as already appended
  1600.      * @param array  the array to add to the {@code toString},
  1601.      *  not {@code null}
  1602.      */
  1603.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final long[] array) {
  1604.         appendSummarySize(buffer, fieldName, array.length);
  1605.     }

  1606.     /**
  1607.      * Appends to the {@code toString} an {@link Object}
  1608.      * value, printing a summary of the {@link Object}.
  1609.      *
  1610.      * @param buffer  the {@link StringBuffer} to populate
  1611.      * @param fieldName  the field name, typically not used as already appended
  1612.      * @param value  the value to add to the {@code toString},
  1613.      *  not {@code null}
  1614.      */
  1615.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object value) {
  1616.         buffer.append(summaryObjectStartText);
  1617.         buffer.append(getShortClassName(value.getClass()));
  1618.         buffer.append(summaryObjectEndText);
  1619.     }

  1620.     /**
  1621.      * Appends to the {@code toString} a summary of an
  1622.      * {@link Object} array.
  1623.      *
  1624.      * @param buffer  the {@link StringBuffer} to populate
  1625.      * @param fieldName  the field name, typically not used as already appended
  1626.      * @param array  the array to add to the {@code toString},
  1627.      *  not {@code null}
  1628.      */
  1629.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final Object[] array) {
  1630.         appendSummarySize(buffer, fieldName, array.length);
  1631.     }

  1632.     /**
  1633.      * Appends to the {@code toString} a summary of a
  1634.      * {@code short} array.
  1635.      *
  1636.      * @param buffer  the {@link StringBuffer} to populate
  1637.      * @param fieldName  the field name, typically not used as already appended
  1638.      * @param array  the array to add to the {@code toString},
  1639.      *  not {@code null}
  1640.      */
  1641.     protected void appendSummary(final StringBuffer buffer, final String fieldName, final short[] array) {
  1642.         appendSummarySize(buffer, fieldName, array.length);
  1643.     }

  1644.     /**
  1645.      * Appends to the {@code toString} a size summary.
  1646.      *
  1647.      * <p>The size summary is used to summarize the contents of
  1648.      * {@link Collection}s, {@link Map}s and arrays.</p>
  1649.      *
  1650.      * <p>The output consists of a prefix, the passed in size
  1651.      * and a suffix.</p>
  1652.      *
  1653.      * <p>The default format is {@code "<size=n>"}.</p>
  1654.      *
  1655.      * @param buffer  the {@link StringBuffer} to populate
  1656.      * @param fieldName  the field name, typically not used as already appended
  1657.      * @param size  the size to append
  1658.      */
  1659.     protected void appendSummarySize(final StringBuffer buffer, final String fieldName, final int size) {
  1660.         buffer.append(sizeStartText);
  1661.         buffer.append(size);
  1662.         buffer.append(sizeEndText);
  1663.     }

  1664.     /**
  1665.      * Appends to the {@code toString} the superclass toString.
  1666.      * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle.</p>
  1667.      *
  1668.      * <p>A {@code null} {@code superToString} is ignored.</p>
  1669.      *
  1670.      * @param buffer  the {@link StringBuffer} to populate
  1671.      * @param superToString  the {@code super.toString()}
  1672.      * @since 2.0
  1673.      */
  1674.     public void appendSuper(final StringBuffer buffer, final String superToString) {
  1675.         appendToString(buffer, superToString);
  1676.     }

  1677.     /**
  1678.      * Appends to the {@code toString} another toString.
  1679.      * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle.</p>
  1680.      *
  1681.      * <p>A {@code null} {@code toString} is ignored.</p>
  1682.      *
  1683.      * @param buffer  the {@link StringBuffer} to populate
  1684.      * @param toString  the additional {@code toString}
  1685.      * @since 2.0
  1686.      */
  1687.     public void appendToString(final StringBuffer buffer, final String toString) {
  1688.         if (toString != null) {
  1689.             final int pos1 = toString.indexOf(contentStart) + contentStart.length();
  1690.             final int pos2 = toString.lastIndexOf(contentEnd);
  1691.             if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
  1692.                 if (fieldSeparatorAtStart) {
  1693.                     removeLastFieldSeparator(buffer);
  1694.                 }
  1695.                 buffer.append(toString, pos1, pos2);
  1696.                 appendFieldSeparator(buffer);
  1697.             }
  1698.         }
  1699.     }

  1700.     /**
  1701.      * Gets the array end text.
  1702.      *
  1703.      * @return the current array end text
  1704.      */
  1705.     protected String getArrayEnd() {
  1706.         return arrayEnd;
  1707.     }

  1708.     /**
  1709.      * Gets the array separator text.
  1710.      *
  1711.      * @return the current array separator text
  1712.      */
  1713.     protected String getArraySeparator() {
  1714.         return arraySeparator;
  1715.     }

  1716.     /**
  1717.      * Gets the array start text.
  1718.      *
  1719.      * @return the current array start text
  1720.      */
  1721.     protected String getArrayStart() {
  1722.         return arrayStart;
  1723.     }

  1724.     /**
  1725.      * Gets the content end text.
  1726.      *
  1727.      * @return the current content end text
  1728.      */
  1729.     protected String getContentEnd() {
  1730.         return contentEnd;
  1731.     }

  1732.     /**
  1733.      * Gets the content start text.
  1734.      *
  1735.      * @return the current content start text
  1736.      */
  1737.     protected String getContentStart() {
  1738.         return contentStart;
  1739.     }

  1740.     /**
  1741.      * Gets the field name value separator text.
  1742.      *
  1743.      * @return the current field name value separator text
  1744.      */
  1745.     protected String getFieldNameValueSeparator() {
  1746.         return fieldNameValueSeparator;
  1747.     }

  1748.     /**
  1749.      * Gets the field separator text.
  1750.      *
  1751.      * @return the current field separator text
  1752.      */
  1753.     protected String getFieldSeparator() {
  1754.         return fieldSeparator;
  1755.     }

  1756.     /**
  1757.      * Gets the text to output when {@code null} found.
  1758.      *
  1759.      * @return the current text to output when null found
  1760.      */
  1761.     protected String getNullText() {
  1762.         return nullText;
  1763.     }

  1764.     /**
  1765.      * Gets the short class name for a class.
  1766.      *
  1767.      * <p>The short class name is the class name excluding
  1768.      * the package name.</p>
  1769.      *
  1770.      * @param cls  the {@link Class} to get the short name of
  1771.      * @return the short name
  1772.      */
  1773.     protected String getShortClassName(final Class<?> cls) {
  1774.         return ClassUtils.getShortClassName(cls);
  1775.     }

  1776.     /**
  1777.      * Gets the end text to output when a {@link Collection},
  1778.      * {@link Map} or array size is output.
  1779.      *
  1780.      * <p>This is output after the size value.</p>
  1781.      *
  1782.      * @return the current end of size text
  1783.      */
  1784.     protected String getSizeEndText() {
  1785.         return sizeEndText;
  1786.     }

  1787.     /**
  1788.      * Gets the start text to output when a {@link Collection},
  1789.      * {@link Map} or array size is output.
  1790.      *
  1791.      * <p>This is output before the size value.</p>
  1792.      *
  1793.      * @return the current start of size text
  1794.      */
  1795.     protected String getSizeStartText() {
  1796.         return sizeStartText;
  1797.     }

  1798.     /**
  1799.      * Gets the end text to output when an {@link Object} is
  1800.      * output in summary mode.
  1801.      *
  1802.      * <p>This is output after the size value.</p>
  1803.      *
  1804.      * @return the current end of summary text
  1805.      */
  1806.     protected String getSummaryObjectEndText() {
  1807.         return summaryObjectEndText;
  1808.     }

  1809.     /**
  1810.      * Gets the start text to output when an {@link Object} is
  1811.      * output in summary mode.
  1812.      *
  1813.      * <p>This is output before the size value.</p>
  1814.      *
  1815.      * @return the current start of summary text
  1816.      */
  1817.     protected String getSummaryObjectStartText() {
  1818.         return summaryObjectStartText;
  1819.     }

  1820.     /**
  1821.      * Gets whether to output array content detail.
  1822.      *
  1823.      * @return the current array content detail setting
  1824.      */
  1825.     protected boolean isArrayContentDetail() {
  1826.         return arrayContentDetail;
  1827.     }

  1828.     /**
  1829.      * Gets whether to use full detail when the caller doesn't
  1830.      * specify.
  1831.      *
  1832.      * @return the current defaultFullDetail flag
  1833.      */
  1834.     protected boolean isDefaultFullDetail() {
  1835.         return defaultFullDetail;
  1836.     }

  1837.     /**
  1838.      * Gets whether the field separator should be added at the end
  1839.      * of each buffer.
  1840.      *
  1841.      * @return fieldSeparatorAtEnd flag
  1842.      * @since 2.0
  1843.      */
  1844.     protected boolean isFieldSeparatorAtEnd() {
  1845.         return fieldSeparatorAtEnd;
  1846.     }

  1847.     /**
  1848.      * Gets whether the field separator should be added at the start
  1849.      * of each buffer.
  1850.      *
  1851.      * @return the fieldSeparatorAtStart flag
  1852.      * @since 2.0
  1853.      */
  1854.     protected boolean isFieldSeparatorAtStart() {
  1855.         return fieldSeparatorAtStart;
  1856.     }

  1857.     /**
  1858.      * Is this field to be output in full detail.
  1859.      *
  1860.      * <p>This method converts a detail request into a detail level.
  1861.      * The calling code may request full detail ({@code true}),
  1862.      * but a subclass might ignore that and always return
  1863.      * {@code false}. The calling code may pass in
  1864.      * {@code null} indicating that it doesn't care about
  1865.      * the detail level. In this case the default detail level is
  1866.      * used.</p>
  1867.      *
  1868.      * @param fullDetailRequest  the detail level requested
  1869.      * @return whether full detail is to be shown
  1870.      */
  1871.     protected boolean isFullDetail(final Boolean fullDetailRequest) {
  1872.         if (fullDetailRequest == null) {
  1873.             return defaultFullDetail;
  1874.         }
  1875.         return fullDetailRequest.booleanValue();
  1876.     }

  1877.     // Setters and getters for the customizable parts of the style
  1878.     // These methods are not expected to be overridden, except to make public
  1879.     // (They are not public so that immutable subclasses can be written)
  1880.     /**
  1881.      * Gets whether to use the class name.
  1882.      *
  1883.      * @return the current useClassName flag
  1884.      */
  1885.     protected boolean isUseClassName() {
  1886.         return useClassName;
  1887.     }

  1888.     /**
  1889.      * Gets whether to use the field names passed in.
  1890.      *
  1891.      * @return the current useFieldNames flag
  1892.      */
  1893.     protected boolean isUseFieldNames() {
  1894.         return useFieldNames;
  1895.     }

  1896.     /**
  1897.      * Gets whether to use the identity hash code.
  1898.      *
  1899.      * @return the current useIdentityHashCode flag
  1900.      */
  1901.     protected boolean isUseIdentityHashCode() {
  1902.         return useIdentityHashCode;
  1903.     }

  1904.     /**
  1905.      * Gets whether to output short or long class names.
  1906.      *
  1907.      * @return the current useShortClassName flag
  1908.      * @since 2.0
  1909.      */
  1910.     protected boolean isUseShortClassName() {
  1911.         return useShortClassName;
  1912.     }

  1913.     /**
  1914.      * Appends to the {@code toString} the detail of an array type.
  1915.      *
  1916.      * @param buffer  the {@link StringBuffer} to populate
  1917.      * @param fieldName  the field name, typically not used as already appended
  1918.      * @param array  the array to add to the {@code toString},
  1919.      *  not {@code null}
  1920.      * @since 2.0
  1921.      */
  1922.     protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
  1923.         buffer.append(arrayStart);
  1924.         final int length = Array.getLength(array);
  1925.         for (int i = 0; i < length; i++) {
  1926.             appendDetail(buffer, fieldName, i, Array.get(array, i));
  1927.         }
  1928.         buffer.append(arrayEnd);
  1929.     }

  1930.     /**
  1931.      * Remove the last field separator from the buffer.
  1932.      *
  1933.      * @param buffer  the {@link StringBuffer} to populate
  1934.      * @since 2.0
  1935.      */
  1936.     protected void removeLastFieldSeparator(final StringBuffer buffer) {
  1937.         if (Strings.CS.endsWith(buffer, fieldSeparator)) {
  1938.             buffer.setLength(buffer.length() - fieldSeparator.length());
  1939.         }
  1940.     }

  1941.     /**
  1942.      * Sets whether to output array content detail.
  1943.      *
  1944.      * @param arrayContentDetail  the new arrayContentDetail flag
  1945.      */
  1946.     protected void setArrayContentDetail(final boolean arrayContentDetail) {
  1947.         this.arrayContentDetail = arrayContentDetail;
  1948.     }

  1949.     /**
  1950.      * Sets the array end text.
  1951.      *
  1952.      * <p>{@code null} is accepted, but will be converted to
  1953.      * an empty String.</p>
  1954.      *
  1955.      * @param arrayEnd  the new array end text
  1956.      */
  1957.     protected void setArrayEnd(String arrayEnd) {
  1958.         if (arrayEnd == null) {
  1959.             arrayEnd = StringUtils.EMPTY;
  1960.         }
  1961.         this.arrayEnd = arrayEnd;
  1962.     }

  1963.     /**
  1964.      * Sets the array separator text.
  1965.      *
  1966.      * <p>{@code null} is accepted, but will be converted to
  1967.      * an empty String.</p>
  1968.      *
  1969.      * @param arraySeparator  the new array separator text
  1970.      */
  1971.     protected void setArraySeparator(String arraySeparator) {
  1972.         if (arraySeparator == null) {
  1973.             arraySeparator = StringUtils.EMPTY;
  1974.         }
  1975.         this.arraySeparator = arraySeparator;
  1976.     }

  1977.     /**
  1978.      * Sets the array start text.
  1979.      *
  1980.      * <p>{@code null} is accepted, but will be converted to
  1981.      * an empty String.</p>
  1982.      *
  1983.      * @param arrayStart  the new array start text
  1984.      */
  1985.     protected void setArrayStart(String arrayStart) {
  1986.         if (arrayStart == null) {
  1987.             arrayStart = StringUtils.EMPTY;
  1988.         }
  1989.         this.arrayStart = arrayStart;
  1990.     }

  1991.     /**
  1992.      * Sets the content end text.
  1993.      *
  1994.      * <p>{@code null} is accepted, but will be converted to
  1995.      * an empty String.</p>
  1996.      *
  1997.      * @param contentEnd  the new content end text
  1998.      */
  1999.     protected void setContentEnd(String contentEnd) {
  2000.         if (contentEnd == null) {
  2001.             contentEnd = StringUtils.EMPTY;
  2002.         }
  2003.         this.contentEnd = contentEnd;
  2004.     }

  2005.     /**
  2006.      * Sets the content start text.
  2007.      *
  2008.      * <p>{@code null} is accepted, but will be converted to
  2009.      * an empty String.</p>
  2010.      *
  2011.      * @param contentStart  the new content start text
  2012.      */
  2013.     protected void setContentStart(String contentStart) {
  2014.         if (contentStart == null) {
  2015.             contentStart = StringUtils.EMPTY;
  2016.         }
  2017.         this.contentStart = contentStart;
  2018.     }

  2019.     /**
  2020.      * Sets whether to use full detail when the caller doesn't
  2021.      * specify.
  2022.      *
  2023.      * @param defaultFullDetail  the new defaultFullDetail flag
  2024.      */
  2025.     protected void setDefaultFullDetail(final boolean defaultFullDetail) {
  2026.         this.defaultFullDetail = defaultFullDetail;
  2027.     }

  2028.     /**
  2029.      * Sets the field name value separator text.
  2030.      *
  2031.      * <p>{@code null} is accepted, but will be converted to
  2032.      * an empty String.</p>
  2033.      *
  2034.      * @param fieldNameValueSeparator  the new field name value separator text
  2035.      */
  2036.     protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
  2037.         if (fieldNameValueSeparator == null) {
  2038.             fieldNameValueSeparator = StringUtils.EMPTY;
  2039.         }
  2040.         this.fieldNameValueSeparator = fieldNameValueSeparator;
  2041.     }

  2042.     /**
  2043.      * Sets the field separator text.
  2044.      *
  2045.      * <p>{@code null} is accepted, but will be converted to
  2046.      * an empty String.</p>
  2047.      *
  2048.      * @param fieldSeparator  the new field separator text
  2049.      */
  2050.     protected void setFieldSeparator(String fieldSeparator) {
  2051.         if (fieldSeparator == null) {
  2052.             fieldSeparator = StringUtils.EMPTY;
  2053.         }
  2054.         this.fieldSeparator = fieldSeparator;
  2055.     }

  2056.     /**
  2057.      * Sets whether the field separator should be added at the end
  2058.      * of each buffer.
  2059.      *
  2060.      * @param fieldSeparatorAtEnd  the fieldSeparatorAtEnd flag
  2061.      * @since 2.0
  2062.      */
  2063.     protected void setFieldSeparatorAtEnd(final boolean fieldSeparatorAtEnd) {
  2064.         this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
  2065.     }

  2066.     /**
  2067.      * Sets whether the field separator should be added at the start
  2068.      * of each buffer.
  2069.      *
  2070.      * @param fieldSeparatorAtStart  the fieldSeparatorAtStart flag
  2071.      * @since 2.0
  2072.      */
  2073.     protected void setFieldSeparatorAtStart(final boolean fieldSeparatorAtStart) {
  2074.         this.fieldSeparatorAtStart = fieldSeparatorAtStart;
  2075.     }

  2076.     /**
  2077.      * Sets the text to output when {@code null} found.
  2078.      *
  2079.      * <p>{@code null} is accepted, but will be converted to
  2080.      * an empty String.</p>
  2081.      *
  2082.      * @param nullText  the new text to output when null found
  2083.      */
  2084.     protected void setNullText(String nullText) {
  2085.         if (nullText == null) {
  2086.             nullText = StringUtils.EMPTY;
  2087.         }
  2088.         this.nullText = nullText;
  2089.     }

  2090.     /**
  2091.      * Sets the end text to output when a {@link Collection},
  2092.      * {@link Map} or array size is output.
  2093.      *
  2094.      * <p>This is output after the size value.</p>
  2095.      *
  2096.      * <p>{@code null} is accepted, but will be converted to
  2097.      * an empty String.</p>
  2098.      *
  2099.      * @param sizeEndText  the new end of size text
  2100.      */
  2101.     protected void setSizeEndText(String sizeEndText) {
  2102.         if (sizeEndText == null) {
  2103.             sizeEndText = StringUtils.EMPTY;
  2104.         }
  2105.         this.sizeEndText = sizeEndText;
  2106.     }

  2107.     /**
  2108.      * Sets the start text to output when a {@link Collection},
  2109.      * {@link Map} or array size is output.
  2110.      *
  2111.      * <p>This is output before the size value.</p>
  2112.      *
  2113.      * <p>{@code null} is accepted, but will be converted to
  2114.      * an empty String.</p>
  2115.      *
  2116.      * @param sizeStartText  the new start of size text
  2117.      */
  2118.     protected void setSizeStartText(String sizeStartText) {
  2119.         if (sizeStartText == null) {
  2120.             sizeStartText = StringUtils.EMPTY;
  2121.         }
  2122.         this.sizeStartText = sizeStartText;
  2123.     }

  2124.     /**
  2125.      * Sets the end text to output when an {@link Object} is
  2126.      * output in summary mode.
  2127.      *
  2128.      * <p>This is output after the size value.</p>
  2129.      *
  2130.      * <p>{@code null} is accepted, but will be converted to
  2131.      * an empty String.</p>
  2132.      *
  2133.      * @param summaryObjectEndText  the new end of summary text
  2134.      */
  2135.     protected void setSummaryObjectEndText(String summaryObjectEndText) {
  2136.         if (summaryObjectEndText == null) {
  2137.             summaryObjectEndText = StringUtils.EMPTY;
  2138.         }
  2139.         this.summaryObjectEndText = summaryObjectEndText;
  2140.     }

  2141.     /**
  2142.      * Sets the start text to output when an {@link Object} is
  2143.      * output in summary mode.
  2144.      *
  2145.      * <p>This is output before the size value.</p>
  2146.      *
  2147.      * <p>{@code null} is accepted, but will be converted to
  2148.      * an empty String.</p>
  2149.      *
  2150.      * @param summaryObjectStartText  the new start of summary text
  2151.      */
  2152.     protected void setSummaryObjectStartText(String summaryObjectStartText) {
  2153.         if (summaryObjectStartText == null) {
  2154.             summaryObjectStartText = StringUtils.EMPTY;
  2155.         }
  2156.         this.summaryObjectStartText = summaryObjectStartText;
  2157.     }

  2158.     /**
  2159.      * Sets whether to use the class name.
  2160.      *
  2161.      * @param useClassName  the new useClassName flag
  2162.      */
  2163.     protected void setUseClassName(final boolean useClassName) {
  2164.         this.useClassName = useClassName;
  2165.     }

  2166.     /**
  2167.      * Sets whether to use the field names passed in.
  2168.      *
  2169.      * @param useFieldNames  the new useFieldNames flag
  2170.      */
  2171.     protected void setUseFieldNames(final boolean useFieldNames) {
  2172.         this.useFieldNames = useFieldNames;
  2173.     }

  2174.     /**
  2175.      * Sets whether to use the identity hash code.
  2176.      *
  2177.      * @param useIdentityHashCode  the new useIdentityHashCode flag
  2178.      */
  2179.     protected void setUseIdentityHashCode(final boolean useIdentityHashCode) {
  2180.         this.useIdentityHashCode = useIdentityHashCode;
  2181.     }

  2182.     /**
  2183.      * Sets whether to output short or long class names.
  2184.      *
  2185.      * @param useShortClassName  the new useShortClassName flag
  2186.      * @since 2.0
  2187.      */
  2188.     protected void setUseShortClassName(final boolean useShortClassName) {
  2189.         this.useShortClassName = useShortClassName;
  2190.     }
  2191. }