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

// 
//	機能名称	
//		CMI-CBT communication module     
//
//		変更履歴	
//			2003.12.01 gxl 新規作成
//

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

//import java.util.StringTokenizer;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;

/**
 * CMI データモデル　lesson クラス<br>
 * @author gxl
 */
public class CMILesson extends CMIObjBase {
	
	/**  
	 * CMIError オブジェクト
	 */
	private CMIError m_lastError = null;

	/**  
	 * CMIEvaluationData オブジェクト
	 */
    private CMIEvaluationData m_evaluationData = null;
	
	/**
	 * CMILesson クラスの初期処理。<br>
	 */
	public CMILesson() {
		// オープン状態フラグを初期化(未オープンに)する
		m_isOpen = false;	
		// プロトタイプデータをセットする
		m_proto	= CMIElementProto.g_lessonProto;
		// 子データモデルを生成する
		makeChildren(CMIElementProto.g_lessonProto);		
	}

	/**
	 * CMILesson クラスの初期処理。<br>
	 * @param cbt CBTオブジェクト
	 */
	public CMILesson(CMICBT  cbt) {
		// オープン状態フラグを(未オープンに)初期化する
		m_isOpen = false;	
		// プロトタイプデータをセットする
		m_proto	= CMIElementProto.g_lessonProto;
		// CBTオブジェクトをセットする
		m_CBT = cbt;						
		// 上記は、makeChildren より前にセットする必要がある。
		
		// 子データモデルを生成する
		makeChildren(CMIElementProto.g_lessonProto);	
	}
	
	/**
	 * CMILesson CMILesson クラスの終了処理。<br>
	 */
	public void finalize() {
		m_lastError = null;
		m_evaluationData = null;
	}
	
	/**
	 * セッションを開始する。<br>
	 * @return String CBT起動URL
	 */
	public String open() {
		// CBTオブジェクトのオープンをコールする
		return m_CBT.open();			
	}
	
	/**
	 * セッションを終了する。<br>
	 */
	public void close() {
		// CBTオブジェクトのクローズをコールする
		m_CBT.close();			
	}
	
	/**
	 * 受信データを解析し、コマンドに対応する処理を実行する。<br>
	 * <p>その応答文字列をresponceに返却する。
	 * 戻り値としてコマンド名(文字列)を返す。
	 * @param request リクエスト
	 * @return String HACPコマンド名(文字列)
	 */
	public String hacp(String request) {
		String strRes = "";
		// CBTオブジェクトのHACPをコールする
		strRes = m_CBT.hacp(request);
		// HACPコマンド名を返す
		return strRes;
	}
	
	/**	
	* (LocalValueに)値を設定処理<br>
	* @param name　String エレメントパス
	* @param value　String 値
	*/
	public void setValue(String name, String value) {
		// エラー情報をクリアする
		m_CBT.clearError();
		if (!m_isOpen) {
			// オープン状態ならば、指定パスのオブジェクトに値をセットする
			CMIParameterPath param = new CMIParameterPath(name);
			String strDataModelName	= param.popFirstElement();
			String strCategory			= param.getNthElement(1);

			localSetValue(param.getPath(), value);
		} else {
			// 未オープン状態ならば、初期化エラーをセットする
			setError(CMIError.ERR_NOT_INITALIZED);
			setVendorError(getVendorData(("CMILesson.setValue"), this));
		}
	}
	
	/**			
	* (LessonValueの)値を取得処理<br>		
	* @param name　String エレメントパス		
	* @return String  パラメータの値（文字列）		
	*/
	public String getValue(String name) {
		// エラー情報をクリアする
		m_CBT.clearError();
		// 指定パスのオブジェクトの値を取得する
		String strRet = null;
		CMIParameterPath param = new CMIParameterPath(name);
		String strDataModelName	= param.popFirstElement();
		String strCategory			= param.getNthElement(1);

		strRet = localGetValue(param.getPath());
		// 取得した値を返す
		return strRet;
	}
	
	/**			
	* 保存データを設定処理<br>		
	* @param valueStr String 保存データ
	*/	
	public void setAllValue(String valueStr) {
		// CBTオブジェクトのsetAllValue をコールする
		m_CBT.setAllValue(valueStr);
	}
	
