/*                          
 * mBench: The Open Source Micro Benchmark Tool 
 *                                             
 * Distributable under GPL license. 
 * See terms of license at gnu.org.                 
 *
 * Copyright (C) 2005 Sumisho Computer Systems Corp.
 */
package jp.co.scs.mbench;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import jp.co.scs.mbench.jdbc.DataSourceInfo;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 *      Utility class regarding to configuration file.
 * 
 *      @author Tetsuro Ikeda
 */
public class DocumentUtil {
    
    /**
     *  the path to configuration file
     */
    private static final String CONFIGURATION_FILE_NAME = 
        "conf/mbench.xml";

    /**
     *  default encoding type
     */
    private static final String FILE_ENCODING = 
        System.getProperty("file.encoding");
    
    /**
     *  Gets the DOM document from configration file
     * 
     *  @return DOM document
     */
    public static Document getDocument() 
            throws IOException, ParserConfigurationException, SAXException{
        return getDocument(CONFIGURATION_FILE_NAME, FILE_ENCODING);
    }

    /**
     *  Gets the DOM document from configuration file.
     * 
     *  @param fileName configuration file name (path)
     *  @return DOM document
     */
    public static Document getDocument(String fileName) 
            throws IOException, ParserConfigurationException, SAXException{
        return getDocument(fileName, FILE_ENCODING);
    }
    
