jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r616041 - in /jackrabbit/trunk/jackrabbit-jcr-commons/src: main/java/org/apache/jackrabbit/commons/SerializingContentHandler.java test/java/org/apache/jackrabbit/commons/SerializingContentHandlerTest.java
Date Mon, 28 Jan 2008 21:02:35 GMT
Author: jukka
Date: Mon Jan 28 13:02:33 2008
New Revision: 616041

URL: http://svn.apache.org/viewvc?rev=616041&view=rev
Log:
JCR-1350: Add a serializing content handler
    - Streamlined the SerializingContentHandler class

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

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/SerializingContentHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/SerializingContentHandler.java?rev=616041&r1=616040&r2=616041&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/SerializingContentHandler.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/SerializingContentHandler.java
Mon Jan 28 13:02:33 2008
@@ -32,258 +32,255 @@
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
 
 /**
  * A {@link ContentHandler} that serializes SAX events to a given
  * {@link Result} instance. The JAXP {@link SAXTransformerFactory}
- * facility is used for the serialization. This class handles also cases
- * problems like the Xalan serializer in JDK 1.4 not outputting correct
- * xmlns attributes.
+ * facility is used for the serialization.
+ * <p>
+ * This class explicitly ensures that all namespace prefixes are also
+ * present as xmlns attributes in the serialized XML document. This avoids
+ * problems with Xalan's serialization behaviour which was (at least during
+ * JDK 1.4) to ignore namespaces if they were not present as xmlns attributes.
+ * <p>
+ * NOTE: The code in this class was originally written for Apache Cocoon and
+ * is included with some modifications here in Apache Jackrabbit. See the
+ * org.apache.cocoon.serialization.AbstractTextSerializer class in the
+ * cocoon-pipeline-impl component for the original code.
  */
 public class SerializingContentHandler extends DefaultContentHandler {
 
-    public static ContentHandler getSerializer(Result result)
-            throws SAXException, TransformerConfigurationException {
-        SAXTransformerFactory factory = (SAXTransformerFactory)
-            SAXTransformerFactory.newInstance();
+    /** The URI for xml namespaces */
+    private static final String XML = "http://www.w3.org/XML/1998/namespace";
 
-        TransformerHandler handler = factory.newTransformerHandler();
-        handler.setResult(result);
+    /**
+     * Creates a serializing content handler that writes to the given result.
+     *
+     * @param result serialization target
+     * @return serializing content handler
+     * @throws SAXException if the content handler could not be initialized
+     */
+    public static DefaultHandler getSerializer(Result result)
+            throws SAXException {
+        try {
+            SAXTransformerFactory factory = (SAXTransformerFactory)
+            SAXTransformerFactory.newInstance();
 
-        TransformerHandler probe = factory.newTransformerHandler();
-        StringWriter writer = new StringWriter();
-        probe.setResult(new StreamResult(writer));
-        probe.startDocument();
-        probe.startPrefixMapping("p", "uri");
-        probe.startElement("uri", "e", "p:e", new AttributesImpl());
-        probe.endElement("uri", "e", "p:e");
-        probe.endPrefixMapping("p");
-        probe.endDocument();
+            TransformerHandler handler = factory.newTransformerHandler();
+            handler.setResult(result);
 
-        if (writer.toString().indexOf("xmlns") == -1) {
-            return new NamespaceAsAttributes(handler);
-        } else {
-            return handler;
+            // Test whether the NamespaceAsAttributes wrapper is needed
+            StringWriter writer = new StringWriter();
+            TransformerHandler probe = factory.newTransformerHandler();
+            probe.setResult(new StreamResult(writer));
+            probe.startDocument();
+            probe.startPrefixMapping("p", "uri");
+            probe.startElement("uri", "e", "p:e", new AttributesImpl());
+            probe.endElement("uri", "e", "p:e");
+            probe.endPrefixMapping("p");
+            probe.endDocument();
+            if (writer.toString().indexOf("xmlns") == -1) {
+                // The serializer does not output xmlns declarations,
+                // so we need to do it explicitly with this wrapper
+                return new SerializingContentHandler(handler);
+            } else {
+                return new DefaultContentHandler(handler);
+            }
+        } catch (TransformerConfigurationException e) {
+            throw new SAXException("Failed to initialize XML serializer", e);
         }
     }
 
-    public SerializingContentHandler(Result result)
-            throws TransformerConfigurationException, SAXException {
-        super(getSerializer(result));
-    }
-
-
     /**
-     * A pipe that ensures that all namespace prefixes are also present as
-     * 'xmlns:' attributes. This used to circumvent Xalan's serialization behaviour
-     * which is to ignore namespaces if they're not present as 'xmlns:xxx' attributes.
-     * <p>
-     * NOTE: This class was originally written for Apache Cocoon and is
-     * included with minor modifications here in Apache Jackrabbit. See the
-     * org.apache.cocoon.serialization.AbstractTextSerializer class in the
-     * cocoon-pipeline-impl component for the original code.
+     * The prefixes of startPrefixMapping() declarations for the coming element.
      */
-    private static class NamespaceAsAttributes extends DefaultContentHandler {
-
-        /** The URI for xml namespaces */
-        private static final String XML_NAMESPACE_URI =
-            "http://www.w3.org/XML/1998/namespace";
-
-        /**
-         * The prefixes of startPrefixMapping() declarations for the coming element.
-         */
-        private List prefixList = new ArrayList();
+    private List prefixList = new ArrayList();
 
-        /**
-         * The URIs of startPrefixMapping() declarations for the coming element.
-         */
-        private List uriList = new ArrayList();
-
-        /**
-         * Maps of URI<->prefix mappings. Used to work around a bug in the Xalan
-         * serializer.
-         */
-        private Map uriToPrefixMap = new HashMap();
-        private Map prefixToUriMap = new HashMap();
-
-        /**
-         * True if there has been some startPrefixMapping() for the coming element.
-         */
-        private boolean hasMappings = false;
+    /**
+     * The URIs of startPrefixMapping() declarations for the coming element.
+     */
+    private List uriList = new ArrayList();
 
-        public NamespaceAsAttributes(ContentHandler handler) {
-            super(handler);
-        }
+    /**
+     * Maps of URI<->prefix mappings. Used to work around a bug in the Xalan
+     * serializer.
+     */
+    private Map uriToPrefixMap = new HashMap();
+    private Map prefixToUriMap = new HashMap();
 
-        public void startDocument() throws SAXException {
-            // Cleanup
-            this.uriToPrefixMap.clear();
-            this.prefixToUriMap.clear();
-            clearMappings();
-            super.startDocument();
-        }
+    /**
+     * True if there has been some startPrefixMapping() for the coming element.
+     */
+    private boolean hasMappings = false;
 
-        /**
-         * Track mappings to be able to add <code>xmlns:</code> attributes
-         * in <code>startElement()</code>.
-         */
-        public void startPrefixMapping(String prefix, String uri) throws SAXException {
-            // Store the mappings to reconstitute xmlns:attributes
-            // except prefixes starting with "xml": these are reserved
-            // VG: (uri != null) fixes NPE in startElement
-            if (uri != null && !prefix.startsWith("xml")) {
-                this.hasMappings = true;
-                this.prefixList.add(prefix);
-                this.uriList.add(uri);
-
-                // append the prefix colon now, in order to save concatenations later, but
-                // only for non-empty prefixes.
-                if (prefix.length() > 0) {
-                    this.uriToPrefixMap.put(uri, prefix + ":");
-                } else {
-                    this.uriToPrefixMap.put(uri, prefix);
-                }
+    private SerializingContentHandler(ContentHandler handler) {
+        super(handler);
+    }
 
-                this.prefixToUriMap.put(prefix, uri);
-            }
-            super.startPrefixMapping(prefix, uri);
-        }
+    public void startDocument() throws SAXException {
+        // Cleanup
+        this.uriToPrefixMap.clear();
+        this.prefixToUriMap.clear();
+        clearMappings();
+        super.startDocument();
+    }
 
-        /**
-         * 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>
-         * ignores <code>start/endPrefixMapping()</code>.
-         */
-        public void startElement(String eltUri, String eltLocalName, String eltQName, Attributes
attrs)
-                throws SAXException {
-
-            // try to restore the qName. The map already contains the colon
-            if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri))
{
-                eltQName = this.uriToPrefixMap.get(eltUri) + eltLocalName;
+    /**
+     * Track mappings to be able to add <code>xmlns:</code> attributes
+     * in <code>startElement()</code>.
+     */
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        // Store the mappings to reconstitute xmlns:attributes
+        // except prefixes starting with "xml": these are reserved
+        // VG: (uri != null) fixes NPE in startElement
+        if (uri != null && !prefix.startsWith("xml")) {
+            this.hasMappings = true;
+            this.prefixList.add(prefix);
+            this.uriList.add(uri);
+
+            // append the prefix colon now, in order to save concatenations later, but
+            // only for non-empty prefixes.
+            if (prefix.length() > 0) {
+                this.uriToPrefixMap.put(uri, prefix + ":");
+            } else {
+                this.uriToPrefixMap.put(uri, prefix);
             }
-            if (this.hasMappings) {
-                // Add xmlns* attributes where needed
 
-                // New Attributes if we have to add some.
-                AttributesImpl newAttrs = null;
-
-                int mappingCount = this.prefixList.size();
-                int attrCount = attrs.getLength();
+            this.prefixToUriMap.put(prefix, uri);
+        }
+        super.startPrefixMapping(prefix, uri);
+    }
 
-                for (int mapping = 0; mapping < mappingCount; mapping++) {
-
-                    // Build infos for this namespace
-                    String uri = (String) this.uriList.get(mapping);
-                    String prefix = (String) this.prefixList.get(mapping);
-                    String qName = prefix.equals("") ? "xmlns" : ("xmlns:" + prefix);
-
-                    // Search for the corresponding xmlns* attribute
-                    boolean found = false;
-                    for (int attr = 0; attr < attrCount; attr++) {
-                        if (qName.equals(attrs.getQName(attr))) {
-                            // Check if mapping and attribute URI match
-                            if (!uri.equals(attrs.getValue(attr))) {
-                                throw new SAXException("URI in prefix mapping and attribute
do not match");
-                            }
-                            found = true;
-                            break;
+    /**
+     * 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>
+     * ignores <code>start/endPrefixMapping()</code>.
+     */
+    public void startElement(
+            String eltUri, String eltLocalName, String eltQName, Attributes attrs)
+            throws SAXException {
+
+        // try to restore the qName. The map already contains the colon
+        if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri))
{
+            eltQName = this.uriToPrefixMap.get(eltUri) + eltLocalName;
+        }
+        if (this.hasMappings) {
+            // Add xmlns* attributes where needed
+
+            // New Attributes if we have to add some.
+            AttributesImpl newAttrs = null;
+
+            int mappingCount = this.prefixList.size();
+            int attrCount = attrs.getLength();
+
+            for (int mapping = 0; mapping < mappingCount; mapping++) {
+
+                // Build infos for this namespace
+                String uri = (String) this.uriList.get(mapping);
+                String prefix = (String) this.prefixList.get(mapping);
+                String qName = prefix.equals("") ? "xmlns" : ("xmlns:" + prefix);
+
+                // Search for the corresponding xmlns* attribute
+                boolean found = false;
+                for (int attr = 0; attr < attrCount; attr++) {
+                    if (qName.equals(attrs.getQName(attr))) {
+                        // Check if mapping and attribute URI match
+                        if (!uri.equals(attrs.getValue(attr))) {
+                            throw new SAXException("URI in prefix mapping and attribute do
not match");
                         }
+                        found = true;
+                        break;
                     }
+                }
 
-                    if (!found) {
-                        // Need to add this namespace
-                        if (newAttrs == null) {
-                            // Need to test if attrs is empty or we go into an infinite loop...
-                            // Well know SAX bug which I spent 3 hours to remind of :-(
-                            if (attrCount == 0) {
-                                newAttrs = new AttributesImpl();
-                            } else {
-                                newAttrs = new AttributesImpl(attrs);
-                            }
-                        }
-
-                        if (prefix.equals("")) {
-                            newAttrs.addAttribute(
-                                    XML_NAMESPACE_URI, "xmlns", "xmlns", "CDATA", uri);
+                if (!found) {
+                    // Need to add this namespace
+                    if (newAttrs == null) {
+                        // Need to test if attrs is empty or we go into an infinite loop...
+                        // Well know SAX bug which I spent 3 hours to remind of :-(
+                        if (attrCount == 0) {
+                            newAttrs = new AttributesImpl();
                         } else {
-                            newAttrs.addAttribute(
-                                    XML_NAMESPACE_URI, prefix, qName, "CDATA", uri);
+                            newAttrs = new AttributesImpl(attrs);
                         }
                     }
-                } // end for mapping
 
-                // Cleanup for the next element
-                clearMappings();
+                    if (prefix.equals("")) {
+                        newAttrs.addAttribute(XML, qName, qName, "CDATA", uri);
+                    } else {
+                        newAttrs.addAttribute(XML, prefix, qName, "CDATA", uri);
+                    }
+                }
+            } // end for mapping
 
-                // Start element with new attributes, if any
-                super.startElement(eltUri, eltLocalName, eltQName, newAttrs == null ? attrs
: newAttrs);
-            } else {
-                // Normal job
-                super.startElement(eltUri, eltLocalName, eltQName, attrs);
-            }
+            // Cleanup for the next element
+            clearMappings();
+
+            // Start element with new attributes, if any
+            super.startElement(eltUri, eltLocalName, eltQName, newAttrs == null ? attrs :
newAttrs);
+        } else {
+            // Normal job
+            super.startElement(eltUri, eltLocalName, eltQName, attrs);
         }
+    }
 
 
-        /**
-         * Receive notification of the end of an element.
-         * Try to restore the element qName.
-         */
-        public void endElement(String eltUri, String eltLocalName, String eltQName) throws
SAXException {
-            // try to restore the qName. The map already contains the colon
-            if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri))
{
-                eltQName = this.uriToPrefixMap.get(eltUri) + eltLocalName;
-            }
-            super.endElement(eltUri, eltLocalName, eltQName);
+    /**
+     * Receive notification of the end of an element.
+     * Try to restore the element qName.
+     */
+    public void endElement(String eltUri, String eltLocalName, String eltQName) throws SAXException
{
+        // try to restore the qName. The map already contains the colon
+        if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri))
{
+            eltQName = this.uriToPrefixMap.get(eltUri) + eltLocalName;
         }
+        super.endElement(eltUri, eltLocalName, eltQName);
+    }
 
-        /**
-         * End the scope of a prefix-URI mapping:
-         * remove entry from mapping tables.
-         */
-        public void endPrefixMapping(String prefix) throws SAXException {
-            // remove mappings for xalan-bug-workaround.
-            // Unfortunately, we're not passed the uri, but the prefix here,
-            // so we need to maintain maps in both directions.
-            if (this.prefixToUriMap.containsKey(prefix)) {
-                this.uriToPrefixMap.remove(this.prefixToUriMap.get(prefix));
-                this.prefixToUriMap.remove(prefix);
-            }
-
-            if (hasMappings) {
-                // most of the time, start/endPrefixMapping calls have an element event between
them,
-                // which will clear the hasMapping flag and so this code will only be executed
in the
-                // rather rare occasion when there are start/endPrefixMapping calls with
no element
-                // event in between. If we wouldn't remove the items from the prefixList
and uriList here,
-                // the namespace would be incorrectly declared on the next element following
the
-                // endPrefixMapping call.
-                int pos = prefixList.lastIndexOf(prefix);
-                if (pos != -1) {
-                    prefixList.remove(pos);
-                    uriList.remove(pos);
-                }
+    /**
+     * End the scope of a prefix-URI mapping:
+     * remove entry from mapping tables.
+     */
+    public void endPrefixMapping(String prefix) throws SAXException {
+        // remove mappings for xalan-bug-workaround.
+        // Unfortunately, we're not passed the uri, but the prefix here,
+        // so we need to maintain maps in both directions.
+        if (this.prefixToUriMap.containsKey(prefix)) {
+            this.uriToPrefixMap.remove(this.prefixToUriMap.get(prefix));
+            this.prefixToUriMap.remove(prefix);
+        }
+
+        if (hasMappings) {
+            // most of the time, start/endPrefixMapping calls have an element event between
them,
+            // which will clear the hasMapping flag and so this code will only be executed
in the
+            // rather rare occasion when there are start/endPrefixMapping calls with no element
+            // event in between. If we wouldn't remove the items from the prefixList and
uriList here,
+            // the namespace would be incorrectly declared on the next element following
the
+            // endPrefixMapping call.
+            int pos = prefixList.lastIndexOf(prefix);
+            if (pos != -1) {
+                prefixList.remove(pos);
+                uriList.remove(pos);
             }
-
-            super.endPrefixMapping(prefix);
         }
 
-        /**
-         *
-         */
-        public void endDocument() throws SAXException {
-            // Cleanup
-            this.uriToPrefixMap.clear();
-            this.prefixToUriMap.clear();
-            clearMappings();
-            super.endDocument();
-        }
+        super.endPrefixMapping(prefix);
+    }
 
-        private void clearMappings() {
-            this.hasMappings = false;
-            this.prefixList.clear();
-            this.uriList.clear();
-        }
+    public void endDocument() throws SAXException {
+        // Cleanup
+        this.uriToPrefixMap.clear();
+        this.prefixToUriMap.clear();
+        clearMappings();
+        super.endDocument();
+    }
 
+    private void clearMappings() {
+        this.hasMappings = false;
+        this.prefixList.clear();
+        this.uriList.clear();
     }
 
 }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/SerializingContentHandlerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/SerializingContentHandlerTest.java?rev=616041&r1=616040&r2=616041&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/SerializingContentHandlerTest.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/commons/SerializingContentHandlerTest.java
Mon Jan 28 13:02:33 2008
@@ -31,7 +31,7 @@
         StringWriter writer = new StringWriter();
 
         ContentHandler handler =
-            new SerializingContentHandler(new StreamResult(writer));
+            SerializingContentHandler.getSerializer(new StreamResult(writer));
         handler.startDocument();
         handler.startPrefixMapping("p", "uri");
         handler.startElement("uri", "a", "p:a", new AttributesImpl());



Mime
View raw message