//
// CopyRight (C) 2002, IPA, eLC, NTT Resonant Inc. All rights reserved.
//
//	Sequencing
//
//	変更履歴
//		2002.12.25  新規作成
//

package jp.co.ntt.lms.lo.scorm.kernel;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Hashtable;

import jp.co.ntt.lms.lo.scorm.kernel.exception.CommunicationModuleWrapperException;
import jp.co.ntt.lms.lo.scorm.kernel.exception.SystemException;
import jp.co.ntt.lms.lo.scorm.util.CachedNode;
import jp.co.ntt.lms.lo.scorm.util.DateUtility;
import jp.co.ntt.lms.lo.scorm.util.XMLUtil;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Sequencing クラス<BR>
 * シーケンシング情報を表すクラス<BR>
 */
public class Sequencing {
	/** sequencing node */
	private CachedNode sequencingNode;

	/** progress information */
	private Progress progress;

	/** status information */
	private Status status;

	/** local objective */
	private LocalObjective[] localObjective;

	/** communication module */
	private SCORMCommunicationModule communicationModule;

	/** activity */
	private Activity activity;

	/** IsAttemptedInCurrentAttempt */
	private boolean IsAttemptedInCurrentAttempt;

	/**
	 * コンストラクタ<BR>
	 *
	 * @param activity Sequencing を持つアクティビティ<BR>
	 * @param manifestNode 教材構造ノード<BR>
	 * @param sequencingNode sequencingNode<BR>
	 * @param globalObjectives globalObjective の配列<BR>
	 */
	public Sequencing(
		Activity activity,
		CachedNode manifestNode,
		CachedNode sequencingNode,
		GlobalObjective[] globalObjectives,
		boolean hasChildren)
		throws SystemException {

		try {
			// アクティビティの登録
			this.activity = activity;

			IsAttemptedInCurrentAttempt = false;

			// sequencingの登録
			this.sequencingNode = sequencingNode;

			// progressのインスタンス化
			progress = new Progress(activity, manifestNode);

			// Statusのインスタンス化
			status = new Status(activity, manifestNode);

			if (!hasChildren) {
				// SCORMCommunicationModuleのインスタンス化
				communicationModule = new SCORMCommunicationModule(activity);
			}

			CachedNode objectivesNode = null;

			if (sequencingNode != null) {
				objectivesNode = sequencingNode.selectSingleChildNode("objectives");
			}

			if (objectivesNode == null) {
				/*
				 * primary を作成する
				 * objective ID は SCO ID + 教材ID である
				 */
				localObjective = new LocalObjective[1];
				localObjective[0] = new LocalObjective(activity);
				localObjective[0].setObjectiveID(activity.getIdentifier() + "." + activity.getCourseIdentifier());
				// 20040802 ADD NTTR IDなしObjectiveの識別
				localObjective[0].setHasObjectiveID();
				return;
			}

			/*
			 * primary objective を作成する
			 * id がなければ SCO ID と 教材ID で作成する
			 * 
			 */
			CachedNode primaryObjectiveNode = objectivesNode.selectSingleChildNode("primaryObjective");

			LocalObjective primaryObjective =
				new LocalObjective(activity, manifestNode, primaryObjectiveNode, globalObjectives);

			CachedNode idNode = primaryObjectiveNode.selectSingleChildNode("@objectiveID");

			if (idNode != null) {
				primaryObjective.setObjectiveID(idNode.getNodeValue());
			}
			else {
				//IDがなかった場合はSCOIDと教材IDで作る
				primaryObjective.setObjectiveID(activity.getIdentifier() + "." + activity.getCourseIdentifier());
				// 20040802 ADD NTTR IDなしObjectiveの識別
				primaryObjective.setHasObjectiveID();
			}

			/*
			 * objectiveの生成
			 * objective の node の分だけ生成する
			 * この場合、 objectiveID がなければ SCO ID + 教材ID + カウント で作成する
			 */
			CachedNode[] objectiveNodes = objectivesNode.selectChildNodeList("objective");

			LocalObjective[] objectives = new LocalObjective[objectiveNodes.length];

			for (int i = 0; i < objectiveNodes.length; i++) {
				objectives[i] = new LocalObjective(activity, manifestNode, objectiveNodes[i], globalObjectives);
				objectives[i].setObjectiveID(objectiveNodes[i].selectSingleChildNode("@objectiveID").getNodeValue());
			}

			localObjective = new LocalObjective[objectiveNodes.length + 1];
			localObjective[0] = primaryObjective;
			System.arraycopy(objectives, 0, localObjective, 1, objectives.length);
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("Sequencing failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * 中断情報を作成する
	 *
	 * @param doc 保存するDOM DocumentのOwner Documentを指定する
	 * @param parent 保存するxml情報を設定する(親)のエレメント
	 */
	public void save(Document doc, Element parent, boolean traceLog) throws SystemException {
		try {
			// status の保存
			status.save(doc, parent, traceLog);

			// localObjective の保存
			Element objectivesNode = XMLUtil.append(doc, parent, "objectives");

			for (int i = 0; i < localObjective.length; i++) {
				localObjective[i].save(doc, objectivesNode, traceLog);
			}

			// progress の保存
			progress.save(doc, parent, traceLog);
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("save failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * useCurrentAttemptの為の候補値を更新する
	 */
	public void setCandidatePreviousValue() throws SystemException {
		for (int i = 0; i < localObjective.length; i++) {
			localObjective[i].setCandidatePreviousValue();
		}

		progress.setCandidatePreviousValue();
	}

	/**
	 * 中断情報を復帰する
	 *
	 * @param parent 中断情報を取り出す為の(親)のノードを指定する
	 */
	public void initializeSequencingInformation(CachedNode parent) throws SystemException {
		try {
			// status の初期化
			status.initializeStatusInformation(parent);

			// localObjective の初期化
			CachedNode objectivesNode;

			if (parent == null) {
				objectivesNode = null;
			}
			else {
				objectivesNode = parent.selectSingleChildNode("objectives");
			}

			for (int i = 0; i < localObjective.length; i++) {
				localObjective[i].initializeLocalObjectiveInformation(objectivesNode);
			}

			// progress の初期化
			progress.initializeProgressInformation(parent);
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("initialize failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * 試行情報の初期化<BR>
	 */
	public void initializeAttemptInformation() throws SystemException {
		try {
			// progress の試行情報の初期化
			progress.initializeAttemptInformation();

			// status の試行情報の初期化
			status.initializeAttemptInformation();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("initializeAttempt failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * 終期化<BR>
	 */
	public void terminate() throws SystemException {
		// CachedNode のクリア
		sequencingNode = null;

		// progress の終期化
		progress.termiante();
		progress = null;

		// status の終期化
		status.terminate();
		status = null;

		// localObjective の終期化
		if (localObjective != null) {
			for (int i = 0; i < localObjective.length; i++) {
				localObjective[i].terminate();
				localObjective[i] = null;
			}
		}

		if (communicationModule != null) {
			// SCORM Communication Module の終期化
			communicationModule.terminate();
			communicationModule = null;
		}

		// activity のクリア
		activity = null;
	}

	/**
	 * communication module の初期化<BR>
	 *
	 * @param userID 学習者ID
	 * @param userName 学習者名
	 * @param URL aiccURL
	 * @param parent 初期化情報が設定されている(親)のノードを指定する<BR>
	 */
	public void initializeAPIAdapter(String userID, String userName, String URL, String encoding, CachedNode parent)
		throws SystemException {

		try {
			if (communicationModule != null) {
				Hashtable initialParameter = new Hashtable();

				initialParameter.put("userID", userID);
				initialParameter.put("userName", userName);
				initialParameter.put("URL", URL);
				initialParameter.put("encoding", encoding);

				communicationModule.initialize(
					initialParameter,
					(parent == null) ? null : parent.selectSingleChildNode("text()").getNodeValue());
			}
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"initializeAPIAdapter failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * communication module の初期化<BR>
	 *
	 * @param userID 学習者ID
	 * @param userName 学習者名
	 * @param URL aiccURL
	 * @param parent 初期化情報が設定されている(親)のノードを指定する<BR>
	 */
	public void initializeAPIAdapter2(String userID, String userName, String URL, String encoding)
		throws SystemException {

		try {
			if (communicationModule != null) {
				Hashtable initialParameter = new Hashtable();

				initialParameter.put("userID", userID);
				initialParameter.put("userName", userName);
				initialParameter.put("URL", URL);
				initialParameter.put("encoding", encoding);

				communicationModule.initialize2(initialParameter);
			}
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"initializeAPIAdapter failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}
	
	/**
	 * 外部API情報をすべて保存する<BR>
	 */
	public void saveAPIAdapter(Document doc, Element parent) throws SystemException {
		try {
			if (communicationModule != null) {
				parent.appendChild(XMLUtil.createTextNode(doc, communicationModule.save()));
			}
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("saveAPIAdapter failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * 外部APIAdpaterの再開を行う
	 */
	public void resumeAPIAdapter() throws SystemException {
		if (communicationModule != null) {
			communicationModule.resumeAPIAdapter();
		}
	}

	/**
	 * トラッキング情報の同期
	 */
	public void synchronizeTrackingModel() throws SystemException {
		try {
			if (communicationModule != null) {
				communicationModule.close();
				communicationModule.synchronizeTrackingModel();
			}

			for (int i = 0; i < localObjective.length; i++) {
				localObjective[i].synchronizeTrackingModel();
			}

		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"synchronizeTrackingModel failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * 次に遷移するSCOのURLを取得する
	 * @param learningLogFolder 学習ログの出力先を指定する
	 * @return 次に遷移するSCOのURL
	 */
	public String getURL(String learningLogFolder) throws CommunicationModuleWrapperException {
		if (communicationModule == null) {
			return null;
		}

		return communicationModule.open(learningLogFolder);
	}

	/**
	 * 外部APIアダプタを取得する
	 * @return APIAdapterWrapper
	 */
	public SCORMCommunicationModule getCommunicationModule() {
		return communicationModule;
	}

	/**
	 * progress information取得
	 * @return Progress アクティビティの progress クラスのオブジェクトを返す<BR>
	 */
	public Progress getProgress() {
		return progress;
	}

	/**
	 * status オブジェクトの取得<BR>
	 * @return Status アクティビティのStatusクラスのオブジェクトを返す<BR>
	 */
	public Status getStatus() {
		return status;
	}

	/**
	 * <rollupRules>タグのCachedNodeを取得する
	 * 
	 * @return <rollupRules>タグのCachedNodeを返却する 
	 */
	public CachedNode getRollupRules() {
		CachedNode rollupRules = null;

		if (sequencingNode != null) {
			rollupRules = sequencingNode.selectSingleChildNode("rollupRules");
		}

		return rollupRules;
	}

	/**
	 * <sequencingRules>タグのCachedNodeを取得する
	 * 
	 * @return <sequencingRules>タグのCachedNodeを返却する 
	 */
	public CachedNode getSequencingRules() {
		CachedNode sequencingRules = null;

		if (sequencingNode != null) {
			sequencingRules = sequencingNode.selectSingleChildNode("sequencingRules");
		}

		return sequencingRules;
	}

	/**
	 * control modeのフラグを取得する<BR>
	 * @param controlModeType コントロールモード定数<BR>
	 * @return control mode が有効か否か<BR>
	 */
	public boolean getControlModeFlag(String controlModeType) throws SystemException {
		try {
			CachedNode controlMode = null;

			if (sequencingNode != null) {
				controlMode = sequencingNode.selectSingleChildNode("controlMode");
			}

			/*
			 * controlMode の node がない場合はデフォルトから取得する
			 * ある場合は node から取得する
			 * element がない場合もデフォルトから取得する
			 */
			String controlFlag;

			if (controlMode == null) {
				controlFlag = Default.getValue("controlMode/@" + controlModeType);
			}
			else {
				CachedNode controlNode = controlMode.selectSingleChildNode("@" + controlModeType);

				if (controlNode == null) {
					controlFlag = Default.getValue("controlMode/@" + controlModeType);
				}
				else {
					controlFlag = controlNode.getNodeValue();
				}
			}

			return Boolean.valueOf(controlFlag).booleanValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("getControlModeFlag failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * アクティビティがAvailableChildrenに含まれるか否か<BR>
	 * @return boolean 含まれるか否かを示す<BR>
	 *     ( true: 含まれる / false: 含まれない )<BR>
	 *     ただし root の場合は常に true を返す<BR>
	 */
	public boolean isAvailable() {
		/*
		 * root の場合は true を返す
		 */
		if (activity.isRoot()) {
			return true;
		}

		/*
		 * それ以外は親アクティビティの AvailableChildren に thisActivity
		 * がいるか否かを判定する
		 */
		Activity[] availableSibling = activity.getParent().getAvailableChildren();

		if (availableSibling == null) {
			return false;
		}

		for (int i = 0; i < availableSibling.length; i++) {
			if (activity == availableSibling[i]) {
				return true;
			}
		}

		return false;
	}

	/**
	 * attempt limit を取得する<BR>
	 * @return Integer 学習回数の制限値を示す<BR>
	 *         null の場合は学習制限なしを示す<BR>
	 */
	public Integer getAttemptLimit() throws SystemException {
		try {
			CachedNode limitCondition = null;

			if (sequencingNode != null) {
				limitCondition = sequencingNode.selectSingleChildNode("limitConditions");
			}

			/*
			 * limitConditions の element がない場合、もしくは
			 * limitConditions の element があっても attemptLimitの attributeがない場合は
			 * 制限なしとする
			 */
			if (limitCondition == null) {
				return null;
			}

			CachedNode attemtpCountNode = limitCondition.selectSingleChildNode("@attemptLimit");

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

			return new Integer(attemtpCountNode.getNodeValue());
		}
		catch (Exception ex) {
			throw new SystemException("getAttemptLimit failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * Durationの制限取得<BR>
	 * @param limitType Duration の attribute を指定する<BR>
	 * @return attributeに対応した duration を返す<BR>
	 *         制限なしの場合は null を返す<BR>
	 */
	public Duration getDurationLimit(String limitType) throws SystemException {
		try {
			CachedNode limitCondition = null;

			if (sequencingNode != null) {
				limitCondition = sequencingNode.selectSingleChildNode("limitConditions");
			}

			/*
			 * limitConditions の element がない場合、もしくは
			 * limitConditions の element があっても attemptLimitの attributeがない場合は
			 * 制限なしとする
			 */
			if (limitCondition == null) {
				return null;
			}

			CachedNode durationNode = limitCondition.selectSingleChildNode("@" + limitType);

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

			return new Duration(durationNode.getNodeValue());
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("getDurationLimit failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * 時間の制限を取得する<BR>
	 * @param dateTimeType beginTimeLimit/endTimeLimit<BR>
	 * @return 取得した文字列をカレンダー型に変換したもの<BR>
	 */
	public Calendar getDateTimeLimit(String dateTimeType) throws SystemException {
		try {
			CachedNode limitCondition = null;

			if (sequencingNode != null) {
				limitCondition = sequencingNode.selectSingleChildNode("limitConditions");
			}

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

			CachedNode dateTimeNode = limitCondition.selectSingleChildNode("@" + dateTimeType);

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

			return DateUtility.toCalendar(dateTimeNode.getNodeValue());
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("getDateTimeLimit failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * auxiliary resourceを取得する<BR>
	 * @return	AuxiliaryResourceの配列<BR>
	 * 			auxilairy resource がない場合は　null を返す<BR>
	 */
	public AuxiliaryResource[] getAuxiliaryResources() throws SystemException {

		try {
			if (sequencingNode == null) {
				return null;
			}

			/*
			 * auxiliary resource を取得する
			 */
			CachedNode[] auxiliaryResourceNodes =
				sequencingNode.selectSimpleNodeList("auxiliaryResources/auxiliaryResource");

			if ((auxiliaryResourceNodes == null) || (auxiliaryResourceNodes.length <= 0)) {
				return null;
			}

			//配列を確保
			AuxiliaryResource[] auxiliaryResources = new AuxiliaryResource[auxiliaryResourceNodes.length];

			for (int i = 0; i < auxiliaryResourceNodes.length; i++) {
				auxiliaryResources[i] =
					new AuxiliaryResource(
						auxiliaryResourceNodes[i].selectSingleChildNode("@auxiliaryResourceID").getNodeValue(),
						auxiliaryResourceNodes[i].selectSingleChildNode("@purpose").getNodeValue());
			}

			return auxiliaryResources;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getAuxiliaryResources failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * randomization controls の selection timing を取得する<BR>
	 * @return String selectionTimingの文字列<BR>
	 */
	public String getSelectionTiming() throws SystemException {
		try {
			CachedNode randomizationControl = null;

			if (sequencingNode != null) {
				randomizationControl = sequencingNode.selectSingleChildNode("randomizationControls");
			}

			/*
			 * randomization control の elementがない場合、または
			 * randomization control があっても、 selection timing の attribute が
			 * ない場合はデフォルトを使う。それ以外はattribute の値を返す
			 */
			if (randomizationControl == null) {
				return Default.getValue("randomizationControls/@selectionTiming");
			}

			CachedNode timingNode = randomizationControl.selectSingleChildNode("@selectionTiming");
			if (timingNode == null) {
				return Default.getValue("randomizationControls/@selectionTiming");
			}

			return timingNode.getNodeValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("getSelectionTiming failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * randomization controls の randomization timing を取得する
	 * 
	 * @return String randomizationTimingの文字列
	 */
	public String getRandomizationTiming() throws SystemException {
		try {
			CachedNode randomizationControl = null;

			if (sequencingNode != null) {
				randomizationControl = sequencingNode.selectSingleChildNode("randomizationControls");
			}

			/*
			 * randomization control の element がない場合、または
			 * randomization control の element があっても、randomization timing の
			 * attributeがない場合は デフォルトの値を返す
			 * それ以外は、attribute の値を返す
			 */
			if (randomizationControl == null) {
				return Default.getValue("randomizationControls/@randomizationTiming");
			}

			CachedNode timingNode = randomizationControl.selectSingleChildNode("@randomizationTiming");

			if (timingNode == null) {
				return Default.getValue("randomizationControls/@randomizationTiming");
			}

			return timingNode.getNodeValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getRandomizationTiming failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * randomization controls の randomization timing を取得する
	 * @return String randomizationTimingの文字列
	 */
	public boolean getReorderChildren() throws SystemException {
		try {
			CachedNode randomizationControl = null;

			if (sequencingNode != null) {
				randomizationControl = sequencingNode.selectSingleChildNode("randomizationControls");
			}

			/*
			 * randomization control の element がない場合、または
			 * randomization control の element があっても、randomization timing の
			 * attribute がない場合はデフォルトの値をかえす
			 * それ以外は、attributeの値を返す
			 */
			if (randomizationControl == null) {
				return Boolean.valueOf(Default.getValue("randomizationControls/@reorderChildren")).booleanValue();
			}

			CachedNode reOrderNode = randomizationControl.selectSingleChildNode("@reorderChildren");
			if (reOrderNode == null) {
				return Boolean.valueOf(Default.getValue("randomizationControls/@reorderChildren")).booleanValue();
			}

			return Boolean.valueOf(reOrderNode.getNodeValue()).booleanValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException("getReorderChildren failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * randomization controls の randomization timing を取得する<BR>
	 *
	 * @return randomizationTimingの文字列。select count がない場合は null を返す<BR>
	 */
	public Integer getSelectCount() throws SystemException {
		try {
			CachedNode randomizationControl = null;

			if (sequencingNode != null) {
				randomizationControl = sequencingNode.selectSingleChildNode("randomizationControls");
			}

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

			CachedNode countNode = randomizationControl.selectSingleChildNode("@selectCount");
			if (countNode == null) {
				return null;
			}

			return new Integer(countNode.getNodeValue());
		}
		catch (Exception ex) {
			throw new SystemException("getSelectCount failed [ActivityID:" + activity.getIdentifier() + "]", ex);
		}
	}

	/**
	 * delivery control の 属性値を取得する<BR>
	 * @param deliveryAttribute 属性値を指定する。
	 * @return  true 有効<BR>
	 *          false 無効<BR>
	 */
	public boolean getDeliveryControlFlag(String deliveryAttribute) throws SystemException {
		try {
			CachedNode deliveryControl = null;

			if (sequencingNode != null) {
				deliveryControl = this.sequencingNode.selectSingleChildNode("deliveryControls");
			}

			if (deliveryControl == null) {
				return Boolean.valueOf(Default.getValue("deliveryControls/@" + deliveryAttribute)).booleanValue();
			}

			CachedNode node = deliveryControl.selectSingleChildNode("@" + deliveryAttribute);

			if (node == null) {
				return Boolean.valueOf(Default.getValue("deliveryControls/@" + deliveryAttribute)).booleanValue();
			}

			return Boolean.valueOf(node.getNodeValue()).booleanValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getDeliveryControlFlag failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * limit conditions の 属性値を取得する<BR>
	 * @param limitAttribute limitConditions属性値を指定する。
	 * @return 取得できた値を文字列で返却する。属性が存在しない場合はnullを返却する。
	 */
	public String getLimitConditionString(String limitAttribute) throws SystemException {
		try {
			CachedNode limitCondition = null;

			if (sequencingNode != null) {
				limitCondition = sequencingNode.selectSingleChildNode("limitConditions");
			}

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

			CachedNode attribute = limitCondition.selectSingleChildNode("@" + limitAttribute);

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

			return attribute.getNodeValue();
		}
		catch (Exception ex) {
			throw new SystemException(
				"getLimitConditionString failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * constrained choice considerations の属性値を取得する<BR>
	 *
	 * @param attribute constrainedChoiceConsiderations属性値を指定する。
	 * @return trueもしくはfalseを返却する
	 */
	public boolean getConstrainedChoiceConsiderationFlag(String attribute) throws SystemException {
		try {
			CachedNode constrainedChoiceConsiderations = null;

			if (sequencingNode != null) {
				constrainedChoiceConsiderations =
					sequencingNode.selectSingleChildNode("constrainedChoiceConsiderations");
			}

			if (constrainedChoiceConsiderations == null) {
				return Boolean
					.valueOf(Default.getValue("constrainedChoiceConsiderations/@" + attribute))
					.booleanValue();
			}

			CachedNode node = constrainedChoiceConsiderations.selectSingleChildNode("@" + attribute);

			if (node == null) {
				return Boolean
					.valueOf(Default.getValue("constrainedChoiceConsiderations/@" + attribute))
					.booleanValue();
			}

			return Boolean.valueOf(node.getNodeValue()).booleanValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getConstrainedChoiceConsiderationFlag failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * rollup consideration の属性値を取得する<BR>
	 *
	 * @param attribute rollupConsiderations属性値を指定する。
	 * @return 取得できた値を返却する
	 */
	public String getRollupConsiderationValue(String attribute) throws SystemException {
		try {
			CachedNode rollupConsiderations = null;

			if (sequencingNode != null) {
				rollupConsiderations = sequencingNode.selectSingleChildNode("rollupConsiderations");
			}

			if (rollupConsiderations == null) {
				return Default.getValue("rollupConsiderations/@" + attribute);
			}

			CachedNode node = rollupConsiderations.selectSingleChildNode("@" + attribute);

			if (node == null) {
				return Default.getValue("rollupConsiderations/@" + attribute);
			}

			return node.getNodeValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getRollupConsiderationValue failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * rollup に使われる objective を取得する<BR>
	 *
	 * @return primary objective ( ロールアップに使われるオブジェクティブ)<BR>
	 */
	public LocalObjective getObjectiveContributeToRollup() {
		return localObjective[0];
	}

	/**
	 * アクティビティのオブジェクティブを返す<BR>
	 *
	 * @return アクティビティのオブジェクティブを返す<BR>
	 */
	public LocalObjective[] getObjective() {
		return localObjective;
	}

	/**
	 * objective id を指定して、 local objective を取得<BR>
	 * @param objectiveID objective id <BR>
	 * @return objective id を持つ local objective<BR>
	 */
	public LocalObjective getObjective(String objectiveID) throws SystemException {
		for (int i = 0; i < localObjective.length; i++) {
			if (objectiveID.equals(localObjective[i].getObjectiveID())) {
				return localObjective[i];
			}
		}

		throw new SystemException(
			"getObjective failed [ActivityID:" + activity.getIdentifier() + "][ObjectiveID:" + objectiveID + "]");
	}

	/**
	 * ロールアップに参加するか否かのフラグを参照する<BR>
	 *
	 * @return boolean ロールアップに参加するか否かのフラグ<BR>
	 *   ( true: ロールアップに参加する / false; ロールアップに参加しない )<BR>
	 */
	public boolean getRollupObjectiveSatisfiedFlag() throws SystemException {
		try {
			CachedNode rollupRules = null;

			if (sequencingNode != null) {
				rollupRules = sequencingNode.selectSingleChildNode("rollupRules");
			}

			/*
			 *  rollup rules の node がなければ、デフォルトから取得する
			 */
			if (rollupRules == null) {
				return Boolean.valueOf(Default.getValue("rollupRules/@rollupObjectiveSatisfied")).booleanValue();
			}

			/*
			 *  rollup rules の node があっても、 attribute がなければ、デフォルトを取得する
			 */
			CachedNode satisfiedNode = rollupRules.selectSingleChildNode("@rollupObjectiveSatisfied");
			if (satisfiedNode == null) {
				return Boolean.valueOf(Default.getValue("rollupRules/@rollupObjectiveSatisfied")).booleanValue();
			}

			/*
			 * それ以外は attribute から取得する
			 */
			return Boolean.valueOf(satisfiedNode.getNodeValue()).booleanValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getRollupObjectiveSatisfiedFlag failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * ロールアップに参加するか否かのフラグを参照する<BR>
	 *
	 * @return boolean ロールアップに参加するか否かのフラグ<BR>
	 *   ( true: ロールアップに参加する/ false: ロールアップに参加しない )<BR>
	 */
	public boolean getRollupProgressCompletionFlag() throws SystemException {
		try {
			CachedNode rollupRules = null;

			if (sequencingNode != null) {
				rollupRules = sequencingNode.selectSingleChildNode("rollupRules");
			}

			/*
			 *  rollup Rules の node がなければ、デフォルトから取得
			 */
			if (rollupRules == null) {
				return Boolean.valueOf(Default.getValue("rollupRules/@rollupProgressCompletion")).booleanValue();
			}

			/*
			 *  rollup rules の node があっても、 attribute がなければ、
			 *  デフォルトから取得
			 */
			CachedNode completionNode = rollupRules.selectSingleChildNode("@rollupProgressCompletion");

			if (completionNode == null) {
				return Boolean.valueOf(Default.getValue("rollupRules/@rollupProgressCompletion")).booleanValue();
			}

			/*
			 *  それ以外は attribute の値を取得し、返す
			 */
			return Boolean.valueOf(completionNode.getNodeValue()).booleanValue();
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getRollupProgressCompletionFlag failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * ロールアップ時の重み付けを行う<BR>
	 * 
	 * @return BigDecimal 重みを示す<BR>
	 */
	public BigDecimal getRollupObjectiveMeasureWeight() throws SystemException {
		try {
			CachedNode rollupRules = null;

			if (sequencingNode != null) {
				rollupRules = sequencingNode.selectSingleChildNode("rollupRules");
			}

			/*
			 *  rollup rules の node がなければ デフォルトから取得する
			 */
			if (rollupRules == null) {
				return new BigDecimal(Default.getValue("rollupRules/@objectiveMeasureWeight")).setScale(
					4,
					BigDecimal.ROUND_HALF_UP);
			}

			/*
			 *  rollup rules の node があっても, objective measure weight の attribute がなければ
			 *  デフォルトから取得
			 */
			CachedNode weightNode = rollupRules.selectSingleChildNode("@objectiveMeasureWeight");

			if (weightNode == null) {
				return new BigDecimal(Default.getValue("rollupRules/@objectiveMeasureWeight")).setScale(
					4,
					BigDecimal.ROUND_HALF_UP);
			}

			/*
			 *  値がある場合はその値を返す
			 */
			return new BigDecimal(weightNode.getNodeValue()).setScale(4, BigDecimal.ROUND_HALF_UP);
		}
		catch (SystemException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new SystemException(
				"getRollupObjectiveMeasureWeight failed [ActivityID:" + activity.getIdentifier() + "]",
				ex);
		}
	}

	/**
	 * プライマリオブジェクティブのObjective Progress Statusを取得する
	 *
	 * @return プライマリオブジェクティブのObjective Progress Status
	 */
	public boolean getObjectiveProgressStatus() throws SystemException {
		return getObjectiveContributeToRollup().getObjectiveProgressStatus();
	}

	/**
	 * プライマリオブジェクティブのObjective Satisfied Statusを取得する
	 *
	 * @return プライマリオブジェクティブのObjective Satisfied Status
	 */
	public boolean getObjectiveSatisfiedStatus() throws SystemException {
		return getObjectiveContributeToRollup().getObjectiveSatisfiedStatus();
	}

	/**
	 * プライマリオブジェクティブのObjective Measure Statusを取得する
	 *
	 * @return プライマリオブジェクティブのObjective Measure Status
	 */
	public boolean getObjectiveMeasureStatus() throws SystemException {
		return getObjectiveContributeToRollup().getObjectiveMeasureStatus();
	}

	/**
	 * プライマリオブジェクティブのObjective Normalized Measure取得する
	 *
	 * @return プライマリオブジェクティブのObjective Normalized Measure
	 */
	public BigDecimal getObjectiveNormalizedMeasure() throws SystemException {
		return getObjectiveContributeToRollup().getObjectiveNormalizedMeasure();
	}

	/**
	 * Activity Progress Statusを設定する
	 *
	 * @param status Activity Progress Status
	 */
	public void setActivityProgressStatus(boolean status) {
		progress.setActivityProgressStatus(status);
	}

	/**
	 * Activity Progress Statusを取得する
	 *
	 * @return Activity Progress Status
	 */
	public boolean getActivityProgressStatus() throws SystemException {
		return progress.getActivityProgressStatus();
	}

	/**
	 * Activity Absolute Durationを設定する
	 *
	 * @param duration Activity Absolute Duration
	 */
	public void setActivityAbsoluteDuration(Duration duration) throws SystemException {
		progress.setActivityAbsoluteDuration(duration);
	}

	/**
	 * Activity Absolute Durationを取得する
	 *
	 * @return Activity Absolute Duration
	 */
	public Duration getActivityAbsoluteDuration() throws SystemException {
		return progress.getActivityAbsoluteDuration();
	}

	/**
	 * Activity Experienced Durationを設定する
	 *
	 * @param duration Activity Experienced Duration
	 */
	public void setActivityExperiencedDuration(Duration duration) throws SystemException {
		progress.setActivityExperiencedDuration(duration);
	}

	/**
	 * Activity Experienced Durationを取得する
	 *
	 * @return Activity Experienced Duration
	 */
	public Duration getActivityExperiencedDuration() throws SystemException {
		return progress.getActivityExperiencedDuration();
	}

	/**
	 * Activity Attempt Countを設定する
	 *
	 * @param count Activity Attempt Count
	 */
	public void setActivityAttemptCount(int count) {
		progress.setActivityAttemptCount(count);
	}

	/**
	 * Activity Attempt Countを取得する
	 *
	 * @return Activity Attempt Count
	 */
	public int getActivityAttemptCount() {
		return progress.getActivityAttemptCount();
	}

	/**
	 * Attempt Progress Statusを設定する
	 *
	 * @param status Attempt Progress Status
	 */
	public void setAttemptProgressStatus(boolean status) throws SystemException {
		progress.setAttemptProgressStatus(status);
	}

	/**
	 * Attempt Progress Statusを取得する
	 *
	 * @return Attempt Progress Status
	 */
	public boolean getAttemptProgressStatus() throws SystemException {
		return progress.getAttemptProgressStatus();
	}

	/**
	 * Attempt Progress Statusを取得する
	 *
	 * @return Attempt Progress Status
	 */
	public boolean getAttemptProgressStatus(boolean isCurrent) throws SystemException {
		return progress.getAttemptProgressStatus(isCurrent);
	}

	/**
	 * Attempt Completion Amountを設定する
	 *
	 * @param amount Attempt Completion Amount
	 */
	public void setAttemptCompletionAmount(BigDecimal amount) throws SystemException {
		progress.setAttemptCompletionAmount(amount);
	}

	/**
	 * Attempt Completion Amountを取得する
	 *
	 * @return Attempt Completion Amount
	 */
	public BigDecimal getAttemptCompletionAmount() throws SystemException {
		return progress.getAttemptCompletionAmount();
	}

	/**
	 * Attempt Completion Statusを設定する
	 *
	 * @param status Attempt Completion Status
	 */
	public void setAttemptCompletionStatus(boolean status) throws SystemException {
		progress.setAttemptCompletionStatus(status);
	}

	/**
	 * Attempt Completion Statusを取得する
	 *
	 * @return Attempt Completion Status
	 */
	public boolean getAttemptCompletionStatus() throws SystemException {
		return progress.getAttemptCompletionStatus();
	}

	/**
	 * Attempt Absolute Durationを設定する
	 *
	 * @param duration Attempt Absolute Duration
	 */
	public void setAttemptAbsoluteDuration(Duration duration) {
		progress.setAttemptAbsoluteDuration(duration);
	}

	/**
	 * Attempt Absolute Durationを取得する
	 *
	 * @return Attempt Absolute Duration
	 */
	public Duration getAttemptAbsoluteDuration() {
		return progress.getAttemptAbsoluteDuration();
	}

	/**
	 * Attempt Experienced Durationを設定する
	 *
	 * @param duration Attempt Experienced Duration
	 */
	public void setAttemptExperiencedDuration(Duration duration) {
		progress.setAttemptExperiencedDuration(duration);
	}

	/**
	 * Attempt Experienced Durationを取得する
	 *
	 * @return Attempt Experienced Duration
	 */
	public Duration getAttemptExperiencedDuration() {
		return progress.getAttemptExperiencedDuration();
	}

	/**
	 * useCurrentAttempt を取得する
	 *
	 * @return IsAttemptedInCurrentAttempt
	 */
	public boolean getUseCurrentAttempt(){
		return IsAttemptedInCurrentAttempt;
	}

	/**
	 * useCurrentAttempt をセットする
	 *
	 * @return なし
	 */
	public void setUseCurrentAttempt(boolean tmpflag){
		IsAttemptedInCurrentAttempt = tmpflag;
	}
}
