/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/datastore/schema/XDoctypeCache.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.
 */


package org.apache.slide.store.tamino.datastore.schema;

import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.datastore.IContentCollection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 * Holds the defined doctypes for a collection. Guarantees, that internally defined
 * prefix:doctypename is unique.
 *
 * P233042, P233043: special handling for namespace http://www.w3.org/2001/XMLSchema
 *
 * @author martin.wallmer@softwareag.com
 *
 * @version $Revision: 1.3 $ *
 */
public class XDoctypeCache {
    
    /** holds a Map of schemas for each collection, key is collection */
    private Map doctypeCache = new HashMap ();
    private Map prefixCache = new HashMap ();
    
    private static XDoctypeCache theOneAndOnly;
    
    // P233042
    private static final String SCHEMA_URI = XGlobals.XML_SCHEMA_NAMESPACE_URI;
    private static final String SCHEMA_PREFIX = "xs";
    
    
    /**
     * avoid instantiation
     */
    private XDoctypeCache () {};
    
    /**
     * Method getInstance
     *
     * @return   the one and only instance of XSchemaCache
     *
     */
    public static XDoctypeCache getSingleInstance () {
        if (theOneAndOnly == null) {
            theOneAndOnly = new XDoctypeCache ();
        }
        
        return theOneAndOnly;
    }
    
    
    /**
     * Method getDoctype
     *
     * @param    collection          a  String
     * @param    fullQualifiedName   a  String
     *
     * @return   a XDoctype
     *
     */
    public XDoctype getDoctype (IContentCollection collection, String fullQualifiedName) {
        Map doctypes = (Map)doctypeCache.get (collection.getUri());
        
        if (doctypes != null)
            return (XDoctype)doctypes.get (fullQualifiedName);
            
        else
            return null;
    }
    
    
    /**
     * Method addWeakDoctype. This must be called, when a doctype was generated
     * in the userland.
     *
     * @param    collection          an IContentCollection
     * @param    doctype             a  XDoctype
     *
     */
    public synchronized void addWeakDoctype (IContentCollection collection, XDoctype doctype) {
        addDoctype (collection, createStrongDoctype (collection, doctype));
    }
    
    /**
     * Method addDoctype
     *
     * @param    collection          an IContentCollection
     * @param    doctype             a  XDoctype
     *
     */
    public void addDoctype (IContentCollection collection, XDoctype doctype) {
        if (doctype.isWeak())
            throw new IllegalStateException ();
        
        Map doctypes = (Map)doctypeCache.get (collection.getUri());
        if (doctypes == null) {
            doctypes = new HashMap ();
            doctypeCache.put (collection.getUri(), doctypes);
        }
        
        PrefixMap prefixMap = getPrefixMap (collection);
        doctypes.put (doctype.getFullQualifiedName(), doctype);
        prefixMap.setPrefix (doctype);
    }
    
    /**
     * Method addSchema
     *
     * @param    collection          a  String
     * @param    doctypes            a  Set
     *
     */
    public void addDoctype (IContentCollection collection, Set doctypes) {
        Iterator it = doctypes.iterator();
        
        while (it.hasNext())
            addDoctype (collection, (XDoctype)it.next());
    }
    
    
    /**
     * deletes all doctypes from the cache, that are in the docztypes Set
     *
     * @param    collection          an IContentCollection
     * @param    doctypes            a  Set
     *
     */
    public void deleteDoctype (IContentCollection collection, Set doctypes) {
        Iterator it = doctypes.iterator();
        
        while (it.hasNext())
            deleteDoctype (collection, (XDoctype)it.next());
        
    }
    
    
    /**
     * deletes a single doctype from the cache
     *
     * @param    collection          an IContentCollection
     * @param    doctype             a  XDoctype
     *
     */
    public void deleteDoctype (IContentCollection collection, XDoctype doctype) {
        Map doctypes = (Map)doctypeCache.get (collection.getUri());
        doctypes.remove (doctype.getFullQualifiedName());
    }
    
    
    
    /**
     * After call to this method it is guaranteed, that the tuple
     * prefix:name is unique for that collection.
     *
     * @param    collection          the collectionn
     * @param    weakDoctye          a weak XDoctype
     *
     * @return   the strong doctype, maybe with different prefix
     *
     */
    XDoctype createStrongDoctype (IContentCollection collection,
                                  XDoctype weakDoctye)
    {
        String prefix = null;
        
        PrefixMap pm = getPrefixMap (collection);
        String namespaceUri = weakDoctye.getNamespaceUri();
        
        // P233042
        if (SCHEMA_URI.equals (namespaceUri)) {
            prefix = SCHEMA_PREFIX;
        }
        else {
            
            if (namespaceUri != null && namespaceUri.length() > 0)
                prefix = pm.getPrefix (weakDoctye.getName());
        }
        weakDoctye.makeStrong (prefix);
        return weakDoctye;
    }
    
    
    private PrefixMap getPrefixMap (IContentCollection collection) {
        PrefixMap prefixMap = (PrefixMap)prefixCache.get (collection.getUri());
        if (prefixMap == null) {
            prefixMap = new PrefixMap ();
            prefixCache.put (collection.getUri(), prefixMap);
        }
        return prefixMap;
        
    }
}



/**
 * maps a set of prefixes to a doctype name. This helps guarantee, that no two
 * doctypes with the same name and the same prefix, but different namespaceURIs
 * exist within one Tamino collection.
 */
class PrefixMap {
    
    /** key: the doctype name (without uri and prefix
     *  val: the highest used prefix
     */
    private Map prefixMap = new HashMap ();
    static Random random = new Random();
    
    
    /**
     * returns a unique prefix for that doctypeName and adds it to the
     *
     * @param    doctypeName                a  String
     *
     * @return   a String
     *
     */
    String getPrefix (String doctypeName) {
        
        Set usedPrefixes = getUsedPrefixSet(doctypeName);
        
        String prefix = null;
        do {
            prefix = createRandomPrefix ();
        } while (usedPrefixes.contains (prefix));
        
        usedPrefixes.add (prefix);
        return prefix;
    }
    
    /**
     * Method setPrefix
     *
     * @param    doctype             a  XDoctype
     *
     */
    void setPrefix (XDoctype doctype) {
        
        Set usedPrefixes = getUsedPrefixSet(doctype.getName());
        usedPrefixes.add (doctype.getNamespacePrefix());
    }
    
    private String createRandomPrefix () {
        int range = 'z' - 'a';
        char [] charBuf = new char [2];
        charBuf [0] = (char) (random.nextInt (range) + 'a');
        charBuf [1] = (char) (random.nextInt (range) + 'a');
        return new String (charBuf);
    }
    
    private Set getUsedPrefixSet (String doctypeName) {
        Set usedPrefixes = (Set)prefixMap.get (doctypeName);
        
        if (usedPrefixes == null) {
            // special handling for prefix xs, P233042
            usedPrefixes = new HashSet ();
            usedPrefixes.add ("xs");
        }
        return usedPrefixes;
    }
    
}