	/**		
	* 保存データを取得処理<br>	
	* @return String  保存データ（文字列）	
	*/	
	public String getAllValue() {
		// CBTオブジェクトのgetAllValue をコールする
		return m_CBT.getAllValue();
	}
	
	/**			
	* 最後に発生したエラーコードを取得処理<br>		
	* @return int  エラーコード		
	*/	
	public int getLastError() {
		// CBTオブジェクトのgetLastError をコールする
		return m_CBT.getLastError();
	}
	
	/**	
	* エラーメッセージを取得処理<br>
	* @param errcode エラーコード
	* @return String エラーメッセージ
	*/
	public String getErrorString(int errcode) {
		// CBTオブジェクトのgetErrorString をコールする
		return m_CBT.getErrorString(errcode);
	}
	
	/**		
	* 自オブジェクトのオープン処理を実行処理<br>	
	*/	
	public void myOpen() {
		// オープン状態フラグをオープン済みにする
		m_isOpen = true;	
	}
	
	/**			
	* 自オブジェクトのクローズ処理を実行処理<br>		
	*/		
	public void myClose() {
		// オープン状態フラグを未オープンにする
		m_isOpen = false;		
	}
	
	/**		
	* CMIEvaluationDataクラスを設定処理<br>	
	* @param evalDataObj　CMIEvaluationData	
	*/	
	public void setEvaluationData(CMIEvaluationData evalDataObj) {
		m_evaluationData = evalDataObj;
	}
	
	/**		
	* AICCデータをPutParam のデータとして処理<br>	
	* @param aiccData　AICC データ	
	* @param response　レスポンス(未使用)	
	* @return int  エラーコード	
	*/	
	public int doPutParam(String aiccData, StringBuffer response) {
		
		int intErrCode = HACPError.HACP_ERR_SUCCESSFUL;
		String strLineDelimiter = CMIUtil.checkLineDelimiter(aiccData);
		CMITokenizer tokenizer = new CMITokenizer(aiccData, strLineDelimiter);
		String strLine = null;
		String strGroupName = null;
		StringBuffer data = new StringBuffer();

		// 必須項目チェックフラグを初期化
		// Core
		boolean blnCore			= false;
		// Core_Lesson
		boolean blnCoreLesson	= false;

		// PutParamを受信したので、更新フラグをfalseに設定する
		m_CBT.updateFlag(false);

		// AICCデータを１行ずつ処理し、AICCデータがある間繰り返す
		while (tokenizer.hasMoreTokens()){
			// カレント行を取得する
			strLine = tokenizer.nextToken();
			// カレント行がコメント行 かつ 最終行でなければ、次の行を処理する
			if (CMIUtil.isComment(strLine) && tokenizer.hasMoreTokens()) {
				continue;
			}
			// カレント行がグループ名 または 最終行ならば、データバッファを処理する
			if (CMIUtil.isGroup(strLine) || !tokenizer.hasMoreTokens()) {
				// 最終行 かつ コメント行でなければ、カレント行をデータバッファにストックする
				if (!tokenizer.hasMoreTokens() && !CMIUtil.isComment(strLine)) {
					data.append(STR_CRLF).append(strLine);
				}
				// グループ名を保持しているならば、データバッファを処理する
				if ( strGroupName != null && !"".equals(strGroupName)) {
					// グループ名を小文字に変換する
					String keyWord = strGroupName.toLowerCase();
					// グループ名が必須項目ならば、確認フラグを確認済みにセットする
					if ("core".equals(keyWord)) {
						blnCore = true;	
					} else if ("core_lesson".equals(keyWord)) {
						blnCoreLesson = true;	
					}

					// 子オブジェクトからグループ名に該当するオブジェクトを取得する
					CMIElement child = getChildByKeyWord(strGroupName);
					if ( child != null ) {
						// 子オブジェクトが取得できたならば、子オブジェクトのputParam をコールし、
						// データを処理する
						child.putParam(strGroupName, data.toString());
						//データバッファを空にする
						data.setLength(0);
					} else {
						// 子オブジェクトが取得できないならば、エラーをセットする
						// warning
						if (getError() == CMIError.ERR_NO_ERROR) {
							setError(CMIError.WARNING_INVALID_GROUPNAME);
							setVendorError("Invalid GroupName[" + strGroupName + "]");
						}
						data.setLength(0);
					}
				}
				// カレント行からグループ名を取得し、保持する
				strGroupName = CMIUtil.getGroupName(strLine);

			// カレント行がグループ名 または 最終行でなければ、カレント行をデータバッファにストックする
			} else {
				if (data.length() != 0) {
					// データバッファが空でなければ、改行コード＋カレント行をデータバッファに連結する
					data.append(STR_CRLF).append(strLine);
				} else {
					// データバッファが空ならば、カレント行をデータバッファにセットする
					data.append(strLine);
				}
			}
		}
		// 必須グループ名の最終チェック
		int intECode = getError();
		if ( intECode == CMIError.ERR_NO_ERROR ) {
			// Core
			if ( !blnCore ) {
				setError(CMIError.WARNING_INSUFFICIENT_GROUPNAME);
				setVendorError("Insufficient GroupName[Core]");
			}
			// Core_Lesson
			else if (!blnCoreLesson) {
				setError(CMIError.WARNING_INSUFFICIENT_GROUPNAME);
				setVendorError("Insufficient GroupName[Core_Lesson]");
			}
		}
		return intErrCode;
	}

