/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/

//
//	基本XMO DAO
//			変更履歴
//				2004.01.08	新規作成	二條智文
//
package jp.co.ntt.lms.xmo;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;

import org.apache.commons.lang.StringEscapeUtils;

import jp.co.ntt.lms.xmo.util.DebugLog;
import jp.co.ntt.lms.xmo.util.SqlEscape;

/**
 * 基本XMO DAO<BR>
 * データベースのトランザクション管理を行います。<BR>
 * trans()メソッドを実行することで、トランザクションが開始されます。<BR>
 * 終了時はcommitまたはrollbackメソッドを必ず実行してください。<BR>
 * また、XMO間でのトランザクションの共有を行います。<BR>
 * コネクションインデックスを指定すると、キャッシュから指定された
 * コネクションを取得してきます。そのときのcommit、rollback処理は上位の
 * 呼び出し側で行うことになります。<BR>
 * 基本XMOの各DAOはこのクラスを使用してください。
 * @author T.Nijou
 * @version 0.0.0.1 2004/01/08
 */
public class DataAccess {

	/**
	 * 対象となるテーブル名を保持します
	 */
	private String mstrTableName;

	/**
	 * コミットモードを保持します<BR>
	 * true: コミットを行います
	 * false: コミットは上位に任されます。
	 */
	private boolean mblIsCommit;

	/**
	 * データアクセス操作クラスを保持します
	 */
	private DataAccessHandler mobjDataAcHandler = null;

	/**
	 * DBコネクションを共有する場合のためにDBコネクションを保持します
	 */
	private static Hashtable mhtbConList = new Hashtable();

	/**
	 * 文字変換時の変換先文字セット名です。
	 */
	private static final String AFTER_CHARSET = "Windows-31J";

	/**
	 * 文字変換時の変換前文字セット名です。
	 */
	private static final  String BEFORE_CHARSET = "ISO-8859-1";

	/**
	 * キャッシュしているコネクションインデックスを取得するキーです
	 */
	public static final String CONNECTIONINDEX = "connectionindex";

	/**
	 * 検索文字列を取得するためのキーです
	 */
	public static final String CONDITION_KEY = "condition_key";

	/**
	 * 順序文字列を取得するためのキーです
	 */
	public static final String ORDER_BY = "order_by";

	/**
	 * Dade型変換フラグを保持します<BR>
	 * true: 変換を行います
	 * false:変換無し。
	 */

	private boolean boolChangFlag;
	/**
	 * Dade型フィールドを保持します<BR>
	 */
	  private String[] StrDateField = {
	  		"study_start_date"
	  		,"enddate"
	  		,"last_access"
	  		,"passday"
	  		,"study_end_date"
	  		,"score_date"
	  		,"StartDate"
	  		,"EndDate"
	  		,"time_stamp"
	  		,"\"date\""
	  		, "opening_date"
	  		, "closing_date"
	  	};

	/**
	 * Oracle予約語変換フラグを保持します<BR>
	 * true: 変換を行います
	 * false:変換無し。
	 */

	private boolean boolKeyChangFlag;

	/**
	 * 予約語フィールドを保持します<BR>
	 */
	private String[] StrKeyField = {"UID","comment","date"};
	
	private String[] StrKeyField_PSQL = {"UID","OID","COMMENT","DATE"};


	/**
	 * コンストラクタです。<BR>
	 */
	public DataAccess() {
	}

	/**
	 * コンストラクタです。<BR>
	 * 操作対象テーブルを指定して生成します
	 * @param strTableName
	 */
	public DataAccess( String strTableName ) {
		mstrTableName = strTableName;
	}

	/**
	 * DBのトランザクションを開始します
	 */
	public void trans()
		throws SQLException
	{
		mobjDataAcHandler = new DataAccessHandler();
		// コネクションリストに登録します
		String strIndex = Integer.toString( mobjDataAcHandler.getConnectionIndex() );
		mhtbConList.put( strIndex, mobjDataAcHandler );
		// コミットするモードに設定します
		mblIsCommit = true;
	}

