/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.io;

import java.util.concurrent.ConcurrentMap;										// 7.0.1.2 (2018/11/04)
import java.util.concurrent.ConcurrentHashMap;									// 7.0.1.2 (2018/11/04)

// import org.opengion.fukurou.system.HybsConst;
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;				// 8.0.0.0 (2021/08/31)
import static org.opengion.fukurou.system.HybsConst.CR;							// 8.0.0.0 (2021/08/31)

/**
 * JsChartData は、JsChartData の個別属性を管理しているﾃﾞｰﾀ管理ｸﾗｽです。
 *
 * 内部には、data:datasets: の 要素の属性と、options:scales:[x/y]: の 要素の属性を管理します。
 * chartColumn 、useAxis 属性は別管理で、ticks と、grid:属性 は、関連する属性を無効化します。
 * datasetOptions と、yAxesOptions は、直接追加されますので、既存の属性をｾｯﾄしている場合は、
 * 動作保障できません。
 *
 * 8.0.0.0 (2021/08/31) V2 → V3 対応
 *  scaleLabel → title
 *  gridLines  → grid
 *  options       追加
 *  plugins       追加(options:plugins:)
 *  annotations   追加(plugins:annotation:annotations:)
 *
 * @og.rev 5.9.17.2 (2017/02/08) 新規作成
 * @og.rev 7.0.1.1 (2018/10/22) 大幅見直し
 * @og.rev 8.0.0.0 (2021/08/31) Ver3対応 大幅見直し
 *
 * @version	8.0.0.0
 * @author	T.OTA
 * @since	JDK11.0
 *
 */
public class JsChartData {
	/** ﾁｬｰﾄ属性 {@value} */ public static final String DATASET		= "dataset";
	/** ﾁｬｰﾄ属性 {@value} */ public static final String AXIS		= "axis";
	/** ﾁｬｰﾄ属性 {@value} */ public static final String TICKS		= "ticks";
	/** ﾁｬｰﾄ属性 {@value} */ public static final String TIME		= "time";				// X軸用 axis属性
//	/** ﾁｬｰﾄ属性 {@value} */ public static final String SCALE_LABEL	= "scaleLabel";
	/** ﾁｬｰﾄ属性 {@value} */ public static final String TITLE		= "title";				// 8.0.0.0 (2021/08/31) V2 → V3 対応
//	/** ﾁｬｰﾄ属性 {@value} */ public static final String GRID_LINES	= "gridLines";
	/** ﾁｬｰﾄ属性 {@value} */ public static final String GRID		= "grid";				// 8.0.0.0 (2021/08/31) V2 → V3 対応

	/** ﾁｬｰﾄ属性 {@value} */ public static final String OPTIONS		= "options";			// 8.0.0.0 (2021/08/31) V2 → V3 対応
	/** ﾁｬｰﾄ属性 {@value} */ public static final String PLUGINS		= "plugins";			// 8.0.0.0 (2021/08/31) V2 → V3 対応
	/** ﾁｬｰﾄ属性 {@value} */ public static final String ANNOTATIONS	= "annotations";		// 8.0.0.0 (2021/08/31) V2 → V3 対応

	private static final String CR_TAB = CR + "\t\t";							// 8.0.0.0 (2021/08/31)

//	final int MAX_LEN = SCALE_LABEL.length();									// 暫定的に最も長い文字列

//	private final String[] AXIS_OPTS = new String[] { TICKS,TIME,SCALE_LABEL,GRID_LINES } ;	// 7.2.9.4 (2020/11/20) private 追加
	private final String[] AXIS_OPTS = new String[] { TICKS,TIME,TITLE,GRID } ;				// 8.0.0.0 (2021/08/31) V2 → V3 対応

	private final ConcurrentMap<String,StringBuilder> charts  = new ConcurrentHashMap<>();	// 7.0.1.2 (2018/11/04) ﾁｬｰﾄ本体のﾊﾞｯﾌｧのMap (not null保障)
//	private final ConcurrentMap<String,StringBuilder> options = new ConcurrentHashMap<>();	// 7.0.1.2 (2018/11/04) ｵﾌﾟｼｮﾝﾊﾞｯﾌｧのMap (not null保障)

