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

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.LinkedHashMap ;
import java.util.Map.Entry;

import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.db.Transaction;
import org.opengion.hayabusa.resource.ResourceManager;
import org.opengion.hayabusa.common.HybsSystemException;

/**
 * AbstractTableFilter は、TableUpda インターフェースを継承した、DBTableModel 処理用の
 * Abstract実装クラスです。
 *
 * @og.rev 5.5.2.6 (2012/05/25) protected変数をprivateに変更。インターフェースにメソッド追加
 *
 * @version  0.9.0  2000/10/17
 * @author   Kazuhiko Hasegawa
 * @since    JDK1.1,
 */
abstract public class AbstractTableFilter implements TableFilter {

	/** CR 定義 */
//	5.5.2.6 (2012/05/25) I/F 側に移動
//	protected static final String CR	= HybsSystem.CR ;	// 5.1.1.0 (2009/12/01) CR 定義をここで行う。

//	protected DBTableModel	table		= null;
//	protected String		modifyType	= null;
//	protected int[]			rowNo		= null;
//	protected boolean		isDebug		= false;
//	protected ApplicationInfo appInfo	= null;	// 5.1.9.0 (2010/08/01) 廃止
//	protected Transaction	tran		= null;	// 5.1.9.0 (2010/08/01) 追加
//	protected String		sql			= null; // 4.2.4.0 (2008/06/23)
//	protected String		dbid		= null; // 4.2.4.0 (2008/06/23)
//	protected ResourceManager resource	= null; // 4.3.7.4 (2009/07/01)

	// 5.5.2.6 (2012/05/25) protected変数をprivateに変更。インターフェースにメソッド追加
	private DBTableModel	table		= null;
	private String			modifyType	= null;
	private int[]			rowNo		= null;
	private boolean			isDebug		= false;
	private Transaction		tran		= null;	// 5.1.9.0 (2010/08/01) 追加
	private String			sql			= null; // 4.2.4.0 (2008/06/23)
	private String			dbid		= null; // 4.2.4.0 (2008/06/23)
	private ResourceManager resource	= null; // 4.3.7.4 (2009/07/01)

	private int 			errCode		= ErrorMessage.OK;
	private ErrorMessage	errMessage	= null;

	private final Map<String,String>	keysVals	= new HashMap<String,String>();

	// 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
	protected final Map<String,String> keysMap = new LinkedHashMap<String,String>();

	/**
	 * デフォルトコンストラクター
	 * ここでは、keys の整合性チェックを行うための初期設定を行う、init( Map<String,String> )
	 * メソッドを呼び出します。
	 * init( Map<String,String> ) メソッドは、各サブクラスで実装が必要です。
	 *
	 * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェック対応
	 */
	public AbstractTableFilter() {
		init( keysMap );
	}

	/**
	 * keys の整合性チェックを行うための初期設定を行います。
	 * ここでは何もしません。必要であれば、各サブクラスに実装しておきます。
	 *
	 * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェック対応
	 *
	 * @param	keysMap keys の整合性チェックを行うための Map
	 */
	protected void init( final Map<String,String> keysMap ) {}

	/**
	 * DBTableModel をセットします。
	 *
	 * @param	table DBTableModelオブジェクト
	 */
	public void setDBTableModel( final DBTableModel table ) {
		this.table = table;
	}

	/**
	 * DBTableModel を取得します。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return	内部のDBTableModel
	 */
	public DBTableModel getDBTableModel() {
		return table;
	}

	/**
	 * データ処理の方法(A:追加 C:更新 D:削除)を指定します。
	 *
	 * 通常は、DBTableModel に自動設定されている modifyType を元に、データ処理方法を
	 * 選別します。(A:追加 C:更新 D:削除)
	 * この場合、行単位で modifyType の値を取得して判別する必要がありますが、一般には
	 * 処理対象は、全件おなじ modifyType である可能性が高いです。
	 * また、selectedAll などで強制的に全件処理対象とする場合は、modifyType に値が
	 * 設定さていません。その様な場合に外部より modifyType を指定します。
	 * 初期値は、自動判定 です。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) 廃止
	 *
	 * @param  type データ処理の方法(A:追加 C:更新 D:削除)
	 */
	public void setModifyType( final String type ) {
		modifyType = type;
	}

	/**
	 * データ処理の方法(A:追加 C:更新 D:削除)を取得します。
	 *
	 * 初期値は、自動判定 です。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return  データ処理の方法(A:追加 C:更新 D:削除)
	 */
	public String getModifyType() {
		return modifyType ;
	}

