/*
 * 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.db.DBColumn;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.fukurou.util.Attributes;
import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.model.ArrayDataModel;
import org.opengion.fukurou.model.Formatter;

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

import java.util.List;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale ;

import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

/**
 * 検索結果の DBTableModelオブジェクトに値を設定するタグです。
 *
 * columnSet と共に使用する場合は、entryタグ の command属性と、columnSetタグ の command属性が
 * 一致した場合のみ、処理されます。
 * entryタグは、そのコマンドにより、DBTableModelオブジェクトの値を設定します。
 * たとえば、command="INSERT" ならば、１行分のデータを選択された行番号の次に挿入します。
 * また、追加、変更、削除された、DBTableModelオブジェクト でも、内部には元のデータを
 * 持っているため、command="RESET" で元の状態に戻すことが可能です。
 *
 * @og.formSample
 * ●形式：
 *       ・&lt;og:entry command="…"&gt;
 *             &lt;og:columnSet command="…" /&gt;
 *         &lt;/og:entry&gt;
 *       ・&lt;og:entry command="…" /&gt;
 *             ･･･columnSetを使わない場合でもresult.jspから次画面(insert,modify,copy.jsp等)に
 *                にDBTableModelをもっていく場合には、必ず２を書いてください。
 *                (取消のとき、エンジン内でDBTableModelを操作するのに使用する為)
 * ●body：あり(columnSet)
 *
 * ●使用例
 *    &lt;og:entry command="{&#064;command}"  &gt;
 *        &lt;og:columnSet command="{&#064;command}" columnId="ECNO"   action="CLEAR" /&gt;
 *        &lt;og:columnSet command="{&#064;command}" columnId="JYOKYO" action="SET" value="1" /&gt;
 *    &lt;/og:entry&gt;
 *
 *    &lt;og:entry command="MODIFY" rows="1" &gt;
 *        &lt;og:columnSet  command="MODIFY" columnId="key" action="TBLSET" value="[key][lang]"/&gt;
 *    &lt;/og:entry&gt;
 *
 *    &lt;og:entry command="{&#064;command}" repeatCount="5" &gt;
 *        &lt;og:columnSet  command="{&#064;command}" columnId="YKNO" action="ADD" /&gt;
 *    &lt;/og:entry&gt;
 *
 *    command属性 は、columnSetタグのcommand属性と同一の場合のみ、処理します。
 *    [command属性]
 *      INSERT    新規
 *      COPY      複写
 *      MODIFY    変更
 *      DELETE    削除
 *      ENTRY     エントリー
 *      CHANGE    チェンジ
 *      RESET     リセット
 *      ALLRESET  全件リセット
 *      ALLACTION オールアクション
 *      RESETDATA データリセット
 *      INSERTONE 新規(1行のみ)
 *      REALDELETE 物理削除
 *
 *    [action属性]
 *      DEFAULT カラムリソースで定義した初期値をセットします。
 *      CLEAR   値をクリア(ゼロストリング &quot;&quot; ）します。
 *      ADD     現在の値を ＋１ します。  0 ⇒ 1 , A ⇒ B , 9 ⇒ 10
 *      SET     value で設定した値を 新しい値として登録します。
 *      NULLSET 元の値が NULL の場合だけ、value で設定した新しい値を登録します。
 *      LOWER   小文字に変換します。
 *      UPPER   大文字に変換します。
 *      COPY    value にコピー元のカラムIDをセットすれば、その値を代入します。
 *      TBLSET  DBTableModel の内容を取り込んで指定の columnId カラムに設定します。[カラム名] で指定できます。
 *              また、これは文字列を解析して、 value を作成しますので,文字列連結等に使用できます。
 *      WRTCTRL writableControl を使用したカラムデータの先頭アンダーバーを削除します。
 *      DBMENU  DBMENUでパラメータ設定(コロン連結文字)を使用したカラムデータの先頭データのみにします。
 *      REQSET  valueで指定したカラムの値をキーに、リクエスト変数から値を取出し、セットします。
 *
 *    [strictCheck属性]は、カラムＩＤの存在チェックを行うかどうかを指定します
 *      true    カラムＩＤがDBTableModel に存在しない場合は、エラーになる。(初期値)
 *      false   カラムＩＤがDBTableModel に存在しない場合は、無視する。
 *
 * @og.group 画面登録
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class EntryTag extends CommonTagSupport {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "4.0.0 (2005/08/31)" ;

	private static final long serialVersionUID = 4000 ;	// 4.0.0 (2005/01/31)

	/** command 引数に渡す事の出来る コマンド  新規 {@value} */
	public static final String CMD_INSERT  = "INSERT" ;
	/** command 引数に渡す事の出来る コマンド  複写 {@value} */
	public static final String CMD_COPY    = "COPY" ;
	/** command 引数に渡す事の出来る コマンド  変更 {@value} */
	public static final String CMD_MODIFY  = "MODIFY" ;
	/** command 引数に渡す事の出来る コマンド  削除 {@value} */
	public static final String CMD_DELETE  = "DELETE" ;
	/** command 引数に渡す事の出来る コマンド  エントリー {@value} */
	public static final String CMD_ENTRY   = "ENTRY" ;
	/** command 引数に渡す事の出来る コマンド  チェンジ {@value} */
	public static final String CMD_CHANGE  = "CHANGE" ;
	/** command 引数に渡す事の出来る コマンド  リセット {@value} */
	public static final String CMD_RESET   = "RESET" ;
	/** command 引数に渡す事の出来る コマンド  全件リセット {@value} */
	public static final String CMD_ALLRESET   = "ALLRESET" ;	// 3.5.6.3 (2004/07/12)
	/** command 引数に渡す事の出来る コマンド  オールアクション{@value} */
	public static final String CMD_ALLACTION  = "ALLACTION" ;
	/** command 引数に渡す事の出来る コマンド  リセット(データのみ){@value} */
	public static final String CMD_RESETDATA  = "RESETDATA" ;		// 4.3.3.0 (2008/10/01)
	/** command 引数に渡す事の出来る コマンド  追加(1行のみ){@value} */
	public static final String CMD_INSERTONE  = "INSERTONE" ;		// 5.1.5.0 (2010/04/01)
	/** command 引数に渡す事の出来る コマンド  物理削除 {@value} */
	public static final String CMD_REALDELETE  = "REALDELETE" ;		// 5.1.6.0 (2010/05/01)

	// 3.5.6.3 (2004/07/12) CMD_ALLRESET 追加
	// 4.3.3.0 (2008/10/01) CMD_RESETDATA 追加
	// 5.1.5.0 (2010/04/01) CMD_INESRTONE 追加
	//  5.1.6.0 (2010/05/01) CMD_REALDELETE 追加
	private static final String[] COMMAND_LIST = new String[] {
		CMD_INSERT , CMD_COPY , CMD_MODIFY , CMD_DELETE , CMD_ENTRY , CMD_CHANGE , CMD_ALLACTION , CMD_RESET , CMD_ALLRESET, CMD_RESETDATA, CMD_INSERTONE, CMD_REALDELETE };

	/** action 引数に渡す事の出来る アクションコマンド  初期値 {@value} */
	public static final String ACT_DEFAULT  = "DEFAULT" ;
	/** action 引数に渡す事の出来る アクションコマンド  クリア {@value} */
	public static final String ACT_CLEAR    = "CLEAR" ;
	/** action 引数に渡す事の出来る アクションコマンド  ＋１ {@value} */
	public static final String ACT_ADD      = "ADD" ;
	/** action 引数に渡す事の出来る アクションコマンド  小文字化{@value} */
	public static final String ACT_LOWER    = "LOWER" ;
	/** action 引数に渡す事の出来る アクションコマンド  大文字化{@value} */
	public static final String ACT_UPPER    = "UPPER" ;
	/** action 引数に渡す事の出来る アクションコマンド  コピー {@value} */
	public static final String ACT_COPY     = "COPY" ;
	/** action 引数に渡す事の出来る アクションコマンド  セット {@value} */
	public static final String ACT_SET      = "SET" ;
	// 3.4.0.3 (2003/09/10) NULLSET Action を追加します。
	/** action 引数に渡す事の出来る アクションコマンド  NULLセット {@value} */
	public static final String ACT_NULLSET  = "NULLSET" ;
	/** action 引数に渡す事の出来る アクションコマンド  テーブルセット {@value} */
	public static final String ACT_TBLSET   = "TBLSET" ;
	/** action 引数に渡す事の出来る アクションコマンド  ライトコントロール {@value} */
	public static final String ACT_WRTCTRL  = "WRTCTRL" ;		// 3.8.1.5 (2006/03/30)
	/** action 引数に渡す事の出来る アクションコマンド  DBメニュー {@value} */
	public static final String ACT_DBMENU  = "DBMENU" ;		// 3.8.5.3 (2006/08/07)
	/** action 引数に渡す事の出来る アクションコマンド  リクエスト値セット {@value} */
	public static final String ACT_REQSET  = "REQSET" ;		// 5.4.2.1 (2011/12/09)

	// 3.5.6.0 (2004/06/18) すべてを protected から private に変更します。
	private String				tableId		= HybsSystem.TBL_MDL_KEY;
	private transient DBTableModel	table 	= null;
	private String				command		= null;
	private int[]				rowNo      	= null;
	private List<Attributes>	values		= null;
