/*
 * @(#)MapperType1.java
 *
 * RID-X : Mapper
 *
 * @author  Intelligent Technology Inc.
 * @version 1.01 2002/02/15
 *
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 */

package jp.co.nri.rid.mapper;


import java.util.*;
import java.text.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.Vector;
import org.w3c.dom.*;
import org.apache.xpath.XPathAPI;
import jp.co.iti.fagot.doc.DocManager;

import jp.co.nri.rid.error.RidException;
import jp.co.nri.rid.error.*;

import jp.co.iti.fagot.doc.DocManager;
import jp.co.iti.fagot.util.ZProperties;
import jp.co.iti.fagot.util.ZString;


/**
 * Mapper(Type1)
 */
public class MapperType1 extends MapperBase {

    /** エラーメッセージ **/
    private static String pr_errorString;

    /*　ROOT　ELEMENTの定義：RID　*/
    /** RidProjectの定義 **/
    private static final String ROOT_ELEMENT  = "RidProject";
    /** MapAttrの定義 **/
    private static final String MAP_ATTR      = "MapAttr";
    /** MapAttrDataの定義 **/
    private static final String MAP_ATTR_DATA = "MapAttrData";
    /** Rid_Tmpの定義 **/
    private static final String RID_TMP       = "Rid_Tmp";
    /** MapDataの定義 **/
    private static final String MAPDATA       = "MapData";

