/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/

//
//	完結要件ノードクラス
//			変更履歴
//				2004.01.08	新規作成	中山雅文
//
package jp.co.ntt.lms.Common.status.conclusion;

import java.util.Vector;
import jp.co.ntt.lms.xmo.DataAccess;
import jp.co.ntt.lms.xmo.util.DebugLog;

/**
 * 完結要件ノードクラスです。<BR>
 * @author 二條智文
 * @version 0.0.0.1
 */
class ConclusionNode implements DecideConclusion, ConclusionMessage {

	/** 完結要件文字列を保持します */
	private ConclusionData mobjConclusionData;

	/**
	 * ノードタイプを保持します<BR>
	 * デフォルトで論理和タイプです。
	 */
	private int mintNodeType = OR_TYPE;

	/** 子ノードリストを保持します */
	private Vector mvecChildList;

	/** 論理和(OR)ノードタイプです。*/
	private static final int OR_TYPE = 0;

	/** 論理積(AND)ノードタイプです。*/
	private static final int AND_TYPE = 1;

	/** 論理和(OR)記号です。*/
	private static final char OR_CHAR = '|';

	/** 論理積(AND)記号です。*/
	private static final char AND_CHAR = '&';

	/** 左括弧です。*/
	private static final char LEFT_PARENTHESIS = '(';

	/** 右括弧です。*/
	private static final char RIGHT_PARENTHESIS = ')';


	/**
	 * コンストラクタです。<BR>
	 * パースする完結要件情報を格納したデータオブジェクトを指定します。
	 * @param objConclusionData
	 */
	public ConclusionNode( ConclusionData objConclusionData ) {
		mobjConclusionData = objConclusionData;
	}

	/**
	 * 完結要件を満たしているかどうかチェックします
	 * @boolean true: 満たしている false: 満たしていない
	 * @exception ConclusionException 解析中に実行時例外が生じた場合
	 */
	public boolean isConcluded()
		throws ConclusionException
	{
		for( int i = 0; i < mvecChildList.size(); i++ ) {
			DecideConclusion objDecide = (DecideConclusion)mvecChildList.get(i);
			if( mintNodeType == OR_TYPE ) {
				if( objDecide.isConcluded() ) {
					// 論理和の場合はひとつでも正が存在したら正を返す
					return true;
				}
			}
			else if( mintNodeType == AND_TYPE ) {
				if( !objDecide.isConcluded() ) {
					// 論理積の場合はひとつでも負が存在したら負を返す
					return false;
				}
			}
		}

		if( mintNodeType == OR_TYPE ) {
			return false;
		}
		else if( mintNodeType == AND_TYPE ) {
			return true;
		}

		return false;
	}

	/**
	 * 完結要件条件式に使用されているLOIDのリストを取得します。
	 * @return Vector LOIDリスト
	 */
	public Vector getLoIDList() {
		Vector vecReq = new Vector();
		for( int i = 0; i < mvecChildList.size(); i++ ) {
			DecideConclusion objDecide = (DecideConclusion)mvecChildList.get(i);
			Vector vecTemp = objDecide.getLoIDList();
			for( int j = 0; j < vecTemp.size(); j++ ) {
				vecReq.add(vecTemp.get(j));
			}
		}
		return vecReq;
	}

