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

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.DBConstValue;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.ToString;										// 6.1.1.0 (2015/01/17)

import static org.opengion.fukurou.util.StringUtil.nval;

import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Locale;														// 6.4.1.2 (2016/01/22)

/**
 * TableUpdateTag にﾊﾟﾗﾒｰﾀｰを渡す為のﾀｸﾞｸﾗｽです｡
 *
 * 汎用的なﾃﾞｰﾀﾍﾞｰｽ登録処理を行えるﾀｸﾞ tableUpdate ﾀｸﾞを新規作成します｡
 * これは、具体的なSLQを作成する tableUpdateParam ﾀｸﾞと組み合わせて使用できます｡
 * tableUpdate ﾀｸﾞは、queryType に JDBCTableUpdate を指定します｡基本的にこれだけ
 * です｡tableUpdateParam では、sqlType に、INSERT,COPY,UPDATE,MODIFY,DELETE の
 * どれかを指定する事で、SQL文のﾀｲﾌﾟを指定します｡COPY,MODIFY は command と
 * 関連を持たす為に追加しているﾀｲﾌﾟで、UPDATE,INSERT と同じ処理を行います｡
 * tableUpdateParam の table には、作成したい SQL のﾃｰﾌﾞﾙを指定します｡
 * where 属性は、検索結果の DBTableModel の更新時に使用する条件を指定します｡
 *
 * @og.formSample
 * ●形式：&lt;og:tableUpdate command="{&#064;command}" queryType="JDBCTableUpdate" &gt;
 *            &lt;og:tableUpdateParam
 *                sqlType       = "{&#064;sqlType}"       // INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
 *                table         = "{&#064;TABLE_NAME}"    // 処理対象のﾃｰﾌﾞﾙ名
 *                names         = "{&#064;names}"         // 処理対象のｶﾗﾑ名
 *                omitNames     = "{&#064;omitNames}"     // 処理対象外のｶﾗﾑ名
 *                where         = "{&#064;where}"         // 処理対象を特定するｷｰ
 *                whereNames    = "{&#064;whereNames}"    // 処理対象を特定するｷｰ条件(where句)をCSV形式
 *                constKeys     = "{&#064;constKeys}"     // 処理ｶﾗﾑ名の中の固定情報ｶﾗﾑ名
 *                constVals     = "{&#064;constVals}"     // 処理ｶﾗﾑ名の中の固定情報設定値
 *                asNames       = "{&#064;asNames}"       // 別名を付けたｶﾗﾑ名(select A as B from TBL の B を指定)
 *                orgNames      = "{&#064;orgNames}"      // tableの実際のｶﾗﾑ名(select A as B from TBL の A を指定)
 *                funcKeys      = "{&#064;funcKeys}"      // 関数等を設定するｶﾗﾑ名
 *                funcVals      = "{&#064;funcVals}"      // 関数等の設定値
 *                logicalDelete = "{&#064;logicalDelete}" // sqlTypeがDELETEの場合にもUPDATE文を発行
 *            /&gt;
 *         &lt;/og:tableUpdate&gt;
 *
 * ●body：なし
 *
 * ●Tag定義：
 *   &lt;og:tableUpdateParam
 *       sqlType          ○【TAG】BODY部に書かれている SQLﾀｲﾌﾟを指定します(INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE)(必須)
 *       table            ○【TAG】処理対象のﾃｰﾌﾞﾙ名を指定します(必須)
 *       names              【TAG】処理対象のｶﾗﾑ名をCSV形式で複数指定します
 *       omitNames          【TAG】処理対象外のｶﾗﾑ名をCSV形式で複数指定します
 *       where              【TAG】処理対象を特定するｷｰ条件(where句)を指定します
 *       whereNames         【TAG】処理対象を特定するｷｰ条件(where句)をCSV形式で複数指定します
 *       insertOnly         【TAG】true に設定すると、sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない(初期値:false) 7.4.1.0 (2021/04/23)
 *       constKeys          【TAG】設定値を固定値と置き換える対象となるｶﾗﾑ名をCSV形式で複数指定します
 *       constVals          【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します
 *       funcKeys           【TAG】関数等を設定するｶﾗﾑ名をCSV形式で複数指定します
 *       funcVals           【TAG】関数等の設定値をCSV形式で複数指定します
 *       asNames            【TAG】別名を付けたｶﾗﾑ名(select A as B from TBL の B を指定)をCSV形式で複数指定します
 *       orgNames           【TAG】tableの実際のｶﾗﾑ名(select A as B from TBL の A を指定)をCSV形式で複数指定します
 *       quotCheck          【TAG】ﾘｸｴｽﾄ情報の ｼﾝｸﾞﾙｸｫｰﾄ(') 存在ﾁｪｯｸを実施するかどうか[true/false]を設定します (初期値:USE_SQL_INJECTION_CHECK[=true])
 *       constObjKey        【TAG】固定情報ｶﾗﾑの処理ｵﾌﾞｼﾞｪｸﾄを特定するｷｰを設定します(初期値:SYSTEM_ID)
 *       logicalDelete      【TAG】sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します(初期値:false)
 *       caseKey            【TAG】このﾀｸﾞ自体を利用するかどうかの条件ｷｰを指定します(初期値:null)
 *       caseVal            【TAG】このﾀｸﾞ自体を利用するかどうかの条件値を指定します(初期値:null)
 *       caseNN             【TAG】指定の値が、null/ｾﾞﾛ文字列 でない場合(Not Null=NN)は、このﾀｸﾞは使用されます(初期値:判定しない)
 *       caseNull           【TAG】指定の値が、null/ｾﾞﾛ文字列 の場合は、このﾀｸﾞは使用されます(初期値:判定しない)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このﾀｸﾞは使用されます(初期値:判定しない)
 *       debug              【TAG】ﾃﾞﾊﾞｯｸﾞ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   /&gt;
 *
 * ●使用例
 *    ･【entry.jsp】
 *         &lt;og:tableUpdate command="{&#064;command}" queryType="JDBCTableUpdate" &gt;
 *            &lt;og:tableUpdateParam
 *               sqlType  = "{&#064;sqlType}"
 *               table    = "{&#064;MEM.TABLE_NAME}"
 *               where    = "ROWID = [ROWID]"
 *            /&gt;
 *         &lt;/og:tableUpdate&gt;
 *
 * @og.rev 3.8.8.0 (2007/12/22) 新規作成
 * @og.rev 4.1.2.0 (2008/03/12) 実装の大幅な修正
 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のﾏｰｼﾞ(UPDATE,INSERT)対応
 * @og.group DB登録
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class TableUpdateParamTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "8.3.0.3 (2022/08/26)" ;
	private static final long serialVersionUID = 830320220826L ;

	/** sqlType属性に設定できる値			{@value} */
