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

//
// WBT個別ログ教材構造ストアクラス
// 変更履歴
// 2004.01.08 新規作成       浅田
//   機能
//   RDFNodeから個別ログ表示に使用する名称などノード情報のリストを生成する
//   -------------------------------------------
//   | Type  | UID     |  AU名 or セクション名 |
//   -------------------------------------------
//   | SE    | sc00001 | TOP > SEC1 > SEC1-1   |
//   | AU    | el00001 | AU名                  |
//   | AU    | el00002 | AU名                  |
//   | SE    | sc00002 | TOP > SEC1 > SEC1-2   |
//   | AU    | el00003 | AU名                  |
//   -------------------------------------------
package jp.co.ntt.lms.lo.WBT.IndividualLog;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.Map;

import jp.co.ntt.lms.Common.RDF.RDFNode;
import jp.co.ntt.lms.Common.RDF.RDFNodeList;

/**
 * WBT個別ログ教材構造リストクラス。<BR>
 * 個別ログメニューツリー表示からクリックされた以下のセクション/AUを
 * WBT個別ログに表示する順で、タイプ、UID、ノード名称をリストとして保持する。<BR>
 * @author  asada
 * @version 0.0.0.1 2004/01/08
 */
public class IndividualLogRDFNode implements IndividualLogPropertyDef {

	/**
	 * ノード種別：セクション。
	 */
	public static final String STATUS_S = "SE";

	/**
	 * ノード種別：ＡＵ。
	 */
	public static final String STATUS_A = "AU";

	/**
	 * nodeInfoListの要素となるノード情報マップのキー：ＵＩＤ。
	 * @see #nodeInfoList
	 */
	private static final String KEY_NODE_UID = "NODE_UID";

	/**
	 * nodeInfoListの要素となるノード情報マップのキー：ノード種別。
	 * @see #nodeInfoList
	 */
	private static final String KEY_NODE_TYPE = "NODE_TYPE";

	/**
	 * nodeInfoListの要素となるノード情報マップのキー：ノードタイトル。
	 * @see #nodeInfoList
	 */
	private static final String KEY_NODE_TITLE = "NODE_TITLE";

	/**
	 * ノード情報リスト。<br>
	 * 教材構造定義の順序の従ってノード情報を一次化したリストを保持する。<br>
	 * 各要素は１つのノードの情報を表し、Mapとして表現する。
	 * 各要素であるMapは、以下の内容を含む。<br>
	 * <table border="1">
	 *   <tr>
	 *     <td>キー</td>
	 *     <td>値のクラス</td>
	 *     <td>内容</td>
	 *   </tr>
	 *   <tr>
	 *     <td>KEY_NODE_UID</td>
	 *     <td>String</td>
	 *     <td>ノードのUID</td>
	 *   </tr>
	 *   <tr>
	 *     <td>KEY_NODE_TYPE</td>
	 *     <td>String</td>
	 *     <td>
	 *         ノードの種別。<br>
	 *         STATUS_SもしくはSTATUS_A。
	 *     </td>
	 *   </tr>
	 *   <tr>
	 *     <td>KEY_NODE_TITLE</td>
	 *     <td>String</td>
	 *     <td>
	 *         ノードのタイトル。
	 *         セクションの場合、ルートノードからのタイトルを&quot;&gt;&quot;で連結した文字列。
	 *     </td>
	 *   </tr>
	 * </table>
	 * <br>
	 * @see #STATUS_A
	 * @see #STATUS_S
	 * @see #KEY_NODE_TITLE
	 * @see #KEY_NODE_TYPE
	 * @see #KEY_NODE_UID
	 */
	private List nodeInfoList;

	/**
	 * コンストラクタ。<br>
	 * インスタンスを生成した時点でノードリストを生成し内部に保持する。
	 * @param rootNode リストを生成する際にルートとなるノード。
	 */
	public IndividualLogRDFNode(RDFNode rootNode) {
		// 教材構造からノードリストを生成し保持する。
		nodeInfoList = getNodeList(rootNode);

	}