    /*　属性の定義　*/
    /** srcTagの定義 **/
    private static final String SRC_TAG       = "srcTag";
    /** srcKeyの定義 **/
    private static final String SRC_KEY       = "srcKey";
    /** dataTagの定義 **/
    private static final String DATA_TAG      = "dataTag";
    /** dataKeyの定義 **/
    private static final String DATA_KEY      = "dataKey";
    /** inTagの定義 **/
    private static final String IN_TAG        = "inTag";
    /** outAttrの定義 **/
    private static final String OUT_ATTR      = "outAttr";
    /** nameの定義 **/
    private static final String NAME          = "name";


/**
  * コンストラクタ
  *
  */
    public MapperType1() {
        //クリア
        pr_errorString = "";
    }

/**
  * (Mapper_Type1)
  *
  *   Mapファイルに記述された内容をもとに
　*   Inputファイルの指定されたタグに、指定された属性と値を追加する
  *
  * @param  String      in_file         INPUTファイル   (HTML形式)
  * @param  String      map_file        MAPファイル     (XML 形式)
  * @param  Document    data_doc        DATAのDocument  (XML 形式)
  * @param  String      out_file        OUTPUTファイル  (HTML形式)
  *
  * @return boolean     true(成功) / false (失敗)
　*--------------------------------------------------------------------------
  *
  *  #InputFile
  *   
  *   <a name="top"> トップページ </a>
  *
  *  #MapFile
  *
  *   <MapAttr srcTag="a" srcKey="name" dataTag="PageData" dataKey="name">
  *       <MapAttrData inTag="URL" outAttr="hrer"/>
  *   </MapAttr>
  *
  *   srcTag  : inputFileのタグ名
  *   srcKey  : inputFileの属性キー
  *   dataTag : dataFileのタグ名
  *   dataKey : dataFileの属性キー
  *   inTag   : dataFileのデータタグ名
  *   outAttr : inputFileに追加する属性名
  *
  *       キー：srcKeyの値とdataKeyの値が同じもの
  *
  *  #DataFile
  *
  *   <PageData name="top">
  *     <URL>../topFrame.html</URL>
  *   </PageData>
  *
  *  #OutputFile
  *   
  *   <a name="top" hrer="../topFrame.html"> トップページ </a>
  */
    public boolean Type1(String in_file, String map_file, Document data_doc, String out_file)  throws RidException {

        boolean ret = false;
        Vector  map_lst  = new Vector();
        Vector  data_lst = new Vector();
        RidMessage ridMsg = new RidMessage();

        try{
            //ファイルの存在チェック
            if (checkFile(in_file) && checkFile(map_file)) {
                // MAPファイルを解析する
                ret = type1MapFileParser(map_file, map_lst);
                if (ret) {
                    // Dataドキュメントを解析する
                    ret = type1DataDocParser(data_doc, map_lst, data_lst);
                    if (ret) {
                        //HTMLファイルの生成
                        type1Generate(in_file, map_lst, data_lst, out_file);
                        ret = true;
                    }
                }
            }
            //イメージファイルをコピーする
            if (ret){
              MapperResource  mr = new MapperResource();
              mr.imageFileCopy(in_file, out_file);
            }
        }
        catch(Exception e){
            String err_msg = ridMsg.Error("E4002"," (Mapper) " + e.getMessage());
            if (err_msg.indexOf("Filename too long") != -1) {
                err_msg =  ridMsg.Error("E4003"," (Mapper) " + e.getMessage());
            }
            throw new RidException(err_msg);
        }
        return ret;
    }

/**
  * (Mapper_Type1)
  *
  * マップファイルを解析
  *
  * @param  String      map_file        マップファイル名
  * @param  Vector      map_lst         MapItemクラスの配列
  *
  * @return boolean     true(成功) / false(失敗)
  */
    private boolean type1MapFileParser(String map_file, Vector map_lst) throws Exception {

        boolean ret = false;

        // マップXMLファイルのルートエレメントを取得する

        //Element root_elem = xmlFile2RootElement(map_file, ROOT_ELEMENT);
        Element root_elem = DocManager.createMapElement(map_file);

        if(root_elem != null) {
            // マップXMLファイルのマップ情報を取得する
            ret = getMapAttr(root_elem, map_lst);
        } else {
            String msg = "Root element name is different.";
            setErrorString(msg);
        }
        return ret;
    }


/**
  * (Mapper_Type1)
  *
  * マップファイルのマップ情報を取得する
  * マップ情報がに誤りがある場合は、そのデータのみ無効とする
  *
  * @param  Element     root_elem       ルートエレメント
  * @param  Vector      map_lst         MapItemクラスの配列
  * @return boolean     true(成功) / false(失敗)
  */
    private boolean getMapAttr(Element root_elem, Vector map_lst) throws Exception {

        boolean ret = false;

        // <MapAttr>タグの取得
        NodeList map_elem_lst = XPathAPI.selectNodeList(root_elem, MAP_ATTR);
        for (int i =0; i < map_elem_lst.getLength(); i++) {

            Element attr_elem = (Element)map_elem_lst.item(i);

            // Attr属性の取得
            String srcTag  = attr_elem.getAttribute(SRC_TAG);
            String srcKey  = attr_elem.getAttribute(SRC_KEY);
            String dataTag = attr_elem.getAttribute(DATA_TAG);
            String dataKey = attr_elem.getAttribute(DATA_KEY);

            // <MapAttrData>タグの取得
            NodeList data_elem_lst = XPathAPI.selectNodeList(attr_elem, MAP_ATTR_DATA);
            for (int n =0; n < data_elem_lst.getLength(); n++) {
                Element data_elem = (Element)data_elem_lst.item(n);

                MapItem map = new MapItem();

                map.srcTag   = srcTag;
                map.srcKey   = srcKey;
                map.dataTag  = dataTag;
                map.dataKey  = dataKey;
                map.inTag    = data_elem.getAttribute(IN_TAG);
                map.outAttr  = data_elem.getAttribute(OUT_ATTR);

                // マップ情報のチェック（１つでもNULLの場合はエラー）
                if ( checkMapFile(map) ) {
                    if (-1 == map_lst.indexOf(map))   map_lst.add(map);
                }
            }
        }
        return true;
    }


/**
  * (Mapper_Type1)
  *
  * マップ情報のチェック
  * １つでも空白("")のの場合はエラーとする
  *
  * @param  MapItem     map             MapItemクラス
  * @return boolean     true(成功) / false(失敗)
  */
    private boolean checkMapFile(MapItem map) {

        boolean ret = false;

        //全ての属性が設定されているか
        if (!(map.srcTag  == "" || map.srcKey == "" || map.dataTag == "" ||
              map.dataKey == "" || map.inTag  == "" || map.outAttr == "")) {
            ret = true;
        }

        return ret;
    }



/**
  * (Mapper_Type1)
  *
  *  データドキュメントの解析
  *
  * @param  Document    data_doc        データファイルのドュメント
  * @param  Vector      map_lst         MapItemクラスの配列
  * @param  Vector      data_lst        DataItemクラスの配列
  * @return boolean     true(成功) / false(失敗)
  */
    private boolean type1DataDocParser(Document data_doc, Vector map_lst, Vector data_lst) throws Exception {

        boolean ret = false;

        // ルートエレメントを取得する
        Element root_elem = root_elem = data_doc.getDocumentElement();
        if (!checkRootElement(root_elem, ROOT_ELEMENT)) {
            String msg = "Root element name is different.";
            setErrorString(msg);
            root_elem = null;
        }
        if(root_elem != null) {
            // データファイルから値を取得する
            ret = getDataTag(root_elem, map_lst, data_lst);
        }
        return ret;
    }


/**
  * (Mapper_Type1)
  *
  * データドキュメントからマップファイルに記述されている
　* データの取得する
  *
  * @param  Element     root_elem       ルートエレメント
  * @param  MapItem     map_lst         MapItemクラスの配列
  * @param  Vector      data_lst        DataItemクラスの配列
  *
  * @return boolean     true(成功) / false(失敗)
  */
    private boolean getDataTag(Element root_elem, Vector map_lst, Vector data_lst) throws Exception {

        XmlUtil xu = new XmlUtil();

        // マップ情報の件数、繰り返し
        for (int n = 0; n < map_lst.size(); n++) {

            // マップ情報の取得
            MapItem map = (MapItem)map_lst.elementAt(n);

            // マップ情報の[DataTag]のノードリスト取得
            NodeList data_elem_lst = XPathAPI.selectNodeList(root_elem, map.dataTag);
            for (int i = 0; i < data_elem_lst.getLength(); i++) {
                //[DataTag]のタグのエレメント
                Element data_elem = (Element)data_elem_lst.item(i);

                // [DataTag]のタグの[dataKey]属性の取得
                String key_name = data_elem.getAttribute(map.dataKey);

                // [InTag]のノード取得
                Element in_elem = (Element)XPathAPI.selectSingleNode(data_elem, map.inTag);
                if (in_elem != null) {

                    // [inTag]の値を取得
                    String in_data = xu.getTextValue(in_elem);

                    // データを追加
                    DataItem data = new DataItem();
                    data.tagName  = map.srcTag;
                    data.keyName  = key_name;
                    data.inData   = in_data;
                    data.outAttr  = map.outAttr;
                    data_lst.add(data);
                }
            }
        }
        return true;
    }



/**
  * (Mapper_Type1)
　*
  * HTMLファイルの生成
  *
  * @param  String      in_file         入力ファイル名
  * @param  Vector      map_lst         マップ情報（MapItemの配列）
  * @param  Vector      data_lst        データ情報（DataItemの配列）
  * @param  String      out_file        出力ファイル名
  * @return boolean     true(成功) / false(失敗)
  */
    private boolean type1Generate(String in_file, Vector map_lst, Vector data_lst, String out_file) throws Exception {

        boolean ret = false;
        char str[]=new char[256];

        // 入力ファイルからマップ情報のタグ情報だけをぬきだす
        Vector inTag_lst = new Vector();
        inFileParser(in_file, map_lst, inTag_lst);

        // INPUTファイルのオープン
        InputStreamReader InData=new InputStreamReader(new FileInputStream(in_file));

        // OUTOUTファイルのオープン
        OutputStreamWriter outWriter = new OutputStreamWriter(new FileOutputStream(out_file));

        int pos = 0;
        for ( int n = 0; n < inTag_lst.size(); n ++ ) {
            TagItem inTag = (TagItem)inTag_lst.elementAt(n);

            //変換するタグの最初の位置まで出力する（MapﾌｧｲﾙにあるHTMLタグのみ）
            while ((InData.read(str, 0, 1)) != -1) {
                if (pos < inTag.sPos) {
                    outWriter.write(str, 0, 1);
                } else {
                    pos++;
                    break;
                }
                pos++;
            }

            // 変換するタグに一致するデータを検索する
            boolean  flag = false;
            for ( int m = 0; m < data_lst.size(); m ++ ) {
                DataItem data = (DataItem)data_lst.elementAt(m);

                if ( (data.tagName.compareToIgnoreCase(inTag.tagName) == 0) &&
                     (data.keyName.compareToIgnoreCase(inTag.keyName) == 0) ) {

                     String attr;
                     int end = inTag.tagString.length()-1;
                     attr = inTag.tagString.substring(0,end) + " " + data.outAttr + "=\"" + data.inData + "\">";
                     outWriter.write(attr, 0, attr.length());
                     flag = true;
                     break;
                }
            }
            if (!flag) {
                outWriter.write(inTag.tagString, 0, inTag.tagString.length());
            }

            // 変換するタグの終了位置まで読飛ばす（INPUT FILE)
            while ((InData.read(str, 0, 1)) != -1) {
                if (pos < inTag.ePos) {
                    //読み飛ばし
                } else {
                    pos++;
                    break;
                }
                pos++;
            }
        }

        // 最後まで読込む(INPUT FILE)
        while ((InData.read(str, 0, 1)) != -1) {
            outWriter.write(str, 0, 1);
        }

        // ファイルクローズ
        InData.close();
        outWriter.close();

        return true;
   }



/**
  * (Mapper_Type1)
  * HTMLファイルのタグを解析してから、Mapファイルに記述されていタグのみにする
  *
  * @param  String      in_file         入力ファイル名
  * @param  Vector      map_lst         マップ情報（MapItemの配列）
  * @param  Vector      data_lst        データ情報（DataItemの配列）
  * @return void
  */
    private void inFileParser(String in_file, Vector map_lst, Vector inTag_lst) throws Exception {

        //in_file　Parser
        Vector tag_lst = new Vector();
        htmlParser(in_file, tag_lst);

        // HTMLタグの配列から、MapItemのsrcTagタグを抜出す
        for (int n = 0; n < tag_lst.size(); n ++ ) {
            // HTMLタグ情報の取得
            TagItem htmlTag = (TagItem)tag_lst.elementAt(n);

            // HTMLタグの配列から、MapItemのsrcTagタグを抜出す
            for (int m = 0; m < map_lst.size(); m ++ ) {
                // HTMLタグ情報の取得
                MapItem map = (MapItem)map_lst.elementAt(m);

                //大文字に変換
                String tagStringUp  = htmlTag.tagString.toUpperCase();
                String srcTagUp     = "<" + map.srcTag.toUpperCase();

                //srcTagタグの判断
                if (tagStringUp.indexOf(srcTagUp) != -1) {

                    //srcKeyの取得
                    String srcKey = getAttrValue(htmlTag.tagString, map.srcKey);

                    //
                    if (srcKey != ""){
                        TagItem srcTag = new TagItem();
                        srcTag.tagName     = map.srcTag;
                        srcTag.keyName     = srcKey;
                        srcTag.tagString   = htmlTag.tagString;
                        srcTag.sPos        = htmlTag.sPos;
                        srcTag.ePos        = htmlTag.ePos;
                        inTag_lst.add(srcTag);
                    }
                }
            }
        }
  }



/**
  * (Mapper_Type1　DOM版)
　*
  *   Mapファイルに記述された内容をもとに
　*   Input XMLに指定されたタグに、指定された属性と値を追加する
  *   InputXmlを修正するので、新規にXMLを生成しない
  *
  * @param  Element     in_Element      INPUT XMLのルートエレメント
  * @param  String      map_file        MAPファイル     (XML 形式)
  * @param  Element     data_Element    DATA XMLのルートエレメント
  *
  * @return boolean     true(成功) / false (失敗)
  *----------------------------------------------------------------------------
  *
  *  #InputFile
  *   
  *   <a name="top"> トップページ </a>
  *
  *  #MapFile
  *
  *   <MapAttr srcTag="a" srcKey="name" dataTag="PageData" dataKey="name">
  *       <MapAttrData inTag="URL" outAttr="hrer"/>
  *   </MapAttr>
  *
  *   srcTag  : inputFileのタグ名
  *   srcKey  : inputFileの属性キー
  *   dataTag : dataFileのタグ名
  *   dataKey : dataFileの属性キー
  *   inTag   : dataFileのデータタグ名
  *   outAttr : inputFileに追加する属性名
  *
  *       キー：srcKeyの値とdataKeyの値が同じもの
  *
  *  #DataFile
  *
  *   <PageData name="top">
  *     <URL>../topFrame.html</URL>
  *   </PageData>
  
  */
    public boolean Type1Xml(Element in_Element, String map_file, Element data_Element) throws RidException {

        boolean ret = false;
        Vector  map_lst  = new Vector();
        Vector  data_lst = new Vector();

        try{
            //ファイルの存在チェツク
            if (checkFile(map_file)) {

                // MAP XML Parser
                ret = type1MapFileParser(map_file, map_lst);
                if (ret) {
                    // DATA XML Parser
                    if(data_Element != null) {
                        ret = getDataTag(data_Element, map_lst, data_lst);
                        if (ret) {
                            //ドキュメントの修正
                            ret = type1XmlGenerate(in_Element, map_lst, data_lst);
                        }
                    }
                }
            }
        }
        catch(Exception e){
            throw new RidException(e.getMessage());
        }

        return ret;
    }

/**
  * (Mapper_Type1　DOM版)
  *
  * DOMの情報を置換する
  *
  * @param  Element     in_Element      INPUT XMLのルートエレメント
  * @param  Vector      map_lst         マップ情報（MapItemの配列）
  * @param  Vector      data_lst        データ情報（DataItemの配列）
  * @return void
  */
    private boolean type1XmlGenerate(Element in_Element, Vector map_lst, Vector data_lst) throws Exception {

        boolean ret = false;
        XmlUtil xu = new XmlUtil();
        String  xpath = "";

        // Map情報の件数、繰返す
        for (int n = 0; n < map_lst.size(); n++ ) {
            MapItem map = (MapItem)map_lst.elementAt(n);

            // Data情報の件数、繰返す
            for (int m = 0; m < data_lst.size(); m++ ) {
                DataItem data = (DataItem)data_lst.elementAt(m);

                // 定義と出力データが一致する場合のみ
                if (map.outAttr.compareTo(data.outAttr) == 0) {

                    //Elementを取得する 指定された検索条件で探す
                    xpath = "//" + map.srcTag + "[@" + map.srcKey + " = '" + data.keyName + "']";
                    Element lelem = (Element)XPathAPI.selectSingleNode( in_Element, xpath );
                    if (lelem != null) {
						lelem.setAttribute(map.outAttr, data.inData);
//                        Attr attr = xu.createOrGetAttribute(lelem, map.outAttr);
//                        xu.createOrSetTextValue((Node)attr, data.inData);
                    }

                }
            }
            ret = true;
        }
        return ret;
   }

/**
  * 現在の時刻
  *
  * @return String (yyyy/MM/dd (EE) HH:mm:ss.SSS zz)
  */
    public String getDateString() throws Exception {
        Date date = new Date();
        DateFormat df = new SimpleDateFormat("yyyy/MM/dd (EE) HH:mm:ss.SSS zz");
        return df.format(date);
    }



/**
  * ファイルが存在するかどうか、読み込み可能かどうかを確認する
  *
  * @param  String      in_file         入力Lファイル名
  *
  * @return boolean     true(存在する) / false (存在しない)
  */
    public boolean checkFile(String in_file) {

        boolean ret = true;

        if (!isFile(in_file)) {
            String msg = "no such source file: \"" + in_file + "\"";
            setErrorString(msg);
            ret =  false;
        }
        return ret;
    }


/**
  * エラーメッセージの設定
  *
  * @param  String      errorString     エラーメッセージ
  * @return void
  */
    private void setErrorString(String errorString) {
        pr_errorString = errorString;
    }

/**
  * エラーメッセージの取得
  *
  * @return String      エラーメッセージ
  */
    public String getErrorString() {
        return pr_errorString;
    }

}