/* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the "License"). You may not use this file except * in compliance with the License. * * You can obtain a copy of the license at * https://jaxp.dev.java.net/CDDLv1.0.html. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * HEADER in each file and include the License file at * https://jaxp.dev.java.net/CDDLv1.0.html * If applicable add the following below this CDDL HEADER * with the fields enclosed by brackets "[]" replaced with * your own identifying information: Portions Copyright * [year] [name of copyright owner] */ /* * $Id: NamespaceContextHelper.java,v 1.2 2006-03-28 20:54:02 ndw Exp $ * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. * Portions Copyright 2010 Thomas 'PointedEars' Lahn */ package com.nwalsh.namespace; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import javax.xml.namespace.NamespaceContext; /* * NOTE: Skip XML 1.1 validity tests because we don't want to recompile * org.apache.xerces for Android -- PointedEars */ // import org.apache.xerces.util.XML11Char; /** * Helper implementation of the javax.xml.namespace.NamespaceContext interface. * *

* This class implements the JAXP (1.3+) * {@link javax.xml.namespace.NamespaceContext} interface. This is the interface * used by the JAXP XPath APIs to establish namespace bindings for XPath * expressions. *

* *

* There are two errors (in retrospect) with respect to the namespace context in * the XPath API. First, there's no way to construct a new one. Given an XPath * you can find out what context it is using, but if you want to construct a new * expression, there's no standard class that you can instantiate to build a new * context. Second, the {@link javax.xml.namespace.NamespaceContext} interface * is obviously (again, in retrospect) missing a method that returns an iterator * that will allow you to find all the namespace URIs (and/or * prefixes, which would be equivalent) in the context. *

* *

* This class addresses the first error by providing an object that you can * instantiate that implements the {@link javax.xml.namespace.NamespaceContext} * interface. It's not a standard class, but it at least saves you * the trouble of writing it yourself. (Feel free to move it into your own * package, of course.) *

* *

* There's really no way to address the second error. An interface, like * {@link javax.xml.namespace.NamespaceContext}, is immutable once released into * the wild in the Java platform. (This is a consequence of backwards * compatibility rules.) To really address the problem, we'll have to invent a * new interface or provide an alternative abstract class that implementations * will be required to use, or something. However, as an experiment, this class * implements a couple of extra methods that we might wish had been in the * interface. These methods are carefully identified as non-standard. Having * them here really isn't all that useful because your underlying XPath * implementation isn't likely to return instances of this class. *

* *

* There are three ways to instantiate this class: *

* *
    *
  1. The no-argument constructor produces an initially empty namespace * context.
  2. *
  3. Another constructor takes a prefix and URI and produces a namespace * context with that binding.
  4. *
  5. Finally, there's a constructor that takes a hash of namespace/uri pairs * and produces a namespace context with those initial bindings.
  6. *
  7. The obvious constructor, one that takes an existing * {@link javax.xml.namespace.NamespaceContext} so that you can extend it, isn't * there because you can't get the current bindings from that interface; see the * aforementioned bug.
  8. *
* *

