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

import org.opengion.fukurou.db.DBUtil;
import org.opengion.fukurou.model.Formatter;
import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.db.DBTableModel;

/**
 * <p>TableFilter_DBSELECT は、TableFilter インターフェースを継承した、DBTableModel 処理用の
 * 実装クラスです。<br />
 *
 * ここでは、Body部にかかれたSQLを実行した結果を、テーブルモデルにセットします。
 * SQL文から取得されるカラム名とテーブルモデルのカラム名は一致している必要があります。
 * 検索結果のカラムがテーブルモデルに存在していない場合はエラーとなります。
 * 以下の属性を指定しないと、データが存在しない場合はNULLがセットされます。
 * また、2行以上検索された場合でも、1行目のデータのみをセットします。
 * 
 * [属性]
 *  INNER_JOIN : データが存在しない場合、テーブルの該当行を削除します。
 *  APPEND     : 2行以上検索された場合、データをアペンドします。
 *  SEPARATOR  : APPENDする場合の区切り文字を指定します。
 *
 * Body部にかかれたSQLには[XXXX]形式の変数が指定できます。
 *
 * @version  0.9.0  2000/10/17
 * @author   Hiroki Nakamura
 * @since    JDK1.1,
 */
public class TableFilter_DBSELECT extends AbstractTableFilter {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "4.2.4.0 (2008/06/23)" ;

	/**
	 * DBTableModel処理を実行します。<br />
	 * 
	 * @og.rev 4.3.7.0 (2009/06/01) 実装大幅変更
	 *
	 * @return DBTableModel
	 */
	public DBTableModel execute() {
		
		boolean innerJoin = StringUtil.nval( getValue("INNER_JOIN"), false );
		boolean isAppend = StringUtil.nval( getValue("APPEND"), false );
		String separator = StringUtil.nval( getValue("SEPARATOR"), " " );

		Formatter format = new Formatter( table );
		format.setFormat( sql );
		int[] sqlClmNo = format.getClmNos();
		String query = format.getQueryFormatString();

		String[] data = null;
		String[] param = null;
		int[] clmNo = null;
		int rowCount = rowNo.length;
		for ( int row = rowCount - 1; row >= 0; row-- ) {
			try {
				param = getTableModelData( rowNo[row], sqlClmNo );

				String[][] dbData = null;
				if ( row == rowCount - 1 ) {
					String[][] rtn = DBUtil.dbExecute( query, param, appInfo, dbid, true );
					clmNo = getTableColumnNo( rtn[0] );
					dbData = new String[rtn.length - 1][rtn[0].length];
					System.arraycopy( rtn, 1, dbData, 0, dbData.length );
				}
				else {
					dbData = DBUtil.dbExecute( query, param, appInfo, dbid, false );
				}

				data = table.getValues( rowNo[row] );
				if ( dbData != null && dbData.length > 0 && dbData[0] != null && dbData[0].length > 0 ) {
					for ( int i = 0; i < clmNo.length; i++ ) {
						if( isAppend ) {
							StringBuilder buf = new StringBuilder();
							for( int j = 0; j < dbData.length; j++ ) {
								if( j > 0 ) {
									buf.append( separator );
								}
								buf.append( dbData[j][i] );
							}
							data[clmNo[i]] = buf.toString();
						}
						else {
							data[clmNo[i]] = dbData[0][i]; // 副作用を及ぼしています。注意
						}
					}
				}
				else if( innerJoin ) {
					table.removeValue(rowNo[row]);
				}
			}
			catch ( RuntimeException ex ) {
				ErrorMessage errMessage = makeErrorMessage( "TableFilter_DBSELECT Error", ErrorMessage.NG );
				errMessage.addMessage( rowNo[row] + 1, ErrorMessage.NG, ex.getMessage() );
				errMessage.addMessage( rowNo[row] + 1, ErrorMessage.NG, StringUtil.array2csv( data ) );
				errMessage.addMessage( rowNo[row] + 1, ErrorMessage.NG, "SQL=[" + sql + "]" );
				errMessage.addMessage( rowNo[row] + 1, ErrorMessage.NG, StringUtil.array2csv( param ) );
			}
		}

		return table;
	}
	
	/**
	 * 指定の行番号の、カラムNo配列(int[])に対応した値の配列を返します。
	 *
	 * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を
	 * 処理の対象とします。
	 *
	 * @param	row   行番号
	 * @param	clmNo カラムNo配列
	 * @return	String[] 行番号とカラムNo配列に対応した、値の配列
	 */
	private String[] getTableModelData( final int row, final int[] clmNo ) {
		String[] values = new String[clmNo.length];
		for( int i = 0; i < values.length; i++ ) {
			values[i] = table.getValue( row, clmNo[i] );
		}
		return values;
	}
}