	/**	
	* GetParam コマンドの処理<br>
	* @param aiccData　AICC データ(未使用)
	* @param response　レスポンス
	* @return int  エラーコード
	*/
	public int doGetParam(String aiccData, StringBuffer response) {
		int intErr = HACPError.HACP_ERR_SUCCESSFUL;
		// 子エレメントの数を取得する
		int intChildCount = getChildCount();			
		// 子オブジェクトの数だけ繰り返す
		for ( int i = 0; i < intChildCount; i++ ) {
			// 子エレメントを取得する
			CMIElement child = getChild(i);		
			if ( child != null ) {
				// 子エレメントからパラメータ情報を取得する
				intErr = child.getParam(response);		
				if ( intErr != HACPError.HACP_ERR_SUCCESSFUL) {
					break;
				}
			}
		}
		// エラーコードを返す
		return intErr;					
	}
	
	/**			
	* PutObjectives コマンドの処理<br>		
	*		
	* @param aiccData　AICC データ(未使用)		
	* @param response　レスポンス(未使用)		
	* @return int  エラーコード		
	*/	
	public int doPutObjectives(String aiccData, StringBuffer response) {
		// エラーコード(成功)を返す
		return HACPError.HACP_ERR_SUCCESSFUL;
	}

	/**			
	* ExitAU コマンドの処理<br>		
	* @param aiccData　AICC データ(未使用)		
	* @param response　レスポンス(未使用)		
	* @return int  エラーコード		
	*/		
	public int doExitAU(String aiccData, StringBuffer response) {
		
		int intErrCode = HACPError.HACP_ERR_SUCCESSFUL;
		String strGrpName="core";
		String strSubPath="lesson_status";
		// 子オブジェクトからグループ名に該当するオブジェクトを取得する
		CMIElement child = getChildByKeyWord(strGrpName);
		if ( child != null ) {
			//　[lesson_status]値を取得する。
			String strValue = child.getLocalValue(strSubPath);
			// version を取得する。
			int	intVersion = getVersion();
			// 値を小文字に転換
			if ( strValue != null ) {
				strValue = strValue.toLowerCase();
			}
			if ( intVersion>=3500 ) {
				// cmi.core.lesson_status 値が"" or "not attempted"の場合
				// "completed"を設定する。 
				if ( strValue != null && ("".equals(strValue) || 
											strValue.startsWith("n") )) {
					child.setLessonValue(strSubPath,"completed");
					child.setLocalValue(strSubPath,"completed");
					// レッスンステータスを更新したので、更新フラグを立てる
					m_CBT.updateFlag(true);
				}
			}
		}
		return intErrCode;	
	}
	
