/*
このファイルの中身は、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) ______________________________________.
*/

//
//	GroupAndPeople 管理XMO ツリー操作クラス
//			変更履歴
//				2004.01.08	新規作成	二條智文
//
package jp.co.ntt.lms.xmo.GroupAndPeople;

import java.util.*;

import org.apache.commons.lang.StringEscapeUtils;

import jp.co.ntt.lms.xmo.*;
import jp.co.ntt.lms.xmo.Message.Message;
import jp.co.ntt.lms.xmo.util.DebugLog;

/**
 * GroupAndPeople 管理XMO ツリー操作クラスです。<BR>
 * GroupAndPeopleテーブルの全レコードをロードして、ツリー構造として、
 * メモリ上に展開します。<BR>
 * XMOTreeクラスを継承しているため、XMOTreeの中の検索メソッドを使用できます。<BR>
 * ツリーの各ノードを操作するために、XMONodeクラスを使用します。
 * @author T.Nijou
 * @version 0.0.0.1 2004/01/08
 */
public class GroupAndPeopleTree extends XMOTree
	implements GroupAndPeopleMessage, GroupAndPeopleTable
{

	private static final int INT_MOVE = 1;
	private static final int INT_COPY = 2;

	/** データアクセスクラスを保持します */
	GroupAndPeopleDataAccess mobjDataAccess;
	/**
	 * コンストラクタです
	 * @param objDataAccess GroupAndPeopleテーブルにアクセスするときに使用します。<BR>
	 *                      必ずインスタンス化されたものを渡してください。nullの場合の
	 *                      動作はこのクラスでは保証されません。
	 */
	public GroupAndPeopleTree(GroupAndPeopleDataAccess objDataAccess)
	{
		// プライベート変数にデータアクセスクラスを設定します。
		mobjDataAccess = objDataAccess;
	}

	/**
	 * GroupAndPeopleテーブルの全レコードを読み込み
	 * ツリー構造としてメモリ上に展開します。
	 */
	public void load()
		throws XMOException
	{
		// GroupAndPeopleテーブル内のデータをメモリ上に階層構造で展開します
		mobjFirstNode = new XMONode( "/", null );
		try {
			mobjFirstNode.addChild( loadGroupAndPeople( "GR000000", mobjFirstNode, new Hashtable() ) );
		}
		catch( Exception e ) {
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			throw new XMOException(e.getMessage());
		}
	}

	/**
	 * GroupAndPeopleテーブル情報をロードして階層構造でメモリ上に保持します
	 * @param strParentID 親ID
	 * @param objParentNode 親ノード
	 * @param htList グループの２重登録があった場合のために親IDをキーにして子ノードリストを
	 *               キャッシュします<BR>
	 *               キャッシュに存在する場合はキャッシュから子ノードリストを取得します
	 * @return XMONode 階層構造データ
	 * @exception Exception DBエラー
	 */
	private XMONode loadGroupAndPeople( String strParentID, XMONode objParentNode, Hashtable htList )
		throws Exception
	{
		XMONode objChildNode = new XMONode( strParentID, objParentNode );

		// IDがグループのときのみ以下の処理を行います
		if( strParentID.substring( 0,2 ).equals( "GR" ) ) {
			// キャッシュから親IDをキーにして子ノードリストを取得します
			Vector vecChildList = (Vector)htList.get( strParentID );
			if( vecChildList != null ) {
				// キャッシュから取得できた場合はその子ノードリストを追加する
				objChildNode.setChildList( vecChildList );
			}
			else {
				// キャッシュに存在しない場合は親IDを元に子IDをDBから検索し取得する
				String[] strChildID = searchChildList( strParentID );
				for( int i = 0; i < strChildID.length; i++ ) {
					objChildNode.addChild( loadGroupAndPeople( strChildID[i], objChildNode, htList ) );
				}
				// 子ノードリストを親IDをキーにしてキャッシュに保持します
				htList.put( objChildNode.getParentID(), objChildNode.getChildList() );
			}
		}
		return objChildNode;
	}

	/**
	 * 親IDを元に子IDをデータベースから検索します。
	 * @param strParentID 親ID
	 * @return String[] 子IDリスト
	 */
	private String[] searchChildList( String strParentID )
		throws XMOException
	{
		// 検索条件の組み立て
		String strCondition = " WHERE " + mobjDataAccess.PARENT_ID + "='" + StringEscapeUtils.escapeSql(strParentID) + "'";
		Hashtable htbParam = new Hashtable();
		htbParam.put( CONDITION_KEY, strCondition );
		// 検索処理
		Vector vecResult = mobjDataAccess.select( htbParam );
		String[] strChildList = new String[0];
		if( vecResult.size() > 0 ) {
			strChildList = new String[ vecResult.size() ];
			for( int i = 0; i < vecResult.size(); i++ ) {
				GroupAndPeopleData objData = (GroupAndPeopleData)vecResult.get( i );
				strChildList[ i ] = objData.getChildID();
			}
		}
		return strChildList;
	}

	/**
	 * XMONodeより親IDまたは子IDを指定して検索を行います
	 * @param strParentID 親ノード
	 * @param strChildID 子ノード
	 */
	public Vector getGroupAndPeopleList( String strParentID, String strChildID ) {
		Vector vecList = new Vector();
		// パラメータチェック
		if( ( strParentID == null || strParentID.equals( "" ) ) &&
			( strChildID == null || strChildID.equals( "" ) ) )
		{
			// 親IDと子IDが両方指定されていない場合は何もせずに返す
			return vecList;
		}

		// 親ID指定時
		if( strParentID != null && !strParentID.equals( "" ) ) {
			XMONode objParent = searchNode( strParentID );
			if( objParent == null ) {
				return vecList;
			}
			int intCount = objParent.getChildCount();
			for( int i = 0; i < intCount; i++ ) {
				XMONode objChildNode = objParent.getChildNode( i );
				String strSearchChildID = objChildNode.getParentID();
				if( strChildID != null && !strChildID.equals( "" ) ) {
					if( strChildID.equals( strSearchChildID ) ) {
						String strMemberShipID = strParentID + strSearchChildID;
						GroupAndPeopleData objData = new GroupAndPeopleData();
						objData.setMemberShipID( strMemberShipID );
						objData.setParentID( strParentID );
						objData.setChildID( strSearchChildID );
						vecList.add( objData );
					}
				}
				else {
					String strMemberShipID = strParentID + strSearchChildID;
					GroupAndPeopleData objData = new GroupAndPeopleData();
					objData.setMemberShipID( strMemberShipID );
					objData.setParentID( strParentID );
					objData.setChildID( strSearchChildID );
					vecList.add( objData );
				}
			}
		}
		// 子IDのみ指定時
		else {
			XMONode objSearchParent = searchNodeFromChild( strChildID );
			if( objSearchParent != null ) {
				String strSearchParentID = objSearchParent.getParentNode().getParentID();
				String strMemberShipID = strSearchParentID + strChildID;
				GroupAndPeopleData objData = new GroupAndPeopleData();
				objData.setMemberShipID( strMemberShipID );
				objData.setParentID( strSearchParentID );
				objData.setChildID( strChildID );
				vecList.add( objData );
			}
		}

		return vecList;

	}

	/**
	 * XMONodeよりMemberShipIDを指定して検索を行います
	 * @param strMemberShipID メンバーシップID
	 */
	public Vector getGroupAndPeopleListFromID( String strMemberShipID ) {
		Vector vecList = new Vector();
		// パラメータチェック
		if( ( strMemberShipID == null || strMemberShipID.equals( "" ) ) ) {
			// strMemberShipIDが指定されていない場合は何もせずに返す
			return vecList;
		}

		// メンバーシップIDを元に検索をします
		XMONode objSearchNode = searchNodeFromID( strMemberShipID );
		// ノードが見つかった場合その子ノード分ループしてデータオブジェクトを作成します
		if( objSearchNode != null ) {
			// Dataオブジェクトを作成します
			GroupAndPeopleData objData = new GroupAndPeopleData();
			objData.setMemberShipID( objSearchNode.getID() );
			objData.setParentID( objSearchNode.getParentNode().getParentID() );
			objData.setChildID( objSearchNode.getParentID() );
			vecList.add( objData );
		}

		return vecList;

	}

	/**
	 * データベースに登録処理を行い、メモリ上にも反映させます。<BR>
	 * 排他制御を行い、メモリ上のデータとDB上のデータの整合性を
	 * 保ちます。
	 * @param htData 登録情報
	 *               登録に必要な情報はGroupAndPeopleDataクラスの
	 *               inserメソッドの説明を参照してください。
	 *
	 */
	synchronized public String insert( Hashtable htData )
		throws XMOException
	{
		// グループと人データがnullのとき
		if( htData == null ) {
			DebugLog.write( this.getClass(), new Exception(), DebugLog.HIGHT );
			// パラメータエラーをスローする
			throw new XMOException( getMessageData( PARAM_WAR ) );
		}

		// DBに登録処理を行います
		mobjDataAccess.insert( htData );

		// 親IDの取得
		String strParentID = (String)htData.get(PARENT_ID);
		// 子IDの取得
		String strChildID = (String)htData.get(CHILD_ID);

		XMONode objNode = searchNode( strParentID );
		objNode.addChild( new XMONode( strChildID, objNode ) );

		// 正常時空文字返却
		return "";
	}

	/**
	 * データベースに更新処理を行い、メモリ上にも反映させます。<BR>
	 * 排他制御を行い、メモリ上のデータとDB上のデータの整合性を
	 * 保ちます。
	 * @param htData 更新情報
	 *               更新に必要な情報はGroupAndPeopleDataクラスの
	 *               inserメソッドの説明を参照してください。
	 */
	synchronized public String update( Hashtable htData )
		throws XMOException
	{
		// グループと人データがnullのとき
		if( htData == null ) {
			DebugLog.write( this.getClass(), new Exception(), DebugLog.HIGHT );
			// パラメータエラーをスローする
			throw new XMOException( getMessageData( PARAM_WAR ) );
		}

		// 更新元のレコードを取得します
		Vector vecUpRecode = mobjDataAccess.select( htData );

		int intCount = vecUpRecode.size();
		GroupAndPeopleData objData = null;
		if( intCount > 0 ) {
			// 検索結果を取得
			objData = (GroupAndPeopleData)vecUpRecode.get(0);
		}
		else {
			// 検索結果が取得できない場合はエラーメッセージを返す
			throw new XMOException( getMessageData( NO_RECODE_WAR ) );
		}

		// 親IDの取得
		String strParentID = (String)htData.get(PARENT_ID);
		if( strParentID == null ) {
			strParentID = objData.getParentID();
			htData.put( PARENT_ID, strParentID );
		}
		// 子IDの取得
		String strChildID = (String)htData.get(CHILD_ID);
		if( strChildID == null ) {
			strChildID = objData.getChildID();
			htData.put( CHILD_ID, strChildID );
		}
		// メンバーシップIDの設定
		String strMemberShipID = strParentID + strChildID;
		htData.put( MEMBER_SHIP_ID, strMemberShipID );

		// DBに登録処理を行います
		mobjDataAccess.update( htData, this );

		//ノードの移動を行います
		boolean blIsMove = moveNode( objData.getParentID(), strParentID, strChildID );

		// 移動に失敗した場合
		if( !blIsMove ) {
			DebugLog.write( this.getClass(), new Exception(), DebugLog.HIGHT );
			throw new XMOException( getMessageData( MOVE_FAIL_ERR ) );
		}

		// 正常時インフォメーションメッセージID返却
		return "";
	}

	/**
	 * データベースに削除処理を行い、メモリ上にも反映させます。<BR>
	 * 排他制御を行い、メモリ上のデータとDB上のデータの整合性を
	 * 保ちます。
	 * @param htData 削除情報
	 *               削除に必要な情報はGroupAndPeopleDataクラスの
	 *               deleteメソッドの説明を参照してください。
	 */
	synchronized public String delete( Hashtable htData )
		throws XMOException
	{
		// グループと人データがnullのとき
		if( htData == null ) {
			DebugLog.write( this.getClass(), new Exception(), DebugLog.HIGHT );
			// パラメータエラーをスローする
			throw new XMOException( getMessageData( PARAM_WAR ) );
		}

		// DBに削除処理を行います
		String strMessage =  mobjDataAccess.delete( htData );

		// 親IDの取得
		String strParentID = (String)htData.get(PARENT_ID);
		// 子IDの取得
		String strChildID = (String)htData.get(CHILD_ID);

		XMONode objNode = searchNode( strParentID );
		objNode.deleteChild( strChildID );

		// 正常時空文字返却
		return "";
	}

	/**
	 * メッセージオブジェクトを作成します
	 * @param strMessageID メッセージID
	 * @return MessageData メッセージオブジェクト 例外発生時はnullを返します
	 */
	private MessageData getMessageData( String strMessageID) {
		MessageData objMessage = null;
		try {
			objMessage = Message.getInstance().getMessageData( strMessageID, MSG_FIEL_PATH, MSG_FILE_NAME );
		}
		catch( Exception e ) {

		}
		return objMessage;
	}

	/**
	 * 移動処理を実行する
	 * @param  objSendDataList[] 移動元のノードリスト
	 * @param  objReceiveData    移動先のノード
	 * @param  intType			 処理区分
	 * @return boolean			 true:更新成功 false更新失敗
	 */
	public boolean executeMove( GroupAndPeopleData[] objSendDataList,
								GroupAndPeopleData objReceiveData,
								int intType ) throws XMOException
	{

		//-----------------------------------------------------------------------------
		//移動処理を実行する
		//-----------------------------------------------------------------------------
		boolean blResult = updateTreeOnMemory( objSendDataList, objReceiveData, intType, INT_MOVE );

		return blResult;

	}

	/**
	 * 所属追加処理を実行する
	 * @param  objSendDataList[] コピー元のノードリスト
	 * @param  objReceiveData    コピー先のノード
	 * @param  intType			 処理区分
	 * @return boolean			 true:更新成功 false更新失敗
	 */
	public boolean executeCopy( GroupAndPeopleData[] objSendDataList,
								GroupAndPeopleData objReceiveData,
								int intType ) throws XMOException
	{

		//-----------------------------------------------------------------------------
		//所属追加処理を実行する
		//-----------------------------------------------------------------------------
		boolean blResult = updateTreeOnMemory( objSendDataList, objReceiveData, intType, INT_COPY );

		return blResult;

	}

	/**
	 * 移動元ノードを移動先に移動する
 	 * コピー元ノードをコピー先にコピーする
 	 * 移動orコピーのうちどちらの処理を行うかは、intExecuteTypeで判別している
	 * @param  objSendDataList[] 移動(コピー)元のノードリスト
	 * @param  objReceiveData    移動(コピー)先のノード
	 * @param  intType			 処理区分
	 * @param  intExecuteType	 INT_MOVE:移動処理、INT_COPY:所属追加処理
	 * @return boolean			 true:更新成功 false更新失敗
	 */
	private boolean updateTreeOnMemory( GroupAndPeopleData[] objSendDataList,
										GroupAndPeopleData objReceiveData,
										int intType,
										int intExecuteType ) throws XMOException
	{

		boolean blIsDelete;

		//-----------------------------------------------------------------------------
		//移動先の親ID、及び移動先の子ノードリストに追加する位置(index)を取得する
		//-----------------------------------------------------------------------------
		String strReceiveParentID = "";
		//処理区分の判定
		//上へ or 下へのとき
		if ( intType == 1 || intType == 3 )
		{
			//移動先の親ノードを取得するための親IDを取得する
			strReceiveParentID = objReceiveData.getParentID();
		}
		//中へのとき
		else
		{
			//移動先の親ノードを取得するための親IDを取得する
			strReceiveParentID = objReceiveData.getChildID();
		}

		//-----------------------------------------------------------------------------
		//移動先の親ノードを取得する
		//-----------------------------------------------------------------------------
		XMONode objReceiveParentNode = searchNode( strReceiveParentID );
		// 取得できない場合
		if( objReceiveParentNode == null )
		{
			return false;
		}

		//-----------------------------------------------------------------------------
		//移動先にダミーノードを追加する
		//（理由：移動元ノードリストに移動先ノードが含まれている場合でも対応できるようにする）
		//-----------------------------------------------------------------------------
		setDummyNode( objReceiveData.getChildID(), objReceiveParentNode, intType );

		//-----------------------------------------------------------------------------
		//移動元のノードリストの件数分処理する
		//-----------------------------------------------------------------------------
		int intSendDataListMaxLength = objSendDataList.length;
		for ( int i = 0; i < objSendDataList.length; i++ )
		{
			//-----------------------------------------------------------------------------
			//移動元の親ノードを取得する
			//-----------------------------------------------------------------------------
			XMONode objSendParentNode = searchNode( objSendDataList[i].getParentID() );
			// 取得できない場合
			if( objSendParentNode == null )
			{
				//-----------------------------------------------------------------------------
				//上へまたは下への場合のみ、ダミーノードを削除する
				//（※中へのときは、ダミーノードは追加されていない。）
				//-----------------------------------------------------------------------------
				blIsDelete = deleteDummyNode( objReceiveParentNode, intType );
				// 削除に失敗した場合
				if( !blIsDelete )
				{
					return false;
				}
				return false;
			}

			//-----------------------------------------------------------------------------
			//移動元の子ノードリストから指定されたノードを削除する前に、
			//指定されたノードを取得し、保持しておく
			//-----------------------------------------------------------------------------
			XMONode objSendChildNode = objSendParentNode.getChildNode( objSendDataList[i].getChildID() );

			//-----------------------------------------------------------------------------
			//移動処理時のみ移動元ノードリストから指定されたノードを削除する
			//-----------------------------------------------------------------------------
			if ( intExecuteType == INT_MOVE )
			{
				//指定されたノードを削除する
				blIsDelete = objSendParentNode.deleteChild( objSendDataList[i].getChildID() );
				// 削除に失敗した場合
				if( !blIsDelete )
				{
					//-----------------------------------------------------------------------------
					//上へまたは下への場合のみ、ダミーノードを削除する
					//（※中へのときは、ダミーノードは追加されていない。）
					//-----------------------------------------------------------------------------
					blIsDelete = deleteDummyNode( objReceiveParentNode, intType );
					// 削除に失敗した場合
					if( !blIsDelete )
					{
						return false;
					}
					return false;
				}
			}
			//-----------------------------------------------------------------------------
			//所属追加処理の場合はノードを新規に作成する
			//-----------------------------------------------------------------------------
			else {
				String strParentID = objSendChildNode.getParentID();
				objSendChildNode = new XMONode( strParentID, null );
			}

			//-----------------------------------------------------------------------------
			//移動先ノードリストに移動元ノードを追加する
			//-----------------------------------------------------------------------------
			//移動先ノードリストからダミーノードの位置(Index)を取得する
			int intDummyNodeIndex = objReceiveParentNode.getChildNodeIndex( STR_DUMMYNODEID );

			//追加位置の計算(指定された位置 + 移動元ノードリストのカウンタ値(Index値) + 1)
			//+1している理由は、指定された位置の次の位置から追加するため
			int intNodeIndex = intDummyNodeIndex + i + 1;

			//ノードを追加する
			addChildNode( objReceiveParentNode, objSendChildNode, intNodeIndex, intType );
		}

		//-----------------------------------------------------------------------------
		//上へまたは下への場合のみ、ダミーノードを削除する
		//（※中へのときは、ダミーノードは追加されていない。）
		//-----------------------------------------------------------------------------
		blIsDelete = deleteDummyNode( objReceiveParentNode, intType );
		// 削除に失敗した場合
		if( !blIsDelete )
		{
			return false;
		}

		return true;

	}


}