* After the object has been instantiated, you can call the * {@link #add(String,String)} method to add additional bindings to the * namespace context. Because I'm not sure how and where the XPath API * implementations might save pointers to the context object, I've imposed a * number of rules designed to make sure that the context remains coherent: *

* * * *

* Even with these rules, you can't assume that the context is thread safe. * Don't allow it to be changed while someone else is reading it. *

* *

* Other notes: *

* * * * @author Norman Walsh * @version $Revision: 1.2 $, $Date: 2006-03-28 20:54:02 $ * @see Java API for XML Processing * @see Namespaces * in XML * @see Namespaces in * XML Errata */ public class NamespaceContextHelper implements NamespaceContext { private final Hashtable ns = new Hashtable(); /** * Creates a new instance of NamespaceContextHelper. * *

* Creates an empty namespace context. *

*/ public NamespaceContextHelper() { } /** * Creates a new instance of NamespaceContextHelper. * *

* Creates a namespace context with the bindings specified in * initialNamespaces. *

*/ public NamespaceContextHelper(Hashtable initialNamespaces) { Enumeration keys = initialNamespaces.keys(); while (keys.hasMoreElements()) { String prefix = (String) keys.nextElement(); String uri = (String) initialNamespaces.get(prefix); this.add(prefix, uri); } } /** * Creates a new instance of NamespaceContextHelper. * *

* Creates a namespace context with the specified prefix bound to * uri. *

*/ public NamespaceContextHelper(String prefix, String uri) { this.add(prefix, uri); } /** * Adds a new prefix/uri binding to the namespace context. * * @throws NullPointerException * if the prefix or uri is * null. * @throws IllegalArgumentException * if the caller attempts to change the binding of * prefix, if the caller attempts to bind the prefix " * xml" * or the namespace " * http://www.w3.org/XML/1998/namespace" incorrectly, * if the caller attempts to bind the prefix "xmlns" or * the namespace * "http://www.w3.org/2000/xmlns", or if the * prefix is * not a valid NCName. */ public void add(String prefix, String uri) { if (prefix == null || uri == null) { throw new NullPointerException( "Null prefix or uri passed to NamespaceContextHelper"); } if (this.ns.containsKey(prefix)) { String curURI = this.ns.get(prefix); if (uri.equals(curURI)) { return; } throw new IllegalArgumentException( "Attempt to change binding in NamespaceContextHelper"); } if ("xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(uri)) { throw new IllegalArgumentException( "The prefix 'xml' can only be bound to 'http://www.w3.org/XML/1998/namespace' in NamespaceContextHelper"); } if ("http://www.w3.org/XML/1998/namespace".equals(uri) && !"xml".equals(prefix)) { throw new IllegalArgumentException( "The namespace 'http://www.w3.org/XML/1998/namespace' can only have the prefix 'xml' in NamespaceContextHelper"); } if ("xmlns".equals(prefix) || "http://www.w3.org/2000/xmlns".equals(uri)) { throw new IllegalArgumentException( "Neither the prefix 'xmlns' nor the URI 'http://www.w3.org/2000/xmlns' can be bound in NamespaceContextHelper"); } if ("".equals(prefix)) { this.ns.put(prefix, uri); } else { /* * NOTE: Skip XML 1.1 validity tests because we don't want to recompile * org.apache.xerces for Android -- PointedEars */ // if (XML11Char.isXML11ValidNCName (prefix)) { this.ns.put(prefix, uri); // } else { // throw new IllegalArgumentException // ("Prefix is not a valid NCName in NamespaceContextHelper"); // } } } /** Implements the NamespaceContext getNamespaceURI method. */ public String getNamespaceURI(String prefix) { return this.ns.get(prefix); } /** Implements the NamespaceContext getPrefix method. */ public String getPrefix(String namespaceURI) { if (this.ns.containsValue(namespaceURI)) { Enumeration keys = this.ns.keys(); while (keys.hasMoreElements()) { String pfx = keys.nextElement(); String uri = this.ns.get(pfx); if (namespaceURI.equals(uri)) { return pfx; } } } return null; } /** * Implements a NON STANDARD method for finding all of the * prefixes * in the namespace context. * *

* Returns an iterator over all of the prefixes in the namespace context. Note * that multiple prefixes may be bound to the same URI. *

*/ public Iterator getPrefixes() { return this.getPrefixes(null); } /** Implements the NamespaceContext getPrefixes method. */ public Iterator getPrefixes(String namespaceURI) { return new NSIterator(this.ns, namespaceURI); } /** * Implements a NON STANDARD method for finding all of the * namespace URIs * in the namespace context. * *

* Returns an iterator over all of the namespace URIs in the namespace * context. Note that each namespace URI is returned exactly once, even if it * is bound to several different prefixes. *

*/ public Iterator getNamespaceURIs() { // Make sure each URI is returned at most once... Hashtable uriHash = new Hashtable(); Enumeration keys = this.ns.keys(); while (keys.hasMoreElements()) { String pfx = keys.nextElement(); String uri = this.ns.get(pfx); if (!uriHash.containsKey(uri)) { uriHash.put(uri, pfx); } } return new NSIterator(uriHash, null); } /** Implements the Iterator interface over namespace bindings. */ private class NSIterator implements Iterator { private Enumeration keys; public NSIterator(Hashtable hash, String value) { this.keys = hash.keys(); if (value != null) { // We have to copy the hash to get only the keys that have the specified // value Hashtable vHash = new Hashtable(); while (this.keys.hasMoreElements()) { String key = this.keys.nextElement(); String val = hash.get(key); if (val.equals(value)) { vHash.put(key, val); } } this.keys = vHash.keys(); } } public boolean hasNext() { return this.keys.hasMoreElements(); } public String next() { return this.keys.nextElement(); } public void remove() { throw new UnsupportedOperationException( "Cannot remove prefix in NamespaceContextHelper"); } } }