/*
 * 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.fukurou.business;

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Map;
import java.util.HashMap;														// 6.4.3.3 (2016/03/04) not null調査が済むまで､元に戻します｡
import java.util.concurrent.ConcurrentMap;										// 6.4.3.3 (2016/03/04)
import java.util.concurrent.ConcurrentHashMap;									// 6.4.3.1 (2016/02/12) refactoring
import java.util.Locale;
import java.util.Set;
import java.util.Arrays;
import java.util.function.Consumer;												// 8.2.0.3 (2022/06/30)

import org.opengion.fukurou.system.OgRuntimeException ;							// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.db.ConnectionFactory;
import org.opengion.fukurou.db.DBFunctionName;
import org.opengion.fukurou.db.DBUtil;
import org.opengion.fukurou.db.ResultSetValue;									// 8.2.0.3 (2022/06/30)
import org.opengion.fukurou.db.Transaction;
import org.opengion.fukurou.model.Formatter;
import org.opengion.fukurou.model.DataModel;									// 6.7.9.1 (2017/05/19)
import org.opengion.fukurou.system.DateSet;										// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.util.ErrMsg;
import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.util.HybsLoader;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.SystemParameter;
import static org.opengion.fukurou.system.HybsConst.CR;							// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;				// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.system.HybsConst.DB_FETCH_SIZE;				// 6.9.4.1 (2018/04/09)

/**
 * 業務ﾛｼﾞｯｸを処理するために必要な共通ﾒｿｯﾄﾞの実行を行っている抽象ｸﾗｽです｡
 *
 * ﾒｲﾝﾛｼﾞｯｸについては､各ｻﾌﾞｸﾗｽで実装する必要があります｡
 *
 * @og.rev 5.1.1.0 (2009/12/01) 新規作成
 * @og.group 業務ﾛｼﾞｯｸ
 *
 * @version 5.0
 * @author Hiroki Nakamura
 * @since JDK1.6,
 */
public abstract class AbstractBizLogic {

	/** ｴﾗｰﾒｯｾｰｼﾞをｾｯﾄする際に使用します {@value} */
	protected static final int OK        = ErrorMessage.OK;
	/** ｴﾗｰﾒｯｾｰｼﾞをｾｯﾄする際に使用します {@value} */
	protected static final int WARNING   = ErrorMessage.WARNING;
	/** ｴﾗｰﾒｯｾｰｼﾞをｾｯﾄする際に使用します {@value} */
	protected static final int NG        = ErrorMessage.NG;
	/** ｴﾗｰﾒｯｾｰｼﾞをｾｯﾄする際に使用します {@value} */
	protected static final int EXCEPTION = ErrorMessage.EXCEPTION;
	/** ｴﾗｰﾒｯｾｰｼﾞをｾｯﾄする際に使用します {@value} */
	protected static final int ORCL_ERR  = ErrorMessage.ORCL_ERR;

//	/** 6.9.3.0 (2018/03/26) ﾃﾞｰﾀ検索時のﾌｪｯﾁｻｲｽﾞ  {@value} */
//	private static final int DB_FETCH_SIZE = 1001 ;

	private Connection	conn	;
	private Transaction tran	;			// 5.1.9.0 (2010/08/01) ｼｰｹﾝｽ対応
	private String		dbid	;			// 5.1.9.0 (2010/08/01) ｼｰｹﾝｽ対応
	/** ﾃﾞｰﾀﾍﾞｰｽﾌｧﾝｸｼｮﾝ */
	protected DBFunctionName	dbName	;	// 5.1.9.0 (2010/08/01) ｼｰｹﾝｽ対応
	private HybsLoader	loader	;
	private String[]	keys	;
	private String[]	vals	;
	/** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え｡  */
	private final Map<String, String> variableMap  = new HashMap<>();								// 6.4.3.3 (2016/03/04) not null調査が済むまで､元に戻します｡
	/** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え｡  */
	private final ConcurrentMap<String, Formatter> formatMap = new ConcurrentHashMap<>();
	/** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え｡  */
	private final ConcurrentMap<String, SystemParameter> sysParamMap = new ConcurrentHashMap<>();
	private final ErrorMessage errMsg = new ErrorMessage();
	private String bizRtn		;					// 5.1.8.0 (2010/07/01) ﾒｿｯﾄﾞ名と変数名を分ける｡
	private boolean debugFlag	;					// 5.1.8.0 (2010/07/01) ﾒｿｯﾄﾞ名と変数名を分ける｡

	private final StringBuilder debugMsg = new StringBuilder( BUFFER_MIDDLE );
	private boolean	useParamMetaData	;			// 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得｡(PostgreSQL対応)

	private final ConcurrentMap<String, String> rtnMap = new ConcurrentHashMap<>();		// 6.9.9.0 (2018/08/20) 戻り値を返せるようにします｡

	/**
	 * 配列側ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 *
	 * 配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ自体は､protected属性であるため､ｻﾌﾞｸﾗｽから直接参照することができます｡
	 * 但し､これは､各業務ﾛｼﾞｯｸで直接参照することを想定したものではなく､BizLogicの
	 * ﾒｲﾝ構造を拡張するｻﾌﾞｸﾗｽを定義する際に使用することを想定しています｡
	 * (この想定がなければ､本来は､package privateにすべきです)
	 * このため､業務ﾛｼﾞｯｸを各実装ｸﾗｽでは直接参照しないで下さい｡
	 *
	 * @og.rev 6.7.9.1 (2017/05/19) protected ArrayTableModel を､private DataModel に変更します｡
	 */
	private DataModel<String> table ;				// 6.7.9.1 (2017/05/19)

	/**
	 * 配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙの現在の処理行
	 *
	 * 行番号自体は､protected属性であるため､ｻﾌﾞｸﾗｽから直接参照することができます｡
	 * 但し､これは､各業務ﾛｼﾞｯｸで直接参照することを想定したものではなく､BizLogicの
	 * ﾒｲﾝ構造を拡張するｻﾌﾞｸﾗｽを定義する際に使用することを想定しています｡
	 * (この想定がなければ､本来は､package privateにすべきです)
	 * このため､業務ﾛｼﾞｯｸを各実装ｸﾗｽでは直接参照しないで下さい｡
	 *
	 * ※ ｲﾝﾃﾞｯｸｽ(row)とは､このArrayTableModel に持つ vals 配列の行のｲﾝﾃﾞｯｸｽです｡
	 * よって､ｵﾘｼﾞﾅﾙのDBTableModelの行番号ではありません｡
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) protected → default 変更(ｻﾌﾞｸﾗｽからのｱｸｾｽはない)
	 */
//	protected int row = -1;
	/* default */ int row = -1;

	/**
	 * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	protected AbstractBizLogic() { super(); }		// これも､自動的に呼ばれるが､空のﾒｿｯﾄﾞを作成すると警告されるので､明示的にしておきます｡

	/**
	 * DBのﾄﾗﾝｻﾞｸｼｮﾝｵﾌﾞｼﾞｪｸﾄを指定します｡
	 * 各実装ｸﾗｽでは､ｺﾈｸｼｮﾝのcommit,rollbackは行われません｡
	 * (全てのDB処理は､1つのﾄﾗﾝｻﾞｸｼｮﾝとして処理されます｡)
	 * このため､commit,rollbackは呼び出し元で行う必要があります｡
	 * このﾒｿｯﾄﾞは､1度しかｾｯﾄすることができません｡2回以上呼び出しするとｴﾗｰになります｡
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
	 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得｡(PostgreSQL対応)
	 * @og.rev 8.0.2.0 (2021/11/30) protected → default 変更(ｻﾌﾞｸﾗｽからのｱｸｾｽはない)
	 *
	 * @param	tr	ﾄﾗﾝｻﾞｸｼｮﾝ
	 */
//	public void setTransaction( final Transaction tr ) {
	/* default */ void setTransaction( final Transaction tr ) {
		tran = tr;
		conn = tran.getConnection( dbid );
		useParamMetaData = ConnectionFactory.useParameterMetaData( dbid );	// 5.3.8.0 (2011/08/01)
	}

	/**
	 * DBのﾄﾗﾝｻﾞｸｼｮﾝｵﾌﾞｼﾞｪｸﾄを返します｡
	 *
	 * (全てのDB処理は､1つのﾄﾗﾝｻﾞｸｼｮﾝとして処理されます｡)
	 *
	 * @og.rev 7.4.2.0 (2021/05/14) 外部から指定するTransactionｵﾌﾞｼﾞｪｸﾄ 対応
	 * @og.rev 8.0.2.0 (2021/11/30) protected → default 変更(ｻﾌﾞｸﾗｽからのｱｸｾｽはない)
	 * @og.rev 8.1.1.0 (2022/02/04) default ⇒ protected に変更します。
	 *
	 * @return	ﾄﾗﾝｻﾞｸｼｮﾝ
	 */
//	public Transaction getTransaction() {
	protected Transaction getTransaction() {
		return tran;
	}

