/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hu.astrid.xmltools.beantoxml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * The abstract superclass of XmlConverterAbstract classes
 * @author István Zoltán Nagy
 */
public class XmlToBeanConverter extends XmlConverter {

    /**
     * The input stream of the xml document
     */
    private InputStream input;

    /**
     * Returns the input stream of the XML
     * @return input
     */
    public InputStream getInput() {
        return input;
    }

    /**
     * Sets the input stream of the XML
     * @param input The new stream
     */
    public void setInput(InputStream input) {
        this.input = input;
    }

    /**
     * Standard constructor
     */
    public XmlToBeanConverter() {
    }

    /**
     * Creates an XmlToBeanConverter and sets the input source
     * @param input The input stream
     */
    public XmlToBeanConverter(InputStream input) {
        this.input = input;
    }

    /**
     * Creates an XmlToBeanConverter and sets the input source
     * @param input The URL of the input
     */
    public XmlToBeanConverter(URL input) throws IOException {
        this(input.openStream());
    }

    /**
     * Creates an XmlToBeanConverter and sets the input source
     * @param input The input file
     */
    public XmlToBeanConverter(File input) throws FileNotFoundException {
        this(new FileInputStream(input));
    }



    /**
     * Get node value
     * @param node Root node.
     * @param chain Nodes chain to requested value
     * @return requested node value
     */
    public String getNodeValue(Node node, String... chain) {
        Node n = getNode(node, chain);
        try {
            return n.getFirstChild().getNodeValue();
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * Get node
     * @param node Root node.
     * @param chain Nodes chain to requested node
     * @return requested node
     */
    public Node getNode(Node node, String... chain) {
        List<Node> list = getNodes(node, chain);
        return (list == null || list.isEmpty() ? null : list.get(0));
    }

    /**
     * Get node list
     * @param node Root node.
     * @param chain Nodes chain to requested node
     * @return requested nodes
     */
    public List<Node> getNodes(Node node, String... chain) {
        List<Node> result = new ArrayList<Node>();
        Node step = node;
        boolean last = false;

        for (int k = 0; k < chain.length; k++) {
            if (k == (chain.length - 1)) {
                last = true;
            }
            String s = chain[k];
            NodeList nl = step.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node n = nl.item(i);
                if (n.getNodeName().equals(s)) {
                    if (!last) {
                        step = n;
                        break;
                    } else {
                        result.add(n);
                    }
                }
            }

        }
        return result;
    }

    /**
     * Returns a new Node instance with the specified name and text value
     * @param doc The document, to create the Node
     * @param nodeName The name of the new node
     * @param value The value of the node (the textnode child of the original node)
     * @return node The new Node
     */
    public static Node getNodeWithValue(Document doc, String nodeName, String value) {
        Node n = doc.createElement(nodeName);
        n.appendChild(doc.createTextNode(value));
        return n;
    }

    /**
     * Parses the specified XML Document, which can be found at the stream of the current instance
     * @return doc The DOM document of the XML.
     * @throws ParserConfigurationException When the Parser can't be configured
     * @throws IOException When the input is not possible
     * @throws SAXException When the xml document is not valid
     */
    public Document parseDocument() throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilder builder = this.createDocumentBuilder(this.createDocumentBuilderFactory());
        return builder.parse(input);
    }
}