//	private transient Formatter	format		= null;

	// 3.5.4.2 (2003/12/15) 指定の回数繰り返す機能を追加します。
	private int				repeatCount	= 1;

	// 3.5.5.7 (2004/05/10) Consistency キー による整合性チェックを行うかどうかを指定します。
	private boolean			useConsistency	= HybsSystem.sysBool( "USE_CONSISTENCY" );

	// 3.8.1.1 (2005/11/21) 全件選択されたこととして、処理します。
	private boolean selectedAll = false;

	// 3.5.6.4 (2004/07/16) RESET コマンドのデフォルト処理 に、ALLRESET を
	// 使用するかどうかを指定します。初期値は、false(使用しない)です。
	private final boolean RESET_ACTION_ALL_USE = HybsSystem.sysBool( "RESET_ACTION_ALL_USE" );

	// 4.0.0 (2006/09/31) カラムＩＤの存在チェックを行うかどうかを指定します。
	private boolean strictCheck = true;

	private boolean noTransition= false; // 4.3.3.0 (2008/10/01) 追加

	/**
	 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) 戻り値を、EVAL_BODY_INCLUDE → EVAL_BODY_BUFFERED に変更
	 *
	 * @return  int 後続処理の指示
	 */
	@Override
	public int doStartTag() {		
//		int rtn = EVAL_BODY_INCLUDE;
		int rtn = EVAL_BODY_BUFFERED;		// 5.1.9.0 (2010/08/01) 変更

		if( ! check( command, COMMAND_LIST ) ) {
			rtn = SKIP_BODY;
		}

		return rtn;
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.5.5.5 (2004/04/23) 登録時の 整合性パラメータチェックを行います。
	 * @og.rev 3.5.5.6 (2004/04/27) JSP画面の作成不具合。ENTRY系で、command を投げた場合は、無視します。
	 * @og.rev 3.5.5.7 (2004/05/10) Consistency キー による整合性チェックを行うかどうかを指定します。
	 * @og.rev 3.5.5.8 (2004/05/20) Consistency キー による整合性チェックを checkConsistency() に集約します。
	 * @og.rev 3.6.0.8 (2004/11/19) DBTableModel をセーブする時に、トランザクションチェックを行います。
	 * @og.rev 4.3.3.0 (2008/10/01) noTransition 属性を追加します。
	 * @og.rev 4.3.8.0 (2009/08/01) noTransition値取得のメソッド名変更
	 * @og.rev 5.1.3.0 (2010/02/01) noTransitionのコントロールは、requestで行う。
	 *
	 * @return  int 後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)
		// noTransition = isNoTransitionRequest() || noTransition; // 4.3.3.0 (2008/10/01) 追加
