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

import java.util.*;
import jp.co.ntt.lms.xmo.util.*;

/**
 * ノードクラスを操作するクラスです。<BR>
 * @author T.Nijou
 * @version 0.0.0.1 2004/01/08
 */
public class XMOTree extends XMONode {

	public static final String STR_DUMMYNODEID = "|DUMMYNODE_ID|";

	/** 最上位ノードを保持します */
	protected XMONode mobjFirstNode;

	/**
	 * 最上位ノードを設定します
	 * @param objFirstNode 最上位ノード
	 */
	public void setFirstNode( XMONode objFirstNode ) {
		mobjFirstNode = objFirstNode;
	}

	/**
	 * 指定した親IDのノードを取得します
	 * @param strParentID
	 * @return GroupAndPeople
	 */
	public XMONode searchNode( String strParentID ) {
		return searchNode( strParentID, mobjFirstNode );
	}

	/**
	 * 指定した親IDのノードを取得します。<BR>
	 * 取得できない場合はnullを返します。
	 * @param strParentID 親ID
	 * @param objNode 検索対象ノード
	 */
	public XMONode searchNode( String  strParentID, XMONode objNode ) {
		XMONode objChildNode = null;
		int intCount = objNode.getChildCount();
		for( int i = 0; i < intCount; i++ ) {
			objChildNode = objNode.getChildNode( i );
			if( objChildNode.getParentID().equals( strParentID ) ) {
				return objChildNode;
			}
			else {
				XMONode objNodeTemp = searchNode( strParentID, objChildNode );
				if( objNodeTemp != null ) {
					return objNodeTemp;
				}
			}
		}
		return null;
	}

	/**
	 * 子IDを元に親ノードを検索します
	 * @return String 親ID
	 */
	public XMONode searchNodeFromChild( String strChildID ) {
		return searchNodeFromChild( strChildID, mobjFirstNode );
	}

	/**
	 * 子IDを元に親IDを検索します
	 * 見つからない場合はnullを返します
	 */
	public XMONode searchNodeFromChild( String strChildID, XMONode objNode ) {
		XMONode objChildNode = null;
		int intCount = objNode.getChildCount();
		for( int i = 0; i < intCount; i++ ) {
			objChildNode = objNode.getChildNode( i );
			String strTempID = objChildNode.getParentID();
			if( strChildID.equals( strTempID ) ) {
				return objChildNode;
			}
			else {
				XMONode objTempNode = searchNodeFromChild( strChildID, objChildNode );
				if( objTempNode != null ) {
					return objTempNode;
				}
			}
		}
		return null;
	}

	/**
	 * IDを元に下位のノードを検索します。<BR>
	 *  取得できない場合はnullを返します
	 * @param strMemberShip メンバーシップID
	 * @return XMONode 検索されたノード 取得できなかったときはnull
	 */
	public XMONode searchNodeFromID( String strID ) {
		return searchNodeFromID( strID, mobjFirstNode );
	}

	/**
	 * IDを元に下位のノードを検索します。<BR>
	 *  取得できない場合はnullを返します
	 * @param strMemberShip メンバーシップID
	 * @param objNode 検索対象ノード
	 * @return XMONode 検索されたノード 取得できなかったときはnull
	 */
	public XMONode searchNodeFromID( String strID, XMONode objNode ) {
		if( strID == null ) {
			return null;
		}

		// 子ノードの数分ループします
		int intCount = objNode.getChildCount();
		for( int i = 0; i < intCount; i++ ) {
			XMONode objChildNode = objNode.getChildNode( i );
			if( objChildNode.getID().equals( strID ) ) {
				return objChildNode;
			}
			else {
				XMONode objTempChild = searchNodeFromID( strID, objChildNode );
				if( objTempChild != null ) {
					return objTempChild;
				}
			}
		}
		return null;
	}

	/**
	 * ノードの移動を行います<BR>
	 * @param strBeforeID 移動前親ID
	 * @param strAfterID 移動後親ID
	 * @param strChildID 移動対象ID
	 * @return boolean 移動結果 true: 正常に移動
	 *                          false: 移動失敗
	 */
	public boolean moveNode( String strBeforeID, String strAfterID, String strChildID ) {

		// 移動前親IDで親ノードを検索します
		XMONode objBeforeNode = searchNode( strBeforeID );
		// 移動後親IDで移動後親ノードを検索します
		XMONode objAfterNode = searchNode( strAfterID );

		// 検索結果の親IDが見つからない場合
		if( objBeforeNode == null )
		{
			return false;
		}

		if( objAfterNode == null ) {
		}

		// 検索した親ノードから移動対象ノードを検索します
		XMONode objChildNode = searchNode( strChildID, objBeforeNode );

		// 見つからなかった場合
		if( objChildNode == null ) {
			return false;
		}

		// 検索前親ノードから移動対象ノードを削除します
		boolean blIsDelete = objBeforeNode.deleteChild( strChildID );

		// 削除に失敗した場合
		if( !blIsDelete ) {
			return false;
		}

		// 検索後親ノードの子ノードに移動対象ノードを追加します
		objAfterNode.addChild( objChildNode );

		return true;
	}