	/**
	 * 接続先IDを指定します｡
	 * このﾒｿｯﾄﾞは､1度しかｾｯﾄすることができません｡2回以上呼び出しするとｴﾗｰになります｡
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
	 *
	 * @param	id	接続先ID
	 */
	/* default */ void setDbid( final String id ) {
		dbid = id;
	}

	/**
	 * 業務ﾛｼﾞｯｸのｸﾗｽをﾛｰﾄﾞするためのｸﾗｽﾛｰﾀﾞｰをｾｯﾄします｡
	 * このﾒｿｯﾄﾞは､1度しかｾｯﾄすることができません｡2回以上呼び出しするとｴﾗｰになります｡
	 *
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 *
	 * @param	ldr	ｸﾗｽﾛｰﾀﾞｰ
	 */
	/* default */ void setLoader( final HybsLoader ldr ) {
		if( loader != null ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "既にｸﾗｽﾛｰﾀﾞｰがｾｯﾄされています｡"
									+ " OLD:" + loader
									+ " IN :" + ldr ;
			throw new OgRuntimeException( errMsg );
		}
		loader = ldr;
	}

	/**
	 * 配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙをｾｯﾄします｡
	 * このﾒｿｯﾄﾞは､1度しかｾｯﾄすることができません｡2回以上呼び出しするとｴﾗｰになります｡
	 *
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
	 *
	 * @param	tbl	配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 */
	/* default */ void setTable( final DataModel<String> tbl ) {
		if( table != null ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "既に配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙがｾｯﾄされています｡"
								+ " OLD:" + table
								+ " IN :" + tbl ;
			throw new OgRuntimeException( errMsg );
		}
		table = tbl;
	}

	/**
	 * 配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙを取得します｡
	 *
	 * @og.rev 6.7.9.1 (2017/05/19) 新規追加
	 *
	 * @return 配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 */
	protected DataModel<String> getTable() {
		return table ;
	}

	/**
	 * 固定値のｷｰ配列を指定します｡
	 * このﾒｿｯﾄﾞは､1度しかｾｯﾄすることができません｡2回以上呼び出しするとｴﾗｰになります｡
	 *
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 *
	 * @param	ks	ｷｰ配列(可変長引数)
	 */
	/* default */ void setKeys( final String... ks ) {
		if( keys != null ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "既に固定値配列(ｷｰ)がｾｯﾄされています｡" 	+ CR
							+ "   KESY   =" + Arrays.toString( keys )			+ CR
							+ "   in keys=" + Arrays.toString( ks ) ;
			throw new OgRuntimeException( errMsg );
		}
		if( ks != null && ks.length > 0 ) { keys = ks; }		// 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る｡
	}

	/**
	 * 固定値の値配列を指定します｡
	 * このﾒｿｯﾄﾞは､1度しかｾｯﾄすることができません｡2回以上呼び出しするとｴﾗｰになります｡
	 *
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 *
	 * @param	vs	値配列(可変長引数)
	 */
	/* default */ void setVals( final String... vs ) {
		if( vals != null ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "既に固定値配列(値)がｾｯﾄされています｡"	+ CR
							+ "   VALS   =" + Arrays.toString( vals )		+ CR
							+ "   in vals=" + Arrays.toString( vs ) ;
			throw new OgRuntimeException( errMsg );
		}
		if( vs != null && vs.length > 0 ) { vals = vs; }		// 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る｡
	}

	/**
	 * この処理の実行ﾕｰｻﾞｰIDを指定します｡
	 *
	 * @param	id	実行ﾕｰｻﾞｰID(not null)
	 */
	/* default */ void setUserId( final String id ) {
		variableMap.put( "CON.USERID", id);
	}

	/**
	 * 親(呼び出し)PGIDを指定します｡
	 *
	 * @param	id	親PGID
	 */
	/* default */ void setParentPgId( final String id ) {
		variableMap.put( "CON.PGPID", id );
	}

	/**
	 * ﾃﾞﾊﾞｯｸﾞﾓｰﾄﾞにします｡
	 */
	/* default */ void setDebug() {
		debugFlag = true;
	}

	/**
	 * ﾃﾞﾊﾞｯｸﾞﾒｯｾｰｼﾞを取得します｡
	 *
	 * @return	ﾃﾞﾊﾞｯｸﾞﾒｯｾｰｼﾞ
	 * @og.rtnNotNull
	 */
	/* default */ String getDebugMsg() {
		return debugMsg.toString();
	}

	/**
	 * 処理を実行します｡
	 * 処理の方法は､main()ﾒｿｯﾄﾞにより定義されます｡
	 * 実装ｸﾗｽで発生した全ての例外は､Throwableｵﾌﾞｼﾞｪｸﾄとしてｽﾛｰされます｡
	 * 呼び出し元では､例外を確実にcatchして､commit,rollbackを行ってください｡
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) ｼｰｹﾝｽ対応
	 *
	 * @return	処理が成功したかどうか
	 * @throws	Throwable 実行時の全ｴﾗｰを上位に転送します｡
	 */
	/* default */ boolean exec() throws Throwable {
		dbName = DBFunctionName.getDBName( ConnectionFactory.getDBName( dbid ) );
		makeParamMap();
		init();

		return main();
	}

	/**
	 * 処理のﾒｲﾝﾛｼﾞｯｸの前処理を記述します｡
	 *
	 * このﾒｿｯﾄﾞ自体は､protected属性であるため､ｻﾌﾞｸﾗｽから直接参照することができます｡
	 * 但し､これは､各業務ﾛｼﾞｯｸで直接参照することを想定したものではなく､BizLogicの
	 * ﾒｲﾝ構造を拡張するｻﾌﾞｸﾗｽを定義する際に使用することを想定しています｡
	 * (この想定がなければ､本来は､package privateにすべきです)
	 * このため､業務ﾛｼﾞｯｸを各実装ｸﾗｽでは直接参照しないで下さい｡
	 */
	protected abstract void init();

	/**
	 * 処理のﾒｲﾝﾛｼﾞｯｸを記述します｡
	 *
	 * このﾒｿｯﾄﾞ自体は､protected属性であるため､ｻﾌﾞｸﾗｽから直接参照することができます｡
	 * 但し､これは､各業務ﾛｼﾞｯｸで直接参照することを想定したものではなく､BizLogicの
	 * ﾒｲﾝ構造を拡張するｻﾌﾞｸﾗｽを定義する際に使用することを想定しています｡
	 * (この想定がなければ､本来は､package privateにすべきです)
	 * このため､業務ﾛｼﾞｯｸを各実装ｸﾗｽでは直接参照しないで下さい｡
	 *
	 * @return	処理が正常終了したか
	 */
	protected abstract boolean main();

	/**
	 * 結果ｽﾃｰﾀｽを返します｡
	 *
	 * @return	結果ｽﾃｰﾀｽ
	 */
	/* default */ int getKekka() {
		return errMsg.getKekka();
	}

	/**
	 * ｴﾗｰﾒｯｾｰｼﾞｵﾌﾞｼﾞｪｸﾄを返します｡
	 *
	 * @return	ｴﾗｰﾒｯｾｰｼﾞ
	 */
	/* default */ ErrorMessage getErrMsg() {
		return errMsg;
	}

	/**
	 * 業務ﾛｼﾞｯｸの戻り値を返します｡
	 *
	 * @return	戻り値
	 */
	/* default */ String getReturn() {
		return bizRtn;
	}

	/**
	 * 業務ﾛｼﾞｯｸを実行するために､ﾃｰﾌﾞﾙﾓﾃﾞﾙが外部からｾｯﾄされる必要があるか
	 * を返します｡
	 * 必須である場合､その業務ﾛｼﾞｯｸは､子ﾛｼﾞｯｸとして呼び出すことができません｡
	 * これは､子ﾛｼﾞｯｸ呼び出し時は､ﾃｰﾌﾞﾙﾓﾃﾞﾙがｾｯﾄされないためです｡
	 * (このｸﾗｽは､ﾃｰﾌﾞﾙﾓﾃﾞﾙが外部から指定されている必要はありません｡)
	 *
	 * このﾒｿｯﾄﾞ自体は､protected属性であるため､ｻﾌﾞｸﾗｽから直接参照することができます｡
	 * 但し､これは､各業務ﾛｼﾞｯｸで直接参照することを想定したものではなく､BizLogicの
	 * ﾒｲﾝ構造を拡張するｻﾌﾞｸﾗｽを定義する際に使用することを想定しています｡
	 * (この想定がなければ､本来は､package privateにすべきです)
	 * このため､業務ﾛｼﾞｯｸを各実装ｸﾗｽでは直接参照しないで下さい｡
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) protected → default 変更(ｻﾌﾞｸﾗｽからのｱｸｾｽはない)
	 *
	 * @return	ﾃｰﾌﾞﾙﾓﾃﾞﾙが外部からｾｯﾄされる必要があるかどうか(常にfalse)
	 */
//	protected boolean isRequireTable() {
	/* default */ boolean isRequireTable() {
		return false;
	}

