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

import org.opengion.hayabusa.resource.ResourceFactory;
import org.opengion.hayabusa.resource.ResourceManager;

import org.opengion.fukurou.util.ErrorMessage;
import org.opengion.fukurou.util.StringUtil;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Locale;
import java.util.Map;

/**
 * TableFilter_DBARG は、TableFilter インターフェースを継承した、DBTableModel 処理用の
 * 実装クラスです。
 *
 * ここでは、テキストから、オブジェクト名、カラム名、クラス、利用桁数を切り出します。
 *
 * ソースのテキスト部は、",NAME_JA VARCHAR2(100) " という形式のテキストになっています。
 * これを、カラム部、クラス名部、使用桁数部に分解します。上記の例では、
 * それぞれを、NAME_JA、VARCHAR2、100 に分解して、文字列配列に格納します。
 * また、これらのソースに、"--" や "/ * ･･･ * /" などのコメントが含まれるケースが
 * あります。"--" コメントは、それ以降を無視しますが、"/ *" コメントは、複数行に
 * またがる為、今回は処理対象から外します。
 * ソースのテキスト部には、それら以外に、OBJECT_NAME に相当する行や、");" などの
 * ソースの最初や最後の無効な部分があります。無効な部分は、null を返すことで
 * 登録処理から省いてください。
 *
 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。
 * 【パラメータ】
 *  {
 *       TEXT       : 処理をおこなう、ソースのテキスト部のカラム名
 *       OBJ_NAME   : キーとなるオブジェクト名のカラム名
 *       SEQNO      : ソースの行番号のカラム名
 *       CLM        : 処理結果のカラム名を格納するカラム名
 *       CLS_NAME   : 処理結果のクラス名を格納するカラム名
 *       USE_LENGTH : 処理結果の利用桁数を格納するカラム名
 *       CLM_NAME   : 処理結果のカラム名称（ラベル）を格納するカラム名
 *       MAX_LENGTH : 処理結果の桁数を格納するカラム名
 *  }
 *
 * @og.formSample
 * ●形式：
 *              select A.OBJECT_NAME AS OBJ_NAME,B.LINE AS SEQNO,'' AS CLM ,
 *                      '' AS CLS_NAME,'' AS USE_LENGTH,'' AS CLM_NAME,
 *                      '' AS TABLE_NAME ,'' AS MAX_LENGTH,
 *                      B.TEXT , '{&#064;SYSTEM_ID}' AS SYSTEM_ID,'{&#064;TBLSYU}' AS TBLSYU
 *              from USER_OBJECTS A inner join USER_SOURCE B
 *              on    A.OBJECT_NAME = B.NAME
 *              where A.OBJECT_TYPE = 'TYPE'
 *              and   B.TYPE        = 'TYPE'
 *              and   not A.OBJECT_NAME like '%ARRAY'
 *              order by A.OBJECT_NAME,B.LINE
 *      ① &lt;og:tableFilter classId="DBARG"
 *                              keys="TEXT,OBJ_NAME,SEQNO,CLM,CLS_NAME,USE_LENGTH,CLM_NAME,MAX_LENGTH"
 *                              vals="TEXT,OBJ_NAME,SEQNO,CLM,CLS_NAME,USE_LENGTH,CLM_NAME,MAX_LENGTH" /&gt;
 *
 *      ② &lt;og:tableFilter classId="DBARG" &gt;
 *               {
 *                      TEXT       : 処理をおこなう、ソースのテキスト部のカラム名
 *                      OBJ_NAME   : キーとなるオブジェクト名のカラム名
 *                      SEQNO      : ソースの行番号のカラム名
 *                      CLM        : 処理結果のカラム名を格納するカラム名
 *                      CLS_NAME   : 処理結果のクラス名を格納するカラム名
 *                      USE_LENGTH : 処理結果の利用桁数を格納するカラム名
 *                      CLM_NAME   : 処理結果のカラム名称（ラベル）を格納するカラム名
 *                      MAX_LENGTH : 処理結果の桁数を格納するカラム名
 *               }
 *         &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.1,
 */
public class TableFilter_DBARG extends AbstractTableFilter {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "5.6.6.1 (2013/07/12)" ;

