/*
 * DomParser.java
 *
 * Created on 2007/12/04, 13:10
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.itscool.commons.document;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.itscool.commons.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * XML DOMツリーの操作クラス
 * @author kanou
 */
public class DomParser {
    protected Document document;
    
    /**
     * デフォルトコンストラクタ
     */
    public DomParser(){}
    
    /**
     * 引数に指定されたDocumentインスタンスでDomParaserインスタンスを
     * 初期化します
     * @param document Documentインスタンス
     */
    public DomParser(Document document){
        this.document = document;
    }
    
    public Document getDocument(){
        return document;
    }
    
    /**
     * XML文字列からDocumentインスタンスを展開します
     * @param message XML文字列
     * @param enc エンコード
     */
    public void parseText(String message, String enc) throws Exception{
        byte[] buf = message.getBytes(enc);
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        ByteArrayInputStream bIn = new ByteArrayInputStream(buf);
        Document document = builder.parse( bIn  );
        
        this.document = document;
    }
    
    /**
     * 引数pathで指定されたファイルからDocumentインスタンスを展開します
     * @param path XMLファイルのファイルパス
     * @param encode 文字エンコード
     */
    public void parse(String path, String encode) throws Exception{
        DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = dbfactory.newDocumentBuilder();
        FileInputStream fin = new FileInputStream(path);
        InputStreamReader isr = new InputStreamReader(fin, encode);
        InputSource is = new InputSource();
        document = builder.parse(is);
        isr.close();
        fin.close();
    }
    
    /**
     * DomParserに展開済みのDocumentインスタンスをファイルに出力します
     * @param path ファイルの出力先
     * @param outEncode 出力先のエンコード
     */
    public void save(String path, String outEncode) throws Exception{
        FileOutputStream out = new FileOutputStream(path);
        OutputStreamWriter fw = new OutputStreamWriter(out, outEncode);
        TransformerFactory transFactory = TransformerFactory.newInstance();
        Transformer transformer = transFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING, outEncode);
        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(fw);
        transformer.transform(source, result);
        out.flush();
        out.close();
    }
    
    /**
     * 展開済みのDocumentインスタンスから引数nodeNamesで指定されたタグリストの
     * 中から、最初の１件を返します
     * @param nodeNames 取得するタグ名（タグの階層を"."で区切ることができます）。
     *　（Documentルートからの絶対パスを指定してください）
     * @return 該当のタグをNodeインスタンスで返します。該当するタグが見つからなかった場合は
     * nullを返します。
     */
    public Node getFirstElementsByTagNames(String nodeNames) throws Exception{
        //Document document = getDocument(message, enc);
        NodeList nodeList = document.getChildNodes();
        
        NodeList resultList = getElementsByTagNames(nodeList, nodeNames);
        if( resultList.getLength() == 0 ){
            return null;
        }else{
            return resultList.item(0);
        }
    }
    
    /**
     * 展開済みのDocumentインスタンスから指定されたタグ名をもつタグリストを取得します
     * @param nodeName タグ名（Documentルートからの絶対パスを指定してください）
     * @return タグ名に合致するタグ一覧
     */
    public NodeList getElementsByTagNames(String nodeNames) throws Exception{
        //Document document = getDocument(message, enc);
        NodeList nodeList = document.getChildNodes();
        
        return getElementsByTagNames(nodeList, nodeNames);
    }
    
    /**
     * 引数nodeListの中から、引数nodeNamesで指定された最初のタグのテキスト情報を取得します
     * @param nodeList NodeListインスタンス
     * @param nodeName タグ名（タグの階層を"."で区切ることができます）。
     * （NodeListインスタンスからの絶対パスで指定してください）
     */
    public String getFirstTextElementByTagNames(NodeList nodeList, String nodeNames ) throws Exception{
        Node firstNode = getFirstElementsByTagNames(nodeList, nodeNames);
        NodeList childs = firstNode.getChildNodes();
        if( childs.getLength() == 0 ){
            return null;
        }else{
            return childs.item(0).getNodeValue();
        }
    }
    
    /**
     * 引数nodeListの中から、引数nodeNamesで指定された最初のタグを取得します
     * @param nodeList NodeListインスタンス
     * @param nodeName タグ名（タグの階層を"."で区切ることができます）。
     * （NodeListインスタンスからの絶対パスで指定してください）
     * @return nodeListの中でnodeNamesに合致したタグを返します
     */
    public Node getFirstElementsByTagNames(NodeList nodeList, String nodeNames ) throws Exception{
        NodeList resultNodeList = getElementsByTagNames(nodeList, nodeNames);
        if( resultNodeList.getLength() == 0 ){
            return null;
        }else{
            return resultNodeList.item(0);
        }
    }
    
    /**
     * 引数nodeListの中から、引数nodeNamesで指定されたタグの一覧を取得します
     * @param nodeList NodeListインスタンス
     * @param nodeName タグ名（タグの階層を"."で区切ることができます）。
     * （NodeListインスタンスからの絶対パスで指定してください）
     * @return nodeListの中でnodeNamesに合致したタグの一覧を返します
     */
    public NodeList getElementsByTagNames(NodeList nodeList, String nodeNames ) throws Exception{
        List tagNameList = StringUtil.split(nodeNames, ".");
        Node parentDummyNode = document.createElement("RESULT_TEMP_NODE");
        
        for( int i=0; i<nodeList.getLength(); i++){
            Node node = nodeList.item(i);
            if( tagNameList.size() == 0 ){
                return null;
            }else if( !node.getNodeName().equals((String)tagNameList.get(0))){
                continue;
            }else if( tagNameList.size() == 1 ){
                //resultNodeList.add(node);
                parentDummyNode.appendChild(node);
                
                continue;
            }else{
                NodeList childNodeList = node.getChildNodes();
                createNodeListByTagNames(parentDummyNode, tagNameList, childNodeList, 1);
            }
        }
        return parentDummyNode.getChildNodes();
    }
    
    protected void createNodeListByTagNames(Node parentDummyNode, List tagNameList, NodeList nodeList, int nodeFloor){
        int nodeNum = nodeList.getLength();
        for(int i=0; i<nodeNum; i++){
            Node childNode = nodeList.item(i);
            String checkNodeName = (String)tagNameList.get(nodeFloor);
            String childNodeName = childNode.getNodeName();
            if( checkNodeName.equals(childNodeName)){
                if( (nodeFloor+1) == tagNameList.size() ){
                    parentDummyNode.appendChild(childNode.cloneNode(true));
                }else{
                    NodeList magoNodeList = childNode.getChildNodes();
                    createNodeListByTagNames(parentDummyNode, tagNameList, magoNodeList, (nodeFloor + 1) );
                }
            }
        }
    }
}
