/*
 * 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.hayabusa.db.AbstractTableFilter;
import org.opengion.hayabusa.db.DBTableModel;

import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.util.StringUtil;

import java.util.Locale;

/**
 * TableFilter_DTYPE は、TableFilter インターフェースを継承した、DBTableModel 処理用の
 * 実装クラスです。
 *
 * ここでは、キーの CLS_NAME,USE_LENGTH より、DTYPE の値を設定します。
 *  CLS_NAME は、VARCHAR2, NUMBER などのカラムの属性を表します。
 *  USE_LENGTH は、長さ(使用桁数)です。
 *  DTYPE は、X(10) や、 S9(8) などの簡易型カラム属性です。
 * エンジンを使用したシステムでは、この属性より、さらに詳細にカラムを定義する、
 * DBTYPE 属性が、あります。将来的には、この定義を使用するように切り替えていく予定です。
 *
 * CLS_NAME,USE_LENGTH,DTYPE の カラム名については、初期値はこのままですが、
 * keys , vals に指定すれば、別名についても使用することが可能です。
 *
 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。
 * 【パラメータ】
 *  {
 *       CLS_NAME   : CLS_NAME   ;    VARCHAR2, NUMBER などのカラム属性を指定するカラム名を指定します。(初期値:CLS_NAME)
 *       USE_LENGTH : USE_LENGTH ;    長さ(使用桁数)を表すカラム名を指定します。                 (初期値:USE_LENGTH)
 *       DTYPE      : DTYPE      ;    X,R,S9,R,D,CLOB など、エンジンを使用したシステムで規定した詳細定義(初期値:DTYPE)
 *  }
 *
 * @og.formSample
 * ●形式：
 *      ① &lt;og:tableFilter classId="DTYPE" keys="CLS_NAME,USE_LENGTH,DTYPE" vals='"TABLE_NAME,CLM"' /&gt;
 *
 *      ② &lt;og:tableFilter classId="DTYPE" &gt;
 *               {
 *                   CLS_NAME   : CLS_NAME   ;
 *                   USE_LENGTH : USE_LENGTH ;
 *                   DTYPE      : DTYPE      ;
 *               }
 *         &lt;/og:tableFilter&gt;
 *
 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加
 *
 * @version  0.9.0  2000/10/17
 * @author   Kazuhiko Hasegawa
 * @since    JDK1.5,
 */
public class TableFilter_DTYPE extends AbstractTableFilter {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.5.0.1 (2016/10/21)" ;

	private static final int NO_CLS  = 0;
	private static final int NO_LEN  = 1;
	private static final int NO_TYP  = 2;

	// 5.5.8.2 (2012/11/09) DTYPE の条件分けを変更します。
	/** DTYPE の条件分けの マスターデータレコード を設定します。	{@value} */
	private static final String[][] MASTER_DATA = {
	//    cls	, len	, dbtype
		{ "VA"	, null	, "X"		} ,		// VARCHAR , VARCHAR2
		{ "NU"	, ","	, "R"		} ,		// NUMBER(小数)
		{ "NU"	, null	, "S9"		} ,		// NUMBER(整数)
		{ "IN"	, null	, "S9"		} ,		// INTEGER , INT64
		{ "DE"	, null	, "R"		} ,		// DECIMAL
		{ "TI"	, null	, "D"		} ,		// TIMESTAMP
		{ "DA"	, null	, "D"		} ,		// DATE
		{ "CL"	, null	, "CLOB"	} ,		// CLOB
		{ null	, null	, "X"		} 		// その他(CHAR , LONG 他)
	} ;

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。
	 */
	public TableFilter_DTYPE() {
		super();
		initSet( "CLS_NAME"		, "VARCHAR2, NUMBER などのカラム属性を指定するカラム名を指定(初期値:CLS_NAME)"			);
		initSet( "USE_LENGTH"	, "長さ(使用桁数)を表すカラム名を指定 (初期値:USE_LENGTH)"								);
		initSet( "DTYPE"		, "X,R,S9,R,D,CLOB など、エンジンを使用したシステムで規定した詳細定義(初期値:DTYPE)"	);
	}

