/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.apache.commons.lang.builder;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;

import org.apache.commons.lang.SystemUtils;
/**
 * <code>ToStringStyle</code> は ToStringBuilder と共に動作し、toString を生成します。 
 * 主な public interface は常に ToStringBuilder を介して処理されます。
 * {@primary <code>ToStringStyle</code> works with ToStringBuilder to create a
 * toString. The main public interface is always via ToStringBuilder.}
 * <p>
 * このクラスはシングルトンで利用されることを意図されています。
 * 毎回新しいスタイルのインスタンスを生成する必要はありません。
 * プログラムは一般的にあらかじめ定義された1つの静的なこのクラスを使用することになります。
 * 代わりに {@link StandardToStringStyle} クラスを個々の設定に使用することができます。
 * このクラスの代わりに {@link StandardToStringStyle} クラスを使用すれば独自の設定を行うことが可能です。
 * 従ってほとんどのスタイルはサブクラス化を行わなくても実現することができます。
 * {@primary These classes are intended to be used as singletons. There is no need 
 * to instantiate a new style each time. A program will generally use one
 * of the predefined constants on this class. Alternatively, the 
 * {@link StandardToStringStyle} class can be used to set the individual
 * settings. Thus most styles can be achieved without subclassing.}
 * <p>
 * 必要であれば、サブクラスは必要なだけメソッドをオーバライドすることが可能でです。
 * 各オブジェクト型 (boolean や long や Object や int[] 等) 
 * は自身を出力するためのそれぞれのメソッドを持っています。
 * その内のほとんどは概要と詳細の2つのバージョンを持っています。
 * 例えば、配列を対象とするメソッドの詳細バージョンは全ての内容を出力し、
 * 概要の方は配列の長さだけ出力します。
 * {@primary If required, a subclass can override as many or as few of the methods as 
 * it requires.Each object type (from boolean to long to Object to int[]) has 
 * its own methods to output it. Most have two versions, detail and summary. For
 * example, the detail version of the array based methods will output the
 * whole array, whereas the summary method will just output the array length.}
 *
 * @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
 * @translator 日置 聡
 * @status firstdraft
 * @update 2003/08/16
 * @version $Id: ToStringStyle.java,v 1.1.1.1 2004/02/13 10:02:05 hioki Exp $
 */
public abstract class ToStringStyle implements Serializable {
    
    /**
     * デフォルトの toString スタイル。
     * {@primary The default toString style.}
     */
    public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
    /**
     * 複数行表示する toString スタイル。
     * {@primary The multi line toString style.}
     */
    public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
    /**
     * フィールド名を表示しない toString スタイル。
     * {@primary The no field names toString style.}
     */
    public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
    /**
     * シンプルな toString スタイル。
     * {@primary The simple toString style.}
     */
    public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();

    /**
     * フィールド名を使うかどうか(初期値 'true')。
     * {@primary Whether to use the field names 'true'}
     */
    private boolean useFieldNames = true;
    /**
     * クラス名を使うかどうか(初期値 'true')。
     * {@primary Whether to use the class name 'true'}
     */
    private boolean useClassName = true;
    /**
     * 短縮されたクラス名を使うかどうか(初期値 'false')。
     * {@primary Whether to use short class names 'false'}
     */
    private boolean useShortClassName = false;
    /**
     * ハッシュコードを使うかどうか(初期値 'true')。
     * {@primary Whether to use the identity hash code 'true'}
     */
    private boolean useIdentityHashCode = true;
    
    /**
     * 内容表示の開始記号(初期値 '[')。
     * {@primary The content start '['}
     */
    private String contentStart = "[";
    /**
     * 内容表示の終了記号(初期値 ']')。
     * {@primary The content end ']'}
     */
    private String contentEnd = "]";
    /**
     * フィールドの名称と値の区切り文字(初期値 '=')。
     * {@primary The field name value separator '='}
     */
    private String fieldNameValueSeparator = "=";
    /**
     * フィールドの区切り文字(初期値 ',')。
     * {@primary The field separator ','}
     */
    private String fieldSeparator = ",";
    /**
     * 配列表示の開始記号(初期値 '{')。
     * {@primary The array start '{'}
     */
    private String arrayStart = "{";
    /**
     * 配列の区切り文字(初期値 ',')。
     * {@primary The array separator ','}
     */
    private String arraySeparator = ",";
    /**
     * 配列の詳細を表示するかどうか(初期値 'true')。
     * {@primary The detail for array content 'true'}
     */
    private boolean arrayContentDetail = true;
    /**
     * 配列表示の終了記号(初期値 '&#125')。
     * {@primary The array end '&#125'}
     */
    private String arrayEnd = "}";
    /**
     * fullDetail が null の場合に使用される値(初期値 'true')。
     * {@primary The value to use when fullDetail is null 'true'}
     */
    private boolean defaultFullDetail = true;
    /**
     * null の文字列表現(初期値 '&lt;null&gt;')。
     * {@primary The null text '&lt;null&gt;'}
     */
    private String nullText = "<null>";
    /**
     * サイズ表示の開始記号(初期値 '&lt;size')。
     * {@primary The summary size text start '&lt;size'}
     */
    private String sizeStartText = "<size=";
    /**
     * サイズ表示の終了記号(初期値 '&gt;')。
     * {@primary The summary size text start '&gt;'}
     */
    private String sizeEndText = ">";
    /**
     * オブジェクト情報の開始記号(初期値 '<')。
     * {@primary The summary object text start '<'}
     */
    private String summaryObjectStartText = "<";
    /**
     * オブジェクト情報の終了記号(初期値 '>')。
     * {@primary The summary object text start '>'}
     */
    private String summaryObjectEndText = ">";
    