	/**
	 * 完結要件文字列を分解します。<BR>
	 * 分解は以下のように行われます。<P>
	 *
	 *     完結要件文字列に '|'(論理和) が含まれる場合は
	 *     論理和で分割してリスト化します。<BR>
	 *     完結要件文字列が '&'(論理積) のみの場合は論理積で
	 *     分割します。
	 *  <PRE>
	 *     例1:
	 *       完結要件:: LO5 = p & (LO6 = c | LO7 = p) | LO8 = c
	 *       この場合以下のように分解され、このクラスのノードタイプはOR_TYPEに
	 *       設定されます。
	 *               [0] :: LO5 = p & (LO6 = c | LO7 = p)
	 *               [1] :: LO8 = c
	 *     例2:
	 *       完結要件:: LO5 = p & (LO6 = c | LO7 = p)
	 *       この場合以下のように分解され、このクラスのノードタイプはAND_TYPEに
	 *       設定されます。
	 *               [0] :: LO5 = p
	 *               [1] :: LO6 = c | LO7 = p
	 *  </PRE>
	 * @exception ConclusionParseException パース例外が発生した場合。
	 */
	public void parse()
		throws ConclusionParseException
	{
		if ( mobjConclusionData == null ){
			throw new ConclusionParseException( ConclusionEnv.getMessage(SYSTEM_ERR) );
		}
		
		Vector result;
		//スペース、タブ、CR、LF、()を削除する。
		String strConclusion = eraseSPTABCRLF( mobjConclusionData.getConclusion() );

		// | で構文解析を行う。
		result = parseSentence( strConclusion, OR_CHAR );
		if ( result.size() != 0 ){
			mintNodeType = OR_TYPE;
			for( int i=0; i<result.size(); i++ ){
				makeChildObject( (String)result.get(i), mobjConclusionData.getUserID(), 
													mobjConclusionData.getDataAccess() );
			}
		}
		else{
			// |で分解出来ない場合は、&で構文解析を行う。
			result = parseSentence( strConclusion, AND_CHAR );
			if ( result.size() != 0 ){
				mintNodeType = AND_TYPE;
				for( int i=0; i<result.size(); i++ ){
					makeChildObject( (String)result.get(i), mobjConclusionData.getUserID(),
													mobjConclusionData.getDataAccess() );
				}
			}
		}

		// |でも&でも分解できない場合はプリミティブ
		if ( result.size() == 0 ){
			makeChildObject( strConclusion, mobjConclusionData.getUserID(),
												mobjConclusionData.getDataAccess() );
		}

		return;
	}

	/**
	 * 構文解析を行います。
	 * @param  String 完結要件文字列
	 * @param  char   構文解析記号(| or &)
	 * @return 分割したセンテンスを格納したVector
	 * @exception ConclusionParseException 解析中に実行時例外が生じた場合
	 */
	private Vector parseSentence( String strInput, char cDest)
			throws ConclusionParseException	
	{
		int				iParenthesis = 0;	//括弧の階層数
		char			c;
		int				beforeindex = 0;	//前回検索時の最後＋１を指すオフセット
		Vector			vecResult = new Vector();
		StringBuffer	check = new StringBuffer(strInput);


		for( int i=0; i<check.length(); i++ ){
			c = check.charAt(i);
			switch( c ){
				case LEFT_PARENTHESIS:
						//階層を増やす
						iParenthesis++;
					break;
				case RIGHT_PARENTHESIS:
						//階層を減らす
						iParenthesis--;
						if ( iParenthesis < 0 ){
							throw new ConclusionParseException( ConclusionEnv.getMessage(RIGTHPARENTHESIS_ERR) );
						}
					break;
				default:
					if ( c == cDest ){
						if ( iParenthesis == 0 ){//最上位の場合のみ分解する
							if ( i <= beforeindex ){
									throw new ConclusionParseException( ConclusionEnv.getMessage(SYMBOL_ERR) );
							}
							String strSentence = eraseParenthesis( check.substring(beforeindex, i) );
							vecResult.add( strSentence );
							beforeindex = i+1;//cDestをスキップしたインデックスを設定
						}
					}
					break;
			}
		}
		// iParenthesisが０より大きい場合は)が足りません。
		if ( iParenthesis > 0 ){
			throw new ConclusionParseException( ConclusionEnv.getMessage(LEFTPARENTHESIS_ERR) );
		}
		// すでに切り出した場合、残っている部分を最後のセンテンスとして切り出します。
		if ( beforeindex != 0 ){
			if ( beforeindex >= check.length() ){
				throw new ConclusionParseException( ConclusionEnv.getMessage(ENDSENTENCE_ERR) );
			}
			String strSentence = eraseParenthesis( check.substring(beforeindex, check.length()) );
			vecResult.add( strSentence );
		}
		
		return vecResult;
	}


