/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/tools/stores/XDomain.java,v 1.3 2004/07/30 06:52:06 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:06 $
 *
 * ====================================================================
 *
 * 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.
 */
package org.apache.slide.store.tamino.tools.stores;


import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.slide.util.JDom;
import org.apache.slide.util.XAssertionFailed;
import org.apache.slide.util.XException;
import org.apache.slide.util.XUri;
import org.jdom.Comment;
import org.jdom.Element;
import org.jdom.JDOMException;


/**
 ** @author    michael.hartmeier@softwareag.com
 ** @version   $Revision: 1.3 $
 **/
public class XDomain implements XDomainConstants {
    public static XDomain create() {
        return new XDomain(createTemplate(), XDomainConfig.createDefault());
    }
    public static XDomain fromXml(Element root) throws XException {
        XDomain domain;
        XDomainConfig config;
        Iterator iter;
        
        config = XDomainConfig.fromXml(root);
        domain = new XDomain(createTemplate(root), config);
        iter = root.getChildren( XDomainConstants.NAMESPACE ).iterator();
        while ( iter.hasNext() ) {
            domain.addNamespace(XNamespace.fromXml(config.createTypes(), (Element) iter.next()));
        }
        return domain;
    }

    /**
     ** Use by toXml to create xml. See checkTemplate method for constraints.
     **/
    private Element template;
    
    /** History paths etc. Never null. */
    private XDomainConfig config;
    
    private final List namespaces;
    
    private XDomain(Element template, XDomainConfig config) {
        if (config == null) {
            throw new XAssertionFailed();
        }
        try {
            checkTemplate(template);
        } catch (XException e) {
            throw new XAssertionFailed(e);
        }
        this.template = template;
        this.config = config;
        this.namespaces = new ArrayList();
    }
    
    //-- config
    
    public XDomainConfig getConfig() {
        return config;
    }
    
    public void setConfig(XDomainConfig config) throws XException {
        Iterator iter;
        XNamespace ns;
        
        if (config == null) {
            throw new XAssertionFailed();
        }
        iter = namespaces.iterator();
        while (iter.hasNext()) {
            ns = (XNamespace) iter.next();
            if (ns.getPublicStoreGroups().size() != 0) {
                throw new XException("cannot change global configuration if there are stores configured");
            }
        }
        this.config = config;
    }
    
    //-- namespaces
    
    public List getNamespaces() {
        return namespaces;
    }
    
    public void addNamespace(XNamespace ns) {
        namespaces.add(ns);
    }
    
    /**
     ** @return null if not found
     **/
    public XNamespace lookupNamespace(String namespace) {
        Iterator iter;
        XNamespace ns;
        
        iter = namespaces.iterator();
        while (iter.hasNext()) {
            ns = (XNamespace) iter.next();
            if (ns.name.equals(namespace)) {
                return ns;
            }
        }
        return null;
    }
    
    /**
     ** @throws XException if not found
     **/
    public XNamespace getNamespace(String namespace) throws XException {
        XNamespace ns;
        
        ns = lookupNamespace(namespace);
        if (ns == null) {
            throw new XException("namespace not found: " + namespace);
        }
        return ns;
    }
    
    public String getScope(String namespace, String storeName) throws XException {
        XNamespace ns;
        XStore store;
        
        ns = getNamespace(namespace);
        store = ns.getStore(storeName);
        return store.getScope().toString();
    }
    
    public XStore getStoreByScope(XUri scope) throws XException {
        Iterator iter;
        XNamespace ns;
        XStore store;

        iter = namespaces.iterator();
        while (iter.hasNext()) {
            ns = (XNamespace) iter.next();
            store = ns.lookupStoreByScope(scope);
            if (store != null) {
                return store;
            }
        }
        throw new XException("store not found for scope " + scope);
    }
    
    //-- templates
    
    public static Element createTemplate() {
        Element root;
        
        try {
            root = JDom.forResource(XDomainConstants.RESOURCE_PREFIX + "domain.xml").getRootElement();
        }
        catch (IOException e) {
            throw new XAssertionFailed(e);
        }
        catch (JDOMException e) {
            throw new XAssertionFailed(e);
        }
        try {
            checkTemplate(root);
        }
        catch (XException e) {
            throw new XAssertionFailed(e);
        }
        return root;
    }
    
    public static Element createTemplate(Element orig) throws XException {
        Element template;
        
        template = (Element) orig.clone();
        one(template, XDomainConstants.NAMESPACE);
        one(template, Parameters.PARAMETER);
        checkTemplate(template);
        return template;
    }
    
    public List getParameterComments() {
        List result;
        Iterator iter;
        Object obj;
        Element ele;
        boolean add;

        result = new ArrayList();
        iter = template.getContent().iterator();
        add = false;
        while (iter.hasNext()) {
            obj = iter.next();
            if (obj instanceof Element) {
                ele = (Element) obj;
                if (ele.getName().equals(XDomainConstants.NAMESPACE)) {
                    add = true;
                } else if (ele.getName().equals(Parameters.PARAMETER)) {
                    add = false;
                }
            } else if (obj instanceof Comment) {
                if (add) {
                    result.add(((Comment) obj).clone());
                }
            }
        }
        return result;
    }
    
    public static void checkTemplate(Element template) throws XException {
        if (!template.getName().equals("slide")) {
            throw new XException("illegal root element, expected 'slide', got '" + template.getName() + "'");
        }
        JDom.getElement(template, XDomainConstants.NAMESPACE);
        JDom.getElement(template, Parameters.PARAMETER);
    }
    
    private static void one(Element root, String name) {
        List lst;
        
        lst = root.getChildren(name);
        if (lst.size() == 0) {
            root.addContent(new Element(name));
        } else {
            while (lst.size() > 1) {
                lst.remove(0);
            }
            ((Element) lst.get(0)).getContent().clear();
        }
    }
    
    //-- xml
    
    public Element toXml() {
        Element root;
        Iterator iter;
        List lst;
        Element g;
        List existingContent;
        List newContent;
        Object obj;
        
        root = (Element) template.clone();
        iter = namespaces.iterator();
        lst = root.getChildren(XDomainConstants.NAMESPACE);
        while (iter.hasNext()) {
            lst.add(lst.size() - 1, ((XNamespace) iter.next()).toXml());
        }
        lst.remove(lst.size() - 1); // remove namespace marker element
        root.getChildren(Parameters.PARAMETER).clear(); // remove parameter marker element
        g = config.toConfigXml();
        existingContent = root.getContent();
        newContent = g.getContent();
        iter = newContent.iterator();
        while (iter.hasNext()) {
            obj = iter.next();
            iter.remove();
            existingContent.add(obj);
        }
        return root;
    }
}
