+++ /dev/null
-// NamespaceSupport.java - generic Namespace support for SAX.\r
-// Written by David Megginson, sax@megginson.com\r
-// This class is in the Public Domain. NO WARRANTY!\r
-\r
-// $Id: NamespaceSupport.java,v 1.1 2000/10/02 02:43:20 sboag Exp $\r
-\r
-package org.xml.sax.helpers;\r
-\r
-import java.util.EmptyStackException;\r
-import java.util.Enumeration;\r
-import java.util.Hashtable;\r
-import java.util.Vector;\r
-\r
-\r
-/**\r
- * Encapsulate Namespace logic for use by SAX drivers.\r
- *\r
- * <blockquote>\r
- * <em>This module, both source code and documentation, is in the\r
- * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>\r
- * </blockquote>\r
- *\r
- * <p>This class encapsulates the logic of Namespace processing:\r
- * it tracks the declarations currently in force for each context\r
- * and automatically processes qualified XML 1.0 names into their\r
- * Namespace parts; it can also be used in reverse for generating\r
- * XML 1.0 from Namespaces.</p>\r
- *\r
- * <p>Namespace support objects are reusable, but the reset method\r
- * must be invoked between each session.</p>\r
- *\r
- * <p>Here is a simple session:</p>\r
- *\r
- * <pre>\r
- * String parts[] = new String[3];\r
- * NamespaceSupport support = new NamespaceSupport();\r
- *\r
- * support.pushContext();\r
- * support.declarePrefix("", "http://www.w3.org/1999/xhtml");\r
- * support.declarePrefix("dc", "http://www.purl.org/dc#");\r
- *\r
- * String parts[] = support.processName("p", parts, false);\r
- * System.out.println("Namespace URI: " + parts[0]);\r
- * System.out.println("Local name: " + parts[1]);\r
- * System.out.println("Raw name: " + parts[2]);\r
-\r
- * String parts[] = support.processName("dc:title", parts, false);\r
- * System.out.println("Namespace URI: " + parts[0]);\r
- * System.out.println("Local name: " + parts[1]);\r
- * System.out.println("Raw name: " + parts[2]);\r
-\r
- * support.popContext();\r
- * </pre>\r
- *\r
- * <p>Note that this class is optimized for the use case where most\r
- * elements do not contain Namespace declarations: if the same\r
- * prefix/URI mapping is repeated for each context (for example), this\r
- * class will be somewhat less efficient.</p>\r
- *\r
- * @since SAX 2.0\r
- * @author David Megginson, \r
- * <a href="mailto:sax@megginson.com">sax@megginson.com</a>\r
- * @version 2.0\r
- */\r
-public class NamespaceSupport\r
-{\r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////////\r
- // Constants.\r
- ////////////////////////////////////////////////////////////////////\r
-\r
-\r
- /**\r
- * The XML Namespace as a constant.\r
- *\r
- * <p>This is the Namespace URI that is automatically mapped\r
- * to the "xml" prefix.</p>\r
- */\r
- public final static String XMLNS =\r
- "http://www.w3.org/XML/1998/namespace";\r
-\r
-\r
- /**\r
- * An empty enumeration.\r
- */\r
- private final static Enumeration EMPTY_ENUMERATION =\r
- new Vector().elements();\r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////////\r
- // Constructor.\r
- ////////////////////////////////////////////////////////////////////\r
-\r
-\r
- /**\r
- * Create a new Namespace support object.\r
- */\r
- public NamespaceSupport ()\r
- {\r
- reset();\r
- }\r
-\r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////////\r
- // Context management.\r
- ////////////////////////////////////////////////////////////////////\r
-\r
-\r
- /**\r
- * Reset this Namespace support object for reuse.\r
- *\r
- * <p>It is necessary to invoke this method before reusing the\r
- * Namespace support object for a new session.</p>\r
- */\r
- public void reset ()\r
- {\r
- contexts = new Context[32];\r
- contextPos = 0;\r
- contexts[contextPos] = currentContext = new Context();\r
- currentContext.declarePrefix("xml", XMLNS);\r
- }\r
-\r
-\r
- /**\r
- * Start a new Namespace context.\r
- *\r
- * <p>Normally, you should push a new context at the beginning\r
- * of each XML element: the new context will automatically inherit\r
- * the declarations of its parent context, but it will also keep\r
- * track of which declarations were made within this context.</p>\r
- *\r
- * <p>The Namespace support object always starts with a base context\r
- * already in force: in this context, only the "xml" prefix is\r
- * declared.</p>\r
- *\r
- * @see #popContext\r
- */\r
- public void pushContext ()\r
- {\r
- int max = contexts.length;\r
- contextPos++;\r
-\r
- // Extend the array if necessary\r
- if (contextPos >= max) {\r
- Context newContexts[] = new Context[max*2];\r
- System.arraycopy(contexts, 0, newContexts, 0, max);\r
- max *= 2;\r
- contexts = newContexts;\r
- }\r
-\r
- // Allocate the context if necessary.\r
- currentContext = contexts[contextPos];\r
- if (currentContext == null) {\r
- contexts[contextPos] = currentContext = new Context();\r
- }\r
-\r
- // Set the parent, if any.\r
- if (contextPos > 0) {\r
- currentContext.setParent(contexts[contextPos - 1]);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Revert to the previous Namespace context.\r
- *\r
- * <p>Normally, you should pop the context at the end of each\r
- * XML element. After popping the context, all Namespace prefix\r
- * mappings that were previously in force are restored.</p>\r
- *\r
- * <p>You must not attempt to declare additional Namespace\r
- * prefixes after popping a context, unless you push another\r
- * context first.</p>\r
- *\r
- * @see #pushContext\r
- */\r
- public void popContext ()\r
- {\r
- contextPos--;\r
- if (contextPos < 0) {\r
- throw new EmptyStackException();\r
- }\r
- currentContext = contexts[contextPos];\r
- }\r
-\r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////////\r
- // Operations within a context.\r
- ////////////////////////////////////////////////////////////////////\r
-\r
-\r
- /**\r
- * Declare a Namespace prefix.\r
- *\r
- * <p>This method declares a prefix in the current Namespace\r
- * context; the prefix will remain in force until this context\r
- * is popped, unless it is shadowed in a descendant context.</p>\r
- *\r
- * <p>To declare a default Namespace, use the empty string. The\r
- * prefix must not be "xml" or "xmlns".</p>\r
- *\r
- * <p>Note that you must <em>not</em> declare a prefix after\r
- * you've pushed and popped another Namespace.</p>\r
- *\r
- * <p>Note that there is an asymmetry in this library: while {@link\r
- * #getPrefix getPrefix} will not return the default "" prefix,\r
- * even if you have declared one; to check for a default prefix,\r
- * you have to look it up explicitly using {@link #getURI getURI}.\r
- * This asymmetry exists to make it easier to look up prefixes\r
- * for attribute names, where the default prefix is not allowed.</p>\r
- *\r
- * @param prefix The prefix to declare, or null for the empty\r
- * string.\r
- * @param uri The Namespace URI to associate with the prefix.\r
- * @return true if the prefix was legal, false otherwise\r
- * @see #processName\r
- * @see #getURI\r
- * @see #getPrefix\r
- */\r
- public boolean declarePrefix (String prefix, String uri)\r
- {\r
- if (prefix.equals("xml") || prefix.equals("xmlns")) {\r
- return false;\r
- } else {\r
- currentContext.declarePrefix(prefix, uri);\r
- return true;\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Process a raw XML 1.0 name.\r
- *\r
- * <p>This method processes a raw XML 1.0 name in the current\r
- * context by removing the prefix and looking it up among the\r
- * prefixes currently declared. The return value will be the\r
- * array supplied by the caller, filled in as follows:</p>\r
- *\r
- * <dl>\r
- * <dt>parts[0]</dt>\r
- * <dd>The Namespace URI, or an empty string if none is\r
- * in use.</dd>\r
- * <dt>parts[1]</dt>\r
- * <dd>The local name (without prefix).</dd>\r
- * <dt>parts[2]</dt>\r
- * <dd>The original raw name.</dd>\r
- * </dl>\r
- *\r
- * <p>All of the strings in the array will be internalized. If\r
- * the raw name has a prefix that has not been declared, then\r
- * the return value will be null.</p>\r
- *\r
- * <p>Note that attribute names are processed differently than\r
- * element names: an unprefixed element name will received the\r
- * default Namespace (if any), while an unprefixed element name\r
- * will not.</p>\r
- *\r
- * @param qName The raw XML 1.0 name to be processed.\r
- * @param parts An array supplied by the caller, capable of\r
- * holding at least three members.\r
- * @param isAttribute A flag indicating whether this is an\r
- * attribute name (true) or an element name (false).\r
- * @return The supplied array holding three internalized strings \r
- * representing the Namespace URI (or empty string), the\r
- * local name, and the raw XML 1.0 name; or null if there\r
- * is an undeclared prefix.\r
- * @see #declarePrefix\r
- * @see java.lang.String#intern */\r
- public String [] processName (String qName, String parts[],\r
- boolean isAttribute)\r
- {\r
- String myParts[] = currentContext.processName(qName, isAttribute);\r
- if (myParts == null) {\r
- return null;\r
- } else {\r
- parts[0] = myParts[0];\r
- parts[1] = myParts[1];\r
- parts[2] = myParts[2];\r
- return parts;\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Look up a prefix and get the currently-mapped Namespace URI.\r
- *\r
- * <p>This method looks up the prefix in the current context.\r
- * Use the empty string ("") for the default Namespace.</p>\r
- *\r
- * @param prefix The prefix to look up.\r
- * @return The associated Namespace URI, or null if the prefix\r
- * is undeclared in this context.\r
- * @see #getPrefix\r
- * @see #getPrefixes\r
- */\r
- public String getURI (String prefix)\r
- {\r
- return currentContext.getURI(prefix);\r
- }\r
-\r
-\r
- /**\r
- * Return an enumeration of all prefixes currently declared.\r
- *\r
- * <p><strong>Note:</strong> if there is a default prefix, it will not be\r
- * returned in this enumeration; check for the default prefix\r
- * using the {@link #getURI getURI} with an argument of "".</p>\r
- *\r
- * @return An enumeration of all prefixes declared in the\r
- * current context except for the empty (default)\r
- * prefix.\r
- * @see #getDeclaredPrefixes\r
- * @see #getURI\r
- */\r
- public Enumeration getPrefixes ()\r
- {\r
- return currentContext.getPrefixes();\r
- }\r
-\r
-\r
- /**\r
- * Return one of the prefixes mapped to a Namespace URI.\r
- *\r
- * <p>If more than one prefix is currently mapped to the same\r
- * URI, this method will make an arbitrary selection; if you\r
- * want all of the prefixes, use the {@link #getPrefixes}\r
- * method instead.</p>\r
- *\r
- * <p><strong>Note:</strong> this will never return the empty (default) prefix;\r
- * to check for a default prefix, use the {@link #getURI getURI}\r
- * method with an argument of "".</p>\r
- *\r
- * @param uri The Namespace URI.\r
- * @param isAttribute true if this prefix is for an attribute\r
- * (and the default Namespace is not allowed).\r
- * @return One of the prefixes currently mapped to the URI supplied,\r
- * or null if none is mapped or if the URI is assigned to\r
- * the default Namespace.\r
- * @see #getPrefixes(java.lang.String)\r
- * @see #getURI\r
- */\r
- public String getPrefix (String uri)\r
- {\r
- return currentContext.getPrefix(uri);\r
- }\r
-\r
-\r
- /**\r
- * Return an enumeration of all prefixes currently declared for a URI.\r
- *\r
- * <p>This method returns prefixes mapped to a specific Namespace\r
- * URI. The xml: prefix will be included. If you want only one\r
- * prefix that's mapped to the Namespace URI, and you don't care \r
- * which one you get, use the {@link #getPrefix getPrefix}\r
- * method instead.</p>\r
- *\r
- * <p><strong>Note:</strong> the empty (default) prefix is <em>never</em> included\r
- * in this enumeration; to check for the presence of a default\r
- * Namespace, use the {@link #getURI getURI} method with an\r
- * argument of "".</p>\r
- *\r
- * @param uri The Namespace URI.\r
- * @return An enumeration of all prefixes declared in the\r
- * current context.\r
- * @see #getPrefix\r
- * @see #getDeclaredPrefixes\r
- * @see #getURI\r
- */\r
- public Enumeration getPrefixes (String uri)\r
- {\r
- Vector prefixes = new Vector();\r
- Enumeration allPrefixes = getPrefixes();\r
- while (allPrefixes.hasMoreElements()) {\r
- String prefix = (String)allPrefixes.nextElement();\r
- if (uri.equals(getURI(prefix))) {\r
- prefixes.addElement(prefix);\r
- }\r
- }\r
- return prefixes.elements();\r
- }\r
-\r
-\r
- /**\r
- * Return an enumeration of all prefixes declared in this context.\r
- *\r
- * <p>The empty (default) prefix will be included in this \r
- * enumeration; note that this behaviour differs from that of\r
- * {@link #getPrefix} and {@link #getPrefixes}.</p>\r
- *\r
- * @return An enumeration of all prefixes declared in this\r
- * context.\r
- * @see #getPrefixes\r
- * @see #getURI\r
- */\r
- public Enumeration getDeclaredPrefixes ()\r
- {\r
- return currentContext.getDeclaredPrefixes();\r
- }\r
-\r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////////\r
- // Internal state.\r
- ////////////////////////////////////////////////////////////////////\r
-\r
- private Context contexts[];\r
- private Context currentContext;\r
- private int contextPos;\r
-\r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////////\r
- // Internal classes.\r
- ////////////////////////////////////////////////////////////////////\r
-\r
- /**\r
- * Internal class for a single Namespace context.\r
- *\r
- * <p>This module caches and reuses Namespace contexts, so the number allocated\r
- * will be equal to the element depth of the document, not to the total\r
- * number of elements (i.e. 5-10 rather than tens of thousands).</p>\r
- */\r
- final class Context {\r
-\r
- /**\r
- * Create the root-level Namespace context.\r
- */\r
- Context ()\r
- {\r
- copyTables();\r
- }\r
- \r
- \r
- /**\r
- * (Re)set the parent of this Namespace context.\r
- *\r
- * @param context The parent Namespace context object.\r
- */\r
- void setParent (Context parent)\r
- {\r
- this.parent = parent;\r
- declarations = null;\r
- prefixTable = parent.prefixTable;\r
- uriTable = parent.uriTable;\r
- elementNameTable = parent.elementNameTable;\r
- attributeNameTable = parent.attributeNameTable;\r
- defaultNS = parent.defaultNS;\r
- tablesDirty = false;\r
- }\r
- \r
- \r
- /**\r
- * Declare a Namespace prefix for this context.\r
- *\r
- * @param prefix The prefix to declare.\r
- * @param uri The associated Namespace URI.\r
- * @see org.xml.sax.helpers.NamespaceSupport#declarePrefix\r
- */\r
- void declarePrefix (String prefix, String uri)\r
- {\r
- // Lazy processing...\r
- if (!tablesDirty) {\r
- copyTables();\r
- }\r
- if (declarations == null) {\r
- declarations = new Vector();\r
- }\r
- \r
- prefix = prefix.intern();\r
- uri = uri.intern();\r
- if ("".equals(prefix)) {\r
- if ("".equals(uri)) {\r
- defaultNS = null;\r
- } else {\r
- defaultNS = uri;\r
- }\r
- } else {\r
- prefixTable.put(prefix, uri);\r
- uriTable.put(uri, prefix); // may wipe out another prefix\r
- }\r
- declarations.addElement(prefix);\r
- }\r
-\r
-\r
- /**\r
- * Process a raw XML 1.0 name in this context.\r
- *\r
- * @param qName The raw XML 1.0 name.\r
- * @param isAttribute true if this is an attribute name.\r
- * @return An array of three strings containing the\r
- * URI part (or empty string), the local part,\r
- * and the raw name, all internalized, or null\r
- * if there is an undeclared prefix.\r
- * @see org.xml.sax.helpers.NamespaceSupport#processName\r
- */\r
- String [] processName (String qName, boolean isAttribute)\r
- {\r
- String name[];\r
- Hashtable table;\r
- \r
- // Select the appropriate table.\r
- if (isAttribute) {\r
- table = elementNameTable;\r
- } else {\r
- table = attributeNameTable;\r
- }\r
- \r
- // Start by looking in the cache, and\r
- // return immediately if the name\r
- // is already known in this content\r
- name = (String[])table.get(qName);\r
- if (name != null) {\r
- return name;\r
- }\r
- \r
- // We haven't seen this name in this\r
- // context before.\r
- name = new String[3];\r
- int index = qName.indexOf(':');\r
- \r
- \r
- // No prefix.\r
- if (index == -1) {\r
- if (isAttribute || defaultNS == null) {\r
- name[0] = "";\r
- } else {\r
- name[0] = defaultNS;\r
- }\r
- name[1] = qName.intern();\r
- name[2] = name[1];\r
- }\r
- \r
- // Prefix\r
- else {\r
- String prefix = qName.substring(0, index);\r
- String local = qName.substring(index+1);\r
- String uri;\r
- if ("".equals(prefix)) {\r
- uri = defaultNS;\r
- } else {\r
- uri = (String)prefixTable.get(prefix);\r
- }\r
- if (uri == null) {\r
- return null;\r
- }\r
- name[0] = uri;\r
- name[1] = local.intern();\r
- name[2] = qName.intern();\r
- }\r
- \r
- // Save in the cache for future use.\r
- table.put(name[2], name);\r
- tablesDirty = true;\r
- return name;\r
- }\r
- \r
-\r
- /**\r
- * Look up the URI associated with a prefix in this context.\r
- *\r
- * @param prefix The prefix to look up.\r
- * @return The associated Namespace URI, or null if none is\r
- * declared. \r
- * @see org.xml.sax.helpers.NamespaceSupport#getURI\r
- */\r
- String getURI (String prefix)\r
- {\r
- if ("".equals(prefix)) {\r
- return defaultNS;\r
- } else if (prefixTable == null) {\r
- return null;\r
- } else {\r
- return (String)prefixTable.get(prefix);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Look up one of the prefixes associated with a URI in this context.\r
- *\r
- * <p>Since many prefixes may be mapped to the same URI,\r
- * the return value may be unreliable.</p>\r
- *\r
- * @param uri The URI to look up.\r
- * @return The associated prefix, or null if none is declared.\r
- * @see org.xml.sax.helpers.NamespaceSupport#getPrefix\r
- */\r
- String getPrefix (String uri)\r
- {\r
- if (uriTable == null) {\r
- return null;\r
- } else {\r
- return (String)uriTable.get(uri);\r
- }\r
- }\r
- \r
- \r
- /**\r
- * Return an enumeration of prefixes declared in this context.\r
- *\r
- * @return An enumeration of prefixes (possibly empty).\r
- * @see org.xml.sax.helpers.NamespaceSupport#getDeclaredPrefixes\r
- */\r
- Enumeration getDeclaredPrefixes ()\r
- {\r
- if (declarations == null) {\r
- return EMPTY_ENUMERATION;\r
- } else {\r
- return declarations.elements();\r
- }\r
- }\r
- \r
- \r
- /**\r
- * Return an enumeration of all prefixes currently in force.\r
- *\r
- * <p>The default prefix, if in force, is <em>not</em>\r
- * returned, and will have to be checked for separately.</p>\r
- *\r
- * @return An enumeration of prefixes (never empty).\r
- * @see org.xml.sax.helpers.NamespaceSupport#getPrefixes\r
- */\r
- Enumeration getPrefixes ()\r
- {\r
- if (prefixTable == null) {\r
- return EMPTY_ENUMERATION;\r
- } else {\r
- return prefixTable.keys();\r
- }\r
- }\r
- \r
- \r
-\f\r
- ////////////////////////////////////////////////////////////////\r
- // Internal methods.\r
- ////////////////////////////////////////////////////////////////\r
-\r
-\r
- /**\r
- * Copy on write for the internal tables in this context.\r
- *\r
- * <p>This class is optimized for the normal case where most\r
- * elements do not contain Namespace declarations.</p>\r
- */ \r
- private void copyTables ()\r
- {\r
- if (prefixTable != null) {\r
- prefixTable = (Hashtable)prefixTable.clone();\r
- } else {\r
- prefixTable = new Hashtable();\r
- }\r
- if (uriTable != null) {\r
- uriTable = (Hashtable)uriTable.clone();\r
- } else {\r
- uriTable = new Hashtable();\r
- }\r
- elementNameTable = new Hashtable();\r
- attributeNameTable = new Hashtable();\r
- tablesDirty = true;\r
- }\r
-\r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////\r
- // Protected state.\r
- ////////////////////////////////////////////////////////////////\r
- \r
- Hashtable prefixTable;\r
- Hashtable uriTable;\r
- Hashtable elementNameTable;\r
- Hashtable attributeNameTable;\r
- String defaultNS = null;\r
- \r
-\r
-\f\r
- ////////////////////////////////////////////////////////////////\r
- // Internal state.\r
- ////////////////////////////////////////////////////////////////\r
- \r
- private Vector declarations = null;\r
- private boolean tablesDirty = false;\r
- private Context parent = null;\r
- }\r
-}\r
-\r
-// end of NamespaceSupport.java\r