    /**
     *  Gets the DOM document from configuration file.
     * 
     *  @param fileName configuration file name (path)
     *  @param encoding file encoding
     *  @return DOM document
     */
    public static Document getDocument(String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException{
        File xmlFile = new File(fileName);
        String uri = xmlFile.toURL().toString();
        InputStream istream = new FileInputStream(xmlFile);
        InputSource is =
            new InputSource(new InputStreamReader(istream, encoding));
        is.setSystemId(uri);
        
        DocumentBuilderFactory dbf =
            DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = dbf.newDocumentBuilder();
        Document document = builder.parse(is);
        
        istream.close();
        
        return document;
    }
    
    /**
     *  Gets the initializer information from configuration file
     * 
     *  @param initializerName definition name of initialization
     *  @return initiarizer information
     */
    public static InitializerInfo getInitializerInfo(String initializerName)
            throws IOException, ParserConfigurationException, SAXException {
        return getInitializerInfo(initializerName, CONFIGURATION_FILE_NAME, FILE_ENCODING);
    }
        
    /**
     *  Gets the initializer information from configuration file
     * 
     *  @param initializerName definition name of initialization
     *  @param fileName configuration file name (path)
     *  @return initiarizer information
     */
    public static InitializerInfo getInitializerInfo(String initializerName, String fileName)
            throws IOException, ParserConfigurationException, SAXException {
        return getInitializerInfo(initializerName, fileName, FILE_ENCODING);
    }

    /**
     *  Gets the initializer information from configuration file
     * 
     *  @param initializerName definition name of initialization
     *  @param fileName configuration file name (path)
     *  @param encoding configuration file encoding
     *  @return initiarizer information
     */
    public static InitializerInfo getInitializerInfo(
            String initializerName, String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException {
        InitializerInfo[] infoArray = getInitializerInfoArray(fileName, encoding);
        for (int i = 0; i < infoArray.length; i++) {
            if (infoArray[i].getName().equalsIgnoreCase(initializerName)) {
                return infoArray[i];
            }
        }
        throw new IllegalArgumentException(initializerName + " is not found.");
    }
    
    /**
     *  Gets the datasource information from configuration file.
     * 
     *  @param dataSourceName definition name fo datasource
     *  @return datasource information
     */
    public static DataSourceInfo getDataSourceInfo(String dataSourceName) 
            throws IOException, ParserConfigurationException, SAXException {
        return getDataSourceInfo(dataSourceName, CONFIGURATION_FILE_NAME, FILE_ENCODING);
    }

    /**
     *  Gets the datasource information from configuration file.
     * 
     *  @param dataSourceName definition name fo datasource
     *  @param fileName configuration file name
     *  @return datasource information
     */
    public static DataSourceInfo getDataSourceInfo(String dataSourceName, String fileName) 
            throws IOException, ParserConfigurationException, SAXException {
        return getDataSourceInfo(dataSourceName, fileName, FILE_ENCODING);
    }
    
    /**
     *  Gets the datasource information from configuration file.
     * 
     *  @param dataSourceName definition name fo datasource
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return datasource information
     */
    public static DataSourceInfo getDataSourceInfo(
            String dataSourceName, String fileName, String encoding) 
            throws IOException, ParserConfigurationException, SAXException {
        
        Node targetNode = getDataSourceNode(dataSourceName, fileName, encoding);
    
        DataSourceInfo info = new DataSourceInfo();
        info.setName(dataSourceName);
        
        NodeList childNodes = targetNode.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node childNode = childNodes.item(i);
            if (childNode.getNodeName().equalsIgnoreCase("protocol")) {
                info.setProtocol(childNode.getFirstChild().getNodeValue());
                
            } else if  (childNode.getNodeName().equalsIgnoreCase("driver")) {
                info.setDriver(childNode.getFirstChild().getNodeValue());
                
            } else if (childNode.getNodeName().equalsIgnoreCase("host")) {
                info.setHost(childNode.getFirstChild().getNodeValue());
                
            } else if (childNode.getNodeName().equalsIgnoreCase("port")) {
                info.setPort(childNode.getFirstChild().getNodeValue());
                
            } else if (childNode.getNodeName().equalsIgnoreCase("db")) {
                info.setDB(childNode.getFirstChild().getNodeValue());
                
            } else if (childNode.getNodeName().equalsIgnoreCase("user")) {
                info.setUser(childNode.getFirstChild().getNodeValue());
                
            } else if (childNode.getNodeName().equalsIgnoreCase("password")) {
                info.setPassword(childNode.getFirstChild().getNodeValue());

            } else if (childNode.getNodeName().equalsIgnoreCase("useUnicode")) {
                info.setUseUnicode(childNode.getFirstChild().getNodeValue());
            
            } else if (childNode.getNodeName().equalsIgnoreCase("characterEncoding")) {
                info.setCharacterEncoding(childNode.getFirstChild().getNodeValue());
            }
        }
        
        return info;
    }
    
    /**
     *  Gets the benchmark information from configuration file.
     * 
     *  @param benchmarkName definition name of benchmark
     *  @return benchmark information
     */
    public static BenchmarkInfo getBenchmarkInfo(String benchmarkName) 
            throws IOException, ParserConfigurationException, SAXException {
        return getBenchmarkInfo(benchmarkName, CONFIGURATION_FILE_NAME, FILE_ENCODING);
    }
    
    /**
     *  Gets the benchmark information from configuration file.
     * 
     *  @param benchmarkName definition name of benchmark
     *  @param fileName configuration file name
     *  @return benchmark information
     */
    public static BenchmarkInfo getBenchmarkInfo(String benchmarkName, String fileName) 
            throws IOException, ParserConfigurationException, SAXException {
        return getBenchmarkInfo(benchmarkName, fileName, FILE_ENCODING);
    }
    
    /**
     *  Gets the benchmark information from configuration file.
     * 
     *  @param benchmarkName definition name of benchmark
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return benchmark information
     */
    public static BenchmarkInfo getBenchmarkInfo(
            String benchmarkName, String fileName, String encoding) 
            throws IOException, ParserConfigurationException, SAXException {
        BenchmarkInfo[] info = getBenchmarkInfoArray(fileName, encoding);
        for (int i = 0; i < info.length; i++) {
            if(info[i].getName().equalsIgnoreCase(benchmarkName)) {
                return info[i];
            }
        }
        throw new IllegalArgumentException("benchmark not found named : " + benchmarkName);
    }
    
    /**
     *  Gets the multi-node information from configuration file.
     * 
     *  @param name definition name of multi-node
     *  @return multi-node information
     */
    public static MultiNodeInfo getMultiNodeInfo(String name)
            throws IOException, ParserConfigurationException, SAXException {
        return getMultiNodeInfo(name, CONFIGURATION_FILE_NAME, FILE_ENCODING);
    }
    
    /**
     *  Gets the multi-node information from configuration file.
     * 
     *  @param name definition name of multi-node
     *  @param fileName configuration file name
     *  @return multi-node information
     */
    public static MultiNodeInfo getMultiNodeInfo(String name, String fileName)
            throws IOException, ParserConfigurationException, SAXException {
        return getMultiNodeInfo(name, fileName, FILE_ENCODING);
    }
    
    /**
     *  Gets the multi-node information from configuration file.
     * 
     *  @param name definition name of multi-node
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return multi-node information
     */
    public static MultiNodeInfo getMultiNodeInfo(
            String name, String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException {
        Document document = getDocument(fileName, encoding);
        NodeList candidateList = document.getElementsByTagName("multi-client");
        for (int i = 0 ; i < candidateList.getLength(); i++) {
            Element element = (Element) candidateList.item(i);
            String candidateName = element.getAttribute("name");
            if (candidateName != null && candidateName.equalsIgnoreCase(name)) {
                MultiNodeInfo info = new MultiNodeInfo();
                // name must be set. 
                info.setName(name);
                String type = element.getAttribute("type");
                if (type == null) {
                    throw new IllegalArgumentException(
                        "type for multi-client is not defined named " + name);
                }
                if (type.equalsIgnoreCase(MultiNodeInfo.NODE_PRIMARY)) {
                    info.setNodeType(MultiNodeInfo.NODE_PRIMARY);
                } else if (type.equalsIgnoreCase(MultiNodeInfo.NODE_SECONDARY)) {
                    info.setNodeType(MultiNodeInfo.NODE_SECONDARY);
                } else {
                    throw new IllegalArgumentException(
                        "illegal type for multi-client named " + name + " : " + type);
                }
                if (element.getAttribute("host") != null) {
                    info.setLocalHost(element.getAttribute("host"));
                }
                if (element.getAttribute("port") != null) {
                    info.setLocalPort(Integer.parseInt(element.getAttribute("port")));
                }
                // only if primary node
                if (info.getNodeType().equals(MultiNodeInfo.NODE_PRIMARY)) {
                    NodeList secondaryList = element.getElementsByTagName("secondary");
                    if (secondaryList.getLength() < 1) {
                        throw new IllegalArgumentException(
                            "secondary is not defined for " + name);
                    }
                    String[] secondaryHost = new String[secondaryList.getLength()];
                    int[] secondaryPort = new int[secondaryList.getLength()];
                    info.setSecondaryHost(secondaryHost);
                    info.setSecondaryPort(secondaryPort);
                    for (int j = 0; j < secondaryList.getLength(); j++) {
                        Element secondary = (Element) secondaryList.item(j);
                        if (secondary.getAttribute("host") != null) {
                            secondaryHost[j] = secondary.getAttribute("host");
                        } else {
                            secondaryHost[j] = MultiNodeInfo.defaultLocalHost;
                        }
                        if (secondary.getAttribute("port") != null) {
                            secondaryPort[j] =
                                Integer.parseInt(secondary.getAttribute("port"));
                        } else {
                            secondaryPort[j] = MultiNodeInfo.defaultPort;
                        }
                    }
                }
                return info;
            }
        }
        throw new IllegalArgumentException(
            "multi node infomation for " + name + " is not found");
    }
    
    /**
     *  Gets the finalizer information from configuration file.
     * 
     *  @param finalizerName definition name of finalizer
     *  @return finalizer information
     */
    public static FinalizerInfo getFinalizerInfo(String finalizerName)
            throws IOException, ParserConfigurationException, SAXException {
        return getFinalizerInfo(finalizerName, CONFIGURATION_FILE_NAME, FILE_ENCODING);
    }
    
    /**
     *  Gets the finalizer information from configuration file.
     * 
     *  @param finalizerName definition name of finalizer
     *  @param fileName configuration file name
     *  @return finalizer information
     */
    public static FinalizerInfo getFinalizerInfo(String finalizerName, String fileName)
            throws IOException, ParserConfigurationException, SAXException {
        return getFinalizerInfo(finalizerName, fileName, FILE_ENCODING);
    }
    
    /**
     *  Gets the finalizer information from configuration file.
     * 
     *  @param finalizerName definition name of finalizer
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return finalizer information
     */
    public static FinalizerInfo getFinalizerInfo(
            String finalizerName, String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException {
        FinalizerInfo[] infoArray = getFinalizerInfoArray(fileName, encoding);
        for (int i = 0; i < infoArray.length; i++) {
            if (infoArray[i].getName().equalsIgnoreCase(finalizerName)) {
                return infoArray[i];
            }
        }
        throw new IllegalArgumentException(finalizerName + " is not found.");
    }
    
    /**
     *  Gets the multi-server information from configuration file.
     */
    public static MultiServerInfo getMultiServerInfo(String multiServerName)
            throws IOException, ParserConfigurationException, SAXException {
        return getMultiServerInfo(multiServerName, CONFIGURATION_FILE_NAME, FILE_ENCODING);
    }
    
    /**
     *  Gets the multi-server information from configuration file.
     */
    public static MultiServerInfo getMultiServerInfo(
            String multiServerName, String fileName)
            throws IOException, ParserConfigurationException, SAXException {
        return getMultiServerInfo(multiServerName, fileName, FILE_ENCODING);
    }
    
    /**
     *  Gets the multi-server information from configuration file.
     */
    public static MultiServerInfo getMultiServerInfo(
            String multiServerName, String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException {
        Document document = getDocument(fileName, encoding);
        NodeList candidateList = document.getElementsByTagName("multi-server");
        for (int i = 0 ; i < candidateList.getLength(); i++) {
            Element element = (Element) candidateList.item(i);
            String candidateName = element.getAttribute("name");
            if (candidateName != null && candidateName.equalsIgnoreCase(multiServerName)) {
                MultiServerInfo info = new MultiServerInfo();
                // name must be set. 
                info.setName(multiServerName);
                
                // get connection name
                NodeList connectList = element.getElementsByTagName("connect");
                if (connectList.getLength() < 1) {
                    throw new IllegalArgumentException(
                        "connect is not defined for " + multiServerName);
                }
                String[] connect = new String[connectList.getLength()];
                info.setConnect(connect);
                
                for (int j = 0; j < connectList.getLength(); j++) {
                    Node connectNode = connectList.item(j);
                    connect[j] = connectNode.getFirstChild().getNodeValue();
            System.out.println(connect[j]);
                }
                return info;
            }
        }
        throw new IllegalArgumentException(
            "multi node infomation for " + multiServerName + " is not found");
    }
    
    
    /**
     *  Gets the initializer information array from configuration file.
     * 
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return initializer information array
     */
    private static InitializerInfo[] getInitializerInfoArray(String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException{
        Document document = getDocument(fileName, encoding);
        Element rootElement = document.getDocumentElement();
        NodeList nodeList = rootElement.getElementsByTagName("initializer");
        InitializerInfo[] infoArray = new InitializerInfo[nodeList.getLength()];
        
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            NamedNodeMap attributeMap = node.getAttributes();
            Node nameNode = attributeMap.getNamedItem("name");
            infoArray[i] = new InitializerInfo();
            infoArray[i].setName(nameNode.getNodeValue());
            attributeMap.getNamedItem("type");

            NodeList childNodeList = node.getChildNodes();
            for (int j = 0; j < childNodeList.getLength(); j++) {
                Node childNode = childNodeList.item(j);
                if (childNode.getNodeName().equalsIgnoreCase("class")) {
                    infoArray[i].setClassName(childNode.getFirstChild().getNodeValue());
                    break;
                }
            }           
        }
        return infoArray;
    }

    /**
     *  Gets the DataSource node from configuration file.
     * 
     *  @param dataBaseName definition name of dataSource
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return initializer information
     */
    private static Node getDataSourceNode(
            String databaseName, String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException{
        Document document = DocumentUtil.getDocument(fileName, encoding);
        document.getDocumentElement();
        NodeList nodeList = document.getElementsByTagName("datasource");
        Node targetNode = null;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node candidateNode = nodeList.item(i);
            NamedNodeMap attributeMap = candidateNode.getAttributes();
            Node nameNode = attributeMap.getNamedItem("name");
            if (nameNode.getNodeValue().equalsIgnoreCase(databaseName)) {
                targetNode = candidateNode;
                break;
            }
        }
        if (targetNode == null) {
            throw new IllegalArgumentException("databaseName : "
                + databaseName + " is not found in XML document.");
        }
        return targetNode;
    }
    
    /**
     *  Gets the benchmark information array from configuration file.
     * 
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return benchmark information array
     */
    private static BenchmarkInfo[] getBenchmarkInfoArray(String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException{
        Document document = getDocument(fileName, encoding);
        Element rootElement = document.getDocumentElement();
        NodeList nodeList = rootElement.getElementsByTagName("benchmark");
        BenchmarkInfo[] infoArray = new BenchmarkInfo[nodeList.getLength()];
        
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            NamedNodeMap attributeMap = node.getAttributes();
            Node nameNode = attributeMap.getNamedItem("name");
            infoArray[i] = new BenchmarkInfo();
            infoArray[i].setName(nameNode.getNodeValue());
            NodeList childNodeList = node.getChildNodes();
            for (int j = 0; j < childNodeList.getLength(); j++) {
                Node childNode = childNodeList.item(j);
                if (childNode.getNodeName().equalsIgnoreCase("class")) {
                    infoArray[i].setClassName(childNode.getFirstChild().getNodeValue());
                    break;
                }
            }     
        }
        return infoArray;
    }
    
    /**
     *  Gets the finalizer information array from configuration file.
     * 
     *  @param fileName configuration file name
     *  @param encoding configuration file encoding
     *  @return finalizer information array
     */
    private static FinalizerInfo[] getFinalizerInfoArray(String fileName, String encoding)
            throws IOException, ParserConfigurationException, SAXException{
        Document document = getDocument(fileName, encoding);
        Element rootElement = document.getDocumentElement();
        NodeList nodeList = rootElement.getElementsByTagName("finalizer");
        FinalizerInfo[] infoArray = new FinalizerInfo[nodeList.getLength()];
        
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            NamedNodeMap attributeMap = node.getAttributes();
            Node nameNode = attributeMap.getNamedItem("name");
            infoArray[i] = new FinalizerInfo();
            infoArray[i].setName(nameNode.getNodeValue());
            attributeMap.getNamedItem("type");

            NodeList childNodeList = node.getChildNodes();
            for (int j = 0; j < childNodeList.getLength(); j++) {
                Node childNode = childNodeList.item(j);
                if (childNode.getNodeName().equalsIgnoreCase("class")) {
                    infoArray[i].setClassName(childNode.getFirstChild().getNodeValue());
                    break;
                }
            }     
        }
        return infoArray;
    }
}