//		noTransition = isNoTransitionSession() || noTransition; // 4.3.8.0 (2009/08/01) メソッド名変更
		noTransition = isNoTransitionRequest(); // 5.1.3.0 (2010/02/01)
		startQueryTransaction( tableId );		// 3.6.0.8 (2004/11/19)
		table = (DBTableModel)getObject( tableId );

		if( table != null && check( command, COMMAND_LIST ) ) {
			if( ! checkConsistency() ) { return(SKIP_PAGE); }
			if( rowNo == null ) { rowNo = getParameterRows(); }		// 4.0.0 (2005/01/31)

			commandExec( command );

			// 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
			if( ! commitTableObject( tableId, table ) ) {
				jspPrint( "EntryTag Query処理が割り込まれました。DBTableModel は登録しません。" );
				return (SKIP_PAGE);
			}
		}

		return(EVAL_PAGE);
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 *
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.5.4.2 (2003/12/15) 指定の回数繰り返す機能を追加します。
	 * @og.rev 3.5.5.7 (2004/05/10) Consistency キー による整合性チェックを行うかどうかを指定します。
	 * @og.rev 3.8.1.1 (2005/11/21) selectedAll 追加。全件選択されたこととして、処理します。
	 * @og.rev 4.0.0 (2006/09/31) strictCheck 追加。
	 * @og.rev 4.3.3.0 (2008/10/01) noTransition 属性を追加します。
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		tableId		= HybsSystem.TBL_MDL_KEY;
		table 		= null;
		command		= null;
		rowNo      	= null;
		values		= null;