	/**
	 * keys の整合性チェックを行うための初期設定を行います。
	 *
	 * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェック対応
	 *
	 * @param	keysMap keys の整合性チェックを行うための Map
	 */
	@Override
	protected void init( final Map<String,String> keysMap ) {
		keysMap.put( "TEXT" 		, "処理をおこなう、ソースのテキスト部のカラム名"		);
		keysMap.put( "OBJ_NAME"		, "キーとなるオブジェクト名のカラム名"					);
		keysMap.put( "SEQNO"		, "ソースの行番号のカラム名"							);
		keysMap.put( "CLM"			, "処理結果のカラム名を格納するカラム名"				);
		keysMap.put( "CLS_NAME"		, "処理結果のクラス名を格納するカラム名"				);
		keysMap.put( "USE_LENGTH"	, "処理結果の利用桁数を格納するカラム名"				);
		keysMap.put( "CLM_NAME"		, "処理結果のカラム名称（ラベル）を格納するカラム名"	);
		keysMap.put( "MAX_LENGTH"	, "処理結果の桁数を格納するカラム名"					);
	}

	// 5.5.7.4 (2012/10/25) OBJ_NAME,SEQNO 追加
//	private static final String[] KEYS = new String[] { "TEXT","CLM","CLS_NAME","USE_LENGTH","CLM_NAME","MAX_LENGTH" };
	private static final String[] KEYS = new String[] { "TEXT","OBJ_NAME","SEQNO","CLM","CLS_NAME","USE_LENGTH","CLM_NAME","MAX_LENGTH" };
	private static final int TEXT		= 0;
	private static final int OBJ_NAME	= 1;			// 5.5.7.4 (2012/10/25) OBJ_NAME 追加
	private static final int SEQNO		= 2;			// 5.5.7.4 (2012/10/25) SEQNO 追加
	private static final int CLM		= 3;
	private static final int CLS_NAME	= 4;
	private static final int USE_LENGTH	= 5;
	private static final int CLM_NAME	= 6;
	private static final int MAX_LENGTH	= 7;

	/**
	 * DBTableModel処理を実行します。
	 *
	 * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更
	 * @og.rev 5.5.7.4 (2012/10/25) OBJ_NAME,SEQNO を追加することで、SEQNO を振りなおします。
	 *
	 * @return 処理結果のDBTableModel
	 */
	public DBTableModel execute() {
		DBTableModel table = getDBTableModel();		// 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加

		String lang = getValue( "LANG" );
		ResourceManager resource = ResourceFactory.newInstance( lang );

		int size = KEYS.length;
		int[] clmNo = new int[size];
		for( int i=0; i<size; i++ ) {
			String clm = getValue( KEYS[i] );
			clmNo[i]  = table.getColumnNo( clm,false );	// 存在しない場合は、-1
			if( clmNo[i] < 0 ) {
				String errMsg = "検索結果に、[" + clm + "]を含めてください。" ;
				throw new HybsSystemException( errMsg );
			}
		}

		String[] data  = null;
		int rowCnt = table.getRowCount();
		DBTableModel rtnTbl = table.newModel();
		String bkObjName = "";						// 5.5.7.4 (2012/10/25) SEQNO を振りなおすためのキーブレイク
		int    seqno     = 1 ;						// 5.5.7.4 (2012/10/25) SEQNO
		for( int row=0; row<rowCnt; row++ ) {
			try {
				data = table.getValues( row );
				String text = data[clmNo[TEXT]] ;
				String[] temp = textSeparate( text );
				if( temp != null ) {
					String objName			= data[clmNo[OBJ_NAME]];		// 5.5.7.4 (2012/10/25) SEQNO を振りなおす
					if( ! bkObjName.equals( objName ) ) {
						bkObjName = objName;
						seqno     = 1;
					}

					data[clmNo[SEQNO]]		= String.valueOf( seqno++ );		// 5.5.7.4 (2012/10/25) SEQNO を振りなおす
					data[clmNo[CLM]]		= temp[0];
					data[clmNo[CLS_NAME]]	= temp[1];
					data[clmNo[USE_LENGTH]]	= temp[2];
			//		data[clmNo[CLM_NAME]]	= resource.getLabel( temp[0] ) ;

					DBColumn dbClm = resource.getDBColumn( temp[0] );
					if( dbClm != null ) {
						data[clmNo[CLM_NAME]]	= dbClm.getLabel() ;
						String len = data[clmNo[MAX_LENGTH]] ;
						if( len == null || len.length() == 0 ) {
							data[clmNo[MAX_LENGTH]] = dbClm.getMaxlength() ;
						}
					}
			//		else {
			//			data[clmNo[CLM_NAME]]	= temp[0] ;	// ラベルが存在しない
			//		}
					rtnTbl.addColumnValues( data );
				}
			}
			catch( RuntimeException ex ) {
				ErrorMessage errMessage = makeErrorMessage( "TableFilter_DBARG Error",ErrorMessage.NG );
				errMessage.addMessage( row+1,ErrorMessage.NG,"ERR MSG",ex.getMessage() );
				errMessage.addMessage( row+1,ErrorMessage.NG,"ERR Data",StringUtil.array2csv( data ) );
			}
		}

		return rtnTbl;
	}

//	private static final Pattern pt = Pattern.compile( "[^\\w]*([\\w]*)[^\\w]*(VARCHAR2|NUMBER)[^\\w]*\\(([^\\)]*)\\)",Pattern.CASE_INSENSITIVE );
	private static final Pattern pt = Pattern.compile( "[^\\w]*([\\w]*)[^\\w]*(VARCHAR2|NUMBER)[\\s]*([\\(\\d,\\)]*)",Pattern.CASE_INSENSITIVE );
												//			   (______)       (_______________)       (_____________)

