/*
 * @(#)MapperType3.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.io.*;
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.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(Type3)
 */
public class MapperType3 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 MapperType3() {
        //クリア
        pr_errorString = "";
    }


/**
  * (Mapper_Type3)
  *
  * テンプレートファイル（HTML)の<MapRepeat>タグを
　* DATAドキュメントの値に置換して、指定されたファイルに出力する
  * ※<MapRepeat>---</MapRepeat>を繰り返し可能
  *
  * @param  String      in_file         INPUTファイル   (HTML形式)
  * @param  Document　  data_doc        DATAドキュメント(XML 形式)
  * @param  String      out_file        OUTPUTファイル  (HTML形式)
  *
  * @return boolean     true(成功) / false (失敗)
  */
    public boolean Type3(String in_file,  Document data_doc, String out_file) throws RidException {

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

        try{
            //ファイル有無
            if (checkFile(in_file)) {
                //DATAドキュメントの解析
                ret = type3DataDomParser(data_doc, data_lst);
                if (ret) {
                    //生成
                    ret = type3Generate(in_file, data_lst, out_file);
                }
            }
            //イメージファイルのコピー
            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_Type3)
  * データドキュメントの解析
  *
  * @param  Document    data_doc        ドキュメント
  * @param  Vector      data_lst        DataItemクラスの配列
  *
  * @return boolean     true(成功) / false(失敗)
  */
    public boolean type3DataDomParser(Document data_doc, Vector data_lst) throws Exception {


        XmlUtil xu = new XmlUtil();
        boolean ret = false;

        // データXMLファイルのルートエレメントを取得する
        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) {
            //全てのタグのリストを取得
            NodeList data_elem_lst = XPathAPI.selectNodeList(root_elem, "*");
            for (int i =0; i < data_elem_lst.getLength(); i++) {
                Vector map_lst = new Vector();
                Element data_elem = (Element)data_elem_lst.item(i);

                //全てのMAPDATAを取得する(リスト作成)
                NodeList attr_elem_lst = XPathAPI.selectNodeList(data_elem, "*");
                for (int n =0; n < attr_elem_lst.getLength(); n++) {
                    Element attr_elem = (Element)attr_elem_lst.item(n);
                    MapData map = new MapData();
                    map.name  = attr_elem.getNodeName();
                    map.data  = xu.getTextValue(attr_elem);
                    map_lst.add(map);
                }

                // ADD Data List
                DataItem data = new DataItem();
                data.tagName      = data_elem.getNodeName();
                data.mapData_lst  = map_lst;
                data_lst.add(data);
            }
            ret = true;
        }
        return ret;
    }


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

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

        // 入力ファイルからマップ情報のタグ情報だけをぬきだす
        Vector repeatTag_lst = new Vector();
        inRepeatFileParser(in_file, repeatTag_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 < repeatTag_lst.size(); n ++ ) {
            TagItem repeatTag = (TagItem)repeatTag_lst.elementAt(n);

            // <MapRepeat>タグの最初の位置まで出力する
            while ((InData.read(str, 0, 1)) != -1) {
                if (pos < repeatTag.sPos) {
                    outWriter.write(str, 0, 1);
                } else {
                    pos++;
                    break;
                }
                pos++;
            }
            
            // <MapRepeat>...</MapRepeat>タグのリピート範囲を置換する
            String grpeatStrin = getRepeatString(repeatTag, data_lst);
            outWriter.write(grpeatStrin, 0, grpeatStrin.length());

            // </MapRepeat>タグまでを読飛ばす
            while ((InData.read(str, 0, 1)) != -1) {
                if (pos < repeatTag.ePos) {
                    //読み飛ばし
                } else {
                    pos++;
                    break;
                }
                pos++;
            }
        }
        // 最後まで読込む
        while ((InData.read(str, 0, 1)) != -1) {
          outWriter.write(str, 0, 1);
        }

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

        return true;
   }



