/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
package uk.ac.ic.doc.kevlar.core.framework.internal.loaders.xmlparse;

import java.io.IOException;
import java.io.InputStream;

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

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

/**
 * Provides IProgramXMLFile's from a provided InputStream
 * 
 * @author tora
 */
public class ProgramXMLFileProvider {

    private static DocumentBuilderFactory factory;

    static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";

    static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";

    static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";

    /**
     * TODO toa02: the error handling in here. Given an input stream, gets back
     * an IProgramXMLFile representation
     * 
     * @param is
     *            The input stream to draw the XML file from.
     * @return An IProgramXMLFile which can return the three elements.
     */
    public static synchronized IProgramXMLFile getProgramXMLFile(InputStream is) {
        if (factory == null) {
            //TODO: toa02 setup the factory with the schema location so
            // semantics are
            // guaranteed.
            factory = DocumentBuilderFactory.newInstance();

            //factory.setNamespaceAware(true);
            factory.setValidating(false);
            factory.setNamespaceAware(false);
            //factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
            //factory.setAttribute(JAXP_SCHEMA_SOURCE,
            //        new File(Kevlar.class.getResource("api/programs/program.xsd").getFile()));
            
        }

        try {
            DocumentBuilder db = factory.newDocumentBuilder();
            Document d = db.parse(is);

            return new ProgramXMLFile(d);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    static class ProgramXMLFile implements IProgramXMLFile {

        private Element argModel = null;

        private Element descModel = null;

        private Element retModel = null;

        /**
         * Construct a ProgramXMLFile from a given document. The assertion
         * errors are only thrown if the semantics of the schema of the XML
         * document isn't respected.
         * 
         * i.e. no assertion errors should be thrown from here, but I try and
         * code fail-fast :)
         * 
         * @param d
         */
        public ProgramXMLFile(Document d) {

            NodeList nl = d.getElementsByTagName("program");

            Node root = nl.item(0);

            if (!root.getNodeName().equals("program")) {
                //This should be filtered out by schema validation
                throw new AssertionError("program expected");
            }

            NodeList kids = root.getChildNodes();

            for (int i = 0; i < kids.getLength(); i++) {

                if (kids.item(i).getNodeName().equals("argument-model")) {
                    if (argModel != null) {
                        throw new AssertionError(
                                "Argument model is already set up!");
                    }
                    argModel = (Element) kids.item(i);
                } else if (kids.item(i).getNodeName().equals(
                        "description-model")) {
                    if (descModel != null) {
                        throw new AssertionError(
                                "Description model is already set up!");
                    }
                    descModel = (Element) kids.item(i);
                } else if (kids.item(i).getNodeName().equals("return-model")) {
                    if (retModel != null) {
                        throw new AssertionError(
                                "Return model is already set up!");
                    }
                    retModel = (Element) kids.item(i);
                }
            }

            if (argModel == null || descModel == null || retModel == null) {
                throw new AssertionError("Some model(s) are not setup!");
            }
        }

        /**
         * @see uk.ac.ic.doc.kevlar.core.framework.internal.loaders.xmlparse.IProgramXMLFile#getArgumentModel()
         */
        public Element getArgumentModel() {
            return argModel;
        }

        /**
         * @see uk.ac.ic.doc.kevlar.core.framework.internal.loaders.xmlparse.IProgramXMLFile#getDescriptionModel()
         */
        public Element getDescriptionModel() {
            return descModel;
        }

        /**
         * @see uk.ac.ic.doc.kevlar.core.framework.internal.loaders.xmlparse.IProgramXMLFile#getReturnModel()
         */
        public Element getReturnModel() {
            return retModel;
        }

    }
}
