changeset 22331:920c3ec54499

GraphPrintVisitor rewrite using javax.xml.stream
author Andreas Woess <andreas.woess@oracle.com>
date Wed, 28 Oct 2015 14:00:16 +0100
parents 4db968103a7c
children fdc687bad5d4
files truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java
diffstat 2 files changed, 259 insertions(+), 200 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java	Tue Oct 20 23:53:21 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java	Wed Oct 28 14:00:16 2015 +0100
@@ -25,121 +25,168 @@
 package com.oracle.truffle.api.nodes;
 
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.StringWriter;
+import java.io.OutputStream;
+import java.io.PrintStream;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.net.Socket;
-import java.util.ArrayDeque;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Deque;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
+import com.oracle.truffle.api.TruffleOptions;
 import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind;
 
 /**
  * Utility class for creating output for the ideal graph visualizer.
  */
-public class GraphPrintVisitor {
+public class GraphPrintVisitor implements Closeable {
 
     public static final String GraphVisualizerAddress = "127.0.0.1";
     public static final int GraphVisualizerPort = 4444;
+    private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
 
-    private static DocumentBuilderFactory documentBuilderFactory;
-    private static TransformerFactory transformerFactory;
+    private Map<Object, NodeElement> nodeMap;
+    private List<EdgeElement> edgeList;
+    private Map<Object, NodeElement> prevNodeMap;
+    private int id;
+    private XMLStreamWriter xmlstream;
+    private OutputStream outputStream;
+    private int openGroupCount;
+    private int openGraphCount;
+    private String currentGraphName;
+
+    private static class NodeElement {
+        private final int id;
+        private final Map<String, Object> properties;
+
+        public NodeElement(int id) {
+            super();
+            this.id = id;
+            this.properties = new LinkedHashMap<>();
+        }
+
+        public int getId() {
+            return id;
+        }
 
-    private Document dom;
-    private Map<Object, Element> nodeMap;
-    private List<Element> edgeList;
-    private Map<Object, Element> prevNodeMap;
-    private int id;
-    private Element graphDocument;
-    private Element graphElement;
-    private Element nodesElement;
-    private Element edgesElement;
-    private Deque<Element> groupElementStack;
+        public Map<String, Object> getProperties() {
+            return properties;
+        }
+    }
+
+    private static class EdgeElement {
+        private final NodeElement from;
+        private final NodeElement to;
+        private final int index;
+        private final String label;
+
+        public EdgeElement(NodeElement from, NodeElement to, int index, String label) {
+            this.from = from;
+            this.to = to;
+            this.index = index;
+            this.label = label;
+        }
+
+        public NodeElement getFrom() {
+            return from;
+        }
+
+        public NodeElement getTo() {
+            return to;
+        }
+
+        public int getIndex() {
+            return index;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+    }
 
     public GraphPrintVisitor() {
-        DocumentBuilder db = newDocumentBuilder();
-        dom = db.newDocument();
+        this(new ByteArrayOutputStream());
+    }
 
-        graphDocument = dom.createElement("graphDocument");
-        dom.appendChild(graphDocument);
-        groupElementStack = new ArrayDeque<>();
+    public GraphPrintVisitor(OutputStream outputStream) {
+        this.outputStream = outputStream;
+        try {
+            this.xmlstream = XML_OUTPUT_FACTORY.createXMLStreamWriter(outputStream);
+            this.xmlstream.writeStartDocument();
+            this.xmlstream.writeStartElement("graphDocument");
+        } catch (XMLStreamException | FactoryConfigurationError e) {
+            throw new RuntimeException(e);
+        }
     }
 
     public GraphPrintVisitor beginGroup(String groupName) {
-        Element parent = groupElement() != null ? groupElement() : graphDocument;
+        maybeEndGraph();
+        openGroupCount++;
+        try {
+            xmlstream.writeStartElement("group");
+            xmlstream.writeStartElement("properties");
 
-        Element groupElement = dom.createElement("group");
-        groupElementStack.push(groupElement);
-        parent.appendChild(groupElement);
+            if (!groupName.isEmpty()) {
+                // set group name
+                xmlstream.writeStartElement("p");
+                xmlstream.writeAttribute("name", "name");
+                xmlstream.writeCharacters(groupName);
+                xmlstream.writeEndElement();
+            }
 
-        Element properties = dom.createElement("properties");
-        groupElement.appendChild(properties);
+            xmlstream.writeEndElement(); // properties
 
-        if (!groupName.isEmpty()) {
-            // set group name
-            Element propName = dom.createElement("p");
-            propName.setAttribute("name", "name");
-            propName.setTextContent(groupName);
-            properties.appendChild(propName);
+            // forget old nodes
+            prevNodeMap = null;
+            nodeMap = new IdentityHashMap<>();
+            edgeList = new ArrayList<>();
+
+            return this;
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
         }
+    }
 
-        // forget old nodes
-        nodeMap = prevNodeMap = null;
-        edgeList = null;
+    public GraphPrintVisitor endGroup() {
+        if (openGroupCount <= 0) {
+            throw new IllegalArgumentException("no open group");
+        }
+        maybeEndGraph();
+        openGroupCount--;
+
+        try {
+            xmlstream.writeEndElement(); // group
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
+        }
 
         return this;
     }
 
-    public GraphPrintVisitor endGroup() {
-        groupElementStack.pop();
-        return this;
-    }
-
     public GraphPrintVisitor beginGraph(String graphName) {
-        if (null == groupElement()) {
+        if (openGroupCount == 0) {
             beginGroup(graphName);
         }
+        maybeEndGraph();
+        openGraphCount++;
 
-        graphElement = dom.createElement("graph");
-        groupElement().appendChild(graphElement);
-        Element properties = dom.createElement("properties");
-        graphElement.appendChild(properties);
-        nodesElement = dom.createElement("nodes");
-        graphElement.appendChild(nodesElement);
-        edgesElement = dom.createElement("edges");
-        graphElement.appendChild(edgesElement);
-
-        // set graph name
-        Element propName = dom.createElement("p");
-        propName.setAttribute("name", "name");
-        propName.setTextContent(graphName);
-        properties.appendChild(propName);
+        this.currentGraphName = graphName;
 
         // save old nodes
         prevNodeMap = nodeMap;
@@ -149,96 +196,161 @@
         return this;
     }
 
-    private Element groupElement() {
-        return groupElementStack.peek();
+    private void maybeEndGraph() {
+        if (openGraphCount > 0) {
+            endGraph();
+            assert openGraphCount == 0;
+        }
+    }
+
+    public GraphPrintVisitor endGraph() {
+        if (openGraphCount <= 0) {
+            throw new IllegalArgumentException("no open graph");
+        }
+        openGraphCount--;
+
+        try {
+            xmlstream.writeStartElement("graph");
+
+            xmlstream.writeStartElement("properties");
+
+            // set graph name
+            xmlstream.writeStartElement("p");
+            xmlstream.writeAttribute("name", "name");
+            xmlstream.writeCharacters(currentGraphName);
+            xmlstream.writeEndElement();
+
+            xmlstream.writeEndElement(); // properties
+
+            xmlstream.writeStartElement("nodes");
+            writeNodes();
+            xmlstream.writeEndElement(); // nodes
+
+            xmlstream.writeStartElement("edges");
+            writeEdges();
+            xmlstream.writeEndElement(); // edges
+
+            xmlstream.writeEndElement(); // graph
+
+            xmlstream.flush();
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
+        }
+
+        return this;
+    }
+
+    private void writeNodes() throws XMLStreamException {
+        for (NodeElement node : nodeMap.values()) {
+            xmlstream.writeStartElement("node");
+            xmlstream.writeAttribute("id", String.valueOf(node.getId()));
+
+            xmlstream.writeStartElement("properties");
+            for (Map.Entry<String, Object> property : node.getProperties().entrySet()) {
+                xmlstream.writeStartElement("p");
+                xmlstream.writeAttribute("name", property.getKey());
+                xmlstream.writeCharacters(String.valueOf(property.getValue()));
+                xmlstream.writeEndElement(); // p
+            }
+            xmlstream.writeEndElement(); // properties
+
+            xmlstream.writeEndElement(); // node
+        }
+    }
+
+    private void writeEdges() throws XMLStreamException {
+        for (EdgeElement edge : edgeList) {
+            xmlstream.writeStartElement("edge");
+
+            xmlstream.writeAttribute("from", String.valueOf(edge.getFrom().getId()));
+            xmlstream.writeAttribute("to", String.valueOf(edge.getTo().getId()));
+            xmlstream.writeAttribute("index", String.valueOf(edge.getIndex()));
+            if (edge.getLabel() != null) {
+                xmlstream.writeAttribute("label", edge.getLabel());
+            }
+
+            xmlstream.writeEndElement(); // edge
+        }
     }
 
     @Override
     public String toString() {
-        if (null != dom) {
-            try {
-                Transformer tr = newTransformer();
-                tr.setOutputProperty(OutputKeys.INDENT, "yes");
-                tr.setOutputProperty(OutputKeys.METHOD, "xml");
-                tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
-
-                StringWriter strWriter = new StringWriter();
-                tr.transform(new DOMSource(dom), new StreamResult(strWriter));
-                return strWriter.toString();
-            } catch (TransformerException e) {
-                e.printStackTrace();
-            }
+        if (outputStream instanceof ByteArrayOutputStream) {
+            return new String(((ByteArrayOutputStream) outputStream).toByteArray(), Charset.forName("UTF-8"));
         }
-        return "";
+        return super.toString();
     }
 
     public void printToFile(File f) {
-        try {
-            Transformer tr = newTransformer();
-            tr.setOutputProperty(OutputKeys.INDENT, "yes");
-            tr.setOutputProperty(OutputKeys.METHOD, "xml");
-            tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
-
-            tr.transform(new DOMSource(dom), new StreamResult(new FileOutputStream(f)));
-        } catch (TransformerException | FileNotFoundException e) {
-            e.printStackTrace();
+        close();
+        if (outputStream instanceof ByteArrayOutputStream) {
+            try (OutputStream os = new FileOutputStream(f)) {
+                os.write(((ByteArrayOutputStream) outputStream).toByteArray());
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
         }
     }
 
     public void printToSysout() {
-        try {
-            Transformer tr = newTransformer();
-            tr.setOutputProperty(OutputKeys.INDENT, "yes");
-            tr.setOutputProperty(OutputKeys.METHOD, "xml");
-            tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
-
-            tr.transform(new DOMSource(dom), new StreamResult(System.out));
-        } catch (TransformerException e) {
-            e.printStackTrace();
+        close();
+        if (outputStream instanceof ByteArrayOutputStream) {
+            PrintStream out = System.out;
+            out.println(toString());
         }
     }
 
     public void printToNetwork(boolean ignoreErrors) {
-        try {
-            Transformer tr = newTransformer();
-            tr.setOutputProperty(OutputKeys.METHOD, "xml");
-
-            Socket socket = new Socket(GraphVisualizerAddress, GraphVisualizerPort);
-            BufferedOutputStream stream = new BufferedOutputStream(socket.getOutputStream(), 0x4000);
-            tr.transform(new DOMSource(dom), new StreamResult(stream));
-        } catch (TransformerException | IOException e) {
+        close();
+        try (Socket socket = new Socket(GraphVisualizerAddress, GraphVisualizerPort); BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream(), 0x4000)) {
+            os.write(((ByteArrayOutputStream) outputStream).toByteArray());
+        } catch (IOException e) {
             if (!ignoreErrors) {
                 e.printStackTrace();
             }
         }
     }
 
-    private String nextId() {
-        return String.valueOf(id++);
+    public void close() {
+        if (xmlstream == null) {
+            return;
+        }
+        try {
+            while (openGroupCount > 0) {
+                endGroup();
+            }
+
+            xmlstream.writeEndElement(); // graphDocument
+            xmlstream.writeEndDocument();
+            xmlstream.flush();
+            xmlstream = null;
+        } catch (XMLStreamException e) {
+            throw new RuntimeException(e);
+        }
     }
 
-    private String oldOrNextId(Object node) {
+    private int nextId() {
+        return id++;
+    }
+
+    private int oldOrNextId(Object node) {
         if (null != prevNodeMap && prevNodeMap.containsKey(node)) {
-            Element nodeElem = prevNodeMap.get(node);
-            return nodeElem.getAttribute("id");
+            NodeElement nodeElem = prevNodeMap.get(node);
+            return nodeElem.getId();
         } else {
             return nextId();
         }
     }
 
-    protected Element getElementByObject(Object op) {
-        return nodeMap.get(op);
+    protected NodeElement getElementByObject(Object obj) {
+        return nodeMap.get(obj);
     }
 
     protected void createElementForNode(Object node) {
         boolean exists = nodeMap.containsKey(node);
         if (!exists) {
-            Element nodeElem = dom.createElement("node");
-            nodeElem.setAttribute("id", !exists ? oldOrNextId(node) : nextId());
-            nodeMap.put(node, nodeElem);
-            Element properties = dom.createElement("properties");
-            nodeElem.appendChild(properties);
-            nodesElement.appendChild(nodeElem);
+            int nodeId = !exists ? oldOrNextId(node) : nextId();
+            nodeMap.put(node, new NodeElement(nodeId));
 
             setNodeProperty(node, "name", node.getClass().getSimpleName().replaceFirst("Node$", ""));
             NodeInfo nodeInfo = node.getClass().getAnnotation(NodeInfo.class);
@@ -256,33 +368,9 @@
         }
     }
 
-    private Element getPropertyElement(Object node, String propertyName) {
-        Element nodeElem = getElementByObject(node);
-        Element propertiesElem = (Element) nodeElem.getElementsByTagName("properties").item(0);
-        if (propertiesElem == null) {
-            return null;
-        }
-
-        NodeList propList = propertiesElem.getElementsByTagName("p");
-        for (int i = 0; i < propList.getLength(); i++) {
-            Element p = (Element) propList.item(i);
-            if (propertyName.equals(p.getAttribute("name"))) {
-                return p;
-            }
-        }
-        return null;
-    }
-
     protected void setNodeProperty(Object node, String propertyName, Object value) {
-        Element nodeElem = getElementByObject(node);
-        Element propElem = getPropertyElement(node, propertyName);
-        // if property exists, replace its value, otherwise create a new one
-        if (null == propElem) {
-            propElem = dom.createElement("p");
-            propElem.setAttribute("name", propertyName);
-            nodeElem.getElementsByTagName("properties").item(0).appendChild(propElem);
-        }
-        propElem.setTextContent(String.valueOf(value));
+        NodeElement nodeElem = getElementByObject(node);
+        nodeElem.getProperties().put(propertyName, value);
     }
 
     private void copyDebugProperties(Node node) {
@@ -297,7 +385,7 @@
         for (NodeFieldAccessor field : fields) {
             if (field.getKind() == NodeFieldKind.DATA) {
                 String key = field.getName();
-                if (getPropertyElement(node, key) == null) {
+                if (!getElementByObject(node).getProperties().containsKey(key)) {
                     Object value = field.loadValue(node);
                     setNodeProperty(node, key, value);
                 }
@@ -306,34 +394,25 @@
     }
 
     protected void connectNodes(Object a, Object b, String label) {
-        if (getElementByObject(a) == null || getElementByObject(b) == null) {
+        NodeElement fromNode = getElementByObject(a);
+        NodeElement toNode = getElementByObject(b);
+        if (fromNode == null || toNode == null) {
             return;
         }
 
-        String fromId = getElementByObject(a).getAttribute("id");
-        String toId = getElementByObject(b).getAttribute("id");
-
         // count existing to-edges
         int count = 0;
-        for (Element e : edgeList) {
-            if (e.getAttribute("to").equals(toId)) {
+        for (EdgeElement e : edgeList) {
+            if (e.getTo() == toNode) {
                 ++count;
             }
         }
 
-        Element edgeElem = dom.createElement("edge");
-        edgeElem.setAttribute("from", fromId);
-        edgeElem.setAttribute("to", toId);
-        edgeElem.setAttribute("index", String.valueOf(count));
-        if (label != null) {
-            edgeElem.setAttribute("label", label);
-        }
-        edgesElement.appendChild(edgeElem);
-        edgeList.add(edgeElem);
+        edgeList.add(new EdgeElement(fromNode, toNode, count, label));
     }
 
     public GraphPrintVisitor visit(Object node) {
-        if (null == graphElement) {
+        if (openGraphCount == 0) {
             beginGraph("truffle tree");
         }
 
@@ -343,7 +422,7 @@
         }
 
         // respect node's custom handler
-        if (NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class) != null) {
+        if (!TruffleOptions.AOT && NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class) != null) {
             visit(node, createGraphPrintHandlerFromClass(NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class).handler()));
         } else if (NodeUtil.findAnnotation(node.getClass(), NullGraphPrintHandler.class) != null) {
             // ignore
@@ -394,28 +473,6 @@
         return nodes;
     }
 
-    private static DocumentBuilder newDocumentBuilder() {
-        try {
-            if (documentBuilderFactory == null) {
-                documentBuilderFactory = DocumentBuilderFactory.newInstance();
-            }
-            return documentBuilderFactory.newDocumentBuilder();
-        } catch (ParserConfigurationException ex) {
-            throw new RuntimeException(ex);
-        }
-    }
-
-    private static Transformer newTransformer() {
-        try {
-            if (transformerFactory == null) {
-                transformerFactory = TransformerFactory.newInstance();
-            }
-            return transformerFactory.newTransformer();
-        } catch (TransformerConfigurationException ex) {
-            throw new RuntimeException(ex);
-        }
-    }
-
     public class GraphPrintAdapter {
 
         public void createElementForNode(Object node) {
@@ -449,17 +506,17 @@
 
     public interface GraphPrintHandler {
 
-        void visit(Object node, GraphPrintAdapter gPrinter);
+        void visit(Object node, GraphPrintAdapter printer);
     }
 
     private static final class DefaultGraphPrintHandler implements GraphPrintHandler {
-        public void visit(Object node, GraphPrintAdapter gPrinter) {
-            gPrinter.createElementForNode(node);
+        public void visit(Object node, GraphPrintAdapter printer) {
+            printer.createElementForNode(node);
 
             if (node instanceof Node) {
                 for (Map.Entry<String, Node> child : findNamedNodeChildren((Node) node).entrySet()) {
-                    gPrinter.visit(child.getValue());
-                    gPrinter.connectNodes(node, child.getValue(), child.getKey());
+                    printer.visit(child.getValue());
+                    printer.connectNodes(node, child.getValue(), child.getKey());
                 }
             }
         }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Tue Oct 20 23:53:21 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Oct 28 14:00:16 2015 +0100
@@ -421,9 +421,11 @@
         if (clazz.getAnnotation(annotationClass) != null) {
             return clazz.getAnnotation(annotationClass);
         } else {
-            for (Class<?> intf : clazz.getInterfaces()) {
-                if (intf.getAnnotation(annotationClass) != null) {
-                    return intf.getAnnotation(annotationClass);
+            if (!TruffleOptions.AOT) {
+                for (Class<?> intf : clazz.getInterfaces()) {
+                    if (intf.getAnnotation(annotationClass) != null) {
+                        return intf.getAnnotation(annotationClass);
+                    }
                 }
             }
             if (clazz.getSuperclass() != null) {