	/**
	 * ﾃﾞﾊﾞｯｸﾞﾓｰﾄﾞかどうかを返します｡
	 *
	 * @return	ﾃﾞﾊﾞｯｸﾞﾓｰﾄﾞかどうか
	 */
	protected final boolean isDebug() {
		return debugFlag;
	}

	/**
	 * ﾃﾞﾊﾞｯｸﾞﾒｯｾｰｼﾞを追加します｡
	 *
	 * @param	msg	追加するﾃﾞﾊﾞｯｸﾞﾒｯｾｰｼﾞ
	 */
	protected final void debug( final String msg ) {
		debugMsg.append( msg ).append( CR );
	}

	/**
	 * 指定されたｷｰを大文字に変化後、値を返します｡
	 *
	 * @og.rev 8.3.0.1 (2022/08/12) 新規作成
	 *
	 * @param	key	ｷｰ
	 *
	 * @return	変数値
	 */
	private final String mapGet( final String key ) {
		if( key == null ) { return null; }
		else {
			return variableMap.get( key.toUpperCase(Locale.JAPAN) );
		}
	}

	/**
	 * 指定されたｷｰの値を返します｡
	 *
	 * @og.rev 8.3.0.1 (2022/08/12) keyを大文字変換
	 *
	 * @param	key	ｷｰ
	 *
	 * @return	変数値
	 */
	protected final String var( final String key ) {
//		return variableMap.get( key );
		return mapGet( key );
	}

	/**
	 * 指定されたｷｰの値を返します｡
	 *
	 * 値が､nullや空文字列の場合は､def引数の初期値を返します｡
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) 新規追加
	 * @og.rev 8.3.0.1 (2022/08/12) keyを大文字変換
	 *
	 * @param	key	ｷｰ
	 * @param	def	値が取得できなかった時の初期値
	 *
	 * @return	変数値
	 */
	protected final String var( final String key,final String def ) {
		// Map#getOrDefault( key,def ) ではなく､nval を使います｡
//		return StringUtil.nval( variableMap.get( key ) , def );
		return StringUtil.nval( mapGet( key ) , def );
	}

	/**
	 * 指定されたｷｰの値をint型に変換して返します｡
	 *
	 * 値が､nullや空文字列の場合は､def引数の初期値を返します｡
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) 新規追加
	 * @og.rev 8.3.0.1 (2022/08/12) keyを大文字変換
	 *
	 * @param	key	ｷｰ
	 * @param	def	値が取得できなかった時の初期値
	 *
	 * @return	変数値
	 */
	protected final int var( final String key,final int def ) {
		// Map#getOrDefault( key,def ) ではなく､nval を使います｡
//		return StringUtil.nval( variableMap.get( key ) , def );
		return StringUtil.nval( mapGet( key ) , def );
	}

	/**
	 * 指定されたｷｰの値をdouble型に変換して返します｡
	 *
	 * 値が､nullや空文字列の場合は､def引数の初期値を返します｡
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) 新規追加
	 * @og.rev 8.3.0.1 (2022/08/12) keyを大文字変換
	 *
	 * @param	key	ｷｰ
	 * @param	def	値が取得できなかった時の初期値
	 *
	 * @return	変数値
	 */
	protected final double var( final String key,final double def ) {
		// Map#getOrDefault( key,def ) ではなく､nval を使います｡
//		return StringUtil.nval( variableMap.get( key ) , def );
		return StringUtil.nval( mapGet( key ) , def );
	}

//	/**
//	 * 指定されたｷｰの値をint型に変換して返します｡
//	 *
//	 * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も､0 を返します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) 廃止 vari(String) → var(String,int)
//	 *
//	 * @param	key	ｷｰ
//	 *
//	 * @return	変数値
//	 */
//	protected final int vari( final String key ) {
//		return str2int( var( key ) );			// 6.7.9.1 (2017/05/19)
//	}

//	/**
//	 * 指定されたｷｰの値をdouble型に変換して返します｡
//	 *
//	 * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も､0 を返します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) 廃止 vari(String) → var(String,double)
//	 *
//	 * @param	key	ｷｰ
//	 *
//	 * @return	変数値
//	 */
//	protected final double vard( final String key ) {
//		return str2dbl( var( key ) );			// 6.7.9.1 (2017/05/19)
//	}

	/**
	 * ﾊﾟﾗﾒｰﾀｰのｷｰ一覧を配列形式で返します｡
	 * このﾊﾟﾗﾒｰﾀｰは､業務ﾛｼﾞｯｸ内でｾｯﾄされたﾊﾟﾗﾒｰﾀｰも含まれますのでご注意下さい｡
	 *
	 * @return	ﾊﾟﾗﾒｰﾀｰのｷｰ配列
	 */
	protected final String[] varKeys() {
		final Set<String> keys = variableMap.keySet();
		return keys.toArray( new String[keys.size()] );
	}

	/**
	 * 指定されたｷｰで値を登録します｡
	 * ﾊﾟﾗﾒｰﾀｰとしてこの業務ﾛｼﾞｯｸが呼ばれる際の引数となっている場合は､
	 * ｴﾗｰとなります｡
	 *
	 * @og.rev 5.2.1.0 (2010/10/01) ﾁｪｯｸのﾊﾞｸﾞを修正
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 * @og.rev 8.3.0.1 (2022/08/12) keyを大文字変換
	 *
	 * @param	key	ｷｰ
	 * @param	val	値
	 */
	protected final void set( final String key, final String val ) {
		// 6.0.2.5 (2014/10/31) 素直に､variableMap で､ｷｰ有無を判定する｡
//		if( variableMap.containsKey( key ) ) {
		if( mapGet( key ) != null ) {
			final String errMsg = "すでに登録済みのｷｰを定義することはできません｡"	+ CR
							+ "   key =" + key				+ CR
							+ "   val =" + val				+ CR
//							+ "   元  =" + variableMap.get( key ) ;
							+ "   元  =" + mapGet( key ) ;
			throw new OgRuntimeException( errMsg );
		}

//		variableMap.put( key, val );
		if( key != null ) {
			variableMap.put( key.toUpperCase(Locale.JAPAN), val );
		}
	}

	/**
	 * 指定されたｷｰで値(int型)を登録します｡
	 * ﾊﾟﾗﾒｰﾀｰとしてこの業務ﾛｼﾞｯｸが呼ばれる際の引数となっている場合は､
	 * ｴﾗｰとなります｡
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
	 *
	 * @param	key	ｷｰ
	 * @param	val	値
	 */
	protected final void set( final String key, final int val ) {
		set( key, String.valueOf( val ) );
	}

	/**
	 * 指定されたｷｰで値(double型)を登録します｡
	 * ﾊﾟﾗﾒｰﾀｰとしてこの業務ﾛｼﾞｯｸが呼ばれる際の引数となっている場合は､
	 * ｴﾗｰとなります｡
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
	 *
	 * @param	key	ｷｰ
	 * @param	val	値
	 */
	protected final void set( final String key, final double val ) {
		set( key, String.valueOf( val ) );
	}

//	/**
//	 * 処理中の行の指定されたｷｰ(ｶﾗﾑ名)の値を返します｡
//	 *
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	key	ｷｰ
//	 *
//	 * @return	値
//	 */
//	protected final String line( final String key ) {
//		return line( key, row );
//	}

//	/**
//	 * ﾒｲﾝの配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙに対して､行を指定して値を取得します｡
//	 * 指定された行が範囲を超えている場合は､nullを返します｡
//	 *
//	 * @og.rev 5.1.8.0 (2010/07/01) ﾃｰﾌﾞﾙに存在しないｶﾗﾑ名を指定した場合に､NullPointerExceptionが発生するﾊﾞｸﾞを修正
//	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	key	ｷｰ
//	 * @param	rw	行番号(ｲﾝﾃﾞｯｸｽ)
//	 *
//	 * @return	値
//	 */
//	protected final String line( final String key, final int rw ) {
//		if( table == null ) {
//			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
//			final String errMsg = "配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙがｾｯﾄされていないため､#line( String,int )ﾒｿｯﾄﾞはできません｡"	+ CR
//							+ "   line( " + key + "," + rw + " );"	+ CR ;
//			throw new OgRuntimeException( errMsg );
//		}
//		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
//
//		final int col = table.getColumnNo( key );
//
//		return col < 0 || rw < 0 || rw >= table.getRowCount() ? null : table.getValue( rw, col );
//	}

//	/**
//	 * 処理中の行の指定されたｶﾗﾑ番号の値を返します｡
//	 * line( String )は､毎回､ｶﾗﾑ番号を取得しているため､非効率です｡
//	 * ただし､一旦ｶﾗﾑ名から､ｶﾗﾑ番号を取得し､それを使用するのと､
//	 * linei(String)や､lined(String) などの直接的なﾒｿｯﾄﾞもないため､
//	 * 利用者側でそのあたりの処理を入れる必要があります｡
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) 文字列を整数に変換します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	col	ｶﾗﾑ番号
//	 * @return	値
//	 */
//	protected final String line( final int col ) {
//		return line( col, row );
//	}