	private String	chartColumn			;										// ﾁｬｰﾄｶﾗﾑ
	private String	yid					;										// yAxesIDに使用するキーとなるid ( yAxesID=yid+'Ax' )
	private boolean	useAxis				;										// y軸表示を行うかどうか(true/false)
	private boolean	useTime				;										// x軸の時間表示を使用するかどうか。

	private final StringBuilder errBuf = new StringBuilder();					// 8.0.0.0 (2021/08/31)

//	private final StringBuilder dataset = new StringBuilder( BUFFER_MIDDLE );
//	private final StringBuilder axis    = new StringBuilder( BUFFER_MIDDLE );
//	private final StringBuilder ticks   = new StringBuilder( BUFFER_MIDDLE );	// axis の属性
//	private final StringBuilder scLbl   = new StringBuilder( BUFFER_MIDDLE );	// axis の属性
//	private final StringBuilder grdLine = new StringBuilder( BUFFER_MIDDLE );	// axis の属性
//	private final StringBuilder time    = new StringBuilder( BUFFER_MIDDLE );	// axis の属性

	/**
	 * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
	 *
	 * @og.rev 6.9.7.0 (2018/05/14) PMD Each class should declare at least one constructor
	 */
	public JsChartData() { super(); }	// これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。

	/**
	 * ﾁｬｰﾄｶﾗﾑを設定します。
	 *
	 * @param	chartColumn	ﾁｬｰﾄｶﾗﾑ
	 */
	public void setChartColumn( final String chartColumn ) {
		this.chartColumn = chartColumn;
//		addDataset( "data" , chartColumn , true );	// ｵﾌﾞｼﾞｪｸﾄなので、ｸｵｰﾄ処理しません。
	}

	/**
	 * JsChartData ｵﾌﾞｼﾞｪｸﾄを作成する時のﾁｬｰﾄｶﾗﾑを取得します。
	 *
	 * @return	ﾁｬｰﾄｶﾗﾑ
	 */
	public String getChartColumn() {
		return chartColumn;
	}

	/**
	 * ﾃﾞｰﾀﾁｬｰﾄのIDを指定します。
	 *
	 * yAxisIDに使用するｷｰとなるid ( yAxisID=yid+'Ax' )
	 *
	 * @og.rev 7.0.1.1 (2018/10/22) 属性の追加。
	 *
	 * @param	id	固有の名前
	 */
	public void setId( final String id ) {
		yid = id;

		addAxis( "id" , yid + "Ax" , false );
	}

	/**
	 * y軸表示を使用するかどうか(true/false)を設定します。
	 *
	 * 使用するとは、yAxisID属性を、内部的に登録します。
	 *
	 * @param	flag	true:使用する/false:使用しない
	 */
	public void setUseAxis( final boolean flag ) {
		useAxis = flag;
	}

	/**
	 * y軸表示を使用するかどうか(true/false)を設定します。
	 *
	 * @return	true:使用する/false:使用しない
	 */
	public boolean isUseAxis() {
		return useAxis;
	}

	/**
	 * x軸の時間表示を使用するかどうか(true/false)を設定します。
	 *
	 * 使用しない場合は、time ﾊﾞｯﾌｧｰを axis 属性に追加しません。
	 *
	 * @param	flag	true:使用する/false:使用しない
	 */
	public void setUseTime( final boolean flag ) {
		useTime = flag;
	}

	/**
	 * ｷｰと設定値をdatasetに追加します。
	 *
	 * @param	key		ｷｰ
	 * @param	val		設定値
	 * @param	isNum	数値項目/boolean項目かどうか(true:数値要素/false:文字または配列要素)
	 */
	public void addDataset( final String key , final String val , final boolean isNum ) {
		addBuffer( DATASET,key,val,isNum );
	}

	/**
	 * ｷｰと設定値をaxisに追加します。
	 *
	 *  ※ chartJS上は、Axes(axisの複数形)と、Axis を使い分けていますが、属性は、axis で統一します。
	 *
	 * @param	key		ｷｰ
	 * @param	val		設定値
	 * @param	isNum	数値項目かどうか(true:数値要素/false:文字または配列要素)
	 */
	public void addAxis( final String key , final String val , final boolean isNum ) {
		addBuffer( AXIS,key,val,isNum );
	}

