/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/tools/stores/XStoreGroup.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.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.Scope;
import org.apache.slide.store.tamino.store.XParentStore;
import org.apache.slide.store.tamino.tools.stores.XStore;
import org.apache.slide.util.Configuration;
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.Element;

/**
 ** An array of stores.
 **
 ** @author michael.hartmeier@softwareag.com
 ** @version $Revision: 1.3 $
 **/
public class XStoreGroup {
    public final boolean internal;
    
    /** never null. Cannot be changed - to change it we had to change all store names as well. */
    public final String name;
    
    /** may be null for "unkown" */
    private final XStoreGroupType type;
    
    private final XStore[] stores;
    
    public XStoreGroup(boolean internal, String name, XStoreGroupType type, XStore[] stores) {
        if (name == null) {
            throw new XAssertionFailed();
        }
        if (type != null && type.types.length != stores.length) {
            throw new XAssertionFailed();
        }
        this.internal = internal;
        this.name = name;
        this.type = type;
        this.stores = stores;
    }
    
    public XParentStore[] getParentStores(Namespace namespace) {
        XParentStore[] parentStore;
        XStore store;
        int i;
        
        parentStore = new XParentStore[stores.length];
        for (i = 0; i < stores.length; i++) {
            store = stores[i];
            parentStore[i] = (XParentStore) namespace.getStore( new Scope(store.getScope().toString()));
            if (parentStore[i] == null) {
                throw new XAssertionFailed("no parent store for scope " + store.getScope());
            }
        }
        return parentStore;
    }

    public void forAll(Visitor visitor) {
        int i;

        if (type == null) {
            throw new XAssertionFailed();
        }
        for (i = 0; i < stores.length; i++) {
            visitor.visit(type.types[i], stores[i]);
        }
    }
    
    public XStore lookupStore(String name) {
        int i;
        XStore store;
        
        for (i = 0; i < stores.length; i++) {
            store = stores[i];
            if (name.equals(store.getName())) {
                return store;
            }
        }
        return null;
    }
    
    public void checkCompatible(XStoreGroup cmp) throws XException {
        int i;
        boolean newBinding;
        
        if (type != cmp.type) {
            throw new XException("incompatible store type");
        }
        if (stores.length != cmp.stores.length) {
            throw new XAssertionFailed("same store types must have same number of stores");
        }
        for (i = 0; i < stores.length; i++) {
            newBinding = cmp.stores[i].useBinding();
            if (newBinding && ! Configuration.useGlobalBinding()) {
                throw new XException("cannot enabled binding, it's globallay disabled");
                
            }
            if (stores[i].useBinding() != newBinding) {
                throw new XException("incompatible settings for useBinding");
            }
        }
    }
    
    public XStore lookupByScope(XUri scope) {
        int i;
        XStore store;
        
        for (i = 0; i < stores.length; i++) {
            store = stores[i];
            if (scope.equals(store.getScope())) {
                return store;
            }
        }
        return null;
    }
    
    
    //-- xml (domain.xml)
    
    public void addXmlDefinition(Element definition, List result) {
        int i;
        XStore s;
        
        for (i = 0; i < stores.length; i++) {
            s = stores[i];
            s.addXmlDefinition(definition);
            result.add(s);
        }
    }
    
    /**
     ** @param allStores in-out argument
     ** @return allStores of StoreGroup object
     **/
    public static List fromStores(XStoreGroupTypeList groupTypes, List allStores) throws XException {
        XStore anchor;
        List groupList;
        List storeList;
        String groupName;
        XStoreGroup group;
        
        groupList = new ArrayList();
        while (allStores.size() > 0) {
            anchor = (XStore) allStores.get(0);
            groupName = groupTypes.types.lookupGroupName(anchor);
            if (groupName == null) {
                group = createUnkown(anchor, "has unkown type (group not found)");
                allStores.remove(0);
            } else {
                storeList = extractStores(groupTypes.types, allStores, groupName);
                group = groupTypes.probe(groupName, storeList);
                if (group == null) {
                    // no group type we know ...
                    allStores.addAll(storeList);
                    allStores.remove(anchor);
                    group = createUnkown(anchor, "has unkown type (group incomplete)");
                }
            }
            groupList.add(group);
        }
        return groupList;
    }
    
