/*
 * 作成日: 2005/02/08
 */
package jp.co.ntt.lms.xmo.Lo.lom;

import java.io.File;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import org.apache.commons.lang.StringEscapeUtils;

import jp.co.ntt.lms.lo.LOException;
import jp.co.ntt.lms.lo.Setup;
import jp.co.ntt.lms.lo.scorm.ScormLoEnv;
import jp.co.ntt.lms.lo.scorm.ScormManifestUtil;
import jp.co.ntt.lms.xmf.common.CommonFunction;
import jp.co.ntt.lms.xmo.DataAccess;
import jp.co.ntt.lms.xmo.Lo.LoMessage;
import jp.co.ntt.lms.xmo.Lo.LoTable;
import jp.co.ntt.lms.xmo.util.ChangeEncode;
import jp.co.ntt.lms.xmo.util.DebugLog;

/**
 * <p>LOMデータアクセスクラス。</p>
 * <p>データベースのテーブル、LOMとLOM_TYPEのデータアクセスを提供します。
 * </p>
 * @author t-sugawara
 */
public class LomDataAccess implements LoMessage, LoTable {

    /** タイムスパン取得用パス */
    private static final String PATH_TIMESPAN = "//:lom/:educational/:typicallearningtime";
    /** テーブルLOMの項目：value */
    private static final String LOM_VALUE = "value";
    /** テーブルLOMのテーブル名 */
    private static final String LOM_TABLE = "lom";
    /** テーブルLOM_TYPEのテーブル名 */
    private static final String LOM_TYPE_TABLE = "lom_type";
    /** テーブルLOMの項目：path */
    private static final String LOM_PATH = "path";
    /** テーブルLOM_TYPEの項目：path */
    private static final String LOM_TYPE_PATH = "path";
    /** テーブルLOMの項目：LOM_TYPE */
    private static final String LOM_TYPE = "lom_type";
    /** テーブルLOM_TYPEの項目：name */
    private static final String LOM_TYPE_NAME = "name";
    /** テーブルLOM_TYPEの項目：value */
    private static final String LOM_TYPE_VALUE = "value";
    /** テーブルLOM_TYPEの項目：disp_value */
    private static final String LOM_TYPE_DISPLAY_VALUE = "disp_value";
    /** テーブルLOM_TYPEの項目：selected */
    private static final String LOM_TYPE_SELECTED = "selected";
    /** テーブルLOM_TYPEの項目：display_no */
    private static final String LOM_TYPE_DISPLAY_NO = "display_no";
    /** LOM_TYPEが未確定の場合に一時的に設定します */
    private static final String LOM_TYPE_MAGIC_NO = "ZZ";
    
    /** 表示用データを取得するためのSQL文 */
    private static final String QUERY_GET_DISPLAY_DATA = "SELECT path, lom_type, name, value, disp_value FROM lom_type WHERE NOT lom_type = 'ZZ' ORDER BY display_no";
    /** pathとlom_typeを取得するためのSQL文 */
    private static final String QUERY_GET_LOM_TYPE = "SELECT DISTINCT path, lom_type FROM lom_type WHERE NOT lom_type = 'ZZ'";
    /** テーブルLOMにデータを登録するためのSQL文 */
    private static final String QUERY_INSERT_LOM = "INSERT INTO LOM VALUES(?, ?, ?, ?, ?)";
    /** テーブルLOM_TYPEのパスとパス毎の件数を取得するためのSQL文 */
    private static final String QUERY_GET_LOM_PATH = "SELECT path, count(value) as value  FROM lom_type GROUP BY path";
    /** テーブルLOM_TYPEにデータを登録するためのSQL文 */
    private static final String QUERY_INSERT_LOM_TYPE = "INSERT INTO lom_type VALUES(?, ?, ?, ?, ?, ?, ?)";
    /** テーブルLOM_TYPEのdisplay_noの最大値を取得するためのSQL文 */
    private static final String QUERY_GET_MAX_DISPLAY_NO = "SELECT max(display_no) FROM lom_type";
    /** pathとvalueから、LOMのLoIDを取得するためのSQL文 */
    private static final String QUERY_GET_LOID = "SELECT DISTINCT loid FROM lom WHERE path=? AND value = ? AND NOT lom_type = 'ZZ'";
    /** テーブルLOMにデータを登録するためのSQL文 */
    private static final String QUERY_GET_MAX_LOM_NO = "SELECT max(lom_no) FROM lom";
    
    /**
     * デフォルトコンストラクタ
     */
    public LomDataAccess() {
    }
    