//	/**
//	 * ﾒｲﾝの配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙに対して､行を指定して値を取得します｡
//	 * 指定された行が範囲を超えている場合は､nullを返します｡
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) 文字列を整数に変換します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	col	ｶﾗﾑ番号
//	 * @param	rw	行番号(ｲﾝﾃﾞｯｸｽ)
//	 * @return	値
//	 */
//	protected final String line( final int col, final int rw ) {
//		if( table == null ) {
//			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
//			final String errMsg = "配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙがｾｯﾄされていないため､#line( String,int )ﾒｿｯﾄﾞはできません｡"	+ CR
//							+ "   line( " + col + "," + rw + " );"	+ CR ;
//			throw new OgRuntimeException( errMsg );
//		}
//
//		return col < 0 || rw < 0 || rw >= table.getRowCount() ? null : table.getValue( rw, col );
//	}

//	/**
//	 * 処理中の行の指定されたｷｰ(ｶﾗﾑ名)の値をint型に変換して返します｡
//	 *
//	 * @og.rev 6.7.9.0 (2017/04/28) row を使用して､#linei( String,int )を呼びます｡
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	key	ｷｰ
//	 *
//	 * @return	値
//	 */
//	protected final int linei( final String key ) {
//		return str2int( line( key, row ) );			// 6.7.9.1 (2017/05/19)
//	}

//	/**
//	 * ﾒｲﾝの配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙに対して､行を指定して値をint型に変換して返します｡
//	 * 指定された行が範囲を超えている場合は､nullを返します｡
//	 *
//	 * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も､0 を返します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	key	ｷｰ
//	 * @param	rw	行番号(ｲﾝﾃﾞｯｸｽ)
//	 *
//	 * @return	値
//	 */
//	protected final int linei( final String key, final int rw ) {
//		return str2int( line( key, rw ) );			// 6.7.9.1 (2017/05/19)
//	}

//	/**
//	 * 処理中の行の指定されたｷｰ(ｶﾗﾑ名)の値をdouble型に変換して返します｡
//	 *
//	 * @og.rev 6.7.9.0 (2017/04/28) row を使用して､#lined( String,int )を呼びます｡
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	key	ｷｰ
//	 *
//	 * @return	値
//	 */
//	protected final double lined( final String key ) {
//		return str2dbl( line( key, row ) );			// 6.7.9.1 (2017/05/19)
//	}

//	/**
//	 * ﾒｲﾝの配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙに対して､行を指定して値をdouble型に変換して返します｡
//	 * 指定された行が範囲を超えている場合は､nullを返します｡
//	 *
//	 * @og.rev 6.7.9.0 (2017/04/28) nullと isEmpty() も､0 を返します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) lineﾒｿｯﾄﾞ廃止(いまいち､使い道がない)
//	 *
//	 * @param	key	ｷｰ
//	 * @param	rw	行番号(ｲﾝﾃﾞｯｸｽ)
//	 *
//	 * @return	値
//	 */
//	protected final double lined( final String key, final int rw ) {
//		return str2dbl( line( key, rw ) );			// 6.7.9.1 (2017/05/19)
//	}

	/**
	 * 指定のｶﾗﾑ名引数に相当するﾃﾞｰﾀを２重配列で返します｡
	 *
	 * @og.rev 6.8.5.0 (2018/01/09) 新規追加
	 *
	 * @param	clmNms	値が参照されるｶﾗﾑ名配列(可変長引数)
	 *
	 * @return	指定された名引数に相当するﾃﾞｰﾀの２重配列
	 * @og.rtnNotNull
	 */
	protected String[][] getValues( final String... clmNms ) {
		// 6.9.8.0 (2018/05/28) FindBugs:ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで null 値を利用している
		if( table == null ) {
			final String errMsg = "配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙがｾｯﾄされていないため､#getValues( String... )ﾒｿｯﾄﾞはできません｡"	+ CR
							+ "   clmNms= " + Arrays.toString( clmNms ) + " );"	+ CR ;
			throw new OgRuntimeException( errMsg );
		}

		return ((ArrayTableModel)table).getValues( clmNms );
	}

//	/**
//	 * 文字列を整数に変換します｡
//	 * 文字列が､nullか､空文字列の場合は､0 を返します｡
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) 文字列を整数に変換します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) str2intﾒｿｯﾄﾞ廃止(必要であれば､StringUtilを使用)
//	 *
//	 * @param	val	入力文字列
//	 * @return	int値
//	 */
//	protected final int str2int( final String val ) {
//		return val == null || val.isEmpty() ? 0 : Integer.parseInt( val );
//	}

//	/**
//	 * 文字列をdoubleに変換します｡
//	 * 文字列が､nullか､空文字列の場合は､0d を返します｡
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) 文字列をdoubleに変換します｡
//	 * @og.rev 8.0.2.0 (2021/11/30) str2dblﾒｿｯﾄﾞ廃止(必要であれば､StringUtilを使用)
//	 *
//	 * @param	val	入力文字列
//	 * @return	double値
//	 */
//	protected final double str2dbl( final String val ) {
//		return val == null || val.isEmpty() ? 0d : Double.parseDouble( val );
//	}

	/**
	 * 文字列配列をdouble配列に変換します｡
	 * 文字列が､nullか､空文字列の場合は､長さ０の配列を返します｡
	 *
	 * @og.rev 6.8.5.0 (2018/01/09) 新規追加
	 * @og.rev 8.0.2.0 (2021/11/30) StringUtil#nval(String.doubleを使用)
	 *
	 * @param	vals	double配列に変換する元の文字列配列
	 * @return	指定された文字列配列に対するdoubleに変換された値配列
	 * @og.rtnNotNull
	 */
	protected final double[][] str2dblVals( final String[][] vals ) {
		if( vals == null || vals.length == 0 || vals[0] == null || vals[0].length == 0 ) {
			return new double[0][0];
		}

		final int rowLen = vals.length;
		final int colLen = vals[0].length;

		final double[][] dbls = new double[rowLen][colLen];

		for( int row=0; row<rowLen; row++ ) {
			for( int col=0; col<colLen; col++ ) {
//				dbls[row][col] = str2dbl( vals[row][col] );
				dbls[row][col] = StringUtil.nval( vals[row][col],0d );	// 8.0.2.0 (2021/11/30)
			}
		}

		return dbls;
	}

	/**
	 * ﾃｰﾌﾞﾙのｶﾗﾑ名の一覧を配列形式で返します｡
	 *
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 * @og.rev 8.0.2.0 (2021/11/30) ﾒｿｯﾄﾞ名変更(lineKeys → getNames)
	 *
	 * @return	ﾃｰﾌﾞﾙのｶﾗﾑ名配列
	 */
//	protected final String[] lineKeys() {
	protected final String[] getNames() {
		if( table == null ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙがｾｯﾄされていないため､#lineKeys()ﾒｿｯﾄﾞはできません｡" ;
			throw new OgRuntimeException( errMsg );
		}
		else {
			return table.getNames();
		}
	}

//	/**
//	 * ﾃｰﾌﾞﾙにｶﾗﾑが存在しているかを返します｡
//	 *
//	 * @og.rev 5.2.0.0 (2010/09/01)
//	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
//	 * @og.rev 8.0.2.0 (2021/11/30) isLineﾒｿｯﾄﾞ廃止(必要なら､DataModel#getColumnNo(String) で判定する)
//	 *
//	 * @param	clm	ｶﾗﾑ名
//	 *
//	 * @return	存在している場合true､存在していない場合false
//	 */
//	protected final boolean isLine( final String clm ) {
//		if( table == null ) {
//			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
//			final String errMsg = "配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙがｾｯﾄされていないため､#isLine( String )ﾒｿｯﾄﾞはできません｡"	+ CR
//							+ "   isLine( " + clm + " );"	+ CR ;
//			throw new OgRuntimeException( errMsg );
//		}
//		return table.getColumnNo( clm ) >= 0 ;
//	}

	/**
	 * 業務ﾛｼﾞｯｸの戻り値をｾｯﾄします｡
	 *
	 * @param	rtn	戻り値
	 */
	protected final void rtn( final String rtn ) {
		bizRtn = rtn;
	}

