/* * Copyright (c) 2013, University of Toronto. * * 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 edu.toronto.cs.xcurator.common; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; /** * * @author ekzhu */ public final class NsContext implements NamespaceContext { private final Map<String, String> prefixMap; private static final Map<String, String> defaultMap = new HashMap<>(); static { defaultMap.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI); defaultMap.put(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI); defaultMap.put(XMLConstants.DEFAULT_NS_PREFIX, XMLConstants.NULL_NS_URI); } ; public NsContext() { prefixMap = new HashMap<>(); } public NsContext(Element element) { this(); discover(element); } public NsContext(NsContext nsContext) { this.prefixMap = new HashMap<>(nsContext.getNamespaces()); } private NsContext(Map<String, String> prefixMap) { this.prefixMap = new HashMap<>(prefixMap); } public void discover(Element element) { discover(element, true); } public void discover(Element element, boolean override) { // Currently we only looking for namespace // definitions in root element's attributes NamedNodeMap attributeMap = element.getAttributes(); for (int i = 0; i < attributeMap.getLength(); i++) { org.w3c.dom.Attr attr = (Attr) attributeMap.item(i); String prefix = attr.getPrefix(); if (attr.getNodeName().equals(XMLConstants.XMLNS_ATTRIBUTE)) { // This is the default namespace addNamespace(XMLConstants.DEFAULT_NS_PREFIX, attr.getValue(), override); } else if (prefix != null && prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) { // This is a regular namespace definition addNamespace(attr.getLocalName(), attr.getValue(), override); } } } /** * Add a prefix to namespace URI mapping to the context; if override is * false, there is no guarantee the given prefix name will be used in the * context. When a prefix with the same name already exist in our context: * if override is true, then it will replace the existing prefix to URI * mapping with the new one; if override is false, then a new prefix name * will be chosen and added to the context. * * @param prefix * @param namespaceURI * @param override */ public void addNamespace(String prefix, String namespaceURI, boolean override) { // If we choose to override the existing prefix definition, or if the prefix // definition does not already exist, we can go ahead and put the new prefix // definition in our map. if (override || !prefixMap.containsKey(prefix)) { prefixMap.put(prefix, namespaceURI); return; } // If the existing prefix definition is the same as the new one, we do nothing. if (prefixMap.get(prefix).equals(namespaceURI)) { return; } // If we choose to not override and a different prefix definition already exist, // we need to find a new prefix for it. int count = 1; String altPrefixName = prefix + "_" + Integer.toString(count); while (prefixMap.containsKey(altPrefixName)) { count++; altPrefixName = prefix + "_" + Integer.toString(count); } prefixMap.put(altPrefixName, namespaceURI); } public void addNamespace(String prefix, String namespaceURI) { addNamespace(prefix, namespaceURI, true); } public NsContext merge(NsContext other) { return merge(other, true); } /** * Merge other namespace with this one, allowing cascading calls. * * @param other * @param override * @return */ public NsContext merge(NsContext other, boolean override) { Map<String, String> otherPrefixMap = other.getNamespaces(); for (Map.Entry<String, String> entry : otherPrefixMap.entrySet()) { addNamespace(entry.getKey(), entry.getValue(), override); } return this; } public boolean hasNamespace(String prefix, String namespaceUri) { return prefixMap.containsKey(prefix) && prefixMap.get(prefix).equals(namespaceUri); } @Override public String getNamespaceURI(String prefix) { if (prefix == null) { throw new IllegalArgumentException(); } if (prefixMap.containsKey(prefix)) { return prefixMap.get(prefix); } return defaultMap.get(prefix); } public Map<String, String> getNamespaces() { return new HashMap<>(prefixMap); } @Override public String getPrefix(String namespaceURI) { for (String key : prefixMap.keySet()) { if (prefixMap.get(key).equals(namespaceURI)) { return key; } } for (String key : defaultMap.keySet()) { if (defaultMap.get(key).equals(namespaceURI)) { return key; } } return null; } @Override public Iterator getPrefixes(final String namespaceURI) { return new Iterator() { private Object[] namespaceMap = prefixMap.entrySet().toArray(); private int i = 0; private int size = prefixMap.size(); @Override public boolean hasNext() { while (i < size) { if (((Map.Entry) namespaceMap[i]).getValue().equals(namespaceURI)) { return true; } i++; } return false; } @Override public Object next() { if (!hasNext()) { throw new NoSuchElementException(); } i++; return ((Map.Entry) namespaceMap[i - 1]).getKey(); } @Override public void remove() { } }; } }