	/**
	 * DBTableModel処理を実行します。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更
	 * @og.rev 5.5.8.2 (2012/11/09) X,S9 の条件分けを変更します。(MICS対応)
	 * @og.rev 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。
	 *
	 * @return 処理結果のDBTableModel
	 */
	public DBTableModel execute() {
		final DBTableModel table = getDBTableModel();		// 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加

		final String cls = StringUtil.nval( getValue( "CLS_NAME" )		,"CLS_NAME" );
		final String len = StringUtil.nval( getValue( "USE_LENGTH" )	,"USE_LENGTH" );
		final String typ = StringUtil.nval( getValue( "DTYPE" )			,"DTYPE" );

		final int clsNo  = table.getColumnNo( cls,false );	// 存在しない場合は、-1 を返す。
		final int lenNo  = table.getColumnNo( len,false );
		final int typNo  = table.getColumnNo( typ,false );

		if( clsNo >= 0 && lenNo >= 0 && typNo >= 0 ) {
			String[] data  = null;
			final int rowCnt = table.getRowCount();
			String clsVal = null;
			String lenVal = null;
			for( int row=0; row<rowCnt; row++ ) {
				try {
					data   = table.getValues( row );
					clsVal = ( data[clsNo] == null ) ? "" : data[clsNo].trim().toUpperCase(Locale.JAPAN) ;	// CLS_NAME(VARCHAR,NUMBER など)
					lenVal = ( data[lenNo] == null ) ? "" : data[lenNo].trim().replace( '.',',' ) ;			// USE_LENGTH(使用桁数)

					// 長さで、小数部が、 0 の場合は、整数のみと判断する。
					final int cm = lenVal.indexOf( ",0" );
					if( cm >= 0 ) { lenVal = lenVal.substring( 0,cm ); }

					// 5.5.8.2 (2012/11/09) X,S9 の条件分けを変更します。(MICS対応)
					final String typVal = makeDTYPE( clsVal ,lenVal ) ;
					data[typNo] = typVal;

					if( lenVal.contains( "," ) ) { data[lenNo] = lenVal ; }		// "," が含まれている場合は、、再設定

	//				table.setValueAt( typVal,row,typNo );
				}
				catch( final RuntimeException ex ) {
					// 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。
					makeErrorMessage( "TableFilter_DTYPE Error",ErrorMessage.NG )
						.addMessage( row+1,ErrorMessage.NG,"DTYPE"
							, "CLS=[" + clsVal + "],LEN=[" + lenVal + "]"
							, StringUtil.array2csv( data )
						)
						.addMessage( ex );
				}
			}
		}

		return table;
	}

	/**
	 * クラス,長さデータ から、DTYPE 文字列を作成します。
	 *
	 * if文の羅列から、マスタデータを利用した条件判定方式で判定します。
	 * また、以前からの、S9:数字 と、 X:文字 だけではなく、R:小数、D:日時、CLOB:テキスト を追加します。
	 * 桁数未設定の場合は、(桁数) を使用せず、記号だけになります。
	 *
	 * @og.rev 5.5.8.2 (2012/11/09) 新規追加
	 *
	 * @param  clsVal クラスデータ(not null保障 , 大文字保障)
	 * @param  lenVal 長さデータ(not null , カンマ保障)
	 * @return 対応する DTYPE 文字列
	 * @og.rtnNotNull
	 */
	private String makeDTYPE( final String clsVal,final String lenVal ) {
		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

		final int size = MASTER_DATA.length;
		for( int i=0; i<size; i++ ) {
			final String clsMstr = MASTER_DATA[i][NO_CLS];
			final String lenMstr = MASTER_DATA[i][NO_LEN];

			if( ( clsMstr == null || clsVal.startsWith( clsMstr ) ) &&
				( lenMstr == null || lenVal.contains(   lenMstr ) ) ) {
				buf.append( MASTER_DATA[i][NO_TYP] );
				break ;
			}
		}
		if( buf.length() == 0 ) { buf.append( MASTER_DATA[size-1][NO_TYP] ); }		// MASTER_DATA の最終レコードでマッチするので、来ないはず

		if( ! lenVal.isEmpty() ) {		// 長さが存在する場合は、(xx)の数字部分を記述する。これが通常のケース
			buf.append( '(' ).append( lenVal ).append( ')' );		// 6.0.2.5 (2014/10/31) char を append する。
		}

		return buf.toString();
	}
}