//	/**
//	 * 子ﾛｼﾞｯｸを実行します｡
//	 * 実行する子ﾛｼﾞｯｸの呼び出しは､親ｸﾗｽと同じｿｰｽﾊﾟｽ､ｸﾗｽﾊﾟｽで呼び出しされます｡
//	 * 子ﾛｼﾞｯｸに渡す引数には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
//	 * また､子ﾛｼﾞｯｸの戻り値は､val("SUB_RETURN")で取得することができます｡
//	 *
//	 * @og.rev 8.0.2.0 (2021/11/30) 外部から､行番号とDataModelを渡すﾒｿｯﾄﾞは廃止｡
//	 *
//	 * @param	subLogicName	子ﾛｼﾞｯｸ名
//	 * @param	key				ｷｰ(CSV形式)
//	 * @param	val				値(CSV形式)
//	 *
//	 * @return	処理が正常終了したか
//	 */
//	protected final boolean call( final String subLogicName, final String key, final String val ) {
//		return call( subLogicName, key, val, row, table );
//	}

	/**
	 * 子ﾛｼﾞｯｸを実行します｡
	 * 実行する子ﾛｼﾞｯｸの呼び出しは､親ｸﾗｽと同じｿｰｽﾊﾟｽ､ｸﾗｽﾊﾟｽで呼び出しされます｡
	 * 子ﾛｼﾞｯｸに渡す引数には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
	 * この場合の値は､引数で指定された､配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙの行に対応する値になります｡
	 * また､子ﾛｼﾞｯｸの戻り値は､val("RETURN")で取得することができます｡
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) ｼｰｹﾝｽ対応
	 * @og.rev 5.4.1.0 (2011/11/01) 値にｶﾝﾏが含まれている場合に正しく動作しないﾊﾞｸﾞを修正
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 * @og.rev 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
	 * @og.rev 8.0.2.0 (2021/11/30) 外部から､行番号とDataModelを渡すﾒｿｯﾄﾞは廃止｡
	 *
	 * @param	subLogicName	子ﾛｼﾞｯｸ名
	 * @param	key				ｷｰ(CSV形式)
	 * @param	val				値(CSV形式)
//	 * @param	rw				行番号(ｲﾝﾃﾞｯｸｽ)
//	 * @param	tbl				配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 *
	 * @return	処理が正常終了したか
	 */
//	protected final boolean call( final String subLogicName, final String key, final String val, final int rw, final DataModel<String> tbl ) {
	protected final boolean call( final String subLogicName, final String key, final String val ) {
		// 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
		if( loader == null ) {
			final String errMsg = "#setLoader(HybsLoader)を先に実行しておいてください｡"	+ CR
							+ "   subLogicName =" + subLogicName	+ CR
							+ "   key =" + key	+ CR
							+ "   val =" + val	+ CR
							+ "   ArrayTableModel=" + table ;
			throw new OgRuntimeException( errMsg );
		}

		final AbstractBizLogic subLogic = (AbstractBizLogic)loader.newInstance( subLogicName );

		if( subLogic.isRequireTable() ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "このｸﾗｽは､外部からﾃｰﾌﾞﾙﾓﾃﾞﾙをｾｯﾄする必要があるため､子ﾛｼﾞｯｸとして呼び出すことはできません｡" + CR
							+ "  [ｸﾗｽ名=" + subLogic.getClass().getName() + "]"	+ CR
							+ "   subLogicName =" + subLogicName
							+ "   key =[" + key + "]"
							+ "   val =[" + val + "]" + CR ;
			throw new OgRuntimeException( errMsg );
		}

		subLogic.setTransaction( tran );
		subLogic.setLoader( loader );
		subLogic.setKeys( StringUtil.csv2Array( key ) );
		// 5.4.1.0 (2011/11/01) 値にｶﾝﾏが含まれている場合に正しく動作しないﾊﾞｸﾞを修正
		// 8.0.2.0 (2021/11/30) #replaceParam( String , int , DataModel ) 廃止に伴う処置
		final String[] vals = StringUtil.csv2Array( val );
		replaceParam( vals );			// 8.0.2.0 (2021/11/30) 配列内部を書き換えます｡
//		for( int i=0; i<vals.length; i++ ) {
//			vals[i] = replaceParam( vals[i], row, table );
//		}
		subLogic.setVals( vals );
		subLogic.setUserId( variableMap.get( "CON.USERID" ) );
		subLogic.setParentPgId( variableMap.get( "CON.PGID" ) );
		if( debugFlag ) {
			subLogic.setDebug();
		}

		final boolean rtn;						// 6.3.9.0 (2015/11/06) Found 'DU'-anomaly for variable(PMD)
		try {
			rtn = subLogic.exec();
		}
		catch( final Throwable th ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "子ﾛｼﾞｯｸの呼び出しでｴﾗｰが発生しました｡" + CR
							+ "   subLogicName =" + subLogicName  + CR
							+ "   key =[" + key + "]"
							+ "   val =[" + val + "]" + CR ;
			throw new OgRuntimeException( errMsg ,th );
		}
		variableMap.put( "RETURN", subLogic.getReturn() );

		if( debugFlag ) { debug( subLogic.getDebugMsg() ); }

		final ErrMsg[] errs = subLogic.getErrMsg().toArray();
		if( errs.length > 0 ) {
			final ErrorMessage errMsgTmp = new ErrorMessage();
			for( int i=0; i<errs.length; i++ ) {
				errMsgTmp.addMessage( errs[i].copy( row ) );
			}
			errMsg.append( errMsgTmp );
		}

		return rtn;
	}

