Author: veithen
Date: Thu Jan 22 13:13:02 2009
New Revision: 736795
URL: http://svn.apache.org/viewvc?rev=736795&view=rev
Log:
Fixed a subtle issue in OMXMLReader related to namespace declarations (see the
comments in the code and unit tests for more details). Should fix SYNAPSE-501
on trunk.
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/OMXMLReader.java
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/java/org/apache/axiom/om/impl/jaxp/TransformerTest.java
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/resources/org/apache/axiom/om/impl/jaxp/test.xml
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/OMXMLReader.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/OMXMLReader.java?rev=736795&r1=736794&r2=736795&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/OMXMLReader.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/serialize/OMXMLReader.java
Thu Jan 22 13:13:02 2009
@@ -21,11 +21,14 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMComment;
+import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.OMNode;
@@ -46,6 +49,18 @@
* SAX {...@link XMLReader} implementation that traverses a given OM tree and
invokes the
* callback methods on the configured {...@link ContentHandler}. This can be
used to
* serialize an Axiom tree to SAX.
+ * <p>
+ * This class can also generate SAX events for a subtree. This is the case if
the
+ * element passed to the constructor is not the root element of the document.
In this
+ * case, care is taken to properly generate <code>startPrefixMapping</code> and
+ * <code>endPrefixMapping</code> events also for namespace mappings declared
on the ancestors
+ * of the element.
+ * <p>
+ * To understand why this is important, consider the following example:
+ * <pre><root xmlns:ns="urn:ns"><element
attr="ns:someThing"/><root></pre>
+ * In that case, to correctly interpret the attribute value, the SAX content
handler must be
+ * aware of the namespace mapping for the <tt>ns</tt> prefix, even if the
serialization starts
+ * only at the child element.
*/
public class OMXMLReader implements XMLReader {
private static final String URI_LEXICAL_HANDLER =
"http://xml.org/sax/properties/lexical-handler";
@@ -148,21 +163,55 @@
private void parse() throws SAXException {
contentHandler.startDocument();
+ generateParentPrefixMappingEvents(element, true);
generateEvents(element);
+ generateParentPrefixMappingEvents(element, false);
contentHandler.endDocument();
}
+ private void generatePrefixMappingEvents(OMNamespace ns, boolean start)
throws SAXException {
+ String prefix = ns.getPrefix();
+ if (prefix != null) {
+ if (start) {
+ contentHandler.startPrefixMapping(prefix,
ns.getNamespaceURI());
+ } else {
+ contentHandler.endPrefixMapping(prefix);
+ }
+ }
+ }
+
private void generatePrefixMappingEvents(OMElement omElement, boolean
start)
throws SAXException {
for (Iterator it = omElement.getAllDeclaredNamespaces(); it.hasNext();
) {
- OMNamespace ns = (OMNamespace)it.next();
- String prefix = ns.getPrefix();
- if (prefix != null) {
- if (start) {
- contentHandler.startPrefixMapping(prefix,
ns.getNamespaceURI());
- } else {
- contentHandler.endPrefixMapping(prefix);
+ generatePrefixMappingEvents((OMNamespace)it.next(), start);
+ }
+ }
+
+ private void generateParentPrefixMappingEvents(OMElement omElement,
boolean start)
+ throws SAXException {
+
+ if (!(omElement.getParent() instanceof OMElement)) {
+ return;
+ }
+ // Maintain a set of the prefixes we have already seen. This is
required to take into
+ // account that a namespace mapping declared on an element can hide
another one declared
+ // for the same prefix on an ancestor of the element.
+ Set/*<String>*/ seenPrefixes = new HashSet();
+ for (Iterator it = omElement.getAllDeclaredNamespaces(); it.hasNext();
) {
+ seenPrefixes.add(((OMNamespace)it.next()).getPrefix());
+ }
+ OMElement current = omElement;
+ while (true) {
+ OMContainer parent = current.getParent();
+ if (!(parent instanceof OMElement)) {
+ return;
+ }
+ current = (OMElement)parent;
+ for (Iterator it = current.getAllDeclaredNamespaces();
it.hasNext(); ) {
+ OMNamespace ns = (OMNamespace)it.next();
+ if (seenPrefixes.add(ns.getPrefix())) {
+ generatePrefixMappingEvents(ns, start);
}
}
}
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/java/org/apache/axiom/om/impl/jaxp/TransformerTest.java
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/java/org/apache/axiom/om/impl/jaxp/TransformerTest.java?rev=736795&r1=736794&r2=736795&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/java/org/apache/axiom/om/impl/jaxp/TransformerTest.java
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/java/org/apache/axiom/om/impl/jaxp/TransformerTest.java
Thu Jan 22 13:13:02 2009
@@ -21,6 +21,8 @@
import static org.custommonkey.xmlunit.XMLAssert.assertXMLIdentical;
import static org.custommonkey.xmlunit.XMLUnit.compareXML;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import java.io.InputStream;
import java.io.StringWriter;
@@ -32,6 +34,8 @@
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,7 +63,7 @@
}
@Test
- public void test() throws Exception {
+ public void testIdentity() throws Exception {
Transformer transformer = factory.newTransformer();
OMSource omSource = new OMSource(new
StAXOMBuilder(getInput()).getDocumentElement());
@@ -73,4 +77,37 @@
assertXMLIdentical(compareXML(out.toString(),
omResult.getRootElement().toString()), true);
}
+
+ /**
+ * Test that all namespace mappings in scope of the source element are
available on the result.
+ * This checks for an issue that may arise under the following
circumstances:
+ * <ol>
+ * <li>The source element, i.e. the element passed as argument to
+ * {...@link OMSource#OMSource(OMElement)} is not the root element of
the document.</li>
+ * <li>One of the ancestors declares a namespace mapping.</li>
+ * <li>The namespace mapping is not used in the name of the source
element or any of its
+ * descendant elements or attributes (but may be used in the value of an
attribute).</li>
+ * </ol>
+ * Example:
+ * <pre><root xmlns:ns="urn:ns"><element
attr="ns:someThing"/><root></pre>
+ * In that case, when constructing an {...@link OMSource} from the child
element, the namespace
+ * mapping for the <tt>ns</tt> prefix should be visible to the consumer.
Otherwise it would not
+ * be able to interpret the attribute value correctly. This is relevant
e.g. when validating
+ * a part of a document against an XML schema (see SYNAPSE-501).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testNamespaceMappingsOnFragment() throws Exception {
+ Transformer transformer = factory.newTransformer();
+
+ OMElement element = new
StAXOMBuilder(getInput()).getDocumentElement().getFirstElement();
+ OMSource omSource = new OMSource(element);
+ OMResult omResult = new OMResult();
+ transformer.transform(omSource, omResult);
+
+ OMNamespace ns = omResult.getRootElement().findNamespaceURI("p");
+ assertNotNull(ns);
+ assertEquals("urn:some:namespace", ns.getNamespaceURI());
+ }
}
Modified:
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/resources/org/apache/axiom/om/impl/jaxp/test.xml
URL:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/resources/org/apache/axiom/om/impl/jaxp/test.xml?rev=736795&r1=736794&r2=736795&view=diff
==============================================================================
---
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/resources/org/apache/axiom/om/impl/jaxp/test.xml
(original)
+++
webservices/commons/trunk/modules/axiom/modules/axiom-integration/src/test/resources/org/apache/axiom/om/impl/jaxp/test.xml
Thu Jan 22 13:13:02 2009
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<root>
+<root xmlns:p="urn:some:namespace">
<!-- Some comment -->
<element xmlns="urn:ns1" xmlns:ns2="urn:ns2">
<ns2:subelement attr1="test" attr2="test2">Some text</ns2:subelement>