	/**
	 * 指定された親ノードの子ノードリスト対して、
	 * 指定されたChildID先にダミーノードを追加する<BR>
	 * @param  strChildID		ChildID
	 * @param  objParentNode	親ノード
 	 * @param  intType			処理区分
	 * @return Nothing
	 */
	protected void setDummyNode( String strChildID, XMONode objParentNode, int intType )
	{

		//-----------------------------------------------------------------------------
		//ダミーノードを追加する
		//-----------------------------------------------------------------------------
		//コピー先ノードリストから指定されたノードの位置(Index)を取得する
		int intChildNodeIndex = objParentNode.getChildNodeIndex( strChildID );
		//ダミーノードを生成する
		XMONode objDummyNode = new XMONode( STR_DUMMYNODEID, objParentNode );

		//存在しない場合、ダミーノードをコピー先に追加する
		//上へのとき
		if ( intType == 1 )
		{
			//コピー先ノードリストの指定された位置にダミーノードを追加する
			objParentNode.addChild( intChildNodeIndex, objDummyNode );
		}
		//下へのとき
		else if (intType == 3)
		{
			//指定されたノードの位置がコピー先ノードリストの最下位である場合
			if ( objParentNode.getChildCount() -1 == intChildNodeIndex )
			{
				//コピー先ノードリストの一番下にダミーノードを追加する
				objParentNode.addChild( objDummyNode );
			}
			else
			{
				//コピー先ノードリストの指定された位置 + 1 にダミーノードを追加する
				//+1を行わないと、指定したノードの上にダミーノードが作られてしまうため
				objParentNode.addChild( intChildNodeIndex + 1, objDummyNode );
			}
		}
		else
		{
		//中へのときはダミーノード自体を追加しない方法で処理できるためここでの処理は行わない
		}
	}