//	/**
//	 * SQLを実行します｡
//	 * SQL文には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
//	 * select文を発行した場合､その結果ｾｯﾄは､var(ｶﾗﾑ名)で取得することができます｡
//	 * 2行以上が返された場合でも､1行目のみが登録されます｡
//	 * また､検索件数､更新件数については､var("SQL_ROWCOUNT")で取得することができます｡
//	 *
//	 * @og.rev 8.0.2.0 (2021/11/30) 外部から､行番号とDataModelを渡すﾒｿｯﾄﾞは廃止｡
//	 *
//	 * @param	sq	SQL文字列
//	 */
//	protected final void sql( final String sq ) {
//		sql( sq, row, table );
//	}

	/**
	 * SQLを実行します｡
	 * SQL文には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
	 * [XXXX]形式の変数の置き換えには､引数で指定された配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙの行が使用されます｡
	 * select文を発行した場合､その結果ｾｯﾄは､var(ｶﾗﾑ名)で取得することができます｡
	 * 2行以上が返された場合でも､1行目のみが登録されます｡
	 * また､検索件数､更新件数については､var("SQL_ROWCOUNT")で取得することができます｡
	 *
	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
	 * @og.rev 8.0.2.0 (2021/11/30) 外部から､行番号とDataModelを渡すﾒｿｯﾄﾞは廃止｡
	 *
	 * @param	sq	SQL文字列
//	 * @param	rw	行番号(ｲﾝﾃﾞｯｸｽ)
//	 * @param	tbl	配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 */
//	protected final void sql( final String sq, final int rw, final DataModel<String> tbl ) {
	protected final void sql( final String sq ) {
		final DataModel<String> tbl2 = execSQL( sq, row, table );

		if( tbl2 != null && tbl2.getRowCount() > 0 ) {
			final String[] names = tbl2.getNames();
			final String[] vals = tbl2.getValues( 0 );
			for( int i=0; i<names.length; i++ ) {
				variableMap.put( names[i], vals[i] );							// execSQLでnamesを大文字化済
			}
		}
	}

	/**
	 * ｼｰｹﾝｽ名よりｼｰｹﾝｽｵﾌﾞｼﾞｪｸﾄを検索し､次の値を取り出します｡
	 * DBに対するｼｰｹﾝｽｵﾌﾞｼﾞｪｸﾄは予め作成されている必要があります｡
	 *
	 * また､MySQLの場合は､ｼｰｹﾝｽｵﾌﾞｼﾞｪｸﾄが実装されていないため､
	 * 内部的には､引数のｼｰｹﾝｽ名と同じ名前のﾃｰﾌﾞﾙから､Integer型の
	 * "SEQID"という項目名を検索することにより､ｼｰｹﾝｽをｴﾐｭﾚｰﾄしています｡
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) 新規追加
	 * @og.rev 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
	 *
	 * @param	seqName	ｼｰｹﾝｽ名
	 *
	 * @return	ｼｰｹﾝｽ番号
	 * @see org.opengion.fukurou.db.DBFunctionName#getSequence(String,Transaction)
	 */
	protected final int seq( final String seqName ) {
		// 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
		if( dbName == null ) {
			final String errMsg = "#exec()を先に実行しておいてください｡"	+ CR
							+ "   seqName =" + seqName ;
			throw new OgRuntimeException( errMsg );
		}

		return dbName.getSequence( seqName, tran );
	}

	/**
	 * SQLを実行します｡
	 *
	 * @param	sq	SQL文字列
	 * @param	rw	行番号(ｲﾝﾃﾞｯｸｽ)
	 * @param	tbl	配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 *
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 *
	 * @return	結果ｾｯﾄ(配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ)
	 *
	 * @og.rev 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す｡(PostgreSQL対応)
	 * @og.rev 5.1.8.0 (2010/07/01) column名は大文字化し､項目名の取得は#getColumnLabel()で行う｡(PotgreSQL対応&ﾊﾞｸﾞ修正)
	 * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData を ConnectionFactory経由で取得｡(PostgreSQL対応)､setNull 対応
	 * @og.rev 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
	 * @og.rev 6.4.2.1 (2016/02/05) try-with-resources 文で記述｡
	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
	 * @og.rev 6.9.3.0 (2018/03/26) ﾐｽ修正(検索件数のところを､ﾌｪｯﾁ件数を取得していた)
	 * @og.rev 6.9.3.0 (2018/03/26) ﾃﾞｰﾀ検索時のﾌｪｯﾁｻｲｽﾞを設定｡
	 */
	private DataModel<String> execSQL( final String sq, final int rw, final DataModel<String> tbl ) {
		// 6.3.9.0 (2015/11/06) ｺﾝｽﾄﾗｸﾀで初期化されていないﾌｨｰﾙﾄﾞを null ﾁｪｯｸなしで利用している(findbugs)
		if( conn == null ) {
			final String errMsg = "#setTransaction(Transaction)を先に実行しておいてください｡"	+ CR
							+ "   sql =" + sq		+ CR
							+ "   ArrayTableModel=" + tbl ;
			throw new OgRuntimeException( errMsg );
		}

		String sql = replaceParam( sq, false ); // [XXXX]の変換はここでは行わない｡
		Formatter format = null ;
		if( tbl != null && sql.indexOf( '[' ) >= 0 ) {
			format = getFormatter( sql, tbl );
			sql = format.getQueryFormatString();
		}

		DataModel<String>	tbl2	= null;
		// 6.4.2.1 (2016/02/05) try-with-resources 文
		try( PreparedStatement pstmt = conn.prepareStatement( sql ) ) {
			if( tbl != null && format != null ) {
				final int[] clmNo = format.getClmNos();

				// 5.1.2.0 (2010/01/01) setObject に ParameterMetaData の getParameterType を渡す｡(PostgreSQL対応)
				if( useParamMetaData ) {
					final ParameterMetaData pMeta = pstmt.getParameterMetaData();
					for( int i=0; i<clmNo.length; i++ ) {
						final int type = pMeta.getParameterType( i+1 );
						// 5.3.8.0 (2011/08/01) setNull 対応
						final String val = tbl.getValue( rw, clmNo[i] );
						if( val == null || val.isEmpty() ) {
							pstmt.setNull( i+1, type );
						}
						else {
							pstmt.setObject( i+1, val, type );
						}
					}
				}
				else {
					for( int i=0; i<clmNo.length; i++ ) {
						pstmt.setObject( i+1, tbl.getValue( rw, clmNo[i] ) );
					}
				}
			}
			final boolean status = pstmt.execute();
			// 6.4.2.1 (2016/02/05) try-with-resources 文
			try( ResultSet result = pstmt.getResultSet() ) {
				if( status ) {
					result.setFetchSize( DB_FETCH_SIZE );				// 6.9.3.0 (2018/03/26) ﾃﾞｰﾀ検索時のﾌｪｯﾁｻｲｽﾞ

					final ResultSetMetaData metaData = result.getMetaData();
					final int cols = metaData.getColumnCount();

					String[] names = new String[cols];
					for( int i=0; i<cols; i++ ) {
						// 5.1.8.0 (2010/07/01) column名は大文字化し､項目名の取得は#getColumnLabel()で行う｡(PotgreSQL対応&ﾊﾞｸﾞ修正)
						names[i] = metaData.getColumnLabel( i+1 ).toUpperCase( Locale.JAPAN );
					}

					final String[][] tblVals = DBUtil.resultToArray( result, false );
					tbl2 = new ArrayTableModel( names, tblVals );

//					variableMap.put( "SQL_ROWCOUNT", String.valueOf( pstmt.getFetchSize() ) );
					variableMap.put( "SQL_ROWCOUNT", String.valueOf( tbl2.getRowCount() ) );			// 6.9.3.0 (2018/03/26) ﾐｽ修正
				}
				else {
					variableMap.put( "SQL_ROWCOUNT", String.valueOf( pstmt.getUpdateCount() ) );
				}
			}
		}
		catch( final SQLException ex ) {		// catch は､close() されてから呼ばれます｡
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙの生成に失敗しました｡"	+ CR
							+ "   sql =" + sql		+ CR
							+ "   ArrayTableModel=" + tbl ;
			throw new OgRuntimeException( errMsg,ex );
		}
		return tbl2;
	}

	/**
	 * 検索用SQLを実行して、Consumer#accept に検索結果の配列を繰り返しセットします｡
	 *
	 * 内部でResultSetValue のカーソルを回すため、大量のデータについて
	 * 順次処理する場合に使用します。
	 *
	 * @og.rev 8.2.0.3 (2022/06/30) 内部でカーソルを回す検索用SQLを実行する
	 * @og.rev 8.2.1.0 (2022/07/15) querySQL では、[XXXX]の変換 自体を行わない
	 *
	 * @param	sq		SQL文字列
	 * @param	call	CallBack関数(Consumer)
	 */
	protected void querySQL( final String sq , final Consumer<String[]> call ) {
		if( conn == null ) {
			final String errMsg = "#setTransaction(Transaction)を先に実行しておいてください｡"	+ CR
							+ "   sql =" + sq		+ CR ;
			throw new OgRuntimeException( errMsg );
		}

		final String sql = replaceParam( sq, false ); // [XXXX]の変換はここでは行わない｡
//		8.2.1.0 (2022/07/15) querySQL では、[XXXX]の変換 自体を行わない
//		Formatter format = null ;
//		if( table != null && sql.indexOf( '[' ) >= 0 ) {
//			format = getFormatter( sql, table );
//			sql = format.getQueryFormatString();
//		}

		try( PreparedStatement pstmt = conn.prepareStatement( sql ) ) {
			try( ResultSet result = pstmt.executeQuery() ) {
	//			result.setFetchSize( DB_FETCH_SIZE );
				try( ResultSetValue rsv = new ResultSetValue( result ) ) {
					while( rsv.next() ) {
						call.accept( rsv.getValues() );
					}
				}
			}
		}
		catch( final SQLException ex ) {		// catch は､close() されてから呼ばれます｡
			final String errMsg = "検索系SQL の実行に生成に失敗しました｡"	+ CR
							+ "   sql =" + sql		+ CR ;
			throw new OgRuntimeException( errMsg,ex );
		}
	}

	/**
	 * ｴﾗｰﾒｯｾｰｼﾞを追加します｡
	 * ｴﾗｰﾒｯｾｰｼﾞの引数には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
	 *
	 * @param	kekka	ｴﾗｰﾚﾍﾞﾙ
	 * @param	id		ｴﾗｰﾒｯｾｰｼﾞID
	 * @param	args	ｴﾗｰﾒｯｾｰｼﾞﾊﾟﾗﾒｰﾀｰ
	 */
	protected final void error( final int kekka, final String id, final String... args ) {
		error( row, kekka, id, args );
	}

	/**
	 * 行指定でｴﾗｰﾒｯｾｰｼﾞを追加します｡
	 * ｴﾗｰﾒｯｾｰｼﾞの引数には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
	 *
	 * @param	rw		行番号(ｲﾝﾃﾞｯｸｽ)
	 * @param	kekka	ｴﾗｰﾚﾍﾞﾙ
	 * @param	id		ｴﾗｰﾒｯｾｰｼﾞID
	 * @param	args	ｴﾗｰﾒｯｾｰｼﾞﾊﾟﾗﾒｰﾀｰ
	 */
	protected final void error( final int rw, final int kekka, final String id, final String... args ) {
		errMsg.addMessage( rw, kekka, id, replaceParam( args ) );
	}

	/**
	 * ﾊﾟﾗﾒｰﾀｰの必須ﾁｪｯｸを行います｡
	 * ｷｰは､CSV形式で複数指定することができます｡
	 *
	 * @og.rev 8.3.0.1 (2022/08/12) keyを大文字変換
	 *
	 * @param	cs	ｶﾗﾑ(CSV形式)
	 *
	 * @return	ｴﾗｰが発生した場合はfalse､それ以外はtrue
	 */
	protected final boolean must( final String cs ) {
		if( cs == null || cs.isEmpty() ) {
			return true;
		}

		final String[] clms = StringUtil.csv2Array( cs );
		for( int i=0; i<clms.length; i++ ) {
//			final String val = variableMap.get( clms[i] );						// 8.3.0.1 (2022/08/12)
			final String val = mapGet( clms[i] );
			if( val == null || val.isEmpty() ) {
//				error( 2, "ERR0012", "{#" + clms[i] + "}" );
				error( NG, "ERR0012", "{#" + clms[i] + "}" );					// 7.2.9.5 (2020/11/28)
				return false ;
			}
		}
		return true;
	}

	/**
	 * ﾏｽﾀﾁｪｯｸを行います｡
	 *
	 * @og.rev 5.6.3.1 (2013/04/05) isErrThrow 引数を追加
	 *
	 * @see #exist(String, String, String, String, String, String)
	 * @param	type	ｴﾗｰﾁｪｯｸのﾀｲﾌﾟ
	 * @param	tblId	ﾃｰﾌﾞﾙ名
	 * @param	ns		ｶﾗﾑ(CSV形式)
	 * @param	vs		値(CSV形式)
	 *
	 * @return	ｴﾗｰが発生した場合はfalse､それ以外はtrue
	 */
	protected final boolean exist( final String type, final String tblId, final String ns, final String vs ) {
		return exist( type, tblId, ns, vs, null, null,true );
	}

	/**
	 * ﾏｽﾀﾁｪｯｸを行います｡
	 *
	 * 引数に指定されたﾃｰﾌﾞﾙ名､及び条件句を生成するためのｶﾗﾑ､値から
	 * 件数を取得し､typeに応じて件数ﾁｪｯｸを行います｡
	 * (ｶﾗﾑ､値には､CSV形式で複数指定することができます)
	 *  type=true  存在する場合true  存在しない場合false
	 *  type=false 存在する場合false 存在しない場合true
	 *  type=one   1件以内    true  2件以上     false
	 *
	 * 必須ﾁｪｯｸの引数には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
	 *
	 * また､固定値ｶﾗﾑ､値にも条件となるｶﾗﾑ及び値を指定することができますが､
	 * ここで指定されたｶﾗﾑは､ｴﾗｰﾒｯｾｰｼﾞ表示時にｶﾗﾑ､値が画面に表示されません｡
	 *
	 * @og.rev 5.6.3.1 (2013/04/05) isErrThrow 引数を追加
	 *
	 * @param	type	ｴﾗｰﾁｪｯｸのﾀｲﾌﾟ
	 * @param	tblId	ﾃｰﾌﾞﾙ名
	 * @param	ns		ｶﾗﾑ(CSV形式)
	 * @param	vs		値(CSV形式)
	 * @param	conNs	固定値ｶﾗﾑ(CSV形式)
	 * @param	conVs	固定値(CSV形式)
	 *
	 * @return	ｴﾗｰが発生した場合はfalse､それ以外はtrue
	 */
	protected final boolean exist( final String type, final String tblId
			, final String ns, final String vs, final String conNs, final String conVs ) {
		return exist( type, tblId, ns, vs, conNs, conVs,true );
	}

	/**
	 * ﾏｽﾀﾁｪｯｸを行います｡
	 * 引数に指定されたﾃｰﾌﾞﾙ名､及び条件句を生成するためのｶﾗﾑ､値から
	 * 件数を取得し､typeに応じて件数ﾁｪｯｸを行います｡
	 * (ｶﾗﾑ､値には､CSV形式で複数指定することができます)
	 *  type=true  存在する場合true  存在しない場合false
	 *  type=false 存在する場合false 存在しない場合true
	 *  type=one   1件以内    true  2件以上     false
	 *
	 * 必須ﾁｪｯｸの引数には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
	 *
	 * また､固定値ｶﾗﾑ､値にも条件となるｶﾗﾑ及び値を指定することができますが､
	 * ここで指定されたｶﾗﾑは､ｴﾗｰﾒｯｾｰｼﾞ表示時にｶﾗﾑ､値が画面に表示されません｡
	 *
	 * isErrThrow は､ｴﾗｰが発生した場合に､ｴﾗｰﾒｯｾｰｼﾞ（ErrorMessage）に書き込むかどうかを指定します｡
	 * 基本は､互換性を考慮し､true(書き込む)です｡
	 * false にするｹｰｽは､存在ﾁｪｯｸを行い､あれば更新､なければ追加 など後続処理を行いたい場合に使います｡
	 *
	 * @og.rev 5.6.3.1 (2013/04/05) isErrThrow 引数を追加
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
	 *
	 * @param	type		ｴﾗｰﾁｪｯｸのﾀｲﾌﾟ
	 * @param	tblId		ﾃｰﾌﾞﾙ名
	 * @param	ns			ｶﾗﾑ(CSV形式)
	 * @param	vs			値(CSV形式)
	 * @param	conNs		固定値ｶﾗﾑ(CSV形式)
	 * @param	conVs		固定値(CSV形式)
	 * @param	isErrThrow	判定結果がfalseの場合に､error関数を呼ぶ場合は､true｡呼ばない場合は､falseをｾｯﾄします｡
	 *
	 * @return	ｴﾗｰが発生した場合はfalse､それ以外はtrue
	 */
	protected final boolean exist( final String type, final String tblId
			, final String ns, final String vs, final String conNs, final String conVs, final boolean isErrThrow ) {
		if( ns == null || ns.isEmpty() || vs == null || vs.isEmpty() ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "ｶﾗﾑ又は､値にnullは指定できません｡"	+ CR
							+ "   ns =[" + ns + "]"
							+ "   vs =[" + vs + "]" ;
			throw new OgRuntimeException( errMsg );
		}

		final String namesStr   = ns + ( conNs == null || conNs.isEmpty() ? "" : "," + conNs );
		final String[] namesArr = StringUtil.csv2Array( namesStr );
		final String valsStr    = vs + ( conVs == null || conVs.isEmpty() ? "" : "," + conVs );
		final String[] valsArr  = StringUtil.csv2Array( valsStr );
		if( namesArr.length != valsArr.length ) {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "ｶﾗﾑと値の個数が異なります｡"			+ CR
							+ "   names = [" + namesStr	+ "]"	+ CR
							+ "   vals  = [" + valsStr	+ "]";
			throw new OgRuntimeException( errMsg );
		}

		final StringBuilder sb = new StringBuilder( BUFFER_MIDDLE );
		sb.append( "select count(*) CNT from " ).append( tblId );
		for( int i=0 ;i<namesArr.length; i++ ) {
			if( i==0 )	{ sb.append( " where " ); }
			else 		{ sb.append( " and " ); }
			sb.append( namesArr[i] ).append( " = " ).append( valsArr[i] );
		}

		int count = 0;
		final DataModel<String> tbl2 = execSQL( sb.toString(), row, table );		// 6.7.9.1 (2017/05/19)
		if( tbl2 != null && tbl2.getRowCount() >= 0 ) {
			count = Integer.parseInt( tbl2.getValues( 0 )[0] );			// 6.0.2.4 (2014/10/17) ﾒｿｯﾄﾞ間違い
		}

		final String repVals = replaceParam( vs );
		if( "true".equalsIgnoreCase( type ) ) {
			// ERR0025=ﾃﾞｰﾀ未登録ｴﾗｰ｡ｷｰ={0}､値={1} のﾃﾞｰﾀは､存在していません｡
			if( count <= 0 ) {
				if( isErrThrow ) { error( NG, "ERR0025", "{#" + ns + "}", repVals ); }	// 5.6.3.1 (2013/04/05)
				return false;
			}
		}
		else if( "false".equalsIgnoreCase( type ) ) {
			// ERR0026=ﾃﾞｰﾀ登録済みｴﾗｰ｡ｷｰ={0}､値={1} のﾃﾞｰﾀは､すでに存在しています｡
			if( count > 0 ) {
				if( isErrThrow ) { error( NG, "ERR0026", "{#" + ns + "}", repVals ); }	// 5.6.3.1 (2013/04/05)
				return false;
			}
		}
		else if( "one".equalsIgnoreCase( type ) ) {
			// ERR0027=ﾃﾞｰﾀ２重登録ｴﾗｰ｡ｷｰ={0}､値={1} のﾃﾞｰﾀは､重複して存在しています｡
			if( count > 1 ) {
				if( isErrThrow ) { error( NG, "ERR0027", "{#" + ns + "}", repVals ); }	// 5.6.3.1 (2013/04/05)
				return false;
			}
		}
		else {
			// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
			final String errMsg = "typeは､true､false､oneのいずれかで指定する必要があります｡"	+ CR
							+ "   type = [" + type	+ "]";
			throw new OgRuntimeException( errMsg );
		}
		return true;
	}

	/**
	 * 引数に指定されたｷｰ､値をﾏｯﾌﾟ形式に変換します｡
	 *
	 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します｡
	 * @og.rev 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
	 * @og.rev 8.3.0.1 (2022/08/12) keyを大文字変換
	 */
	private void makeParamMap() {
		if( keys != null && vals != null ) {
			if( keys.length == vals.length ) {
				for( int i=0; i<keys.length; i++ ) {
//					variableMap.put( keys[i], vals[i] );
					if( keys[i] != null ) {
						variableMap.put( keys[i].toUpperCase(Locale.JAPAN), vals[i] );	// 8.3.0.1 (2022/08/12)
					}
				}
			}
			else {
				// 5.6.7.0 (2013/07/27) Exception を throw するとき､一旦､errMsg 変数にｾｯﾄします｡
				final String errMsg = "keysとvalsの個数が異なります｡"		+ CR
							+ "   keys   =" + Arrays.toString( keys )		+ CR
							+ "   vals   =" + Arrays.toString( vals ) ;
				throw new OgRuntimeException( errMsg );
			}
		}

		final String ymdh = DateSet.getDate( "yyyyMMddHHmmss" );		// 5.5.7.2 (2012/10/09) HybsDateUtil を利用
		variableMap.put( "CON.YMDH", ymdh );
		variableMap.put( "CON.YMD", ymdh.substring( 0,8 ) );
		variableMap.put( "CON.HMS", ymdh.substring( 8 ) );

		variableMap.put( "CON.PGID", this.getClass().getSimpleName() );
	}

	/**
	 * {&#064;XXXX}形式及び[XXXX]形式の文字列配列の置き換えを行います｡
	 *
	 * @og.rev 6.2.2.0 (2015/03/27) #replaceParam( String[] , int , ArrayTableModel ) 廃止に伴う処置
	 * @og.rev 8.0.2.0 (2021/11/30) #replaceParam( String , int , DataModel ) 廃止に伴う処置
	 *
	 * @param	str	置き換え対象の配列
	 *
	 * @return	置き換え結果の文字列(引数配列の内部を書き換えます)
	 */
	private String[] replaceParam( final String[] str ) {
		for( int i=0; i<str.length; i++ ) {
//			str[i] = replaceParam( str[i], row, table );
			str[i] = replaceParam( str[i], true );			// [XXXX]の変換を行う｡
		}
		return str;
	}

	/**
	 * {&#064;XXXX}形式及び[XXXX]形式の文字列の置き換えを行います｡
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) #replaceParam( String , int , DataModel ) 廃止に伴う処置
	 *
	 * @param	str	置き換え対象の文字列
	 *
	 * @return	置き換え結果の文字列
	 */
	private String replaceParam( final String str ) {
//		return replaceParam( str, row, table );
		return replaceParam( str, true );					// [XXXX]の変換を行う｡
	}