	/**
	 * 教材構造からノードリストを生成し戻す。<br>
	 * 指定したノードがＡＵの場合、親ノードと自身のノード情報だけの情報を戻す。<br>
	 * 指定したノードがセクションの場合、自身と配下のノードをリスト化して戻す。<br>
	 * 戻すリストの要素はMapであり、その構成はnodeInfoListと同様である。<br>
	 * @param rootNode リストを生成する際にルートとなるノード。
	 * @see #nodeInfoList
	 */
	private List getNodeList(RDFNode rootNode) {
		// このメソッドの戻り値。要素はMap。
		List nodeList = null;
		// nodeListの要素となるノード情報を表すMap。
		Map nodeInfoMap = null;
		// 自身の子ノードのリスト。
		List childNodeList = null;
		// 祖先ノードのタイトルリスト
		List ancestorTitles = null;
		// 自身を含めたノードタイトルリスト
		List currentAncestorTitles = null;

		// 戻り値となるリスト。
		nodeList = new ArrayList();

		// ルートノードの親ノードまでのタイトルリストを取得。
		ancestorTitles = getAncestorTitles(rootNode);
		////////////////////////////////////////////////////////////////////////
		// ルートノードがＡＵの場合、
		// 親セクション・ノードと自身の情報だけを保持
		if (rootNode.getNodeType() == RDFNode.AU_NODE) {
			//親セクションのノード情報を保持
			nodeInfoMap = makeNodeInfoMap("", STATUS_S, getJoinedTitle(ancestorTitles));
			nodeList.add(nodeInfoMap);
			//自身のノード情報を保持
			nodeInfoMap = makeNodeInfoMap(rootNode.getID(), STATUS_A, rootNode.getTitle());
			nodeList.add(nodeInfoMap);
		}
		////////////////////////////////////////////////////////////////////////
		// ルートノードがセクションの場合、
		// 自身の情報と配下の情報を取得し保持する
		else {
			// 自身を含めたタイトルを取得する
			currentAncestorTitles = new ArrayList(ancestorTitles);
			currentAncestorTitles.add(rootNode.getTitle());
			//自身のノード情報を保持する
			nodeInfoMap = makeNodeInfoMap("", STATUS_S, getJoinedTitle(currentAncestorTitles));
			nodeList.add(nodeInfoMap);
			// 自身の配下のノードリストを取得し、あわせて保持する
			childNodeList = getChildNodeList(rootNode, currentAncestorTitles);
			nodeList.addAll(childNodeList);
		}
		// 全体としてのノードリストを返却する。
		return nodeList;
	}

	/**
	 * 指定したノードの親ノードまでのタイトルリストを取得する。<br>
	 * 指定したノードの親ノードを順に辿り、ノードのタイトルをリストとして取得し、戻す。
	 * 戻すリストないの順序は、上位ノードが先となる。
	 * よって、指定したノードの親ノードのタイトルは最後要素となる。
	 * 
	 * @param node タイトルリスト取得の基点となるノード。
	 * @return 指定したノードの親ノードまでの各ノードのタイトルを要素（String）とするリスト。
	 */
	private List getAncestorTitles(RDFNode node) {
		// このメソッドの戻り値。
		List ancestorTitles = null;
		// 親ノードを順次取得用
		RDFNode parentNode = null;

		ancestorTitles = new ArrayList();
		
		parentNode = node;
		while (true) {
			// 次の親ノードを辿る
			parentNode = parentNode.getParentNode();
			// 辿れなくなったら終了。
			if (parentNode == null) {
				break;
			}
			//親ノードがセクション（ブロック）の場合、タイトルを追加登録
			if (parentNode.getNodeType() == RDFNode.BLOCK_NODE) {
				ancestorTitles.add(parentNode.getTitle());
			}
		}
		
		// 上位が先になるように要素を逆順にしてから戻す。
		Collections.reverse(ancestorTitles);
		return ancestorTitles;
	}

	/**
	 * 指定したノード配下のノード情報リストを取得する。<br>
	 * 指定したノードの（直下の）子ノードを取得し、それら子ノードを情報を順次リストの要素として追加する。
	 * もし、子ノードがセクションの場合、その情報を追加した直後に、
	 * その子ノードの配下のノードリストを取得し、取得したノードリストも統合して戻す。
	 * すなわち、再帰的にノード情報を取得することとなり、このメソッドは再帰呼出となる。<br>
	 * 戻すリストの要素はMapであり、その構成はnodeInfoListと同様である。<br>
	 * 
	 * @param parentNode 取得するノードリストの基点となる親ノード。
	 * @param ancestorTitles parentNodeまでのノード・タイトルを要素(String)とするリスト。
	 * @see #nodeInfoList
	 */
	private List getChildNodeList(RDFNode parentNode, List ancestorTitles) {
		// このメソッドの戻り値。
		List nodeList = null;
		// 子ノードがセクションの場合、その配下のノードリストを取得するためのリスト。
		List childNodeList = null;
		// 子ノードがセクションの場合、その子ノードタイトルを含んだタイトルリスト。
		List currentAncestorTitles = null;
		// nodeListの要素となるMap
		Map nodeInfoMap = null;
		// parentNodeの子ノードのリスト
		RDFNodeList objNodeList = null;
		// parentNodeの子ノード数
		int intLength = 0;

		nodeList = new ArrayList();
		////////////////////////////////////////////////////////////////////////
		//子ノードを取得する。
		objNodeList = parentNode.getChildNodes();
		intLength = objNodeList.getLength();
		////////////////////////////////////////////////////////////////////////
		// 子ノード１つ１つを検査
		for (int i = 0; i < intLength; i++) {
			RDFNode objChildNode = objNodeList.getNode(i);
			////////////////////////////////////////////////////////////////////
			// 取得したノードがＡＵの場合、
			// それ自身をリストに追加して、次のノードへ
			if (objChildNode.getNodeType() == RDFNode.AU_NODE) {
				nodeInfoMap =
					makeNodeInfoMap(objChildNode.getID(), STATUS_A, objChildNode.getTitle());
				nodeList.add(nodeInfoMap);
				continue;
			}
			////////////////////////////////////////////////////////////////////
			// 以下、ノードがセクションの場合について
			// 1.ノードが教材でなければそれ自身をリストに追加する。
			// 2.自身の子ノードについて情報を取得する。
			currentAncestorTitles = new ArrayList(ancestorTitles);
			currentAncestorTitles.add(objChildNode.getTitle());
			if (parentNode.getNodeType() != RDFNode.COURSE_NODE) {
				nodeInfoMap =
					makeNodeInfoMap(
						objChildNode.getID(),
						STATUS_S,
						getJoinedTitle(currentAncestorTitles));
				nodeList.add(nodeInfoMap);
			}
			// 子ノードのため再帰呼び出して、リストにすべて追加
			childNodeList = getChildNodeList(objChildNode, currentAncestorTitles);
			nodeList.addAll(childNodeList);
		}
		return nodeList;
	}