	/**
	 * 指定されたダミーノードID先のダミーノードを削除する<BR>
	 * @param  objParentNode	ダミーノードを配下に持つ親ノード
 	 * @param  intType			処理区分
	 * @return boolean			true:削除成功、false:削除失敗
	 */
	protected boolean deleteDummyNode( XMONode objParentNode, int intType )
	{

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

	/**
	 * 指定されたChildID先に指定されたノードを追加する<BR>
	 * @param  objParentNode	親ノード
	 * @param  objAddNode		追加するノード
 	 * @param  intAddIndex		追加するノードの位置(Index)
 	 * @param  intType			処理区分
	 * @return Nothing
	 */
	protected void addChildNode( XMONode objParentNode, XMONode objAddNode, int intAddIndex, int intType)
	{

		//-----------------------------------------------------------------------------
		//ノードリストに、ノードを追加する（処理区分別）
		//-----------------------------------------------------------------------------
		//上へ or 下へのとき
		if ( intType == 1 || intType == 3 )
		{
			//指定されたノードの位置がノードリストの範囲外である場合
			if ( objParentNode.getChildCount() -1 < intAddIndex )
			{
				//一番下に追加する
				objParentNode.addChild( objAddNode );
			}
			else
			{
				//指定された位置(Index)に追加する
				objParentNode.addChild( intAddIndex, objAddNode );
			}
		}
		//中へのとき
		else if ( intType == 2 )
		{
			objParentNode.addChild( objAddNode );
		}
	}


	/**
	 * 指定したIDまでのパスを検索します。<BR>
	 * パスはString[]で表します。<BR>
	 * 上記のパスはVectorに格納して返します。
	 * @param strID 検索ID
	 * @return Vector 検索結果
	 */
	public Vector getPath( String strID ) {

		// 指定されたIDを持つノードを検索し、そのパスを取得する。
		// ノードが存在しない場合はnullが返る
		Vector vecPath = getPath( strID, mobjFirstNode );

		if( vecPath == null ) {
			// パス上に指定されたIDのノードが存在しない場合は
			// 長さ0のVectorを返す。
			return new Vector();
		}

		// 戻りVector
		Vector vecReturn = new Vector();
		// 検索結果を戻り値に変換します
		Vector vecTemp = new Vector();
		for( int i = 0; i < vecPath.size(); i++ ) {

			String strIDTemp = (String)vecPath.get(i);
			if( strIDTemp.equals( strID ) ) {
				vecTemp.add( strIDTemp );
				String strRootPath = (String)vecTemp.get(0);
				if( !strRootPath.equals( mobjFirstNode.getParentID() ) ) {
					Vector vecRootPath = getPath( strRootPath, mobjFirstNode );
					for( int j = 0; j < vecRootPath.size() - 1; j++ ) {
						String strRootTemp = (String)vecRootPath.get(j);
						vecTemp.add( j, strRootTemp );
					}
				}
				if( vecTemp.get(0).equals( "/" ) ) {
					vecTemp.remove(0);
				}
				// 配列に変換
				String[] strPathList = new String[vecTemp.size()];
				vecTemp.copyInto( strPathList );
				// テンポラリVectorを初期化
				vecTemp = new Vector();
				vecReturn.add( strPathList );
			}
			else {
				vecTemp.add( strIDTemp );
			}
		}

		return vecReturn;
	}

	// 再帰メソッド
	private Vector getPath( String strID, XMONode objNode ) {

		// パス格納要のVectorを用意します
		Vector vecPath = new Vector();

		String strParentID = objNode.getParentID();

		// 検索IDと同じ場合
		if( strParentID.equals( strID ) ) {
			vecPath.add( strParentID );
		}
		// 検索IDと違う場合
		else {
			// 再帰呼び出し
			for( int i = 0; i < objNode.getChildCount(); i++ ) {
				Vector vecPathTemp = getPath( strID, objNode.getChildNode(i) );
				if( vecPathTemp != null ) {
					vecPath.add( strParentID );
					// 子ノードに検索IDが存在した場合
					for( int j = 0; j < vecPathTemp.size(); j++ ) {
						vecPath.add( vecPathTemp.get(j) );
					}
				}
			}
			// 見つからない場合はnullを返す
			if( vecPath.size() == 0 ) {
				return null;
			}
		}

		return vecPath;

	}

	/**
	 * 指定IDを元に配下のIDを取得します
	 * @param strID 指定メンバーシップID
	 * @return String[] 指定配下のIDリスト
	 */
	public String[] getUnderIDList( String strID ) {
		if( strID == null ) {
			return null;
		}

		// メンバーシップIDを元に下位のノードを検索します。
		XMONode objNode = searchNodeFromID( strID );

		if( objNode == null ) {
			return null;
		}

		Vector vecIDList = null;
		// ノードにロックをかけます
		synchronized( objNode ) {
			// 指定ID配下のIDリストを取得します。
			vecIDList = getUnderIDList( objNode );
		}
		String[] strIDList = new String[vecIDList.size()];
		vecIDList.copyInto( strIDList );

		return strIDList;
	}

	/**
	 * 指定IDを元に配下のIDを取得します
	 * @param objNode 指定配下のノード
	 * @return Vector IDリスト
	 */
	private Vector getUnderIDList( XMONode objNode ) {
		Vector vecList = new Vector();
		if( objNode == null ) {
			return vecList;
		}

		if(vecList.indexOf(objNode.getParentID()) == -1){
			vecList.add(objNode.getParentID());
		}

		// カーソルを先頭に戻します。
		objNode.first();
		while(objNode.next() ) {
			XMONode objChildNode = objNode.getChildNode();
			if(vecList.indexOf(objNode.getParentID()) == -1){
				vecList.add( objChildNode.getParentID() );
			}
			vecList.addAll( getUnderIDList( objChildNode ) );
		}

		return vecList;
	}


	/**
	 * 指定した親IDを持つノードを引数で渡されたノードで入れ替えます。
	 * @param strParentID 親ID
	 * @param objNode 入れ替えるノード
	 */
	public void switchNode( String strParentID, XMONode objNode ) {
		XMONode objSwitchNode = searchNode( strParentID );
		if( objSwitchNode == null ) {
			return;
		}
		objSwitchNode.getParentNode().switchNode(strParentID, objNode);
	}


	/**
	 * 指定されたIDの置換を行います<BR>
	 * @param strBeforeID 変更前ID
	 * @param strAfterID 変更後ID
	 * @return boolean 移動結果 true: 正常に移動
	 *                          false: 移動失敗
	 */
	public boolean changeID( String strBeforeID, String strAfterID ) {
		// 変更前親IDでノードを検索します
		XMONode objBeforeNode = searchNode( strBeforeID );

		// 検索結果のIDが見つからない場合
		if( objBeforeNode == null )
		{
			return false;
		}

		objBeforeNode.setParentID(strAfterID);

		return true;
	}
}