//		format		= null;
		repeatCount	= 1;			// 3.5.4.2 (2003/12/15)
		useConsistency	= HybsSystem.sysBool( "USE_CONSISTENCY" );		// 3.5.5.7 (2004/05/10)
		selectedAll = false;		// 3.8.1.1 (2005/11/21)
		strictCheck = true;			// 4.0.0 (2006/09/31)
		noTransition= false;		// 4.3.3.0 (2008/10/01) 追加
	}

	/**
	 * 内部タグの ColumnSetTag より、個々のカラムの値を書き換える為の属性を指定します。
	 *
	 * 複数の値を受け取って、後ほど、すべてのカラムに対して処理を行います。
	 *
	 * @og.rev 3.1.0.0 (2003/03/20) Vector を使用している箇所で、非同期でも構わない箇所を、ArrayList に置換え。
	 * @og.rev 3.1.2.0 (2003/04/07) taglib パッケージ内部で使用している箇所を protected 化する。
	 *
	 * @param	attri Attributes
	 */
	protected void setAttributes( final Attributes attri ) {
		if( values == null ) { values = new ArrayList<Attributes>(); }
		if( command.equalsIgnoreCase( attri.get( "command" ) ) ) {
			values.add( attri );
		}
	}

	/**
	 * 【TAG】(通常使いません)sessionから所得する DBTableModel オブジェクトの ID。
	 *
	 * @og.tag
	 * 表示処理後に，（内部ポインタを書き換えた）DBTableModel オブジェクトを
	 * 同じキーで、sessionに登録します。
	 * 初期値は、HybsSystem.TBL_MDL_KEY です。
	 *
	 * @param	id sessionに登録する時の ID
	 */
	public void setTableId( final String id ) {
		tableId   = nval( getRequestParameter( id ),tableId );	// 3.8.0.9 (2005/10/17)
	}

	/**
	 * 【TAG】コマンド(INSERT,COPY,MODIFY,DELETE,ENTRY,CHANGE,ALLACTION,RESET)をセットします。
	 *
	 * @og.tag
	 * コマンドは,HTMLから（get/post)指定されますので,CMD_xxx で設定される
	 * フィールド定数値のいづれかを、指定できます。
	 *
	 * @param	cmd コマンド（public static final 宣言されている文字列)
	 * @see		<a href="{@docRoot}/constant-values.html#org.opengion.hayabusa.taglib.EntryTag.CMD_INSERT">コマンド定数</a>
	 */
	public void setCommand( final String cmd ) {
		String cmd2 = getRequestParameter( cmd );
		if( cmd2 != null && cmd2.length() > 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
	}

	/**
	 * コマンドを実行します。
	 *
	 * コマンドは,HTMLから（get/post)指定されますので,setCommand()メソッドで
	 * 登録します。
	 * コマンドを登録すると同時に,実行も行ないます。
	 *
	 * @og.rev 3.5.6.3 (2004/07/12) ALLRESET コマンドを追加する。
	 * @og.rev 4.3.3.0 (2008/10/01) RESETDATA コマンドを追加する。
	 * @og.rev 5.1.5.0 (2010/04/01) INSERTONE コマンドを追加する。
	 * @og.rev 5.1.6.0 (2010/05/01) REALDELETE コマンドを追加する。
	 *
	 * @param   command コマンド（public static final 宣言されている文字列)
	 */
	private void commandExec( final String command ) {

		table.setDefaultRowWritable( false );
		table.setDefaultRowChecked(  false );

		if( CMD_INSERT.equals(      command ) ) { insert() ; }
		else if( CMD_COPY.equals(   command ) ) { copy()   ; }
		else if( CMD_MODIFY.equals( command ) ) { modify() ; }
		else if( CMD_CHANGE.equals( command ) ) { change() ; }
		else if( CMD_DELETE.equals( command ) ) { delete() ; }
		else if( CMD_ENTRY.equals(  command ) ) { entry()  ; }
		else if( CMD_RESET.equals(  command ) ) {
				if( RESET_ACTION_ALL_USE )		{ allReset() ; }	// 3.5.6.4 (2004/07/16)
				else 							{ reset()    ; }
		}
		else if( CMD_ALLRESET.equals(  command ) ) { allReset()  ; }	// 3.5.6.3 (2004/07/12)
		else if( CMD_ALLACTION.equals(  command ) ) { allAction() ; }
		else if( CMD_RESETDATA.equals(  command ) ) { resetData() ; }	// 4.3.3.0 (2008/10/01)
		else if( CMD_INSERTONE.equals(  command ) ) { insertOne() ; }	// 5.1.5.0 (2010/04/01)
		else if( CMD_REALDELETE.equals(  command ) ) { realDelete() ; }	// 5.1.6.0 (2010/05/01)
	}

	/**
	 * DBTableModelに行を追加します。
	 *
	 * 注意：writableカラムの暫定対応が入っています。単純な空白データを
	 * インサートすると、カラムデータが null になる為､ 制御がおかしく
	 * なります。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) repeatCount による繰り返し処理を追加
	 * @og.rev 4.3.3.0 (2008/10/01) noTransition 属性対応
	 *
	 */
	private void insert() {
		if( rowNo.length == 0 ) { rowNo = new int[] { -1 }; }

		boolean rowWritableFlag = "WRITABLE".equalsIgnoreCase( table.getColumnName( 0 ) );	// writable 対策
		// src の作成は、各チェック毎に行う必要はない。最初の一度だけでよい。
		String[] src = new String[ table.getColumnCount() ];
		for( int j=0; j<src.length; j++ ) {
			DBColumn dbColumn = table.getDBColumn( j );
			src[j] = dbColumn.getDefault();
		}
		if( rowWritableFlag ) { src[0] = "true"; }	// writable 対策

		int rowCount = table.getRowCount();

		// 逆順にINSERTしないと、行番号がずれてしまう。
		for( int i=rowNo.length-1; i>=0; i-- ) {
			int row = rowNo[i];
			for( int cnt=0; cnt<repeatCount; cnt++ ) {
				if( cnt >= 1 ) {		// ２回目以降
					src = table.getValues( row );
				}

				String[] dst = new String[ table.getColumnCount() ];
				System.arraycopy( src,0,dst,0,dst.length );
				dst = setColumnValues( dst );

				// 4.3.3.0 (2008/10/01) noTransition属性対応
				if( noTransition ) { row = rowCount; }
				else { row ++; }					// 指定行の下に追加する。
				table.addValues( dst,row );
				table.setRowWritable( row,true );
				table.setRowChecked(  row,true );
			}
		}
	}

	/**
	 * DBTableModelに行を追加します。(1行のみ)
	 *
	 * 行が選択されているかどうかに関わらず、1行のみを追加します。
	 * (動きとしては、行が選択されていない状態でINSERTコマンドを発行した場合と同じです)
	 *
	 * @og.rev 5.1.5.0 (2010/04/01) 新規作成
	 *
	 */
	private void insertOne() {
		rowNo = new int[0];
		insert();
	}

	/**
	 * DBTableModelに行を追加し、チェックされた行の値をセットします。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) repeatCount による繰り返し処理を追加
	 * @og.rev 4.3.3.0 (2008/10/01) noTransition 属性対応
	 *
	 */
	private void copy() {
		if( rowNo.length == 0 ) { insert() ; return ;}

		int rowCount = table.getRowCount();

		// 逆順にCOPYしないと、行番号がずれてしまう。
		for( int i=rowNo.length-1; i>=0; i-- ) {
			for( int cnt=0; cnt<repeatCount; cnt++ ) {
				String[] src = table.getValues( rowNo[i]+cnt );
				String[] dst = new String[ table.getColumnCount() ];
				System.arraycopy( src,0,dst,0,dst.length );
				dst = setColumnValues( dst );

				// 4.3.3.0 (2008/10/01) noTransition属性対応
				int row = -1;
				if( noTransition ) { row = rowCount; }
				else { row = rowNo[i] + cnt + 1; }			// 指定行の下に追加する。

				table.addValues( dst,row );
				table.setRowWritable( row,true );
				table.setRowChecked(  row,true );
			}
		}
	}

	/**
	 * DBTableModelの行を書込み可とます。
	 * 
	 * @og.rev 4.3.4.6 (2009/01/15) 画面遷移なし登録で既に改廃Cがセットされている場合は、columnSetタグの処理を行わない。
	 *
	 */
	private void modify() {
		for( int i=0; i<rowNo.length; i++ ) {
			String[] src = table.getValues( rowNo[i] );
			String[] dst = new String[ table.getColumnCount() ];
			System.arraycopy( src,0,dst,0,dst.length );

			// 4.3.4.6 (2009/01/15)
			// 画面遷移なし登録の場合、既に改廃Cが付いている(編集されている)場合は、
			// columnSetによる値のセットを行わない。
			// (同じコマンドで複数のボタンを割り当てている場合、複数回の変更・削除によって、先に登録された
			// 値が削除されてしまうため。
			if( !( noTransition && table.getModifyType( rowNo[i] ) != null && table.getModifyType( rowNo[i] ).length() > 0 ) ){
				dst = setColumnValues( dst );
			}

			table.setValues( dst,rowNo[i] );
			table.setRowWritable( rowNo[i],true );
			table.setRowChecked(  rowNo[i],true );
		}
	}

	/**
	 * DBTableModelの行を変更します。

	 * @og.rev 4.3.4.6 (2009/01/15) 画面遷移なし登録で既に改廃Cがセットされている場合は、columnSetタグの処理を行わない。
	 *
	 */
	private void change() {
		for( int i=0; i<rowNo.length; i++ ) {
			String[] src = table.getValues( rowNo[i] );
			String[] dst = new String[ table.getColumnCount() ];
			System.arraycopy( src,0,dst,0,dst.length );

			// 4.3.4.6 (2009/01/15)
			if( !( noTransition && table.getModifyType( rowNo[i] ) != null && table.getModifyType( rowNo[i] ).length() > 0 ) ){
				dst = setColumnValues( dst );
			}

			table.setValues( dst,rowNo[i] );
		}
	}

	/**
	 * DBTableModelの行を削除します。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) DELETE時にも値の書き換えができるようにします。
	 * @og.rev 4.3.4.6 (2009/01/15) 画面遷移なし登録で既に改廃Cがセットされている場合は、columnSetタグの処理を行わない。
	 *
	 */
	private void delete() {
		for( int i=0; i<rowNo.length; i++ ) {
			// 3.5.4.2 (2003/12/15) 書き換え処理を追加
			String[] src = table.getValues( rowNo[i] );
			String[] dst = new String[ table.getColumnCount() ];
			System.arraycopy( src,0,dst,0,dst.length );

			// 4.3.4.6 (2009/01/15)
			if( !( noTransition && table.getModifyType( rowNo[i] ) != null && table.getModifyType( rowNo[i] ).length() > 0 ) ){
				dst = setColumnValues( dst );
			}

			table.rowDelete( dst,rowNo[i] );
			table.setRowWritable( rowNo[i],true );
			table.setRowChecked( rowNo[i],true );
		}
	}

	/**
	 * DBTableModelの行を物理削除します。
	 *
	 * 通常のデータベース等で削除する場合は、DELETE行も残しておかないと、どの行を削除するか
	 * 判らなくなります。また、アプリケーションによっては、削除ではなく、フラグだけを立てる
	 * ケースもあるため、現在の commend="DELETE" では、論理削除＋値の書き換えも可能になっています。
	 * ここでの物理削除は、WriteTable など、ファイル出力時には、その行そのものをなくしておくほうが
	 * 良いケースがあるためです。
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) REALDELETE コマンドを追加する。
	 */
	private void realDelete() {
		// 逆順にしないと、行番号がずれてしまう。
		for( int i=rowNo.length-1; i>=0; i-- ) {
			table.removeValue( rowNo[i] );
		}
	}

	/**
	 * リクエスト情報から、セットされたカラム名と値を取り出し、設定します。
	 *
	 * 設定値は、個々のキー＋"__" ＋ 行番号 です。
	 * よって、値は,一つだけ設定されています。
	 *
	 * @og.rev 3.5.3.1 (2003/10/31) チェックボックスカラムを指定します。
	 * @og.rev 3.6.0.6 (2004/10/22) chboxNames 属性は廃止します。
	 */
	private void entry() {
		if( rowNo.length > 0 ) {
			setRequestValues();
			for( int i=0; i<rowNo.length; i++ ) {
				String[] src = table.getValues( rowNo[i] );
				String[] dst = new String[ table.getColumnCount() ];
				System.arraycopy( src,0,dst,0,dst.length );
				dst = setColumnValues( dst );

				table.setValues( dst,rowNo[i] );
				table.setRowWritable( rowNo[i],true );
				table.setRowChecked(  rowNo[i],true );
			}
		}
	}

	/**
	 * リクエスト情報のテーブルモデルデータを、リセットします。
	 *
	 */
	private void reset() {
		for( int i=rowNo.length-1; i>=0; i-- ) {
			table.resetRow( rowNo[i] );
		}
	}
	
	/**
	 * リクエスト情報のテーブルモデルデータを、リセットします。
	 * (但し、リセットされた行は、チェックされた状態のままになります)
	 * 
	 * @og.rev 4.3.3.0 (2008/10/01) 新規作成
	 */
	private void resetData() {
		for( int i=rowNo.length-1; i>=0; i-- ) {
			String cdkh = table.getModifyType( rowNo[i] );
			table.resetRow( rowNo[i] );
			// 更新又は、削除の時のみ書き込み可能になる。
			if( DBTableModel.UPDATE_TYPE.equals( cdkh ) || DBTableModel.DELETE_TYPE.equals( cdkh ) ) {
				table.setRowWritable( rowNo[i],true );
				table.setRowChecked(  rowNo[i],true );
			}
		}
	}

	/**
	 * テーブルモデルデータを、全件リセットします。
	 *
	 * @og.rev 3.5.6.3 (2004/07/12) 新規作成
	 */
	private void allReset() {
		int rowMax = table.getRowCount()-1 ;
		for( int i=rowMax; i>=0; i-- ) {
			table.resetRow( i );
		}
	}

	/**
	 * DBTableModelの全ての行に対して,値をセットします。
	 *
	 */
	private void allAction() {
		int rowMax = table.getRowCount()-1 ;		// 3.5.6.3 (2004/07/12)
		for( int i=rowMax; i>=0; i-- ) {
			String[] src = table.getValues( i );
			String[] dst = new String[ table.getColumnCount() ];
			System.arraycopy( src,0,dst,0,dst.length );
			dst = setColumnValues( dst );

			table.setValues( dst,i );
			table.setRowWritable( i,true );
			table.setRowChecked(  i,true );
		}
	}

	/**
	 * リクエスト情報から、セットされたカラム名と値を取り出し、設定します。
	 *
	 * 設定値は、個々のキー＋"__" ＋ 行番号 です。
	 * よって、値は,一つだけ設定されています。
	 *
	 * @og.rev 3.1.0.0 (2003/03/20) 名前と行番号の区切り記号を "^" から "__" に変更。
	 * @og.rev 3.5.5.0 (2004/03/12) 名前と行番号の区切り記号("__")を、HybsSystem.JOINT_STRING  に変更。
	 * @og.rev 3.6.0.6 (2004/10/22) chboxNames 属性は廃止します。
	 * @og.rev 3.8.0.1 (2005/06/17) チェックボックス対応で、エラーチェックをPL/SQLで行う場合の処理機能の追加
	 * @og.rev 3.8.0.2 (2005/07/11) チェックボックス対応で、判定を DBColumnのgetEditor()を使用します
	 * @og.rev 4.0.0 (2006/09/31) strictCheck 追加。
	 * @og.rev 4.3.7.3 (2009/06/22) HSQLDB対応でリクエストが空文字の場合はnull文字に変換する
	 * @og.rev 5.0.0.2 (2009/09/15) XSS対応(ALLはチェックしない）
	 *
	 */
	private void setRequestValues() {
		Enumeration<?> enume = getParameterNames();		// 4.3.3.6 (2008/11/15) Generics警告対応

		while( enume.hasMoreElements() ) {
			String key  = (String)(enume.nextElement());
			int    idx  = key.lastIndexOf(HybsSystem.JOINT_STRING);

			if( idx > 0 ) {
				String   column   = key.substring(0,idx);
				int      clmNo    = table.getColumnNo( column,strictCheck );
				if( clmNo < 0 ) { continue; }	// strictCheck 対応
				DBColumn dbColumn = table.getDBColumn( clmNo );
				int      row      = Integer.parseInt( key.substring(idx + 2) );
				// 5.0.0.2 (2009/09/15) 文字種別ALLはXSSチェックしない
				// String   val      = dbColumn.valueSet( getRequestValue( key ) );
				String   val = null;
				if( "ALL".equals( dbColumn.getDbType() ) ){
					val      = dbColumn.valueSet( getRequestValue( key, false ) );
				}
				else{
					val      = dbColumn.valueSet( getRequestValue( key ) );
				}

				// 3.6.0.6 (2004/10/22) チェックボックスはマルチでデータが来ます。
				// 3.8.0.2 (2005/07/11) 判定を DBColumnのgetEditor()を使用
				if( "0".equals(val) && "CHBOX".equals( dbColumn.getEditor() ) ) {
					String[] vals = getRequestValues( key );
					if( vals != null ) {
						for( int i=0; i<vals.length; i++ ) {
							if( "1".equals( vals[i] ) ) { val = "1"; break; }
						}
					}
				}

				for( int i=0; i<rowNo.length; i++ ) {
					if( rowNo[i] == row ) {
						// 4.3.7.3 (2009/06/22) HSQLDB対応
						if( val != null && val.length() == 0 ){
							val = null;
						}
						table.setValue(row, column, val );
					}
				}
			}
		}
	}

	/**
	 * ColumnSetTag で指定された条件を元に、その行の値を書き換えます。
	 *
	 * @og.rev 3.6.0.6 (2004/10/22) conditionKey と、 conditionList 属性を追加
	 * @og.rev 3.8.1.5 (2006/03/30) writableControl を使用したカラムデータの先頭アンダーバーを削除します。
	 * @og.rev 4.0.0 (2006/09/31) strictCheck 追加。
	 * @og.rev 4.3.7.3 (2009/06/22) HSQLDB対応で空文字→NULL
	 *
	 * @param	values 指定行データ配列
	 * @return	変更後の指定行データ配列
	 */
	private String[] setColumnValues( final String[] val ) {
		if( values != null ) {
			int size = values.size();
			for( int i=0; i<size; i++ ) {
				Attributes attri = values.get( i );
				String columnId = attri.get( "columnId" );
				int clmNo = table.getColumnNo( columnId,strictCheck );
				if( clmNo < 0 ) { continue; }	// strictCheck 対応
				String action = attri.get( "action" );
				String newVal = attri.get( "value"  );
				String oldVal = val[clmNo];

				// 3.6.0.6 (2004/10/22) 条件による処理の実行可否判定
				String conditionList = attri.get( "conditionList" );
				if( conditionList != null ) {	// null の場合は、無条件実行
					String conditionKey = attri.get( "conditionKey" );
					int condClmNo = table.getColumnNo( conditionKey );
					String condValue = "|" + val[condClmNo] + "|";
					if( conditionList.indexOf( condValue ) < 0 ) {
						continue;
					}
				}

				if( ACT_COPY.equals( action ) ) {
					int copyClmNo = table.getColumnNo( newVal );	// newVal はコピー元カラム名
					val[clmNo] = val[copyClmNo];
				}
				else if( ACT_TBLSET.equals( action ) ) {
//					if( format == null ) {
//						format = new Formatter( model );
//					}
					ArrayDataModel model = new ArrayDataModel( table.getNames() );
					model.setValues( val,0 );
					Formatter format = new Formatter( model );
					format.setFormat( newVal );		// newValue はフォーマットされた文字列
					val[clmNo] = format.getFormatString( 0 );
				}
				// 3.8.1.5 (2006/03/30) writableControl を使用したカラムデータの先頭アンダーバーを削除します。
				else if( ACT_WRTCTRL.equals( action ) ) {
					if( oldVal != null && oldVal.length() > 0 && oldVal.charAt(0) == '_' ) {
						val[clmNo] = oldVal.substring( 1 );
					}
				}
				// 3.8.5.3 (2006/08/07) DBMENUでパラメータ設定(コロン連結文字)を使用したカラムデータの先頭データのみにします。
				else if( ACT_DBMENU.equals( action ) ) {
					if( oldVal != null && oldVal.length() > 0 ) {
						int adrs = oldVal.indexOf( ':' );
						if( adrs >= 0 ) {
							val[clmNo] = oldVal.substring( 0,adrs );
						}
					}
				}
				// 5.4.2.1 (2011/12/09) valueで指定したカラムの値をキーに、リクエスト変数から値を取出し、セットします。
				else if( ACT_REQSET.equals( action ) ) {
					if( newVal != null && newVal.length() > 0 ) {
						int reqClmNo = table.getColumnNo( newVal );	// newVal はリクエスト取得元カラム名
						String reqClm = val[reqClmNo];				// この時点では、コロン引数が付いている可能性がある。

						int adrs = reqClm.indexOf( ':' );			// 先頭がカラム名
						if( adrs >= 0 ) {
							reqClm = reqClm.substring( 0,adrs );	// コロンより前方の分だけ取り出す。
						}
						val[clmNo] = getRequestValue( reqClm );
					}
				}
				else {
					val[clmNo] = actionExec( action,columnId,oldVal,newVal );
				}
				
				// 4.3.7.3 (2009/06/22) HSQLDB対応
				if( val[clmNo] != null && val[clmNo].length() == 0){
					val[clmNo] = null;
				}
			}
		}
		
		return val;
	}

	/**
	 * アクションを実行します。
	 *
	 * アクションは,指定のアクションコマンドに対応する処理を入力データに
	 * 対して行います。
	 *
	 * @og.rev 3.4.0.3 (2003/09/10) NULLSET Action を追加します。
	 *
	 * @param   action アクションコマンド
	 * @param   clmId カラムＩＤ
	 * @param   oldValue 入力データ（旧データ）
	 * @param   newValue 入力データ（新データ）
	 * @return	実行後のデータ
	 * @see		<a href="{@docRoot}/constant-values.html#org.opengion.hayabusa.taglib.EntryTag.ACT_DEFAULT">アクション定数</a>
	 */
	private String actionExec( final String action,final String clmId,
								final String oldValue,final String newValue ) {
		String rtnVal = oldValue;

		DBColumn dbColumn = getDBColumn( clmId );
		if( ACT_DEFAULT.equals(    action ) ) { rtnVal = dbColumn.getDefault(); }
		else if( ACT_CLEAR.equals( action ) ) { rtnVal = ""; }
		else if( ACT_SET.equals(   action ) ) { rtnVal = dbColumn.valueSet( newValue ); }
		else if( ACT_ADD.equals(   action ) ) { rtnVal = dbColumn.valueAdd( oldValue ); }
		else if( ACT_LOWER.equals( action ) ) {
			if( oldValue == null ) { rtnVal = dbColumn.getDefault();  }
			else                   { rtnVal = oldValue.toLowerCase(Locale.JAPAN); }
		}
		else if( ACT_UPPER.equals( action ) ) {
			if( oldValue == null ) { rtnVal = dbColumn.getDefault();  }
			else                   { rtnVal = oldValue.toUpperCase(Locale.JAPAN); }
		}
		// 3.4.0.3 (2003/09/10) NULLSET Action を追加します。
		else if( ACT_NULLSET.equals( action ) ) {
			if( oldValue == null || oldValue.length() == 0 ) {
				rtnVal = dbColumn.valueSet( newValue );
			}
		}
		else {
			rtnVal = dbColumn.valueAction( action,oldValue,newValue );
		}

		if( rtnVal == null ) { rtnVal = dbColumn.getDefault(); }

		return rtnVal;
	}

	/**
	 * 【TAG】指定の回数分だけ、繰り返し処理を行う回数を指定します(初期値:1)。
	 *
	 * @og.tag
	 * 追加や複写時に、指定の回数分だけ、処理を繰り返して、新規に行を
	 * 作成します。
	 * 繰り返しは、指定の行に対して行われ、繰り返し毎に、直前に作成された
	 * 行を元に処理します。これは、例えば、columnSet で、action="ADD"の場合に、
	 * 繰り返す毎に、ADD処理が実行されることを意味します。
	 * 行が指定されていない場合は、先頭空行に追加します。
	 * 初期値は、１回です。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) 新規追加
	 *
	 * @param	rc String  繰り返し処理を行う回数を指定(初期値:1)
	 */
	public void setRepeatCount( final String rc ) {
		repeatCount = nval( getRequestParameter( rc ),repeatCount );
	}

	/**
	 * 【TAG】Consistency キー による整合性チェックを行うかどうかを指定します(初期値:true)
	 *
	 * @og.tag
	 * 検索結果を DBTableModel にセットする時に、整合性キーの Consistency キーを
	 * 作成します。これを、Viewタグでhidden出力しておき、Entryタグでデータ書き換え時に
	 * 整合性チェックを行います。これは、IEの戻るボタンで戻った場合に、画面の
	 * キーと検索結果の DBTableModel の内容が一致しない場合のエラーチェックに
	 * なります。
	 * この属性は、何らかのケースで、このエラーチェックを行いたくない場合に、
	 * false に設定することで、整合性チェックを行いません。
	 * 初期値は、true(整合性チェックを行う)です。
	 *
	 * @og.rev 3.5.5.7 (2004/05/10) 新規登録
	 *
	 * @param	ck String  Consistency Key の使用するかどうか（初期値：true）
	 */
	public void setUseConsistency( final String ck ) {
		useConsistency = nval( getRequestParameter( ck ),useConsistency );
	}

	/**
	 * DBTableModel の 整合性パラメータとリクエスト情報を比較チェックします。
	 * リクエスト情報は、その DBTableModel が出力された view で hidden 属性で
	 * 設定されます。
	 * 設定されるキーは、tableId が変更されていなければ、HybsSystem.CONSISTENCY_KEY です。
	 * 変更されていれば、HybsSystem.CONSISTENCY_KEY + tableId です。
	 *
	 * @og.rev 3.5.5.8 (2004/05/20) Consistency キー による整合性チェックを checkConsistency() に集約します。
	 *
	 * @return チェック結果  true:正常/false:異常
	 * @see org.opengion.hayabusa.common.HybsSystem#CONSISTENCY_KEY
	 */
	private boolean checkConsistency() {
		boolean rtn = true;
		String key = HybsSystem.CONSISTENCY_KEY ;

		String consisKey = getRequestValue( key );
		if( consisKey != null && consisKey.length() > 0 ) {
			if( useConsistency && ! consisKey.equals( table.getConsistencyKey() ) ) {

				ErrorMessage errMsgObj = new ErrorMessage( "Consistency Key Check Error!" );
				errMsgObj.addMessage( 0,ErrorMessage.NG,"ERR0033.1" );
				errMsgObj.addMessage( 0,ErrorMessage.NG,"ERR0033.2" );
				errMsgObj.addMessage( 0,ErrorMessage.NG,"ERR0033.3" );
				jspPrint( TaglibUtil.makeHTMLErrorTable( errMsgObj,getResource() ) );
				rtn = false;
			}
		}
		else {
			System.out.println( "EntryTag:Consistency Key is null" );
		}
		return rtn ;
	}

	/**
	 * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を
	 * 処理の対象とします。
	 *
	 * <del>rowNo[] が null の場合のみ、取込を行います。</del>
	 *
	 * @og.rev 3.8.1.1 (2005/11/21) selectedAll 追加。全件選択されたこととして、処理します。
	 * @og.rev 4.0.0 (2005/01/31) getParameterRows() を使用するように変更
	 *
	 * @return int[]
	 */
	protected int[] getParameterRows() {
		final int[] rowNo ;
		if( selectedAll ) {
			int rowCnt = table.getRowCount();		// 3.5.5.7 (2004/05/10)
			rowNo = new int[ rowCnt ];
			for( int i=0; i<rowCnt; i++ ) {
				rowNo[i] = i;
			}
		} else {
			rowNo = super.getParameterRows();		// 4.0.0 (2005/01/31)
		}
		return rowNo ;
	}

	/**
	 * 【TAG】データを全件選択済みとして処理するかどうか(true/false)を指定します(初期値:false)。
	 *
	 * @og.tag
	 * 全てのデータを選択済みデータとして扱って処理します。
	 * 全件処理する場合に、（true/false)を指定します。
	 * 初期値は false です。
	 *
	 * @param  all データを全件選択済み(true) / 通常（false)
	 */
	public void setSelectedAll( final String all ) {
		selectedAll = nval( getRequestParameter( all ),selectedAll );
	}

	/**
	 * 【TAG】カラムＩＤの存在チェックを行うかどうかを指定します(初期値:true)。
	 *
	 * @og.tag
	 * true の場合、カラムＩＤがDBTableModel に存在しない場合は、エラーになります。
	 * false の場合、カラムＩＤがDBTableModel に存在しない場合は、無視します。
	 * これは、検索条件によって、設定されるカラムが異なる場合でも、entryタグを
	 * 正常に動作させたい場合に、使用します。
	 * 初期値は true (チェックを行う) です。
	 *
	 * @param  check 存在チェックを行うかどうか(true:行う) / (false:行わない)
	 */
	public void setStrictCheck( final String check ) {
		strictCheck = nval( getRequestParameter( check ),strictCheck );
	}

	/**
	 * 【TAG】(通常は使いません)画面遷移を行わない形式の登録方法を使用するかを指定します。
	 *
	 * @og.tag
	 * 画面遷移なしの登録を行うかどうかを指定します。
	 * trueが指定された場合、entryタグでは、行の追加・複写時にDBTableModel上の最終行にデータを
	 * 追加します。
	 * 画面遷移なしモードの場合、途中行に挿入された場合、既にクライアントに出力されている
	 * チェックボックスの行番号や各入力フィールドの変数名との整合性を合わせるためには、
	 * 編集行以降の各変数値を全て再計算する必要があります。
	 * この処理は、レスポンス悪化に繋がるため、DBTableModel上は、中間に行の挿入を行いません。
	 * 但し画面表示上は、通常通り選択行の直下に行が挿入されるため、DBTableModelの順番と標準順が
	 * 異なります。(エンジン側では、各チェックボックスの値で行を識別しているため、問題は発生しません)
	 * 
	 * この値は、og:headタグで設定値、または前画面からの値を継承するため、通常、この属性ではセットしません。
	 *
	 * @og.rev 4.3.3.0 (2008/10/01) 新規追加
	 * @og.rev 5.1.3.0 (2010/02/01) noTransition、ajaxSubmitのコントロールは、requestで行う。
	 *
	 * @param   noTrnstn 画面遷移を行わない形式の登録方法を使用するか
	 */
	public void setNoTransition( final String noTrnstn ) {
		setNoTransitionRequest( nval( getRequestParameter( noTrnstn ), isNoTransitionRequest() ) );
//		noTransition = nval( getRequestParameter( noTrnstn ), noTransition );
	}

	/**
	 * シリアライズ用のカスタムシリアライズ書き込みメソッド
	 *
	 * @og.rev 4.0.0 (2006/09/31) 新規追加
	 * @serialData
	 *
	 * @param strm ObjectOutputStream
	 */
	private void writeObject( final ObjectOutputStream strm ) throws IOException {
		strm.defaultWriteObject();
	}

	/**
	 * シリアライズ用のカスタムシリアライズ読み込みメソッド
	 *
	 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
	 *
	 * @og.rev 4.0.0 (2006/09/31) 新規追加
	 * @serialData
	 *
	 * @param strm ObjectInputStream
	 * @see #release2()
	 */
	private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
		strm.defaultReadObject();
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 */
	public String toString() {
		return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
				.println( "VERSION"					,VERSION				)
				.println( "tableId"					,tableId				)
				.println( "command"					,command				)
				.println( "rowNo"      				,rowNo      			)
				.println( "repeatCount"				,repeatCount			)
				.println( "useConsistency"			,useConsistency			)
				.println( "selectedAll"				,selectedAll			)
				.println( "strictCheck"				,strictCheck			)
				.println( "noTransition"			,noTransition			)
				.println( "RESET_ACTION_ALL_USE"	,RESET_ACTION_ALL_USE	)
				.println( "Other..."				,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