	/**
	 * キーと値のペアの変数配列を受け取ります。
	 *
	 * ここでは、この方式以外に、パラメーターMapを受け取る方法もあります。
	 * この受け取る時に、キーを大文字化します。TableFilter の keys は、
	 * 大文字のみで定義しておくことで、HTMLやWindows世代の曖昧な表記方法に
	 * 対応しています。(unixやxmlのような厳格な方が好きですけど)
	 *
	 * keys,vals とパラメーターMapを同時に指定した場合は、両方とも有効です。
	 * ただし、キーが重複した場合は、不定と考えてください。
	 *
	 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
	 *
	 * @param   keys キー配列
	 * @param   vals 値配列
	 * @see		#setParamMap( Map  )
	 */
	public void setKeysVals( final String[] keys,final String[] vals ) {
		if( keys != null && vals != null ) {
			for( int i=0; i<keys.length; i++ ) {
				// 5.6.6.0 (2013/07/05) 共通のセッターメソッド経由で登録します。
//				keysVals.put( keys[i].toUpperCase(Locale.JAPAN),vals[i] );
				setKeyVal( keys[i],vals[i] );
			}
		}
	}

	/**
	 * キーと値のペアを受け取り、内部の keysVals マップに追加します。
	 *
	 * キーか値のどちらかが null の場合は、何もしません。つまり、val に
	 * null をセットすることはできません。
	 *
	 * このメソッドは、setKeysVals( String[] ,String[] ) メソッドと、
	 * setParamMap( Map<String,String> ) メソッドの両方から、使用します。
	 * 処理を行うに当たり、下記の処理を行います。
	 * １．キーを大文字化します。
	 * ２．各クラスの keys と整合性チェックを行います。
	 *
	 * ただし、setKeysVals と setParamMap の登録順は、不定と考えてください。
	 * 両方に同じキーを指定すると、どちらの値がセットされたかは、不定です。
	 *
	 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
	 *
	 * @param   key キー文字列(null の場合は、処理しない)
	 * @param   val 値文字列(null の場合は、処理しない)
	 * @see		#setKeysVals( String[] ,String[] )
	 * @see		#setParamMap( Map )
	 */
	private void setKeyVal( final String key,final String val ) {
		// key か val かどちらかが null の場合は、処理を行わない。
		if( key == null || val == null ) { return; }

		String upKey = key.toUpperCase(Locale.JAPAN);

		if( keysMap.containsKey( upKey ) ) {		// keysMap は、各サブクラスで定義
			keysVals.put( upKey,val );
		}
		else {
			String BR = "<br />" + CR ;
			StringBuilder errMsg = new StringBuilder();
			errMsg.append( BR )
				  .append( "指定のキーは、この tableFilter では、使用できません。" ).append( BR )
				  .append( "  class=[" ).append( getClass().getName() ).append( "]" ).append( BR )
				  .append( "  key  =[" ).append( key				  ).append( "]" ).append( BR )
				  .append( "  ======== usage keys ======== " ).append( BR ) ;
			for( Map.Entry<String, String> entry : keysMap.entrySet() ) {
				errMsg.append( "  " ).append( entry.getKey() ).append( " : " )
									 .append( entry.getValue() ).append( BR ) ;
			}
			errMsg.append( "  ============================ " ).append( BR );

			throw new HybsSystemException( errMsg.toString() );
		}
	}

	/**
	 * 選択された行番号の配列をセットします。
	 *
	 * 表示データの HybsSystem.ROW_SELECTED_KEY を元に、選ばれた 行を
	 * 処理の対象とします。
	 *
	 * @param   rowNoTmp 行番号配列
	 */
	public void setParameterRows( final int[] rowNoTmp ) {
		if( rowNoTmp != null ) {
			int size = rowNoTmp.length ;
			rowNo = new int[size];
			System.arraycopy( rowNoTmp,0,rowNo,0,size );
		}
	}

	/**
	 * 選択された行番号の配列を取得します。
	 *
	 * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を
	 * 処理の対象とします。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return   行番号の配列
	 */
	public int[] getParameterRows() {
		return (rowNo != null ) ? rowNo.clone() : null ;
	}

	/**
	 * アクセスログ取得の為,ApplicationInfoオブジェクトを設定します。
	 *
	 * @og.rev 3.8.7.0 (2006/12/15) 新規追加
	 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 のため、廃止
	 *
	 * @param   appInfo ApplicationInfo
	 */
//	public void setApplicationInfo( final ApplicationInfo appInfo ) {
//		this.appInfo = appInfo;
//	}

	/**
	 * アクセスログ取得の為,Transactionオブジェクトを設定します。
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応(新規追加)
	 *
	 * @param   tran Transactionオブジェクト
	 */
	public void setTransaction( final Transaction tran ) {
		this.tran = tran;
	}

	/**
	 * アクセスログ取得の為,Transactionオブジェクトを取得します。
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応(新規追加)
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return   Transactionオブジェクト
	 */
	public Transaction getTransaction() {
		return tran;
	}

	/**
	 * DBIDを指定します。
	 *
	 * @og.rev 4.2.4.0 (2008/06/23) 新規追加
	 *
	 * @param dbid 接続先ID
	 */
	public void setDbid( final String dbid ) {
		this.dbid = dbid;
	}