	/**	
	* ExitAU コマンドの処理<br>
	* @param aiccData　AICC データ
	* @return int  エラーコード
	*/
	public int doGetPutParam(StringBuffer aiccData) {
		int intErr = HACPError.HACP_ERR_SUCCESSFUL;		
		// 子エレメントの数を取得する
		int intChildCount = getChildCount();			
		// 子オブジェクトの数だけ繰り返す
		for ( int i = 0; i< intChildCount; i++ ) {
			// 子エレメントを取得する
			CMIElement child = getChild(i);		
			if ( child != null ) {
				// 子エレメントからパラメータ情報を取得する
				intErr = child.getPutParam(aiccData);		
				if ( intErr != HACPError.HACP_ERR_SUCCESSFUL ) {
					break;
				}
			}
		}
		// エラーコードを返す
		return intErr;					
	}

	/**
	 * GetLmsCommentsコマンド実行処理<br>
	 * 内部メモリへ保持しているComment from LMSデータをHACPフォーマットで
	 * 取得する。<br>
	 * @param scoData StringBuffer 取得したComments from LMSデータ
	 * @return 　int  HACPエラーコード	
	 */	
	public int doGetLmsComments(String scoData, StringBuffer response) {
		
		int intErrCode = HACPError.HACP_ERR_SUCCESSFUL;

		//	"cmi.comments_from_lms"
		String strSubPath = "comments_from_lms";
		CMINode lms_comments_Node = (CMINode)m_CBT.getChildByKeyWord(strSubPath);

		// Comment from LMSデータの件数を取得する
		//	"comments_from_lms._count"
		int intNum = lms_comments_Node.getChildCount();

		// タイトル行を作成する
		String strTitleLine = "\"Date\",\"Location\",\"Comment\"\n";
		response.append(strTitleLine);

		// SCORM1.3のLmsCommentデータを１行ずつ処理し、AICCデータフォーマットを作成する
		for(int i = 0; i < intNum; i++){

			String strValue = null;

			try {
				// タイトルに相当するエレメントのパスを生成し、そのエレメントに
				// データをセットする
				//date_time
				strSubPath = String.valueOf(i) + ".date_time";
				strValue = lms_comments_Node.getLocalValue(strSubPath);
				if (strValue == null) {
					strValue = "";
				}
				// 文字コードをUTF-8にエンコードする
				strValue = URLEncoder.encode(strValue,"UTF-8");
				response.append("\"" + strValue + "\",");
	
				//location
				strSubPath = String.valueOf(i) + ".location";
				strValue = lms_comments_Node.getLocalValue(strSubPath);
				if (strValue == null) {
					strValue = "";
				}
				// 文字コードをUTF-8にエンコードする
				strValue = URLEncoder.encode(strValue,"UTF-8");
				response.append("\"" + strValue + "\",");
	
				//comment
				strSubPath = String.valueOf(i) + ".comment";
				strValue = lms_comments_Node.getLocalValue(strSubPath);
				if (strValue == null) {
					strValue = "";
				}
				// 文字コードをUTF-8にエンコードする
				strValue = URLEncoder.encode(strValue,"UTF-8");
				response.append("\"" + strValue + "\"" + CMIElement.STR_CRLF);

				// 取得データ全体をエンコードする
				strValue = URLEncoder.encode(response.toString(),"UTF-8");
				response.delete(0,response.length());
				response.append(strValue);
			}
			catch(UnsupportedEncodingException ex) {
				intErrCode = HACPError.HACP_ERR_CMI;
				setError(CMIError.ERR_INVALID_AURGUMENT_ERROR);
				setVendorError(getVendorData( new String("CMILesson.getLmsComments"), this));
				break;
			}

		}
		// エラーコード(成功)を返す
		return intErrCode;	
	}

