/*
 * 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 static org.opengion.fukurou.util.StringUtil.nval;

import java.io.File;

import org.opengion.fukurou.db.ConnectionFactory;
import org.opengion.fukurou.db.DatabaseConfig;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.db.DBTableModelUtil;

/**
 * DBConfig.xmlの編集を行うためのタグです。
 *
 * 自分自身のコンテキストのDBConfig.xmlの編集及び、ファイルパスを指定して他の
 * コンテキストのDBConfig.xmlの編集を行うことができます。
 *
 * 編集タイプは、type属性により、「DBドライバークラス」と「DBIDの設定情報」の2つを
 * 切り替えることができます。
 *
 * また、このタグ自体の機能としては、「取得」と「保存」に分離されています。
 *
 * (1)取得
 *  type属性で指定された情報を読み取り、DBTableModel形式に変換し、セッションに登録します。
 *  (tableIdは"DEFAULT"です)
 *
 * (2)保存
 *  セッションからDBTableModelを読み取り、type属性で指定された情報の形式にオブジェクト化し、
 *  DBConfig.xmlに保存します。
 *  (DBTableModelを取得するtableIdは"DEFAULT"です)
 *
 *  保存を実行すると、元のDBConfig.xmlファイルと同じ場所に、タイプスタンプ付きのバックアップ
 *  ファイルが自動的に作成されます。
 *
 *  また、xmlFile(DBConfig.xml)を指定しない状態で、保存(command="SET")を行うと、
 *  内部にキャッシュしている接続情報は、一旦全てクリアされます。
 *
 * @og.formSample
 * ●形式：&lt;dbConfigEditor command="..." type="..." xmlFile="..." /&gt;
 * ●body：なし
 *
 * ●Tag定義：
 *   &lt;og:dbConfigEditor
 *       command            【TAG】DBConfig.xmlに対する操作のコマンドを指定します(初期値:GET)
 *       type               【TAG】DBConfig.xmlに対する操作のタイプを指定します(初期値:DBID)
 *       xmlFile            【TAG】編集を行うDBConfig.xmlのファイルのパスを指定します(初期値:自身のDBConfig.xml)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   /&gt;
 *
 * ●使用例
 *    ・取得
 *        &lt;dbConfigEditor command="GET" type="{&#064;TYPE}" xmlFile="{&#064;DBCXML}" /&gt;
 *
 *    ・保存
 *        &lt;dbConfigEditor command="SET" type="{&#064;MEM.TYPE}" xmlFile="{&#064;MEM.DBCXML}" /&gt;
 *
 * @og.group その他部品
 *
 * @version  4.0
 * @author	 Hiroki Nakamura
 * @since    JDK5.0,
 */
public class DBConfigEditorTag extends CommonTagSupport {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "5.1.9.0 (2010/08/01)" ;

	private static final long serialVersionUID = 5160 ;

	private transient DBTableModel	table	= null;		// 5.1.9.0 (2010/08/01) transient 追加
	private String			command = "GET";
	private String			type	= "DBID";
	private String			xmlFile	= null;

	/**
	 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
	 *
	 * @return	後続処理の指示( SKIP_BODY )
	 */
	@Override
	public int doStartTag() {
		startQueryTransaction( HybsSystem.TBL_MDL_KEY );
		return ( SKIP_BODY );
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();

		// DatabaseConfigオブジェクトを取得します。
		DatabaseConfig dbc = null;
		if( xmlFile == null ) {
			dbc = new DatabaseConfig();
		}
		else {
			if( !( new File( xmlFile ) ).exists() ) {
				throw new HybsSystemException( "編集対象のDBConfig.xmlが存在しません。File=[" + xmlFile + "]" );
			}
			dbc = new DatabaseConfig( xmlFile );
		}

		// 編集タイプに応じてキー情報をセットします。
		String[] keys = null;
		if( "DBID".equals( type ) ) {
			keys = DatabaseConfig.getDbidInfoKeys();
		}
		else if( "CLASS".equals( type ) ) {
			keys = new String[1];
			keys[0] = DatabaseConfig.getDriverKey();
		}

		// DBConfig.xmlから情報を取得します。
		if( "GET".equals( command ) ) {
			table = getDBConfig( dbc, keys );

			if( table != null && !commitTableObject( HybsSystem.TBL_MDL_KEY, table ) ) {
				jspPrint( "QueryTag Query処理が割り込まれました。DBTableModel は登録しません。" );
				return(SKIP_PAGE);
			}
		}
		// DBConfig.xmlに情報を保存します。
		else if( "SET".equals( command ) ) {
			setDBConfig( dbc, keys );

			// xmlFileが指定されていない(=自信のDBConfig.xml)を書き換えた場合は、キャッシュをクリアします。
			if( xmlFile == null ) {
				ConnectionFactory.realClose();
				ConnectionFactory.init( HybsSystem.getContextName(), null );
			}
		}

		return( EVAL_PAGE );
	}

