001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.util.xml;
018
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.Iterator;
022import java.util.LinkedHashSet;
023import java.util.Map;
024import java.util.Set;
025
026import javax.xml.XMLConstants;
027import javax.xml.namespace.NamespaceContext;
028
029import org.springframework.lang.Nullable;
030import org.springframework.util.Assert;
031
032/**
033 * Simple {@code javax.xml.namespace.NamespaceContext} implementation.
034 * Follows the standard {@code NamespaceContext} contract, and is loadable
035 * via a {@code java.util.Map} or {@code java.util.Properties} object
036 *
037 * @author Arjen Poutsma
038 * @author Juergen Hoeller
039 * @since 3.0
040 */
041public class SimpleNamespaceContext implements NamespaceContext {
042
043        private final Map<String, String> prefixToNamespaceUri = new HashMap<>();
044
045        private final Map<String, Set<String>> namespaceUriToPrefixes = new HashMap<>();
046
047        private String defaultNamespaceUri = "";
048
049
050        @Override
051        public String getNamespaceURI(String prefix) {
052                Assert.notNull(prefix, "No prefix given");
053                if (XMLConstants.XML_NS_PREFIX.equals(prefix)) {
054                        return XMLConstants.XML_NS_URI;
055                }
056                else if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) {
057                        return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
058                }
059                else if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
060                        return this.defaultNamespaceUri;
061                }
062                else if (this.prefixToNamespaceUri.containsKey(prefix)) {
063                        return this.prefixToNamespaceUri.get(prefix);
064                }
065                return "";
066        }
067
068        @Override
069        @Nullable
070        public String getPrefix(String namespaceUri) {
071                Set<String> prefixes = getPrefixesSet(namespaceUri);
072                return (!prefixes.isEmpty() ? prefixes.iterator().next() : null);
073        }
074
075        @Override
076        public Iterator<String> getPrefixes(String namespaceUri) {
077                return getPrefixesSet(namespaceUri).iterator();
078        }
079
080        private Set<String> getPrefixesSet(String namespaceUri) {
081                Assert.notNull(namespaceUri, "No namespaceUri given");
082                if (this.defaultNamespaceUri.equals(namespaceUri)) {
083                        return Collections.singleton(XMLConstants.DEFAULT_NS_PREFIX);
084                }
085                else if (XMLConstants.XML_NS_URI.equals(namespaceUri)) {
086                        return Collections.singleton(XMLConstants.XML_NS_PREFIX);
087                }
088                else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceUri)) {
089                        return Collections.singleton(XMLConstants.XMLNS_ATTRIBUTE);
090                }
091                else {
092                        Set<String> prefixes = this.namespaceUriToPrefixes.get(namespaceUri);
093                        return (prefixes != null ?  Collections.unmodifiableSet(prefixes) : Collections.emptySet());
094                }
095        }
096
097
098        /**
099         * Set the bindings for this namespace context.
100         * The supplied map must consist of string key value pairs.
101         */
102        public void setBindings(Map<String, String> bindings) {
103                bindings.forEach(this::bindNamespaceUri);
104        }
105
106        /**
107         * Bind the given namespace as default namespace.
108         * @param namespaceUri the namespace uri
109         */
110        public void bindDefaultNamespaceUri(String namespaceUri) {
111                bindNamespaceUri(XMLConstants.DEFAULT_NS_PREFIX, namespaceUri);
112        }
113
114        /**
115         * Bind the given prefix to the given namespace.
116         * @param prefix the namespace prefix
117         * @param namespaceUri the namespace uri
118         */
119        public void bindNamespaceUri(String prefix, String namespaceUri) {
120                Assert.notNull(prefix, "No prefix given");
121                Assert.notNull(namespaceUri, "No namespaceUri given");
122                if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
123                        this.defaultNamespaceUri = namespaceUri;
124                }
125                else {
126                        this.prefixToNamespaceUri.put(prefix, namespaceUri);
127                        Set<String> prefixes =
128                                        this.namespaceUriToPrefixes.computeIfAbsent(namespaceUri, k -> new LinkedHashSet<>());
129                        prefixes.add(prefix);
130                }
131        }
132
133        /**
134         * Remove the given prefix from this context.
135         * @param prefix the prefix to be removed
136         */
137        public void removeBinding(@Nullable String prefix) {
138                if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
139                        this.defaultNamespaceUri = "";
140                }
141                else if (prefix != null) {
142                        String namespaceUri = this.prefixToNamespaceUri.remove(prefix);
143                        if (namespaceUri != null) {
144                                Set<String> prefixes = this.namespaceUriToPrefixes.get(namespaceUri);
145                                if (prefixes != null) {
146                                        prefixes.remove(prefix);
147                                        if (prefixes.isEmpty()) {
148                                                this.namespaceUriToPrefixes.remove(namespaceUri);
149                                        }
150                                }
151                        }
152                }
153        }
154
155        /**
156         * Remove all declared prefixes.
157         */
158        public void clear() {
159                this.prefixToNamespaceUri.clear();
160                this.namespaceUriToPrefixes.clear();
161        }
162
163        /**
164         * Return all declared prefixes.
165         */
166        public Iterator<String> getBoundPrefixes() {
167                return this.prefixToNamespaceUri.keySet().iterator();
168        }
169
170}