You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2008/11/25 14:30:37 UTC

svn commit: r720492 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src: main/java/org/apache/jackrabbit/commons/xml/SerializingContentHandler.java test/java/org/apache/jackrabbit/commons/xml/SerializingContentHandlerTest.java

Author: jukka
Date: Tue Nov 25 05:30:36 2008
New Revision: 720492

URL: http://svn.apache.org/viewvc?rev=720492&view=rev
Log:
JCR-1767: XML serialization in JDK 1.4 broken (mostly for WebDAV)

Automatically call startPrefixMapping/endPrefixMapping in SerializingContentHandler if the client (most notably the DOM to SAX transformer in Sun Java 1.4) doesn't do that.

Modified:
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/xml/SerializingContentHandler.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/xml/SerializingContentHandlerTest.java

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/xml/SerializingContentHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/xml/SerializingContentHandler.java?rev=720492&r1=720491&r2=720492&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/xml/SerializingContentHandler.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/xml/SerializingContentHandler.java Tue Nov 25 05:30:36 2008
@@ -21,6 +21,7 @@
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -191,6 +192,22 @@
      */
     private boolean hasMappings = false;
 
+    /**
+     * Stack of the prefixes of explicitly generated prefix mapping calls
+     * per each element level. An entry is appended at the beginning of each
+     * {@link #startElement(String, String, String, Attributes)} call and
+     * removed at the end of each {@link #endElement(String, String, String)}
+     * call. By default the entry for each element is <code>null</code> to
+     * avoid losing performance, but whenever the code detects a new prefix
+     * mapping that needs to be registered, the <code>null</code> entry is
+     * replaced with a list of explicitly registered prefixes for that node.
+     * When that element is closed, the listed prefixes get unmapped.
+     *
+     * @see #checkPrefixMapping(String, String)
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-1767">JCR-1767</a>
+     */
+    private final List addedPrefixMappings = new ArrayList();
+
     private SerializingContentHandler(ContentHandler handler) {
         super(handler);
     }
@@ -230,6 +247,50 @@
     }
 
     /**
+     * Checks whether a prefix mapping already exists for the given namespace
+     * and generates the required {@link #startPrefixMapping(String, String)}
+     * call if the mapping is not found. By default the registered prefix
+     * is taken from the given qualified name, but a different prefix is
+     * automatically selected if that prefix is already used.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-1767">JCR-1767</a>
+     * @param uri namespace URI
+     * @param qname element name with the prefix, or <code>null</code>
+     * @throws SAXException if the prefix mapping can not be added
+     */
+    private void checkPrefixMapping(String uri, String qname)
+            throws SAXException {
+        // Only add the prefix mapping if the URI is not already known
+        if (uri != null && uri.length() > 0 && !uri.startsWith("xml")
+                && !uriToPrefixMap.containsKey(uri)) {
+            // Get the prefix
+            String prefix = "ns";
+            if (qname != null && qname.length() > 0) {
+                int colon = qname.indexOf(':');
+                if (colon != -1) {
+                    prefix = qname.substring(0, colon);
+                }
+            }
+
+            // Make sure that the prefix is unique
+            String base = prefix;
+            for (int i = 2; prefixToUriMap.containsKey(prefix); i++) {
+                prefix = base + i;
+            }
+
+            int last = addedPrefixMappings.size() - 1;
+            List prefixes = (List) addedPrefixMappings.get(last);
+            if (prefixes == null) {
+                prefixes = new ArrayList();
+                addedPrefixMappings.set(last, prefixes);
+            }
+            prefixes.add(prefix);
+
+            startPrefixMapping(prefix, uri);
+        }
+    }
+
+    /**
      * Ensure all namespace declarations are present as <code>xmlns:</code> attributes
      * and add those needed before calling superclass. This is a workaround for a Xalan bug
      * (at least in version 2.0.1) : <code>org.apache.xalan.serialize.SerializerToXML</code>
@@ -238,6 +299,12 @@
     public void startElement(
             String eltUri, String eltLocalName, String eltQName, Attributes attrs)
             throws SAXException {
+        // JCR-1767: Generate extra prefix mapping calls where needed
+        addedPrefixMappings.add(null);
+        checkPrefixMapping(eltUri, eltQName);
+        for (int i = 0; i < attrs.getLength(); i++) {
+            checkPrefixMapping(attrs.getURI(i), attrs.getQName(i));
+        }
 
         // try to restore the qName. The map already contains the colon
         if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri)) {
@@ -313,7 +380,18 @@
         if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri)) {
             eltQName = this.uriToPrefixMap.get(eltUri) + eltLocalName;
         }
+
         super.endElement(eltUri, eltLocalName, eltQName);
+
+        // JCR-1767: Generate extra prefix un-mapping calls where needed
+        int last = addedPrefixMappings.size() - 1;
+        List prefixes = (List) addedPrefixMappings.remove(last);
+        if (prefixes != null) {
+            Iterator iterator = prefixes.iterator();
+            while (iterator.hasNext()) {
+                endPrefixMapping((String) iterator.next());
+            }
+        }
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/xml/SerializingContentHandlerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/xml/SerializingContentHandlerTest.java?rev=720492&r1=720491&r2=720492&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/xml/SerializingContentHandlerTest.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/xml/SerializingContentHandlerTest.java Tue Nov 25 05:30:36 2008
@@ -61,6 +61,45 @@
         assertContains(xml, "</p:a>");
     }
 
+    /**
+     * Test case for JCR-1767.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/JCR-1767">JCR-1767</a>
+     */
+    public void testNoPrefixMappingCalls() throws Exception {
+        StringWriter writer = new StringWriter();
+
+        ContentHandler handler =
+            SerializingContentHandler.getSerializer(writer);
+        handler.startDocument();
+        handler.startElement("uri", "a", "p:a", new AttributesImpl());
+        AttributesImpl attributes = new AttributesImpl();
+        attributes.addAttribute("uri", "foo", "p:foo", "CDATA", "bar");
+        handler.startElement(null, "b", "b", attributes);
+        handler.characters("abc".toCharArray(), 0, 3);
+        handler.endElement(null, "b", "b");
+        handler.startElement(null, "c", "c", new AttributesImpl());
+        handler.endElement(null, "c", "c");
+        handler.characters("xyz".toCharArray(), 0, 3);
+        handler.endElement("uri", "a", "p:a");
+        handler.endDocument();
+
+        String xml = writer.toString();
+        assertContains(xml, "<p:a");
+        assertContains(xml, "xmlns:p");
+        assertContains(xml, "=");
+        assertContains(xml, "uri");
+        assertContains(xml, ">");
+        assertContains(xml, "<b");
+        assertContains(xml, "p:foo");
+        assertContains(xml, "bar");
+        assertContains(xml, "abc");
+        assertContains(xml, "</b>");
+        assertContains(xml, "<c/>");
+        assertContains(xml, "xyz");
+        assertContains(xml, "</p:a>");
+    }
+
     private void assertContains(String haystack, String needle) {
         if (haystack.indexOf(needle) == -1) {
             fail("'" + haystack + "' does not contain '" + needle+ "'");