    /**
     * <p>学習時間を取得します。</p>
     * <p>LoIDを元にLOM情報の学習情報を取得します。</p>
     * <p>レコードが存在しない場合は<code>null</code>を返します。</p>
     * @param loID LoID
     * @return 学習時間
     * @throws SQLException
     */
    public String getEductionalTimeSpan(String loID) throws SQLException {
        ResultSet rs = null;
        DataAccess dataAccess = new DataAccess();
        try {
	        
	        dataAccess.trans();
	        StringBuffer queryString = new StringBuffer();
			queryString.append("SELECT ").append(LOM_VALUE);
			queryString.append(" FROM ").append(LOM_TABLE);
			queryString.append(" WHERE ").append(LO_ID).append(" = '").append(StringEscapeUtils.escapeSql(loID)).append("'");
			queryString.append(" AND ").append(LOM_PATH).append(" = '").append(PATH_TIMESPAN).append("'");
			
			rs = dataAccess.executeQuery(queryString.toString());
	
			if(rs != null && rs.next()) {
				return ChangeEncode.setChar(rs.getString(LOM_VALUE));
			}
        }
        finally {
            if (rs != null) {
                rs.close();
            }
		    dataAccess.commit();
        }
        
        return null;
    }

    /**
     * <p>LOM情報をDBから取得します。</p>
     * <p>LOM属性追加の初期表示に使用します。</p>
     * @return LOM情報（DisplayDataを格納した配列）
     * @throws SQLException
     */
    public Vector getDisplayData() throws SQLException {
        ResultSet rs = null;
        DataAccess dataAccess = new DataAccess();
        try {
            dataAccess.trans();
            rs = getLomType(dataAccess);
			return editEmptyDisplayData(rs);
        }
        finally {
            if (rs != null) {
                rs.close();
            }
			dataAccess.commit();
        }
    }

    /**
     * <p>lom_typeテーブルのリザルトセットを取得します。</p>
     * @param dataAccess DB接続情報
     * @return lom_typeテーブルのリザルトセット
     * @throws SQLException
     */
    private ResultSet getLomType(DataAccess dataAccess) throws SQLException{
        //単体テストの都合、DataAccessを引数に受け取る
      return dataAccess.executeQuery(QUERY_GET_DISPLAY_DATA);
    }

    /**
     * <p>リザルトセットを元に、LOM情報表示用データを作成します。</p>
     * @param rs 取得したLOM情報のリザルトセット
     * @return LOM情報（DisplayDataを格納した配列）
     * @throws SQLException
     */
    private Vector editEmptyDisplayData(ResultSet rs) throws SQLException{
        Vector data = new Vector();
        boolean end = false;
        
		if (rs == null || !(rs.next())) {
		    return null;
		}

		while(!end) {
            String path = getEncodingString(rs.getString(LOM_TYPE_PATH));
            String lom_type = getEncodingString(rs.getString(LOM_TYPE));
            //nameはDispValueから取得する
            String name = null;
            Vector temp = new Vector();
            //各項目毎にDisplayDataにまとめる
            while(!end && path.equals(getEncodingString(rs.getString(LOM_TYPE_PATH)))) {
                name = getEncodingString(rs.getString(LOM_TYPE_NAME));
                temp.add(editDisplayValue(rs, null)); 
                //無くなれば終了
                if (!(rs.next())) {
                    end = true;
                }
            }
            data.add(new DisplayData(path, lom_type, name, temp));
        }
        if (data.size() == 0) {
            data = null;
        }

        return data;
    }

    /**
     * <p>LoIDを元に、LOM情報をDBから取得します。</p>
     * <p>LOM属性追加の初期表示に使用します。</p>
     * @param loID LoID
     * @return LOM情報（DisplayDataを格納した配列）
     * @throws SQLException
     */
    public Vector getDisplayData(String loID) throws SQLException {
        DataAccess dataAccess = new DataAccess();
        ResultSet lomTypeResult = null;
        try {
            dataAccess.trans();
            //LoIDを元にLOMから項目値を取得
            Hashtable lomHash = getLomHashtable(dataAccess, loID);
            //LOM_TYPEから表示項目を取得
            lomTypeResult = getLomType(dataAccess);
            //LOM_TYPEから表示項目を取得、LOMの項目値と、LOM_TYPEの表示項目をマッチングする
            return editDisplayData(lomHash, lomTypeResult);
        }
        finally {
            if (lomTypeResult == null) {
                lomTypeResult.close();
            }
            dataAccess.commit();
        }
    }