	/**
	 * DBのトランザクションを開始します<BR>
	 * 指定されたインデックスのコネクションを使用します<BR>
	 * この場合commit,rollback処理は上位に任されます。
	 * @param intIndex インデックス
	 */
	public void trans( int intIndex )
		throws SQLException
	{
		// コネクションリストから取得します
		mobjDataAcHandler = (DataAccessHandler)mhtbConList.get( Integer.toString( intIndex ) );
		// コミットしないモードに設定します
		mblIsCommit = false;
	}

	/**
	 * DBのトランザクションを開始します<BR>
	 * パラメータ情報のHashtableからインデックス情報が取得できた
	 * 場合は指定されたインデックスのコネクションを使用します<BR>
	 * この場合commit,rollback処理は上位に任されます。
	 */
	public void trans( Hashtable htParam )
		throws SQLException
	{
		Object objConIndex = htParam.get( CONNECTIONINDEX );
		if( objConIndex == null ) {
			trans();
		}
		else {
			try {
				int intIndex = ((Integer)objConIndex).intValue();
				trans( intIndex );
			}
			catch( Exception ne ) {
				trans();
			}
		}
	}

	/**
	 * コミット処理を行います。<BR>
	 * コミットしないモードの場合なにも行いません。
	 */
	public void commit()
		throws SQLException
	{
		if( mobjDataAcHandler == null ) {
			return;
		}

		if( mblIsCommit ) {
			mobjDataAcHandler.commit();
		}
	}

	/**
	 * ロールバック処理を行います。<BR>
	 * コミットしないモードの場合は何もしません。
	 */
	public void rollback()
		throws SQLException
	{
		if( mobjDataAcHandler == null ) {
			return;
		}

		if( mblIsCommit ) {
			mobjDataAcHandler.rollback();
		}
	}

	/**
	 * クエリーを実行します
	 * @param strSQL	実行するSQL文
	 * @return ResultSet 結果セット
	 */
	public ResultSet executeQuery( String strSQL )
		throws SQLException
	{
		return mobjDataAcHandler.executeQuery(strSQL);
	}

	/**
	 * SQLを実行します
	 * @param strSQL	実行するSQL文
	 */
	public void execute( String strSQL )
		throws SQLException
	{
		mobjDataAcHandler.execute(strSQL);
	}

	/**
	 * 現在使用しているコネクションのインデックスを取得します
	 * @return int インデックス
	 */
	public int getConnectionIndex() {
		return mobjDataAcHandler.getConnectionIndex();
	}

	/**
	 * 指定されたSQLでCollableStatementを作成して返します
	 */
	public CallableStatement prepareCall( String strSQL )
		throws SQLException
	{
		return mobjDataAcHandler.prepareCall( strSQL );
	}

	/**
	 * 指定されたSQLでPreparedStatementを作成して返します
	 */
	public PreparedStatement prepareStatement( String strSQL )
		throws SQLException
	{
		return mobjDataAcHandler.prepareStatement( strSQL );
	}


	/**
	 * 存在チェックを行います
	 * @param htbParam パラメータ
	 * @param strFeildName フィールド名
	 * @return boolean ture: 存在する false: 存在しない
	 * @exception XMOException データベースエラー
	 */
	public boolean isExit( Hashtable htbParam, String strFieldName )
		throws SQLException
	{
		return isExit( htbParam, strFieldName, mstrTableName );
	}

	/**
	 * 存在チェックを行います
	 * @param htbParam パラメータ
	 * @param strFeildName フィールド名
	 * @param strTableName テーブル名
	 * @return boolean ture: 存在する false: 存在しない
	 * @exception XMOException データベースエラー
	 */
	public boolean isExit( Hashtable htbParam, String strFieldName, String strTableName )
		throws SQLException
	{

		if( htbParam == null ) {
			return false;
		}

		String strFieldValue = (String)htbParam.get( strFieldName );
		if( strFieldValue == null ) {
			return false;
		}

		// 検索条件の組み立て
		String strCondition = " WHERE " + strFieldName + "='" + SqlEscape.escape(strFieldValue) + "'";

		return isExit( strCondition, strTableName );
	}