	/**
	 * PutParamコマンド実行処理<br>
	 * PutParamコマンドで受信したHACPフォーマットのSCORM1.3データを内部メモリへ
	 * 保管する。内部メモリに保管後、その保管データを取り出す。<br>
	 * @param scoData SCORM1.3 データ	
	 * @param response 応答データ（未使用 I/F共通化のため実装）
	 * @return 　int  HACPエラーコード	
	 */	
	public int doPutParamToS13(String scoData, StringBuffer response) {
		int intErrCode = HACPError.HACP_ERR_SUCCESSFUL;
		String strLineDelimiter = CMIUtil.checkLineDelimiter(scoData);
		CMITokenizer tokenizer = new CMITokenizer(scoData, strLineDelimiter);
		String strLine = null;
		String strGroupName = null;
		StringBuffer data = new StringBuffer();

		// PutParamを受信したので、更新フラグをfalseに設定する
		m_CBT.updateFlag(false);

		// AICCデータを１行ずつ処理し、AICCデータがある間繰り返す
		while (tokenizer.hasMoreTokens()){
			// カレント行を取得する
			strLine = tokenizer.nextToken();
			// カレント行がコメント行 かつ 最終行でなければ、次の行を処理する
			if (CMIUtil.isComment(strLine) && tokenizer.hasMoreTokens()) {
				continue;
			}
			// カレント行がグループ名 または 最終行ならば、データバッファを処理する
			if (CMIUtil.isGroup(strLine) || !tokenizer.hasMoreTokens()) {
				// 最終行 かつ コメント行でなければ、カレント行をデータバッファにストックする
				if (!tokenizer.hasMoreTokens() && !CMIUtil.isComment(strLine)) {
					data.append(STR_CRLF).append(strLine);
				}
				// グループ名を保持しているならば、データバッファを処理する
				if ( strGroupName != null && !"".equals(strGroupName)) {
					// グループ名を小文字に変換する
					String keyWord = strGroupName.toLowerCase();

					// 子オブジェクトからグループ名に該当するオブジェクトを取得する
					CMIElement child = getChildByKeyWord(strGroupName);
					if ( child != null ) {
						// 子オブジェクトが取得できたならば、子オブジェクトのputParam をコールし、
						// データを処理する
						child.putParam(strGroupName, data.toString());
						//データバッファを空にする
						data.setLength(0);
					} else {
						// 子オブジェクトが取得できないならば、エラーをセットする
						// warning
						if (getError() == CMIError.ERR_NO_ERROR) {
							setError(CMIError.WARNING_INVALID_GROUPNAME);
							setVendorError("Invalid GroupName[" + strGroupName + "]");
						}
						data.setLength(0);
					}
				}
				// カレント行からグループ名を取得し、保持する
				strGroupName = CMIUtil.getGroupName(strLine);

			// カレント行がグループ名 または 最終行でなければ、カレント行をデータバッファにストックする
			} else {
				if (data.length() != 0) {
					// データバッファが空でなければ、改行コード＋カレント行をデータバッファに連結する
					data.append(STR_CRLF).append(strLine);
				} else {
					// データバッファが空ならば、カレント行をデータバッファにセットする
					data.append(strLine);
				}
			}
		}
		getPutParamToS13(response);
		return intErrCode;
	}