    /**
     * <p>LOM情報に対して、パスをキーとしたハッシュテーブルを作成します。</p>
     * <p>LoIDををキーとして、DBからLOM情報を取得します。LOM情報の項目pathをキーとして、
     *    DisplayValueを格納したハッシュテーブルを作成します。
     * </p>
     * @param dataAccess DB接続情報
     * @param loID LoID
     * @return LOM情報に対して、パスをキーとしたハッシュテーブル
     * @throws SQLException
     */
    private Hashtable getLomHashtable(DataAccess dataAccess, String loID) throws SQLException {
        ResultSet rs = null;
        try {
            //LoIDを元にLOMから項目値を取得
            StringBuffer queryString = new StringBuffer();
			queryString.append("SELECT ").append(LOM_TYPE_PATH);
			queryString.append(", ").append(LOM_TYPE_VALUE);
			queryString.append(" FROM ").append(LOM_TABLE);
			queryString.append(" WHERE ").append("loid = '").append(StringEscapeUtils.escapeSql(loID)).append("'");
			queryString.append(" order by ").append(LOM_PATH).append(", ").append(LOM_VALUE);
			rs = dataAccess.executeQuery(queryString.toString());
			Hashtable lomHash = new Hashtable();
			if (rs != null && rs.next()) {
			    boolean end = false;
			    while(!end) {
	                String path = getEncodingString(rs.getString(LOM_PATH));
	                Vector displayValue = new Vector();
		            while(!end && path.equals(getEncodingString(rs.getString(LOM_TYPE_PATH)))) {
		                String value = getEncodingString(rs.getString(LOM_TYPE_VALUE));
		                displayValue.add(new DisplayValue(value, value, true));
		                //無くなれば終了
		                if (!(rs.next())) {
		                    end = true;
		                }
		            }
			        lomHash.put(path, displayValue);
			    }
			}
			return lomHash;
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
    }

    /**
     * <p>LOM情報表示用データを編集します。</p>
     * <p>LoIDから取得したLOM情報のハッシュテーブルと、LOM_TYPEのリザルトセットから、
     *    対象のLoのLOM情報を画面表示用に編集します。
     *    <code>lomHash</code>は、選択項目用に項目毎に全ての選択項目が格納されています。
     *    テキスト項目については、値をそのまま、セットし、選択項目はこの<code>lomHash</code>を比較し、
     *    値が同じ物に選択フラグを付与します。
     * </p>
     * @param lomHash LOM情報
     * @param rs リザルトセット
     * @return DisplayDataを格納したベクタ
     * @throws SQLException
     */
    private Vector editDisplayData(Hashtable lomHash, ResultSet rs) throws SQLException {
        Vector data = new Vector();
        boolean end = false;
        
		if (rs == null || !(rs.next())) {
		    return null;
		}

		while(!end) {
            String path = getEncodingString(rs.getString(LOM_TYPE_PATH));
            String lom_type = getEncodingString(rs.getString(LOM_TYPE));
            //nameはDispValueから取得する
            Vector lomValue = (Vector) lomHash.get(path);
            Vector temp = new Vector();
//            System.out.println("lom_type:" + lom_type);
            String name = null;
            //各項目毎にDisplayDataにまとめる
            while(!end && path.equals(getEncodingString(rs.getString(LOM_TYPE_PATH)))) {
                name = getEncodingString(rs.getString(LOM_TYPE_NAME));
//                System.out.println("lomValue:" + lomValue);
                //テキスト項目と、複数のテキスト項目の場合
                if(lom_type.equals(DisplayData.LOM_TYPE_TEXT)
                        || lom_type.equals(DisplayData.LOM_TYPE_ANYTEXT)) {
                    if (lomValue == null) {
                        temp.add(new DisplayValue("","",false));
                    }
                    else {
                        temp = lomValue;
                    }
                }
                else {
                    //選択項目の場合
                    temp.add(editDisplayValue(rs, lomValue)); 
                }

                //無くなれば終了
                if (!(rs.next())) {
                    end = true;
                }
            }
            data.add(new DisplayData(path, lom_type, name, temp));
//            System.out.println("end... " + name);
        }
        if (data.size() == 0) {
            data = null;
        }

        return data;
    }
    
    /**
     * <p>LOM情報の選択項目を編集します。</p>
     * <p>LOM情報で画面上、選択項目に指定されている項目を編集します。<br>
     *    LoIDから取得したLOM情報に対して、LOM_TYPEの選択項目とマッチングをかけます。<br>
     *    マッチした選択項目に対して、画面上選択されていることを判別させるため、<code>DisplayValue.selected</code>の値を<code>true</code>にして作成します。
     * </p>
     * @param rs リザルトセット
     * @param lomValue
     * @return 編集後の選択項目
     * @throws SQLException
     */
    private DisplayValue editDisplayValue(ResultSet rs, Vector lomValue) throws SQLException{
        String value = getEncodingString(rs.getString(LOM_TYPE_VALUE));
        String display_value = getEncodingString(rs.getString(LOM_TYPE_DISPLAY_VALUE));
        boolean selected = false;
        if (lomValue != null) {
            for (int i = 0; i < lomValue.size(); ++i) {
                DisplayValue selectedValue = (DisplayValue) lomValue.get(i);
                if(selectedValue.getLomTypeValue().equals(value)) {
                    selected = true;
                }
            }
        }
        return new DisplayValue(value, display_value, selected);
    }
    
    /**
     * <p>LOM情報のテキスト項目を対象に検索します。</p>
     * <p>LO属性検索画面の項目「含まれる文字列」に入力された文字列をキーにLoIDを検索します。
     * </p>
     * @param searchText 検索キーワード
     * @return LoIDを格納したベクタ
     * @throws SQLException
     */
    public Vector searchText(String searchText) throws SQLException {
        DataAccess dataAccess = null;
        ResultSet rs = null;
        try {
            dataAccess = new DataAccess();
            dataAccess.trans();
            StringBuffer queryString = new StringBuffer();
            queryString.append("SELECT DISTINCT loid FROM lom ");
            queryString.append(" WHERE (lom_type='00' OR lom_type='15') AND value LIKE '%");
            queryString.append(/*CommonFunction.encodeSJIS(*/searchText/*)*/).append("%'");

            DebugLog.write(getClass(),
    				"searchText() QueryString"+queryString.toString(), DebugLog.ROW);

            rs = dataAccess.executeQuery(queryString.toString());
    		if (rs == null) {
    		    return new Vector();
    		}

    		Vector loIDArray = new Vector();
    		while(rs.next()) {
    		    loIDArray.add(rs.getString(1).trim());
    		}
    		dataAccess.commit();

    		if (loIDArray.size() == 0) {
                loIDArray = new Vector();
            }

            return loIDArray;
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
    }
    
    /**
     * <p>LOM情報の選択項目を対象に検索します。</p>
     * <p>LO属性検索画面の各選択項目で入力された入力値と、項目（パス）からLoIDを検索します。
     *    LoIDが取得できない場合、要素ゼロのベクタを返します。
     * </p>
     * @param params 検索条件を格納したVector
     * @return LoIDを格納したベクタ
     * @throws SQLException
     */
    public Vector searchLom(Vector params) throws SQLException {
        DataAccess dataAccess = null;
        PreparedStatement pstmt = null;
        try {
            Iterator paramIterator = params.iterator();
            dataAccess = new DataAccess();
            dataAccess.trans();
            pstmt = dataAccess.prepareStatement(QUERY_GET_LOID);
            
            Hashtable loIDTable = null;
            while (paramIterator.hasNext()) {
                DisplayData data = (DisplayData) paramIterator.next();
                Iterator itemIterator = data.getDisplayValueIterator();
                while (itemIterator.hasNext()) {
                    DisplayValue value = (DisplayValue) itemIterator.next();
                    //初回データの場合、ハッシュテーブルにそのままセット
                    if (loIDTable == null) {
                        loIDTable = getLoIDTable(pstmt, data.getPath(), value.getLomTypeValue());
                        //取得できなかったら、空のベクタを返して終了。
                        if(loIDTable == null) {
                            return new Vector();
                        }
                    }
                    else {
                        //データ取得（ベクタ）
                        Vector loIDArray = getLoIDArray(pstmt, data.getPath(), value.getLomTypeValue());
                        //ハッシュと比較。
                        loIDTable = matchingLoID(loIDTable, loIDArray);
                    }
                    //どれともマッチしなければ空のベクタを返して終了。
                    if (loIDTable == null || loIDTable.size() == 0) {
                        return new Vector();
                    }
                }
            }
            return new Vector(loIDTable.values());
        }
        finally {
            if (pstmt != null) {
                pstmt.close();
            }
            dataAccess.commit();
        }
    }

    /**
     * <p>LOMテーブルから、LoIDのハッシュテーブルを取得します。</p>
     * @param pstmt
     * @param path
     * @param value
     * @return 取得したLoIDのハッシュテーブル
     * @throws SQLException
     */
    private Hashtable getLoIDTable(PreparedStatement pstmt, String path, String value) throws SQLException {
        ResultSet rs = null;
        Hashtable loIDTable = new Hashtable();
        try {
            pstmt.setString(1, path);
            pstmt.setString(2, value);
            rs = pstmt.executeQuery();
            
            if (rs == null) {
                return null;
            }
            
            while(rs.next()) {
                String loID = rs.getString(1).trim();
                loIDTable.put(loID, loID);
            }
            
            //一件も取得できなかった場合。
            if (loIDTable.size() == 0) {
                loIDTable = null;
            }
            return loIDTable;
        }
        finally {
            if (rs == null) {
                rs.close();
            }
        }
    }
    
    /**
     * <p>LOMテーブルから、LoIDのベクタを取得します。</p>
     * @param pstmt
     * @param path
     * @param value
     * @return 取得したLoIDを格納したベクタ
     * @throws SQLException
     */
    private Vector getLoIDArray(PreparedStatement pstmt, String path, String value) throws SQLException {
        ResultSet rs = null;
        Vector loIDArray = new Vector();
        try {
            pstmt.setString(1, path);
            pstmt.setString(2, value);
            rs = pstmt.executeQuery();
            
            if (rs == null) {
                return null;
            }
            
            while(rs.next()) {
                String loID = rs.getString(1).trim();
                loIDArray.add(loID);
            }
            
            //一件も取得できなかった場合。
            if (loIDArray.size() == 0) {
                loIDArray = null;
            }
            return loIDArray;
        }
        finally {
            if (rs == null) {
                rs.close();
            }
        }
    }
    
    /**
     * <p>LoIDのマッチング処理を行います。</p>
     * @param loIDTable
     * @param loIDArray
     * @return マッチしたLoIDを格納したハッシュマップ
     */
    public static final Hashtable matchingLoID(Hashtable loIDTable, Vector loIDArray) {
        //取得できなかったら、nullを返して終了。
        if (loIDTable == null || loIDArray == null) {
            return new Hashtable();
        }

        Hashtable matchID = new Hashtable();
        Iterator loIDIterator = loIDArray.iterator();
//        System.out.println("loIDTable" + loIDTable);
        while (loIDIterator.hasNext()) {
            String loID2 = (String) loIDIterator.next();
            String loID = (String) loIDTable.get(loID2);
//            System.out.println("loids" + loID2 + "/" + loID);
            if (loID != null) {
                matchID.put(loID, loID);
            }
        }
        
        return matchID;
    }
    
    /**
     * 画面にて入力したLOM情報を元にDBに登録します。
     * @param displayData
     * @return 成功可否
     * @throws SQLException
     */
    public boolean enrollDisplayData(Vector displayData) throws SQLException {
        DataAccess dataAccess = new DataAccess();
        boolean isSuccess = false;

        dataAccess.trans();
        //LoIDを格納
        String loID = (String) displayData.get(0);
        if (loID == null) {
            return isSuccess;
        }
        //LoIDの要素を削除
        displayData.remove(0);

        try {
            //LOM_TYPEを元に、パスをキーにしたLOM_TYPEを取得する。
            Hashtable displayType = getDisplayType(dataAccess);
            //LoIDを元に、LOM情報を一度削除する
            deleteLomDara(dataAccess, loID);
            //LOMデータを設定。
            insertDisplayData(dataAccess, loID, displayData, displayType);
        }
        catch (SQLException ex) {
            dataAccess.rollback();
            throw ex;
        }
        
        dataAccess.commit();
        isSuccess = true;

        return isSuccess;
    }
    
    /**
     * <p>パスをキーとするLOM_TYPEテーブルの項目LOM_TYPEを格納したハッシュテーブルを取得します。</p>
     * @param dataAccess DB接続情報
     * @return パスをキーとするLOM_TYPEテーブルの項目LOM_TYPEを格納したハッシュテーブル
     * @throws SQLException
     */
    private Hashtable getDisplayType(DataAccess dataAccess) throws SQLException {
        ResultSet rs = null;
        Hashtable displayType = new Hashtable();
        try {
            rs = dataAccess.executeQuery(QUERY_GET_LOM_TYPE);
            if (rs == null) {
                return null;
            }
            while(rs.next()) {
                String path = getEncodingString(rs.getString(LOM_TYPE_PATH));
                String lom_type = getEncodingString(rs.getString(LOM_TYPE));
                displayType.put(path, lom_type);
            }
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
        return displayType;
    }

    /**
     * <p>LoIDを元にLOM情報を削除します。</p>
     * @param dataAccess DB接続情報
     * @param loID LOID
     * @throws SQLException
     */
    private void deleteLomDara(DataAccess dataAccess, String loID) throws SQLException {
        StringBuffer queryString = new StringBuffer();
        queryString.append("DELETE FROM lom WHERE loid = '").append(StringEscapeUtils.escapeSql(loID)).append("'");
        dataAccess.execute(queryString.toString());
    }
    
    /**
     * <p>データ登録処理を行います。</p>
     * @param dataAccess DB接続情報
     * @param loID LoID
     * @param displayDataArray
     * @param displayType
     * @throws SQLException
     */
    private void insertDisplayData(DataAccess dataAccess,
            						String loID,
            						Vector displayDataArray,
            						Hashtable displayType) throws SQLException{
        PreparedStatement insertPstmt = null;
        try {
            Iterator iterator = displayDataArray.iterator();

            while(iterator.hasNext()) {
                DisplayData displayData = (DisplayData) iterator.next();
                String path = displayData.getPath();
                String lom_type = (String) displayType.get(path);

                Iterator values = displayData.getDisplayValueIterator();
                while(values.hasNext()) {
                    int lom_no = getNextLomNo(dataAccess);
                    DisplayValue displayValue = (DisplayValue)values.next();
                    insertPstmt = dataAccess.prepareStatement(QUERY_INSERT_LOM);
                    insertPstmt.setInt(1, lom_no);
                    insertPstmt.setString(2, loID);
                    insertPstmt.setString(3, path);
                    insertPstmt.setString(4, lom_type);
                    insertPstmt.setString(5, displayValue.getLomTypeValue());
                    insertPstmt.execute();
                }
            }
        }
        catch (Exception ex) {
            dataAccess.rollback();
            throw new SQLException(ex.getMessage());
        }
        finally {
            if (insertPstmt != null) {
                insertPstmt.close();
            }
        }
    }
    
    /**
     * <p>マニフェストファイルからLOM情報をDBに登録します。</p>
     * @param dataAccess DB接続情報
     * @param params
     * @return 成功可否
     * @throws SQLException
     * @throws Exception
     */
    public boolean enrollEntryData(DataAccess dataAccess, Hashtable params) throws SQLException, Exception{
		// メソッド開始ログ
		DebugLog.write(getClass(),
			"HEAD enrollEntryData(" + params + ")", DebugLog.ROW);
        boolean isSuccess = false;

        String loID = (String) params.get(Setup.LOID);
        if (loID == null) {
            return isSuccess;
        }
        
        File manifest = ScormManifestUtil.getManifestTempFile((String)params.get(Setup.MATERIALID));
        File entry = getLomEntryFile();
//        System.out.println("getManifestOK? " + manifest + "/" + entry);
        EntryDataFactory factory = new EntryDataFactory(manifest, entry);
        Vector entryData = factory.makeEntryData();
//        System.out.println("entry.xml取得完了");
		DebugLog.write(getClass(),"entry.xml取得完了", DebugLog.ROW);
        //LOM項目更新
        updateLomType(dataAccess, entryData, factory.getEntryTable());
//        System.out.println("LOM項目更新完了");
		DebugLog.write(getClass(),"LOM項目更新完了", DebugLog.ROW);
        //選択項目のvalueをボキャブラリから、lom_typeのvalueに置き換える
        changeVocabulary(dataAccess, entryData);
//        System.out.println("Lom.Value変換完了");
		DebugLog.write(getClass(),"Lom.Value変換完了", DebugLog.ROW);
        //LoIDを元に、LOM情報を一度削除する
        deleteLomDara(dataAccess, loID);
        //LOM更新
        insertEntryData(dataAccess, loID, entryData);

        isSuccess = true;

//        System.out.println("終了");
		DebugLog.write(getClass(),"Lom.Value変換完了", DebugLog.ROW);
		// メソッド終了ログ
		DebugLog.write(getClass(),
			"TAIL enrollEntryData(" + isSuccess + ")", DebugLog.ROW);

        return isSuccess;
    }

    /**
     * <p>LOM取り込み要素ファイルを取得します。</p>
     * @return 取得したLOM取り込みファイル
     * @throws LOException
     */
	private File getLomEntryFile() throws LOException {
		// メソッド開始ログ
		DebugLog.write(
			ScormManifestUtil.class,
			"HEAD getLomEntryFile()",
			DebugLog.ROW);

		ScormLoEnv env = new ScormLoEnv();
		String entryFile = env.getLomEntryFile();
		// メソッド終了ログ
		DebugLog.write(
			ScormManifestUtil.class,
			"TAIL getLomEntryFile()"
				+ " return " + entryFile,
			DebugLog.ROW);
		return new File(entryFile);

	}

	/**
     * <p>LOM_TYPEテーブルの更新を行います。</p>
     * @param dataAccess DB接続情報
	 * @param entryData
	 * @param entryTable
	 * @throws SQLException
	 */
    private void updateLomType(DataAccess dataAccess, Vector entryData, Hashtable entryTable) throws SQLException {
        PreparedStatement insertPstmt = null;

        Hashtable dbPathTable = getLomPath(dataAccess);
        try {
            for (int i = 0; i < entryData.size(); ++i) {
                EntryDataUnit dataUnit = (EntryDataUnit) entryData.get(i);
                
                // 選択項目の場合
                if (dataUnit.isChoices()) {
                    //LOM_TYPEに項目が無い場合は選択肢込みで項目を追加
                    if (dbPathTable.get(dataUnit.getNodeString()) == null) {
                        String displayNo = getNextDisplayNo(dataAccess);
                        insertPstmt = dataAccess.prepareStatement(QUERY_INSERT_LOM_TYPE);
                        insertPstmt.setString(1, displayNo);
                        insertPstmt.setString(2, dataUnit.getNodeString());
                        insertPstmt.setString(3, LOM_TYPE_MAGIC_NO);
                        insertPstmt.setString(4, dataUnit.getName());
                        insertPstmt.setString(5, "0");
                        insertPstmt.setString(6, dataUnit.getValue());
                        insertPstmt.setString(7, dataUnit.getValue());
                        insertPstmt.execute();
                        dbPathTable.put(dataUnit.getNodeString(), "1");
                    }
                    else {
                        if (!checkVocabulary(dataAccess, dataUnit.getNodeString(), dataUnit.getValue())) {
                            String displayNo = getDisplayNo(dataAccess, dataUnit.getNodeString());
                            String value = (String) dbPathTable.get(dataUnit.getNodeString());
                            insertPstmt = dataAccess.prepareStatement(QUERY_INSERT_LOM_TYPE);
                            insertPstmt.setString(1, displayNo);
                            insertPstmt.setString(2, dataUnit.getNodeString());
                            insertPstmt.setString(3, LOM_TYPE_MAGIC_NO);
                            insertPstmt.setString(4, dataUnit.getName());
                            insertPstmt.setString(5, value);
                            insertPstmt.setString(6, dataUnit.getValue());
                            insertPstmt.setString(7, dataUnit.getValue());
                            insertPstmt.execute();
                            int nextValue = Integer.parseInt(value);
                            dbPathTable.put(dataUnit.getNodeString(), String.valueOf(nextValue));
                        }
                    }
                }
                else {
                    //LOM_TYPEに項目が無い場合
                    if (dbPathTable.get(dataUnit.getNodeString()) == null) {
                        String displayNo = getNextDisplayNo(dataAccess);
                        insertPstmt = dataAccess.prepareStatement(QUERY_INSERT_LOM_TYPE);
                        insertPstmt.setString(1, displayNo);
                        insertPstmt.setString(2, dataUnit.getNodeString());
                        insertPstmt.setString(3, LOM_TYPE_MAGIC_NO);
                        insertPstmt.setString(4, dataUnit.getName());
                        insertPstmt.setString(5, null);
                        insertPstmt.setString(6, null);
                        insertPstmt.setString(7, null);
                        insertPstmt.execute();
                        dbPathTable.put(dataUnit.getNodeString(), "1");
                    }
                }
            }
        }
        catch(SQLException ex) {
            ex.printStackTrace();
            throw ex;
        }
        finally {
            if (insertPstmt != null) {
                insertPstmt.close();
            }
        }
    }

    /**
     * <p>LOM_TYPEテーブルPATHをキーにvalueの項目数を値としたハッシュテーブルを返します。</p>
     * @param dataAccess DB接続情報
     * @return LOM_TYPEテーブルPATHをキーにvalueの項目数を値としたハッシュテーブル
     * @throws SQLException
     */
    private Hashtable getLomPath(DataAccess dataAccess) throws SQLException {
        Hashtable tempTable = new Hashtable();
        ResultSet rs = null;
        
        try {
            rs = dataAccess.executeQuery(QUERY_GET_LOM_PATH);
            if (rs != null) {
                while (rs.next()) {
                    tempTable.put(rs.getString(LOM_TYPE_PATH), rs.getString(LOM_TYPE_VALUE));
                }
            }
        }
        catch (Exception ex) {
            dataAccess.rollback();
            throw new SQLException(ex.getMessage());
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
        return tempTable;
    }

    /**
     * <p>ボキャブラリのチェックを行います。</p>
     * <p>テーブルLOM_TYPEのscorm_vocabularyと比較し、そのボキャブラリの有無をチェックします。
     *    テーブルに存在していれば<code>true</code>を、存在していなければ<code>false</code>を返します。</p>
     * @param dataAccess
     * @param path
     * @param vocabulary
     * @return
     * @throws SQLException
     */
    private boolean checkVocabulary(DataAccess dataAccess, String path, String vocabulary) throws SQLException {
        StringBuffer queryString = new StringBuffer();
        queryString.append("SELECT value FROM lom_type ");
        queryString.append(" WHERE path='").append(StringEscapeUtils.escapeSql(path)).append("'");
        queryString.append(" AND scorm_vocabulary='").append(StringEscapeUtils.escapeSql(vocabulary)).append("'");
        ResultSet rs = dataAccess.executeQuery(queryString.toString());
        try {
            String displayNo = null;
            if (rs == null) {
                throw new SQLException("ボキャブラリのチェックに失敗しました。");
            }
            while (rs.next()) {
                displayNo = rs.getString(1);
            }
            if (displayNo == null) {
                return false;
            }
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
        return true;
    }
    
    /**
     * <p>LOM_TYPEテーブルのDisplayNoの次の値を取得します。</p>
     * <p>LOM項目が増えたときに使用。登録するLOM項目のために、
     *    現在のdisplay_noの最大値の次の数字を取得します。
     *    ゼロ詰の文字列に変換し、返します。
     * </p>
     * @param dataAccess DB接続情報
     * @return LOM_TYPEテーブルのDisplayNoの次の値
     * @throws SQLException
     */
    private String getNextDisplayNo(DataAccess dataAccess) throws SQLException {
        ResultSet rs = null;
        
        try {
            rs = dataAccess.executeQuery(QUERY_GET_MAX_DISPLAY_NO);
            int no = 0;
            if (rs != null && rs.next()) {
                    no = rs.getInt(1);
            }
            ++no;
            DecimalFormat format = new DecimalFormat("0000000000");
            return format.format(no);
        }
        catch (Exception ex) {
            dataAccess.rollback();
            throw new SQLException(ex.getMessage());
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
        
    }

    /**
     * <p>LOM_TYPEテーブルのdisplay_noを取得します。</p>
     * @param dataAccess
     * @param path 取得するdisplay_noのパス
     * @return 取得したdisplay_no
     * @throws SQLException
     */
    private String getDisplayNo(DataAccess dataAccess, String path) throws SQLException {
//        "SELECT DISTINCT display_no FROM lom_type WHERE path=?"
        StringBuffer queryString = new StringBuffer();
        queryString.append("SELECT DISTINCT display_no FROM lom_type ");
        queryString.append(" WHERE path='").append(StringEscapeUtils.escapeSql(path)).append("'");
        ResultSet rs = dataAccess.executeQuery(queryString.toString());
        try {
            if (rs == null) {
                throw new SQLException("display_noの取得に失敗しました。");
            }
            String displayNo = null;
            while (rs.next()) {
                displayNo = rs.getString(LOM_TYPE_DISPLAY_NO);
            }
            if (displayNo == null) {
                throw new SQLException("display_noの取得に失敗しました。");
            }

            return displayNo;
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
    }
    
    /**
     * <p>EntryDataUnitのvalueをボキャブラリから、ボキャブラリに対応するLOM_TYPEの項目値へ変更します。</p>
     * @param dataAccess DB接続情報
     * @param entryData
     * @throws SQLException
     */
    private void changeVocabulary(DataAccess dataAccess, Vector entryData) throws SQLException{
        ResultSet rs = null;
        try {
            for (int i = 0; i < entryData.size(); ++i) {
                EntryDataUnit dataUnit = (EntryDataUnit) entryData.get(i);
                if (dataUnit.isChoices()) {
                    StringBuffer queryString = new StringBuffer();
                    queryString.append("SELECT value FROM lom_type ");
                    queryString.append(" WHERE path='").append(StringEscapeUtils.escapeSql(dataUnit.getNodeString())).append("'");
                    queryString.append(" AND scorm_vocabulary='").append(StringEscapeUtils.escapeSql(dataUnit.getValue())).append("'");
                    rs = dataAccess.executeQuery(queryString.toString());
                    if (rs != null && rs.next()) {
                        String value = rs.getString(LOM_TYPE_VALUE);
                        EntryDataUnit replaceUnit = new EntryDataUnit(dataUnit.getNodeString(),
                                										dataUnit.getName(),
                                										value,
                                										dataUnit.isChoices());
                        entryData.set(i, replaceUnit);
                    }
                    if (rs != null) {
                        rs.close();
                    }
                }
            }
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
    }

    /**
     * <p>LOMテーブルにデータを登録します。</p>
     * <p>SCORMからLOM情報を取得した際に使用します。
     * </p>
     * @param dataAccess DB接続情報
     * @param loID LoID
     * @param entryData
     * @throws SQLException
     */
    private void insertEntryData(DataAccess dataAccess, String loID, Vector entryData) throws SQLException{
        Hashtable displayType = getDisplayType(dataAccess);
        Iterator iterator = entryData.iterator();
        while(iterator.hasNext()) {
            int lom_no = getNextLomNo(dataAccess);
            EntryDataUnit dataUnit = (EntryDataUnit) iterator.next();
//            private static final String QUERY_INSERT_LOM = "INSERT INTO LOM VALUES(?, ?, ?, ?, ?)";
            StringBuffer queryString = new StringBuffer();
            queryString.append("INSERT INTO LOM VALUES(");
            queryString.append(lom_no);
            queryString.append(", '").append(StringEscapeUtils.escapeSql(loID)).append("'");
            queryString.append(", '").append(StringEscapeUtils.escapeSql(dataUnit.getNodeString())).append("'");
            queryString.append(", '").append(StringEscapeUtils.escapeSql((String) displayType.get(dataUnit.getNodeString()))).append("'");
            queryString.append(", '").append(StringEscapeUtils.escapeSql(dataUnit.getValue())).append("'");
            queryString.append(")");
    		DebugLog.write(getClass(),
    				"SQL insertEntryData:" + queryString.toString(), DebugLog.ROW);
            dataAccess.execute(queryString.toString());
        }
    }

    /**
     * <p>LOM_NOの次の値を取得する。</p>
     * <p>DBのLOMテーブルに登録する際のLOM_NOを取得する。
     *    取得するLOM_NOは、現在のLOM_NOの最大値に1を加算したものが設定される。
     * </p>
     * @param dataAccess
     * @return LOM_NO
     * @throws SQLException
     */
    private int getNextLomNo(DataAccess dataAccess) throws SQLException {
//        private static final String QUERY_GET_MAX_LOM_NO = "SELECT max(lom_no) FROM lom";
        StringBuffer queryString = new StringBuffer();
        queryString.append("SELECT max(lom_no) FROM lom");
        ResultSet rs = null;

        try {
            int lom_no = 0;
            
            rs = dataAccess.executeQuery(queryString.toString());
            if (rs != null && rs.next()) {
                lom_no = rs.getInt(1);
                ++lom_no;
            }
            else {
                throw new SQLException("データベースのテーブルLOMのアクセスに失敗しました。");
            }
            return lom_no;
        }
        finally {
            if (rs != null) {
                rs.close();
            }
        }
    }

    /**
     * <p>文字列のエンコーディングを行います。</p>
     * <p>各DBに対してのエンコーディングを行う。行う際、引数のが<code>null</code>の場合、
     *    長さゼロの文字列を返します。</p>
     * @param value
     * @return エンコーディング後の文字列
     */
    private static final String getEncodingString(String value) {
        if (value == null) {
            value = "";
        }
        return ChangeEncode.setChar(value);
    }
}
