]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
DOM2SAX code replaced. Copied and adapted from Xalan-J: http://svn.apache.org/repos...
authorJeremias Maerki <jeremias@apache.org>
Fri, 17 Feb 2006 13:20:40 +0000 (13:20 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 17 Feb 2006 13:20:40 +0000 (13:20 +0000)
There were issues with well-formedness and namespace prefixes when an XMP fragment was serialized to XML. Now this works fine.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@378503 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/fo/extensions/xmp/XMPMetadata.java
src/java/org/apache/fop/render/xml/XMLXMLHandler.java
src/java/org/apache/fop/util/DOM2SAX.java

index fd84b241299c2bee095bb1f2c13205c976f6ef06..57a66ec03ffe79af6c176b8d7feba8a8e26d0add 100644 (file)
@@ -86,7 +86,7 @@ public class XMPMetadata implements ExtensionAttachment, Serializable, XMLizable
     
     /** @see org.apache.fop.util.XMLizable#toSAX(org.xml.sax.ContentHandler) */
     public void toSAX(ContentHandler handler) throws SAXException {
-        DOM2SAX.writeDocument(getDocument(), handler);
+        new DOM2SAX(handler).writeDocument(getDocument(), true);
     }
     
 }
index da44bc725e0eac5de3b6682d8459eba87ac16faf..91553bbd8a257c908f21b874eae89b516a1380e2 100644 (file)
@@ -38,7 +38,7 @@ public class XMLXMLHandler implements XMLHandler {
                 org.w3c.dom.Document doc, String ns) throws Exception {
         ContentHandler handler = (ContentHandler) context.getProperty(HANDLER);
 
-        DOM2SAX.writeDocument(doc, handler);
+        new DOM2SAX(handler).writeDocument(doc, true);
     }
 
     /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */
index a26e714295e53465c6ef958f41849a4d6809357f..6c5142157e8f8bdc768ee51ac1b0098526f23f28 100644 (file)
 
 package org.apache.fop.util;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
 
