/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/datastore/schema/XSchemaKnownProperty.java,v 1.3 2004/07/30 06:51:56 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:51:56 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/**
 *
 *
 * @author martin.wallmer@softwareag.com
 *
 * @version $Revision: 1.3 $
 */
package org.apache.slide.store.tamino.datastore.schema;

import com.softwareag.tamino.db.api.objectModel.TXMLObject;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.util.XAssertionFailed;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.DOMOutputter;

public class XSchemaKnownProperty {

    private static final String KNOWN_PROPERTY_TEMPLATE = "knownProperty_schema.xml";
    private static final String METADATA_TEMPLATE =       "metadata_schema3.xml";
    private static String TEMPLATE_KNOWN_PROPERITIES =    "metadata_schema3_knownProperties.xml";

    private static final Namespace TSD3_NAMESPACE =
        Namespace.getNamespace (XGlobals.TAMINO_TSD_3_NAMESPACE_PREFIX,
                                XGlobals.TAMINO_TSD_3_NAMESPACE_URI);

    private static final Namespace XS_NAMESPACE =
        Namespace.getNamespace ("xs", XGlobals.XML_SCHEMA_NAMESPACE_URI);




    /**
     * creates the metadata schema for a specific content collection.
     *
     *
     * @param    contentCollection   a  String
     *
     * @return   a TXMLObject
     *
     * @throws   JDOMException
     *
     */
    public static TXMLObject getMetadataSchema (String contentCollection, String tsdLanguage)
        throws JDOMException, IOException {
        TXMLObject result;
        Element schemaElement;

        // get the template
        InputStream in;
        in = XSchemaKnownProperty.class.getResourceAsStream (METADATA_TEMPLATE);

        SAXBuilder sax = new SAXBuilder();
        Document template = sax.build (in);
        schemaElement = template.getRootElement();

        setXmlNs (schemaElement, tsdLanguage);

        importKnownPropertiesSchemas (schemaElement, tsdLanguage);
        adoptSchema (schemaElement, contentCollection, tsdLanguage);
        referenceProperties (schemaElement, tsdLanguage);

        //System.out.println(JDom.toString (schemaElement));

        result = TXMLObject.newInstance(new DOMOutputter().output(template));

        return result;
    }


    public static TXMLObject getKnownPropertySchema (Namespace propertyNamespace,
                                                     String schemaLanguage)  {
        TXMLObject result = null;

        InputStream in = XSchemaKnownProperty.class.getResourceAsStream (KNOWN_PROPERTY_TEMPLATE);
        try {
            SAXBuilder sax = new SAXBuilder();
            Document template = sax.build (in);
            Element root = template.getRootElement();
            root.setAttribute ("targetNamespace", propertyNamespace.getURI());

            root.addNamespaceDeclaration (propertyNamespace);

            Element el = getFirstElement ("xs:annotation/xs:appinfo/tsd:schemaInfo", root);
            el.setAttribute ("name", propertyNamespace.getPrefix());

            List knownPropertyList = getKnownProps (propertyNamespace, schemaLanguage);
            Iterator it = knownPropertyList.iterator();
            while (it.hasNext()) {
                Element propElem = (Element)it.next();
                root.addContent (propElem);
            }

            result = TXMLObject.newInstance(new DOMOutputter().output(template));
            //System.out.println (JDom.toString(root));
        } catch (Exception e) {
            e.printStackTrace();
            throw new XAssertionFailed (e);
        }

        return result;
    }