	/**
	 * 設定値をaxisに追加します。
	 *
	 * これは、chartsﾊﾞｯﾌｧに、bufKey 毎のﾊﾞｯﾌｧに、引数をそのまま追加します。
	 *
	 * @og.rev 8.0.0.0 (2021/08/31) 新規作成
	 *
	 * @param	bufKey	追加するﾊﾞｯﾌｧのｷｰ
	 * @param	val		設定値
	 */
	public void addAxis( final String bufKey , final String val ) {
		if( val != null && val.length() > 0 ) {
			// ﾁｬｰﾄ本体のﾊﾞｯﾌｧに追加していきます。
			final StringBuilder buf = charts.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) );
			// 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
			final int st = val.indexOf( ':' );
			if( st > 0 && buf.indexOf( val.substring( 0,st+1 ) ) >= 0 ) {		// ｷｰﾁｪｯｸは、':' も含めて確認する
				errBuf.append( "addAxisで登録された" ).append( bufKey )
						.append( "属性は、chartsに設定済みです。" ).append( CR )
						.append( "val=" ).append( val ).append( CR );
			}
			else {
				buf.append( val ).append( ',' );
			}
		}
	}

	/**
	 * ｷｰと設定値をaxisのticks に追加します。
	 *
	 * @param	key		ｷｰ
	 * @param	val		設定値
	 * @param	isNum	数値項目かどうか(true:数値要素/false:文字または配列要素)
	 */
	public void addTicks( final String key , final String val , final boolean isNum ) {
		addBuffer( TICKS,key,val,isNum );
	}

	/**
	 * ｷｰと設定値をaxisのtime に追加します。
	 *
	 * @param	key		ｷｰ
	 * @param	val		設定値
	 * @param	isNum	数値項目かどうか(true:数値要素/false:文字または配列要素)
	 */
	public void addTime( final String key , final String val , final boolean isNum ) {
		addBuffer( TIME,key,val,isNum );
	}

	/**
	 * ｷｰと設定値をplugins に追加します。
	 *
	 * @og.rev 8.0.0.0 (2021/08/31) 新規作成
	 *
	 * @param	key		ｷｰ
	 * @param	val		設定値
	 * @param	isNum	数値項目かどうか(true:数値要素/false:文字または配列要素)
	 */
	public void addPlugins( final String key , final String val , final boolean isNum ) {
		addBuffer( PLUGINS,key,val,isNum );
	}

	/**
	 * ｷｰと設定値をannotations に追加します。
	 *
	 * @og.rev 8.0.0.0 (2021/08/31) 新規作成
	 *
	 * @param	key		ｷｰ
	 * @param	val		設定値
	 * @param	isNum	数値項目かどうか(true:数値要素/false:文字または配列要素)
	 */
	public void addAnnotations( final String key , final String val , final boolean isNum ) {
		addBuffer( ANNOTATIONS,key,val,isNum );
	}

	/**
	 * ｷｰと設定値を指定のﾊﾞｯﾌｧｰに追加します。
	 *
	 * isNum=true か、内部で、先頭文字が、'[' か '{' の場合は、ｸｵｰﾃｰｼｮﾝを付けません。
	 * また、引数が、nullか、空文字列の場合は、追加しません。
	 *
	 * @param	bufKey	追加するﾊﾞｯﾌｧのｷｰ
	 * @param	key		ｷｰ
	 * @param	val		設定値
	 * @param	isNum	数値項目かどうか(true:数値要素/false:文字または配列、ｵﾌﾞｼﾞｪｸﾄ要素)
	 */
	private void addBuffer( final String bufKey , final String key , final String val , final boolean isNum ) {
		if( val != null && !val.trim().isEmpty() ) {
			final String val2 = val.trim();

			// ﾁｬｰﾄ本体のﾊﾞｯﾌｧに追加していきます。
			final StringBuilder buf = charts.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) );

			// 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
			if( buf.indexOf( key+':' ) >= 0 ) {				// ｷｰﾁｪｯｸは、':' も含めて確認する
				errBuf.append( "addBufferで登録された" ).append( bufKey ).append( ':' ).append( key )
						.append( "属性は、chartsに設定済みです。" ).append( CR )
						.append( "key:val=" ).append( key ).append( ':' ).append( val2 ).append( CR );
			}
			else {
				// bufKey が DATASET とAXIS の場合は、40文字単位で改行する。
				if( DATASET.equals( bufKey ) ||  AXIS.equals( bufKey ) ) {
					if( buf.length() - buf.lastIndexOf( CR ) > 40 ) {
						buf.append( CR_TAB );
					}
				}

				if( isNum || '[' == val2.charAt(0) || '{' == val2.charAt(0) ) {
					buf.append( key ).append( ':' ).append( val2 ).append( ',' );
				}
				else {
					buf.append( key ).append( ":'" ).append( val2 ).append( "'," );
				}
			}
		}
	}