	/**
	 * DBIDを取得します。
	 *
	 * @og.rev 4.2.4.0 (2008/06/23) 新規追加
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return DBID(接続先情報)
	 */
	public String getDbid() {
		return dbid;
	}

	/**
	 * ボディー部分のSQLを指定します。
	 *
	 * @og.rev 4.2.4.0 (2008/06/23) 新規追加
	 *
	 * @param sql ボディー部分のSQL
	 */
	public void setSql( final String sql ) {
		this.sql = sql;
	}

	/**
	 * ボディー部分のSQLを取得します。
	 *
	 * @og.rev 4.2.4.0 (2008/06/23) 新規追加
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return ボディー部分のSQL
	 */
	public String getSql() {
		return sql;
	}

	/**
	 * パラメーターMapを指定します。
	 *
	 * keys,vals と パラメーターMapを同時に指定した場合は、両方とも有効です。
	 * ただし、キーが重複した場合は、不定と考えてください。
	 *
	 * この受け取る時に、キーを大文字化します。TableFilter の keys は、
	 * 大文字のみで定義しておくことで、HTMLやWindows世代の曖昧な表記方法に
	 * 対応しています。(unixやxmlのような厳格な方が好きですけど)
	 *
	 * @og.rev 5.6.5.2 (2013/06/21) 新規追加
	 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを行います。
	 *
	 * @param paramMap パラメーターMap
	 * @see		#setKeysVals( String[] ,String[] )
	 */
	public void setParamMap( final Map<String,String> paramMap ) {
		if( paramMap != null ) {
//			keysVals.putAll( paramMap );
			// 5.6.6.0 (2013/07/05) Map を一つづつ回して登録します。
			for( Map.Entry<String, String> entry : paramMap.entrySet() ) {
				setKeyVal( entry.getKey(),entry.getValue() );
			}
		}
	}

	/**
	 * リソースオブジェクトを指定します。
	 *
	 * @og.rev 4.3.7.4 (2009/07/01) 新規追加
	 *
	 * @param resource リソースオブジェクト
	 */
	public void setResource( final ResourceManager resource ) {
		this.resource = resource;
	}

	/**
	 * リソースオブジェクトを取得します。
	 *
	 * @og.rev 4.3.7.4 (2009/07/01) 新規追加
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return リソースオブジェクト
	 */
	public ResourceManager getResource() {
		return resource;
	}

	/**
	 * デバッグ情報を表示するかどうか[true/false]を指定します。
	 * true でデバッグ情報を表示します。
	 *
	 * @param   flag  [true:出力する/それ以外:しない]
	 */
	public void setDebug( final boolean flag ) {
		isDebug = flag;
	}

	/**
	 * デバッグ情報を表示するかどうかを取得します。
	 * true でデバッグ情報を表示します。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
	 *
	 * @return  デバッグ情報(true:デバッグ情報を出力する)
	 */
	public boolean isDebug() {
		return isDebug ;
	}

	/**
	 * エラーコード を取得します。
	 * エラーコード は、ErrorMessage クラスで規定されているコードです。
	 *
	 * @return   エラーコード
	 */
	public int getErrorCode() {
		return errCode;
	}

	/**
	 * エラーメッセージオブジェクト を取得します。
	 *
	 * @return   エラーメッセージオブジェクト
	 */
	public ErrorMessage getErrorMessage() {
		return errMessage;
	}

	/**
	 * タイトルとエラーコードを指定して、エラーメッセージオブジェクト を作成します。
	 * すでに、作成済みの場合は、作成済みのオブジェクトを、まだ、未作成の場合は、
	 * 新規に作成します。
	 *
	 * @param	title	タイトル
	 * @param	code	エラーコード
	 *
	 * @return	エラーメッセージオブジェクト
	 */
	protected ErrorMessage makeErrorMessage( final String title,final int code ) {
		if( errMessage == null ) {
			errMessage = new ErrorMessage( title );
		}
		if( errCode < code ) { errCode = code; }
		return errMessage;
	}

	/**
	 *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
	 *
	 * @param	nameArray カラム名配列
	 *
	 * @return	カラムNo配列
	 */
	protected int[] getTableColumnNo( final String[] nameArray ) {
		int[] clmNo = new int[ nameArray.length ];
		for( int i=0; i<clmNo.length; i++ ) {
			clmNo[i] = table.getColumnNo( nameArray[i] );
		}
		return clmNo;
	}

	/**
	 *  設定されたパラメータキーに対する値を取得します。
	 * 引数、および、パラメータが null の場合は、 null を返します。
	 *
	 * @param	key	パラメータキー
	 *
	 * @return	パラメータ値
	 */
	protected String getValue( final String key ) {
		return keysVals.get( key );
	}
}