	/**
	 * ()を削除します。
	 * @param  String 分割後の完結要件文字列
	 * @return ()を削除した完結要件文字列
	 */
	private String eraseParenthesis(String strInput)
	{
		int				iParenthesis = 0;
		char			c;
		int				iEndIndex = strInput.length()-1;
		
		//先頭と最後が()ならば削除する。
		if ( strInput.charAt(0) == LEFT_PARENTHESIS && strInput.charAt(iEndIndex) == RIGHT_PARENTHESIS ){
			StringBuffer check = new StringBuffer( strInput.substring( 1, iEndIndex ) );
			//()を削除した後の文字列をチェックする。
			for( int i=0; i<check.length(); i++ ){
				c = check.charAt(i);
				switch( c ){
					case LEFT_PARENTHESIS:
							//階層を増やす
							iParenthesis++;
						break;
					case RIGHT_PARENTHESIS:
							//階層を減らす
							iParenthesis--;
							if ( iParenthesis < 0 ){
								// ()を削除したことによる不整合の為入力されたものを返却する。
								return strInput;
							}
						break;
					default:
						break;
				}
			}
			if ( iParenthesis > 0 ){
				// ()を削除したことによる不整合の為入力されたものを返却する。
				return strInput;
			}
			//再帰呼び出しを行い次の()の削除を試みる。
			return eraseParenthesis( check.toString() );
		}
		else{
			// 最初と最後が()でない場合は入力されたものを返却する。
			return strInput;
		}
	}

	/**
	 * 解析に不要なコードと括弧を削除します。
	 * @param strInput 完結要件文字列
	 * @return 削除後の文字列
	 */
	private String eraseSPTABCRLF(String strInput)
	{
		
		String strCheck = replace(strInput, ' ');
		strCheck = replace(strCheck, '\t');
		strCheck = replace(strCheck, '\r');
		strCheck = replace(strCheck, '\n');
		strCheck = eraseParenthesis( strCheck );
		return strCheck;
	}

	private String replace(String src, char match) {
		StringBuffer	result = new StringBuffer(src);
		
		int i = 0;
		while( true ){
			if ( i >= result.length() ){
				break;
			}
			if ( result.charAt(i) == match ){
				result = result.deleteCharAt(i);
				i--;
			}
			i++;
		}
		return result.toString();
	}

	/**
	 * プリミティブかどうかチェックします。
	 * @param strInput 完結要件文字列
	 * @return true:プリミティブ
	 * @exception ConclusionParseException 解析中に実行時例外が生じた場合
	 */
	private boolean isPrimitive(String strInput) throws ConclusionParseException
	{
		
		Vector result;
				// | で構文解析を行う。
		result = parseSentence( strInput, OR_CHAR );
		if ( result.size() != 0 ){
			return false;
		}
		else{
			// |で分解出来ない場合は、&で構文解析を行う。
			result = parseSentence( strInput, AND_CHAR );
			if ( result.size() != 0 ){
				return false;
			}
		}

		return true;
	}

	/**
	 * 子ノードの解析を行います。
	 * @param  String 完結要件文字列
	 * @param  String ユーザID
	 * @exception ConclusionParseException 解析中に実行時例外が生じた場合
	 */
	private void makeChildObject(String strSentence,String strUserID, DataAccess objDataAccess ) 
			throws ConclusionParseException
	{
		ConclusionData objChildData = new ConclusionData( strSentence, strUserID, objDataAccess );

		if ( isPrimitive(strSentence) != true ){
			//プリミティブでない場合、ノードオブジェクトを生成しパース
			ConclusionNode objChildNode = new ConclusionNode( objChildData );
			objChildNode.parse();
			if ( mvecChildList == null ){
				mvecChildList = new Vector();
			}
			mvecChildList.add( objChildNode );
		}
		else{
			//プリミティブの場合
			ConclusionPrimitive objChildPrimitive = new ConclusionPrimitive( objChildData );
			objChildPrimitive.parse();
			if ( mvecChildList == null ){
				mvecChildList = new Vector();
			}
			mvecChildList.add( objChildPrimitive );
		}
		return;
	}



}


