You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-commits@xmlgraphics.apache.org by ca...@apache.org on 2008/06/02 03:37:54 UTC

svn commit: r662304 - in /xmlgraphics/batik/trunk: ./ sources/org/apache/batik/apps/svgbrowser/ sources/org/apache/batik/bridge/ sources/org/apache/batik/dom/util/ sources/org/apache/batik/script/ sources/org/apache/batik/script/rhino/

Author: cam
Date: Sun Jun  1 18:37:54 2008
New Revision: 662304

URL: http://svn.apache.org/viewvc?rev=662304&view=rev
Log:
1. Add support for the printNode() script function.
2. Make prompt() return a proper ECMAScript String value.
3. Fix up the serialization code so that the output will be namespace-
   well-formed, regardless of the prefixes and namespace declarations
   in the document.
4. Create a DocumentType node for documents that have a DTD.

Modified:
    xmlgraphics/batik/trunk/CHANGES
    xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/XMLPreferenceManager.java
    xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/BaseScriptingEnvironment.java
    xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/ScriptingEnvironment.java
    xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/DOMUtilities.java
    xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/SAXDocumentFactory.java
    xmlgraphics/batik/trunk/sources/org/apache/batik/script/Window.java
    xmlgraphics/batik/trunk/sources/org/apache/batik/script/rhino/WindowWrapper.java

Modified: xmlgraphics/batik/trunk/CHANGES
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/CHANGES?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/CHANGES (original)
+++ xmlgraphics/batik/trunk/CHANGES Sun Jun  1 18:37:54 2008
@@ -11,6 +11,22 @@
 
     44590, 44966
 
+2. New features
+
+  * Support for the printNode() script function.
+
+3. Improvements
+
+  * DocumentType nodes are now created for XML documents where one was
+    supplied in the markup (although only the name, publicId and systemId
+    properties of the DocumentType object are filled in).
+
+4. Bug fixes
+
+  * Better DOM serialization code.
+  * The prompt() script function now returns a proper ECMAScript String
+    value instead of an object.
+
 1.7beta1 -> 1.7
 ---------------
 

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/XMLPreferenceManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/XMLPreferenceManager.java?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/XMLPreferenceManager.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/apps/svgbrowser/XMLPreferenceManager.java Sun Jun  1 18:37:54 2008
@@ -169,7 +169,11 @@
                 String v = (String)m.get(n);
                 
                 w.write("<property name=\"" + n + "\">");
