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

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

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Vector;

import org.apache.commons.lang.StringEscapeUtils;

import jp.co.ntt.lms.Common.status.PropagateControl;
import jp.co.ntt.lms.Common.status.StatusPropagation;
import jp.co.ntt.lms.lo.LoLogDataAccess;
import jp.co.ntt.lms.lo.LoLogRecord;
import jp.co.ntt.lms.lo.LoPropertyDef;
import jp.co.ntt.lms.lo.LoTopClient;
import jp.co.ntt.lms.lo.util.PropagateUtil;
import jp.co.ntt.lms.xmo.DataAccess;
import jp.co.ntt.lms.xmo.Environment;
import jp.co.ntt.lms.xmo.MenuData;
import jp.co.ntt.lms.xmo.MessageData;
import jp.co.ntt.lms.xmo.XMO;
import jp.co.ntt.lms.xmo.XMOException;
import jp.co.ntt.lms.xmo.XMONode;
import jp.co.ntt.lms.xmo.XMOPropertyDef;
import jp.co.ntt.lms.xmo.Assignments.AssignmentsClient;
import jp.co.ntt.lms.xmo.GroupAndPeople.GroupAndPeopleTable;
import jp.co.ntt.lms.xmo.Lo.LoClient;
import jp.co.ntt.lms.xmo.Lo.LoDataAccess;
import jp.co.ntt.lms.xmo.Lo.LoTable;
import jp.co.ntt.lms.xmo.Message.Message;
import jp.co.ntt.lms.xmo.util.DebugLog;

/**
 * LoRelation 管理XMO サーバクラスです。<BR>
 * XMO基本メソッドを実装し、クライアントからの登録、更新、削除、検索処理
 * 要求をうけそれぞれの処理を行います。<BR>
 * RMIサーバとして動作するためRemoteインタフェースであるXMOインタフェースを実装し、
 * 永続的な起動状態を確保するためjava.rmi.server.UnicastRemoteObjectクラスを継承します。
 * @author T.Nijou
 * @version 0.0.0.1 2004/01/08
 */