    private static int unkownCount = 0;
    
    private static XStoreGroup createUnkown(XStore store, String msg) {
        XStore[] array;
        
        unkownCount++;
        array = new XStore[] { store };
        System.out.println("warning: store '" + store.getName() + "' " + msg);
        return new XStoreGroup(true, "$unkown" + unkownCount, null, array);
    }
    
    /** @return List of stores with the specified name **/
    private static List extractStores(XStoreTypeList types, List storeList, String groupName) throws XException {
        List found;
        Iterator iter;
        XStore store;
        
        found = new ArrayList();
        iter = storeList.iterator();
        while (iter.hasNext()) {
            store = (XStore) iter.next();
            if (groupName.equals(types.lookupGroupName(store))) {
                found.add(store);
                iter.remove();
            }
        }
        return found;
    }
    
    
    //-- xml (config.xml)
    
    public static XStoreGroup fromConfigXml(XStoreGroupTypeList groupTypes, String name, Element root)
        throws XException
    {
        int i;
        Element sub;
        XStoreGroupType type;
        XStoreGroup result;
        
        type = groupTypes.lookup(JDom.getAttribute(root, XDomainConstants.TYPE, groupTypes.userTamino.getName()));
        result = type.create(name);
        result.setParameter(Parameters.fromXml(root));
        for (i = 0; i < result.stores.length; i++) {
            sub = root.getChild(type.types[i].name);
            if (sub != null) {
                result.stores[i].parms.setAll(Parameters.fromXml(sub));
            }
        }
        return result;
    }
    
    public void setParameter(Parameters defaultParms) {
        int i;
        
        for (i = 0; i < stores.length; i++) {
            stores[i].parms.setAll(defaultParms);
        }
    }
    
    public Element toConfigXml(String namespace) {
        Parameters defaultParms;
        Element result;
        int i;
        Element dest;
        Parameters tmp;
        
        result = new Element( XDomainConstants.CONFIGURATION );
        result.setAttribute( XDomainConstants.NAMESPACE, namespace );
        result.setAttribute( XDomainConstants.TYPE, type.getName());
        tmp = new Parameters();
        defaultParms = stores[0].parms;
        defaultParms.addXml(result);
        for (i = 1; i < stores.length; i++) {
            tmp = new Parameters();
            tmp.setAll(stores[i].parms, defaultParms);
            if (tmp.size() > 0) {
                dest = new Element(type.types[i].name);
                tmp.addXml(dest);
                result.addContent(dest);
            }
        }
        return result;
    }
    
    public String toString() {
        StringBuffer b = new StringBuffer();
        b.append("\nStore Group " + name + ":\n");
        for (int i = 0; i < stores.length; i++) {
            b.append("  [");
            b.append(stores[i].getName());
            b.append("]\n");
        }
        return b.toString();
    }
    
    public XStore getMainStore() {
        return stores[0];
    }
    
    public String[] getScopes() {
        int i;
        String[] scopes;
        
        scopes = new String[stores.length];
        for (i = 0; i < stores.length; i++) {
            scopes[i] = stores[i].getScope().toString();
        }
        return scopes;
    }
    
    public void addPrefixes(Collection col) {
        for (int i = 0; i < stores.length; i++) {
            stores[i].getScope().addPrefixes(col);
        }
    }
    
    /**
     * @return first store with the specified type or null
     */
    public XStore getByType(String typeName) {
        int i;
        
        if (type == null) {
            // type not kown
            return null;
        }
        for (i = 0; i < stores.length; i++) {
            if (typeName.equals(type.types[i].name)) {
                return stores[i];
            }
        }
        return null;
    }
    
    public Map getInvolvedCollections() {
        Map collections;
        int i;
        XStore store;
        String url;
        String coll;
        
        collections = new HashMap();
        for (i = 0; i < stores.length; i++) {
            store = stores[i];
            url = store.getTaminoCollectionUrl();
            coll = store.getTaminoCollection();
            if (!collections.containsKey(url)) {
                collections.put( url, coll );
            }
        }

        return collections;
    }
    
    public static abstract class Visitor {
        public abstract void visit(XStoreType type, XStore store);
    }
}
