/*
 * 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.DBTableModel;
import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.util.XHTMLTag;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletException;
import java.io.IOException;
// import java.io.ObjectInputStream;
// import java.io.ObjectOutputStream;
import java.util.Enumeration;
import java.util.Map;
import java.util.HashMap;
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

/**
 * submitタグを用いてページ転送するタグです(forward.jsp で使用)。
 *
 * 通常、forward は、ページ内転送(サーバー内部での転送)のため、別画面への遷移には
 * 使用できません。これは、別画面では、index.jsp 等でフレーム分割する際の基準フォルダは、
 * forward の場合、クライアントは理解していないため、もう一度先のフォルダに対する
 * フレーム分割を行おうとするためです。
 * (よく、index.jsp の frame タグに、src="../XXXX/query.jsp" などと、自分自身のフォルダ名を
 * 記述したページを見かけますが、これは、フォルダをまたがる転送に、forward を使用する
 * 為の悪い対策です。)
 * 実際は、forward ではなく、redirect を使うべきです。redirect は、指定のアドレス要求を、
 * 一旦クライアントに投げてそこから再度要求しなおしてもらう方式のため、このようにフォルダを
 * またがる転送も正常に処理できます。
 * この、commonForward タグでは、画面遷移の条件に応じて、forward か redirect の自動
 * 判定を行い、適切に処理しています。
 * 判定条件は、拡張子や、選択件数などを加味して以下の判定を順次テストします。
 *
 *   FORWARD :
 *       アドレスが、 null(自分自身) か、.jsp を含み、"/" が入っていない場合
 *   REDIRECT:
 *       アドレスが、.jsp を含まないか、
 *       それ以外(.jsp を含み、"/" も含む)で、選択数が１件のみの場合
 *       もしくはuseRedirectCheck="false"の場合
 *   COUNT_0 :
 *       それ以外で、選択数が０件の場合
 *   COUNT_N :
 *       それ以外で、選択数が１件以上の場合、または、その他。
 *
 * ここで、COUNT_0 の場合は、未選択エラー、COUNT_N は、複数選択エラーを自動的に返します。
 *
 * @og.formSample
 * ●形式：&lt;og:commonForward /&gt;
 * ●body：なし
 *
 * ●Tag定義：
 *   &lt;og:commonForward
 *       dbkeys             【TAG】DBキーをCSV形式でセットします
 *       tableId            【TAG】(通常使いません)sessionから所得する DBTableModelオブジェクトの ID
 *       useRedirectCheck   【TAG】非選択状態の場合にforwardを許可するかどうか[true/false]を指定します(初期値:true)
 *       useRedirectHidden  【TAG】redirectの処理時に、hiddenパラメータを転送するかどうか[true:する/false:しない]指定します(初期値:false)。
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   /&gt;
 *
 * ●使用例
 *     フォワードキャッシュによりページ転送します。
 *     &lt;og:commonForward dbkeys="{&#064;dbkeys}" /&gt;
 *
 * @og.group 画面制御
 *
 * @version  4.0
 * @author	 Masaharu Endo
 * @since    JDK5.0,
 */
public class CommonForwardTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.4.3.1 (2016/02/12)" ;
	private static final long serialVersionUID = 643120160212L ;

	// 3.5.5.3 (2004/04/09) 共通アドレスで指定することで、クライアントキャッシュを有効利用する。
	private static final String DUMMY_HTML	= "/" + HybsSystem.getContextName() + "/jsp/common/dummy.html";

	// 3.8.5.1 (2006/04/28) dbkeys が null の場合に全件取得するかどうかを COMMON_FORWARD_DBKEYS_NULL_ALL で指定します。

	private static final int FORWARD  = 0;
	private static final int REDIRECT = 1;
	private static final int COUNT_0  = 2;
	private static final int COUNT_N  = 3;

	// 3.5.5.2 (2004/04/02) 選択行が、１行のみか、そうでないか
	private int rowCount = -1;

	private int	rowNo	 = -1;

	// 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
	private String	dbkeys		;

	// 3.5.5.8 (2004/05/20) submitタグの keys,vals を扱う 仮想リクエストMap
	private	String	tableIdTemp	;
	private String	dbkeysTemp	;
