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>&lt;root xmlns:ns="urn:ns">&lt;element 
attr="ns:someThing"/>&lt;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>&lt;root xmlns:ns="urn:ns">&lt;element 
attr="ns:someThing"/>&lt;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>


Reply via email to