//	/**
//	 * {&#064;XXXX}形式及び[XXXX]形式の文字列の置き換えを行います｡
//	 * isRepTableにfalseを指定した場合､Formatterによる[XXXX]変換は行われません｡
//	 * (SQLの変換の場合は､PreparedStatementで処理させるため､[XXXX]の変換は行わない｡)
//	 *
//	 * @og.rev 8.0.2.0 (2021/11/30) #replaceParam( String , int , DataModel ) 廃止に伴う処置
//	 *
//	 * @param	str			置き換え対象の文字列
//	 * @param	isRepTable	Formatterによる[XXXX]変換を行うか
//	 *
//	 * @return	置き換え結果の文字列
//	 */
//	private String replaceParam( final String str, final boolean isRepTable ) {
//		return isRepTable ? replaceParam( str, row, table) : replaceParam( str, 0, null ) ;
//	}

	/**
	 * {&#064;XXXX}形式及び[XXXX]形式の文字列の置き換えを行います｡
	 * [XXXX]形式の置き換えには､引数で指定された配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ､行番号(ｲﾝﾃﾞｯｸｽ)を使用します｡
	 *
	 * @og.rev 5.1.8.0 (2010/07/01) 引数ﾁｪｯｸ漏れ対応
	 * @og.rev 5.3.9.0 (2011/09/01) nullが連続する場合にｾﾞﾛｽﾄﾘﾝｸﾞに置き換えられないﾊﾞｸﾞを修正
	 * @og.rev 6.4.3.2 (2016/02/19) Formatterを､値が null の場合は､ｾﾞﾛ文字列を設定する｡
	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
	 * @og.rev 8.0.2.0 (2021/11/30) #replaceParam( String , int , DataModel ) 廃止に伴う処置
	 *
	 * @param	str			置き換え対象の文字列
	 * @param	isRepTable	Formatterによる[XXXX]変換を行うか
//	 * @param	rw			行番号(ｲﾝﾃﾞｯｸｽ)
//	 * @param	tbl			配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 *
	 * @return	置き換え結果の文字列
	 */