-                w.write(DOMUtilities.contentToString(v));
+                try {
+                    w.write(DOMUtilities.contentToString(v, false));
+                } catch (IOException ex) {
+                    // unlikely to happen
+                }
                 w.write("</property>\n");
             }
 

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/BaseScriptingEnvironment.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/BaseScriptingEnvironment.java?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/BaseScriptingEnvironment.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/BaseScriptingEnvironment.java Sun Jun  1 18:37:54 2008
@@ -784,6 +784,13 @@
         }
 
         /**
+         * Serializes the given node.
+         */
+        public String printNode(Node n) {
+            return null;
+        }
+
+        /**
          * Gets data from the given URI.
          * @param uri The URI where the data is located.
          * @param h A handler called when the data is available.

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/ScriptingEnvironment.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/ScriptingEnvironment.java?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/ScriptingEnvironment.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/bridge/ScriptingEnvironment.java Sun Jun  1 18:37:54 2008
@@ -26,6 +26,7 @@
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.StringReader;
+import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
 import java.net.URL;
@@ -979,6 +980,20 @@
         }
 
         /**
+         * Serializes the given node.
+         */
+        public String printNode(Node n) {
+            try {
+                Writer writer = new StringWriter();
+                DOMUtilities.writeNode(n, writer);
+                writer.close();
+                return writer.toString();
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        /**
          * Implements {@link
          * org.apache.batik.script.Window#getURL(String,org.apache.batik.script.Window.URLResponseHandler)}.
          */

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/DOMUtilities.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/DOMUtilities.java?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/DOMUtilities.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/DOMUtilities.java Sun Jun  1 18:37:54 2008
@@ -23,10 +23,12 @@
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.batik.dom.AbstractDocument;
 import org.apache.batik.xml.XMLUtilities;
 
 import org.w3c.dom.Attr;
@@ -46,98 +48,247 @@
  * @version $Id$
  */
 public class DOMUtilities extends XMLUtilities {
+
     /**
-     * Do not need to be instantiated.
+     * Does not need to be instantiated.
      */
     protected DOMUtilities() {
     }
+ 
+    private static class NSMap {
+        private String prefix;
+        private String ns; // "" is used for no namespace
+        private NSMap next;
+        private int nextPrefixNumber;
+
+        public NSMap declare(String prefix, String ns) {
+            NSMap m = new NSMap();
+            m.prefix = prefix;
+            m.ns = ns;
+            m.next = this;
+            m.nextPrefixNumber = this.nextPrefixNumber;
+            return m;
+        }
+
+        public String getNewPrefix() {
+            String prefix;
+            do {
+                prefix = "a" + nextPrefixNumber++;
+            } while (getNamespace(prefix) != null);
+            return prefix;
+        }
+
+        public String getNamespace(String prefix) {
+            for (NSMap m = this; m.next != null; m = m.next) {
+                if (m.prefix.equals(prefix)) {
+                    return m.ns;
+                }
+            }
+            return null;
+        }
+
+        public String getPrefixForElement(String ns) {
+            for (NSMap m = this; m.next != null; m = m.next) {
+                if (ns.equals(m.ns)) {
+                    return m.prefix;
+                }
+            }
+            return null;
+        }
+
+        public String getPrefixForAttr(String ns) {
+            for (NSMap m = this; m.next != null; m = m.next) {
+                if (ns.equals(m.ns) && !m.prefix.equals("")) {
+                    return m.prefix;
+                }
+            }
+            return null;
+        }
+    }
 
     /**
-     * Writes the given document using the given writer.
+     * Serializes the specified <code>Document</code>, writing it to the given
+     * <code>Writer</code>.
      */
     public static void writeDocument(Document doc, Writer w) throws IOException {
+        AbstractDocument d = (AbstractDocument) doc;
+        if (doc.getDocumentElement() == null) {
+            throw new IOException("No document element");
+        }
+        NSMap m = new NSMap();
         for (Node n = doc.getFirstChild();
              n != null;
              n = n.getNextSibling()) {
-            writeNode(n, w);
+            writeNode(n, w, m, "1.1".equals(d.getXmlVersion()));
         }
     }
 
-    /**
-     * Writes a node using the given writer.
-     */
-    public static void writeNode(Node n, Writer w) throws IOException {
+    protected static void writeNode(Node n, Writer w, NSMap m, boolean isXML11)
+            throws IOException {
         switch (n.getNodeType()) {
-        case Node.ELEMENT_NODE:
-            w.write("<");
-            w.write(n.getNodeName());
+        case Node.ELEMENT_NODE: {
+            if (n.hasAttributes()) {
+                NamedNodeMap attr = n.getAttributes();
+                int len = attr.getLength();
+                for (int i = 0; i < len; i++) {
+                    Attr a = (Attr)attr.item(i);
+                    String name = a.getNodeName();
+                    if (name.startsWith("xmlns")) {
+                        if (name.length() == 5) {
+                            m = m.declare("", a.getNodeValue());
+                        } else {
+                            String prefix = name.substring(6);
+                            m = m.declare(prefix, a.getNodeValue());
+                        }
+                    }
+                }
+            }
+
+            w.write('<');
+            String ns = n.getNamespaceURI();
+            String tagName;
+            if (ns == null) {
+                tagName = n.getNodeName();
+                w.write(tagName);
+                if (!"".equals(m.getNamespace(""))) {
+                    w.write(" xmlns=\"\"");
+                    m = m.declare("", "");
+                }
+            } else {
+                String prefix = n.getPrefix();
+                if (prefix == null) {
+                    prefix = "";
+                }
+                if (ns.equals(m.getNamespace(prefix))) {
+                    tagName = n.getNodeName();
+                    w.write(tagName);
+                } else {
+                    prefix = m.getPrefixForElement(ns);
+                    if (prefix == null) {
+                        prefix = m.getNewPrefix();
+                        tagName = prefix + ':' + n.getLocalName();
+                        w.write(tagName + " xmlns:" + prefix + "=\""
+                                 + contentToString(ns, isXML11) + '"');
+                        m = m.declare(prefix, ns);
+                    } else {
+                        if (prefix.equals("")) {
+                            tagName = n.getLocalName();
+                        } else {
+                            tagName = prefix + ':' + n.getLocalName();
+                        }
+                        w.write(tagName);
+                    }
+                }
+            }
 
             if (n.hasAttributes()) {
                 NamedNodeMap attr = n.getAttributes();
                 int len = attr.getLength();
                 for (int i = 0; i < len; i++) {
                     Attr a = (Attr)attr.item(i);
-                    w.write(" ");
-                    w.write(a.getNodeName());
-                    w.write("=\"");
-                    w.write(contentToString(a.getNodeValue()));
-                    w.write("\"");
+                    String name = a.getNodeName();
+                    String prefix = a.getPrefix();
+                    String ans = a.getNamespaceURI();
+                    if (ans != null &&
+                            !("xmlns".equals(prefix) || name.equals("xmlns"))) {
+                        if (prefix != null
+                                && !ans.equals(m.getNamespace(prefix))
+                                || prefix == null) {
+                            prefix = m.getPrefixForAttr(ans);
+                            if (prefix == null) {
+                                prefix = m.getNewPrefix();
+                                m = m.declare(prefix, ans);
+                            }
+                            name = prefix + ':' + a.getLocalName();
+                        }
+                    }
+                    w.write(' ' + name + "=\""
+                             + contentToString(a.getNodeValue(), isXML11)
+                             + '"');
                 }
             }
 
             Node c = n.getFirstChild();
             if (c != null) {
-                w.write(">");
-                for (; c != null;
-                     c = c.getNextSibling()) {
-                    writeNode(c, w);
-                }
-                w.write("</");
-                w.write(n.getNodeName());
-                w.write(">");
+                w.write('>');
+                do {
+                    writeNode(c, w, m, isXML11);
+                    c = c.getNextSibling();
+                } while (c != null);
+                w.write("</" + tagName + '>');
             } else {
                 w.write("/>");
             }
             break;
+        }
         case Node.TEXT_NODE:
-            w.write(contentToString(n.getNodeValue()));
+            w.write(contentToString(n.getNodeValue(), isXML11));
             break;
-        case Node.CDATA_SECTION_NODE:
-            w.write("<![CDATA[");
-            w.write(n.getNodeValue());
-            w.write("]]>");
+        case Node.CDATA_SECTION_NODE: {
+            String data = n.getNodeValue();
+            if (data.indexOf("]]>") != -1) {
+                throw new IOException("Unserializable CDATA section node");
+            }
+            w.write("<![CDATA["
+                     + assertValidCharacters(data, isXML11)
+                     + "]]>");
             break;
+        }
         case Node.ENTITY_REFERENCE_NODE:
-            w.write("&");
-            w.write(n.getNodeName());
-            w.write(";");
+            w.write('&' + n.getNodeName() + ';');
             break;
-        case Node.PROCESSING_INSTRUCTION_NODE:
-            w.write("<?");
-            w.write(n.getNodeName());
-            // TD: Bug #19392
-            w.write(" ");
-            w.write(n.getNodeValue());
-            w.write("?>");
+        case Node.PROCESSING_INSTRUCTION_NODE: {
+            String target = n.getNodeName();
+            String data = n.getNodeValue();
+            if (target.equalsIgnoreCase("xml")
+                    || target.indexOf(':') != -1
+                    || data.indexOf("?>") != -1) {
+                throw new
+                    IOException("Unserializable processing instruction node");
+            }
+            w.write("<?" + target + ' ' + data + "?>");
             break;
-        case Node.COMMENT_NODE:
+        }
+        case Node.COMMENT_NODE: {
             w.write("<!--");
-            w.write(n.getNodeValue());
+            String data = n.getNodeValue();
+            int len = data.length();
+            if (len != 0 && data.charAt(len - 1) == '-'
+                    || data.indexOf("--") != -1) {
+                throw new IOException("Unserializable comment node");
+            }
+            w.write(data);
             w.write("-->");
             break;
+        }
         case Node.DOCUMENT_TYPE_NODE: {
             DocumentType dt = (DocumentType)n;
-            w.write ("<!DOCTYPE ");
-            w.write (n.getOwnerDocument().getDocumentElement().getNodeName());
+            w.write("<!DOCTYPE "
+                     + n.getOwnerDocument().getDocumentElement().getNodeName());
             String pubID = dt.getPublicId();
             if (pubID != null) {
-                w.write (" PUBLIC \"" + dt.getNodeName() + "\" \"" +
-                           pubID + "\">");
-            } else {
-                String sysID = dt.getSystemId();
-                if (sysID != null)
-                    w.write (" SYSTEM \"" + sysID + "\">");
+                char q = getUsableQuote(pubID);
+                if (q == 0) {
+                    throw new IOException("Unserializable DOCTYPE node");
+                }
+                w.write(" PUBLIC " + q + pubID + q);
             }
+            String sysID = dt.getSystemId();
+            if (sysID != null) {
+                char q = getUsableQuote(sysID);
+                if (q == 0) {
+                    throw new IOException("Unserializable DOCTYPE node");
+                }
+                if (pubID == null) {
+                    w.write(" SYSTEM");
+                }
+                w.write(" " + q + sysID + q);
+            }
+            String subset = dt.getInternalSubset();
+            if (subset != null) {
+                w.write('[' + subset + ']');
+            }
+            w.write('>');
             break;
         }
         default:
@@ -146,6 +297,47 @@
     }
 
     /**
+     * Writes a node using the given writer.
+     */
+    public static void writeNode(Node n, Writer w) throws IOException {
+        if (n.getNodeType() == Node.DOCUMENT_NODE) {
+            writeDocument((Document) n, w);
+        } else {
+            AbstractDocument d = (AbstractDocument) n.getOwnerDocument();
+            writeNode(n, w, new NSMap(),
+                      d == null ? false : "1.1".equals(d.getXmlVersion()));
+        }
+    }
+
+    /**
+     * Returns the quote character to use when quoting the specified string.
+     * If the string contains both single and double quotes, then 0 will be
+     * returned.
+     */
+    private static char getUsableQuote(String s) {
+        char ret = 0;
+        int i = s.length() - 1;
+        while (i >= 0) {
+            char c = s.charAt(i);
+            if (c == '"') {
+                if (ret == 0) {
+                    ret = '\'';
+                } else {
+                    return 0;
+                }
+            } else if (c == '\'') {
+                if (ret == 0) {
+                    ret = '"';
+                } else {
+                    return 0;
+                }
+            }
+            i--;
+        }
+        return ret == 0 ? '"' : ret;
+    }
+
+    /**
      * Serializes the given DOM node using {@link #writeNode(Node,Writer)}
      * and returns the XML as a String.
      *
@@ -164,15 +356,36 @@
         return writer.toString();
     }
 
+    protected static String assertValidCharacters(String s, boolean isXML11)
+            throws IOException {
+
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt(i);
+            if (!isXML11 && !isXMLCharacter(c)
+                    || isXML11 && !isXML11Character(c)) {
+                throw new IOException("Invalid character");
+            }
+        }
+        return s;
+    }
+
     /**
      * Returns the given content value transformed to replace invalid
      * characters with entities.
      */
-    public static String contentToString(String s) {
-        StringBuffer result = new StringBuffer( s.length() );
+    public static String contentToString(String s, boolean isXML11)
+            throws IOException {
+
+        StringBuffer result = new StringBuffer(s.length());
 
-        for (int i = 0; i < s.length(); i++) {
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
             char c = s.charAt(i);
+            if (!isXML11 && !isXMLCharacter(c)
+                    || isXML11 && !isXML11Character(c)) {
+                throw new IOException("Invalid character");
+            }
 
             switch (c) {
             case '<':

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/SAXDocumentFactory.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/SAXDocumentFactory.java?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/SAXDocumentFactory.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/dom/util/SAXDocumentFactory.java Sun Jun  1 18:37:54 2008
@@ -47,6 +47,7 @@
 
 import org.w3c.dom.DOMImplementation;
 import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
@@ -106,6 +107,12 @@
      * Contains collected string data.  May be Text, CDATA or Comment.
      */
     protected StringBuffer stringBuffer = new StringBuffer();
+
+    /**
+     * The DTD to use when the document is created.
+     */
+    protected DocumentType doctype;
+
     /**
      * Indicates if stringBuffer has content, needed in case of
      * zero sized "text" content.
@@ -320,6 +327,7 @@
         currentNode = null;
         Document ret = document;
         document = null;
+        doctype = null;
         return ret;
     }
 
@@ -440,6 +448,7 @@
         currentNode  = null;
         Document ret = document;
         document     = null;
+        doctype      = null;
         locator      = null;
         parser       = null;
         return ret;
@@ -531,6 +540,7 @@
         inProlog     = true;
         currentNode  = null;
         document     = null;
+        doctype      = null;
         isStandalone = false;
         xmlVersion   = XMLConstants.XML_VERSION_10;
 
@@ -614,7 +624,7 @@
         String nsURI = namespaces.get(nsp);
         if (currentNode == null) {
             implementation = getDOMImplementation(version);
-            document = implementation.createDocument(nsURI, rawName, null);
+            document = implementation.createDocument(nsURI, rawName, doctype);
             Iterator i = preInfo.iterator();
             currentNode = e = document.getDocumentElement();
             while (i.hasNext()) {
@@ -733,6 +743,7 @@
     public void startDTD(String name, String publicId, String systemId)
         throws SAXException {
         appendStringData(); // Add collected string data before entering DTD
+        doctype = implementation.createDocumentType(name, publicId, systemId);
         inDTD = true;
     }
 

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/script/Window.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/script/Window.java?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/script/Window.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/script/Window.java Sun Jun  1 18:37:54 2008
@@ -82,6 +82,11 @@
     Node parseXML(String text, Document doc);
 
     /**
+     * Serializes the given node.
+     */
+    String printNode(Node n);
+
+    /**
      * Gets data from the given URI.
      * @param uri The URI where the data is located.
      * @param h A handler called when the data is available.

Modified: xmlgraphics/batik/trunk/sources/org/apache/batik/script/rhino/WindowWrapper.java
URL: http://svn.apache.org/viewvc/xmlgraphics/batik/trunk/sources/org/apache/batik/script/rhino/WindowWrapper.java?rev=662304&r1=662303&r2=662304&view=diff
==============================================================================
--- xmlgraphics/batik/trunk/sources/org/apache/batik/script/rhino/WindowWrapper.java (original)
+++ xmlgraphics/batik/trunk/sources/org/apache/batik/script/rhino/WindowWrapper.java Sun Jun  1 18:37:54 2008
@@ -30,7 +30,9 @@
 import org.mozilla.javascript.NativeObject;
 import org.mozilla.javascript.Scriptable;
 import org.mozilla.javascript.ScriptableObject;
+
 import org.w3c.dom.Document;
+import org.w3c.dom.Node;
 
 /**
  * This class wraps a Window object to expose it to the interpreter.
@@ -60,8 +62,8 @@
     public WindowWrapper(Context context) {
         super(context);
         String[] names = { "setInterval", "setTimeout", "clearInterval",
-                           "clearTimeout", "parseXML", "getURL", "postURL",
-                           "alert", "confirm", "prompt" };
+                           "clearTimeout", "parseXML", "printNode", "getURL",
+                           "postURL", "alert", "confirm", "prompt" };
         this.defineFunctionProperties(names, WindowWrapper.class,
                                       ScriptableObject.DONTENUM);
     }
@@ -187,6 +189,32 @@
     }
 
     /**
+     * Wraps the 'printNode' method of the Window interface.
+     */
+    public static Object printNode(Context cx,
+                                   Scriptable thisObj,
+                                   final Object[] args,
+                                   Function funObj) {
+        if (args.length != 1) {
+            throw Context.reportRuntimeError("invalid argument count");
+        }
+
+        WindowWrapper ww = (WindowWrapper)thisObj;
+        final Window window = ww.window;
+
+        AccessControlContext acc =
+            ((RhinoInterpreter)window.getInterpreter()).getAccessControlContext();
+
+        Object ret = AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return window.printNode
+                        ((Node) Context.jsToJava(args[0], Node.class));
+                }
+            }, acc);
+        return Context.toString(ret);
+    }
+
+    /**
      * Wraps the 'getURL' method of the Window interface.
      */
     public static void getURL(Context cx,
@@ -366,7 +394,7 @@
         if (result == null) {
             return null;
         }
-        return Context.toObject(result, thisObj);
+        return Context.toString(result);
     }
 
     /**