-import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
@@ -33,99 +33,253 @@ import org.xml.sax.helpers.AttributesImpl;
 
 /**
  * Helper class that produces a SAX stream from a DOM Document.
+ * <p>
+ * Part of the code here copied and adapted from Apache Xalan-J, 
+ * src/org/apache/xalan/xsltc/trax/DOM2SAX.java
  */
 public class DOM2SAX {
 
-    /** Logging instance */
-    private static Log log = LogFactory.getLog(DOM2SAX.class);
+    private static final String EMPTYSTRING = "";
+    private static final String XMLNS_PREFIX = "xmlns";
 
+    private ContentHandler contentHandler;
+    private LexicalHandler lexicalHandler;
+    
+    private Map prefixes = new java.util.HashMap();
+    
+    /**
+     * Main constructor
+     * @param handler the ContentHandler to send SAX events to
+     */
+    public DOM2SAX(ContentHandler handler) {
+        this.contentHandler = handler;
+        if (handler instanceof LexicalHandler) {
+            this.lexicalHandler = (LexicalHandler)handler;
+        }
+    }
+    
     /**
-     * Writes the given document using the given TransformerHandler.
+     * Writes the given document using the given ContentHandler.
      * @param doc DOM document
-     * @param handler TransformerHandler to write to
+     * @param fragment if false no startDocument() and endDocument() calls are issued.
      * @throws SAXException In case of a problem while writing XML
      */
-    public static void writeDocument(Document doc,
-                                     ContentHandler handler) throws SAXException {
-        AttributesImpl atts = new AttributesImpl();
+    public void writeDocument(Document doc, boolean fragment) throws SAXException {
+        if (!fragment) {
+            contentHandler.startDocument();
+        }
         for (Node n = doc.getFirstChild(); n != null;
                 n = n.getNextSibling()) {
-            writeNode(n, handler, atts);
+            writeNode(n);
+        }
+        if (!fragment) {
+            contentHandler.endDocument();
+        }
+    }
+
+    /**
+     * Begin the scope of namespace prefix. Forward the event to the SAX handler
+     * only if the prefix is unknown or it is mapped to a different URI.
+     */
+    private boolean startPrefixMapping(String prefix, String uri)
+            throws SAXException {
+        boolean pushed = true;
+        Stack uriStack = (Stack)prefixes.get(prefix);
+
+        if (uriStack != null) {
+            if (uriStack.isEmpty()) {
+                contentHandler.startPrefixMapping(prefix, uri);
+                uriStack.push(uri);
+            } else {
+                final String lastUri = (String) uriStack.peek();
+                if (!lastUri.equals(uri)) {
+                    contentHandler.startPrefixMapping(prefix, uri);
+                    uriStack.push(uri);
+                } else {
+                    pushed = false;
+                }
+            }
+        } else {
+            contentHandler.startPrefixMapping(prefix, uri);
+            uriStack = new Stack();
+            prefixes.put(prefix, uriStack);
+            uriStack.push(uri);
+        }
+        return pushed;
+    }
+
+    /*
+     * End the scope of a name prefix by popping it from the stack and passing
+     * the event to the SAX Handler.
+     */
+    private void endPrefixMapping(String prefix) throws SAXException {
+        final Stack uriStack = (Stack)prefixes.get(prefix);
+
+        if (uriStack != null) {
+            contentHandler.endPrefixMapping(prefix);
+            uriStack.pop();
         }
     }
 
+    /**
+     * If the DOM was created using a DOM 1.0 API, the local name may be null.
+     * If so, get the local name from the qualified name before generating the
+     * SAX event.
+     */
+    private static String getLocalName(Node node) {
+        final String localName = node.getLocalName();
+
+        if (localName == null) {
+            final String qname = node.getNodeName();
+            final int col = qname.lastIndexOf(':');
+            return (col > 0) ? qname.substring(col + 1) : qname;
+        }
+        return localName;
+    }
+
     /**
      * Writes a node using the given writer.
      * @param node node to serialize
-     * @param handler ContentHandler to write to
-     * @param atts AttributesImpl instance that is reused during SAX event generation
      * @throws SAXException In case of a problem while writing XML
      */
-    private static void writeNode(Node node, ContentHandler handler, AttributesImpl atts
+    private void writeNode(Node node
                 throws SAXException {
-        char[] ca;
+        if (node == null) {
+            return;
+        }
+
         switch (node.getNodeType()) {
-            case Node.ELEMENT_NODE:
-                atts.clear();
-
-                if (node.hasAttributes()) {
-                    NamedNodeMap attr = node.getAttributes();
-                    int len = attr.getLength();
-                    for (int i = 0; i < len; i++) {
-                        Attr a = (Attr) attr.item(i);
-                        atts.addAttribute("", a.getNodeName(), a.getNodeName(),
-                                "CDATA", a.getNodeValue());
+        case Node.ATTRIBUTE_NODE: // handled by ELEMENT_NODE
+        case Node.DOCUMENT_FRAGMENT_NODE:
+        case Node.DOCUMENT_TYPE_NODE:
+        case Node.ENTITY_NODE:
+        case Node.ENTITY_REFERENCE_NODE:
+        case Node.NOTATION_NODE:
+            // These node types are ignored!!!
+            break;
+        case Node.CDATA_SECTION_NODE:
+            final String cdata = node.getNodeValue();
+            if (lexicalHandler != null) {
+                lexicalHandler.startCDATA();
+                contentHandler.characters(cdata.toCharArray(), 0, cdata.length());
+                lexicalHandler.endCDATA();
+            } else {
+                // in the case where there is no lex handler, we still
+                // want the text of the cdate to make its way through.
+                contentHandler.characters(cdata.toCharArray(), 0, cdata.length());
+            }
+            break;
+
+        case Node.COMMENT_NODE: // should be handled!!!
+            if (lexicalHandler != null) {
+                final String value = node.getNodeValue();
+                lexicalHandler.comment(value.toCharArray(), 0, value.length());
+            }
+            break;
+        case Node.DOCUMENT_NODE:
+            contentHandler.startDocument();
+            Node next = node.getFirstChild();
+            while (next != null) {
+                writeNode(next);
+                next = next.getNextSibling();
+            }
+            contentHandler.endDocument();
+            break;
+
+        case Node.ELEMENT_NODE:
+            String prefix;
+            List pushedPrefixes = new java.util.ArrayList();
+            final AttributesImpl attrs = new AttributesImpl();
+            final NamedNodeMap map = node.getAttributes();
+            final int length = map.getLength();
+
+            // Process all namespace declarations
+            for (int i = 0; i < length; i++) {
+                final Node attr = map.item(i);
+                final String qnameAttr = attr.getNodeName();
+
+                // Ignore everything but NS declarations here
+                if (qnameAttr.startsWith(XMLNS_PREFIX)) {
+                    final String uriAttr = attr.getNodeValue();
+                    final int colon = qnameAttr.lastIndexOf(':');
+                    prefix = (colon > 0) ? qnameAttr.substring(colon + 1)
+                            : EMPTYSTRING;
+                    if (startPrefixMapping(prefix, uriAttr)) {
+                        pushedPrefixes.add(prefix);
                     }
                 }
-                handler.startElement(node.getNamespaceURI(),
-                        node.getLocalName(), node.getLocalName(), atts);
+            }
+
+            // Process all other attributes
+            for (int i = 0; i < length; i++) {
+                final Node attr = map.item(i);
+                final String qnameAttr = attr.getNodeName();
+
+                // Ignore NS declarations here
+                if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
+                    final String uriAttr = attr.getNamespaceURI();
 
-                Node c = node.getFirstChild();
-                if (c != null) {
-                    for (; c != null; c = c.getNextSibling()) {
-                        writeNode(c, handler, atts);
+                    // Uri may be implicitly declared
+                    if (uriAttr != null) {
+                        final int colon = qnameAttr.lastIndexOf(':');
+                        prefix = (colon > 0) ? qnameAttr.substring(0, colon)
+                                : EMPTYSTRING;
+                        if (startPrefixMapping(prefix, uriAttr)) {
+                            pushedPrefixes.add(prefix);
+                        }
                     }
+
+                    // Add attribute to list
+                    attrs.addAttribute(attr.getNamespaceURI(),
+                            getLocalName(attr), qnameAttr, "CDATA", attr
+                                    .getNodeValue());
                 }
-                handler.endElement(node.getNamespaceURI(), node.getNodeName(), node.getNodeName());
-                break;
-            case Node.TEXT_NODE:
-                ca = node.getNodeValue().toCharArray();
-                handler.characters(ca, 0, ca.length);
-                break;
-            case Node.CDATA_SECTION_NODE:
-                ca = node.getNodeValue().toCharArray();
-                if (handler instanceof LexicalHandler) {
-                    LexicalHandler lh = (LexicalHandler)handler;
-                    lh.startCDATA();
-                    handler.characters(ca, 0, ca.length);
-                    lh.endCDATA();
-                } else {
-                    handler.characters(ca, 0, ca.length);
-                }
-                break;
-            case Node.ENTITY_REFERENCE_NODE:
-                log.warn("Ignoring ENTITY_REFERENCE_NODE. NYI");
-                /*
-                writer.write("&");
-                writer.write();
-                writer.write(";");
-                */
-                break;
-            case Node.PROCESSING_INSTRUCTION_NODE:
-                handler.processingInstruction(node.getNodeName(), node.getNodeValue());
-                break;
-            case Node.COMMENT_NODE:
-                ca = node.getNodeValue().toCharArray();
-                if (handler instanceof LexicalHandler) {
-                    LexicalHandler lh = (LexicalHandler)handler;
-                    lh.comment(ca, 0, ca.length);
+            }
+
+            // Now process the element itself
+            final String qname = node.getNodeName();
+            final String uri = node.getNamespaceURI();
+            final String localName = getLocalName(node);
+
+            // Uri may be implicitly declared
+            if (uri != null) {
+                final int colon = qname.lastIndexOf(':');
+                prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
+                if (startPrefixMapping(prefix, uri)) {
+                    pushedPrefixes.add(prefix);
                 }
-                break;
-            case Node.DOCUMENT_TYPE_NODE:
-                break;
-            default:
-                throw new IllegalArgumentException("Unexpected node type ("
-                        + node.getNodeType() + ")");
+            }
+
+            // Generate SAX event to start element
+            contentHandler.startElement(uri, localName, qname, attrs);
+
+            // Traverse all child nodes of the element (if any)
+            next = node.getFirstChild();
+            while (next != null) {
+                writeNode(next);
+                next = next.getNextSibling();
+            }
+
+            // Generate SAX event to close element
+            contentHandler.endElement(uri, localName, qname);
+
+            // Generate endPrefixMapping() for all pushed prefixes
+            final int nPushedPrefixes = pushedPrefixes.size();
+            for (int i = 0; i < nPushedPrefixes; i++) {
+                endPrefixMapping((String)pushedPrefixes.get(i));
+            }
+            break;
+
+        case Node.PROCESSING_INSTRUCTION_NODE:
+            contentHandler.processingInstruction(node.getNodeName(), node.getNodeValue());
+            break;
+
+        case Node.TEXT_NODE:
+            final String data = node.getNodeValue();
+            contentHandler.characters(data.toCharArray(), 0, data.length());
+            break;
+        default:
+            //nop
         }
     }