//	private transient Map<String,String> submitRequestMap = new HashMap<>();	// 3.5.6.2 (2004/07/05)
//	private transient Map<String,String> submitTableMap	  = new HashMap<>();	// 3.8.5.1 (2006/04/28)
	/** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
	private final transient ConcurrentMap<String,String> submitRequestMap = new ConcurrentHashMap<>();
	/** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
	private final transient ConcurrentMap<String,String> submitTableMap	  = new ConcurrentHashMap<>();

	// 4.0.0.0 (2007/11/09) 非選択状態でのforwardのための属性追加
	private boolean isRedirectCheck		= true;

	// 6.2.4.0 (2015/05/15) redirectの処理時に、hiddenパラメータを転送するかどうか[true:する/false:しない]
	// 互換性の関係で、初期値は、false にします。
	private boolean isRedirectHidden	;

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public CommonForwardTag() {
		super();		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 3.3.1.1 (2003/07/03) ＵＲＬにリクエスト情報をＵＲＬエンコードして追加します。
	 * @og.rev 3.5.5.2 (2004/04/02) フォルダ外転送時は、１行以外選択は、エラーとします。
	 * @og.rev 3.5.5.3 (2004/04/09) デバッグ時は、転送しないようにします。
	 * @og.rev 3.8.0.4 (2005/08/08) requestUrlEncode 廃止
	 * @og.rev 3.8.0.8 (2005/10/03) dbkeys が null の場合に全件取得していた処理を復活します。
	 * @og.rev 4.0.0.0 (2007/11/09) 非選択状態からの遷移を許可するフラグを追加(11/12に振り分け処理をselectResponseMethodに移動)
	 * @og.rev 5.0.0.2 (2009/09/15) XSS対応⇒チェックする
	 * @og.rev 5.3.6.0 (2011/06/01) エラーメッセージ変更(URLの振り分け処理時に...のメッセージは出力しない)
	 * @og.rev 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)

		// useXssCheck( false ); // 5.0.0.2 (2009/09/15)

		final HttpServletRequest request  = ((HttpServletRequest)getRequest());
		final HttpSession session = pageContext.getSession();
		String page = getForwardURI( request, session );

		// 3.5.5.8 (2004/05/20) 内部仮想リクエストMap より値を取得
		dbkeys = nval( getSubmitRequestParameter( dbkeysTemp ),dbkeys );

		final HttpServletResponse response = (HttpServletResponse)pageContext.getResponse();

		String errMsgKey = null;
		try {
			// 3.5.5.2 (2004/04/02) 振り分け条件を判定するメソッドを通します。
			// このメソッドにより、useTableData が再設定されます。処理順に注意
			final int flag = selectResponseMethod( page );

			// 3.8.0.8 (2005/10/03) dbkeys が null の場合に全件取得していた処理を復活します。
			// 6.2.4.0 (2015/05/15) dbkeys != null の場合しか、[]付のパラメータを処理できなかった。
//			if( rowCount == 1 && ( dbkeys != null || submitTableMap.size() > 0 ) ) {
			if( rowCount == 1 && ( dbkeys != null || !submitTableMap.isEmpty() ) ) {		// 6.3.9.0 (2015/11/06) size() == 0 判定は、isEmpty() に置き換え可能
				page = XHTMLTag.addUrlEncode( page,getTableUrlData() );
			}

			final String url = response.encodeRedirectURL( page );
			// 3.8.0.8 (2005/10/03) GET時の URL の長さ制限チェック(最大文字数は 2,083 文字)
			if( url != null && url.length() >= HybsSystem.MAX_GET_LENGTH ) {
				final String errMsg = "GET時の URL の長さは,最大2,083 文字です。"
							+ " URL.length=" + url.length() + " , MAX_LENGTH=" + HybsSystem.MAX_GET_LENGTH ;
				throw new HybsSystemException( errMsg );
			}

			switch( flag ) {
				case FORWARD:	if( isDebug() ) { jspPrint( "FORWARD URL = [" + url + "]" ); }
								else { pageContext.forward( url ); }
								break;
				case REDIRECT:	// url = requestUrlEncode( url );		// 3.8.0.4 (2005/08/08)
								if( isDebug() ) { jspPrint( "REDIRECT URL = [" + url + "]" ); }
								else { response.sendRedirect( url ); }
								break;
				case COUNT_0:
								errMsgKey = "ERR0028" ;  // 選択されていません。もう一度、選択しなおして下さい。
								break;
				default: errMsgKey = "ERR0029" ;	// 複数選択されました。１件のみ選択しなおして下さい。
								break;
			}

		} catch ( IOException ex ) {
			// 5.3.6.0 (2011/06/01) エラーメッセージ表示変更
			final String errMsg = ex.getMessage();					// 5.1.8.0 (2010/07/01) errMsg 修正
			throw new HybsSystemException( errMsg,ex );		// 3.5.5.4 (2004/04/15) 引数の並び順変更
		} catch ( ServletException ex ) {
			// 5.3.6.0 (2011/06/01) エラーメッセージ表示変更
			final String errMsg = ex.getMessage();					// 5.1.8.0 (2010/07/01) errMsg 修正
			throw new HybsSystemException( errMsg,ex );		// 3.5.5.4 (2004/04/15) 引数の並び順変更
		}

		// 3.5.5.2 (2004/04/02) フォルダ外転送時は、１行以外選択は、エラーとします。
		if( errMsgKey != null ) {
			final ErrorMessage errMsg = new ErrorMessage( "Row Count Error Maximal Error!" );
			errMsg.addMessage( 0,ErrorMessage.NG,errMsgKey );

			jspPrint( TaglibUtil.makeHTMLErrorTable( errMsg,getResource() ) );
		}

		return SKIP_PAGE ;		// ページの残りの処理を行わない。
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 3.5.5.2 (2004/04/02) 新規追加(rowCount,useTableData)
	 * @og.rev 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
	 * @og.rev 3.8.5.1 (2006/04/28) URLに連結するDBTableModelのカラムを[カラム]形式で指定します。
	 * @og.rev 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
	 */
	@Override
	protected void release2() {
		super.release2();
		rowCount     = -1;
		rowNo		 = -1;
		dbkeys		 = null;		// 3.5.5.5 (2004/04/23)
//		submitRequestMap	= new HashMap<>();		// 3.5.5.8 (2004/05/20)
//		submitTableMap		= new HashMap<>();		// 3.8.5.1 (2006/04/28)
//		submitRequestMap	= new ConcurrentHashMap<>();		// 6.4.3.1 (2016/02/12)
//		submitTableMap		= new ConcurrentHashMap<>();		// 6.4.3.1 (2016/02/12)
		submitRequestMap.clear();		// 6.4.3.3 (2016/03/04)
		submitTableMap.clear();			// 6.4.3.3 (2016/03/04)
		tableIdTemp	 		= null;		// 3.5.5.8 (2004/05/20)
		dbkeysTemp	 		= null;		// 3.5.5.8 (2004/05/20)
		isRedirectCheck		= true;		// 4.0.0.0 (2007/11/12)
		isRedirectHidden	= false;	// 6.2.4.0 (2015/05/15)
	}

	/**
	 * フォワード先URIを取得します。
	 *
	 * @og.rev 3.1.2.0 (2003/04/07) ソースコード中の固定値を、定義ファイルを使用するように変更する。
	 * @og.rev 3.1.4.1 (2003/04/21) request.getQueryString() をaddUrlEncodeしている箇所を削除。
	 * @og.rev 3.1.7.0 (2003/05/02) コマンド RENEW で、forward した場合に、result.jsp に遷移するように修正。
	 * @og.rev 3.1.7.0 (2003/05/02) 画面ＩＤのセットで、自画面を、BACK_GAMENID 、飛び先を、GAMENID にする。
	 * @og.rev 3.1.8.0 (2003/05/16) SubmitData クラスを利用するように変更。
	 * @og.rev 3.3.1.1 (2003/07/03) ForwardManager クラスの廃止。飛び先のキャッシュを廃止します。
	 * @og.rev 3.5.5.2 (2004/04/02) 選択行の件数を設定しておきます。
	 * @og.rev 3.5.5.3 (2004/04/09) dummy.html を static final で絶対パス指定します。
	 * @og.rev 3.5.5.4 (2004/04/15) メソッド内で使用していない、gamenId,jspID 変数を削除します。
	 * @og.rev 3.5.5.5 (2004/04/23) 余計なボタン関連情報を転送しない為に、キーを変更します。
	 * @og.rev 3.5.5.5 (2004/04/23) SubmitTag の keys,vals 属性で指定した値のみ、転送します。
	 * @og.rev 3.5.5.8 (2004/05/20) SubmitTag の keys,vals 属性で指定した値を、内部仮想リクエスト Mapにセットします。
	 * @og.rev 3.7.0.1 (2005/01/31) リクエスト変数に選択された件数を追加
	 * @og.rev 3.7.0.3 (2005/03/01) 指定の行番号まで画面をスクロールさせる機能を追加。
	 * @og.rev 3.8.0.8 (2005/10/03) BACK_GAMENID があれば BACK_ROW を追加する。
	 * @og.rev 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
	 * @og.rev 5.1.8.0 (2010/07/01) VIEWの場合も、直前のJSPに遷移する。
	 * @og.rev 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
	 *
	 * @param	request	HttpServletRequestオブジェクト
	 * @param	session	HttpSessionオブジェクト
	 *
	 * @return	フォワード先URI
	 * @og.rtnNotNull
	 */
	private String getForwardURI( final HttpServletRequest request,
								  final HttpSession        session ) {

		final String[] rows   = request.getParameterValues( HybsSystem.ROW_SEL_KEY );

		// 4.0.0 (2007/05/16) query.jsp で複数command 時の処理修正
		String   cmd  = request.getParameter( "command" );
		final String[] cmds = request.getParameterValues( "command" );
		if( cmds != null && cmds.length > 1 ) {
			for( int i=0; i<cmds.length; i++ ) {
				if( ! "NEW".equals( cmds[i] ) ) {
					cmd = cmds[i]; break;	// NEW でない、最初の一つ
				}
			}
			// すべてが NEW の場合は、単体(getParameter) が NEW なので素通りでよい。
		}

		// 3.5.5.2 (2004/04/02) 選択行の件数
		// 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
		if( rows == null ) {
			rowCount = 0;
		 }
		else {
//		if( rows != null ) {
			rowCount = rows.length;
			rowNo = Integer.parseInt( rows[0] );
		}
//		else {
//			rowCount = 0;
//		 }
		// 3.7.0.1 (2005/01/31) リクエスト変数に選択された件数を追加
		setRequestAttribute( "ROW_COUNT",String.valueOf( rowCount ) );

		if( cmd == null || cmd.isEmpty() ) { cmd = "INIT"; }
		final String forwardPage;

		final String backPage = (String)session.getAttribute( HybsSystem.FORWARD_PAGE_KEY );
		String command  = cmd;		// 3.5.5.5 (2004/04/23)
		int    pagePlus = 0;

		// コマンドパラメータにより振分け
		// 5.1.8.0 (2010/07/01) VIEWの場合も、直前のJSPに遷移する。
		if( "FIRST,PREV,NEXT,LAST,VIEW".indexOf( cmd ) >= 0 ) {
			forwardPage = backPage;
		} else if( "NEW,RENEW".indexOf( cmd ) >= 0 ) {
			// 初期値
			forwardPage = "result.jsp";
		} else if( "INIT".equals( cmd ) ) {
			forwardPage = DUMMY_HTML;			// 3.5.5.3 (2004/04/09)
		} else {
			// 共有オブジェクト検索

			// リンク元コマンド名取得
			// 3.5.5.5 (2004/04/23) 余計なボタン関連情報を転送しない為に、キーを変更します。
			command = request.getParameter( HybsSystem.NO_XFER_KEY + cmd + "CMD" );

			// 3.7.0.3 (2005/03/01) 指定の行番号まで画面をスクロールさせる機能を追加。
			if( rows != null && "ENTRY".equals( command ) ) {
				setRequestCacheData( "SEL_ROW",String.valueOf( rowNo ) );
			}

			if( "RESET".equals( command ) ) { // RESET 時
				forwardPage = "result.jsp";
			}
			else {
				// リンク先取得
				forwardPage = request.getParameter( HybsSystem.NO_XFER_KEY + cmd );
				// INSERTとCOPYの場合のみ
				if( "INSERT".equals( command ) || "COPY".equals( command ) ) {
					if( rows != null ) { pagePlus = rows.length; }
				}
			}
		}

		if( ! forwardPage.equals( backPage ) ) {
			session.setAttribute( HybsSystem.REVIEW_PAGE_KEY , backPage );
		}
		session.setAttribute( HybsSystem.FORWARD_PAGE_KEY, forwardPage );

		// 3.5.5.5 (2004/04/23) SubmitTag の keys,vals 属性で指定した値のみ、転送します。
		// 6.0.2.5 (2014/10/31) char を append する。
		final StringBuilder strURL = new StringBuilder( BUFFER_MIDDLE )
			.append( '&' ).append( "command"  ).append( '=' ).append( command )
			.append( '&' ).append( "pagePlus" ).append( '=' ).append( String.valueOf( pagePlus ) );

		final String btnKey = HybsSystem.NO_XFER_KEY + cmd + "KEY_" ;
		final int    keylen = btnKey.length() ;

		final Enumeration<?> enm = getParameterNames();		// 4.3.3.6 (2008/11/15) Generics警告対応

		// 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
		final Map<String,String> hiddenMap = new HashMap<>();
		boolean isGamenIdFlag = false;

		while( enm.hasMoreElements() ) {
			final String key = (String)( enm.nextElement() );
			// 3.5.5.5 (2004/04/23) 余計な情報を転送しない様に、キーを選別します。
			if( key != null && key.startsWith( btnKey ) ) {
				// 3.5.5.8 (2004/05/20) 内部の仮想リクエスト Map に設定します。
				final String kk = key.substring( keylen ) ;
//				final String vv = getRequestValue( key ) ;
				final String vv = nval( getRequestValue( key ) , "" );			// 6.4.3.1 (2016/02/12) ConcurrentHashMap の条件は、key,val ともに、not null
				// 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
//				if( vv != null && vv.length() > 2 && vv.charAt(0) == '[' && vv.charAt(vv.length()-1) == ']' ) {
				if( vv.length() > 2 && vv.charAt(0) == '[' && vv.charAt(vv.length()-1) == ']' ) {
					submitTableMap.put( kk,vv.substring( 1,vv.length()-1 ));
				}
	//			else if( "dbkeys".equals( kk ) ) {
	//				submitRequestMap.put( kk,vv );
	//			}
				else {
					submitRequestMap.put( kk,vv );
					strURL.append( '&' )					// 6.0.2.5 (2014/10/31) char を append する。
						.append( kk ).append( '=' )			// 6.0.2.5 (2014/10/31) char を append する。
						.append( StringUtil.urlEncode( vv ) );
				}

				// 3.8.0.8 (2005/10/03) BACK_GAMENID があれば BACK_ROW を追加する。
				if( "BACK_GAMENID".equalsIgnoreCase( kk ) && rowNo >= 0 ) {
					strURL.append( "&BACK_ROW=" ).append( rowNo );
				}

				// 6.2.4.0 (2015/05/15) 個別に GAMENID があれば、submitTag で指定されたはず。
				if( "GAMENID".equalsIgnoreCase( kk ) ) {
					isGamenIdFlag = true;
				}
			}
			// 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
			// 取りあえず対象のリクエストは、Mapにセットしておきます。
			if( isRedirectHidden && key != null && !key.startsWith( "h_" ) && !key.startsWith( "hX_" ) ) {
				hiddenMap.put( key,getRequestValue( key,false ) );
			}
		}

		// 6.2.4.0 (2015/05/15) useRedirectHidden 属性追加
		// 個別に GAMENID がある場合(isGamenIdFlag == true)のみ、このフラグを有効にします。
		if( isRedirectHidden && isGamenIdFlag ) {
			for( final Map.Entry<String,String> entry : hiddenMap.entrySet() ) {
				final String key = '&' + entry.getKey() + '=';		// strURL は、必ず & で始まっている。
				if( strURL.indexOf( key ) < 0 ) {				// すでに、個別定義済みのパラメータは転送しない。
					strURL.append( key ).append( StringUtil.urlEncode( entry.getValue() ) );
				}
			}
		}

		return XHTMLTag.addUrlEncode( forwardPage,strURL.toString() );
	}

	/**
	 * ページを リダイレクトかフォワードか選択します。
	 * 判定条件は、拡張子や、選択件数などを加味して以下の判定を順次テストします。
	 *
	 *   FORWARD :
	 *       アドレスが、 null か、.jsp を含み、"/" が入っていない場合
	 *   REDIRECT:
	 *       アドレスが、.jsp を含まないか、それ以外(.jsp を含み、"/" も含む)で、選択数が１件のみの場合
	 *   COUNT_0 :
	 *       それ以外で、選択数が０件の場合
	 *   COUNT_N :
	 *       それ以外で、選択数が１件以上の場合、または、その他。
	 *
	 * @og.rev 3.5.5.2 (2004/04/02) 新規作成：isJspPrefix( String page ) の代用です。
	 * @og.rev 4.0.0.0 (2007/05/23) useTableData の書き換えを中止します。
	 * @og.rev 4.0.0.0 (2007/11/12) 非選択状態でもリダイレクト可能なフラグ(isRedirectCheck)を追加
	 *
	 * @param   page	判定する転送先アドレス
	 *
	 * @return  FORWARD,REDIRECT,COUNT_0,COUNT_N のうち、どれか
	 */
	private int selectResponseMethod( final String page ) {
		if( page == null ) { return FORWARD; }

		final int adrs = page.indexOf( ".jsp" );

		if( adrs >= 0 && page.lastIndexOf( '/',adrs ) < 0 ) {
			return FORWARD;
		}
		else if( adrs < 0 || !isRedirectCheck ) { // 4.0.0.0 (2007/11/12) 非選択リダイレクト許可フラグ追加
			return REDIRECT;
		}
		else if( rowCount == 1 ) {
			return REDIRECT;
		}
		else if( rowCount == 0 ) {
			return COUNT_0;
		}
		else {
			return COUNT_N;
		}
	}

	/**
	 * 【TAG】(通常使いません)sessionから所得する DBTableModelオブジェクトの ID
	 *		(初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
	 *
	 * @og.tag
	 * 表示処理後に，(内部ポインタを書き換えた)DBTableModelオブジェクトを
	 * 同じキーで、sessionに登録します。
	 *		(初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
	 *
	 * @og.rev 3.5.5.2 (2004/04/02) 新規追加
	 * @og.rev 3.5.5.8 (2004/05/20) 内部仮想リクエスト Map を参照できるようにする。
	 *
	 * @param	id テーブルID (sessionから所得する時のID)
	 */
	public void setTableId( final String id ) {
		// 注意:引数のリクエスト変数が無ければ、自分自身({@XXX}形式)を再セットする。
		tableIdTemp   = nval( getRequestParameter( id ),id );
	}

	/**
	 * 指定のスコープの内部キャッシュ情報に、DBTableModel の選択された値を登録します。
	 *
	 * 複数選択行が存在する場合は、先頭行を処理します。ただし、action="APPEND"の
	 * 場合は、separator属性で指定された文字を使用して、連結します。
	 *
	 * @og.rev 3.5.5.2 (2004/04/02) 新規作成
	 * @og.rev 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
	 * @og.rev 3.8.0.4 (2005/08/08) dbkeys が null の場合に全件取得していた処理を中止します。
	 * @og.rev 3.8.0.8 (2005/10/03) dbkeys が null の場合に全件取得していた処理を復活します。
	 * @og.rev 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
	 * @og.rev 3.8.5.1 (2006/04/28) dbkeys が null の場合に全件取得するかどうかを COMMON_FORWARD_DBKEYS_NULL_ALL で指定します。
	 * @og.rev 4.0.0.0 (2007/05/23) tableId をこのメソッド内で求めます。
	 *
	 * @return	DBTableModelの選択された値の文字列
	 * @og.rtnNotNull
	 */
	private String getTableUrlData() {
		final String tableId = nval( getSubmitRequestParameter( tableIdTemp ),HybsSystem.TBL_MDL_KEY );

		final DBTableModel table = (DBTableModel)getSessionAttribute( tableId );

		String dbkeysUrl  = "";
		String tblkeysUrl = "";

		if( table != null ) {
			String[] keys = null;
			String[] vals = null;
			if( dbkeys != null ) {
				keys = StringUtil.csv2Array( dbkeys );
				vals = new String[keys.length];
				for( int i=0; i<keys.length; i++ ) {
					final int clmNo = table.getColumnNo( keys[i] );
					vals[i] = table.getValue(rowNo,clmNo);
				}
			}

			dbkeysUrl = XHTMLTag.urlEncode( keys, vals );

			// 3.8.5.1 (2006/04/28) vals="[カラム名]" という引数を処理できる機能を追加。
			final int size = submitTableMap.size();
			if( size > 0 ) {
				@SuppressWarnings("rawtypes")
				final Map.Entry[] entry = submitTableMap.entrySet().toArray( new Map.Entry[size] );

				String[] tblkeys = new String[size];
				String[] tblvals = new String[size];

				for( int i=0; i<size; i++ ) {
					tblkeys[i]  = (String)entry[i].getKey();
					final String temp = (String)entry[i].getValue();
					final int clmNo = table.getColumnNo( temp );
					tblvals[i] = table.getValue( rowNo,clmNo );
				}
				tblkeysUrl = XHTMLTag.urlEncode( tblkeys, tblvals );
			}
		}

		String rtn = dbkeysUrl;

		if( tblkeysUrl.length() > 0 ) {
			if( rtn.length() > 0 ) {
				rtn += "&" + tblkeysUrl;
			}
			else {
				rtn = tblkeysUrl ;
			}
		}
		return rtn ;
	}

	/**
	 * 【TAG】DBキーをCSV形式でセットします。
	 *
	 * @og.tag
	 * URI の引数にセットするキーを CSV形式でセットします。
	 * ここの指定は,DBTableModel 上のデータを取り込みます。
	 *
	 * @og.rev 3.5.5.5 (2004/04/23) URLに連結するDBTableModelのカラムをCSV形式で指定します。
	 * @og.rev 3.5.5.8 (2004/05/20) 内部仮想リクエスト Map を参照できるようにする。
	 *
	 * @param	key DBキー(CSV形式)
	 */
	public void setDbkeys( final String key ) {
		// 注意:引数のリクエスト変数が無ければ、自分自身({@XXX}形式)を再セットする。
		dbkeysTemp = nval( getRequestParameter( key ),key ) ;
	}

	/**
	 * 内部の仮想リクエスト Map より、リクエストパラメータより値を設定します。
	 *
	 * submitタグの keys,vals より送信されたリクエスト値は、このクラスで
	 * 処理され、内部の仮想リクエスト Map に保存されます。
	 * 通常のリクエスト設定時点では、この値は取り出すことが出来ない為、
	 * Map に保存(getForwardURI 処理で設定)された後に、引き出します。
	 *
	 * @og.rev 3.5.5.8 (2004/05/20) 新規作成
	 *
	 * @param	key	DBキー(CSV形式)
	 *
	 * @return	仮想リクエスト Map を反映させた、リクエスト値
	 */
	private String getSubmitRequestParameter( final String key ) {
		String rtn = key;

		// 変数が "{@XXXX}" の場合のみ対応
		if( key != null && key.startsWith( "{@" ) && key.charAt(key.length()-1) == '}' ) {
			rtn = submitRequestMap.get( key.substring( 2,key.length()-1 ) );
		}

		return rtn;
	}

	/**
	 * 【TAG】非選択状態の場合にforwardを許可するかどうか[true/false]を指定します(初期値:true)。
	 *
	 * @og.tag
	 * 初期値はtrueが設定されています
	 * falseにすると許可されます
	 *
	 * @og.rev 4.0.0.0 (2007/11/09) 新規作成
	 *
	 * @param	flag	非選択状態のforward許可 [true:不許可/false:許可]
	 */
	public void setUseRedirectCheck(final String flag) {
		isRedirectCheck = nval( getRequestParameter( flag ),isRedirectCheck );
	}

	/**
	 * 【TAG】redirectの処理時に、hiddenパラメータを転送するかどうか[true:する/false:しない]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * submitTag で、gamenId を指定すると、redirect されます。
	 * その場合、従来の画面では、hidden 属性が転送されていなかったため、
	 * 個別に keys,vals で指定する必要がありました。
	 * hidden で記述した値は、redirect でも転送できるようにします。
	 * なお、個別定義済みのパラメータがあり、hiddenパラメータを同じキーの場合は、
	 * 個別パラメータを優先し、hiddenパラメータは転送しません。
	 *
	 * 本来は、gamenId指定の submit の場合でも、必要最小限の値のみ転送すべきで、
	 * この値を true にするのは、過去のJSPソースを、修正せずに使いたいケースに限定してください。
	 * 互換性の関係で、初期値は、false にします。
	 *
	 * @og.rev 6.2.4.0 (2015/05/15) 新規作成
	 *
	 * @param	flag	非選択状態のforward許可 [true:不許可/false:許可]
	 */
	public void setUseRedirectHidden(final String flag) {
		isRedirectHidden = nval( getRequestParameter( flag ),isRedirectHidden );
	}

