001/*
002Copyright (c) 2011+, HL7, Inc
003All rights reserved.
004
005Redistribution and use in source and binary forms, with or without modification, 
006are permitted provided that the following conditions are met:
007
008 * Redistributions of source code must retain the above copyright notice, this 
009   list of conditions and the following disclaimer.
010 * Redistributions in binary form must reproduce the above copyright notice, 
011   this list of conditions and the following disclaimer in the documentation 
012   and/or other materials provided with the distribution.
013 * Neither the name of HL7 nor the names of its contributors may be used to 
014   endorse or promote products derived from this software without specific 
015   prior written permission.
016
017THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
018ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
019WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
020IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
021INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
022NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
023PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
024WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
025ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
026POSSIBILITY OF SUCH DAMAGE.
027
028*/
029package org.hl7.fhir.utilities.xml;
030
031// see http://illegalargumentexception.blogspot.com.au/2009/05/java-using-xpath-with-namespaces-and.html
032
033import java.util.Collections;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037import java.util.Map;
038import java.util.Set;
039
040import javax.xml.XMLConstants;
041import javax.xml.namespace.NamespaceContext;
042
043/**
044* An implementation of <a
045* href="http://java.sun.com/javase/6/docs/api/javax/xml/namespace/NamespaceContext.html">
046* NamespaceContext </a>. Instances are immutable.
047* 
048* @author McDowell
049*/
050public final class NamespaceContextMap implements NamespaceContext {
051
052 private final Map<String, String> prefixMap;
053 private final Map<String, Set<String>> nsMap;
054
055 /**
056  * Constructor that takes a map of XML prefix-namespaceURI values. A defensive
057  * copy is made of the map. An IllegalArgumentException will be thrown if the
058  * map attempts to remap the standard prefixes defined in the NamespaceContext
059  * contract.
060  * 
061  * @param prefixMappings
062  *          a map of prefix:namespaceURI values
063  */
064 public NamespaceContextMap(
065     Map<String, String> prefixMappings) {
066   prefixMap = createPrefixMap(prefixMappings);
067   nsMap = createNamespaceMap(prefixMap);
068 }
069
070 /**
071  * Convenience constructor.
072  * 
073  * @param mappingPairs
074  *          pairs of prefix-namespaceURI values
075  */
076 public NamespaceContextMap(String... mappingPairs) {
077   this(toMap(mappingPairs));
078 }
079
080 private static Map<String, String> toMap(
081     String... mappingPairs) {
082   Map<String, String> prefixMappings = new HashMap<String, String>(
083       mappingPairs.length / 2);
084   for (int i = 0; i < mappingPairs.length; i++) {
085     prefixMappings
086         .put(mappingPairs[i], mappingPairs[++i]);
087   }
088   return prefixMappings;
089 }
090
091 private Map<String, String> createPrefixMap(
092     Map<String, String> prefixMappings) {
093   Map<String, String> prefixMap = new HashMap<String, String>(
094       prefixMappings);
095   addConstant(prefixMap, XMLConstants.XML_NS_PREFIX,
096       XMLConstants.XML_NS_URI);
097   addConstant(prefixMap, XMLConstants.XMLNS_ATTRIBUTE,
098       XMLConstants.XMLNS_ATTRIBUTE_NS_URI);
099   return Collections.unmodifiableMap(prefixMap);
100 }
101
102 private void addConstant(Map<String, String> prefixMap,
103     String prefix, String nsURI) {
104   String previous = prefixMap.put(prefix, nsURI);
105   if (previous != null && !previous.equals(nsURI)) {
106     throw new IllegalArgumentException(prefix + " -> "
107         + previous + "; see NamespaceContext contract");
108   }
109 }
110
111 private Map<String, Set<String>> createNamespaceMap(
112     Map<String, String> prefixMap) {
113   Map<String, Set<String>> nsMap = new HashMap<String, Set<String>>();
114   for (Map.Entry<String, String> entry : prefixMap
115       .entrySet()) {
116     String nsURI = entry.getValue();
117     Set<String> prefixes = nsMap.get(nsURI);
118     if (prefixes == null) {
119       prefixes = new HashSet<String>();
120       nsMap.put(nsURI, prefixes);
121     }
122     prefixes.add(entry.getKey());
123   }
124   for (Map.Entry<String, Set<String>> entry : nsMap
125       .entrySet()) {
126     Set<String> readOnly = Collections
127         .unmodifiableSet(entry.getValue());
128     entry.setValue(readOnly);
129   }
130   return nsMap;
131 }
132
133 @Override
134 public String getNamespaceURI(String prefix) {
135   checkNotNull(prefix);
136   String nsURI = prefixMap.get(prefix);
137   return nsURI == null ? XMLConstants.NULL_NS_URI : nsURI;
138 }
139
140 @Override
141 public String getPrefix(String namespaceURI) {
142   checkNotNull(namespaceURI);
143   Set<String> set = nsMap.get(namespaceURI);
144   return set == null ? null : set.iterator().next();
145 }
146
147 @Override
148 public Iterator<String> getPrefixes(String namespaceURI) {
149   checkNotNull(namespaceURI);
150   Set<String> set = nsMap.get(namespaceURI);
151   return set.iterator();
152 }
153
154 private void checkNotNull(String value) {
155   if (value == null) {
156     throw new IllegalArgumentException("null");
157   }
158 }
159
160 /**
161  * @return an unmodifiable map of the mappings in the form prefix-namespaceURI
162  */
163 public Map<String, String> getMap() {
164   return prefixMap;
165 }
166
167}