	/**
	 * ソースのテキスト部を分割します。
	 *
	 * ソースのテキスト部は、",NAME_JA VARCHAR2(100) " という形式のテキストになっています。
	 * これを、カラム部、クラス名部、使用桁数部に分解します。上記の例では、
	 * それぞれを、NAME_JA、VARCHAR2、100 に分解して、文字列配列に格納します。
	 * また、これらのソースに、"--" や "/ * ･･･ * /" などのコメントが含まれるケースが
	 * あります。"--" コメントは、それ以降を無視しますが、"/ *" コメントは、複数行に
	 * またがる為、今回は処理対象から外します。
	 * ソースのテキスト部には、それら以外に、OBJECT_NAME に相当する行や、");" などの
	 * ソースの最初や最後の無効な部分があります。無効な部分は、null を返すことで
	 * 登録処理から省いてください。
	 *
	 * @og.rev 5.5.7.4 (2012/10/25) カラムの大文字化と、桁数の 前0 削除
	 * @og.rev 5.5.8.5 (2012/11/27) 桁数なしの場合の対応
	 *
	 * @param	text	ソースのテキスト部
	 *
	 * @return	分割後の文字列配列(CLM,CLS_NAME,USE_LENGTHの順)、無効なデータは null
	 */
	private String[] textSeparate( final String text ) {

		int adrs = text.indexOf( "--" );
		String text2 = (adrs<0) ? text : text.substring( 0,adrs ) ;

		String[] rtnTxt = null ;
		Matcher mt = pt.matcher( text2 );
		if( mt.lookingAt() ) {
			String clm = mt.group( 1 );		// カラムの名称:パターンとして、空白は含まない
			String cls = mt.group( 2 );		// クラスの名称(VARCHAR2|NUMBER)
			String len = mt.group( 3 );		// 桁数:パターンとして、前後に空白を含む、()を含む、何もないなど

			// 5.5.8.5 (2012/11/27) 桁数なしの場合の対応
			if( len != null ) {
				int st = len.indexOf( '(' );
				int ed = len.indexOf( ')' );
				if( st >= 0 && ed >= 0 ) {
					len = len.substring( st+1,ed );
					len = len.trim().replaceFirst("^0+", "");						// 5.5.7.4 (2012/10/25) 先頭の 0 を削除する。
				}
				else {
					len = "";
				}
			}
			else {
				len = "";
			}

//			if( clm != null && cls != null && len != null ) {
			if( clm != null && cls != null ) {
				clm = clm.toUpperCase(Locale.JAPAN);								// 5.5.7.4 (2012/10/25) 大文字化
				cls = cls.toUpperCase(Locale.JAPAN);								// 5.5.7.4 (2012/10/25) 大文字化
//				len = len.trim();
//				len = len.trim().replaceFirst("^0+", "");							// 5.5.7.4 (2012/10/25) 先頭の 0 を削除する。
//				if( len.length() > 0 ) {
					rtnTxt = new String[] { clm,cls,len };
//				}
			}
		}

		// マッチしない または 条件を満たさない場合は、null
		return rtnTxt ;
	}
}