	/**
	 * 指定したノード情報からノード情報Mapを生成し戻す。<br>
	 * 戻すMapの構成はnodeInfoListの要素と同様である。<br>
	 * 
	 * @param uid ノードのUID。
	 * @param type ノードの種別。
	 * @param title ノードのタイトル。
	 * @return nodeInfoListの要素と同じ形式のノード情報Map。
	 */
	private Map makeNodeInfoMap(String uid, String type, String title) {
		Map nodeInfoMap = null;
		nodeInfoMap = new HashMap();

		nodeInfoMap.put(KEY_NODE_UID, uid);
		nodeInfoMap.put(KEY_NODE_TYPE, type);
		nodeInfoMap.put(KEY_NODE_TITLE, title);

		return nodeInfoMap;
	}

	/**
	 * タイトルの連結を行う。<br>
	 * 渡されたタイトル・リストの要素を連結した文字列を戻す。
	 * 要素と要素の間にはTITLE_TOKENを挟む。
	 * 
	 * @param ancestorTitles タイトル(String)が要素であるリスト。
	 * @return 連結したタイトル。
	 * @see IndividualLogPropertyDef#TITLE_TOKEN
	 */
	private String getJoinedTitle(List ancestorTitles) {

		StringBuffer joinedTitle = new StringBuffer();
		// 区切り文字、タイトルの順で要素を連結する。
		for (int i = 0; i < ancestorTitles.size(); i++) {
			joinedTitle.append(TITLE_TOKEN);
			joinedTitle.append((String)ancestorTitles.get(i));
		}
		// 先頭の区切り文字を外した状態で戻す
		return joinedTitle.substring(TITLE_TOKEN.length());
	}

	/**
	 * 保持しているノードリストから指定したノードの種別を戻す。
	 * @param index ノードリストから取得するノードのインデックス。
	 * @return 取得したノードの種別。STATUS_SまたはSTATUS_A。
	 * @see #STATUS_A
	 * @see #STATUS_S
	 */
	public String getType(int index) {
		String title = null;
		Map nodeInfoMap = null;

		nodeInfoMap = (Map) nodeInfoList.get(index);
		title = (String) nodeInfoMap.get(KEY_NODE_TYPE);
		return title;
	}

	/**
	 * 保持しているノードリストから指定したノードのUIDを戻す。
	 * @param index ノードリストから取得するノードのインデックス。
	 * @return 取得したノードのUID。
	 */
	public String getAUID(int index) {
		String uid = null;
		Map nodeInfoMap = null;

		nodeInfoMap = (Map) nodeInfoList.get(index);
		uid = (String) nodeInfoMap.get(KEY_NODE_UID);
		return uid;
	}

	/**
	 * 保持しているノードリストから指定したノードのタイトルを戻す。
	 * @param index ノードリストから取得するノードのインデックス。
	 * @return 取得したノードのタイトル。
	 */
	public String getTitle(int index) {
		String title = null;
		Map nodeInfoMap = null;

		nodeInfoMap = (Map) nodeInfoList.get(index);
		title = (String) nodeInfoMap.get(KEY_NODE_TITLE);
		return title;
	}

	/**
	 * 保持しているノードリストの要素数を戻す。
	 * @return 保持しているノードリストの要素数。
	 */
	public int getMaxRow() {
		return nodeInfoList.size();
	}

}