//	/**
//	 * 指定のﾊﾞｯﾌｧｰに、ｵﾌﾟｼｮﾝ属性を追加します。
//	 *
//	 * ｵﾌﾟｼｮﾝ属性は、各ﾊﾞｯﾌｧｰの一番最後にまとめて追加します。
//	 * key:val の関係ではなく、val だけをそのまま追加していきます。
//	 * ｵﾌﾟｼｮﾝの追加は、まとめて最後に行いますので、このﾒｿｯﾄﾞ上では
//	 * 最後にｶﾝﾏは付けません。必要であれば、追加する設定値にｶﾝﾏをつけてください。
//	 *
//	 * @og.rev 8.0.0.0 (2021/08/31) ｺﾒﾝﾄ修正(scaleLabel→title , gridLines→grid)
//	 *
//	 * @param	bufKey	ｷｰ [dataset,axis,ticks,time,scaleLabel,gridLines] が指定可能
//	 * @param	val		設定値
//	 */
//	public void addOptions( final String bufKey , final String val ) {
//		if( val != null && val.length() > 0 ) {
//			// ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧに追加していきます。
//			// これは、ﾁｬｰﾄ本体のﾊﾞｯﾌｧに対して、最後に追加する必要があるためです。
////			options.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) )
////						.append( val ).append( ',' );
//
//			// ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧに追加していきます。
//			final StringBuilder buf = options.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) );
//
//			// 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
//			final int st = val.indexOf( ':' );
//			if( st > 0 && buf.indexOf( val.substring( 0,st+1 ) ) >= 0 ) {	// ｷｰﾁｪｯｸは、':' も含めて確認する
//				errBuf.append( "addOptionsで登録された" ).append( bufKey )
//						.append( "属性は、options に設定済みです。" ).append( CR )
//						.append( "val=" ).append( val ).append( CR );
//			}
//			else {
//				buf.append( val ).append( ',' ).append( CR_TAB );
//			}
//		}
//	}