	/**
	 * 存在チェックを行います
	 * @param strCondition 条件文
	 * @return boolean ture: 存在する false: 存在しない
	 * @exception XMOException データベースエラー
	 */
	public boolean isExit( String strCondition )
		throws SQLException
	{
		return isExit( strCondition, mstrTableName );
	}

	/**
	 * 存在チェックを行います
	 * @param strCondition 条件文
	 * @param strTableName テーブル名
	 * @return boolean ture: 存在する false: 存在しない
	 * @exception XMOException データベースエラー
	 */
	public boolean isExit( String strCondition, String strTableName )
		throws SQLException
	{

		// 検索SQLの組み立て
		String strSQL = "SELECT COUNT(0) as record_count FROM " + strTableName + " " + strCondition;

		ResultSet rs = executeQuery(strSQL);

		if( rs.next() ) {
			int intCount = rs.getInt( "record_count" );
			if( intCount > 0 ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * 追加SQL生成メソッド
	 * @param htData 追加データ
	 */
	public String getInsertSQL( Hashtable htData ) {
		return getInsertSQL( htData, mstrTableName );
	}

	/**
	 * 追加SQL生成メソッド
	 * @param htData 追加データ
	 */
	public String getInsertSQL( Hashtable htData, String strTableName ) {
		StringBuffer strFieldName = new StringBuffer();
		StringBuffer strFieldValue = new StringBuffer();
		StringBuffer strSQL = new StringBuffer();

		Enumeration e=htData.keys();
		while(e.hasMoreElements()) {
			String key = (String)e.nextElement();
			String value = (String)htData.get(key);

			//キーがOracleの予約語で有れば""をつける

			String StrKey =	KeyChange(key);
			if(boolKeyChangFlag){
				key = StrKey;
			}


			strFieldName.append( key );
			strFieldName.append( "," );
			if( value.equals( "" ) ) {
				strFieldValue.append( "NULL," );
			}
			else {

				String strValue = DateChange(key,value);
				if(boolChangFlag){
					strFieldValue.append(strValue);
					strFieldValue.append( "," );
				}else{

					strFieldValue.append( "'" );
					strFieldValue.append( SqlEscape.escape(strValue) );
					strFieldValue.append( "'," );
				}
			}
		}

		strSQL.append( "Insert into " );
		strSQL.append( strTableName );
		strSQL.append( " (" );
		strSQL.append( strFieldName.substring(0, strFieldName.length()-1) );
		strSQL.append( ") values (" );
		strSQL.append( strFieldValue.substring(0, strFieldValue.length()-1) );
		strSQL.append( " )" );

		return new String(strSQL);
	}


	/**
	 * 更新SQL生成メソッド
	 * @param htData 更新データ
	 */
	public String getUpdateSQL(Hashtable htData)
	{
		return getUpdateSQL( htData, mstrTableName );

	}

	/**
	 * 更新SQL生成メソッド
	 * @param htData 更新データ
	 */
	public String getUpdateSQL(Hashtable htData, String strTableName )
	{
		StringBuffer strFieldNameAndValue = new StringBuffer();
		StringBuffer strSQL = new StringBuffer();

		Enumeration e=htData.keys();
		while(e.hasMoreElements()) {
			String key = (String)e.nextElement();
			String value = (String)htData.get(key);


			//キーがOracleの予約語で有れば""をつける

			String StrKey =	KeyChange(key);
			if(boolKeyChangFlag){
				key = StrKey;
			}
			strFieldNameAndValue.append( key );



			if( value.equals( "" ) ) {
				strFieldNameAndValue.append( "= NULL," );
			}
			else if( value != null ) {

				String strValue = DateChange(key,value);
				if(boolChangFlag){

					strFieldNameAndValue.append( "= " );
					strFieldNameAndValue.append(strValue);
					strFieldNameAndValue.append( "," );
				}else{

					strFieldNameAndValue.append( "= '" );
					strFieldNameAndValue.append( SqlEscape.escape(strValue) );
					strFieldNameAndValue.append( "'," );
				}
			}
		}
		strSQL.append( "Update " );
		strSQL.append( strTableName );
		strSQL.append( " Set " );
		strSQL.append( strFieldNameAndValue.substring(0,strFieldNameAndValue.length()-1) );

		return new String(strSQL);

	}


	/**
	 * 文字列変換を行います
	 * @param strChange 変換対象文字列
	 * @return String 変換後文字列
	 */
	protected String setChar(String strChange) {
		String strRes = "";
		if( strChange==null || strChange.equals( "" ) ) {
			return strRes;
		}
		else {
			try {
				strRes = new String(strChange.getBytes(BEFORE_CHARSET),AFTER_CHARSET);
			}
			catch(Exception e) {
				DebugLog.write( this.getClass(), "文字列変換エラー", DebugLog.HIGHT );
				DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
				return strRes;
			}
		}
		return strRes.trim();
	}
	/**
	 * Oracleの場合 OR PostgreSQLの場合Date型の値をTO_DATEで変換する
	 * @param strValue 変換対象文字列
     * @param strKey  変換対象文字列のKey
	 * @return String 変換後文字列
	 */
	private String DateChange(String strKey,String strValue) {
		//変更フラグ初期化
		boolChangFlag  = false;
		try{
			//XMS.iniファイルロードクラスのインスタンス
			Environment objEnv=new Environment();
			//Oracleの場合
			if(objEnv.getDBType().equals( objEnv.DB_ORACLE )){
				for (int i=0; i<StrDateField.length; i++){
					if(strKey.equals(StrDateField[i]) ){
						String OraValue="TO_DATE('" + StringEscapeUtils.escapeSql(strValue) + "\' ," + "\'YYYY/MM/DD HH24:MI:SS"+"\')";
						strValue = OraValue;
						//フラグ更新
						boolChangFlag  = true;
					}
				}
			}
			//PostgreSQLの場合
			else if(objEnv.getDBType().equals( objEnv.DB_POSTGRE )){
				for (int i=0; i<StrDateField.length; i++){
					if(strKey.equals(StrDateField[i]) ){
						String PsqlValue="TO_TIMESTAMP('" + StringEscapeUtils.escapeSql(strValue) + "\' ," + "\'YYYY/MM/DD HH24:MI:SS"+"\')";
						strValue = PsqlValue;
						//フラグ更新
						boolChangFlag  = true;
					}
				}
			}
		}
		catch(Exception ex)
		{
		}
		return strValue;
	}
	/**
	 * Oracleの場合 OR PostgreSQLの場合予約語Keyに””をつける
	 * @param strKey 変換対象文字列
 	 * @return String 変換後文字列
	 */
	private String KeyChange(String strKey) {
		//変更フラグ初期化
		boolKeyChangFlag  = false;
		try{
			//XMS.iniファイルロードクラスのインスタンス
			Environment objEnv=new Environment();
			//Oracleの場合
			if(objEnv.getDBType().equals( objEnv.DB_ORACLE )){
				for (int i=0; i<StrKeyField.length; i++){
					if(strKey.equals(StrKeyField[i]) ){
						String OraKey = StrKeyField[i];
						strKey = "\""+StringEscapeUtils.escapeSql(OraKey.toUpperCase())+"\"";
						//フラグ更新
						boolKeyChangFlag  = true;
					}
				}
			}
			//PostgreSQLの場合
			else if(objEnv.getDBType().equals( objEnv.DB_POSTGRE )){
				for (int i=0; i<StrKeyField_PSQL.length; i++){
					if(strKey.toUpperCase().equals(StrKeyField_PSQL[i]) ){
						String OraKey = StrKeyField_PSQL[i];
						strKey = "\""+StringEscapeUtils.escapeSql(OraKey.toUpperCase())+"\"";
						//フラグ更新
						boolKeyChangFlag  = true;
					}
				}
			}
		}
		catch(Exception ex)
		{
		}
		return strKey;
	}

}
