Skip to content
This repository was archived by the owner on May 26, 2020. It is now read-only.

Commit 5137bd0

Browse files
committed
SANTUARIO-534 - DOMNamespaceContext now implements the NamespaceContext contract correctly + settable context node. Thanks to Peter De Maeyer for the patch. This closes #23.
git-svn-id: https://svn.apache.org/repos/asf/santuario/xml-security-java/trunk@1876653 13f79535-47bb-0310-9956-ffa450edef68
1 parent 6662c17 commit 5137bd0

2 files changed

Lines changed: 235 additions & 31 deletions

File tree

src/main/java/org/apache/xml/security/utils/DOMNamespaceContext.java

Lines changed: 98 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,58 +18,125 @@
1818
*/
1919
package org.apache.xml.security.utils;
2020

21-
import java.util.HashMap;
2221
import java.util.Iterator;
23-
import java.util.Map;
24-
import java.util.Map.Entry;
22+
import java.util.Objects;
2523

2624
import javax.xml.namespace.NamespaceContext;
2725

28-
import org.w3c.dom.Attr;
29-
import org.w3c.dom.Element;
30-
import org.w3c.dom.NamedNodeMap;
3126
import org.w3c.dom.Node;
3227

28+
import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
29+
import static javax.xml.XMLConstants.NULL_NS_URI;
30+
import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE;
31+
import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
32+
import static javax.xml.XMLConstants.XML_NS_PREFIX;
33+
import static javax.xml.XMLConstants.XML_NS_URI;
34+
3335
/**
36+
* This class adapts the {@link Node} namespace/prefix lookup API to that of {@link NamespaceContext}.
37+
* There are some differences:
38+
* <table>
39+
* <tr>
40+
* <th>Function</th>
41+
* <th>{@link NamespaceContext} API</th>
42+
* <th>{@link Node} API</th>
43+
* </tr>
44+
* <tr>
45+
* <td>Look up the prefix for a given namespace URI.</td>
46+
* <td>{@link NamespaceContext#getPrefix(String)}</td>
47+
* <td>{@link Node#lookupPrefix(String)}</td>
48+
* </tr>
49+
* <tr>
50+
* <td>Look up all the prefixes for a given namespace URI.</td>
51+
* <td>{@link NamespaceContext#getPrefixes(String)}</td>
52+
* <td>/</td>
53+
* </tr>
54+
* <tr>
55+
* <td>Look up the namespace URI for a given prefix.</td>
56+
* <td>{@link NamespaceContext#getNamespaceURI(String)}</td>
57+
* <td>{@link Node#lookupNamespaceURI(String)}</td>
58+
* </tr>
59+
* <tr>
60+
* <td>The default prefix.</td>
61+
* <td>{@link javax.xml.XMLConstants#DEFAULT_NS_PREFIX}</td>
62+
* <td>{@code null}</td>
63+
* </tr>
64+
* <tr>
65+
* <td>The default namespace URI.</td>
66+
* <td>{@link javax.xml.XMLConstants#NULL_NS_URI}</td>
67+
* <td>{@code null}</td>
68+
* </tr>
69+
* </table>
3470
*/
3571
public class DOMNamespaceContext implements NamespaceContext {
3672

37-
private Map<String, String> namespaceMap = new HashMap<>();
73+
private Node context;
3874

39-
public DOMNamespaceContext(Node contextNode) {
40-
addNamespaces(contextNode);
75+
public DOMNamespaceContext(Node context) {
76+
setContext(context);
4177
}
4278

43-
public String getNamespaceURI(String arg0) {
44-
return namespaceMap.get(arg0);
79+
public void setContext(Node context) {
80+
this.context = context;
4581
}
4682

47-
public String getPrefix(String arg0) {
48-
for (Entry<String, String> entry : namespaceMap.entrySet()) {
49-
if (entry.getValue().equals(arg0)) {
50-
return entry.getKey();
83+
public String getNamespaceURI(String prefix) {
84+
if (prefix == null) {
85+
throw new IllegalArgumentException("prefix is null");
86+
}
87+
if (prefix.equals(DEFAULT_NS_PREFIX)) {
88+
prefix = null;
89+
}
90+
if (context != null) {
91+
String namespaceURI = context.lookupNamespaceURI(prefix);
92+
if (namespaceURI != null) {
93+
return namespaceURI;
5194
}
5295
}
53-
return null;
54-
}
55-
56-
public Iterator<String> getPrefixes(String arg0) {
57-
return namespaceMap.keySet().iterator();
96+
if (prefix == null) {
97+
return NULL_NS_URI;
98+
} else if (prefix.equals(XML_NS_PREFIX)) {
99+
return XML_NS_URI;
100+
} else if (prefix.equals(XMLNS_ATTRIBUTE)) {
101+
return XMLNS_ATTRIBUTE_NS_URI;
102+
}
103+
return NULL_NS_URI;
58104
}
59105

60-
private void addNamespaces(Node element) {
61-
if (element.getParentNode() != null) {
62-
addNamespaces(element.getParentNode());
106+
public String getPrefix(String namespaceURI) {
107+
if (namespaceURI == null) {
108+
throw new IllegalArgumentException("namespace URI is null");
109+
}
110+
if (namespaceURI.equals(NULL_NS_URI)) {
111+
namespaceURI = null;
63112
}
64-
if (element instanceof Element) {
65-
Element el = (Element)element;
66-
NamedNodeMap map = el.getAttributes();
67-
for (int x = 0; x < map.getLength(); x++) {
68-
Attr attr = (Attr)map.item(x);
69-
if ("xmlns".equals(attr.getPrefix())) {
70-
namespaceMap.put(attr.getLocalName(), attr.getValue());
71-
}
113+
if (context != null) {
114+
String prefix = context.lookupPrefix(namespaceURI);
115+
if (prefix != null) {
116+
return prefix;
117+
} else if (Objects.equals(context.lookupNamespaceURI(null), namespaceURI)) {
118+
// context.lookupPrefix(namespaceURI) returns null when a namespace URI is unbound but also when it is
119+
// bound to the default prefix.
120+
// To distinguish the case of an unbound namespace URI from a bound one to the default prefix,
121+
// we look up the namespace URI for the default prefix (null) and if it matches, we return the default
122+
// prefix.
123+
return DEFAULT_NS_PREFIX;
72124
}
73125
}
126+
if (namespaceURI == null) {
127+
return context.lookupNamespaceURI(null) != null ? null : DEFAULT_NS_PREFIX;
128+
} else if (namespaceURI.equals(XML_NS_URI)) {
129+
return XML_NS_PREFIX;
130+
} else if (namespaceURI.equals(XMLNS_ATTRIBUTE_NS_URI)) {
131+
return XMLNS_ATTRIBUTE;
132+
}
133+
return null;
134+
}
135+
136+
/**
137+
* Throws {@link UnsupportedOperationException}.
138+
*/
139+
public Iterator<String> getPrefixes(String namespaceURI) {
140+
throw new UnsupportedOperationException();
74141
}
75142
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.xml.security.utils;
20+
21+
import org.junit.jupiter.api.Test;
22+
import org.w3c.dom.Document;
23+
import org.w3c.dom.Node;
24+
import org.w3c.dom.traversal.DocumentTraversal;
25+
import org.w3c.dom.traversal.TreeWalker;
26+
import org.xml.sax.InputSource;
27+
import org.xml.sax.SAXException;
28+
29+
import javax.xml.parsers.DocumentBuilderFactory;
30+
import javax.xml.parsers.ParserConfigurationException;
31+
import java.io.IOException;
32+
import java.io.StringReader;
33+
34+
import static javax.xml.XMLConstants.DEFAULT_NS_PREFIX;
35+
import static javax.xml.XMLConstants.NULL_NS_URI;
36+
import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE;
37+
import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
38+
import static javax.xml.XMLConstants.XML_NS_PREFIX;
39+
import static javax.xml.XMLConstants.XML_NS_URI;
40+
import static org.hamcrest.MatcherAssert.assertThat;
41+
import static org.hamcrest.Matchers.equalTo;
42+
import static org.hamcrest.Matchers.is;
43+
import static org.hamcrest.Matchers.nullValue;
44+
import static org.junit.jupiter.api.Assertions.assertThrows;
45+
import static org.w3c.dom.traversal.NodeFilter.SHOW_ELEMENT;
46+
47+
public class DOMNamespaceContextTest {
48+
49+
private static final DocumentBuilderFactory DEFAULT_DOCUMENT_BUILDER_FACTORY;
50+
51+
static {
52+
DEFAULT_DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
53+
DEFAULT_DOCUMENT_BUILDER_FACTORY.setNamespaceAware(true);
54+
}
55+
56+
private static Document createDocument(String xml) throws IOException, SAXException, ParserConfigurationException {
57+
return DEFAULT_DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
58+
}
59+
60+
@Test
61+
public void testUnboundDefaultNamespace() throws Exception {
62+
Document document = createDocument("<root/>");
63+
DOMNamespaceContext namespaceContext = new DOMNamespaceContext(document);
64+
assertThrows(IllegalArgumentException.class, () -> namespaceContext.getNamespaceURI(null));
65+
assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo(NULL_NS_URI)));
66+
assertThat(namespaceContext.getNamespaceURI(XML_NS_PREFIX), is(equalTo(XML_NS_URI)));
67+
assertThat(namespaceContext.getNamespaceURI(XMLNS_ATTRIBUTE), is(equalTo(XMLNS_ATTRIBUTE_NS_URI)));
68+
assertThat(namespaceContext.getNamespaceURI("unbound-ns"), is(equalTo(NULL_NS_URI)));
69+
assertThrows(IllegalArgumentException.class, () -> namespaceContext.getPrefix(null));
70+
assertThat(namespaceContext.getPrefix(NULL_NS_URI), is(equalTo(DEFAULT_NS_PREFIX)));
71+
assertThat(namespaceContext.getPrefix(XML_NS_URI), is(equalTo(XML_NS_PREFIX)));
72+
assertThat(namespaceContext.getPrefix(XMLNS_ATTRIBUTE_NS_URI), is(equalTo(XMLNS_ATTRIBUTE)));
73+
assertThat(namespaceContext.getPrefix("urn:unbound-ns"), is(nullValue()));
74+
}
75+
76+
@Test
77+
public void testBoundDefaultNamespace() throws Exception {
78+
Document document = createDocument("<root xmlns='urn:ns'/>");
79+
TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
80+
Node root = walker.nextNode();
81+
DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
82+
assertThrows(IllegalArgumentException.class, () -> namespaceContext.getNamespaceURI(null));
83+
assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns")));
84+
assertThat(namespaceContext.getNamespaceURI(XML_NS_PREFIX), is(equalTo(XML_NS_URI)));
85+
assertThat(namespaceContext.getNamespaceURI(XMLNS_ATTRIBUTE), is(equalTo(XMLNS_ATTRIBUTE_NS_URI)));
86+
assertThat(namespaceContext.getNamespaceURI("unbound-ns"), is(equalTo(NULL_NS_URI)));
87+
assertThrows(IllegalArgumentException.class, () -> namespaceContext.getPrefix(null));
88+
assertThat(namespaceContext.getPrefix(NULL_NS_URI), is(nullValue()));
89+
assertThat(namespaceContext.getPrefix("urn:ns"), is(equalTo(DEFAULT_NS_PREFIX)));
90+
assertThat(namespaceContext.getPrefix(XML_NS_URI), is(equalTo(XML_NS_PREFIX)));
91+
assertThat(namespaceContext.getPrefix(XMLNS_ATTRIBUTE_NS_URI), is(equalTo(XMLNS_ATTRIBUTE)));
92+
assertThat(namespaceContext.getPrefix("urn:unbound-ns"), is(nullValue()));
93+
}
94+
95+
@Test
96+
public void testNamespaceInheritance() throws Exception {
97+
Document document = createDocument("<root xmlns='urn:ns'><branch xmlns:ns1='urn:ns1'/></root>");
98+
TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
99+
Node root = walker.nextNode();
100+
DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
101+
assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns")));
102+
assertThat(namespaceContext.getPrefix("urn:ns"), is(equalTo(DEFAULT_NS_PREFIX)));
103+
assertThat(namespaceContext.getNamespaceURI("urn:ns1"), is(equalTo(DEFAULT_NS_PREFIX)));
104+
assertThat(namespaceContext.getPrefix("ns1"), is(nullValue()));
105+
Node branch = walker.nextNode();
106+
namespaceContext.setContext(branch);
107+
assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns")));
108+
assertThat(namespaceContext.getPrefix("urn:ns"), is(equalTo(DEFAULT_NS_PREFIX)));
109+
assertThat(namespaceContext.getNamespaceURI("ns1"), is(equalTo("urn:ns1")));
110+
assertThat(namespaceContext.getPrefix("urn:ns1"), is(equalTo("ns1")));
111+
}
112+
113+
@Test
114+
public void testOverriddenDefaultNamespace() throws Exception {
115+
Document document = createDocument("<root xmlns='urn:ns1'><branch xmlns='urn:ns2'/></root>");
116+
TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
117+
Node root = walker.nextNode();
118+
DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
119+
assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns1")));
120+
assertThat(namespaceContext.getPrefix("urn:ns1"), is(equalTo(DEFAULT_NS_PREFIX)));
121+
assertThat(namespaceContext.getPrefix("urn:ns2"), is(nullValue()));
122+
Node branch = walker.nextNode();
123+
namespaceContext.setContext(branch);
124+
assertThat(namespaceContext.getNamespaceURI(DEFAULT_NS_PREFIX), is(equalTo("urn:ns2")));
125+
assertThat(namespaceContext.getPrefix("urn:ns2"), is(equalTo(DEFAULT_NS_PREFIX)));
126+
assertThat(namespaceContext.getPrefix("urn:ns1"), is(nullValue()));
127+
}
128+
129+
@Test
130+
public void testGetPrefixesIsUnsupported() throws Exception {
131+
Document document = createDocument("<root/>");
132+
TreeWalker walker = ((DocumentTraversal) document).createTreeWalker(document, SHOW_ELEMENT, null, true);
133+
Node root = walker.nextNode();
134+
DOMNamespaceContext namespaceContext = new DOMNamespaceContext(root);
135+
assertThrows(UnsupportedOperationException.class, () -> namespaceContext.getPrefixes(NULL_NS_URI));
136+
}
137+
}

0 commit comments

Comments
 (0)