//	/**
//	 * ｷｰと設定値をoptions に追加します。
//	 *
//	 * @og.rev 8.0.0.0 (2021/08/31) 新規作成
//	 *
//	 * @param	key		ｷｰ
//	 * @param	val		設定値
//	 * @param	isNum	数値項目かどうか(true:数値要素/false:文字または配列要素)
//	 */
//	public void addOptions( final String key , final String val , final boolean isNum ) {
//		if( val != null && !val.trim().isEmpty() ) {
//			final String val2 = val.trim();
//
//			// ﾁｬｰﾄ本体のﾊﾞｯﾌｧに追加していきます。
//			final StringBuilder buf = options.computeIfAbsent( key , k -> new StringBuilder( BUFFER_MIDDLE ) );
//
//			// 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
//			if( buf.indexOf( key+':' ) >= 0 ) {	// ｷｰﾁｪｯｸは、':' も含めて確認する
//				errBuf.append( "addOptionsで登録された" ).append( key )
//						.append( "属性は、optionsに設定済みです。" ).append( CR )
//						.append( "key:val=" ).append( key ).append( ':' ).append( val2 ).append( CR );
//			}
//			else {
//				if( isNum || '[' == val2.charAt(0) || '{' == val2.charAt(0) ) {
//					buf.append( key ).append( ':' ).append( val2 ).append( ',' ) ;
//				}
//				else {
//					buf.append( key ).append( ":'" ).append( val2 ).append( "'," ) ;
//				}
//			}
//		}
//	}

	/**
	 * ﾊﾞｯﾌｧｷｰ内に、設定ｷｰの値がすでに登録済みかどうか(あればtrue)を判定します。
	 *
	 * 一般とｵﾌﾟｼｮﾝの両方を検索します。
	 *
	 * @og.rev 7.0.1.3 (2018/11/12) ﾊﾞｯﾌｧｷｰ検索処理追加
	 *
	 * @param	bufKey	ﾁｪｯｸするﾊﾞｯﾌｧのｷｰ
	 * @param	key		ｷｰ
	 * @return	すでに登録済みかどうか [true:登録済み/false:未登録]
	 */
	public boolean contains( final String bufKey , final String key ) {
		boolean isContains = false;

		final StringBuilder chBuf = charts.get( bufKey );
		if( chBuf != null && chBuf.indexOf( key ) >= 0 ) { isContains = true; }
//		else {
//			final StringBuilder optBuf = options.get( bufKey );
//			if( optBuf != null && optBuf.indexOf( key ) >= 0 ) { isContains = true; }
//		}

		return isContains ;
	}

	/**
	 * JsChartData ｵﾌﾞｼﾞｪｸﾄのdata:datasets: ﾊﾟﾗﾒｰﾀ情報を取得します。
	 *
	 * ここで返す値は、yidが、'y0' とすると、
	 * const y0Ds = { dataset.toString() } ; という文字列を返します。
	 * 引数は、'x' か 'y' を指定します。
	 * 通常、Y軸表示を行う場合は、'y' を指定しまが、horizontalBar 使用時は、
	 * 'x' を指定することになります。
	 * ただし、useAxis=false の場合は、(x,y)AxisID は出力されません。
	 *
	 * @og.rev 7.0.1.1 (2018/10/22) data:datasets: ﾊﾟﾗﾒｰﾀ情報
	 *
	 * @param	xy	idのｷｰﾜｰﾄﾞ [x,y]
	 * @return	ﾊﾟﾗﾒｰﾀ文字列
	 */
	public String getDataset( final char xy ) {
		// ﾁｬｰﾄ本体のﾊﾞｯﾌｧから取得します。
		final StringBuilder dataset = charts.computeIfAbsent( DATASET , k -> new StringBuilder( BUFFER_MIDDLE ) );

		// chartColumn は linear の場合、名前が変更されるので、出力の直前にｾｯﾃｨﾝｸﾞします。
		dataset.append( "data:" ).append( chartColumn ).append( ',' ) ;

		// 8.0.0.0 (2021/08/31) yidの初期値(1つ目)は、AxisID を出さない。
//		if( useAxis && dataset.indexOf( "AxisID:" ) < 0 ) {
		if( useAxis && dataset.indexOf( "AxisID:" ) < 0 && !"y0".equals( yid ) ) {
			dataset.append( xy ).append( "AxisID:'" ).append( getAxisKey() ).append( "'," );
		}

		return new StringBuilder( BUFFER_MIDDLE )
//					.append( "var " ).append( getDatasetKey() ).append( "={" )
					.append( "const " ).append( getDatasetKey() ).append( "={" )
					.append( dataset )
//					.append( mapGet( options , DATASET ) )		// ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧ
					.append( CR ).append( "\t};" ).toString();
	}

	/**
	 * JsChartData ｵﾌﾞｼﾞｪｸﾄのdata:datasets: ﾊﾟﾗﾒｰﾀ情報の変数名を取得します。
	 *
	 * ここで返す値は、yidが、'y0' とすると、
	 * "y0Ds" という文字列を返します。
	 *
	 * @og.rev 7.0.1.1 (2018/10/22) data:datasets: ﾊﾟﾗﾒｰﾀ変数名
	 *
	 * @return	ﾊﾟﾗﾒｰﾀ文字列
	 */
	public String getDatasetKey() {
		return yid + "Ds" ;
	}

	/**
	 * JsChartData ｵﾌﾞｼﾞｪｸﾄのoptions:scales:yAxes: ﾊﾟﾗﾒｰﾀ情報を取得します。
	 *
	 * ここで返す値は、yidが、'y0' とすると、
	 * const y0Ax = { addAxis.toString() } ; という文字列を返します。
	 * ただし、useAxis=false の場合は、ｾﾞﾛ文字列を返します。
	 *
	 * @og.rev 7.0.1.1 (2018/10/22) options:scales:yAxes: ﾊﾟﾗﾒｰﾀ情報
	 *
	 * @return	ﾊﾟﾗﾒｰﾀ文字列
	 */
	public String getAxis() {
		// ﾁｬｰﾄ本体のﾊﾞｯﾌｧから取得します。
		final StringBuilder axis = charts.computeIfAbsent( AXIS , k -> new StringBuilder( BUFFER_MIDDLE ) );

		// AXISのｵﾌﾟｼｮﾝである、TICKS,TIME,TITLE,GRID を追加します。
		// これらは、ﾁｬｰﾄ本体とｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧから取得しますが、ｵﾌﾟｼｮﾝ専用ﾊﾞｯﾌｧは最後に追加します。
		for( final String opt : AXIS_OPTS ) {
			// 超特殊処理：useTime=false のときは、TIME は、処理しません。
			if( !useTime && TIME.equals( opt ) ) { continue; }

			final String key = opt + ":{" ;
//			if( axis.indexOf( key ) < 0 && ( charts.containsKey( opt ) || options.containsKey( opt ) ) ) {
			if( axis.indexOf( key ) < 0 && charts.containsKey( opt ) ) {
				axis.append( CR_TAB ).append( key )
					.append( mapGet( charts  , opt ) )			// ﾁｬｰﾄ本体のﾊﾞｯﾌｧ
//					.append( mapGet( options , opt ) )			// ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧ
					.append( "}," );
			}
		}

		return new StringBuilder( BUFFER_MIDDLE )
//					.append( "var " ).append( getAxisKey() ).append( "={" )
					.append( "const " ).append( getAxisKey() ).append( "={" )
					.append( axis )
//					.append( mapGet( options , AXIS ) )			// オプション専用のバッファ
					.append( CR ).append( "\t};" ).toString();
	}

	/**
	 * JsChartData ｵﾌﾞｼﾞｪｸﾄのoptions:scales:yAxes: ﾊﾟﾗﾒｰﾀ情報の変数名を取得します。
	 *
	 * ここで返す値は、yidが、'y0' とすると、
	 * "y0Ax ," という文字列を返します。便宜上、後ろのｺﾛﾝも追加しています。
	 * その際、useAxis=false の場合は、空文字列を返します。
	 *  ※ chartJS上は、Axes(axisの複数形)と、Axis を使い分けていますが、属性は、axis で統一します。
	 *
	 * @og.rev 7.0.1.1 (2018/10/22) options:scales:yAxes:ﾊﾟﾗﾒｰﾀ情報の変数名
	 *
	 * @return	ﾊﾟﾗﾒｰﾀ文字列
	 */
	public String getAxisKey() {
		return yid + "Ax" ;
	}

	/**
	 * MapのStringBuilderがnullなら、ｾﾞﾛ文字列を、そうでなければ、StringBuilder#toString()
	 * の値を返します。
	 *
	 * map.getOrDefault( KEY , new StringBuilder() ) ).toString()
	 * という処理の簡易版です。
	 *
	 * final StringBuilder buf = map.get( KEY );
	 * return buf == null || buf.length() == 0 ? "" : buf.toString();
	 *
	 * @og.rev 7.0.1.2 (2018/11/04) 新規登録
	 *
	 * @param	map	判定するMap
	 * @param	key	Mapから取り出すｷｰ
	 * @return	MapにStringBuilderがあれば、#toString()を、無ければ、ｾﾞﾛ文字列を返します。
	 */
