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