//	public static final String SQL_TYPE  = "|INSERT|COPY|UPDATE|MODIFY|DELETE|" ;
	public static final String SQL_TYPE  = "|INSERT|COPY|UPDATE|MODIFY|DELETE|MERGE|" ;		// 7.2.9.1 (2020/10/23)

	// 3.8.0.4 (2005/08/08) 印刷時に使用するｼｽﾃﾑID
	private static final String SYSTEM_ID =HybsSystem.sys( "SYSTEM_ID" );

	// 4.3.6.0 (2009/05/01) ﾃﾞﾌｫﾙﾄで利用するconstObjのｼｽﾃﾑﾘｿｰｽ名
	private static final String DEFAULT_CONST_OBJ = HybsSystem.sys( "DEFAULT_CONST_CLASS" );

	private String		sqlType		;											// INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
	private String		table		;											// 処理対象のﾃｰﾌﾞﾙ名
	private String[]	names		;											// 処理対象のｶﾗﾑ名
	private String		omitNames	= ",ROWID,ROWNUM,WRITABLE,";				// 処理対象外のｶﾗﾑ名
	private String		where		;											// 処理対象を特定するｷｰ
	private String		whereNames	;											// 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のｶﾗﾑ名
	private boolean		insertOnly;												// 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	private String[]	constKeys	;											// 処理ｶﾗﾑ名の中の固定情報ｶﾗﾑ名
	private String[]	constVals	;											// 処理ｶﾗﾑ名の中の固定情報設定値
	private String[]	funcKeys	;											// 5.5.1.9 (2012/04/19) 関数等を設定するｶﾗﾑ名
	private String[]	funcVals	;											// 5.5.1.9 (2012/04/19) 関数等の設定値
	private String[]	asNames		;											// 5.5.1.9 (2012/04/19) 別名を付けたｶﾗﾑ名(select A as B from TBL の B を指定)
	private String[]	orgNames	;											// 5.5.1.9 (2012/04/19) tableの実際のｶﾗﾑ名(select A as B from TBL の A を指定)
	private String		constObjKey	= SYSTEM_ID;								// 固定情報ｶﾗﾑの処理ｵﾌﾞｼﾞｪｸﾄを特定するｷｰ
	private boolean		quotCheck	= HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );
	private boolean		logicalDelete;											// 4.3.7.0 (2009/06/01) sqlTypeがDELETEの場合にもUPDATE文を発行

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

	/**
	 * Taglibの開始ﾀｸﾞが見つかったときに処理する doStartTag() を ｵｰﾊﾞｰﾗｲﾄﾞします｡
	 *
	 * @og.rev 5.5.1.9 (2012/04/19) ｴﾗｰﾁｪｯｸを先に行います｡
	 * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
	 * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"／＞" 止めを、"＞" に変更します)｡
	 *
	 * @return	後続処理の指示( SKIP_BODY )
	 */
	@Override
	public int doStartTag() {
		if( !useTag() ) { return SKIP_BODY ; }									// 6.3.4.0 (2015/08/01)

		// constKeys,constVals の個数チェック
		if( constKeys != null ) {
			if( constVals == null || constKeys.length != constVals.length ) {
//				final String errMsg = "<b>constKeys と、constVals の個数が異なります。</b><br />"
				final String errMsg = "<b>constKeys と、constVals の個数が異なります。</b><br>"		// 7.0.1.0 (2018/10/15)
									+ " constKeys=[" + StringUtil.array2csv( constKeys ) + "]"
									+ " constVals=[" + StringUtil.array2csv( constVals ) + "]" ;
				throw new HybsSystemException( errMsg );
			}
		}

		// funcKeys,funcVals の個数チェック
		if( funcKeys != null ) {
			if( funcVals == null || funcKeys.length != funcVals.length ) {
//				final String errMsg = "<b>funcKeys と、funcVals の個数が異なります。</b><br />"
				final String errMsg = "<b>funcKeys と、funcVals の個数が異なります。</b><br>"		// 7.0.1.0 (2018/10/15)
									+ " funcKeys=[" + StringUtil.array2csv( funcKeys ) + "]"
									+ " funcVals=[" + StringUtil.array2csv( funcVals ) + "]" ;
				throw new HybsSystemException( errMsg );
			}
		}

		// asNames,orgNames の個数チェック
		if( orgNames != null ) {
			if( asNames == null || orgNames.length != asNames.length ) {
//				final String errMsg = "<b>orgNames と、asNames の個数が異なります。</b><br />"
				final String errMsg = "<b>orgNames と、asNames の個数が異なります。</b><br>"		// 7.0.1.0 (2018/10/15)
									+ " orgNames=[" + StringUtil.array2csv( orgNames ) + "]"
									+ " asNames=[" + StringUtil.array2csv( asNames ) + "]" ;
				throw new HybsSystemException( errMsg );
			}
		}

		return SKIP_BODY ;	// Body を評価しない
	}

	/**
	 * Taglibの終了ﾀｸﾞが見つかったときに処理する doEndTag() を ｵｰﾊﾞｰﾗｲﾄﾞします｡
	 *
	 * @og.rev 4.3.7.0 (2009/06/01) 論理削除対応
	 * @og.rev 6.3.4.0 (2015/08/01) caseKey,caseVal,caseNN,caseNull,caseIf 属性対応
	 * @og.rev 7.2.9.1 (2020/10/23) TableUpdateParamTag のﾏｰｼﾞ(UPDATE,INSERT)対応
	 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	 * @og.rev 8.3.0.3 (2022/08/26) sqlType="MERGE" の場合、UPDATE文 に追加関連固定ｶﾗﾑｷｰが追加される不具合対応
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();
//		if( !useTag() ) { return EVAL_PAGE ; }									// 6.3.4.0 (2015/08/01)
		if( !useTag() || sqlType == null ) { return EVAL_PAGE ; }				// 7.2.9.1 (2020/10/23) sqlType == null の時は、何もしない。

		final TableUpdateTag updateTag = (TableUpdateTag)findAncestorWithClass( this,TableUpdateTag.class );
		if( updateTag == null ) {
			final String errMsg = "<b>" + getTagName() + "ﾀｸﾞは、TableUpdateTagの内側(要素)に記述してください。</b>";
			throw new HybsSystemException( errMsg );
		}

		final String upSqlType = updateTag.getSqlType() ;

		// 7.2.9.1 (2020/10/23) TableUpdateParamTag のﾏｰｼﾞ(UPDATE,INSERT)対応
		final boolean useInsert = "INSERT,COPY,MERGE".contains( sqlType ) ;
		final boolean useUpdate = "UPDATE,MODIFY,MERGE".contains( sqlType ) || "DELETE".contains( sqlType ) && logicalDelete ;
		final boolean useDelete = "DELETE".equals( sqlType ) && !logicalDelete ;
		final boolean useMerge  = "MERGE".contains( sqlType ) ;					// 8.3.0.3 (2022/08/26) Add

		if( upSqlType == null || upSqlType.equals( sqlType ) ) {
			// 通常の names ｶﾗﾑ配列を設定します｡
			if( names == null ) { names = updateTag.getNames(); }
			final NamesData namesData = makeNamesData( names );

			// 8.3.0.3 (2022/08/26) Modify
			if( useInsert ) {
//				updateTag.setQuery( "INSERT",getInsertSQL( namesData ) );
				final NamesData namesData1 = useMerge ? namesData.newInstance() : namesData;
				updateTag.setQuery( "INSERT",getInsertSQL( namesData1 ) );
			}
			if( useUpdate ) {
				if( insertOnly ) {												// 7.4.1.0 (2021/04/23)
					updateTag.setQuery( "SELECT",getSelectSQL() );
				}
				// 8.3.0.3 (2022/08/26) Modify
				else {
//					updateTag.setQuery( "UPDATE",getUpdateSQL( namesData ) );
					final NamesData namesData2 = useMerge ? namesData.newInstance() : namesData;
					updateTag.setQuery( "UPDATE",getUpdateSQL( namesData2 ) );
				}
			}
			if( useDelete ) {
				updateTag.setQuery( "DELETE",getDeleteSQL() );
			}
		}

//		// 7.2.9.1 (2020/10/23) TableUpdateParamTag のﾏｰｼﾞ(UPDATE,INSERT)対応
//		if( upSqlType == null || upSqlType.equals( sqlType ) ||  ) {
//			String query = null;
//			if( "INSERT".equalsIgnoreCase( sqlType ) || "COPY".equalsIgnoreCase( sqlType ) ) {
//				query = getInsertSQL( namesData );
//			}
//			else if( "UPDATE".equalsIgnoreCase( sqlType ) || "MODIFY".equalsIgnoreCase( sqlType )
//					|| ( "DELETE".equalsIgnoreCase( sqlType ) && logicalDelete ) ) { // 4.3.7.0 (2009/06/01)
//				query = getUpdateSQL( namesData );
//			}
//			else if( "DELETE".equalsIgnoreCase( sqlType ) ) {
//				query = getDeleteSQL();
//			}
//
//			jspPrint( query );
//		}

		return EVAL_PAGE ;
	}

	/**
	 * ﾀｸﾞﾘﾌﾞｵﾌﾞｼﾞｪｸﾄをﾘﾘｰｽします｡
	 * ｷｬｯｼｭされて再利用されるので、ﾌｨｰﾙﾄﾞの初期設定を行います｡
	 *
	 * @og.rev 4.3.7.0 (2009/06/01) logicalDelete属性追加
	 * @og.rev 5.5.1.9 (2012/04/19) asNames、orgNames、funcKeys、funcVals属性追加
	 * @og.rev 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のｶﾗﾑ名
	 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	 */
	@Override
	protected void release2() {
		super.release2();														// 3.5.6.0 (2004/06/18) 追加(抜けていた)
		sqlType			= null;													// INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE
		table			= null;													// 処理対象のﾃｰﾌﾞﾙ名
		names			= null;													// 処理対象のｶﾗﾑ名
		omitNames		= ",ROWID,ROWNUM,WRITABLE,";							// 処理対象外のｶﾗﾑ名
		where			= null;													// 処理対象を特定するｷｰ
		whereNames		= null;													// 5.5.8.5 (2012/11/27) 処理対象を特定するCSV形式のｶﾗﾑ名
		insertOnly		= false;												// 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
		constKeys		= null;													// 処理ｶﾗﾑ名の中の固定情報ｶﾗﾑ名
		constVals		= null;													// 処理ｶﾗﾑ名の中の固定情報設定値
		quotCheck		= HybsSystem.sysBool( "USE_SQL_INJECTION_CHECK" );
		constObjKey		= SYSTEM_ID;											// 固定情報ｶﾗﾑの処理ｵﾌﾞｼﾞｪｸﾄを特定するｷｰ
		logicalDelete	= false;												// 4.3.7.0 (2009/06/01)
		funcKeys		= null;													// 5.5.1.9 (2012/04/19) 関数等を設定するｶﾗﾑ名
		funcVals		= null;													// 5.5.1.9 (2012/04/19) 関数等の設定値
		asNames			= null;													// 5.5.1.9 (2012/04/19) 別名を付けたｶﾗﾑ名(select A as B from TBL の B を指定)
		orgNames		= null;													// 5.5.1.9 (2012/04/19) tableの実際のｶﾗﾑ名(select A as B from TBL の A を指定)
	}

	/**
	 * 【TAG】BODY部に書かれている SQLﾀｲﾌﾟを指定します｡
	 *
	 * @og.tag
	 * SQLﾀｲﾌﾟは、INSERT,COPY,UPDATE,MODIFY,DELETE,MERGE の中から指定する
	 * 必要があります｡これらは、内部に書かれるSQLの形式を指定するのに使用します｡
	 * 内部処理は、DBTableModelの改廃ｺｰﾄﾞ(A,C,D)に対して使用される
	 * SQL を選択する場合の情報に使用されます｡
	 * なお、COPY と MODIFY は、command で指定できる簡易機能として用意しています｡
	 * 上位の TableUpdateTag の sqlType 属性 と同じsqlType 属性の場合のみ、SQL文を
	 * 合成・出力します｡(上位のsqlTypeがnullの場合は、無条件実行します｡)
	 * 指定のﾀｲﾌﾟが、異なる場合は、なにも処理を行いません｡
	 *
	 * @param	type	SQLﾀｲﾌﾟ [INSERT/COPY/UPDATE/MODIFY/DELETE/MERGE]
	 */
	public void setSqlType( final String type ) {
		sqlType = nval( getRequestParameter( type ),sqlType );
		if( sqlType != null && SQL_TYPE.indexOf( "|" + sqlType + "|" ) < 0 ) {
			sqlType = null;
		}
	}

	/**
	 * 【TAG】処理対象のﾃｰﾌﾞﾙ名を指定します｡
	 *
	 * @og.tag
	 * ﾃｰﾌﾞﾙ名を指定することで、sqlTypeに応じた QUERYを生成することが出来ます｡
	 * 生成する場合のｶﾗﾑを特定する場合は、names 属性で指定できます｡
	 * また、WHERE条件は、where属性で指定します｡
	 *
	 * @param	tbl	ﾃｰﾌﾞﾙ名
	 * @see		#setNames( String )
	 * @see		#setWhere( String )
	 * @see		#setSqlType( String )
	 */
	public void setTable( final String tbl ) {
		table = nval( getRequestParameter( tbl ),table );
	}

	/**
	 * 【TAG】処理対象のｶﾗﾑ名をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * 生成するQUERYのｶﾗﾑ名をCSV形式(CSV)で複数指定します｡
	 * 指定がない場合は、DBTableModel の全ｶﾗﾑ(※)を使用して、QUERYを構築します｡
	 * 一般に、ﾃｰﾌﾞﾙ結合してDBTableModelを構築した場合は、登録すべきｶﾗﾑを
	 * 指定する必要があります｡
	 * (※)正確には、DBTableModel の全ｶﾗﾑのうち、ROWID,ROWNUM,WRITABLE ｶﾗﾑは
	 * 無視します｡
	 * 分解方法は、通常のﾊﾟﾗﾒｰﾀ取得後に、CSV分解します｡
	 *
	 * @og.rev 3.8.8.5 (2007/03/09) 通常のﾊﾟﾗﾒｰﾀ取得後に、CSV分解に戻します｡
	 *
	 * @param	nms	ｶﾗﾑ名 (CSV形式)
	 * @see		#setTable( String )
	 * @see		#setOmitNames( String )
	 */
	public void setNames( final String nms ) {
		names = StringUtil.csv2Array( getRequestParameter( nms ) );
		if( names.length == 0 ) { names = null; }
	}

	/**
	 * 【TAG】処理対象外のｶﾗﾑ名をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * 生成するQUERYのｶﾗﾑ名に指定しないｶﾗﾑ名をCSV形式(CSV)で複数指定します｡
	 * 指定がない場合は、DBTableModel の全ｶﾗﾑ(※)を使用して、QUERYを構築します｡
	 * ﾃｰﾌﾞﾙ結合などで、処理したくないｶﾗﾑ数の方が少ない場合に、names ですべてを
	 * 指定するより少ない記述ですみます｡
	 * (※)正確には、DBTableModel の全ｶﾗﾑのうち、ROWID,ROWNUM,WRITABLE ｶﾗﾑは
	 * 無視します｡
	 *
	 * @param	nms	ｶﾗﾑ名 (CSV形式)
	 * @see		#setTable( String )
	 * @see		#setNames( String )
	 */
	public void setOmitNames( final String nms ) {
		omitNames = omitNames + nval( getRequestParameter( nms ),"" ) + ",";
	}

	/**
	 * 【TAG】処理対象を特定するｷｰ条件(where句)を指定します｡
	 *
	 * @og.tag
	 * 生成するQUERYのwhere 句を指定します｡通常の WHERE 句の書き方と同じで、
	 * DBTableModelの値を割り当てたい箇所に[ｶﾗﾑ名] を記述します｡
	 * 文字列の場合、設定値をｾｯﾄするときに、ｼﾝｸﾞﾙｺｰﾃｰｼｮﾝを
	 * 使用しますが、[ｶﾗﾑ名]で指定する場合は、その前後に、(')ｼﾝｸﾞﾙ
	 * ｺｰﾃｰｼｮﾝは、不要です｡
	 * {&#064;XXXX}変数を使用する場合は、ﾊﾟｰｽ時に固定文字に置き換えられる為、
	 * 文字列指定時の(')ｼﾝｸﾞﾙｺｰﾃｰｼｮﾝが必要になります｡
	 * ※ 5.5.8.5 (2012/11/27) whereNames 属性と併用した場合は、where が、and を付けて、文字列結合されます｡
	 * 例：FGJ='1' and CLM=[CLM] and SYSTEM_ID in ([SYSID],'**') and KBSAKU='{&#064;KBSAKU}'
	 *
	 * @param	wr	検索条件 (where句)
	 */
	public void setWhere( final String wr ) {
		where = nval( getRequestParameter( wr ),where );
	}

	/**
	 * 【TAG】処理対象を特定するｷｰ条件(where句)をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * 生成するQUERYのwhere 句を指定する方法として、複数のｶﾗﾑ名をCSV指定し、内部で
	 * KEY=[KEY] 文字列を作成します｡
	 * ここでは、ｶﾗﾑ名は、ﾃﾞｰﾀﾍﾞｰｽのｶﾗﾑ名と同じで、かつ、DBTableModel にも
	 * 同じｶﾗﾑのﾃﾞｰﾀが存在していること、という条件付きとします｡
	 * また、where 条件との併用を行いますが、こちらの条件が先に使用され、where 条件は、
	 * and を付けて、文字列結合されます｡
	 * 例： CLM,SYSTEM_ID,KBSAKU   ⇒   CLM=[CLM] and SYSTEM_ID=[SYSTEM_ID] and KBSAKU=[KBSAKU]
	 *
	 * @og.rev 5.5.8.5 (2012/11/27) 新規追加
	 *
	 * @param	wrnm	検索条件ｶﾗﾑ (where句)作成のためのｶﾗﾑ名(CSV形式)
	 */
	public void setWhereNames( final String wrnm ) {
		whereNames = nval( getRequestParameter( wrnm ),whereNames );
	}

	/**
	 *【TAG】true に設定すると、sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない(初期値:false)｡
	 *
	 * @og.tag
	 * true に設定すると、sqlType="MERGE" 時に、where条件で、検索して、存在しない場合は、追加します｡
	 * 存在する場合は、何もしません｡
	 * 何もしない（更新しない）ところが、通常のMERGEと異なる箇所です｡
	 * 動作としては、UPDATEの代わりに、SELECT で判定します｡
	 * 初期値は、false で、あれば更新、なければ追加処理を行います｡
	 *
	 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	 *
	 * @param	flag	"true" に設定すると、sqlType="MERGE" 時に、where条件で検索
	 */
	public void setInsertOnly( final String flag ) {
		insertOnly = nval( getRequestParameter( flag ),insertOnly );
	}

	/**
	 * 【TAG】設定値を固定値と置き換える対象となるｶﾗﾑ名をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * names 属性のｶﾗﾑや table 属性より、QUERYを作成して、DBTableModelの値を
	 * 割り当てる場合、DBTableModelの値ではなく、外部から指定した固定値を
	 * 割り当てたい場合に、そのｶﾗﾑ名をCSV形式(CSV)で複数指定します｡
	 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します｡
	 * こうしないとﾃﾞｰﾀ自身にｶﾝﾏを持っている場合に分解をﾐｽる為です｡
	 *
	 * @param	keys	固定値ｶﾗﾑ (CSV形式)
	 * @see		#setConstVals( String )
	 */
	public void setConstKeys( final String keys ) {
		constKeys = getCSVParameter( keys );
	}

	/**
	 * 【TAG】設定値を固定値と置き換える対象となる設定値をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * names 属性のｶﾗﾑや table 属性より、QUERYを作成して、DBTableModelの
	 * 値を割り当てる場合、DBTableModelの値ではなく、外部から指定した固定値を
	 * 割り当てたい場合に、そのｶﾗﾑ名に対応する設定値をCSV形式(CSV)で
	 * 複数指定します｡ここで指定する設定値は、constKeys 属性と対応させます｡
	 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します｡
	 * こうしないとﾃﾞｰﾀ自身にｶﾝﾏを持っている場合に分解をﾐｽる為です｡
	 *
	 * @param	vals	設定値(CSV形式)
	 * @see		#setConstKeys( String )
	 */
	public void setConstVals( final String vals ) {
		constVals = getCSVParameter( vals );
	}

	/**
	 * 【TAG】関数等を設定するｶﾗﾑ名をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * constVals 属性で設定する値は、必ずｼﾝｸﾞﾙｸｵｰﾄが付与されます｡
	 * その場合、関数などを設定したい場合でも、文字列として設定しようとします｡
	 * ここで指定するｶﾗﾑ名(funcKeys)自身は、constKeys と同じ書式です｡
	 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します｡
	 * こうしないとﾃﾞｰﾀ自身にｶﾝﾏを持っている場合に分解をﾐｽる為です｡
	 *
	 * @og.rev 5.5.1.9 (2012/04/19) 新規追加
	 *
	 * @param	keys	関数ｶﾗﾑ (CSV形式)
	 * @see		#setFuncVals( String )
	 */
	public void setFuncKeys( final String keys ) {
		funcKeys = getCSVParameter( keys );
	}

	/**
	 * 【TAG】関数等の設定値をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * funcKeys 属性に対応する 関数などの設定値を割り当てます｡
	 * constVals 属性との違いは、funcVals の設定値は、そのままの形で、SQL文の
	 * 構築に使われます｡
	 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します｡
	 * こうしないとﾃﾞｰﾀ自身にｶﾝﾏを持っている場合に分解をﾐｽる為です｡
	 *
	 * @og.rev 5.5.1.9 (2012/04/19) 新規追加
	 *
	 * @param	vals	関数設定値 (CSV形式)
	 * @see		#setFuncKeys( String )
	 */
	public void setFuncVals( final String vals ) {
		funcVals = getCSVParameter( vals );
	}

	/**
	 * 【TAG】別名を付けたｶﾗﾑ名(select A as B from TBL の B を指定)をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * SELECT 文を記述したとき、別名を付けていたり、SELECTしたﾃｰﾌﾞﾙと別のﾃｰﾌﾞﾙに
	 * DBTableModelの値を書き込む場合、DBTableModel の持っているｶﾗﾑ名と、実際に
	 * 書き込むｶﾗﾑ名が異なります｡そのようなｹｰｽに、元の別名ｶﾗﾑを指定します｡
	 * orgNames属性の並び順と、asNames属性の並び順を合わせておく必要があります｡
	 * このｶﾗﾑ名は、DBTableModel には持っているが、ﾃｰﾌﾞﾙ側には持っていない値
	 * なので、内部的に omitNames 属性に値を設定します｡利用者は、omitNames に
	 * 書き込む必要はありません｡
	 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します｡
	 * こうしないとﾃﾞｰﾀ自身にｶﾝﾏを持っている場合に分解をﾐｽる為です｡
	 *
	 * @og.rev 5.5.1.9 (2012/04/19) 新規追加
	 *
	 * @param	keys	別名ｶﾗﾑ (CSV形式)
	 * @see		#setOrgNames( String )
	 */
	public void setAsNames( final String keys ) {
		asNames = getCSVParameter( keys );
	}

	/**
	 * 【TAG】tableの実際のｶﾗﾑ名(select A as B from TBL の A を指定)をCSV形式で複数指定します｡
	 *
	 * @og.tag
	 * SELECT 文を記述したとき、別名を付けていたり、SELECTしたﾃｰﾌﾞﾙと別のﾃｰﾌﾞﾙに
	 * DBTableModelの値を書き込む場合、DBTableModel の持っているｶﾗﾑ名と、実際に
	 * 書き込むｶﾗﾑ名が異なります｡そのようなｹｰｽに、ﾃｰﾌﾞﾙの実ｶﾗﾑを指定します｡
	 * orgNames属性の並び順と、asNames属性の並び順を合わせておく必要があります｡
	 * このｶﾗﾑ名は、DBTableModel には持っていませんが、ﾃｰﾌﾞﾙ側には持っている値
	 * なので、このｶﾗﾑ名で、SQL文を構築します｡ UPDATE TBL SET A=[B] WHERE … となります｡
	 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します｡
	 * こうしないとﾃﾞｰﾀ自身にｶﾝﾏを持っている場合に分解をﾐｽる為です｡
	 *
	 * @og.rev 5.5.1.9 (2012/04/19) 新規追加
	 *
	 * @param	keys	実ｶﾗﾑ (CSV形式)
	 * @see		#setAsNames( String )
	 */
	public void setOrgNames( final String keys ) {
		orgNames = getCSVParameter( keys );
	}

	/**
	 * 【TAG】ﾘｸｴｽﾄ情報の ｼﾝｸﾞﾙｸｫｰﾄ(') 存在ﾁｪｯｸを実施するかどうか[true/false]を設定します
	 *		(初期値:USE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])｡
	 *
	 * @og.tag
	 * SQLｲﾝｼﾞｪｸｼｮﾝ対策の一つとして、暫定的ではありますが、SQLのﾊﾟﾗﾒｰﾀに
	 * 渡す文字列にｼﾝｸﾞﾙｸｫｰﾄ(') を許さない設定にすれば、ある程度は防止できます｡
	 * 数字ﾀｲﾌﾟの引数には、 or 5=5 などのｼﾝｸﾞﾙｸｫｰﾄを使用しないｺｰﾄﾞを埋めても、
	 * 数字ﾁｪｯｸで検出可能です｡文字ﾀｲﾌﾟの場合は、必ず (')をはずして、
	 * ' or 'A' like 'A のような形式になる為、(')ﾁｪｯｸだけでも有効です｡
	 * (') が含まれていたｴﾗｰにする(true)/かﾉｰﾁｪｯｸか(false)を指定します｡
	 * (初期値:ｼｽﾃﾑ定数のUSE_SQL_INJECTION_CHECK[={@og.value SystemData#USE_SQL_INJECTION_CHECK}])｡
	 *
	 * @param	flag	ｸｫｰﾄﾁｪｯｸ [true:する/それ以外:しない]
	 * @see		org.opengion.hayabusa.common.SystemData#USE_SQL_INJECTION_CHECK
	 */
	public void setQuotCheck( final String flag ) {
		quotCheck = nval( getRequestParameter( flag ),quotCheck );
	}

	/**
	 * 【TAG】固定情報ｶﾗﾑの処理ｵﾌﾞｼﾞｪｸﾄを特定するｷｰを設定します(初期値:SYSTEM_ID)｡
	 *
	 * @og.tag
	 * 固定情報ｶﾗﾑ をｼｽﾃﾑ単位にJavaｸﾗｽで管理できます｡
	 * そのｸﾗｽｵﾌﾞｼﾞｪｸﾄは、org.opengion.hayabusa.db.DBConstValue ｲﾝﾀｰﾌｪｰｽを
	 * 継承した、plugin ｸﾗｽになります｡
	 * そのｸﾗｽを特定するｷｰﾜｰﾄﾞを指定します｡
	 * 初期値は、SYSTEM_ID でｼｽﾃﾑ単位にｸﾗｽを作成します｡
	 * もし、他のｼｽﾃﾑと共通の場合は、継承だけさせることも可能です｡
	 * 対応したDBConstValueｸﾗｽがﾌﾟﾗｸﾞｲﾝとして存在しない場合は、
	 * ｼｽﾃﾑﾘｿｰｽのDEFAULT_CONST_CLASSで指定されたｸﾗｽが利用されます｡
	 * 固定情報ｶﾗﾑを使用しない場合は、constObjKey="" をｾｯﾄしてください｡
	 *
	 * 初期値は、SYSTEM_ID です｡
	 *
	 * @og.rev 6.9.8.0 (2018/05/28) 固定情報ｶﾗﾑを使用しない場合は、constObjKey="" をｾｯﾄ｡
	 *
	 * @param	key	固定ｶﾗﾑｷｰ
	 */
	public void setConstObjKey( final String key ) {
//		constObjKey = nval( getRequestParameter( key ),constObjKey );
		final String objKey = getRequestParameter( key );
		if( objKey != null ) { constObjKey = objKey; }
	}

	/**
	 * 【TAG】sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します(初期値:false)｡
	 *
	 * @og.tag
	 * sqlType="DELETE"の場合に論理削除(UPDATE)を行うかどうかを指定します｡
	 * trueが指定された場合は、DELETE文ではなく、UPDATE文が発行されます｡
	 * falseが指定された場合は、DELETE文が発行されます｡
	 * さらに論理削除を行う場合、org.opengion.hayabusa.db.DBConstValue ｲﾝﾀｰﾌｪｰｽに
	 * 定義されている、getLogicalDeleteKeys()及びgetLogicalDeleteValsを実装することで、
	 * 論理削除する際のﾌﾗｸﾞの更新方法を統一的に管理することが可能になります｡
	 * 初期値は、false(物理削除する)です
	 *
	 * @param	flag	論理削除可否 [true:UPDATE文/false:DELETE文]
	 */
	public void setLogicalDelete( final String flag ) {
		logicalDelete = nval( getRequestParameter( flag ),logicalDelete );
	}

	/**
	 * ﾃﾞｰﾀをｲﾝｻｰﾄする場合に使用するSQL文を作成します｡
	 *
	 * @og.rev 4.1.2.1 (2008/03/17) DBConstValue による固定値ｾｯﾄを採用
	 * @og.rev 4.3.6.4 (2009/05/01) ﾃﾞﾌｫﾙﾄ設定をｼｽﾃﾑﾘｿｰｽで設定可能にする
	 * @og.rev 5.3.4.0 (2011/04/01) DEFAULT_CONST_OBJの初期値変更(null→ｾﾞﾛ文字列)
	 * @og.rev 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用｡
	 *
	 * @param	namesData	NamesDataｵﾌﾞｼﾞｪｸﾄ
	 *
	 * @return	ｲﾝｻｰﾄSQL
	 * @og.rtnNotNull
	 */
	private String getInsertSQL( final NamesData namesData ) {
		String cls = HybsSystem.sys( "DBConstValue_" + constObjKey ) ;

		// 4.3.6.4 (2009/05/01) 標準の追加
		if( cls == null){
			cls = DEFAULT_CONST_OBJ;
		}

		if( cls != null && !cls.isEmpty() ) {
			final DBConstValue constVal = HybsSystem.newInstance( cls );
			// 4.2.1.0 (2008/04/16) 初期化追加
			constVal.init( table,getUser().getUserID(),getGUIInfoAttri( "KEY" ) );
			final String[] keys = constVal.getInsertKeys();
			final String[] vals = constVal.getInsertVals();
			namesData.add( keys,vals );
		}

		final String[] nms = namesData.getNames();
		final String[] vls = namesData.getVals();

		// 6.2.3.0 (2015/05/01) CSV形式の作成を、String#join( CharSequence , CharSequence... )を使用。
		final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE )
			.append( "INSERT INTO " ).append( table )
			.append( " ( " )
			.append( String.join( "," , nms ) )		// 6.2.3.0 (2015/05/01)
			.append( " ) VALUES ( " )
			.append( String.join( "," , vls ) )		// 6.2.3.0 (2015/05/01)
			.append( " )" );

		return sql.toString();
	}

	/**
	 * ﾃﾞｰﾀをｱｯﾌﾟﾃﾞｰﾄする場合に使用するSQL文を作成します｡
	 *
	 * where と whereNames が同時に指定された場合は、whereNames が先に処理され
	 * where 条件は、and 結合されます｡
	 *
	 * @og.rev 4.1.2.1 (2008/03/17) DBConstValue による固定値ｾｯﾄを採用
	 * @og.rev 4.3.6.4 (2009/05/01) ﾃﾞﾌｫﾙﾄ設定をｼｽﾃﾑﾘｿｰｽで設定可能にする
	 * @og.rev 4.3.7.0 (2009/06/01) 論理削除対応
	 * @og.rev 5.3.7.0 (2011/07/01) DEFAULT_CONST_OBJの初期値変更(null→ｾﾞﾛ文字列) 対応忘れ
	 * @og.rev 5.5.8.5 (2012/11/27) whereNames 対応
	 *
	 * @param	namesData	NamesDataｵﾌﾞｼﾞｪｸﾄ
	 *
	 * @return	ｱｯﾌﾟﾃﾞｰﾄSQL
	 * @og.rtnNotNull
	 */
	private String getUpdateSQL( final NamesData namesData ) {
		String cls = HybsSystem.sys( "DBConstValue_" + constObjKey ) ;

		// 4.3.6.4 (2009/05/01) 標準の追加
		if( cls == null){
			cls = DEFAULT_CONST_OBJ;
		}

		if( cls != null && !cls.isEmpty() ) {		// 5.3.7.0 (2011/07/01)
			final DBConstValue constVal = HybsSystem.newInstance( cls );
			// 4.2.1.0 (2008/04/16) 初期化追加
			constVal.init( table,getUser().getUserID(),getGUIInfoAttri( "KEY" ) );
			// 4.3.7.0 (2009/06/01) 論理削除対応
			String[] keys = null;
			String[] vals = null;
			if( "DELETE".equalsIgnoreCase( sqlType ) ) {
				keys = constVal.getLogicalDeleteKeys();
				vals = constVal.getLogicalDeleteVals();
			}
			else {
				keys = constVal.getUpdateKeys();
				vals = constVal.getUpdateVals();
			}
			namesData.add( keys,vals );
		}

		final String[] nms = namesData.getNames();
		final String[] vls = namesData.getVals();

		final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE )
			.append( "UPDATE " ).append( table ).append( " SET " )
			.append( nms[0] ).append( '=' ).append( vls[0] );					// 6.0.2.5 (2014/10/31) char を append する。

		for( int i=1; i<nms.length; i++ ) {
			sql.append( ',' ).append( nms[i] ).append( '=' ).append( vls[i] );	// 6.0.2.5 (2014/10/31) char を append する。
		}

		// 5.5.8.5 (2012/11/27) whereNames 対応
		String whereAnd = " WHERE " ;
		if( whereNames != null && whereNames.length() > 0 ) {
			final String[] wnms = whereNames.split(",");
			sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' );	// 6.0.2.5 (2014/10/31) char を append する。

			for( int i=1; i<wnms.length; i++ ) {
				sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' );	// 6.0.2.5 (2014/10/31) char を append する。
			}
			whereAnd = " AND " ;		// whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる
		}

		// 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。
		if( where != null && where.length() > 0 ) {
			sql.append( whereAnd ).append( where );
		}

		return sql.toString();
	}

	/**
	 * ﾃﾞｰﾀをﾃﾞﾘｰﾄする場合に使用するSQL文を作成します｡
	 *
	 * where と whereNames が同時に指定された場合は、whereNames が先に処理され
	 * where 条件は、and 結合されます｡
	 *
	 * @og.rev 5.5.8.5 (2012/11/27) whereNames 対応
	 *
	 * @return	ﾃﾞﾘｰﾄSQL
	 * @og.rtnNotNull
	 */
	private String getDeleteSQL() {
		final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE );
		sql.append( "DELETE FROM " ).append( table );

		// 5.5.8.5 (2012/11/27) whereNames 対応
		String whereAnd = " WHERE " ;
		if( whereNames != null && whereNames.length() > 0 ) {
			final String[] wnms = whereNames.split(",");
			sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' );	// 6.0.2.5 (2014/10/31) char を append する。

			for( int i=1; i<wnms.length; i++ ) {
				sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' );	// 6.0.2.5 (2014/10/31) char を append する。
			}
			whereAnd = " AND " ;		// whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる
		}

		// 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。
		if( where != null && where.length() > 0 ) {
			sql.append( whereAnd ).append( where );
		}
		return sql.toString();
	}

	/**
	 * ﾃﾞｰﾀを検索する場合に使用するSQL文を作成します｡
	 *
	 * where と whereNames が同時に指定された場合は、whereNames が先に処理され
	 * where 条件は、and 結合されます｡
	 *
	 * これは、sqlType="MERGE" 時に、insertOnly="true" が指定された時に、呼ばれます｡
	 *
	 * @og.rev 7.4.1.0 (2021/04/23) sqlType="MERGE" 時のみ有効で、where 条件で存在すれば何もしない
	 *
	 * @return	検索SQL
	 * @og.rtnNotNull
	 */
	private String getSelectSQL() {
		final StringBuilder sql = new StringBuilder( BUFFER_MIDDLE );
		sql.append( "SELECT count(*) FROM " ).append( table );

		// 5.5.8.5 (2012/11/27) whereNames 対応
		String whereAnd = " WHERE " ;
		if( whereNames != null && whereNames.length() > 0 ) {
			final String[] wnms = whereNames.split(",");
			sql.append( whereAnd ).append( wnms[0] ).append( "=[" ).append( wnms[0] ).append( ']' );	// 6.0.2.5 (2014/10/31) char を append する。

			for( int i=1; i<wnms.length; i++ ) {
				sql.append( " AND " ).append( wnms[i] ).append( "=[" ).append( wnms[i] ).append( ']' );	// 6.0.2.5 (2014/10/31) char を append する。
			}
			whereAnd = " AND " ;		// whereNames 優先。ここを通らなければ、初期値のまま、" WHERE " が使われる
		}

		// 5.5.8.5 (2012/11/27) whereNames 対応。whereNames が登録されていれば、AND で繋げる。
		if( where != null && where.length() > 0 ) {
			sql.append( whereAnd ).append( where );
		}
		return sql.toString();
	}

	/**
	 * names,constKeys,omitNames から、必要なｷｰ情報と、属性情報を持った NamesData を作成します｡
	 *
	 * @og.rev 4.1.2.1 (2008/03/17) 固定値の constVals の前後に、"'" を入れる｡
	 * @og.rev 5.5.1.9 (2012/04/19) asNames、orgNames、funcKeys、funcVals属性追加
	 *
	 * @param	nms	ｶﾗﾑ名配列(可変長引数)
	 *
	 * @return	属性情報を持ったNamesData
	 */
	private NamesData makeNamesData( final String... nms ) {
		final NamesData namesData = new NamesData();

		// 5.5.1.9 (2012/04/19) omitNames に、asNames配列の値を設定しておきます。
		if( asNames != null ) {
			for( int i=0; i<asNames.length; i++ ) {
				if( asNames[i] != null && asNames[i].length() > 0 ) {
					omitNames = omitNames + asNames[i] + ",";
				}
			}
		}

		// names で指定されたｶﾗﾑ名
		for( int i=0; i<nms.length; i++ ) {
			final String nm = nms[i];
			if( nm != null && nm.length() > 0 && omitNames.indexOf( "," + nm + "," ) < 0 ) {
				namesData.add( nm,"[" + nm + "]" ) ;
			}
		}

		// 固定値の constKeys ｶﾗﾑ配列を設定します｡
		if( constKeys != null && constKeys.length > 0 ) {
			for( int j=0; j<constKeys.length; j++ ) {
				final String nm = constKeys[j];
				if( nm != null && nm.length() > 0 ) {
					namesData.add( nm,"'" + constVals[j] + "'" ) ;	// constVals は、シングルクオートで囲います。
				}
			}
		}

		// 関数値の funcKeys ｶﾗﾑ配列を設定します｡
		if( funcKeys != null && funcKeys.length > 0 ) {
			for( int j=0; j<funcKeys.length; j++ ) {
				final String nm = funcKeys[j];
				if( nm != null && nm.length() > 0 ) {
					namesData.add( nm, funcVals[j] ) ;		// funcVals は、シングルクオートで囲いません。
				}
			}
		}

		// 別名の asNames,orgNames ｶﾗﾑ配列を設定します｡
		if( orgNames != null && orgNames.length > 0 ) {
			for( int j=0; j<orgNames.length; j++ ) {
				final String onm = orgNames[j];
				if( onm != null && onm.length() > 0 ) {
					namesData.add( onm,"[" + asNames[j] + "]" ) ;
				}
			}
		}

		return namesData ;
	}

	/**
	 * 内部ﾃﾞｰﾀを受け渡す為の、簡易ｸﾗｽです｡
	 * 更新するｶﾗﾑ名と値のｾｯﾄ配列を管理しています｡
	 *
	 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更｡
	 *    ※ classのfinal化と変数のprivate化、ﾒｿｯﾄﾞの修飾子なし(ﾊﾟｯｹｰｼﾞﾌﾟﾗｲﾍﾞｰﾄ)化を行います｡
	 * @og.rev 6.4.1.2 (2016/01/22) nameの値を、大文字小文字の区別をなくすために、常に大文字で登録します｡
	 */
	private static final class NamesData {
		private final Map<String,String> nameMap = new LinkedHashMap<>() ;

		/**
		 * 内部のMapをｺﾋﾟｰした新しいｲﾝｽﾀﾝｽを返します｡
		 * 
		 * @og.rev 8.3.0.3 (2022/08/26) sqlType="MERGE" の場合、UPDATE文 に追加関連固定ｶﾗﾑｷｰが追加される不具合対応
		 *
		 * @return	新しいｲﾝｽﾀﾝｽ
		 */
		/* default */ NamesData newInstance () {
			final NamesData newNames = new NamesData();
			newNames.nameMap.putAll( nameMap );
			return newNames;
		}

		/**
		 * ｷｰと値のｾｯﾄを追加します｡
		 *
		 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更｡
		 * @og.rev 6.4.1.2 (2016/01/22) nameの値を、大文字小文字の区別をなくすために、常に大文字で登録します｡
		 *
		 * @param	nm	ｷｰ（大文字のみ｡内部で変換しておきます｡）
		 * @param	val	値
		 */
		/* default */ void add( final String nm,final String val ) {
			nameMap.put( nm.toUpperCase(Locale.JAPAN),val );
		}

		/**
		 * ｷｰ配列と対応する、値配列のｾｯﾄを追加します｡
		 *
		 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更｡
		 *
		 * @param	nms		ｷｰ配列
		 * @param	vals	値配列
		 */
		/* default */ void add( final String[] nms,final String[] vals ) {
			if( nms != null ) {
				for( int i=0; i<nms.length; i++ ) {
					nameMap.put( nms[i].toUpperCase(Locale.JAPAN),vals[i] );
				}
			}
		}

		/**
		 * ｷｰ配列を返します｡
		 *
		 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更｡
		 *
		 * @return	ｷｰ配列
		 * @og.rtnNotNull
		 */
		/* default */ String[] getNames() {
			return nameMap.keySet().toArray( new String[nameMap.size()] );
		}

		/**
		 * 値配列を返します｡
		 *
		 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private に変更｡
		 *
		 * @return	値配列
		 * @og.rtnNotNull
		 */
		/* default */ String[] getVals()  {
			return nameMap.values().toArray( new String[nameMap.size()] );
		}
	}

	/**
	 * このｵﾌﾞｼﾞｪｸﾄの文字列表現を返します｡
	 * 基本的にﾃﾞﾊﾞｯｸﾞ目的に使用します｡
	 *
	 * @return	このｸﾗｽの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"			,VERSION		)
				.println( "sqlType"			,sqlType		)
				.println( "table"			,table			)
				.println( "names"			,names			)
				.println( "omitNames"		,omitNames		)
				.println( "where"			,where			)
				.println( "whereNames"		,whereNames		)
				.println( "constKeys"		,constKeys		)
				.println( "constVals"		,constVals		)
				.println( "logicalDelete"	,logicalDelete	)
				.fixForm().toString() ;
	}
}