//	private String nval( final ConcurrentMap<String,StringBuilder> map , final String key ) {
	private String mapGet( final ConcurrentMap<String,StringBuilder> map , final String key ) {
		final StringBuilder buf = map.get( key );
		return buf == null || buf.length() == 0 ? "" : buf.toString();
	}

	/**
	 * ｴﾗｰﾒｯｾｰｼﾞを返します。
	 *
	 * ｴﾗｰが存在しなかった場合は、長さｾﾞﾛの文字列になります。
	 *
	 * @og.rev 8.0.0.0 (2021/08/31) 新規作成
	 *
	 * @return	ｴﾗｰﾒｯｾｰｼﾞの内部ﾊﾞｯﾌｧを文字列にして返します。
	 * @og.rtnNotNull
	 */
	public String getErrorMessage() {
		return errBuf.toString();
	}

	/**
	 * 内部ﾊﾞｯﾌｧを文字列にして返します。
	 *
	 * @return	内部ﾊﾞｯﾌｧを文字列にして返します。
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
			.append( "chartColumn=" ).append( chartColumn     ).append( CR )
			.append( "datasetKey =" ).append( getDatasetKey() ).append( CR )
			.append( "axisKey    =" ).append( getAxisKey()    ).append( CR );

		charts.forEach(  (k,v) -> buf.append( k ).append( " = "     ).append( v ).append( CR ) );
//		options.forEach( (k,v) -> buf.append( k ).append( " opt = " ).append( v ).append( CR ) );

		return buf.toString();
	}
}
