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;
044/**
045* An implementation of <a
046* href="http://java.sun.com/javase/6/docs/api/javax/xml/namespace/NamespaceContext.html">
047* NamespaceContext </a>. Instances are immutable.
048* 
049* @author McDowell
050*/
051public final class NamespaceContextMap  {
052
053 private final Map<String, String> prefixMap;
054 private final Map<String, Set<String>> nsMap;
055
056 /**
057  * Constructor that takes a map of XML prefix-namespaceURI values. A defensive
058  * copy is made of the map. An IllegalArgumentException will be thrown if the
059  * map attempts to remap the standard prefixes defined in the NamespaceContext
060  * contract.
061  * 
062  * @param prefixMappings
063  *          a map of prefix:namespaceURI values
064  */
065 public NamespaceContextMap(
066     Map<String, String> prefixMappings) {
067   prefixMap = createPrefixMap(prefixMappings);
068   nsMap = createNamespaceMap(prefixMap);
069 }
070
071 /**
072  * Convenience constructor.
073  * 
074  * @param mappingPairs
075  *          pairs of prefix-namespaceURI values
076  */
077 public NamespaceContextMap(String... mappingPairs) {
078   this(toMap(mappingPairs));
079 }
080
081 private static Map<String, String> toMap(
082     String... mappingPairs) {
083   Map<String, String> prefixMappings = new HashMap<String, String>(
084       mappingPairs.length / 2);
085   for (int i = 0; i < mappingPairs.length; i++) {
086     prefixMappings
087         .put(mappingPairs[i], mappingPairs[++i]);
088   }
089   return prefixMappings;
090 }
091
092 private Map<String, String> createPrefixMap(
093     Map<String, String> prefixMappings) {
094   Map<String, String> prefixMap = new HashMap<String, String>(
095       prefixMappings);
096   addConstant(prefixMap, XMLConstants.XML_NS_PREFIX,
097       XMLConstants.XML_NS_URI);
098   addConstant(prefixMap, XMLConstants.XMLNS_ATTRIBUTE,
099       XMLConstants.XMLNS_ATTRIBUTE_NS_URI);
100   return Collections.unmodifiableMap(prefixMap);
101 }
102
103 private void addConstant(Map<String, String> prefixMap,
104     String prefix, String nsURI) {
105   String previous = prefixMap.put(prefix, nsURI);
106   if (previous != null && !previous.equals(nsURI)) {
107     throw new IllegalArgumentException(prefix + " -> "
108         + previous + "; see NamespaceContext contract");
109   }
110 }
111
112 private Map<String, Set<String>> createNamespaceMap(
113     Map<String, String> prefixMap) {
114   Map<String, Set<String>> nsMap = new HashMap<String, Set<String>>();
115   for (Map.Entry<String, String> entry : prefixMap
116       .entrySet()) {
117     String nsURI = entry.getValue();
118     Set<String> prefixes = nsMap.get(nsURI);
119     if (prefixes == null) {
120       prefixes = new HashSet<String>();
121       nsMap.put(nsURI, prefixes);
122     }
123     prefixes.add(entry.getKey());
124   }
125   for (Map.Entry<String, Set<String>> entry : nsMap
126       .entrySet()) {
127     Set<String> readOnly = Collections
128         .unmodifiableSet(entry.getValue());
129     entry.setValue(readOnly);
130   }
131   return nsMap;
132 }
133
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 public String getPrefix(String namespaceURI) {
141   checkNotNull(namespaceURI);
142   Set<String> set = nsMap.get(namespaceURI);
143   return set == null ? null : set.iterator().next();
144 }
145
146 public Iterator<String> getPrefixes(String namespaceURI) {
147   checkNotNull(namespaceURI);
148   Set<String> set = nsMap.get(namespaceURI);
149   return set.iterator();
150 }
151
152 private void checkNotNull(String value) {
153   if (value == null) {
154     throw new IllegalArgumentException("null");
155   }
156 }
157
158 /**
159  * @return an unmodifiable map of the mappings in the form prefix-namespaceURI
160  */
161 public Map<String, String> getMap() {
162   return prefixMap;
163 }
164
165}