package org.fcrepo.utilities.xml; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; /** * Default implementation of a {@link NamespaceContext} */ public class DefaultNamespaceContext implements NamespaceContext { /* URI -> prefixes map */ private Map<String, Collection<String>> namespace; /* prefix -> URI map */ private Map<String, String> prefixes; private String defaultNamespaceURI; /** * <p>Constructs a NamespaceContext with no default namespace. * </p> * Beware that the default namespace can only be set during * construction. */ public DefaultNamespaceContext() { this(null); } /** * Constructs a NamespaceContext with a default namespace. * * @param defaultNamespaceURI , the default namespace for this context. */ public DefaultNamespaceContext(String defaultNamespaceURI) { namespace = new HashMap<String, Collection<String>>(); prefixes = new HashMap<String, String>(); this.defaultNamespaceURI = defaultNamespaceURI; namespace.put(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, Arrays.asList(XMLConstants.XMLNS_ATTRIBUTE)); prefixes.put(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI); namespace.put(XMLConstants.XML_NS_URI, Arrays.asList(XMLConstants.XML_NS_PREFIX)); prefixes.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI); } public DefaultNamespaceContext(String defaultNamespaceURI, String... nsContext) { this(defaultNamespaceURI); if (nsContext.length % 2 != 0) { throw new IllegalArgumentException("Odd number of arguments. " + "Prefix/URI pairs must match up"); } for (int i = 0; i < nsContext.length; i += 2) { setNameSpace(nsContext[i + 1], nsContext[i]); } } /** * <p>Set or add a namespace to the context and associated it with a prefix. * </p><p> * A given prefix can only be associated with one namespace in the context. * A namespace can have multiple prefixes. * </p> * The prefixes: {@code xml}, and {@code xmlns} are reserved and * predefined in any context. * * @param namespaceURL the namespace uri * @param prefix the prefix to register with the uri * @throws IllegalArgumentException thrown when trying to assign a * namespace to a reserved prefix */ public void setNameSpace(String namespaceURL, String prefix) throws IllegalArgumentException { Collection<String> s = namespace.get(namespaceURL); if (s == null) { s = new HashSet<String>(); } s.add(prefix); namespace.put(namespaceURL, s); this.prefixes.put(prefix, namespaceURL); } @Override public String getNamespaceURI(String prefix) { if (prefix == null) { throw new IllegalArgumentException(); } if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX) && this.defaultNamespaceURI != null) { return this.defaultNamespaceURI; } if (!this.prefixes.containsKey(prefix)) { return ""; } return this.prefixes.get(prefix); } @Override public String getPrefix(String namespaceURI) { if (namespaceURI == null) { throw new IllegalArgumentException(); } if (namespaceURI.equals(defaultNamespaceURI)) { return XMLConstants.DEFAULT_NS_PREFIX; } Collection<String> s = namespace.get(namespaceURI); if (s != null && !s.isEmpty()) { return s.iterator().next(); } else { return null; } } @Override public Iterator<String> getPrefixes(String namespaceURI) { if (namespaceURI == null) { throw new IllegalArgumentException(); } if (namespaceURI.equals(XMLConstants.XML_NS_URI)) { return new OneResultIterator<String>(XMLConstants.XML_NS_PREFIX); } if (namespaceURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) { return new OneResultIterator<String>(XMLConstants.XMLNS_ATTRIBUTE); } if (namespaceURI.equals(defaultNamespaceURI)) { return new OneResultIterator<String>(XMLConstants.DEFAULT_NS_PREFIX); } Collection<String> s = namespace.get(namespaceURI); if (s != null && !namespace.isEmpty()) { return new NonModifiableIterator<String>(s.iterator()); } else { return NO_STRINGS; } } private static final EmptyIterator<String> NO_STRINGS = new EmptyIterator<String>(); static class EmptyIterator<T> implements Iterator<T> { @Override public boolean hasNext() { return false; } @Override public T next() { return null; } @Override public void remove() { } } static class OneResultIterator<T> implements Iterator<T> { private T result; public OneResultIterator(T oneResult) { result = oneResult; } @Override public boolean hasNext() { return result != null; } @Override public T next() { T next = result; result = null; return next; } @Override public void remove() { } } /** * This Iterator wraps any Iterator and makes the remove() method * unsupported.<br></br> * * @author Hans Lund, State and University Library, Aarhus Denamrk. * @version $Id: DefaultNamespaceContext.java,v 1.5 2007/10/04 13:28:21 te Exp $ * @param <T> * @see javax.xml.namespace.NamespaceContext#getPrefixes(String) */ static class NonModifiableIterator<T> implements Iterator<T> { Iterator<T> wrapped; NonModifiableIterator(Iterator<T> iter) { wrapped = iter; } /** * Returns <tt>true</tt> if the iteration has more elements. (In other * words, returns <tt>true</tt> if <tt>next</tt> would return an element * rather than throwing an exception.) * * @return <tt>true</tt> if the iterator has more elements. */ public boolean hasNext() { return wrapped.hasNext(); } /** * Returns the next element in the iteration. Calling this method * repeatedly until the {@link #hasNext()} method returns false will * return each element in the underlying collection exactly once. * * @return the next element in the iteration. * @throws java.util.NoSuchElementException * iteration has no more elements. */ public T next() { return wrapped.next(); } /** * This method is not supported on this Iterator. * <p/> * Allways throws UnsupportedOperationException {@link javax.xml.namespace.NamespaceContext#getPrefixes(String)} * * @throws UnsupportedOperationException if the <tt>remove</tt> * operation is not supported by this Iterator. */ public void remove() { throw new UnsupportedOperationException("Conform to XML API please"); } } }