/**
  * (Mapper_Type3)
  * 指定された、HTMLファイルの開始位置から終了位置までをStringで戻す
  *
  * @param  String      in_file         HTMLファイル名
  * @param  int         sPos            開始位置
  * @param  int         ePos            終了位置
  *
  * @return String      指定した範囲の文字列
  */
    public String getRepeat(String in_file, int sPos, int ePos) throws Exception{

        String repeatString = "";
        char str[] = new char[256];

        InputStreamReader InData = new InputStreamReader(new FileInputStream(in_file));
        int pos = 0;
        while ((InData.read(str, 0, 1)) != -1) {
            //開始位置まで読み飛ばし
            if (pos >= sPos) {
                //終了位置まで読み込む
                if (pos <= ePos) {
                    repeatString = repeatString + str[0];
                }
            }
            pos++;
        }
        InData.close();

        return repeatString;
   }

/**
  * (Mapper_Type3)
  * <MapRepeat>タグ内の{MapData}をデータで置換する
  *
  * @param  TagItem     repeatTag       リピートタグ
  * @param  Vector      data_lst        データ情報（DataItemの配列）
  * @return String      リピート文字列
  */
    private String  getRepeatString(TagItem repeatTag, Vector data_lst) {

        // Repeatのタグを解析
        String repeatString = "";
        String buff = "";

        //前後の<MapRepeat>タグを削除
        // begin-<MapRepeat>タグの終了からend-<MapRepeat>の始まりまでをコピーする
        // 例
        // <MapRepeat name="PresentData">
        //   {MapData name="linkStart"}<img src="img/Showmode.gif" alt="紙芝居" height="27" width="27" border="0">{MapData name="linkEnd"}
        // </MapRepeat>
        buff = repeatTag.tagString.substring(repeatTag.tagString.indexOf(">") + 1, repeatTag.tagString.lastIndexOf("<"));

        // Repeat文字列から{MapData}タグ情報を検索
        Vector mapTag_lst = new Vector();
        mapDataParser(buff, mapTag_lst);

        // dataのloop
        for ( int m = 0; m < data_lst.size(); m ++ ) {
            DataItem data = (DataItem)data_lst.elementAt(m);
            
            // <MapRepeat> の name属性と同じDataであること
            if (repeatTag.keyName.compareToIgnoreCase(data.tagName) == 0) {
                int pos = 0;
                //{MapData}のloop
                // {MapData}をdataに置き換える
                for ( int n = 0; n < mapTag_lst.size(); n ++ ) {
                    TagItem tag = (TagItem)mapTag_lst.elementAt(n);
                    repeatString = repeatString + buff.substring(pos, tag.sPos);
                    for ( int i = 0; i < data.mapData_lst.size(); i ++ ) {
                        MapData map = (MapData)data.mapData_lst.elementAt(i);
                        if (map.name.compareToIgnoreCase(tag.keyName) == 0) {
                            repeatString = repeatString + map.data;
                        }
                    }
                    pos = tag.ePos + 1;
                }
                repeatString = repeatString + buff.substring(pos);
            }
        }
        return repeatString;
    }



/**
  * (Mapper_Type3)
  * <MapRepeat>タグ内の文字列から、{...}タグを解析し
　* {MapData}タグだけにし、{MapData}のName属性の値をセットする
  *
  * @param  String      repeat          <MapRepeat>タグ内の文字列
  * @param  Vector      data_lst        DataItemクラスの配列
  *
  * @return void
  */
    private void mapDataParser(String repeat, Vector inTag_lst) {

        //in_file　Parser
        Vector tag_lst = new Vector();
        htmlStringParser(repeat, tag_lst);

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

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

            //大文字に変換し、{MAPDATA}か判断
            if (tagStringUp.indexOf(srcTagUp) != -1) {

                //Name属性の値を取得する
                String srcKey = getAttrValue(htmlTag.tagString, NAME);

                //Name属性があるものだけ追加
                if (srcKey != ""){
                  TagItem srcTag = new TagItem();
                  srcTag.tagName     = MAPDATA;
                  srcTag.keyName     = srcKey;
                  srcTag.tagString   = htmlTag.tagString;
                  srcTag.sPos        = htmlTag.sPos;
                  srcTag.ePos        = htmlTag.ePos;
                  inTag_lst.add(srcTag);
                }
            }
        }
  }