    //----------------------------------------------------------------------------
    
    /**
     * コンストラクタ。{@primary Constructor.}
     */
    protected ToStringStyle() {
        super();
    }
    
    //----------------------------------------------------------------------------
    
    /**
     * データの開始指示を追加します。
     * {@primary Append the start of data indicator.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param object  toString を生成する際に対象となるオブジェクト、null でない必要があります。
     * {@primary the object to build a toString for, must not be null}
     */
    public void appendStart(StringBuffer buffer, Object object) {
        appendClassName(buffer, object);
        appendIdentityHashCode(buffer, object);
        appendContentStart(buffer);
    }

    /**
     * データの終了指示を追加します。
     * {@primary Append the end of data indicator.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param object  toString を生成する際に対象となるオブジェクト、null でない必要があります。
     * {@primary the object to build a toString for, must not be null}
     */
    public void appendEnd(StringBuffer buffer, Object object) {
        appendContentEnd(buffer);
    }
    
    //----------------------------------------------------------------------------
    
    /**
     * toString にオブジェクトから生成された内容を追加します。
     * {@primary Append to the toString an Object value, printing the full 
     * toString of the object passed in.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (value == null) {
            appendNullText(buffer, fieldName);
            
        } else {
            appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
        }
        
        appendFieldEnd(buffer, fieldName);
    }
    
    /**
     * toString にオブジェクトの情報をその型を判断して追加します。
     * {@primary Append to the toString an Object, correctly interpretting its type.}
     * <p>
     * このメソッドはクラス型が配列、コレクション、マップ、オブジェクトの順に該当するメソッドを
     * 指定された詳細または概要のレベルで判断し、使用します。
     * {@primary This method performs the main lookup by Class type to correctly route
     * arrays, collections, maps and objects to the appropriate method. Either
     * detail or summary views can be specified.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する( null でない)値
     * {@primary the value to add to the toString, not null}
     * @param detail  詳細を表示するかどうか
     * {@primary output detail or not}
     */
    protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) {
        if (value instanceof Collection) {
            if (detail) {
                appendDetail(buffer, fieldName, (Collection) value);
            } else {
                appendSummarySize(buffer, fieldName, ((Collection) value).size());
            }
            
        } else if (value instanceof Map) {
            if (detail) {
                appendDetail(buffer, fieldName, (Map) value);
            } else {
                appendSummarySize(buffer, fieldName, ((Map) value).size());
            }
            
        } else if (value instanceof long[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (long[]) value);
            } else {
                appendSummary(buffer, fieldName, (long[]) value);
            }
            
        } else if (value instanceof int[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (int[]) value);
            } else {
                appendSummary(buffer, fieldName, (int[]) value);
            }
            
        } else if (value instanceof short[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (short[]) value);
            } else {
                appendSummary(buffer, fieldName, (short[]) value);
            }
            
        } else if (value instanceof byte[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (byte[]) value);
            } else {
                appendSummary(buffer, fieldName, (byte[]) value);
            }
            
        } else if (value instanceof char[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (char[]) value);
            } else {
                appendSummary(buffer, fieldName, (char[]) value);
            }
            
        } else if (value instanceof double[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (double[]) value);
            } else {
                appendSummary(buffer, fieldName, (double[]) value);
            }
            
        } else if (value instanceof float[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (float[]) value);
            } else {
                appendSummary(buffer, fieldName, (float[]) value);
            }
            
        } else if (value instanceof boolean[]) {
            if (detail) {
                appendDetail(buffer, fieldName, (boolean[]) value);
            } else {
                appendSummary(buffer, fieldName, (boolean[]) value);
            }
        
        } else if (value.getClass().isArray()) {
            if (detail) {
                appendDetail(buffer, fieldName, (Object[]) value);
            } else {
                appendSummary(buffer, fieldName, (Object[]) value);
            }
            
        } else {
            if (detail) {
                appendDetail(buffer, fieldName, (Object) value);
            } else {
                appendSummary(buffer, fieldName, (Object) value);
            }
        }
    }
    
    /**
     * toString にオブジェクトの全ての詳細情報を追加します。
     * {@primary Append to the toString an Object value, printing the full detail of the object.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する( null でない)値
     * {@primary the value to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
        buffer.append(value);
    }
    
    /**
     * toString に Collection を追加します。
     * {@primary Append to the toString a Collection.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param coll  toString に追加する( null でない)コレクション
     * {@primary the collection to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, Collection coll) {
        buffer.append(coll);
    }
    
    /**
     * toString に Map を追加します。
     * {@primary Append to the toString a Map.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param map  toString に追加する( null でない)マップ
     * {@primary the maps to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, Map map) {
        buffer.append(map);
    }
    
    /**
     * toString にオブジェクトの概要を追加します。
     * {@primary Append to the toString an Object value, printing a summary of the object.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する( null でない)値
     * {@primary the value to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, Object value) {
        buffer.append(summaryObjectStartText);
        buffer.append(getShortClassName(value.getClass()));
        buffer.append(summaryObjectEndText);
    }
    
    //----------------------------------------------------------------------------
    
    /**
     * toString に long の値を追加します。
     * {@primary Append to the toString a long value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, long value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に long の値を追加します。
     * {@primary Append to the toString a long value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, long value) {
        buffer.append(value);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に int の値を追加します。
     * {@primary Append to the toString an int value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, int value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に int の値を追加します。
     * {@primary Append to the toString an int value.}
     *
     * @param buffer  the StringBuffer to populate
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, int value) {
        buffer.append(value);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に short の値を追加します。
     * {@primary Append to the toString a short value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, short value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に short の値を追加します。
     * {@primary Append to the toString a short value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, short value) {
        buffer.append(value);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に byte の値を追加します。
     * {@primary Append to the toString a byte value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, byte value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に byte の値を追加します。
     * {@primary Append to the toString a byte value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, byte value) {
        buffer.append(value);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に char の値を追加します。
     * {@primary Append to the toString a char value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, char value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に char の値を追加します。
     * {@primary Append to the toString a char value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
        buffer.append(value);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に double の値を追加します。
     * {@primary Append to the toString a double value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, double value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に double の値を追加します。
     * {@primary Append to the toString a double value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, double value) {
        buffer.append(value);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に float の値を追加します。
     * {@primary Append to the toString a float value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, float value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に float の値を追加します。
     * {@primary Append to the toString a float value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, float value) {
        buffer.append(value);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に boolean の値を追加します。
     * {@primary Append to the toString a boolean value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    public void append(StringBuffer buffer, String fieldName, boolean value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に boolean の値を追加します。
     * {@primary Append to the toString a boolean value.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param value  toString に追加する値
     * {@primary the value to add to the toString}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, boolean value) {
        buffer.append(value);
    }

    /**
     * toString にオブジェクトの配列を追加します。
     * {@primary Append to the toString an Object array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, Object[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString にオブジェクトの配列の詳細を追加します。
     * {@primary Append to the toString the detail of an Object array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            Object item = array[i];
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            if (item == null) {
                appendNullText(buffer, fieldName);
                
            } else {
                appendInternal(buffer, fieldName, item, arrayContentDetail);
            }
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString にオブジェクトの配列の概要を追加します。
     * {@primary Append to the toString a summary of an Object array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, Object[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に long の配列を追加します。
     * {@primary Append to the toString a long array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, long[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に long の配列の詳細を追加します。
     * {@primary Append to the toString the detail of a long array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, long[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に long の配列の概要を追加します。
     * {@primary Append to the toString a summary of a long array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, long[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に int の配列を追加します。
     * {@primary Append to the toString an int array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, int[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に int の配列の詳細を追加します。
     * {@primary Append to the toString the detail of an int array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, int[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に int の配列の概要を追加します。
     * {@primary Append to the toString a summary of an int array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, int[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に short の配列を追加します。
     * {@primary Append to the toString a short array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, short[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に short の配列の詳細を追加します。
     * {@primary Append to the toString the detail of a short array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, short[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に short の配列の概要を追加します。
     * {@primary Append to the toString a summary of a short array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, short[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に byte の配列を追加します。
     * {@primary Append to the toString a byte array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, byte[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に byte の配列の詳細を追加します。
     * {@primary Append to the toString the detail of a byte array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, byte[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に byte の配列の概要を追加します。
     * {@primary Append to the toString a summary of a byte array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, byte[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に char の配列を追加します。
     * {@primary Append to the toString a char array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, char[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に char の配列の詳細を追加します。
     * {@primary Append to the toString the detail of a char array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, char[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に char の配列の概要を追加します。
     * {@primary Append to the toString a summary of a char array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, char[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に double の配列を追加します。
     * {@primary Append to the toString a double array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, double[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に double の配列の詳細を追加します。
     * {@primary Append to the toString the detail of a double array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, double[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に double の配列の概要を追加します。
     * {@primary Append to the toString a summary of a double array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, double[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に float の配列を追加します。
     * {@primary Append to the toString a float array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, float[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に float の配列の詳細を追加します。
     * {@primary Append to the toString the detail of a float array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, float[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に float の配列の概要を追加します。
     * {@primary Append to the toString a summary of a float array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, float[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * toString に boolean の配列を追加します。
     * {@primary Append to the toString a boolean array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     * @param array  the array to add to the toString
     * @param fullDetail  true なら詳細情報、 false なら概要、 null ならスタイルの設定に依存
     * {@primary true for detail, false for summary info, null for style decides}
     */
    public void append(StringBuffer buffer, String fieldName, boolean[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);
        
        if (array == null) {
            appendNullText(buffer, fieldName);
            
        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);
            
        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }

    /**
     * toString に boolean の配列の詳細を追加します。
     * {@primary Append to the toString the detail of a boolean array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendDetail(StringBuffer buffer, String fieldName, boolean[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            appendDetail(buffer, fieldName, array[i]);
        }
        buffer.append(arrayEnd);
    }

    /**
     * toString に boolean の配列の概要を追加します。
     * {@primary Append to the toString a summary of a boolean array.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param array  toString に追加する( null でない)配列
     * {@primary the array to add to the toString, not null}
     */
    protected void appendSummary(StringBuffer buffer, String fieldName, boolean[] array) {
        appendSummarySize(buffer, fieldName, array.length);
    }

    //----------------------------------------------------------------------------
    
    /**
     * クラス名を追加します。
     * {@primary Append the class name.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param object  名前を出力するオブジェクト
     * {@primary the object whose name to output}
     */
    protected void appendClassName(StringBuffer buffer, Object object) {
        if (useClassName) {
            if (useShortClassName) {
                buffer.append(getShortClassName(object.getClass()));
            } else {
                buffer.append(object.getClass().getName());
            }
        }
    }

    /**
     * ハッシュコードを追加します。
     * {@primary Append the IdentityHashCode.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param object  IDを出力するオブジェクト
     * {@primary the object whose id to output}
     */
    protected void appendIdentityHashCode(StringBuffer buffer, Object object) {
        if (useIdentityHashCode) {
            buffer.append('@');
            buffer.append(Integer.toHexString(System.identityHashCode(object)));
        }
    }

    /**
     * バッファに内容の開始記号を追加します。
     * {@primary Append the content start to the buffer.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     */
    protected void appendContentStart(StringBuffer buffer) {
        buffer.append(contentStart);
    }
    
    /**
     * バッファに内容の終了記号を追加します。
     * {@primary Append the content end to the buffer.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     */
    protected void appendContentEnd(StringBuffer buffer) {
        int len = buffer.length();
        int sepLen = fieldSeparator.length();
        if (len > 0 && sepLen > 0 && len >= sepLen && buffer.charAt(len - 1) == fieldSeparator.charAt(sepLen - 1)) {
            buffer.setLength(len - sepLen);
        }
        buffer.append(contentEnd);
    }
    
    /**
     * バッファに null を示す記号を追加します。
     * デフォルトの出力は '&lt;null&gt;' です。
     * {@primary Append an indicator for null to the buffer.
     * Default output is '&lt;null&gt;'.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     */
    protected void appendNullText(StringBuffer buffer, String fieldName) {
        buffer.append(nullText);
    }
    
    /**
     * バッファにフィールドの区切り文字を追加します。
     * {@primary Append the field separator to the buffer.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     */
    protected void appendFieldSeparator(StringBuffer buffer) {
        buffer.append(fieldSeparator);
    }
    
    /**
     * バッファにフィールドの開始を追加します。
     * {@primary Append the field start to the buffer.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名
     * {@primary the field name}
     */
    protected void appendFieldStart(StringBuffer buffer, String fieldName) {
        if (useFieldNames && fieldName != null) {
            buffer.append(fieldName);
            buffer.append(fieldNameValueSeparator);
        }
    }
    
    /**
     * バッファにフィールドの終了を追加します。
     * {@primary Append the field end to the buffer.}
     * 
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     */
    protected void appendFieldEnd(StringBuffer buffer, String fieldName) {
        appendFieldSeparator(buffer);
    }
    
    /**
     * toString にサイズの概要を追加します。
     * {@primary Append to the toString a size summary.}
     * <p>
     * このサイズの概要はコレクション、マップ、配列を要約する際に使用されます。
     * 出力されるテキストはサイズの開始記号、渡されたサイズ、終了記号です。
     * デフォルトのフォーマットは '&lt;size=n&gt;' です。
     * {@primary The size summary is used to summarize the contents of collections, maps 
     * and arrays. The text output is a prefix, the size (passed in) and a suffix.
     * The default format is '&lt;size=n&gt;'.}
     *
     * @param buffer  出力先となる StringBuffer
     * {@primary the StringBuffer to populate}
     * @param fieldName  フィールド名、既に追加されている場合には使用されません
     * {@primary the field name, typically not used as already appended}
     * @param size  追加するサイズ
     * {@primary the size to append}
     */
    protected void appendSummarySize(StringBuffer buffer, String fieldName, int size) {
        buffer.append(sizeStartText);
        buffer.append(size);
        buffer.append(sizeEndText);
    }

    /**
     * この(クラスの)フィールドの詳細を出力するかどうかをチェックします。
     * {@primary Is this field to be output in full detail.}
     * <p>
     * このメソッドは詳細表示の要求を詳細表示レベルに変換します。
     * 詳細情報の表示(true)を設定しても、サブクラスはこれを無視して必ず false を返す可能性があります。
     * null が渡された場合には詳細表示レベルを指定しないことを意味します。
     * この場合にはデフォルトの詳細表示レベルが適用されます。
     * {@primary This method converts a detail request into a detail level. The calling code
     * may request full detail (true), but a subclass might ignore that and always 
     * return false. The calling code may pass in null indicating that it doesn't
     * care about the detail level. In this case the default detail level is used.}
     * 
     * @param fullDetailRequest  要求された詳細表示レベル
     * {@primary the detail level requested}
     * @return 詳細情報を表示するかどうか
     * {@primary whether full detail is to be shown}
     */
    protected boolean isFullDetail(Boolean fullDetailRequest) {
        if (fullDetailRequest == null) {
            return defaultFullDetail;
        }
        return fullDetailRequest.booleanValue();
    }
    
    /**
     * クラスの短縮名を取得します。
     * {@primary Gets the short class name for a class.}
     * <p>
     * クラスの短縮名はパッケージ名が省かれたものとなります。
     * {@primary The short class name is the name excluding the package name.}
     *
     * @param cls  短縮名を取得するクラス
     * {@primary the class to get the short name of}
     * @return 短縮名
     * {@primary the short name}
     */
    protected String getShortClassName(Class cls) {
        String name = cls.getName();
        int pos = name.lastIndexOf('.');
        if (pos == -1) {
            return name;
        }
        return name.substring(pos + 1);
    }

    // Setters and getters for the customizable parts of the style
    // These methods are not expected to be overridden, except to make public
    // (They are not public so that immutable subclasses can be written)
    //---------------------------------------------------------------------
    
    /**
     * クラス名を使用するかどうかを取得します。
     * {@primary Gets whether to use the class name.}
     * @return 現在の useClassName フラグ
     * {@primary the current useClassName flag}
     */
    protected boolean isUseClassName() {
        return useClassName;
    }

    /**
     * クラス名を使用するかどうかを設定します。
     * {@primary Sets whether to use the class name.}
     * @param useClassName  新たな useClassName フラグ
     * {@primary the new useClassName flag}
     */
    protected void setUseClassName(boolean useClassName) {
        this.useClassName = useClassName;
    }

    //---------------------------------------------------------------------
    
    /**
     * クラスの短縮名を使用するかフルネームを使用するかを取得します。
     * {@primary Gets whether to output short or long class names.}
     * @return 現在の shortClassName フラグ
     * {@primary the current shortClassName flag}
     */
    protected boolean isShortClassName() {
        return useShortClassName;
    }

    /**
     * クラスの短縮名を使用するかフルネームを使用するかを設定します。
     * {@primary Sets whether to output short or long class names.}
     * @param shortClassName  新たな shortClassName フラグ
     * {@primary shortClassName  the new shortClassName flag}
     */
    protected void setShortClassName(boolean shortClassName) {
        this.useShortClassName = shortClassName;
    }

    //---------------------------------------------------------------------
    
    /**
     * ハッシュコードを使用するかどうかを取得します。
     * {@primary Gets whether to use the identity hash code.}
     * @return 現在の useIdentityHashCode フラグ
     * {@primary the current useIdentityHashCode flag}
     */
    protected boolean isUseIdentityHashCode() {
        return useIdentityHashCode;
    }

    /**
     * ハッシュコードを使用するかどうかを設定します。
     * {@primary Sets whether to use the identity hash code.}
     * @param useIdentityHashCode  新たな useIdentityHashCode フラグ
     * {@primary useIdentityHashCode  the new useIdentityHashCode flag}
     */
    protected void setUseIdentityHashCode(boolean useIdentityHashCode) {
        this.useIdentityHashCode = useIdentityHashCode;
    }

    //---------------------------------------------------------------------
    
    /**
     * 入力されたフィールド名を使用するかどうかを取得します。
     * {@primary Gets whether to use the field names passed in.}
     * @return 現在の useFieldNames フラグ
     * {@primary the current useFieldNames flag}
     */
    protected boolean isUseFieldNames() {
        return useFieldNames;
    }

    /**
     * 入力されたフィールド名を使用するかどうかを設定します。
     * {@primary Sets whether to use the field names passed in.}
     * @param useFieldNames  新たな useFieldNames フラグ
     * {@primary the new useFieldNames flag}
     */
    protected void setUseFieldNames(boolean useFieldNames) {
        this.useFieldNames = useFieldNames;
    }

    //---------------------------------------------------------------------
    
    /**
     * 明示されなかった場合に詳細情報を(デフォルトで)出力するかどうかを取得します。
     * {@primary Gets whether to use full detail when the caller doesn't specify.}
     * @return 現在の defaultFullDetail フラグ
     * {@primary the current defaultFullDetail flag}
     */
    protected boolean isDefaultFullDetail() {
        return defaultFullDetail;
    }

    /**
     * 明示されなかった場合に詳細情報を(デフォルトで)出力するかどうかを設定します。
     * {@primary Sets whether to use full detail when the caller doesn't specify.}
     * @param defaultFullDetail  新たな defaultFullDetail フラグ
     * {@primary the new defaultFullDetail flag}
     */
    protected void setDefaultFullDetail(boolean defaultFullDetail) {
        this.defaultFullDetail = defaultFullDetail;
    }

    //---------------------------------------------------------------------
    
    /**
     * 配列の詳細な内容を出力するかどうかを取得します。
     * {@primary Gets whether to output array content detail.}
     * @return 現在の配列の詳細な内容の設定
     * {@primary the current array content detail setting}
     */
    protected boolean isArrayContentDetail() {
        return arrayContentDetail;
    }
    
    /**
     * 配列の詳細な内容を出力するかどうかを設定します。
     * {@primary Sets whether to output array content detail.}
     * @param arrayContentDetail  新たな arrayContentDetail フラグ
     * {@primary the new arrayContentDetail flag}
     */
    protected void setArrayContentDetail(boolean arrayContentDetail) {
        this.arrayContentDetail = arrayContentDetail;
    }

    //---------------------------------------------------------------------
    
    /**
     * 配列の開始を示すテキストを取得します。
     * {@primary Gets the array start text.}
     * @return 現在の配列の開始を示すテキスト
     * {@primary the current array start text}
     */
    protected String getArrayStart() {
        return arrayStart;
    }

    /**
     * 配列の開始を示すテキストを設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the array start text.
     * Null is accepted, but will be converted to a blank string.}
     * @param arrayStart  新たな配列の開始を示すテキスト
     * {@primary the new array start text}
     */
    protected void setArrayStart(String arrayStart) {
        if (arrayStart == null) {
            arrayStart = "";
        }
        this.arrayStart = arrayStart;
    }

    //---------------------------------------------------------------------
    
    /**
     * 配列の終了を示すテキストを取得します。
     * {@primary Gets the array end text.}
     * @return 現在の配列の終了を示すテキスト
     * {@primary the current array end text}
     */
    protected String getArrayEnd() {
        return arrayEnd;
    }

    /**
     * 配列の終了を示すテキストを設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the array end text.}
     * Null is accepted, but will be converted to a blank string.
     * @param arrayEnd  新たな配列の終了を示すテキスト
     * {@primary the new array end text}
     */
    protected void setArrayEnd(String arrayEnd) {
        if (arrayStart == null) {
            arrayStart = "";
        }
        this.arrayEnd = arrayEnd;
    }

    //---------------------------------------------------------------------
    
    /**
     * 配列の区切り文字を取得します。
     * {@primary Gets the array separator text.}
     * @return 現在の配列の区切り文字
     * {@primary the current array separator text}
     */
    protected String getArraySeparator() {
        return arraySeparator;
    }

    /**
     * 配列の区切り文字を設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the array separator text.
     * Null is accepted, but will be converted to a blank string.}
     * @param arraySeparator  新たな配列の区切り文字
     * {@primary the new array separator text}
     */
    protected void setArraySeparator(String arraySeparator) {
        if (arraySeparator == null) {
            arraySeparator = "";
        }
        this.arraySeparator = arraySeparator;
    }

    //---------------------------------------------------------------------
    
    /**
     * 内容の開始を示すテキストを取得します。
     * {@primary Gets the content start text.}
     * @return 現在の内容の開始を示すテキスト
     * {@primary the current content start text}
     */
    protected String getContentStart() {
        return contentStart;
    }

    /**
     * 内容の開始を示すテキストを設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the content start text.
     * Null is accepted, but will be converted to a blank string.}
     * @param contentStart  新たな内容の開始を示すテキスト
     * {@primary the new content start text}
     */
    protected void setContentStart(String contentStart) {
        if (contentStart == null) {
            contentStart = "";
        }
        this.contentStart = contentStart;
    }

    //---------------------------------------------------------------------
    
    /**
     * 内容の終了を示すテキストを取得します。
     * {@primary Gets the content end text.}
     * @return 現在の内容の終了を示すテキスト
     * {@primary the current content end text}
     */
    protected String getContentEnd() {
        return contentEnd;
    }

    /**
     * 内容の終了を示すテキストを設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the content end text.
     * Null is accepted, but will be converted to a blank string.}
     * @param contentEnd  新たな内容の終了を示すテキスト
     * {@primary the new content end text}
     */
    protected void setContentEnd(String contentEnd) {
        if (contentEnd == null) {
            contentEnd = "";
        }
        this.contentEnd = contentEnd;
    }

    //---------------------------------------------------------------------
    
    /**
     * フィールドの名前と値の区切り文字を取得します。
     * {@primary Gets the field name value separator text.}
     * @return 現在のフィールドの名前と値の区切り文字
     * {@primary the current field name value separator text}
     */
    protected String getFieldNameValueSeparator() {
        return fieldNameValueSeparator;
    }

    /**
     * フィールドの名前と値の区切り文字を設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the field name value separator text.
     * Null is accepted, but will be converted to a blank string.}
     * @param fieldNameValueSeparator  新たなフィールドの名前と値の区切り文字
     * {@primary the new field name value separator text}
     */
    protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
        if (fieldNameValueSeparator == null) {
            fieldNameValueSeparator = "";
        }
        this.fieldNameValueSeparator = fieldNameValueSeparator;
    }

    //---------------------------------------------------------------------
    
    /**
     * フィールドの区切り文字を取得します。
     * {@primary Gets the field separator text.}
     * @return 現在のフィールドの区切り文字
     * {@primary the current field separator text}
     */
    protected String getFieldSeparator() {
        return fieldSeparator;
    }

    /**
     * フィールドの区切り文字を設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the field separator text.
     * Null is accepted, but will be converted to a blank string.}
     * @param fieldSeparator  新たなフィールドの区切り文字
     * {@primary the new field separator text}
     */
    protected void setFieldSeparator(String fieldSeparator) {
        if (fieldSeparator == null) {
            fieldSeparator = "";
        }
        this.fieldSeparator = fieldSeparator;
    }

    //---------------------------------------------------------------------
    
    /**
     * null が見つかった場合に出力されるテキストを取得します。
     * {@primary Gets the text to output when null found.}
     * @return 現在の null が見つかった場合のテキスト
     * {@primary the current text to output when null found}
     */
    protected String getNullText() {
        return nullText;
    }

    /**
     * null が見つかった場合に出力されるテキストを設定します。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the text to output when null found.
     * Null is accepted, but will be converted to a blank string.}
     * @param nullText  新たな null が見つかった場合のテキスト
     * {@primary the new text to output when null found}
     */
    protected void setNullText(String nullText) {
        if (nullText == null) {
            nullText = "";
        }
        this.nullText = nullText;
    }

    //---------------------------------------------------------------------
    
    /**
     * Collection、Map または Array のサイズを出力する際のテキストを取得します。
     * これはサイズの値の前に出力されます。
     * {@primary Gets the text to output when a Collection, Map or Array size is output.
     * This is output before the size value.}
     * @return 現在のサイズ開始のテキスト
     * {@primary the current start of size text}
     */
    protected String getSizeStartText() {
        return sizeStartText;
    }

    /**
     * Collection、Map または Array のサイズを出力する際のテキストを設定します。
     * これはサイズの値の前に出力されます。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the text to output when a Collection, Map or Array size is output.
     * This is output before the size value.
     * Null is accepted, but will be converted to a blank string.}
     * @param sizeStartText  新たなサイズ開始のテキスト
     * {@primary the new start of size text}
     */
    protected void setSizeStartText(String sizeStartText) {
        if (sizeStartText == null) {
            sizeStartText = "";
        }
        this.sizeStartText = sizeStartText;
    }

    //---------------------------------------------------------------------
    
    /**
     * Collection、Map または Array のサイズを出力する際のテキストを取得します。
     * これはサイズの値の後に出力されます。
     * {@primary Gets the text to output when a Collection, Map or Array size is output.
     * This is output after the size value.}
     * @return 現在のサイズ終了のテキスト
     * {@primary the current end of size text}
     */
    protected String getSizeEndText() {
        return sizeEndText;
    }

    /**
     * Collection、Map または Array のサイズを出力する際のテキストを設定します。
     * これはサイズの値の後に出力されます。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the text to output when a Collection, Map or Array size is output.
     * This is output after the size value.
     * Null is accepted, but will be converted to a blank string.}
     * @param sizeEndText  新たなサイズ終了のテキスト
     * {@primary the new end of size text}
     */
    protected void setSizeEndText(String sizeEndText) {
        if (sizeEndText == null) {
            sizeEndText = "";
        }
        this.sizeEndText = sizeEndText;
    }

    //---------------------------------------------------------------------
    
    /**
     * オブジェクトの概要を出力する際のテキストを取得します。
     * これはサイズの値の前に出力されます。
     * {@primary Gets the text to output when an Object is output in summary mode.
     * This is output before the size value.}
     * @return 現在の概要開始のテキスト
     * {@primary the current start of summary text}
     */
    protected String getSummaryObjectStartText() {
        return summaryObjectStartText;
    }

    /**
     * オブジェクトの概要を出力する際のテキストを設定します。
     * これはサイズの値の前に出力されます。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the text to output when an Object is output in summary mode.
     * This is output before the size value.
     * Null is accepted, but will be converted to a blank string.}
     * @param summaryObjectStartText  新たな概要開始のテキスト
     * {@primary the new start of summary text}
     */
    protected void setSummaryObjectStartText(String summaryObjectStartText) {
        if (summaryObjectStartText == null) {
            summaryObjectStartText = "";
        }
        this.summaryObjectStartText = summaryObjectStartText;
    }

    //---------------------------------------------------------------------
    
    /**
     * オブジェクトの概要を出力する際のテキストを取得します。
     * これはサイズの値の後に出力されます。
     * {@primary Gets the text to output when an Object is output in summary mode.
     * This is output after the size value.}
     * @return 現在の概要終了のテキスト
     * {@primary the current end of summary text}
     */
    protected String getSummaryObjectEndText() {
        return summaryObjectEndText;
    }

    /**
     * オブジェクトの概要を出力する際のテキストを設定します。
     * これはサイズの値の後に出力されます。
     * nullの入力は受け付けられますが、これは空の文字列に変換されます。
     * {@primary Sets the text to output when an Object is output in summary mode.
     * This is output after the size value.
     * Null is accepted, but will be converted to a blank string.}
     * @param summaryObjectEndText  新たな概要終了のテキスト
     * {@primary the new end of summary text}
     */
    protected void setSummaryObjectEndText(String summaryObjectEndText) {
        if (summaryObjectEndText == null) {
            summaryObjectEndText = "";
        }
        this.summaryObjectEndText = summaryObjectEndText;
    }

    //---------------------------------------------------------------------
    
    //----------------------------------------------------------------------------
    
    /**
     * デフォルトの ToStringStyle です。
     * これは不変性を確実にするために StandardToStringStyle が使用するインナークラスです。
     * {@primary Default ToStringStyle.
     * This is an inner class rather than using StandardToStringStyle to
     * ensure its immutability.}
     */
    private static final class DefaultToStringStyle extends ToStringStyle {
        
        /**
         * コンストラクタ。インスタンス化するよりも静的な定数を使用してください。
         * {@primary Constructor - use the static constant rather than instantiating.}
         */
        private DefaultToStringStyle() {
            super();
        }
        
        /**
         * シリアライズ化の後のシングルトンを確実にします。
         * {@primary Ensure singleton after serialization.}
         * @return シングルトン(のインスタンス)
         * {@primary the singleton}
         */
        private Object readResolve() {
            return ToStringStyle.DEFAULT_STYLE;
        }
        
    }
    
    //----------------------------------------------------------------------------
    
    /**
     * フィールド名を表示しない ToStringStyle です。
     * これは不変性を確実にするために StandardToStringStyle が使用するインナークラスです。
     * {@primary ToStringStyle that does not print out the field names.
     * This is an inner class rather than using StandardToStringStyle to
     * ensure its immutability.}
     */
    private static final class NoFieldNameToStringStyle extends ToStringStyle {
        
        /**
         * コンストラクタ。インスタンス化するよりも静的な定数を使用してください。
         * {@primary Constructor - use the static constant rather than instantiating.}
         */
        private NoFieldNameToStringStyle() {
            super();
            this.setUseFieldNames(false);
        }
        
        /**
         * シリアライズ化の後のシングルトンを確実にします。
         * {@primary Ensure singleton after serialization.}
         * @return シングルトン(のインスタンス)
         * {@primary the singleton}
         */
        private Object readResolve() {
            return ToStringStyle.NO_FIELD_NAMES_STYLE;
        }
        
    }
    
    //----------------------------------------------------------------------------
    
    /**
     * クラス名、ハッシュコード、内容の開始記号、フィールド名を表示しないToStringStyle です。
     * これは不変性を確実にするために StandardToStringStyle が使用するインナークラスです。
     * {@primary ToStringStyle that does not print out the classname, identity hashcode,
     * content start or field name.
     * This is an inner class rather than using StandardToStringStyle to
     * ensure its immutability.}
     */
    private static final class SimpleToStringStyle extends ToStringStyle {
        
        /**
         * コンストラクタ。インスタンス化するよりも静的な定数を使用してください。
         * {@primary Constructor - use the static constant rather than instantiating.}
         */
        private SimpleToStringStyle() {
            super();
            this.setUseClassName(false);
            this.setUseIdentityHashCode(false);
            this.setUseFieldNames(false);
            this.setContentStart("");
            this.setContentEnd("");
        }
        
        /**
         * シリアライズ化の後のシングルトンを確実にします。
         * {@primary Ensure singleton after serialization.}
         * @return シングルトン(のインスタンス)
         * {@primary the singleton}
         */
        private Object readResolve() {
            return ToStringStyle.SIMPLE_STYLE;
        }
        
    }
    
    //----------------------------------------------------------------------------
    
    /**
     * 複数行で内容を表示する ToStringStyle です。
     * これは不変性を確実にするために StandardToStringStyle が使用するインナークラスです。
     * {@primary ToStringStyle that outputs on multiple lines.
     * This is an inner class rather than using StandardToStringStyle to
     * ensure its immutability.}
     */
    private static final class MultiLineToStringStyle extends ToStringStyle {

        /**
         * コンストラクタ。インスタンス化するよりも静的な定数を使用してください。
         * {@primary Constructor - use the static constant rather than instantiating.}
         */
        private MultiLineToStringStyle() {
            super();
            this.setContentStart("[" + SystemUtils.LINE_SEPARATOR + "  ");
            this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + "  ");
            this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
        }
        
        /**
         * シリアライズ化の後のシングルトンを確実にします。
         * {@primary Ensure singleton after serialization.}
         * @return シングルトン(のインスタンス)
         * {@primary the singleton}
         */
        private Object readResolve() {
            return ToStringStyle.MULTI_LINE_STYLE;
        }
        
    }
    
    //----------------------------------------------------------------------------
    
    // Removed, as the XML style needs more work for escaping characters, arrays,
    // collections, maps and embedded beans.
