package hibergui.generator.tool.xml;

import hibergui.generator.tool.io.BufferedStream;

import java.io.*;

import javax.xml.parsers.*;
import javax.xml.transform.dom.*;

import org.w3c.dom.*;
import org.xml.sax.*;

class DTDEntityResolver implements EntityResolver {
	InputStream DTDInputStream;
	
	public DTDEntityResolver(InputStream DTDInputStream){
		this.DTDInputStream = DTDInputStream;
	}
	
	public InputSource resolveEntity (String publicId, String systemId){
		return new InputSource(DTDInputStream);
	}
}

/**
 * Reads and validates given XML source stream. 
 * 
 * @author Csaba Gyenge
 *
 */
class XMLReader {
	private DOMSource source;

	/**
	 * Process given stream which contains XML structure. Validate it by DTD
	 * which is defined in DOCTYPE or if it doesn't exist validate it by given
	 * DTD. Finally put the result into the <code>source</code> variable
	 * 
	 * @param inputStream
	 *            Stream which is contains an XML structure.
	 * @param DTD
	 *            Stream which is contains DTD. Specify <code>null</code> if you don't
	 *            want to provide a secondary DTD document.
	 * 
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	protected XMLReader(InputStream inputStream, InputStream DTD) throws ParserConfigurationException, SAXException, IOException{
		// Create buffered stream
		BufferedStream bfr = new BufferedStream(inputStream);
		// Initialize XML reading
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setValidating(true);
		DocumentBuilder builder = factory.newDocumentBuilder();
		builder.setErrorHandler(new XMLErrorHandler());
		Document xmlDocument;
		// Try to parse and read XML file
		try {
			xmlDocument = builder.parse(bfr);
		} catch (IOException ex){
			// If DTD isn't specified throw up exception
			if (DTD == null) throw ex;
			// Set local DTD file
			builder.setEntityResolver(new DTDEntityResolver(DTD));
			// Reset the stream
			bfr.reset();
			// Try to parse and read XML file again
			xmlDocument = builder.parse(bfr);
		}
		source = new DOMSource(xmlDocument);
	}
	
	protected DOMSource getDOM(){
		return source;
	}
	
	/**
	 * This is a general method to get given node.
	 * 
	 * @param nodeType
	 *            Type of the target node.
	 * @param node
	 *            Root of searching.
	 * @param nodePath
	 *            Path to the target node from the root node. For example if you
	 *            want to get the node called <code>targetnode</code> and the
	 *            node called <code>child</code> is child of the node which is
	 *            specified in second parameter then value of this parameter
	 *            will be <code>child.grandchild.targetnode</code>
	 * @param attribute
	 *            If you want to get the attribute of target node, then set the
	 *            name of attribute here, otherwise set it to <code>null</code>.
	 */
	protected Node getNode(short nodeType, Node node, String nodePath, String attribute){
		int index = nodePath.indexOf(".");
		String actNodeName = index == -1 ? nodePath : nodePath.substring(0, index);
		String nextNodePath = index == -1 ? null : nodePath.substring(index + 1);
		
		for (int i = 0; i < node.getChildNodes().getLength(); i++){
			Node nextNode = node.getChildNodes().item(i);
			// Did we find the node?
			if (nextNode.getNodeName().equals(actNodeName) && nextNode.getNodeType() == nodeType){
				if (nextNodePath == null) {
					if (attribute == null || attribute.equals("")){
						return nextNode;
					} else {
						return nextNode.getAttributes().getNamedItem(attribute);
					}
				} else {
					Node n = getNode(nodeType, nextNode, nextNodePath, attribute);
					if (n != null){
						return n;
					}
				}
			}
		}
		
		return null;
	}
}