/**
  * (Mapper_Type3)
  *     HTMLの文字列から、{ ... }タグを検索し、TagItemクラスの配列を戻す
  *
  * @param  String      html            HTMLファイル名
  * @param  Vector      tmpEl           Template Element
  * @return void
  */
    public boolean htmlStringParser(String html, Vector mLstTag) {

        boolean intag = false;        // タグの中=true、タグの外=false
        int pos = 0;                  // ファイルの位置
        int start = 0;                // タグ開始位置
        int end = 0;                  // タグ終了位置
        String data = "";             // タグの内容

        // HTMLファイルを1文字ずつ読み込み、解析する
        for (int i = 0; i < html.length(); i ++ ) {
            if (!intag) {
                if (html.charAt(i) == '{'){
                    intag = true;           //タグの開始位置を検出
                    start = pos;            //開始位置
                    data = data + html.charAt(i);   //文字をコピー
                }
            } else {
                data = data + html.charAt(i);       //文字をコピー
                if (html.charAt(i) == '}') {
                    intag = false;          //HTMLタグの中なら、タグの終了位置を検出
                    end = pos;              //終了位置

                    TagItem item = new TagItem();
                    item.tagString  = data;
                    item.sPos       = start;
                    item.ePos       = end;
                    mLstTag.add(item);
                    data = "";
                    start = 0;
                     end = 0;
                }
            }
            pos++;
        }
        return true;
    }



/**
  * (Mapper_Type3)
  * 入力ファイルからマップ情報のタグ情報だけをぬきだす
  *
  *     <MAPREPEAT> .... </MAPREPEAT>
  *
  * @param  String      in_file         HTMLファイル名
  * @param  Vector      inTag_lst       マップ情報のリスト
  *
  * @return void
  */
     private void inRepeatFileParser(String in_file, Vector inTag_lst) throws Exception{

        String startTag = "<MAPREPEAT";
        String endTag   = "</MAPREPEAT";

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

        // START TAG
        for (int n = 0; n < tag_lst.size(); n ++ ) {
            int ePos=0;

            // HTMLタグ情報の取得
            TagItem sTag = (TagItem)tag_lst.elementAt(n);

            //大文字に変換
            String startTagUp  = sTag.tagString.toUpperCase();

            // 先頭検索
            if (startTagUp.indexOf(startTag) != -1) {

                String name = getAttrValue(sTag.tagString, "name");
                ePos  = sTag.ePos;
                // END TAG
                for (int m = n; m < tag_lst.size(); m ++ ) {

                    // HTMLタグ情報の取得
                    TagItem eTag = (TagItem)tag_lst.elementAt(m);

                    //大文字に変換
                    String endTagUp  = eTag.tagString.toUpperCase();

                    // 先頭検索
                    if (endTagUp.indexOf(endTag) != -1) {
                        ePos = eTag.ePos;
                        break;
                    }
                }
                TagItem srcTag = new TagItem();
                srcTag.tagName     = "MAPREPEAT";
                srcTag.keyName     = name;
                srcTag.sPos        = sTag.sPos;
                srcTag.ePos        = ePos;
                srcTag.tagString   = getRepeat(in_file, srcTag.sPos, srcTag.ePos);
                inTag_lst.add(srcTag);
            }
      }
  }


/**
  * ファイルが存在するかどうか、読み込み可能かどうかを確認する
  *
  * @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;
    }

}