	/**
	 * 内部メモリへ保持しているPutParamコマンドで受信したHACP
	 * フォーマットのSCORM1.3データを取得する。<br>
	 * @param scoData String SCORM1.3 データ
	 * @param response String 取得したparameterデータ
	 * @return 　int  HACPエラーコード
	 */
	private int getPutParamToS13(StringBuffer response) {

		int intErrCode = HACPError.HACP_ERR_SUCCESSFUL;

		/**
		 * パラメータ情報のサブノードを取得する
		 */
		// Coreノード（cmi.core）を取得する
		String strValue;
		String strCompStatus;
		String strSuccessStatus;
		String strSubPath = "core";
		CMINode objCore = (CMINode)getNChild(strSubPath);
		response.append("[");
		response.append(objCore.getKeyWord());
		response.append("]");
		response.append(CMIElement.STR_CRLF);

		// Coreノード配下の各パラメータ情報を取得する
		// completion_threshold
		
		strSubPath = "completion_threshold";
		strValue = objCore.getLessonValue(strSubPath);
		if (strValue != null) {
			response.append("Completion_Threshold=");
			response.append(strValue);
			response.append(CMIElement.STR_CRLF);
		} 
		
		strSubPath = "progress_measure";
		strValue = objCore.getLessonValue(strSubPath);
		if (strValue != null) {
			response.append("Progress_Measure=");
			response.append(strValue);
			response.append(CMIElement.STR_CRLF);
		} 

		// Coreノード配下の各パラメータ情報を取得する
		// Lesson_Location
		strSubPath = "lesson_location";
		strValue = objCore.getLessonValue(strSubPath);
		if (strValue != null) {
			response.append("Lesson_Location=");
			response.append(strValue);
			response.append(CMIElement.STR_CRLF);
		}

		// Lesson_Status
		// (cmi.completion_status,cmi.success_status,cmi.entry)
		response.append("Lesson_Status=");

		//	"cmi.success_status"
		strSubPath = "lesson_status";
		strValue = objCore.getLessonValue(strSubPath);
		if (strValue != null) {
			response.append(strValue);
		}
		response.append(",");

		//	"cmi.completion_status"
		strSubPath = "completion_status";
		strValue = objCore.getLessonValue(strSubPath);
		if (strValue != null) {
			response.append(strValue);
		}
		response.append(",");

		//	"cmi.exit"
		strSubPath = "exit";
		strValue = objCore.getLessonValue(strSubPath);
		if (strValue != null) {
			response.append(strValue);
		}
		response.append(CMIElement.STR_CRLF);

		// Score
		// (cmi.score.scaled,cmi.score.raw,cmi.score.max,cmi.score.min)
		//	"cmi.score"
		strSubPath = "score";
		CMINode objScore = (CMINode)objCore.getChild(strSubPath);
		strValue = CMIUtil.getScoreValue(objScore);
		if ((strValue != null) && ("".equals(strValue) != true)) {
			response.append("Score=");
			response.append(strValue);
			response.append(CMIElement.STR_CRLF);
		}

		// Time
		// (cmi.session_time)
		strSubPath = "session_time";
		strValue = objCore.getLessonValue(strSubPath);
		if (strValue != null) {
			response.append("Time=");
			response.append(strValue);
			response.append(CMIElement.STR_CRLF);
		}

		// Core_Lesson（cmi.suspend_data）を取得する
		strSubPath = "suspend_data";
		CMIElement objCoreLesson = getChild(strSubPath);
		response.append("[");
		response.append(objCoreLesson.getKeyWord());
		response.append("]");
		response.append(CMIElement.STR_CRLF);

		strValue = objCoreLesson.getLessonValue();
		if (strValue != null) {
			response.append(CMIUtil.urlEncode(strValue));
		}
		response.append(CMIElement.STR_CRLF);

		// Objectives_Statusノード（cmi.objectives）を取得する
		CMIElement	child = null;
		strSubPath = "objectives";
		CMINode objObjectives = (CMINode)getNChild(strSubPath);
		if (objObjectives != null) {
			response.append("[");
			response.append(objObjectives.getKeyWord());
			response.append("]");
			response.append(CMIElement.STR_CRLF);

			// objectives配下の設定件数を取得する
			int intCount = objObjectives.getChildCount();
			for ( int i = 0; i < intCount; i++ ) {
				// 件数が０件より上である場合、インデックス0から順に子
				// (objectives.n)の項目を取得する
				CMINode	child_n = (CMINode)objObjectives.getChild(i);

				// J_ID.n
				// cmi.objectives.n.id
				strSubPath = "id";
				child = child_n.getChild(strSubPath);

				// ローカル値を取得する
				strValue = child.getLocalValue();

				// cmi.objectives.n.id項目のデータがNULL値、または
				// 空データ値の場合は、取得対象データに含めず、次(n+1)
				// のデータを取得するためにcontinueする。
				// （nに該当する"J_Score"、"J_Status"も取得データに
				// 含めない）
				if (strValue == null) {
					continue;
				} else {
					response.append("J_ID.");
					response.append(String.valueOf(i+1));
					response.append("=");
					response.append(strValue);
					response.append(CMIElement.STR_CRLF);
				}

				// J_Score.n
				// (cmi.objectives.n.score.scaled,cmi.objectives.n.score.raw,
				//  cmi.objectives.n.score.max,   cmi.objectives.n.score.min)
				strSubPath = "score";
				child = child_n.getChild(strSubPath);

				// スコア値を取得する
				strValue = CMIUtil.getScoreValue(child);
				if (strValue != null) {
					response.append("J_Score." + String.valueOf(i+1));
					response.append("=");
					response.append(strValue);
					response.append(CMIElement.STR_CRLF);
				}

				// J_Status.n
				// (cmi.objectives.n.success_status,
				strSubPath = "status";
				child = child_n.getChild(strSubPath);
				strSuccessStatus = child.getLessonValue();

				strSubPath = "completion_status";
				child = child_n.getChild(strSubPath);
				strCompStatus = child.getLessonValue();

				response.append("J_Status." + String.valueOf(i+1));
				response.append("=");
				if (strSuccessStatus != null) {
					response.append(strSuccessStatus);
				}
				if (strCompStatus != null) {
					response.append(",");
					response.append(strCompStatus);
				}
				response.append(CMIElement.STR_CRLF);
			}
		}

		// Student_Dataノード（cmi.student_data）を取得する
		strSubPath = "student_data";
		CMINode objStudentData = (CMINode)getNChild(strSubPath);
		if (objStudentData != null) {
			response.append("[");
			response.append(objStudentData.getKeyWord());
			response.append("]");
			response.append(CMIElement.STR_CRLF);

			// Mastery_Score
			// (cmi.scaled_passing_score)
			strSubPath = "mastery_score";
			// ローカル値を取得する
			strValue = objStudentData.getLocalValue(strSubPath);
			if (strValue != null) {
				// ローカル値がNULL以外の場合、PutParam用のデータ取得対象とする
				response.append("Mastery_Score");
				response.append("=");
				response.append(strValue);
				response.append(CMIElement.STR_CRLF);
			}

			// Max_Time_Allowed
			// (cmi.max_time_allowed)
			strSubPath = "max_time_allowed";
			// ローカル値を取得する
			strValue = objStudentData.getLocalValue(strSubPath);
			if (strValue != null) {
				// ローカル値がNULL以外の場合、PutParam用のデータ取得対象とする
				response.append("Max_Time_Allowed");
				response.append("=");
				response.append(strValue);
				response.append(CMIElement.STR_CRLF);
			}

			// Time_Limit_Action
			// (cmi.time_limit_action)
			strSubPath = "time_limit_action";
			// ローカル値を取得する
			strValue = objStudentData.getLocalValue(strSubPath);
			if (strValue != null) {
				// ローカル値がNULL以外の場合、PutParam用のデータ取得対象
				// とする
				response.append("Time_Limit_Action");
				response.append("=");
				response.append(strValue);
				response.append(CMIElement.STR_CRLF);
			}
		}

		// Student_Preferencesノード（cmi.student_preferences）を取得する
		strSubPath = "student_preference";
		CMINode objStudentPreference = (CMINode)getNChild(strSubPath);
		if (objStudentPreference != null) {
			response.append("[");
			response.append(objStudentPreference.getKeyWord());
			response.append("]");
			response.append(CMIElement.STR_CRLF);

			// Student_Preferenceグループに属する各項目のローカル値を取得する
			// Audio 取得
			// cmi.learner_preferences.audio
			strSubPath = "audio";
			strValue = objStudentPreference.getLocalValue(strSubPath);
			if (strValue != null) {
				response.append("Audio");
				response.append("=");
				response.append(strValue);
				response.append(CMIElement.STR_CRLF);
			}

			// Language 取得
			// cmi.learner_preferences.language
			strSubPath = "language";
			strValue = objStudentPreference.getLocalValue(strSubPath);
			if (strValue != null) {
				response.append("Language");
				response.append("=");
				response.append(strValue);
				response.append(CMIElement.STR_CRLF);
			}

			// Speed 取得
			// cmi.learner_preferences.speed
			strSubPath = "speed";
			strValue = objStudentPreference.getLocalValue(strSubPath);
			if (strValue != null) {
				response.append("Speed");
				response.append("=");
				response.append(strValue);
				response.append(CMIElement.STR_CRLF);
			}

			// Text 取得
			// cmi.learner_preferences.text
			strSubPath = "text";
			strValue = objStudentPreference.getLocalValue(strSubPath);
			if (strValue != null) {
				response.append("Text");
				response.append("=");
				response.append(CMIUtil.urlEncode(strValue));
				response.append(CMIElement.STR_CRLF);
			}
		}
		return intErrCode;
	}
}