	/**
	 * DBConfig.xmlから情報を取得します。
	 *
	 * @param	dbc		DatabaseConfigオブジェクト
	 * @param	keys	編集する情報のカラムキー配列
	 *
	 * @return	テーブルモデル
	 */
	private DBTableModel getDBConfig( final DatabaseConfig dbc, final String[] keys ) {
		String[][] vals = null;
		if( "DBID".equals( type ) ) {
			vals = dbc.getDbidInfo();
		}
		else if( "CLASS".equals( type ) ) {
			String[] tmpVals = dbc.getDriverList();
			vals = new String[tmpVals.length][1];
			for( int i=0; i<tmpVals.length; i++ ) {
				vals[i][0] = tmpVals[i];
			}
		}

		return DBTableModelUtil.makeDBTable( keys, vals, getResource() );
	}

	/**
	 * DBConfig.xmlに情報を保存します。
	 *
	 * @param dbc DatabaseConfigオブジェクト
	 * @param keys 編集する情報のカラムキー配列
	 */
	private void setDBConfig( final DatabaseConfig dbc, final String[] keys ) {
		table = (DBTableModel)getObject( HybsSystem.TBL_MDL_KEY );

		String tmpKeys = StringUtil.array2csv( table.getNames() );
		if( !StringUtil.array2csv( keys ).equals( tmpKeys ) ) {
			throw new RuntimeException( "DBTableModelのキーが不正です" );
		}

		int row = table.getRowCount();
		String[][] vals = new String[row][keys.length];
		for( int i=0; i<row; i++ ) {
			vals[i] = table.getValues( i );
		}

		if( "DBID".equals( type ) ) {
			dbc.setDbidInfo( vals );
		}
		else if( "CLASS".equals( type ) ) {
			String[] tmpVals = new String[vals.length];
			for( int i=0; i<vals.length; i++ ) {
				tmpVals[i] = vals[i][0];
			}
			dbc.setDriverList( tmpVals );
		}
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		command			= "GET";
		type			= "DBID";
		xmlFile			= null;
	}

	/**
	 * 【TAG】DBConfig.xmlに対する操作のコマンドを指定します(初期値:GET)。
	 *
	 * @og.tag
	 *  GET -> typeに応じた情報をDBConfig.xmlから読み出し、DBTableModelに登録します。
	 *  SET -> typeに応じた情報をDBTableModelから読み出し、DBConfig.xmlに登録します。
	 *
	 * なお、保存(SET)を実行すると、元のDBConfig.xmlファイルと同じ場所に、タイプスタンプ付きの
	 * バックアップファイルが自動的に作成されます。
	 *
	 * また、xmlFile(DBConfig.xml)を指定しない状態で、保存(command="SET")を行うと、
	 * 内部にキャッシュしている接続情報は、一旦全てクリアされます。
	 *
	 * @param	cmd コマンド
	 */
	public void setCommand( final String cmd ) {
		command = nval( getRequestParameter( cmd ),command );
	}

	/**
	 * 【TAG】DBConfig.xmlに対する操作のタイプを指定します(初期値:DBID)。
	 *
	 * @og.tag
	 *  DBID  -> DBIDの各種情報を読み取り/書き込みします。
	 *  CLASS -> ロードするDBドライバーの一覧を読み取り/書き込みします。
	 *
	 * @param	tp タイプ
	 */
	public void setType( final String tp ) {
		type = nval( getRequestParameter( tp ),type );
	}

	/**
	 * 【TAG】編集を行うDBConfig.xmlのファイルのパスを指定します(初期値:自身のDBConfig.xml)。
	 *
	 * @og.tag
	 * 何も指定されない場合は、自身のコンテキストのDBConfig.xmlに対する編集となります。
	 *
	 * @param	file DBConfig.xmlのファイルパス
	 */
	public void setXmlFile( final String file ) {
		xmlFile = nval( getRequestParameter( file ),xmlFile );
	}

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