blob: f8888b079e41c380cd9c41ad62c3827c2b907eae [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000 The Apache Software Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xerces.util;
import org.apache.xerces.xni.NamespaceContext;
/**
* Namespace support for XML document handlers. This class doesn't
* perform any error checking and assumes that all strings passed
* as arguments to methods are unique symbols. The SymbolTable class
* can be used for this purpose.
*
* @author Andy Clark, IBM
*
* @version $Id$
*/
public class NamespaceSupport
implements NamespaceContext {
//
// Data
//
/**
* Namespace binding information. This array is composed of a
* series of tuples containing the namespace binding information:
* &lt;prefix, uri&gt;. The default size can be set to anything
* as long as it is a power of 2 greater than 1.
*
* @see #fNamespaceSize
* @see #fContext
*/
protected String[] fNamespace = new String[16 * 2];
/** The top of the namespace information array. */
protected int fNamespaceSize;
// NOTE: The constructor depends on the initial context size
// being at least 1. -Ac
/**
* Context indexes. This array contains indexes into the namespace
* information array. The index at the current context is the start
* index of declared namespace bindings and runs to the size of the
* namespace information array.
*
* @see #fNamespaceSize
*/
protected int[] fContext = new int[8];
/** The current context. */
protected int fCurrentContext;
// symbols
/** Symbol: "xml". */
protected String fXmlSymbol;
/** Symbol: "xmlns". */
protected String fXmlnsSymbol;
//
// Constructors
//
/** Default constructor. */
public NamespaceSupport() {
} // <init>()
/**
* Constructs a namespace context object and initializes it with
* the prefixes declared in the specified context.
*/
public NamespaceSupport(NamespaceContext context) {
pushContext();
while (context != null) {
int count = context.getDeclaredPrefixCount();
for (int i = 0; i < count; i++) {
String prefix = context.getDeclaredPrefixAt(i);
String uri = getURI(prefix);
if (uri == null) {
uri = context.getURI(prefix);
declarePrefix(prefix, uri);
}
}
context = context.getParentContext();
}
} // <init>(NamespaceContext)
//
// Public methods
//
// context management
/**
* Reset this Namespace support object for reuse.
*
* <p>It is necessary to invoke this method before reusing the
* Namespace support object for a new session.</p>
*/
public void reset(SymbolTable symbolTable) {
// save symbols
fXmlSymbol = symbolTable.addSymbol("xml");
fXmlnsSymbol = symbolTable.addSymbol("xmlns");
// reset namespace and context info
fNamespaceSize = 0;
fCurrentContext = 0;
fContext[fCurrentContext] = fNamespaceSize;
// bind "xml" prefix to the XMLNS uri
fNamespace[fNamespaceSize++] = fXmlSymbol;
fNamespace[fNamespaceSize++] = symbolTable.addSymbol(XML_URI);
++fCurrentContext;
} // reset(SymbolTable)
/**
* Start a new Namespace context.
* <p>
* Normally, you should push a new context at the beginning
* of each XML element: the new context will automatically inherit
* the declarations of its parent context, but it will also keep
* track of which declarations were made within this context.
* <p>
* The Namespace support object always starts with a base context
* already in force: in this context, only the "xml" prefix is
* declared.
*
* @see #popContext
*/
public void pushContext() {
// extend the array, if necessary
if (fCurrentContext + 1 == fContext.length) {
int[] contextarray = new int[fContext.length * 2];
System.arraycopy(fContext, 0, contextarray, 0, fContext.length);
fContext = contextarray;
}
// push context
fContext[++fCurrentContext] = fNamespaceSize;
} // pushContext()
/**
* Revert to the previous Namespace context.
* <p>
* Normally, you should pop the context at the end of each
* XML element. After popping the context, all Namespace prefix
* mappings that were previously in force are restored.
* <p>
* You must not attempt to declare additional Namespace
* prefixes after popping a context, unless you push another
* context first.
*
* @see #pushContext
*/
public void popContext() {
fNamespaceSize = fContext[fCurrentContext--];
} // popContext()
// operations within a context.
/**
* Declare a Namespace prefix.
* <p>
* This method declares a prefix in the current Namespace
* context; the prefix will remain in force until this context
* is popped, unless it is shadowed in a descendant context.
* <p>
* To declare a default Namespace, use the empty string. The
* prefix must not be "xml" or "xmlns".
* <p>
* Note that you must <em>not</em> declare a prefix after
* you've pushed and popped another Namespace.
*
* @param prefix The prefix to declare, or null for the empty
* string.
* @param uri The Namespace URI to associate with the prefix.
*
* @return true if the prefix was legal, false otherwise
*
* @see #getURI
* @see #getDeclaredPrefixAt
*/
public boolean declarePrefix(String prefix, String uri) {
// ignore "xml" and "xmlns" prefixes
if (prefix == fXmlSymbol || prefix == fXmlnsSymbol) {
return false;
}
// see if prefix already exists in current context
for (int i = fNamespaceSize; i > fContext[fCurrentContext]; i -= 2) {
if (fNamespace[i - 2] == prefix) {
// REVISIT: [Q] Should the new binding override the
// previously declared binding or should it
// it be ignored? -Ac
// NOTE: The SAX2 "NamespaceSupport" helper allows
// re-bindings with the new binding overwriting
// the previous binding. -Ac
fNamespace[i - 1] = uri;
return true;
}
}
// resize array, if needed
if (fNamespaceSize == fNamespace.length) {
String[] namespacearray = new String[fNamespaceSize * 2];
System.arraycopy(fNamespace, 0, namespacearray, 0, fNamespaceSize);
fNamespace = namespacearray;
}
// bind prefix to uri in current context
fNamespace[fNamespaceSize++] = prefix;
fNamespace[fNamespaceSize++] = uri;
return true;
} // declarePrefix(String,String):boolean
/**
* Look up a prefix and get the currently-mapped Namespace URI.
* <p>
* This method looks up the prefix in the current context.
* Use the empty string ("") for the default Namespace.
*
* @param prefix The prefix to look up.
*
* @return The associated Namespace URI, or null if the prefix
* is undeclared in this context.
*
* @see #getDeclaredPrefixAt
*/
public String getURI(String prefix) {
// find prefix in current context
for (int i = fNamespaceSize; i > 0; i -= 2) {
if (fNamespace[i - 2] == prefix) {
return fNamespace[i - 1];
}
}
// prefix not found
return null;
} // getURI(String):String
/**
* Return a count of all prefixes currently declared, including
* the default prefix if bound.
*/
public int getDeclaredPrefixCount() {
return (fNamespaceSize - fContext[fCurrentContext]) / 2;
} // getDeclaredPrefixCount():int
/**
* Returns the prefix at the specified index in the current context.
*/
public String getDeclaredPrefixAt(int index) {
return fNamespace[fContext[fCurrentContext] + index * 2];
} // getDeclaredPrefixAt(int):String
/**
* Returns the parent namespace context or null if there is no
* parent context. The total depth of the namespace contexts
* matches the element depth in the document.
* <p>
* <strong>Note:</strong> This method <em>may</em> return the same
* NamespaceContext object reference. The caller is responsible for
* saving the declared prefix mappings before calling this method.
*/
public NamespaceContext getParentContext() {
if (fCurrentContext == 1) {
return null;
}
return new Context(fCurrentContext - 1);
} // getParentContext():NamespaceContext
//
// Classes
//
/**
* Namespace context information. The current context is always
* handled directly by the NamespaceSupport class. This class is
* used when a user queries the parent context.
*
* @author Andy Clark, IBM
*/
final class Context
implements NamespaceContext {
//
// Data
//
/** The current context. */
private int fCurrentContext;
//
// Constructors
//
/**
* Constructs a new context. Once constructed, this object will
* be re-used when the application calls <code>getParentContext</code.
*/
public Context(int currentContext) {
setCurrentContext(currentContext);
} // <init>(int)
//
// Public methods
//
/** Sets the current context. */
public void setCurrentContext(int currentContext) {
fCurrentContext = currentContext;
} // setCurrentContext(int)
//
// NamespaceContext methods
//
/**
* Look up a prefix and get the currently-mapped Namespace URI.
* <p>
* This method looks up the prefix in the current context.
* Use the empty string ("") for the default Namespace.
*
* @param prefix The prefix to look up.
*
* @return The associated Namespace URI, or null if the prefix
* is undeclared in this context.
*
* @see #getPrefix
* @see #getPrefixes
*/
public String getURI(String prefix) {
// find prefix in current context
for (int i = fNamespaceSize; i > 0; i -= 2) {
if (fNamespace[i - 2] == prefix) {
return fNamespace[i - 1];
}
}
// prefix not found
return null;
} // getURI(String):String
/**
* Return a count of all prefixes currently declared, including
* the default prefix if bound.
*/
public int getDeclaredPrefixCount() {
return (fNamespaceSize - fContext[fCurrentContext]) / 2;
} // getDeclaredPrefixCount():int
/**
* Returns the prefix at the specified index in the current context.
*/
public String getDeclaredPrefixAt(int index) {
return fNamespace[fContext[fCurrentContext] + index * 2];
} // getDeclaredPrefixAt(int):String
/**
* Returns the parent namespace context or null if there is no
* parent context. The total depth of the namespace contexts
* matches the element depth in the document.
* <p>
* <strong>Note:</strong> This method <em>may</em> return the same
* NamespaceContext object reference. The caller is responsible for
* saving the declared prefix mappings before calling this method.
*/
public NamespaceContext getParentContext() {
if (fCurrentContext == 1) {
return null;
}
setCurrentContext(fCurrentContext - 1);
return this;
} // getParentContext():NamespaceContext
} // class Context
} // class NamespaceSupport