//	/**
//	 * シリアライズ用のカスタムシリアライズ書き込みメソッド。
//	 *
//	 * @og.rev 4.3.1.1 (2008/08/23) 新規追加
//	 * @serialData 一部のオブジェクトは、シリアライズされません。
//	 *
//	 * @param	strm	ObjectOutputStreamオブジェクト
//	 * @throws IOException	シリアライズに関する入出力エラーが発生した場合
//	 */
//	private void writeObject( final ObjectOutputStream strm ) throws IOException {
//		strm.defaultWriteObject();
//	}

//	/**
//	 * シリアライズ用のカスタムシリアライズ読み込みメソッド
//	 *
//	 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
//	 *
//	 * @og.rev 4.3.1.1 (2008/08/23) 新規追加
//	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
//	 * @og.rev 6.4.3.3 (2016/03/04) clear() するだけなので、なにもセットしません。
//	 *
//	 * @serialData 一部のオブジェクトは、シリアライズされません。
//	 *
//	 * @param	strm	ObjectInputStreamオブジェクト
//	 * @see #release2()
//	 * @throws IOException	シリアライズに関する入出力エラーが発生した場合
//	 * @throws ClassNotFoundException	クラスを見つけることができなかった場合
//	 */
//	private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
//		strm.defaultReadObject();
////		submitRequestMap	= new HashMap<>();
////		submitTableMap		= new HashMap<>();
//		submitRequestMap	= new ConcurrentHashMap<>();		// 6.4.3.1 (2016/02/12)
//		submitTableMap		= new ConcurrentHashMap<>();		// 6.4.3.1 (2016/02/12)
//	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"				,VERSION			)
				.println( "rowCount"			,rowCount			)
				.println( "rowNo"				,rowNo				)
				.println( "dbkeys"				,dbkeys				)
				.println( "tableIdTemp"			,tableIdTemp		)
				.println( "dbkeysTemp"			,dbkeysTemp			)
				.println( "useRedirectCheck"	,isRedirectCheck	)	// 6.2.4.0 (2015/05/15)
				.println( "useRedirectHidden"	,isRedirectHidden	)	// 6.2.4.0 (2015/05/15)
				.println( "Other..."			,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
