/*
 * 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.fukurou.process;

import org.opengion.fukurou.util.Argument;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.LogWriter;

import java.util.Map ;
import java.util.LinkedHashMap ;

/**
 * Process_UnicodeEscape は、上流から受け取ったデータをエスケープ変換する
 * ChainProcess インターフェースの実装クラスです。
 *
 *  ※ 5.0.0.2 (2009/09/15) このクラスは、廃止になりました。
 *     類似機能に、Process_StringUtil を利用してください。
 *     -action 引数に、getUnicodeEscape か、getReplaceEscape を指定することで、
 *     ESCAPE(変換)/REVERSE(戻し) の指定と同一の処理を行います。
 *
 * 上流（プロセスチェインのデータは上流から下流へと渡されます。）から受け取った
 * LineModel を元に、指定のカラムのUTF-8 文字コードと、HTML のエスケープ記号
 * (&amp;#xZZZZ;)文字との相互変換を行います。
 * 入力文字を、キャラクタ(char)型に分解し、(&amp;#xZZZZ;)に変換していきます。
 * よって、通常に１文字(Shift-JISで２Byte,UTF-8で３Byte)が、８Byteになります。
 * この変換された文字列を、HTML上でそのまま取り出すと、元のUnicode文字に戻る為、
 * 通常のShift-JISでは、扱えない文字(中国語など)でも表示可能になります。
 *
 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。
 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に
 * 繋げてください。
 *
 * @og.formSample
 *  Process_UnicodeEscape -action=ESC|REV [-keys=AA,BB,CC] [-all=false|true]
 *
 *     -action=ESC|REV       ：ESCAPE(変換)/REVERSE(戻し) を指定します。先頭文字で判定します。（必須)
 *   [ -keys=AA,BB,CC      ] ：変換するカラムをCSV形式で複数指定できます。
 *   [ -all=false|true     ] ：全カラムを変換する場合は、trueを設定します（初期値 false:部分指定)
 *   [ -display=false|true ] ：結果を標準出力に表示する(true)かしない(false)か（初期値 false:表示しない)
 *
 * @og.rev 5.0.0.2 (2009/09/15) 廃止クラスです。
 * @deprecated
 * @version  0.9.0  2004/02/27
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
@Deprecated public class Process_UnicodeEscape extends AbstractProcess implements ChainProcess {

	private boolean		isEscape	= true;		// 変換方法(true:Escape/false:Reverse)
	private String		keys		= null;		// 変換するカラム名配列のアドレス
	private int[]		clmNos		= null;		// 変換するカラム名配列のアドレス
	private boolean		all			= false;	// 部分指定
	private boolean		display		= false;	// 表示しない

	private boolean		firstRow	= true;		// 最初の一行目
	private int			count		= 0;

	private final static Map<String,String> mustProparty   ;		// ［プロパティ］必須チェック用 Map
	private final static Map<String,String> usableProparty ;		// ［プロパティ］整合性チェック Map

	static {
		mustProparty = new LinkedHashMap<String,String>();
		mustProparty.put( "action",		"ESCAPE(変換)/REVERSE(戻し) を指定します。（必須)" );

		usableProparty = new LinkedHashMap<String,String>();
		usableProparty.put( "keys",		"変換するカラムをCSV形式で複数指定できます。" );
		usableProparty.put( "all",		"全カラムを変換する場合は、trueを設定します。"
										+ CR + "（初期値 false:部分指定)" );
		usableProparty.put( "display",	"結果を標準出力に表示する(true)かしない(false)か"
										+ CR + " (初期値 false:表示しない)" );
	}

	/**
	 * デフォルトコンストラクター。
	 * このクラスは、動的作成されます。デフォルトコンストラクターで、
	 * super クラスに対して、必要な初期化を行っておきます。
	 *
	 */
	public Process_UnicodeEscape() {
		super( "org.opengion.fukurou.process.Process_UnicodeEscape",mustProparty,usableProparty );
	}

	/**
	 * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
	 * 初期処理（ファイルオープン、ＤＢオープン等）に使用します。
	 *
	 * @param   paramProcess ParamProcess
	 */
	public void init( final ParamProcess paramProcess ) {
		Argument arg = getArgument();

		keys		= arg.getProparty( "keys",keys );
		all			= arg.getProparty( "all",all );
		display		= arg.getProparty( "display",display );

		String act		= arg.getProparty( "action" );

		if(      act.charAt( 0 ) == 'E' ) { isEscape = true;  }
		else if( act.charAt( 0 ) == 'R' ) { isEscape = false; }
		else {
			String errMsg = "action=[" + act + "] は、E(SCAPE) か、R(EVERSE) を設定して下さい。"
								+ "先頭１文字目(大文字)で判定しています。";
			throw new RuntimeException( errMsg );
		}

		if( ( keys == null || keys.length() == 0 ) && !all ) {
			String errMsg = "keys か、all は設定して下さい。"
								+ "keys=[" + keys + "], all=[" + all + "]";
			throw new RuntimeException( errMsg );
		}
	}

	/**
	 * 引数の LineModel を処理するメソッドです。
	 * 変換処理後の LineModel を返します。
	 * 後続処理を行わない場合（データのフィルタリングを行う場合）は、
	 * null データを返します。つまり、null データは、後続処理を行わない
	 * フラグの代わりにも使用しています。
	 * なお、変換処理後の LineModel と、オリジナルの LineModel が、
	 * 同一か、コピー（クローン）かは、各処理メソッド内で決めています。
	 * ドキュメントに明記されていない場合は、副作用が問題になる場合は、
	 * 各処理ごとに自分でコピー（クローン）して下さい。
	 *
	 * @param   data LineModel オリジナルのLineModel
	 * @return  LineModel  処理変換後のLineModel
	 */
	public LineModel action( final LineModel data ) {
		count++ ;
		try {
			if( firstRow ) {
				makeColumnNos( data );
				firstRow = false;
			}

			for( int i=0; i<clmNos.length; i++ ) {
				String val = (String)data.getValue( clmNos[i] ) ;

				if( isEscape ) {
					val = StringUtil.getUnicodeEscape( val ) ;
				}
				else {
					val = StringUtil.getReplaceEscape( val ) ;
				}
				data.setValue( clmNos[i],val );
			}

			if( display ) { printKey( count,data ); }
		}
		catch( Throwable ex ) {
			String errMsg = "row=[" + count + "]" + CR +
						"    data=[" + data + "]" + CR ;
			throw new RuntimeException( errMsg,ex );
		}
		return data;
	}

	/**
	 * プロセスの終了を行います。最後に一度だけ、呼び出されます。
	 * 終了処理（ファイルクローズ、ＤＢクローズ等）に使用します。
	 *
	 * @param   isOK トータルで、OKだったかどうか(true:成功/false:失敗）
	 */
	public void end( final boolean isOK ) {
		keys		= null;		// 変換するカラム名配列のアドレス
		clmNos		= null;		// 変換するカラム名配列のアドレス
	}

	/**
	 * プロセスの処理結果のレポート表現を返します。
	 * 処理プログラム名、入力件数、出力件数などの情報です。
	 * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
	 * 形式で出してください。
	 *
	 * @return   処理結果のレポート
	 */
	public String report() {
		String report = "[" + getClass().getName() + "]" + CR
				+ TAB + "Output Count : " + count ;

		return report ;
	}

	/**
	 * カラム番号配列を取得します。
	 * 繰返し処理を行う場合に、事前にアドレスでアクセスできるように処理するカラム番号を
	 * キャッシュしておきます。
	 *
	 * @param	data  LineModel
	 */
	private void makeColumnNos( final LineModel data ) {
		if( all ) {
			String[] names = data.getNames();
			int size = names.length;
			clmNos = new int[size];
			for( int i=0; i<size; i++ ) {
				clmNos[i] = i;
			}
		}
		else {
			String[] clms = StringUtil.csv2Array( keys );
			int size = clms.length;
			clmNos = new int[size];
			for( int i=0; i<size; i++ ) {
				clmNos[i] = data.getColumnNo( clms[i] );
			}
		}
	}

	/**
	 * 画面出力用のフォーマットを作成します。
	 *
	 * @param	rowNo  int データ読み取り件数
	 * @param	data   LineModel
	 */
	private void printKey( final int rowNo , final LineModel data ) {
		StringBuilder buf = new StringBuilder();

		buf.append( "row=[" ).append( rowNo ).append( "] : " );
		for( int i=0; i < clmNos.length; i++ ) {
			buf.append( data.getName( clmNos[i] ) );
			buf.append( " ⇒ " );
			buf.append( data.getValue( clmNos[i] ) );
			buf.append( " , " );
		}

		println( buf.toString() );
	}

	/**
	 * このクラスの使用方法を返します。
	 *
	 * @return	String
	 */
	public String usage() {
		StringBuilder buf = new StringBuilder();

		buf.append( "Process_UnicodeEscape は、上流から受け取ったデータをエスケープ変換する" 		).append( CR );
		buf.append( "CainProcess インターフェースの実装クラスです。"								).append( CR );
		buf.append( CR );
		buf.append( "上流（プロセスチェインのデータは上流から下流へと渡されます。）から"			).append( CR );
		buf.append( " LineModel を元に、指定のカラムのUTF-8 文字コードと、HTML のエスケープ"		).append( CR );
		buf.append( "記号(&#xZZZZ;)文字との相互変換を行います。"									).append( CR );
		buf.append( "入力文字を、キャラクタ(char)型に分解し、(&#xZZZZ;)に変換していきます。"		).append( CR );
		buf.append( "よって、通常に１文字(Shift-JISで２Byte,UTF-8で３Byte)が、８Byteになります。"	).append( CR );
		buf.append( "この変換された文字列を、HTML上でそのまま取り出すと、元のUnicode文字に戻る為"	).append( CR );
		buf.append( "通常のShift-JISでは、扱えない文字(中国語など)でも表示可能になります。"			).append( CR );
		buf.append( CR );
		buf.append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。"	).append( CR );
		buf.append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に"		).append( CR );
		buf.append( "繋げてください。"																).append( CR );
		buf.append( CR ).append( CR );
		buf.append( getArgument().usage() ).append( CR );

		return buf.toString();
	}

	/**
	 * このクラスは、main メソッドから実行できません。
	 *
	 * @param	args String[]
	 */
	public static void main( final String[] args ) {
		LogWriter.log( new Process_UnicodeEscape().usage() );
	}
}