//	private String replaceParam( final String str, final int rw, final DataModel<String> tbl ) {
	private String replaceParam( final String str, final boolean isRepTable ) {
		// 5.1.8.0 (2010/07/01) 引数ﾁｪｯｸ漏れ対応
		if( str == null || str.isEmpty() ) { return ""; }

		String rtn = str;

		// {@XXXX}の変換
		if( !variableMap.isEmpty() && rtn.indexOf( "{@" ) >= 0 ) {		// 6.1.1.0 (2015/01/17) refactoring
			final SystemParameter sysParam = getSysParam( rtn );
			rtn = sysParam.replace( variableMap );
		}

		// [XXXX]の変換
//		if( tbl != null && rtn.indexOf( '[' ) >= 0 ) {
		if( isRepTable && rtn.indexOf( '[' ) >= 0 ) {
			final Formatter format = getFormatter( rtn, table );
			rtn = format.getFormatString( row );
		}

		return rtn;
	}

	/**
	 * [XXXX]変換を行うためのFormatterを取得します｡
	 *
	 * @og.rev 6.4.3.4 (2016/03/11) Formatterに新しいｺﾝｽﾄﾗｸﾀｰを追加する｡
	 * @og.rev 6.4.3.4 (2016/03/11) Map#computeIfAbsent で対応する｡
	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
	 *
	 * @param	str	変換文字列
	 * @param	tbl	配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
	 *
	 * @return	Formatterｵﾌﾞｼﾞｪｸﾄ
	 */
	private Formatter getFormatter( final String str, final DataModel<String> tbl ) {
		// Map#computeIfAbsent ： 戻り値は､既存の､または計算された値｡追加有り､置換なし､削除なし
		final String key = str + tbl.toString();
		return formatMap.computeIfAbsent( key , k -> new Formatter( tbl,str ) );
	}

	/**
	 * {&#064;XXXX}変換を行うためのSystemParameterｵﾌﾞｼﾞｪｸﾄを取得します｡
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のﾁｪｯｸ追加
	 *
	 * @param	str	変換文字列
	 *
	 * @return	SystemParameterｵﾌﾞｼﾞｪｸﾄ
	 */
	private SystemParameter getSysParam( final String str ) {
		// 6.4.3.3 (2016/03/04) ｷｰが null のときも､SystemParameter ｵﾌﾞｼﾞｪｸﾄを構築しているので､
		// それも合わせて､Mapで管理するようにします｡
		final String key = str == null ? "NULL" : str ;
		// Map#computeIfAbsent ： 戻り値は､既存の､または計算された値｡追加有り､置換なし､削除なし
		return sysParamMap.computeIfAbsent( key , k -> new SystemParameter( k ) );
	}

//	/**
//	 * 検索SQLを実行し､結果を配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙとして返します｡
//	 * SQL文には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
//	 * また､検索件数については､var("SQL_ROWCOUNT")で取得することができます｡
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
//	 * @og.rev 8.0.2.0 (2021/11/30) BizLogic_CURSOR で使用しているだけなので､廃止｡
//	 *
//	 * @param	sq	SQL文
//	 *
//	 * @return	配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
//	 */
//	protected final DataModel<String> createTableBySql( final String sq ) {
////		return createTableBySql( sq, row, table );
//		return execSQL( sq, row, table );
//	}

//	/**
//	 * 検索SQLを実行し､結果を配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙとして返します｡
//	 * SQL文には､{&#064;XXXX}形式及び[XXXX]形式の変数を使用することができます｡
//	 * [XXXX]形式の変数の置き換えには､引数で指定された配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙの行が使用されます｡
//	 * また､検索件数については､var("SQL_ROWCOUNT")で取得することができます｡
//	 *
//	 * @og.rev 6.7.9.1 (2017/05/19) ArrayTableModel をDataModel に変更｡
//	 * @og.rev 8.0.2.0 (2021/11/30) 外部から､行番号とDataModelを渡すﾒｿｯﾄﾞは廃止｡
//	 *
//	 * @param	sq	SQL文
//	 * @param	rw	行番号(ｲﾝﾃﾞｯｸｽ)
//	 * @param	tbl	配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
//	 *
//	 * @return	配列型ﾃｰﾌﾞﾙﾓﾃﾞﾙ
//	 */
//	protected final DataModel<String> createTableBySql( final String sq, final int rw, final DataModel<String> tbl ) {
//		return execSQL( sq, rw, tbl );
//	}

	/**
	 * 変数に関連付けた値を､返します｡
	 * これは､BizLogicから､呼び出し元のJSPに､RETURN 変数以外の {&#064;XXXX} ﾊﾟﾗﾒｰﾀを返します｡
	 * 既存のｱﾄﾘﾋﾞｭｰﾄがあれば､上書きされます｡
	 *
	 * @og.rev 6.9.9.0 (2018/08/20) 戻り値を返せるようにします｡
	 *
	 * @param	key	ｷｰ
	 * @param	val	値
	 *
	 */
	protected final void setRtnMap( final String key, final String val ) {
		if( key != null && val != null ) {			// ConcurrentMap なので｡
			rtnMap.put( key, val );
		}
	}

	/**
	 * 変数に関連付けた値を､返します｡
	 * これは､BizLogicから､呼び出し元のJSPに､RETURN 変数以外の {&#064;XXXX} ﾊﾟﾗﾒｰﾀを返します｡
	 * 既存のｱﾄﾘﾋﾞｭｰﾄがあれば､上書きされます｡
	 *
	 * @og.rev 6.9.9.0 (2018/08/20) 戻り値を返せるようにします｡
	 *
	 * @return	内部ﾏｯﾌﾟｵﾌﾞｼﾞｪｸﾄ
	 */
	protected final Map<String,String> getReturnMap() {
		return rtnMap;
	}
}
