View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.util;
23  
24  import static org.opencastproject.util.EqualsUtil.eq;
25  
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Optional;
34  
35  import javax.xml.XMLConstants;
36  import javax.xml.namespace.NamespaceContext;
37  
38  public final class XmlNamespaceContext implements NamespaceContext {
39    // the number of default bindings
40    private static final int DEFAULT_BINDINGS = 2;
41  
42    // prefix -> namespace URI
43    private final Map<String, String> prefixToUri = new HashMap<String, String>();
44  
45    /**
46     * Create a new namespace context with bindings from prefix to URI and bind the
47     * default namespaces as described in the documentation of {@link javax.xml.namespace.NamespaceContext}.
48     */
49    public XmlNamespaceContext(Map<String, String> prefixToUri) {
50      this.prefixToUri.putAll(prefixToUri);
51      this.prefixToUri.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
52      this.prefixToUri.put(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI);
53    }
54  
55    public static XmlNamespaceContext mk(Map<String, String> prefixToUri) {
56      return new XmlNamespaceContext(prefixToUri);
57    }
58  
59    public static XmlNamespaceContext mk(XmlNamespaceBinding... bindings) {
60      return mk(Arrays.asList(bindings));
61    }
62  
63    public static XmlNamespaceContext mk(String prefix, String namespaceUri) {
64      return new XmlNamespaceContext(Collections.singletonMap(prefix, namespaceUri));
65    }
66  
67    public static XmlNamespaceContext mk(List<XmlNamespaceBinding> bindings) {
68      Map<String, String> prefixToUri = new HashMap<>();
69      for (XmlNamespaceBinding binding : bindings) {
70        prefixToUri.put(binding.getPrefix(), binding.getNamespaceURI());
71      }
72      return new XmlNamespaceContext(prefixToUri);
73    }
74  
75  
76    @Override
77    public String getNamespaceURI(String prefix) {
78      return Optional.ofNullable(prefixToUri.get(prefix)).orElse(XMLConstants.NULL_NS_URI);
79    }
80  
81    @Override
82    public String getPrefix(String uri) {
83      RequireUtil.notNull(uri, "uri");
84      for (Map.Entry<String, String> entry : prefixToUri.entrySet()) {
85        if (uri.equals(entry.getValue())) {
86          return entry.getKey();
87        }
88      }
89      return null;
90    }
91  
92    @Override
93    public Iterator<String> getPrefixes(String uri) {
94      RequireUtil.notNull(uri, "uri");
95      List<String> matchingPrefixes = new ArrayList<>();
96      for (Map.Entry<String, String> entry : prefixToUri.entrySet()) {
97        if (uri.equals(entry.getValue())) {
98          matchingPrefixes.add(entry.getKey());
99        }
100     }
101     return matchingPrefixes.iterator();
102   }
103 
104 
105   public List<XmlNamespaceBinding> getBindings() {
106     List<XmlNamespaceBinding> bindings = new ArrayList<>();
107     for (Map.Entry<String, String> entry : prefixToUri.entrySet()) {
108       bindings.add(new XmlNamespaceBinding(entry.getKey(), entry.getValue()));
109     }
110     return bindings;
111   }
112 
113   /** Create a new context with the given bindings added. Existing bindings will not be overwritten. */
114   public XmlNamespaceContext add(XmlNamespaceBinding... bindings) {
115     return add(Arrays.asList(bindings));
116   }
117 
118   /** Create a new context with the given bindings added. Existing bindings will not be overwritten. */
119   public XmlNamespaceContext add(XmlNamespaceContext binding) {
120     if (binding.prefixToUri.size() == DEFAULT_BINDINGS) {
121       return this;
122     } else {
123       return add(binding.getBindings());
124     }
125   }
126 
127   private XmlNamespaceContext add(List<XmlNamespaceBinding> newBindings) {
128     Map<String, String> existingMap = new HashMap<>();
129     for (XmlNamespaceBinding b : getBindings()) {
130       existingMap.put(b.getPrefix(), b.getNamespaceURI());
131     }
132     for (XmlNamespaceBinding b : newBindings) {
133       if (!existingMap.containsKey(b.getPrefix())) {
134         existingMap.put(b.getPrefix(), b.getNamespaceURI());
135       }
136     }
137     return mk(existingMap);
138   }
139 
140   public NamespaceContext merge(final NamespaceContext precedence) {
141     return merge(this, precedence);
142   }
143 
144   /** Merge <code>b</code> into <code>a</code> so that <code>b</code> takes precedence over <code>a</code>. */
145   public static NamespaceContext merge(final NamespaceContext a, final NamespaceContext b) {
146     return new NamespaceContext() {
147       @Override public String getNamespaceURI(String prefix) {
148         final String uri = b.getNamespaceURI(prefix);
149         if (eq(XMLConstants.DEFAULT_NS_PREFIX, prefix) && eq(XMLConstants.NULL_NS_URI, uri)) {
150           return a.getNamespaceURI(prefix);
151         } else {
152           return uri;
153         }
154       }
155 
156       @Override public String getPrefix(final String uri) {
157         return Optional.ofNullable(b.getPrefix(uri))
158             .orElseGet(() -> a.getPrefix(uri));
159       }
160 
161       @Override public Iterator getPrefixes(String uri) {
162         final Iterator prefixes = b.getPrefixes(uri);
163         if (prefixes.hasNext()) {
164           return prefixes;
165         } else {
166           return a.getPrefixes(uri);
167         }
168       }
169     };
170   }
171 }