public class LoRelation extends UnicastRemoteObject
	implements XMO, LoRelationMessage, LoRelationTable
{

	//////////////////////////////////////////////////////////////////
	// メソッド名定義
	//////////////////////////////////////////////////////////////////
	/** 登録メソッド名です */
	private static final String METHOD1 = "insert";	// 登録
	/** 更新メソッド名です */
	private static final String METHOD2 = "update";	// 更新
	/** 削除メソッド名です */
	private static final String METHOD3 = "delete";	// 削除
	/** 検索メソッド名です */
	private static final String METHOD4 = "select";	// 検索
	/** パス検索メソッド名です */
	private static final String METHOD5 = "getPath";	// パス検索
	/** コピーメソッド名です */
	private static final String METHOD6 = "copy";	// コピー
	/** 移動します。 */
	private static final String METHOD7 = "transfer";
	/** コンテナ配下のLOの前提条件チェックを行います。 */
	private static final String METHOD8 = "checkUnderPrere";


	/** 複製LOキーワード1 */
	private static final String PREFIX_LO_TYTLE1 = "コピー";
	/** 複製LOキーワード2 */
	private static final String PREFIX_LO_TYTLE2 = "  ";

	/**
	 * LoRelationテーブルアクセスクラスを保持します
	 */
	private LoRelationDataAccess mobjDataAccess = new LoRelationDataAccess();

	/**
	 * LoRelationの階層情報を保持します
	 */
	public static LoRelationTree mobjTree;
	
	//	ADD 2005/03/25 START Koji Umehara [メソッド管理追加]
	/**
	 * LoRelationの階層情報を保持します(メソッドツリー)
	 */
	public static LoRelationTree mobjMethodTree;
	//	ADD 2005/03/25 END Koji Umehara [メソッド管理追加]

	/**
	 * コンストラクタです。
	 */
	public LoRelation() throws RemoteException, XMOException {
		// リモートオブジェクトをエクスポートする
		// java.rmi.server.UnicastRemoteObject の
		// 引数なしのコンストラクタを呼び出す
		super();

		if( mobjTree == null ) {
			mobjTree = new LoRelationTree(mobjDataAccess);
			mobjTree.load();
		}
		//	ADD 2005/03/25 START Koji Umehara [メソッド管理追加]
		if( mobjMethodTree == null ) {
			mobjMethodTree = new LoRelationTree(mobjDataAccess);
			mobjMethodTree.loadMethod();
		}
		//	ADD 2005/03/25 END Koji Umehara [メソッド管理追加]
	}

	/**
	 * メニュー一覧を返します
	 * @return Vector メニュー一覧
	 */
	public Vector getMenuList(Hashtable htbParam) {
		Vector vecMenuList = new Vector();
		try {
			checkAssign(htbParam);
			String check = (String)htbParam.get(LoPropertyDef.MANAGE_ASSIGN);
			//管理アサインされている場合のみメニューを取得する
			if ( check != null && check.equals("1") == true ){
				LoRelationEnv objEnv = LoRelationEnv.getInstance();
				vecMenuList = objEnv.getMenuList();
			}
		}
		catch( Exception e ) {
			return new Vector();
		}
		return vecMenuList;
	}

	/**
	 * メソッド一覧を返します
	 * @return Vector
	 */
	public Vector getMethodList() {
		Vector vecMethod = new Vector();
		vecMethod.add( METHOD1 );
		vecMethod.add( METHOD2 );
		vecMethod.add( METHOD3 );
		vecMethod.add( METHOD4 );
		return vecMethod;
	}

	/**
	 * デフォルトメニューを返します
	 * @return String
	 */
	public MenuData getDefaultMenu() {
		return null;
	}

	/**
	 * このXMOで定義された、メソッドを実行するときの
	 * クライアント側からの入り口になるメソッドです。
	 * @param strMethodName 実行するメソッド名を指定します
	 * @param htData メソッドで実行するパラメータを指定します<BR>
	 *               なお必要なキーと値は各DAOの処理メソッドの説明を参照して下さい。
	 * @retunr Object 戻り値（さまざまな戻り値に対応するためObject型としてあります)<BR>
	 *                受け取り側では適切な値にキャストする必要があります。
	 */
	public Object execute( String strMethodName, Hashtable htData ) {

		Object objReturn = null;

		if( strMethodName.equals( METHOD1 ) ) {
			objReturn = insert( htData );
		}
		else if( strMethodName.equals( METHOD2 ) ) {
			objReturn = update( htData );
		}
		else if( strMethodName.equals( METHOD3 ) ) {
			objReturn = delete( htData );
		}
		else if( strMethodName.equals( METHOD4 ) ) {
			objReturn = select( htData );
		}
		else if( strMethodName.equals( METHOD5 ) ) {
			objReturn = getPath( htData );
		}
		else if( strMethodName.equals( METHOD6 ) ) {
			objReturn = copy( htData );
		}
		else if( strMethodName.equals( METHOD7 ) ) {
			objReturn = transfer( htData );
		}
		else if( strMethodName.equals( METHOD8 ) ) {
			objReturn = checkUnderPrere( htData );
		}

		return objReturn;
	}

	/**
	 * 起動確認（必ずtrueを返す）
	 * @return boolean 必ずtrueが返ります
	 */
	public boolean isLaunch() {
		return true;
	}

	/**
	 * LoRelationテーブルへ登録処理を行います。<BR>
	 * 以下のパラメータチェックを行い、すべてOKの場合は
	 * LoRelationTreeクラスの登録処理メソッドを実行します。
	 * <PRE>
	 *             key                     説明
	 *          ---------------------------------------------
	 *             PARENT_ID               親ID
	 *             CHILD_ID                子ID
	 *             METHOD_FLAG             メソッドツリーが対象かどうか
	 *          ---------------------------------------------
	 * </PRE>
	 * @param htData 登録情報
	 * @return String 結果(正しく終了した場合のみ)
	 * @exception XMOException データベースエラーが発生した場合
	 */
	private Vector insert( Hashtable htData ) {
		//	ADD 2005/04/22 START Koji Umehara [メソッド管理追加]
		// 対象となるツリーをMETHOD_FLAGにより判別する
		LoRelationTree objTree = 
			(htData.containsKey(METHOD_FLAG) && ((Boolean)htData.get(METHOD_FLAG)).booleanValue())
				? mobjMethodTree : mobjTree;
		//	ADD 2005/04/22 START Koji Umehara [メソッド管理追加]

		Vector vecReturn = new Vector();

		MessageData objMessage = null;
		// 登録処理のパラメータチェックを行います
		String strMessage = insertCheck( htData );
		if( strMessage.equals( "" ) ) {
			try {
				//mobjTree.insert( htData );
				objTree.insert( htData );
			}
			catch( XMOException xe ) {
				objMessage = xe.getMessageData();
			}
		}
		else {
			objMessage = getMessageData( strMessage );
		}

		vecReturn.add( objMessage );
		return vecReturn;
	}

	/**
	 * LoRelationテーブルへ更新処理を行います。<BR>
	 * 以下のパラメータチェックを行い、すべてOKの場合は
	 * LoRelationTreeクラスの更新処理メソッドを実行します。
	 * <PRE>
	 *             key                     説明
	 *          ---------------------------------------------
	 *             CONDITION_KEY           更新条件文
	 *             METHOD_FLAG             メソッドツリーが対象かどうか
	 *          ---------------------------------------------
	 * </PRE>
	 * @param htData 登録情報
	 * @return Vector 結果（配列の先頭にMessageDataオブジェクトを格納して返します)
	 */
	private Vector update( Hashtable htData ) {
		//	ADD 2005/04/22 START Koji Umehara [メソッド管理追加]
		// 対象となるツリーをMETHOD_FLAGにより判別する
		LoRelationTree objTree = 
			(htData.containsKey(METHOD_FLAG) && ((Boolean)htData.get(METHOD_FLAG)).booleanValue())
				? mobjMethodTree : mobjTree;
		//	ADD 2005/04/22 START Koji Umehara [メソッド管理追加]

		Vector vecReturn = new Vector();

		MessageData objMessage = null;
		//更新処理パラメータチェック
		String strMessage = updateCheck( htData );
		if( strMessage.equals( "" ) ) {
			try {
				//mobjTree.update( htData );
				objTree.update( htData );
			}
			catch( XMOException xe ) {
				objMessage = xe.getMessageData();
			}
		}
		else {
			objMessage = getMessageData( strMessage );
		}

		vecReturn.add( objMessage );
		return vecReturn;
	}

	/**
	 * LoRelationテーブルへ削除処理を行います。<BR>
	 * 以下のパラメータチェックを行い、すべてOKの場合は
	 * LoRelationTreeクラスの削除処理メソッドを実行します。
	 * <PRE>
	 *             key                     説明
	 *          ---------------------------------------------
	 *             CONDITION_KEY           更新条件文
	 *             METHOD_FLAG             メソッドツリーが対象かどうか
	 *          ---------------------------------------------
	 * </PRE>
	 * @param htData 削除情報
	 * @return Vector 結果（配列の先頭にMessageDataオブジェクトを格納して返します)
	 */
	private Vector delete( Hashtable htData ) {
		//	ADD 2005/04/22 START Koji Umehara [メソッド管理追加]
		// 対象となるツリーをMETHOD_FLAGにより判別する
		LoRelationTree objTree = 
			(htData.containsKey(METHOD_FLAG) && ((Boolean)htData.get(METHOD_FLAG)).booleanValue())
				? mobjMethodTree : mobjTree;
		//	ADD 2005/04/22 START Koji Umehara [メソッド管理追加]

		Vector vecReturn = new Vector();

		MessageData objMessage = null;
		try {
			//mobjTree.delete( htData );
			objTree.delete( htData );
		}
		catch( XMOException xe ) {
			objMessage = xe.getMessageData();
		}

		vecReturn.add( objMessage );
		return vecReturn;
	}

	/**
	 * LoRelation テーブルへ検索処理を行います。<BR>
	 * マテリアルIDおよび親IDおよび子IDが設定されている場合は
	 * GroupAndPeopleTreeクラスの各検索メソッドを実行します。<BR>
	 * もし上記の条件が設定されていない場合はDBから検索を行います。<BR>
	 * <PRE>
	 *             key                     説明
	 *          ---------------------------------------------
	 *             MEMBER_SHIP_ID          メンバーシップID
	 *             PARENT_ID               親ID
	 *             CHILD_ID                子ID
	 *          ---------------------------------------------
	 * </PRE>
	 * @param htData 検索情報
	 * @return Vector 結果（配列の先頭にMessageDataオブジェクトを格納し、２番目移行に
	 *                      GroupAndPeopleクラスを格納して返します。)
	 */
	private Vector select( Hashtable htData ) {
		Vector vecResult = new Vector();

		MessageData objMessage = null;

		// マテリアルIDの取得
		String strMaterialID = (String)htData.get( LoRelationDataAccess.MATERIAL_ID );
		// 親IDの取得
		String strSearchParentID = (String)htData.get( LoRelationDataAccess.PARENT_ID );
		// 子IDの取得
		String strSearchChildID = (String)htData.get( LoRelationDataAccess.CHILD_ID );
		// メンバーシップIDが設定されている場合はメモリ上から検索を行う
		if( strMaterialID != null ) {
			// メンバーシップIDから検索を行うメソッドを実行します
			vecResult = mobjTree.getLoRelationListFromID( strMaterialID );
			//	ADD 2005/03/25 START Koji Umehara [メソッド管理追加]
			if( vecResult.size() == 0 ) {
				vecResult = mobjMethodTree.getLoRelationListFromID( strMaterialID );
			}
			//	ADD 2005/03/25 END Koji Umehara [メソッド管理追加]
		}
		// 親IDと子IDどちらかが設定されていた場合はメモリー上から検索を行う
		else if( strSearchParentID != null || strSearchChildID != null ) {
			vecResult = mobjTree.getLoRelationList( strSearchParentID, strSearchChildID );
			//	ADD 2005/03/25 START Koji Umehara [メソッド管理追加]
			if( vecResult.size() == 0 ) {
				vecResult = mobjMethodTree.getLoRelationList( strSearchParentID, strSearchChildID );
			}
			//	ADD 2005/03/25 END Koji Umehara [メソッド管理追加]
		}
		// 親IDと子IDどちらも指定していない場合はDBから検索を行う
		else {
			try {
				vecResult = mobjDataAccess.select( htData );
			}
			catch( XMOException xe ) {
				objMessage = xe.getMessageData();
			}
		}

		// 配列の先頭にメッセージオブジェクトを格納する
		vecResult.add( 0, objMessage );
		return vecResult;
	}

	/**
	 * 指定されたIDのパスを検索します
	 * @param htData パラメータ
	 * @return Vector 検索結果
	 */
	private Vector getPath( Hashtable htData ) {
		// ハッシュから検索IDを取得する
		String strID = (String)htData.get( PARENT_ID );
		// メッセージデータオブジェクトの作成
		MessageData objMessage = null;
		// 戻り値設定
		Vector vecReturn = new Vector();
		if( strID == null ) {
			objMessage = getMessageData( PARAM_WAR );
		}
		else {
			// ツリーからパスを検索
			vecReturn = mobjTree.getPath( strID );
			//	ADD 2005/03/25 START Koji Umehara [メソッド管理追加]
			if( vecReturn.size() == 0 ) {
				vecReturn = mobjMethodTree.getPath( strID );
			}
			//	ADD 2005/03/25 END Koji Umehara [メソッド管理追加]
		}

		vecReturn.add( 0, objMessage );
		return vecReturn;
	}

	/**
	 * コピーします<BR>
	 * @param htData パラメータ
	 * @return Vector コピー結果
	 */
	private Vector copy( Hashtable htData )
	{
		// 移動元メンバーシップIDリスト
		String[] strSendRelationIDList = null;
		// 移動先メンバーシップID
		String strReceiveRelationID = null;
		// ログインユーザID
		String strLoginUserID = null;
		// 処理タイプ
		int intType = 0;
		// F2FLoタイプ
		String strF2F = "8";

		// 戻り値用ベクター
		Vector vecReturn = new Vector();

		// 共通データアクセスクラスのインスタンス化
		DataAccess objDataAccess  = new DataAccess();

		try {
			try {
				// 移動元メンバーシップIDリストを取得します
				strSendRelationIDList = (String[])htData.get(SEND_RELATIONID_LIST);
				// 移動先メンバーシップIDを取得します
				strReceiveRelationID = (String)htData.get(RECEIVE_RELATIONID);
				// 処理タイプを取得します
				intType = ((Integer)htData.get(PROCESS_TYPE)).intValue();
				// ログインユーザIDを取得します
				strLoginUserID = (String)htData.get("userId");
			}
			catch(ClassCastException ce) {
				// 内部エラー
				MessageData objMessage = getMessageData(PARAM_WAR);
				throw new XMOException(objMessage);
			}
			// パラメータチェック
			if( strSendRelationIDList == null || strReceiveRelationID == null || strLoginUserID == null ) {
				// 内部エラー
				MessageData objMessage = getMessageData(PARAM_WAR);
				throw new XMOException(objMessage);
			}
			// 処理タイプチェック
			if( !(intType == UP_TYPE || intType == IN_TYPE || intType == DOWN_TYPE) ) {
				// 内部エラー
				MessageData objMessage = getMessageData(PARAM_WAR);
				throw new XMOException(objMessage);
			}
			
			// ADD 2005/04/25 START Koji Umehara [メソッド管理追加]
			// メソッドフラグを取得します
			boolean blnMethodFlag = htData.containsKey(METHOD_FLAG) ? ((Boolean)htData.get(METHOD_FLAG)).booleanValue() : false;
			// ADD 2005/04/25 END Koji Umehara [メソッド管理追加]

			// トランザクションの開始
			objDataAccess.trans(htData);

			// 複製処理を行います。
			//XMONode[] objCopiedNodeList = copyAction( strSendRelationIDList, strReceiveRelationID, intType, strLoginUserID, objDataAccess );
			XMONode[] objCopiedNodeList = copyAction( strSendRelationIDList, strReceiveRelationID, intType, strLoginUserID, objDataAccess, blnMethodFlag );

			// 戻り値用Vectorにコピーされたノードリストを設定します。
			vecReturn.add( objCopiedNodeList );

			// コミットを行います。
			objDataAccess.commit();

			// コピーで作成されたノードオブジェクトにF2Fが存在した場合はF2Fに対して、初期ログの書き込み処理を行う。

			// コピーノードからF2Fのノードをリストとして取得する
			XMONode[] objNodeList = convertNodesToList( objCopiedNodeList, strF2F );

			// F2Fのノードが存在した場合のみ以下の処理を行う。
			if( objNodeList.length > 0 ) {
				//複製先IDリストを親IDと子IDに分割する。
				//複製先のstrReceiveRelationIDの”＾”を削除する。
				LoRelationData objReceiveData = makeLoRelationData( strReceiveRelationID );
				String strParentID = null;
				if( intType == IN_TYPE ) {
					strParentID = objReceiveData.getChildID();
				}
				else {
					strParentID = objReceiveData.getParentID();
				}

				// 複製先先にアサインされているユーザのリストを取得します。
				// 移動先LOのアサインユーザリストを取得する。
				AssignmentsClient objAssignments = new AssignmentsClient();
				Vector vecUserList = new Vector();
				if(!objAssignments.getUserList(strParentID)) {
					// エラーの場合
					throw new XMOException( objAssignments.getMessageData() );
				}
				else {
					vecUserList = objAssignments.getUserListResult();
					if(vecUserList != null){
						htData.put("UserList", vecUserList);
					}
				}

				if( vecUserList.size() > 0 ) {
					for( int i = 0; i < objNodeList.length; i++ ) {
						XMONode objNode = objNodeList[i];

						// LOID → RealLOID の変換を行う
						LoDataAccess objLoDataAccess = new LoDataAccess();
						String strRealLoID = objLoDataAccess.getRealLOID(objNode.getParentID());

						// 登録項目設定
						Hashtable htbData = new Hashtable();
						htData.put( "LOID", strRealLoID );
						htData.put( "UserList", vecUserList );
						// LOサーバ名の取得
						Environment objEnv = new Environment();
						String strLoServerName = objEnv.getLoServerName();
						// LOクラスをインスタンス化します
						LoTopClient objLoClient = new LoTopClient( strLoServerName, strF2F );
						// 登録処理
						if( !objLoClient.setUp( LoTopClient.SETUP_INSERT, htData ) ) {
							throw new XMOException( objLoClient.getMessageData() );
						}
					}
				}
			}


		}
		catch( SQLException se ) {
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// エラー処理
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			// データベースエラーを設定
			vecReturn = new Vector();
			vecReturn.add( getMessageData( DB_ERR ) );
			return vecReturn;
		}
		catch( XMOException xe ) {
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// エラー処理
			DebugLog.write(this.getClass(), xe, DebugLog.HIGHT);
			vecReturn = new Vector();
			vecReturn.add( xe.getMessageData() );
			return vecReturn;
		}
		catch( Exception exc ) {
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// エラー処理
			DebugLog.write(this.getClass(), exc, DebugLog.HIGHT);
			vecReturn = new Vector();
			vecReturn.add( exc.getMessage() );
			return vecReturn;
		}

		// 正常に終了下ので配列の１番目にnullを格納して返す。
		vecReturn.add(0, null);
		return vecReturn;
	}

	/**
	 * 複製処理を行います。
	 * @param strSendRelationIDList 複製元リレーションIDリスト
	 * @param strReceiveRelationID 複製先リレーションIDリスト
	 * @param intType 処理区分
	 * @param objDataAccess データアクセスクラス
	 * @param blnMethodFlag メソッドフラグ
	 * @return XMONode コピー後に作成されたノードオブジェクトの配列
	 * @exception XMOException XMO独自例外
	 */
	private XMONode[] copyAction(	String[] strSendRelationIDList,
									String strReceiveRelationID,
									int intType,
									String strLoginUserID,
									DataAccess objDataAccess,
									// ADD 2005/04/25 START Koji Umehara [メソッド管理追加]
									boolean blnMethodFlag )
									// ADD 2005/04/25 END Koji Umehara [メソッド管理追加]
		throws XMOException
	{
		// ADD 2005/04/25 START Koji Umehara [メソッド管理追加]
		// 対象とするツリーを切り替え
		LoRelationTree objTree = blnMethodFlag ? mobjMethodTree : mobjTree;
		// ADD 2005/04/25 END Koji Umehara [メソッド管理追加]
		
		MessageData objMessage = null;

		//移動元IDリストをforループで回りながら、親IDと子IDに分割する。
		//移動元のstrSendRelationIDListの”＾”を削除する。
		LoRelationData[] objSendDataList = new LoRelationData[strSendRelationIDList.length];

		// コピーにより作成されるノードオブジェクトを格納する配列
		XMONode[] objCopiedNodeList = new XMONode[strSendRelationIDList.length];

		for( int i = 0; i < strSendRelationIDList.length; i++ ) {
			objSendDataList[i] = makeLoRelationData( strSendRelationIDList[i] );

		}
		//複製先IDリストを親IDと子IDに分割する。
		//複製先のstrReceiveRelationIDの”＾”を削除する。
		LoRelationData objReceiveData = makeLoRelationData( strReceiveRelationID );


		// LoRelationテーブルアクセスクラスのインスタンス化
		LoRelationDataAccess objLoRelationDataAccess = new LoRelationDataAccess();

		try {

			// 処理前のDB更新チェック処理を行う。データベースが更新されていた場合はエラーを投げる
			if( !copyCheck( objSendDataList, objReceiveData, intType, objDataAccess ) ) {
				// 更新済み警告をスローします。
				throw new XMOException( getMessageData( DB_UPDATE_WAR ) );
			}

			int intReceiveSortNo = 0;
			int intReceiveMaxSortNo = 0;
			int intSortNo = 0;
			//上へ or 下への時
			if (intType == 1 || intType == 3) {
				// 複製先のsort_noを取得する。
				intReceiveSortNo = objLoRelationDataAccess.getMaterialIDSortNo(objReceiveData.getMaterialID(), objDataAccess);

				// コピー先の親Nodeを検索して保持しておく
				//XMONode objXMORecieveNode = mobjTree.searchNode( objReceiveData.getParentID() );
				XMONode objXMORecieveNode = objTree.searchNode( objReceiveData.getParentID() );

				// コピーしたノードを追加するメモリ上の配列の位置を保持しておく
				int intMemoryArrayIndex = objXMORecieveNode.getChildNodeIndex( objReceiveData.getChildID() );
				// 下への場合は位置にプラス1する
				if( intType == 3 ) {
					intMemoryArrayIndex++;
				}


				//複製元IDリスト配列数
				int intSendListLength = strSendRelationIDList.length;
				//updateReciveSort_noメソッド内で上への時、下への時のUPDATE文の判定を行っている。

				objLoRelationDataAccess.updateSortNo(intReceiveSortNo,
																intSendListLength,
																objReceiveData.getParentID(),
																intType,
																objDataAccess);

				for( int i = 0; i < strSendRelationIDList.length; i++ ){
					//上への時：複製元のsort_noに対して、複製先のsort_noに1++した値を割り振る為メソッドに渡す前に1++する。
					if (intType == 1){
						intSortNo = intReceiveSortNo++;
					}
					//下への時：複製元のsort_noに対して、複製先のsort_noに++1した値を割り振る為メソッドに渡す前に++1する。
					if (intType == 3){
						intSortNo = ++intReceiveSortNo;
					}

					try
					{
						// 同一グループを同一階層にコピーするかチェック
						//XMONode objNode = mobjTree.searchNode(objSendDataList[i].getChildID());
						XMONode objNode = objTree.searchNode(objSendDataList[i].getChildID());
						// ノードの複製を作成する
						objNode = objNode.copySelf();

						// 前提条件書き換えに必要な情報を格納するVector
						Vector vecPrere = new Vector();
						Vector vecBeforeID = new Vector();
						Vector vecAfterID = new Vector();
						objCopiedNodeList[i] = reflexiveCopyAction( objNode, objDataAccess, true, objReceiveData.getParentID(), intSortNo, strLoginUserID , vecPrere, vecBeforeID, vecAfterID );
						// 格納された前提条件情報を元に前提条件の書き換えを行う。
						changePrere(vecPrere, vecBeforeID, vecAfterID, objDataAccess);

						// コピー処理で生成されたXMONodeをメモリ上のツリーへ登録する
						objXMORecieveNode.addChild( intMemoryArrayIndex + i, objCopiedNodeList[i] );

					}
					catch(SQLException se)
					{
						DebugLog.write(this.getClass(), se, DebugLog.HIGHT);
						try { objDataAccess.rollback(); } catch( Exception ex ) {}
						//synchronized(mobjTree)
						synchronized(objTree)
						{
							// ツリーのリロード
							//try { mobjTree.load(); } catch( XMOException txe ) {}
							try { objTree.load(); } catch( XMOException txe ) {}
						}
						objMessage = getMessageData( DB_ERR );
					}
					catch(XMOException xe)
					{
						DebugLog.write(this.getClass(), xe, DebugLog.HIGHT);
						try { objDataAccess.rollback(); } catch( Exception ex ) {}
						//synchronized(mobjTree)
						synchronized(objTree)
						{
							// ツリーのリロード
							//try { mobjTree.load(); } catch( XMOException txe ) {}
							try { objTree.load(); } catch( XMOException txe ) {}
						}
						objMessage = xe.getMessageData();
					}
					catch(Exception e)
					{
						DebugLog.write(this.getClass(), e, DebugLog.HIGHT);
						try { objDataAccess.rollback(); } catch( Exception ex ) {}
						//synchronized(mobjTree)
						synchronized(objTree)
						{
							// ツリーのリロード
							//try { mobjTree.load(); } catch( XMOException txe ) {}
							try { objTree.load(); } catch( XMOException txe ) {}
						}
						objMessage = getMessageData( SYSTEM_ERR );
					}
				}
			}
			//中への時
			else{
				// 複製先の子の配下のsort_noの最大値を取得する。
				intReceiveMaxSortNo = objLoRelationDataAccess.getMaxSortNo(objReceiveData.getChildID(), objDataAccess);

				// コピー先の親Nodeを検索して保持しておく
				//XMONode objXMORecieveNode = mobjTree.searchNode( objReceiveData.getChildID() );
				XMONode objXMORecieveNode = objTree.searchNode( objReceiveData.getChildID() );

				//複製元のsort_noに対して、複製先の子の配下のsort_noの最大値に++1したsort_noを割り振る。
				for( int i = 0; i < strSendRelationIDList.length; i++ ){
					intSortNo = ++intReceiveMaxSortNo;

					try
					{
						// 同一グループを同一階層にコピーするかチェック
						//XMONode objNode = mobjTree.searchNode(objSendDataList[i].getChildID());
						XMONode objNode = objTree.searchNode(objSendDataList[i].getChildID());
						// ノードの複製を作成する
						objNode = objNode.copySelf();

						// 前提条件書き換えに必要な情報を格納するVector
						Vector vecPrere = new Vector();
						Vector vecBeforeID = new Vector();
						Vector vecAfterID = new Vector();
						objCopiedNodeList[i] = reflexiveCopyAction( objNode, objDataAccess, true, objReceiveData.getChildID(), intSortNo, strLoginUserID , vecPrere, vecBeforeID, vecAfterID );
						// 格納された前提条件情報を元に前提条件の書き換えを行う。
						changePrere(vecPrere, vecBeforeID, vecAfterID, objDataAccess);

						// コピー処理で生成されたXMONodeをメモリ上のツリーへ登録する(中への時は一番最後の位置に追加する。)
						objXMORecieveNode.addChild( objCopiedNodeList[i] );

					}
					catch(SQLException se)
					{
						DebugLog.write(this.getClass(), se, DebugLog.HIGHT);
						try { objDataAccess.rollback(); } catch( Exception ex ) {}
						//synchronized(mobjTree)
						synchronized(objTree)
						{
							// ツリーのリロード
							//try { mobjTree.load(); } catch( XMOException txe ) {}
							try { objTree.load(); } catch( XMOException txe ) {}
						}
						objMessage = getMessageData( DB_ERR );
					}
					catch(XMOException xe)
					{
						DebugLog.write(this.getClass(), xe, DebugLog.HIGHT);
						try { objDataAccess.rollback(); } catch( Exception ex ) {}
						//synchronized(mobjTree)
						synchronized(objTree)
						{
							// ツリーのリロード
							//try { mobjTree.load(); } catch( XMOException txe ) {}
							try { objTree.load(); } catch( XMOException txe ) {}
						}
						objMessage = xe.getMessageData();
					}
					catch(Exception e)
					{
						DebugLog.write(this.getClass(), e, DebugLog.HIGHT);
						try { objDataAccess.rollback(); } catch( Exception ex ) {}
						//synchronized(mobjTree)
						synchronized(objTree)
						{
							// ツリーのリロード
							//try { mobjTree.load(); } catch( XMOException txe ) {}
							try { objTree.load(); } catch( XMOException txe ) {}
						}
						objMessage = getMessageData( SYSTEM_ERR );
					}
				}
			}

			// 複製(先)処理に関連したLOに対して、ステータス伝搬を実行します
			String strPropagateLOID = null;
			if (intType == 1 || intType == 3){
				strPropagateLOID = objReceiveData.getParentID();
			}
			else{
				strPropagateLOID = objReceiveData.getChildID();

			}
			StatusPropagation objPropagation = new StatusPropagation( strPropagateLOID );
			PropagateControl.getInstance().addPropagate(objPropagation);


		}
		catch( SQLException se ) {
			// エラー処理
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			// データベースエラーを設定
			throw new XMOException( getMessageData( DB_ERR ) );
		}

		// コピー処理で作成されたノードオブジェクトを返す
		return objCopiedNodeList;
	}

	/**
	 * コピー再帰呼出し<BR>
	 * @param objNode ツリー構造
	 * @param objDataAccess トランザクション
	 * @param boolFlag LOタイトルコピー作成フラグ
	 * @param strCopyDescID コピー先ID
	 * @param intSortNo ソート番号
	 * @return XMONode コピーツリー
	 */
	private XMONode reflexiveCopyAction(XMONode objNode,
							DataAccess objDataAccess,
							boolean boolFlag,
							String strCopyDescID,
							int intSortNO,
							String strLoginUserID,
							Vector vecPrere,
							Vector vecBeforeID,
							Vector vecAfterID )
			throws XMOException, Exception
	{
		// XMONodeオブジェクトからID取得
		String strID = objNode.getParentID();

		// 詳細データ取得
		LoClient objLo = new LoClient();
		// コネクションの共有
		objLo.setConnectionIndex( objDataAccess.getConnectionIndex() );
		objLo.setWhereLoID(strID);
		if(!objLo.select())
		{
			throw new XMOException(objLo.getMessageData());
		}

		String strLoTitle = "";
		String strLoType = "";
		String strLoexplanation = "";
		String strForeignID = "";
		String strKeyWord = "";
		String strDeleteFlag = "";
		String strVersion = "";
		String strConclusion = "";
		String strRealLOID = "";
		String strPrerequisite = "";

		if(objLo.next())
		{
			strLoTitle = objLo.getLoTytle();
			strLoType = objLo.getLoType();
			strLoexplanation = objLo.getLoExplanation();
			strForeignID = objLo.getForeignID();
			strKeyWord = objLo.getKeyWord();
			strDeleteFlag = objLo.getDeleteFlag();
			strVersion = objLo.getLoVersion();
			strConclusion = objLo.getConclusion();
			strRealLOID = objLo.getRealLOID();
			strPrerequisite = objLo.getPrere();
		}

		objLo.clear();

		// LOタイトル編集処理
		if(boolFlag) {
			strLoTitle = editLoTitle( strLoTitle, strCopyDescID, objDataAccess );
		}
		// 外部ID編集処理
		strForeignID = editForeignID(strForeignID, objDataAccess);

		// コネクションの共有
		objLo.setConnectionIndex( objDataAccess.getConnectionIndex() );
		objLo.setEncode("Windows-31J");									// エンコード設定
		// 登録処理
		objLo.setParentID(strCopyDescID);
		objLo.setLoTytle(strLoTitle);
		objLo.setLoType(strLoType);
		objLo.setLoExplanation(strLoexplanation);
		objLo.setForeignID(strForeignID);
		objLo.setKeyWord(strKeyWord);
		objLo.setDeleteFlag(strDeleteFlag);
		objLo.setLoVersion(strVersion);
		objLo.setConclusion(strConclusion);
		objLo.setLoginID(strLoginUserID);
		//コピー元LOIDをセットする
		objLo.setCopyLOID(strRealLOID);

		// コンテナ又はカタログ以外のLOコピーの時は、RealLOIDを設定する。
		if(!strLoType.equals("0") && !strLoType.equals("6")){
			objLo.setRealLOID(strRealLOID);
		}

		// コピー処理なので、コピーフラグを設定する。
		objLo.setCopyFlag();

		if(!objLo.insert())
		{
			// 登録に失敗した場合
			throw new XMOException(objLo.getMessageData());
		}
		// 登録LOIDを取得する
		String strCopyLoID = "";
		if(objLo.next())
		{
			// サーバ側登録後のLOIDを取得する
			strCopyLoID = objLo.getLoID();
		}

		// 前提条件の書き換えに必要な情報をストックしておく。
		vecPrere.add(strPrerequisite);
		vecBeforeID.add(strID);
		vecAfterID.add(strCopyLoID);

		// コピーにより新たに作成されたLOIDでノードオブジェクトを生成します。
		XMONode objCopiedNode = new XMONode();
		objCopiedNode.setParentID( strCopyLoID );
		objCopiedNode.setType( strLoType );			// タイプを設定

		// リレーションの作成
		// LoRelation登録処理
		// コピー元データを生成します.(子IDのみ使用する)
		LoRelationData objSendData = new LoRelationData();
		objSendData.setChildID( strCopyLoID );

		// LoRelationテーブルアクセスクラスのインスタンス化
		LoRelationDataAccess objLoRelationDataAccess = new LoRelationDataAccess();
		objLoRelationDataAccess.insertLoRelation(objSendData, strCopyDescID, intSortNO, objDataAccess);




		// 子ノード分再帰処理を繰り返す。
		for( int i = 0; i < objNode.getChildCount(); i++ )
		{
			XMONode objChildNode = objNode.getChildNode(i);
			// 前提条件配列、コピー前LOID配列、コピー後LOID配列をクリアします。
			XMONode objChildCopiedNode = reflexiveCopyAction( objChildNode, objDataAccess, false, strCopyLoID, i + 1, strLoginUserID , vecPrere, vecBeforeID, vecAfterID );
			objCopiedNode.addChild( objChildCopiedNode );

			// もともとのLOID
			String strOldChildLoID = objChildNode.getParentID();

			// 新たなLOID
			String strNewChildLoID = objChildCopiedNode.getParentID();

			// 完結要件の strOldChildLoID を strNewChildLoID に変える処理
			if(strConclusion != null && !strConclusion.equals("")){
				// 完結要件文字列のLOID置換処理
				int intFromIndex = 0;
				while(strConclusion.indexOf(strOldChildLoID, intFromIndex) >= 0){
					StringBuffer sbConclusion  = new StringBuffer();
					int intIndex = strConclusion.indexOf(strOldChildLoID, intFromIndex);
					String strCheck = strConclusion.substring(intIndex + strOldChildLoID.length(), intIndex + strOldChildLoID.length() + 1);
					if(strCheck.equals("=") || strCheck.equals("!") || strCheck.equals(" ")){
						sbConclusion.append(strConclusion.substring(0, intIndex));
						sbConclusion.append(strNewChildLoID);
						sbConclusion.append(strConclusion.substring(intIndex + strOldChildLoID.length(), strConclusion.length()));
						strConclusion = sbConclusion.toString();
						intFromIndex = intIndex + strNewChildLoID.length();
					}else{
						intFromIndex = intIndex + strOldChildLoID.length();
					}
				}
			}

		}

		// 新しい完結要件を更新する
		//-------------------------------
		// LOタイプがコンテナの場合の処理
		//-------------------------------
		if(strLoType.equals( CONTAINER_TYPE )){
			if(strConclusion != null || !strConclusion.equals("")){
				// 完結要件更新処理
				if(!editConclusion(strCopyLoID, strConclusion, objDataAccess)){
					throw new XMOException(SYSTEM_ERR);
				} else {
					// LOテーブルへの更新処理が正常に行えた場合
					// PropagateRelationテーブルへ完結要件を登録
					PropagateUtil.editPropagate(strCopyLoID, strConclusion, objDataAccess);
				}
			}
			//ステータス伝搬の代理処理
			//コピー元のLoLogをコピーし、LOIDのみ変更したLoLogをインサートする。
			//（これによりステータス伝搬を走らせる必要はなくなる。）
			LoLogDataAccess objLoLogDataAccess = new LoLogDataAccess();
			LoLogRecord[] objArrLoLogRecord = objLoLogDataAccess.getCopyLoLog( objNode.getParentID(), strCopyLoID, objDataAccess );
			objLoLogDataAccess.insert( objArrLoLogRecord, objDataAccess );
		}

		return objCopiedNode;
	}

	/**
	 * LOタイトルリストを取得します<BR>
	 * @param String コピー元の親ID
	 * @return  Vector LOタイトルリスト
	 */
	private Vector getLoTitleList( String strParentID, DataAccess objDataAccess )
		throws XMOException, Exception
	{
		Vector vecList = new Vector();

		LoClient objLo = new LoClient();
		// コネクション共有
		objLo.setConnectionIndex( objDataAccess.getConnectionIndex() );

		StringBuffer sbCondition = new StringBuffer();
		sbCondition.append( LoClient.LO_ID );
		sbCondition.append( " in (SELECT " );
		sbCondition.append( CHILD_ID );
		sbCondition.append( " FROM " );
		sbCondition.append( TABLE_NAME );
		sbCondition.append( " WHERE " );
		sbCondition.append( PARENT_ID );
		sbCondition.append( "='" );
		sbCondition.append( StringEscapeUtils.escapeSql(strParentID) );
		sbCondition.append( "')" );

		// 検索条件の設定
		objLo.setCondition( sbCondition.toString() );
		// 検索処理
		if( !objLo.select() ) {
			throw new XMOException( objLo.getMessageData() );
		}

		// 検索結果をVectorに追加する
		while( objLo.next() ) {
			vecList.add( objLo.getLoTytle() );
		}

		return vecList;
	}

	/**
	 * 完結要件を更新します。<BR>
	 * @param strLOID         完結要件を更新するLOID
	 * @param  strConclusion 更新完結要件
	 */
	private void updateConclusion( String strLOID, String strConclusion, DataAccess objDataAccess )
		throws SQLException
	{
		StringBuffer sbSQL = new StringBuffer();
		sbSQL.append( "UPDATE LO SET " );
		sbSQL.append( "conclusion = '" );
		sbSQL.append( strConclusion );
		sbSQL.append( "' WHERE LOID" );
		sbSQL.append( " = '" );
		sbSQL.append( StringEscapeUtils.escapeSql(strLOID) );
		sbSQL.append( "'" );

		// SQLの実行を行います
		objDataAccess.execute( sbSQL.toString() );

	}

	/**
	 * 前提条件を更新します。<BR>
	 * @param strLOID         前提条件を更新するLOID
	 * @param  strConclusion 更新前提条件
	 */
	private void updatePrerequisite( String strLOID, String strPrere, DataAccess objDataAccess )
		throws SQLException
	{
		StringBuffer sbSQL = new StringBuffer();
		sbSQL.append( "UPDATE LO SET " );
		sbSQL.append( "Prere = '" );
		sbSQL.append( strPrere );
		sbSQL.append( "' WHERE LOID" );
		sbSQL.append( " = '" );
		sbSQL.append( StringEscapeUtils.escapeSql(strLOID) );
		sbSQL.append( "'" );
		// SQLの実行を行います
		objDataAccess.execute( sbSQL.toString() );

	}

	/**
	 * LOタイトルを編集します<BR>
	 * @param strLoTitle LOタイトル(編集前)
	 * @return  String LOタイトル(編集後)
	 */
	private String editLoTitle(String strLoTitle, String strParentID, DataAccess objDataAccess)
		throws Exception, XMOException
	{
		// LOタイトルのリストを取得します
		Vector vecLoTitleList = getLoTitleList( strParentID, objDataAccess );

		int intCount = 0;
		String strTitleTemp = strLoTitle;	// LOタイトル(編集後)
		while( true ) {
			String strNumber = "";
			// 同ディレクトリに同タイトルのLOが存在する場合
			if( vecLoTitleList.contains( strTitleTemp ) ) {
				intCount++;
				strNumber = "(" + intCount + ")";
				strTitleTemp = makeDecimalStringForTitle( strLoTitle, strNumber, LoTable.LOTYTLE_LENB );
				continue;
			}
			// 同ディレクトリに同タイトルのLOが存在しない場合
			else {
				break;
			}
		}

		return strTitleTemp;
	}

	/**
	 * 外部IDを編集します<BR>
	 * NULLで戻った場合エラーとします
	 * @param String 外部ID(編集前)
	 * @return  String 外部ID(編集後)
	 */
	private String editForeignID(String strForeignID, DataAccess objDataAccess)
		throws Exception, XMOException
	{
		LoClient objLo = new LoClient();

		int intCount = 1;
		while( true ) {
			String strNumber = "_" + intCount;

			String strForeignTemp = makeDecimalStringForForeignID( strForeignID, strNumber, LoTable.FOREIGNID_LENB );
			objLo.clear();
			// エンコード設定
			objLo.setEncode("Windows-31J");
			// コネクションの共有
			objLo.setConnectionIndex( objDataAccess.getConnectionIndex() );
			// 外部IDを検索条件に設定
			objLo.setWhereForeignID( strForeignTemp );
			// 検索処理
			if( !objLo.select() ) {
				throw new XMOException( objLo.getMessageData() );
			}

			if( objLo.next() ) {
				// レコードがあった場合
				intCount++;
				continue;
			}
			else {
				// レコードがない場合はこれを返す。
				strForeignID = strForeignTemp;
				break;
			}
		}
		return strForeignID;
	}

	/**
	 * 完結要件を編集します<BR>
	 * @param String 完結要件(編集前)
	 * @param String LOID(複製元)
	 * @return  String 完結要件(編集後)
	 */
	private boolean editConclusion(String strLOID, String strConclusion, DataAccess objDataAccess)
		throws Exception
	{
		boolean boolRet = true;
		// LOテーブル完結要件更新処理
		try{
			updateConclusion(strLOID, strConclusion, objDataAccess);
		}
		catch(SQLException se){
			boolRet = false;
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// データベースエラーを設定
			throw new XMOException( getMessageData( DB_ERR ) );
		}
		return boolRet;
	}

	/**
	 * 前提条件を編集します<BR>
	 * @param String 前提条件(編集前)
	 * @param String LOID(複製先)
	 * @return  String 前提条件(編集後)
	 */
	private boolean editPrerequisite(String strLOID, String strPrere, DataAccess objDataAccess)
		throws Exception
	{
		boolean boolRet = true;
		// LOテーブル前提条件更新処理
		try{
			updatePrerequisite(strLOID, strPrere, objDataAccess);

			// prerequisiteテーブル登録処理
			Hashtable htData = new Hashtable();
			if(strLOID == null || strLOID.equals("") || strPrere == null || strPrere.equals("")){
				throw new XMOException(getMessageData(PARAM_WAR));
			}
			htData.put(LO_ID, strLOID);
			htData.put(PRERE, strPrere);
			mobjDataAccess.insertPrerequisiteAction( htData, objDataAccess );
		}
		catch(SQLException se){
			boolRet = false;
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// データベースエラーを設定
			throw new XMOException( getMessageData( DB_ERR ) );
		}
		return boolRet;
	}

	/**
	 * コピー用の名称作成メソッド
	 */
	private String makeDecimalStringForTitle( String strName, String strNumber, int intLen ) {
		if( strName == null || strNumber == null || intLen < 0 ) {
			return "";
		}
		byte[] nameByte = strName.getBytes();
		byte[] numByte = strNumber.getBytes();
		byte[] pre1Byte = PREFIX_LO_TYTLE1.getBytes();
		byte[] pre2Byte = PREFIX_LO_TYTLE2.getBytes();
		while( nameByte.length + numByte.length + pre1Byte.length + pre2Byte.length > intLen ) {
			strName = strName.substring( 0, strName.length() - 1 );
			nameByte = strName.getBytes();
		}

		return PREFIX_LO_TYTLE1 + strNumber + PREFIX_LO_TYTLE2 + strName;
	}

	/**
	 * コピー用の外部ID作成メソッド
	 */
	private String makeDecimalStringForForeignID( String strForeignID, String strNumber, int intLen ) {
		if( strForeignID == null || strNumber == null || intLen < 0 ) {
			return "";
		}
		byte[] nameByte = strForeignID.getBytes();
		byte[] numByte = strNumber.getBytes();
		while( nameByte.length + numByte.length > intLen ) {
			strForeignID = strForeignID.substring( 0, strForeignID.length() - 1 );
			nameByte = strForeignID.getBytes();
		}

		return strForeignID + strNumber;
	}

	/**
	 * メッセージオブジェクトを作成します
	 * @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;
	}

	/**
	 * 登録処理のパラメータチェックを行います<BR>
	 * 以下のパラメータのチェックを行います。
	 * <PRE>
	 *             key                     説明
	 *          ---------------------------------------------
	 *             MATERIAL_ID             コンポーネントID
	 *             PARENT_ID               親ID
	 *             CHILD_ID                子ID
	 *          ---------------------------------------------
	 * </PRE>
	 * @param htbParam パラメータ
	 * @return String エラー時はエラー番号 OKのときは空文字を返します
	 */
	private String insertCheck( Hashtable htbParam ) {

		String strMessage = "";
		String strCheck = "";

		// コンポーネントIDのチェック(必須チェックなし)
		strCheck = (String)htbParam.get( MATERIAL_ID );
		strMessage = LoRelationCheck.checkMaterialID( strCheck, false );
		if( !strMessage.equals( "" ) ) {
			return strMessage;
		}

		// 親IDのチェック(必須チェックあり)
		strCheck = (String)htbParam.get( PARENT_ID );
		strMessage = LoRelationCheck.checkParentID( strCheck, true );
		if( !strMessage.equals( "" ) ) {
			return strMessage;
		}

		// 子IDのチェック(必須チェックあり)
		strCheck = (String)htbParam.get( CHILD_ID );
		strMessage = LoRelationCheck.checkChildID( strCheck, true );
		if( !strMessage.equals( "" ) ) {
			return strMessage;
		}

		// 正常時は空文字を返す
		return "";
	}

	/**
	 * 更新処理のパラメータチェックを行います<BR>
	 * 以下のパラメータのチェックを行います。
	 * <PRE>
	 *             key                     説明
	 *          ---------------------------------------------
	 *             MATERIAL_ID             コンポーネントID
	 *             PARENT_ID               親ID
	 *             CHILD_ID                子ID
	 *          ---------------------------------------------
	 * </PRE>
	 * @param htbParam パラメータ
	 * @return String エラー時はエラー番号 OKのときは空文字を返します
	 */
	private String updateCheck( Hashtable htbParam ) {

		String strMessage = "";
		String strCheck = "";

		// 人々IDのチェック
		strCheck = (String)htbParam.get( MATERIAL_ID );
		strMessage = LoRelationCheck.checkMaterialID( strCheck, false );
		if( !strMessage.equals( "" ) ) {
			return strMessage;
		}

		// 親IDのチェック(必須入力チェックあり)
		strCheck = (String)htbParam.get( PARENT_ID );
		strMessage = LoRelationCheck.checkParentID( strCheck, false );
		if( !strMessage.equals( "" ) ) {
			return strMessage;
		}

		// 子IDのチェック(必須入力チェックあり)
		strCheck = (String)htbParam.get( CHILD_ID );
		strMessage = LoRelationCheck.checkChildID( strCheck, true );
		if( !strMessage.equals( "" ) ) {
			return strMessage;
		}

		// 正常時は空文字を返す
		return "";
	}

	/**
	 * LOの管理アサイン、学習アサイン状況を調べます。<BR>
	 * 管理アサイン、学習アサインされている場合はパラメータで渡したHastable内に以下のように情報を格納します。
	 * <PRE>
	 *              KEY      | 管理アサイン | 学習アサイン |
	 *       MANAGE_ASSIGN   |      ○      |              |
	 *       LEARNER_ASSIGN  |              |      ○      |
	 * </PRE>
	 * @param htbParam 情報格納用のHashtable
	 */
	private void checkAssign( Hashtable htbParam )
		throws XMOException
	{
		try {
			// ユーザIDの取得
			String strUserID = (String)htbParam.get( LoPropertyDef.USER );
			// LOIDの取得
			String strLoID = (String)htbParam.get( LoPropertyDef.LOID );

			if( strUserID == null || strLoID == null ) {
				// ユーザIDとLOIDのどちらかが設定されていない場合は処理は行わない
				return;
			}

			if( strUserID.equals( XMOPropertyDef.ROOT_USER ) ) {
				// ルートユーザの場合は管理アサイン、学習アサイン両方を設定する
				htbParam.put( LoPropertyDef.MANAGE_ASSIGN, "1" );
				htbParam.put( LoPropertyDef.LEARNER_ASSIGN, "1" );
				return;
			}

			// 指定したLoIDのアサイン状況を再帰的に調べます。
			checkAssignRef( htbParam, strLoID );

		}
		catch( XMOException le ) {
			throw le;
		}
		catch( Exception e ) {
			throw new XMOException( SYSTEM_ERR );
		}
	}

	/**
	 * 再帰呼び出し用のメソッド<BR>
	 * ユーザIDとLOIDを受け取りアサイン情報を取得します。
	 * @param htbParam 情報格納用のHashtable
	 * @param strLoID 検索対象のLOID
	 */
	private void checkAssignRef( Hashtable htbParam, String strLoID )
		throws XMOException, Exception
	{
		// 管理アサイン、学習アサイン情報を取り出す。
		String strManageAssign = (String)htbParam.get( LoPropertyDef.MANAGE_ASSIGN );
		String strLearnerAssign = (String)htbParam.get( LoPropertyDef.LEARNER_ASSIGN );
		// すでに管理アサイン、学習アサイン情報が設定されていた場合は処理を行わない
		if( strManageAssign != null &&
			strLearnerAssign != null ) {
			return;
		}

		// ユーザIDの取得
		String strUserID = (String)htbParam.get( LoPropertyDef.USER );

		// ユーザIDからRelationIDを取得する
		StringBuffer sbSubQuery = new StringBuffer();
		sbSubQuery.append( "(SELECT " );
		sbSubQuery.append( GroupAndPeopleTable.MEMBER_SHIP_ID );
		sbSubQuery.append( " FROM " );
		sbSubQuery.append( GroupAndPeopleTable.TABLE_NAME );
		sbSubQuery.append( " WHERE " );
		sbSubQuery.append( GroupAndPeopleTable.CHILD_ID );
		sbSubQuery.append( "='" + StringEscapeUtils.escapeSql(strUserID) +"') " );

		// LOIDを子IDに設定して、親IDを取得する
		LoRelationClient objLoRelation = new LoRelationClient();
		objLoRelation.setChildID( strLoID );
		// 検索処理実行
		if( !objLoRelation.select() ) {
			// 検索処理に失敗した場合
			throw new XMOException( objLoRelation.getMessage() );
		}

		// 検索結果の取得
		while( objLoRelation.next() ) {
			String strParentID = objLoRelation.getParentID();

			String strMaterialID = strParentID + strLoID;

			// Assignmentsの検索処理
			AssignmentsClient objAss = new AssignmentsClient();
			// 検索条件文を作成する
			String strCondition = objAss.MATERIAL_ID + "='" + StringEscapeUtils.escapeSql(strMaterialID) + "' AND " + objAss.MEMBER_SHIP_ID + " in " + sbSubQuery.toString();
			// 検索条件を設定する
			objAss.setCondition( strCondition );
			// 検索処理実行
			if( !objAss.select() ) {
				// 検索処理に失敗した場合
				throw new XMOException( objAss.getMessage() );
			}

			// 検索結果の取得
			while( objAss.next() ) {
				// 管理アサインの取得
				strManageAssign = objAss.getManagementAuthority();
				if( strManageAssign.equals( "1" ) ) {
					// ハッシュテーブルに設定する
					htbParam.put( LoPropertyDef.MANAGE_ASSIGN, strManageAssign );
				}
				// 学習アサインの取得
				strLearnerAssign = objAss.getLearnAuthority();
				if( strLearnerAssign.equals( "1" ) ) {
					// ハッシュテーブルに設定する
					htbParam.put( LoPropertyDef.LEARNER_ASSIGN, strLearnerAssign );
				}
			}

			// 親LOIDで上記の処理を再帰的に繰り返す。
			checkAssignRef( htbParam, strParentID );
		}
	}

	/**
	 * LOツリーオブジェクトを取得します
	 * @return LoRelationTree
	 */
	public static LoRelationTree getTree() {
		return mobjTree;
	}
	
	// ADD 2005/04/22 START Koji Umehara [メソッド管理追加]
	/**
	 * Methodツリーオブジェクトを取得します
	 * @return LoRelationTree
	 */
	public static LoRelationTree getTree( Hashtable htData ) {
		// 対象となるツリーをMETHOD_FLAGにより判別する
		LoRelationTree objTree = 
			(htData.containsKey(METHOD_FLAG) && ((Boolean)htData.get(METHOD_FLAG)).booleanValue())
				? mobjMethodTree : mobjTree;
		return  objTree;
	}
	// ADD 2005/04/22 END Koji Umehara [メソッド管理追加]

	/**
	 * 移動処理を行います
	 * @param htData パラメータを格納したHashtable
	 * @return Vector 結果
	 */
	private Vector transfer( Hashtable htData ) {
		// 移動元メンバーシップIDリスト
		String[] strSendRelationIDList = null;
		// 移動先メンバーシップID
		String strReceiveRelationID = null;
		// 処理タイプ
		int intType = 0;
		// F2FLoタイプ
		String strF2F = "8";

		// 共通データアクセスクラスのインスタンス化
		DataAccess objDataAccess  = new DataAccess();
		try {
			try {
				// 移動元メンバーシップIDリストを取得します
				strSendRelationIDList = (String[])htData.get(SEND_RELATIONID_LIST);
				// 移動先メンバーシップIDを取得します
				strReceiveRelationID = (String)htData.get(RECEIVE_RELATIONID);
				// 処理タイプを取得します
				intType = ((Integer)htData.get(PROCESS_TYPE)).intValue();
			}
			catch(ClassCastException ce) {
				// 内部エラー
				MessageData objMessage = getMessageData(PARAM_WAR);
				throw new XMOException(objMessage);
			}
			// パラメータチェック
			if( strSendRelationIDList == null || strReceiveRelationID == null ) {
				// 内部エラー
				MessageData objMessage = getMessageData(PARAM_WAR);
				throw new XMOException(objMessage);
			}
			// 処理タイプチェック
			if( !(intType == UP_TYPE || intType == IN_TYPE || intType == DOWN_TYPE) ) {
				// 内部エラー
				MessageData objMessage = getMessageData(PARAM_WAR);
				throw new XMOException(objMessage);
			}
			
			// ADD 2005/04/25 START Koji Umehara [メソッド管理追加]
			// メソッドフラグを取得します
			boolean blnMethodFlag = htData.containsKey(METHOD_FLAG) ? ((Boolean)htData.get(METHOD_FLAG)).booleanValue() : false;
			// ADD 2005/04/25 END Koji Umehara [メソッド管理追加]

			// トランザクションの開始
			objDataAccess.trans(htData);

			// 移動処理後のLOアサイン更新処理を行うフラグ
			String strUpdateAssignFlag = (String)htData.get( UPDATE_ASSIGNMENTS );

			String strCallFromDelete = (String)htData.get( CALL_FROM_DELETE );
			// 移動処理を行います。
			//transferAction( strSendRelationIDList, strReceiveRelationID, intType, strUpdateAssignFlag, strCallFromDelete, objDataAccess );
			transferAction( strSendRelationIDList, strReceiveRelationID, intType, strUpdateAssignFlag, strCallFromDelete, objDataAccess, blnMethodFlag );

			// コミットを行います。
			objDataAccess.commit();

			// F2Fの初期データ作成処理を行う。
			// この処理は削除から呼ばれた場合は行わない
			if( strCallFromDelete == null ) {
				//移動先LOIDの取得。
				LoRelationData objReceiveData = makeLoRelationData( strReceiveRelationID );
				String strParentID = null;
				// 処理区分が中への場合は移動先LOIDはChildID
				if( intType == IN_TYPE ) {
					strParentID = objReceiveData.getChildID();		// 移動先LOID
				}
				// 処理区分が上へ、下への場合は移動先LOIDはParentID
				else {
					strParentID = objReceiveData.getParentID();	// 移動先LOID
				}

				// 移動先LOのアサインユーザリストを取得する。
				AssignmentsClient objAssignments = new AssignmentsClient();
				Vector vecUserList = new Vector();
				if(!objAssignments.getUserList(strParentID)) {
					// エラーの場合
					throw new XMOException( objAssignments.getMessageData() );
				}else{
					vecUserList = objAssignments.getUserListResult();
					if(vecUserList != null){
						htData.put("UserList", vecUserList);
					}
				}

				// 移動先にアサインされているユーザが一人以上いた場合のみF2Fの初期ログ作成を行う。
				if( vecUserList != null && vecUserList.size() > 0 ) {

					// F2Fが移動に含まれていたらログを書き込む処理を行う
					LoRelationData[] objSendDataList = new LoRelationData[strSendRelationIDList.length];
					for( int i = 0; i < strSendRelationIDList.length; i++ ){
						objSendDataList[i] = makeLoRelationData( strSendRelationIDList[i] );

						// 移動先親IDと移動元子IDを連結して、移動処理後の新たなMemberShipIDを決定する。
						String strSendID = strParentID + objSendDataList[i].getChildID();	// 移動元LOID

						String[] strList = isLoType(strSendID, strF2F);
						// 移動コンテナ配下にF2FLoが含まれた場合、共通ログを作成します。
						if(strList.length > 0){
							// 移動先にアサインユーザがいた場合、共通ログを作成します。
							if(vecUserList.size() > 0){
								for( int j = 0; j < strList.length; j++ ){
									// 登録項目設定
									Hashtable htbData = new Hashtable();
									htData.put( "LOID", strList[j] );
									htData.put( "UserList", vecUserList );
									// LOサーバ名の取得
									Environment objEnv = new Environment();
									String strLoServerName = objEnv.getLoServerName();
									// LOクラスをインスタンス化します
									LoTopClient objLoClient = new LoTopClient( strLoServerName, strF2F );
									// 登録処理
									if( !objLoClient.setUp( LoTopClient.SETUP_INSERT, htData ) ) {
										throw new XMOException( objLoClient.getMessageData() );
									}
								}
							}
						}
					}
				}
			}
		}
		catch( SQLException se ) {
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// エラー処理
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			// データベースエラーを設定
			Vector vecReturn = new Vector();
			vecReturn.add( getMessageData( DB_ERR ) );
			return vecReturn;
		}
		catch( XMOException xe ) {
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// エラー処理
			DebugLog.write(this.getClass(), xe, DebugLog.HIGHT);
			Vector vecReturn = new Vector();
			vecReturn.add( xe.getMessageData() );
			return vecReturn;
		}
		catch( Exception e ) {
			// ロールバック
			try { objDataAccess.rollback(); } catch( Exception ex ) {}
			// エラー処理
			DebugLog.write(this.getClass(), e, DebugLog.HIGHT);
			Vector vecReturn = new Vector();
			return vecReturn;
		}

		// 正常に終了下ので配列の１番目にnullを格納して返す。
		Vector vecReturn = new Vector();
		vecReturn.add(null);
		return vecReturn;
	}

	/**
	 * 移動処理を行います。
	 * @param strSendRelationIDList 移動元リレーションIDリスト
	 * @param strReceiveRelationID 移動先リレーションIDリスト
	 * @param intType 処理区分
	 * @param strUpdateAssignFlag アサインの更新の有無(nullの場合にアサイン更新処理を行う)
	 * @param strCallFromDelete ステータス伝搬の有無(nullの場合に ステータス伝搬処理を行う)
	 * @param objDataAccess データアクセスクラス
	 * @param blnMethodFlag メソッドフラグ
	 * @exception XMOException XMO独自例外
	 */
	private void transferAction(	String[] strSendRelationIDList,
									String strReceiveRelationID,
									int intType,
									String strUpdateAssignFlag,
									String strCallFromDelete,
									DataAccess objDataAccess,
									// ADD 2005/04/25 START Koji Umehara [メソッド管理追加]
									boolean blnMethodFlag )
									// ADD 2005/04/25 END Koji Umehara [メソッド管理追加]
		throws XMOException
	{
		// ADD 2005/04/25 START Koji Umehara [メソッド管理追加]
		// 対象とするツリーを切り替え
		LoRelationTree objTree = blnMethodFlag ? mobjMethodTree : mobjTree;
		// ADD 2005/04/25 END Koji Umehara [メソッド管理追加]
		
		//移動元IDリストをforループで回りながら、親IDと子IDに分割する。
		//移動元のstrSendRelationIDListの”＾”を削除する。
		LoRelationData[] objSendDataList = new LoRelationData[strSendRelationIDList.length];
		//移動後にステータス伝搬を走らせるため移動元マージ用ベクターに移動元の親IDを格納する。
		Vector vecStatusID = new Vector();
		for( int i = 0; i < strSendRelationIDList.length; i++ ) {
			objSendDataList[i] = makeLoRelationData( strSendRelationIDList[i] );
			if ( !vecStatusID.contains(objSendDataList[i].getParentID()) ){
				vecStatusID.addElement(objSendDataList[i].getParentID());
			}
		}
		//移動先IDリストを親IDと子IDに分割する。
		//移動先のstrReceiveRelationIDの”＾”を削除する。
		LoRelationData objReceiveData = makeLoRelationData( strReceiveRelationID );
		//移動後にステータス伝搬を走らせるため移動元マージ用ベクターに
		//上へまたは下への時、移動先の親ID
		//中への時、移動先の子IDを格納する。
		if (intType == 1 || intType == 3){
			if ( !vecStatusID.contains(objReceiveData.getParentID()) ){
				vecStatusID.addElement(objReceiveData.getParentID());
			}
		}
		else{
			if ( !vecStatusID.contains(objReceiveData.getChildID()) ){
				vecStatusID.addElement(objReceiveData.getChildID());
			}
		}

		// LoRelationテーブルアクセスクラスのインスタンス化
		LoRelationDataAccess objLoRelationDataAccess = new LoRelationDataAccess();

		try {

			// 処理前のDB更新チェック処理を行う。データベースが更新されていた場合はエラーを投げる
			if( !transferCheck( objSendDataList, objReceiveData, intType, objDataAccess ) ) {
				// 更新済み警告をスローします。
				throw new XMOException( getMessageData( DB_UPDATE_WAR ) );
			}

			int intReceiveSortNo = 0;
			int intReceiveMaxSortNo = 0;
			int intSortNo = 0;
			//上へ or 下への時
			if (intType == 1 || intType == 3) {
				// 移動先のsort_noを取得する。
				intReceiveSortNo = objLoRelationDataAccess.getMaterialIDSortNo(objReceiveData.getMaterialID(), objDataAccess);

				//移動元IDリスト配列数
				int intSendListLength = strSendRelationIDList.length;
				//updateReciveSort_noメソッド内で上への時、下への時のUPDATE文の判定を行っている。
				objLoRelationDataAccess.updateSortNo(intReceiveSortNo,
																intSendListLength,
																objReceiveData.getParentID(),
																intType,
																objDataAccess);

				for( int i = 0; i < strSendRelationIDList.length; i++ ){
					//上への時：移動元のsort_noに対して、移動先のsort_noに1++した値を割り振る為メソッドに渡す前に1++する。
					if (intType == 1){
						intSortNo = intReceiveSortNo++;

					}
					//下への時：移動元のsort_noに対して、移動先のsort_noに++1した値を割り振る為メソッドに渡す前に++1する。
					if (intType == 3){
						intSortNo = ++intReceiveSortNo;

					}
					objLoRelationDataAccess.updateLoRelation(objSendDataList[i], objReceiveData.getParentID(), intSortNo, objDataAccess);
				}
			}
			//中への時
			else{
				// 移動先の子の配下のsort_noの最大値を取得する。
				intReceiveMaxSortNo = objLoRelationDataAccess.getMaxSortNo(objReceiveData.getChildID(), objDataAccess);

				//移動元のsort_noに対して、移動先の子の配下のsort_noの最大値に++1したsort_noを割り振る。
				for( int i = 0; i < strSendRelationIDList.length; i++ ){
					intSortNo = ++intReceiveMaxSortNo;
					objLoRelationDataAccess.updateLoRelation(objSendDataList[i], objReceiveData.getChildID(), intSortNo, objDataAccess);
				}
			}

			// アサイン更新フラグがnullの場合はアサイン更新を行う。
			if( strUpdateAssignFlag == null ) {
				// 移動元分ループを行い、アサインの設定を行います。
				for(int i = 0; i < objSendDataList.length; i++ ) {
					// 更新前メンバシップID
					String strSendID = objSendDataList[i].getMaterialID();
					// 削除の場合
					if ( strCallFromDelete != null ){
						// アサインの削除
						deleteAssign( strSendID, objDataAccess );
						continue;
					}
					// 更新後メンバシップID
					String strRecieveID = null;
					if( intType == IN_TYPE ) {
						strRecieveID = objReceiveData.getChildID() + objSendDataList[i].getChildID();
					}
					else {
						strRecieveID = objReceiveData.getParentID() + objSendDataList[i].getChildID();
					}
					// アサインの更新を行います。
					updateAssign( strSendID, strRecieveID, objDataAccess );
				}
			}

			// LO削除処理から呼ばれた場合に伝搬処理を行う
			if( strCallFromDelete == null ) {
				// 移動処理に関連したLOに対して、ステータス伝搬を実行します
				if( vecStatusID.size() >= 2 ) {
					for( int i = 0; i < vecStatusID.size(); i++ ) {
						String strLoID = (String)vecStatusID.get(i);
						StatusPropagation objPropagation = new StatusPropagation( strLoID );
						PropagateControl.getInstance().addPropagate(objPropagation);

					}
				}
			}

			//---------------------------------------------------------------------
			//メモリ上のツリー構造を更新(移動処理)
			//---------------------------------------------------------------------
			//boolean blIsUpdate = mobjTree.executeMove( objSendDataList, objReceiveData, intType );
			boolean blIsUpdate = objTree.executeMove( objSendDataList, objReceiveData, intType );
			//更新に失敗した場合
			if ( !blIsUpdate )
			{
				//エラー
				String strUpdateErr = "メモリ上のツリー更新に失敗しました。";
				throw new XMOException( strUpdateErr );
			}
		}
		catch( SQLException se ) {
			// エラー処理
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			// データベースエラーを設定
			throw new XMOException( getMessageData( DB_ERR ) );
		}
	}

	/**
	 * LOアサインの削除処理を行います。
	 * 
	 * @param strSendID 削除前メンバシップID
	 * @param objDataAccess データアクセスクラス
	 * @exception XMOException XMO例外
	 */
	private void deleteAssign( String strSendID, DataAccess objDataAccess )
		throws XMOException {

		try{

			// 移動元配下のマテリアルIDを取得し、
			// マテリアルIDをキーにAssignmentsテーブルより
			// 該当レコードを削除します。

			// LO割当テーブルを削除します
			AssignmentsClient objAss = new AssignmentsClient();
			// 削除条件を設定
			objAss.setWhereMaterialID( strSendID );
			// 現在使用しているコネクションをしようしトランザクションの共有を行います
			objAss.setConnectionIndex( objDataAccess.getConnectionIndex() );
			// LO割当テーブル更新処理
			if ( !objAss.delete() ){
				// DBエラーを投げる
				throw new XMOException( objAss.getMessageData() );
			}
		} catch ( Exception e ){
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			// システムエラーを投げる
			throw new XMOException( getMessageData( SYSTEM_ERR ) );
		}
	}

	/**
	 * LOアサインの更新処理を行います。
	 * @param strSendID 更新前メンバシップID
	 * @param strRecieveID 更新後メンバシップID
	 * @param objDataAccess データアクセスクラス
	 * @exception XMOException XMO例外
	 */
	private void updateAssign( String strSendID, String strRecieveID, DataAccess objDataAccess )
		throws XMOException
	{
		//LO割り当てテーブルの更新が必要な場合のみ更新します。
		try {
			// LO割当テーブルを更新します
			AssignmentsClient objAss = new AssignmentsClient();
			// 更新メンバシップIDを設定
			objAss.setMaterialID( strRecieveID );
			// 更新条件を設定
			objAss.setWhereMaterialID( strSendID );
			// 現在使用しているコネクションをしようしトランザクションの共有を行います
			objAss.setConnectionIndex( objDataAccess.getConnectionIndex() );
			// LO割当テーブル更新処理
			if( !objAss.update() ) {
				// DBエラーを投げる
				throw new XMOException( objAss.getMessageData() );
			}
		}
		catch( Exception e ) {
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			// システムエラーを投げる
			throw new XMOException( getMessageData( SYSTEM_ERR ) );
		}
	}

	/**
	 * 移動処理前のDB更新チェックを行います。
	 * DBが更新されていた場合はエラーを投げます。
	 * @param objSendDataList 元リストデータ
	 * @param objRecieveData 先データ
	 * @param intType 処理区分(1: 上へ、2: 中へ、3: 下へ)
	 * @param objDataAccess 共通データアクセスクラス
	 * @return boolean true : チェックOK false : チェックNG
	 * @exception SQLException DBエラー
	 * @exception XMOException XMO例外
	 */
	private boolean transferCheck( LoRelationData[] objSendDataList,
									LoRelationData objRecieveData,
									int intType,
									DataAccess objDataAccess )
		throws SQLException, XMOException
	{
		//
		// 移動先ID取得
		String strRecieveID = null;
		if( intType == IN_TYPE ) {
			strRecieveID = objRecieveData.getChildID();
		}
		else {
			strRecieveID = objRecieveData.getParentID();
		}

		// レコードの存在チェック 存在しなかったらエラー
		if( !isExistRecord( objSendDataList, objRecieveData, objDataAccess ) ) {
			return false;
		}

		// 移動元と移動先の親IDが一緒ではないときのみ移動先配下の存在チェックを行う
		for( int i = 0; i < objSendDataList.length; i++ ) {
			if( !objSendDataList[i].getParentID().equals( strRecieveID ) ) {
				// 移動先配下の存在チェック 存在したらエラー
				if( isExistUnderRecord( objSendDataList[i], strRecieveID, objDataAccess ) ) {
					return false;
				}
			}
		}

		// 移動元が移動先の配下のチェックを行います。配下ならエラー
		if( isUnder( objSendDataList, strRecieveID ) ) {
			return false;
		}

		return true;
	}

	/**
	 * 複製処理前のDB更新チェックを行います。
	 * DBが更新されていた場合はエラーを投げます。
	 * @param objSendDataList 元リストデータ
	 * @param objRecieveData 先データ
	 * @param intType 処理区分(1: 上へ、2: 中へ、3: 下へ)
	 * @param objDataAccess 共通データアクセスクラス
	 * @return boolean true : チェックOK false : チェックNG
	 * @exception SQLException DBエラー
	 * @exception XMOException XMO例外
	 */
	private boolean copyCheck( LoRelationData[] objSendDataList,
									LoRelationData objRecieveData,
									int intType,
									DataAccess objDataAccess )
		throws SQLException, XMOException
	{
		//
		// 複製先ID取得
		String strRecieveID = null;
		if( intType == IN_TYPE ) {
			strRecieveID = objRecieveData.getChildID();
		}
		else {
			strRecieveID = objRecieveData.getParentID();
		}

		// レコードの存在チェック 存在しなかったらエラー
		if( !isExistRecord( objSendDataList, objRecieveData, objDataAccess ) ) {
			return false;
		}

		return true;
	}

	/**
	 * レコードの存在チェックを行います。
	 * @param objSendDataList 元リストデータ
	 * @param objRecieveData 先データ
	 * @param objDataAccess 共通データアクセスクラス
	 * @return boolean true : 存在する false : 存在しない
	 * @exception SQLException DB例外
	 */
	public boolean isExistRecord(  LoRelationData[] objSendDataList,
									LoRelationData objRecieveData,
									DataAccess objDataAccess )
		throws SQLException
	{
		LoRelationDataAccess objLoRelationDataAccess =
										new LoRelationDataAccess();
		// 元のレコードの存在チェックを行います
		for( int i = 0; i < objSendDataList.length; i++ ) {
			// メンバシップIDを指定して、レコード件数を取得する。
			int intCount = objLoRelationDataAccess.getCountMaterialID(
												objSendDataList[i].getMaterialID(),
												objDataAccess );

			if( intCount == 0 ) {
				// ０件の場合はすでに移動してしまっている。
				return false;
			}
		}

		// 先のレコードの存在チェック
		// メンバシップIDを指定して、レコード件数を取得する。
		int intCount = objLoRelationDataAccess.getCountMaterialID(
											objRecieveData.getMaterialID(),
											objDataAccess );
		if( intCount == 0 ) {
			// ０件の場合はすでに移動してしまっている。
			return false;
		}

		return true;
	}

	/**
	 * 先の直下に元と同一のレコードが存在するかチェックをします。
	 * @param objSendData 元リスト
	 * @param strRecieveID 先の親ID
	 * @param objDataAccess 共通データアクセスクラス
	 * @return boolean true : 存在する false : 存在しない
	 * @exception SQLException DB例外
	 */
	public boolean isExistUnderRecord( LoRelationData objSendData,
									String strRecieveID,
									DataAccess objDataAccess )
		throws SQLException
	{

		LoRelationDataAccess objLoRelationDataAccess =
										new LoRelationDataAccess();

		// 処理後のレコードのを組み立て、そのレコードが存在するかチェックを行います
		// メンバシップIDを指定して、レコード件数を取得する。
		int intCount = objLoRelationDataAccess.getCountMaterialID(
											strRecieveID + objSendData.getChildID(),
											objDataAccess );

		if( intCount == 0 ) {
			// 0件の場合は存在しない
			return false;
		}

		// 存在する
		return true;
	}

	/**
	 * 元が先の配下かチェックを行います。。
	 * @param objSendDataList 元リストデータ
	 * @param strRecieveID 先の親ID
	 * @return boolean true : 配下 false : 配下じゃない
	 * @exception XMOException XMO例外
	 */
	public boolean isUnder( LoRelationData[] objSendDataList,
							String strRecieveID )
		throws XMOException
	{


		// 元の配下のIDを取得します。
		for( int i = 0; i < objSendDataList.length; i++ ) {
			// メモリツリーから元の配下のIDリストを取得します。
			String[] strUnderIDList = mobjTree.getUnderIDList( objSendDataList[i].getMaterialID() );
			
			// ADD 2005/04/22 START Koji Umehara [メソッド管理追加]
			// なければMethodツリーも検索する。
			if( strUnderIDList == null ) {
				strUnderIDList = mobjMethodTree.getUnderIDList( objSendDataList[i].getMaterialID() );
			}
			// ADD 2005/04/22 END Koji Umehara [メソッド管理追加]

			// 取得できなかった場合はシステムエラーをスローする
			if( strUnderIDList == null ) {
				throw new XMOException( getMessageData( SYSTEM_ERR ) );
			}

			for( int j = 0; j < strUnderIDList.length; j++ ) {
				if( strUnderIDList[j].equals( strRecieveID ) ) {
					// 配下に同じIDが存在した場合
					return true;
				}
			}
		}

		// 存在しなかった場合
		return false;
	}

	/**
	 * デリミタ('^')で区切られたMemberShipIDを受け取り、GroupAndPeopleDataオブジェクトを生成して返します
	 * @param strMemberShipID MemberShipID ただしデリミタ('^')で区切られている
	 * @return LoRelationData 生成されたLoRelationDataオブジェクト
	 */
	private LoRelationData makeLoRelationData( String strLoRelationID ) {
		LoRelationData objData = new LoRelationData();

		String strSendChildID = strLoRelationID.substring( strLoRelationID.indexOf("^") + 1, strLoRelationID.length() );
		String strSendParentID = strLoRelationID.substring( 0, strLoRelationID.indexOf("^") );
		String strSendLoRelationID = strSendParentID + strSendChildID;

		objData.setParentID( strSendParentID );
		objData.setChildID( strSendChildID );
		objData.setMaterialID( strSendLoRelationID );

		return objData;
	}

	/**
	 * 配下に指定したLOタイプのLOが存在するか検索します。
	 * 存在した場合、RealLOIDリストを返却します。
	 * 存在しない場合、nullを返却します。
	 * @param strMaterialID マテリアルID
	 * @param strLoType 検索するLOタイプ
	 * @return String[] LOIDリスト
	 */
	public String[] isLoType(String strMaterialID, String strLoType) throws XMOException
	{
		Vector vecList = new Vector();
		Hashtable htbParam = new Hashtable();	// パラメータ格納

		// パラメータのチェック
		if(strMaterialID == null || strMaterialID.equals("")){
			throw new XMOException(PARAM_WAR);
		}
		if(strLoType == null || strLoType.equals("")){
			throw new XMOException(PARAM_WAR);
		}

		// 指定IDを元に配下のIDを取得します。
		String[] strList = mobjTree.getUnderIDList(strMaterialID);
		
		// ADD 2005/04/22 START Koji Umehara [メソッド管理追加]
		// なければMethodツリーも検索する。
		if( strList == null ) {
			strList = mobjMethodTree.getUnderIDList(strMaterialID);
		}
		// ADD 2005/04/22 END Koji Umehara [メソッド管理追加]

		// パラメータの設定
		htbParam.put("LOType", strLoType);
		htbParam.put("LIST", vecList);

		// 配下のLOIDリストの中から指定のLOタイプのリストだけフィルタリングします。
		int intUpperLimit = 100;	// SQLのin条件の上限
		int intCount = 1;

		try{
			// フィルタリング
			StringBuffer sbCondition = new StringBuffer();
			sbCondition.append("IN (");
			// リストの取得(100件ずつ検索条件にin句に設定する
			for(int i=0;i<strList.length;i++){
				sbCondition.append("'" + StringEscapeUtils.escapeSql(strList[i]) + "'");

				if( ((i + 1) % 100) == 0 || i == strList.length - 1 ) {
					sbCondition.append(")");
					htbParam.put(CONDITION_KEY, sbCondition.toString());
					// リストを取得します。
					LoDataAccess objDataAccess = new LoDataAccess();
					vecList = objDataAccess.selectListByLoType(htbParam);
					sbCondition = new StringBuffer();
					sbCondition.append("IN (");
				}
				else {
					sbCondition.append(", ");
				}
			}
		}
		catch(SQLException se){
			DebugLog.write( this.getClass(), se, DebugLog.HIGHT );
			throw new XMOException(SYSTEM_ERR);
		}

		// 配列コピー
		String[] strLOIDList = new String[ vecList.size() ];
		vecList.copyInto( strLOIDList );

		return strLOIDList;
	}

	/**
	 * ノードリストから指定されたタイプを持つノードを抜き出し、リストにして返します。
	 * @param objNodeList ノードリストを指定します。
	 * @param strType 抜き出したいタイプを指定します。
	 * @return XMONode[] ツリー構造から、指定されたタイプのノードを抜き出した結果の配列です。
	 *                   ツリーの上位から見つかった順番で入ります。
	 */
	private XMONode[] convertNodesToList( XMONode[] objNodeList, String strType ) {

		if( objNodeList == null ) {
			// 空の配列を返す
			return new XMONode[0];
		}

		Vector vecNodeList = new Vector();
		for( int i = 0; i < objNodeList.length; i++ ) {
			convertNodeToList( objNodeList[i], vecNodeList, strType );
		}

		XMONode[] strResNodeList = new XMONode[vecNodeList.size()];

		vecNodeList.copyInto( strResNodeList );

		return strResNodeList;

	}

	// ノード抜き出しの1件分の処理→　子ノードが存在した場合は再帰で自身を呼び出す。
	// 引数で受け取ったVector(vecNodeList)に格納していきます。
	private void convertNodeToList( XMONode objNode, Vector vecNodeList, String strType ) {

		String strNodeType = objNode.getType();
		if( strNodeType != null && strNodeType.equals( strType ) ) {
			vecNodeList.add( objNode );
		}

		// 子ノード分繰り返す
		int intChildCount = objNode.getChildCount();
		for( int i = 0; i < intChildCount; i++ ) {
			XMONode objChildNode = objNode.getChildNode(i);
			convertNodeToList( objChildNode, vecNodeList, strType );
		}

	}

	/**
	 * コンテナ配下のLOが前提条件加設定か被設定かを判定します。
	 * コンテナ配下を検索して、最初に加設定または被設定のLOを見つけた時点で配下の検索処理は中断します。
	 * 前提条件加設定かつ被設定の場合、1を返します。
	 * 前提条件加設定の場合、2を返します。
	 * 前提条件被設定の場合、3を返します。
	 * <BR>
	 * @param htData 検索情報
	 * @return Vector 結果を返します。
	 */
	private Vector checkUnderPrere( Hashtable htData ) {
		Vector vecRet = new Vector();				// 戻り値用ベクター
		MessageData objMessage = null;

		try {
			String strActivePrere = "";
			String strPassivePrere = "";
			int intRet = 0;

			String strMaterial = (String)htData.get(MATERIAL_ID);	// コンテナMaterialID
			if(strMaterial == null || strMaterial.equals("")){
				objMessage = getMessageData(PARAM_WAR);
				throw new XMOException(objMessage);
			}
			String strCheckFlg = (String)htData.get("check_flg");	// 前提条件チェックフラグ
			// 配下LOのリストを取得します。
			String[] strUnderLOIDList = mobjTree.getUnderIDList(strMaterial);
			
			// ADD 2005/04/22 START Koji Umehara [メソッド管理追加]
			// なければMethodツリーも検索する。
			if( strUnderLOIDList == null ) {
				strUnderLOIDList = mobjMethodTree.getUnderIDList(strMaterial);
			}
			// ADD 2005/04/22 END Koji Umehara [メソッド管理追加]
			
			for(int i=0;i<strUnderLOIDList.length;i++){
				// 【前提条件加設定チェック】
				LoClient objLo = new LoClient();
				if((strCheckFlg == null || strCheckFlg.equals("active")) && !strActivePrere.equals("on")){
					objLo.setWhereLoID(strUnderLOIDList[i]);
					if(!objLo.select())
					{
						throw new XMOException(objLo.getMessageData());
					}
					if(objLo.next())
					{
						String strPrere = objLo.getPrere();
						if(strPrere !=null && !strPrere.equals("")){
							strActivePrere = "on";
						}
					}
				}
				// 【前提条件被設定チェック】
				if((strCheckFlg == null || strCheckFlg.equals("passive")) && !strPassivePrere.equals("on")){
					objLo.clear();
					if(!objLo.selectLOIDFromPrere(strUnderLOIDList[i])){
						throw new XMOException(objLo.getMessageData());
					}
					String[] strList = objLo.getLOIDFromPrere();
					if(strList.length > 0){
						strPassivePrere = "on";
					}
				}
				if(strActivePrere.equals("on") && strPassivePrere.equals("on")){
					break;
				}
			}

			// 加設定かつ被設定の場合
			if(strActivePrere.equals("on") && strPassivePrere.equals("on")){
				intRet = 1;
			}
			// 加設定のみの場合
			else if(strActivePrere.equals("on")){
				intRet = 2;
			}
			// 被設定のみの場合
			else if(strPassivePrere.equals("on")){
				intRet = 3;
			}
			Integer objRet = new Integer(intRet);
			vecRet.add( objRet );
		}
		catch( XMOException xe ) {
			objMessage = xe.getMessageData();
		}
		catch( Exception e ) {
			objMessage = getMessageData( SYSTEM_ERR );
		}
		vecRet.add( 0, objMessage );

		return vecRet;
	}

	/**
	 * コピー時の前提条件の書き換えを行います。
	 * <BR>
	 * @param htData 検索情報
	 * @return Vector 結果を返します。
	 */
	private void changePrere(Vector vecPrere, Vector vecBeforeID, Vector vecAfterID, DataAccess objDataAccess)
			throws XMOException
	{
		try{
			// 前提条件配列をチェックし、前提条件がセットされている場合は前提条件の書き換え処理を行う。
			for(int k=0;k<vecPrere.size();k++){
				String strPrere = (String)vecPrere.get(k);
				// 書き換え処理
				if(strPrere != null && !strPrere.equals("")){
					for(int j=0;j<vecBeforeID.size();j++){
						String strBeforID = (String)vecBeforeID.get(j);
						String strAfterID = (String)vecAfterID.get(j);
						int intFromIndex = 0;
						while(strPrere.indexOf(strBeforID, intFromIndex) >= 0){
							StringBuffer sbPrere  = new StringBuffer();
							int intIndex = strPrere.indexOf(strBeforID, intFromIndex);
							String strCheck = strPrere.substring(intIndex + strBeforID.length(), intIndex + strBeforID.length() + 1);
							if(strCheck.equals("=") || strCheck.equals("!") || strCheck.equals(" ")){
								sbPrere.append(strPrere.substring(0, intIndex));
								sbPrere.append(strAfterID);
								sbPrere.append(strPrere.substring(intIndex + strBeforID.length(), strPrere.length()));
								strPrere = sbPrere.toString();
								intFromIndex = intIndex + strAfterID.length();
							}else{
								intFromIndex = intIndex + strBeforID.length();
							}
						}
					}
					// 書き換え後の前提条件をLOテーブルに更新する。
					editPrerequisite((String)vecAfterID.get(k), strPrere, objDataAccess);
				}
			}
		}
		catch(Exception e){
			DebugLog.write( this.getClass(), e, DebugLog.HIGHT );
			throw new XMOException(SYSTEM_ERR);
		}
	}
}

