/*
 * Copyright 2001-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.fop.xml;

import org.apache.fop.datatypes.Ints;

import java.util.HashMap;
import java.util.ArrayList;

/**
 * Maintains the namespaces encountered by an invocation of
 * <tt>XMLSerialHandler</tt>.
 * <p>One instance of <i>XMLNamespaces</i> is maintained across all
 * documents that may be processed in a single invocation of
 * <tt>XMLSerialhandler</tt>.  A reference to that instance is kept with
 * every instance of <tt>XMLEvent</tt>.
 * @author <a href="mailto:pbwest@powerup.com.au">Peter B. West</a>
 */

public class XMLNamespaces {

    private static final String tag = "$Name$";
    private static final String revision = "$Revision$";

    public static final String DefAttrNSpace = "";
    public static final String XSLNamespace =
        "http://www.w3.org/1999/XSL/Format";
    public static final String SVGNamespace = "http://www.w3.org/2000/svg";
    public static final String XlinkNamespace =
        "http://www.w3.org/1999/xlink";
    public static final int DefAttrNSIndex = 0;
    public static final int XSLNSpaceIndex = 1;
    public static final int SVGNSpaceIndex = 2;
    public static final int XLinkNSpaceIndex = 3;

    /**
     * A <tt>HashMap</tt> mapping a namespace URI to an <tt>int</tt>
     * index.  The HashMap is initialized with a few well-known URIs.  As
     * URIs are encountered in parsing, they are converted to an integer
     * index by lookup in this HashMap.  If the URI has not been seen, it
     * is added to the <i>uriIndices</i> and <i>uris</i> for future reference.
     * <b>It is vital that no URI, once added, ever be deleted
     * from this <tt>HashMap</tt></b>.
     * <p><tt>HashMap</> is unsynchronized, so accesses and updates must be
     * protected.
     * <p>
     * Updates will be very rare, and accesses are directly related to the
     * number of elements (and attributes) encountered.
     */
    private HashMap uriIndices;
    /**
     * A <tt>ArrayList</tt> of namespace URIs.  Effectively, a mapping of
     * an <tt>int</tt> index onto a URI.
     * ArrayList is initialized with a few well-known URIs.  As
     * URIs are encountered in parsing, they are converted to an integer
     * index by lookup in the <i>uriIndices</i> Hashmap. If the URI has not
     * been seen, it is added to <i>uriIndices</i> and <i>uris</i>
     * for future reference.
     * <p>
     * <tt>ArrayList</> is unsynchronized, so access and updates must be
     * protected.  Both will be more rare than accesses to <i>uriIndices</i>.
     */
    private ArrayList uris;

    /**
     * A sequence object for use by <tt>XMLEvent</tt>s.  Because an
     * <tt>XMLEvent</tt> object must always be associated with an
     * <i>XMLNamespace</i> object, this namespace object will act as a
     * singleton for <tt>XMLEvent</tt>s.  This field provides a
     * counter for those objects.  The range of values which may be
     * assigned to <i>sequence</i> is restricted by <i>seqMask</i>.
     */
    private int sequence = 0;

    /** Mask to restrict the range of values within which <i>sequence</i>
     * may cycle.
     */
    public final int seqMask = (1 << 20) - 1;

    /**
     * The access function for the sequence.
     * @return the next positive sequence number.  This number may wrap
     * but is guaranteed to be within the range seqMask >= sequence >= 0.
     */
    public int getSequence() {
        sequence = ++sequence & seqMask;
        return sequence;
    }

    public XMLNamespaces() {
        uriIndices = new HashMap(4);
        uris = new ArrayList(4);
        uriIndices.put(DefAttrNSpace, Ints.consts.get(DefAttrNSIndex));
        uris.add(DefAttrNSIndex, DefAttrNSpace);
        uriIndices.put(XSLNamespace, Ints.consts.get(XSLNSpaceIndex));
        uris.add(XSLNSpaceIndex, XSLNamespace);
        uriIndices.put(SVGNamespace, Ints.consts.get(SVGNSpaceIndex));
        uris.add(SVGNSpaceIndex, SVGNamespace);
        uriIndices.put(XlinkNamespace, Ints.consts.get(XLinkNSpaceIndex));
        uris.add(XLinkNSpaceIndex, XlinkNamespace);
    }

    /**
     * @return size of the <tt>uris</tt> <ttArrayList</tt>.
     */
    public synchronized int getUrisSize() {
        return uris.size();
    }

    /**
     * If the URI is not pre-defined, and has not been seen before, add
     * it to the stored namespaces, and return the index.
     * @param uri the namespace uri
     * @return integer index of the namespace URI
     */
    public synchronized int getURIIndex(String uri) {
        int i;
        Integer intg = (Integer)uriIndices.get(uri);
        if (intg == null) {
            // update the indices
            i = uris.size();
            //System.out.println("****Adding namespace " + uri + " " + i);
            uriIndices.put(uri, Ints.consts.get(i));
            uris.add(i, uri);
            return i;
        }
        // not null - found the integer
        return intg.intValue();
    }

    /**
     * @param index the integer index of the namespace URI
     * @return the corresponding namespace URI
     */
    public synchronized String getIndexURI(int index) {
        return (String)uris.get(index);
    }

}