    private static List getKnownProps (Namespace namespace, String schemaLanguage) throws JaxenException {
        List knownProps = new ArrayList ();

        KnownPropertyList kpl = XKnownPropertyHelper.getKnownPropertyList (schemaLanguage);

        InputStream in = XSchemaFactory.class.getResourceAsStream (TEMPLATE_KNOWN_PROPERITIES);
        Element knownPropTemplate;
        try {
            SAXBuilder sax = new SAXBuilder();
            knownPropTemplate = sax.build( in ).getRootElement();

            //System.out.println(JDom.toString (indexedPropTemplate));

            XPath xPathShadow = new JDOMXPath ("xs:complexType/xs:sequence/xs:element");
            xPathShadow.addNamespace (XS_NAMESPACE.getPrefix(), XS_NAMESPACE.getURI());

            XPath xPathIndex =
                new JDOMXPath ("xs:annotation/xs:appinfo/tsd:elementInfo/tsd:physical/tsd:native/tsd:index");

            xPathIndex.addNamespace (XS_NAMESPACE.getPrefix(), XS_NAMESPACE.getURI());
            xPathIndex.addNamespace (TSD3_NAMESPACE.getPrefix(),
                                     TSD3_NAMESPACE.getURI());

            Iterator it = kpl.knownPropertyIterator (namespace);

            while (it.hasNext()) {
                KnownProperty knownProp = (KnownProperty)it.next();
                Element knownPropElement = (Element) knownPropTemplate.clone();

                Element shadowElem = (Element) xPathShadow.selectSingleNode (knownPropElement);
                shadowElem.setAttribute ("type", knownProp.getDataType());
                //System.out.println (JDom.toString(el));

                knownPropElement.setAttribute ("name", knownProp.getName());
                knownProps.add (knownPropElement);

                // add index or remove the annotation, if there is no Index
                if (knownProp.isStandardIndexed() || knownProp.isTextIndexed()) {
                    Element indexElem = (Element) xPathIndex.selectSingleNode (shadowElem);
                    if (knownProp.isStandardIndexed())
                        indexElem.addContent (new Element ("standard", TSD3_NAMESPACE));

                    if (knownProp.isTextIndexed())
                        indexElem.addContent (new Element ("text", TSD3_NAMESPACE));
                }
                else {
                    Element annotationElement = shadowElem.getChild ("annotation", XS_NAMESPACE);
                    shadowElem.removeContent (annotationElement);
                }
            }

        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return knownProps;
    }

    private static void referenceProperties (Element schemaElement, String schemaLanguage) {
        String xPath = "/xs:schema/xs:element/xs:complexType/xs:sequence/xs:choice/xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:choice";
        Element choice = getFirstElement (xPath, schemaElement);
        KnownPropertyList kpl = XKnownPropertyHelper.getKnownPropertyList(schemaLanguage);
        Iterator namespaceIt = kpl.getNamespaceIterator();

        while (namespaceIt.hasNext()) {
            Namespace ns = (Namespace)namespaceIt.next();
            Iterator knownPropIt = kpl.knownPropertyIterator(ns);
            while (knownPropIt.hasNext()) {
                KnownProperty knownProp = (KnownProperty)knownPropIt.next();
                Element prop = new Element ("element", XS_NAMESPACE);
                String ref = ns.getPrefix() + ":" + knownProp.getName();
                prop.setAttribute ("ref", ref);
                choice.addContent (prop);
            }
        }


    }

    private static void adoptSchema (Element schemaElement, String contentCollection, String tsdLanguage) {

        String xPath = "/xs:schema/xs:annotation/xs:appinfo/tsd:schemaInfo";
        Element schemaInfo = getFirstElement (xPath, schemaElement);
        schemaInfo.setAttribute ("name", contentCollection);
        Element collection = getFirstElement ("tsd:collection", schemaInfo);
        collection.setAttribute ("name", XGlobals.META_COLLECTION);
        Element doctype = getFirstElement ("tsd:doctype", schemaInfo);
        doctype.setAttribute ("name", contentCollection);
        Element element = getFirstElement ("xs:element", schemaElement);
        element.setAttribute ("name", contentCollection);

        // write Version
        Element tsdVersion = getFirstElement ("tsd:adminInfo/tsd:version", schemaInfo);
        tsdVersion.setText (tsdLanguage);

        String now = XGlobals.XS_DATE_FORMATTER.format(new Date());
        Element date = getFirstElement ("tsd:adminInfo/tsd:created", schemaInfo);
        date.setText (now);
        date = getFirstElement ("tsd:adminInfo/tsd:modified", schemaInfo);
        date.setText (now);


    }

    private static void importKnownPropertiesSchemas (Element schemaElement, String tsdLanguage) {
        Iterator it =
            XKnownPropertyHelper.getKnownPropertyList(tsdLanguage).getNamespaceIterator();

        List content = schemaElement.getChildren();


        while (it.hasNext()) {
            Namespace namespace = (Namespace)it.next();
            Element importEl = new Element ("import", XS_NAMESPACE);
            importEl.setAttribute ("namespace", namespace.getURI());
            importEl.setAttribute ("schemaLocation", "./" + namespace.getPrefix());
            content.add (1, importEl);
        }

    }

    private static void setXmlNs (Element schemaElement, String tsdLanguage) {
        Iterator it = XKnownPropertyHelper.getKnownPropertyList(tsdLanguage).getNamespaceIterator();
        while (it.hasNext()) {
            Namespace ns = (Namespace)it.next();
            schemaElement.addNamespaceDeclaration (ns);
        }
    }

    /**
     * returns the first Element matching xPath relative to the context.
     *
     * @param    sPath               a  String
     * @param    context             an Element
     *
     * @return   an Element
     *
     */
    private static Element getFirstElement (String sPath, Element context) {
        try {
            XPath xPath = new JDOMXPath (sPath);
            xPath.addNamespace (XS_NAMESPACE.getPrefix(), XS_NAMESPACE.getURI());
            xPath.addNamespace (TSD3_NAMESPACE.getPrefix(), TSD3_NAMESPACE.getURI());

            Element result = (Element) xPath.selectSingleNode (context);

            return result;
        } catch (JaxenException e) {
            throw new XAssertionFailed (e);
        }
    }
}