//    /**
//     * ToStringStyle that outputs in XML style
//     */
//    private static class XMLToStringStyle extends ToStringStyle {
//        
//        /**
//         * Constructor - use the static constant rather than instantiating.
//         */
//        private XMLToStringStyle() {
//            super();
//            nullText = "null";
//            sizeStartText = "size=";
//            sizeEndText = "";
//        }
//        
//        /**
//         * @see ToStringStyle#appendStart(StringBuffer, Object)
//         */
//        public void appendStart(StringBuffer buffer, Object object) {
//            buffer.append('<');
//            buffer.append(getShortClassName(object.getClass()));
//            buffer.append(" class=\"");
//            appendClassName(buffer, object);
//            buffer.append("\" hashCode=\"");
//            appendIdentityHashCode(buffer, object);
//            buffer.append("\">");
//            buffer.append(SystemUtils.LINE_SEPARATOR);
//            buffer.append("  ");
//        }
//
//        /**
//         * @see ToStringStyle#appendFieldStart(StringBuffer, String)
//         */
//        protected void appendFieldStart(StringBuffer buffer, String fieldName) {
//            buffer.append('<');
//            buffer.append(fieldName);
//            buffer.append('>');
//        }
//
//        /**
//         * @see ToStringStyle#appendFieldEnd(StringBuffer, String)
//         */
//        protected void appendFieldEnd(StringBuffer buffer, String fieldName) {
//            buffer.append("</");
//            buffer.append(fieldName);
//            buffer.append('>');
//            buffer.append(SystemUtils.LINE_SEPARATOR);
//            buffer.append("  ");
//        }
//
//        /**
//         * @see ToStringStyle#appendEnd(StringBuffer, Object)
//         */
//        public void appendEnd(StringBuffer buffer, Object object) {
//            int len = buffer.length();
//            if (len > 2 && buffer.charAt(len - 1) == ' ' && buffer.charAt(len - 2) == ' ') {
//                buffer.setLength(len - 2);
//            }
//            buffer.append("</");
//            buffer.append(getShortClassName(object.getClass()));
//            buffer.append("\">");
//        }
//
//    }
    
}
