| /* |
| * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| package com.sun.hotspot.igv.data.serialization; |
| |
| import com.sun.hotspot.igv.data.*; |
| import com.sun.hotspot.igv.data.serialization.XMLParser.ElementHandler; |
| import com.sun.hotspot.igv.data.serialization.XMLParser.HandoverElementHandler; |
| import com.sun.hotspot.igv.data.serialization.XMLParser.TopElementHandler; |
| import com.sun.hotspot.igv.data.services.GroupCallback; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.channels.Channels; |
| import java.nio.channels.ReadableByteChannel; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| import javax.swing.SwingUtilities; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.parsers.SAXParserFactory; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.stream.StreamSource; |
| import javax.xml.validation.SchemaFactory; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| import org.xml.sax.XMLReader; |
| |
| /** |
| * |
| * @author Thomas Wuerthinger |
| */ |
| public class Parser implements GraphParser { |
| |
| public static final String INDENT = " "; |
| public static final String TOP_ELEMENT = "graphDocument"; |
| public static final String GROUP_ELEMENT = "group"; |
| public static final String GRAPH_ELEMENT = "graph"; |
| public static final String ROOT_ELEMENT = "graphDocument"; |
| public static final String PROPERTIES_ELEMENT = "properties"; |
| public static final String EDGES_ELEMENT = "edges"; |
| public static final String PROPERTY_ELEMENT = "p"; |
| public static final String EDGE_ELEMENT = "edge"; |
| public static final String NODE_ELEMENT = "node"; |
| public static final String NODES_ELEMENT = "nodes"; |
| public static final String REMOVE_EDGE_ELEMENT = "removeEdge"; |
| public static final String REMOVE_NODE_ELEMENT = "removeNode"; |
| public static final String METHOD_NAME_PROPERTY = "name"; |
| public static final String GROUP_NAME_PROPERTY = "name"; |
| public static final String METHOD_IS_PUBLIC_PROPERTY = "public"; |
| public static final String METHOD_IS_STATIC_PROPERTY = "static"; |
| public static final String TRUE_VALUE = "true"; |
| public static final String NODE_NAME_PROPERTY = "name"; |
| public static final String EDGE_NAME_PROPERTY = "name"; |
| public static final String NODE_ID_PROPERTY = "id"; |
| public static final String FROM_PROPERTY = "from"; |
| public static final String TO_PROPERTY = "to"; |
| public static final String TYPE_PROPERTY = "type"; |
| public static final String PROPERTY_NAME_PROPERTY = "name"; |
| public static final String GRAPH_NAME_PROPERTY = "name"; |
| public static final String FROM_INDEX_PROPERTY = "fromIndex"; |
| public static final String TO_INDEX_PROPERTY = "toIndex"; |
| public static final String TO_INDEX_ALT_PROPERTY = "index"; |
| public static final String LABEL_PROPERTY = "label"; |
| public static final String METHOD_ELEMENT = "method"; |
| public static final String INLINE_ELEMENT = "inline"; |
| public static final String BYTECODES_ELEMENT = "bytecodes"; |
| public static final String METHOD_BCI_PROPERTY = "bci"; |
| public static final String METHOD_SHORT_NAME_PROPERTY = "shortName"; |
| public static final String CONTROL_FLOW_ELEMENT = "controlFlow"; |
| public static final String BLOCK_NAME_PROPERTY = "name"; |
| public static final String BLOCK_ELEMENT = "block"; |
| public static final String SUCCESSORS_ELEMENT = "successors"; |
| public static final String SUCCESSOR_ELEMENT = "successor"; |
| public static final String ASSEMBLY_ELEMENT = "assembly"; |
| public static final String DIFFERENCE_PROPERTY = "difference"; |
| private TopElementHandler<GraphDocument> xmlDocument = new TopElementHandler<>(); |
| private Map<Group, Boolean> differenceEncoding = new HashMap<>(); |
| private Map<Group, InputGraph> lastParsedGraph = new HashMap<>(); |
| private GroupCallback groupCallback; |
| private HashMap<String, Integer> idCache = new HashMap<>(); |
| private ArrayList<Pair<String, String>> blockConnections = new ArrayList<>(); |
| private int maxId = 0; |
| private GraphDocument graphDocument; |
| private final ParseMonitor monitor; |
| private final ReadableByteChannel channel; |
| |
| private int lookupID(String i) { |
| try { |
| return Integer.parseInt(i); |
| } catch (NumberFormatException nfe) { |
| // ignore |
| } |
| Integer id = idCache.get(i); |
| if (id == null) { |
| id = maxId++; |
| idCache.put(i, id); |
| } |
| return id.intValue(); |
| } |
| |
| // <graphDocument> |
| private ElementHandler<GraphDocument, Object> topHandler = new ElementHandler<GraphDocument, Object>(TOP_ELEMENT) { |
| |
| @Override |
| protected GraphDocument start() throws SAXException { |
| graphDocument = new GraphDocument(); |
| return graphDocument; |
| } |
| }; |
| // <group> |
| private ElementHandler<Group, Folder> groupHandler = new XMLParser.ElementHandler<Group, Folder>(GROUP_ELEMENT) { |
| |
| @Override |
| protected Group start() throws SAXException { |
| final Group group = new Group(this.getParentObject()); |
| |
| String differenceProperty = this.readAttribute(DIFFERENCE_PROPERTY); |
| Parser.this.differenceEncoding.put(group, (differenceProperty != null && (differenceProperty.equals("1") || differenceProperty.equals("true")))); |
| |
| ParseMonitor monitor = getMonitor(); |
| if (monitor != null) { |
| monitor.setState(group.getName()); |
| } |
| |
| final Folder parent = getParentObject(); |
| if (groupCallback == null || parent instanceof Group) { |
| SwingUtilities.invokeLater(new Runnable(){ |
| @Override |
| public void run() { |
| parent.addElement(group); |
| } |
| }); |
| } |
| |
| return group; |
| } |
| |
| @Override |
| protected void end(String text) throws SAXException { |
| } |
| }; |
| // <method> |
| private ElementHandler<InputMethod, Group> methodHandler = new XMLParser.ElementHandler<InputMethod, Group>(METHOD_ELEMENT) { |
| |
| @Override |
| protected InputMethod start() throws SAXException { |
| |
| InputMethod method = parseMethod(this, getParentObject()); |
| getParentObject().setMethod(method); |
| return method; |
| } |
| }; |
| |
| private InputMethod parseMethod(XMLParser.ElementHandler<?,?> handler, Group group) throws SAXException { |
| String s = handler.readRequiredAttribute(METHOD_BCI_PROPERTY); |
| int bci = 0; |
| try { |
| bci = Integer.parseInt(s); |
| } catch (NumberFormatException e) { |
| throw new SAXException(e); |
| } |
| InputMethod method = new InputMethod(group, handler.readRequiredAttribute(METHOD_NAME_PROPERTY), handler.readRequiredAttribute(METHOD_SHORT_NAME_PROPERTY), bci); |
| return method; |
| } |
| // <bytecodes> |
| private HandoverElementHandler<InputMethod> bytecodesHandler = new XMLParser.HandoverElementHandler<InputMethod>(BYTECODES_ELEMENT, true) { |
| |
| @Override |
| protected void end(String text) throws SAXException { |
| getParentObject().setBytecodes(text); |
| } |
| }; |
| // <inlined> |
| private HandoverElementHandler<InputMethod> inlinedHandler = new XMLParser.HandoverElementHandler<>(INLINE_ELEMENT); |
| // <inlined><method> |
| private ElementHandler<InputMethod, InputMethod> inlinedMethodHandler = new XMLParser.ElementHandler<InputMethod, InputMethod>(METHOD_ELEMENT) { |
| |
| @Override |
| protected InputMethod start() throws SAXException { |
| InputMethod method = parseMethod(this, getParentObject().getGroup()); |
| getParentObject().addInlined(method); |
| return method; |
| } |
| }; |
| // <graph> |
| private ElementHandler<InputGraph, Group> graphHandler = new XMLParser.ElementHandler<InputGraph, Group>(GRAPH_ELEMENT) { |
| |
| @Override |
| protected InputGraph start() throws SAXException { |
| String name = readAttribute(GRAPH_NAME_PROPERTY); |
| InputGraph curGraph = new InputGraph(name); |
| if (differenceEncoding.get(getParentObject())) { |
| InputGraph previous = lastParsedGraph.get(getParentObject()); |
| lastParsedGraph.put(getParentObject(), curGraph); |
| if (previous != null) { |
| for (InputNode n : previous.getNodes()) { |
| curGraph.addNode(n); |
| } |
| for (InputEdge e : previous.getEdges()) { |
| curGraph.addEdge(e); |
| } |
| } |
| } |
| ParseMonitor monitor = getMonitor(); |
| if (monitor != null) { |
| monitor.updateProgress(); |
| } |
| return curGraph; |
| } |
| |
| @Override |
| protected void end(String text) throws SAXException { |
| // NOTE: Some graphs intentionally don't provide blocks. Instead |
| // they later generate the blocks from other information such |
| // as node properties (example: ServerCompilerScheduler). |
| // Thus, we shouldn't assign nodes that don't belong to any |
| // block to some artificial block below unless blocks are |
| // defined and nodes are assigned to them. |
| |
| final InputGraph graph = getObject(); |
| final Group parent = getParentObject(); |
| if (graph.getBlocks().size() > 0) { |
| boolean blocksContainNodes = false; |
| for (InputBlock b : graph.getBlocks()) { |
| if (b.getNodes().size() > 0) { |
| blocksContainNodes = true; |
| break; |
| } |
| } |
| |
| if (!blocksContainNodes) { |
| graph.clearBlocks(); |
| blockConnections.clear(); |
| } else { |
| // Blocks and their nodes defined: add other nodes to an |
| // artificial "no block" block |
| InputBlock noBlock = null; |
| for (InputNode n : graph.getNodes()) { |
| if (graph.getBlock(n) == null) { |
| if (noBlock == null) { |
| noBlock = graph.addBlock("(no block)"); |
| } |
| |
| noBlock.addNode(n.getId()); |
| } |
| |
| assert graph.getBlock(n) != null; |
| } |
| } |
| } |
| |
| // Resolve block successors |
| for (Pair<String, String> p : blockConnections) { |
| final InputBlock left = graph.getBlock(p.getLeft()); |
| assert left != null; |
| final InputBlock right = graph.getBlock(p.getRight()); |
| assert right != null; |
| graph.addBlockEdge(left, right); |
| } |
| blockConnections.clear(); |
| |
| SwingUtilities.invokeLater(new Runnable(){ |
| |
| @Override |
| public void run() { |
| // Add to group |
| parent.addElement(graph); |
| } |
| }); |
| } |
| }; |
| // <nodes> |
| private HandoverElementHandler<InputGraph> nodesHandler = new HandoverElementHandler<>(NODES_ELEMENT); |
| // <controlFlow> |
| private HandoverElementHandler<InputGraph> controlFlowHandler = new HandoverElementHandler<>(CONTROL_FLOW_ELEMENT); |
| // <block> |
| private ElementHandler<InputBlock, InputGraph> blockHandler = new ElementHandler<InputBlock, InputGraph>(BLOCK_ELEMENT) { |
| |
| @Override |
| protected InputBlock start() throws SAXException { |
| InputGraph graph = getParentObject(); |
| String name = readRequiredAttribute(BLOCK_NAME_PROPERTY); |
| InputBlock b = graph.addBlock(name); |
| for (InputNode n : b.getNodes()) { |
| assert graph.getBlock(n).equals(b); |
| } |
| return b; |
| } |
| }; |
| // <nodes> |
| private HandoverElementHandler<InputBlock> blockNodesHandler = new HandoverElementHandler<>(NODES_ELEMENT); |
| // <node> |
| private ElementHandler<InputBlock, InputBlock> blockNodeHandler = new ElementHandler<InputBlock, InputBlock>(NODE_ELEMENT) { |
| |
| @Override |
| protected InputBlock start() throws SAXException { |
| String s = readRequiredAttribute(NODE_ID_PROPERTY); |
| |
| int id = 0; |
| try { |
| id = lookupID(s); |
| } catch (NumberFormatException e) { |
| throw new SAXException(e); |
| } |
| getParentObject().addNode(id); |
| return getParentObject(); |
| } |
| }; |
| // <successors> |
| private HandoverElementHandler<InputBlock> successorsHandler = new HandoverElementHandler<>(SUCCESSORS_ELEMENT); |
| // <successor> |
| private ElementHandler<InputBlock, InputBlock> successorHandler = new ElementHandler<InputBlock, InputBlock>(SUCCESSOR_ELEMENT) { |
| |
| @Override |
| protected InputBlock start() throws SAXException { |
| String name = readRequiredAttribute(BLOCK_NAME_PROPERTY); |
| blockConnections.add(new Pair<>(getParentObject().getName(), name)); |
| return getParentObject(); |
| } |
| }; |
| // <node> |
| private ElementHandler<InputNode, InputGraph> nodeHandler = new ElementHandler<InputNode, InputGraph>(NODE_ELEMENT) { |
| |
| @Override |
| protected InputNode start() throws SAXException { |
| String s = readRequiredAttribute(NODE_ID_PROPERTY); |
| int id = 0; |
| try { |
| id = lookupID(s); |
| } catch (NumberFormatException e) { |
| throw new SAXException(e); |
| } |
| InputNode node = new InputNode(id); |
| getParentObject().addNode(node); |
| return node; |
| } |
| }; |
| // <removeNode> |
| private ElementHandler<InputNode, InputGraph> removeNodeHandler = new ElementHandler<InputNode, InputGraph>(REMOVE_NODE_ELEMENT) { |
| |
| @Override |
| protected InputNode start() throws SAXException { |
| String s = readRequiredAttribute(NODE_ID_PROPERTY); |
| int id = 0; |
| try { |
| id = lookupID(s); |
| } catch (NumberFormatException e) { |
| throw new SAXException(e); |
| } |
| return getParentObject().removeNode(id); |
| } |
| }; |
| // <graph> |
| private HandoverElementHandler<InputGraph> edgesHandler = new HandoverElementHandler<>(EDGES_ELEMENT); |
| |
| // Local class for edge elements |
| private class EdgeElementHandler extends ElementHandler<InputEdge, InputGraph> { |
| |
| public EdgeElementHandler(String name) { |
| super(name); |
| } |
| |
| @Override |
| protected InputEdge start() throws SAXException { |
| int fromIndex = 0; |
| int toIndex = 0; |
| int from = -1; |
| int to = -1; |
| String label = null; |
| String type = null; |
| |
| try { |
| String fromIndexString = readAttribute(FROM_INDEX_PROPERTY); |
| if (fromIndexString != null) { |
| fromIndex = Integer.parseInt(fromIndexString); |
| } |
| |
| String toIndexString = readAttribute(TO_INDEX_PROPERTY); |
| if (toIndexString == null) { |
| toIndexString = readAttribute(TO_INDEX_ALT_PROPERTY); |
| } |
| if (toIndexString != null) { |
| toIndex = Integer.parseInt(toIndexString); |
| } |
| |
| label = readAttribute(LABEL_PROPERTY); |
| type = readAttribute(TYPE_PROPERTY); |
| |
| from = lookupID(readRequiredAttribute(FROM_PROPERTY)); |
| to = lookupID(readRequiredAttribute(TO_PROPERTY)); |
| } catch (NumberFormatException e) { |
| throw new SAXException(e); |
| } |
| |
| InputEdge conn = new InputEdge((char) fromIndex, (char) toIndex, from, to, label, type == null ? "" : type); |
| return start(conn); |
| } |
| |
| protected InputEdge start(InputEdge conn) throws SAXException { |
| return conn; |
| } |
| } |
| // <edge> |
| private EdgeElementHandler edgeHandler = new EdgeElementHandler(EDGE_ELEMENT) { |
| |
| @Override |
| protected InputEdge start(InputEdge conn) throws SAXException { |
| getParentObject().addEdge(conn); |
| return conn; |
| } |
| }; |
| // <removeEdge> |
| private EdgeElementHandler removeEdgeHandler = new EdgeElementHandler(REMOVE_EDGE_ELEMENT) { |
| |
| @Override |
| protected InputEdge start(InputEdge conn) throws SAXException { |
| getParentObject().removeEdge(conn); |
| return conn; |
| } |
| }; |
| // <properties> |
| private HandoverElementHandler<Properties.Provider> propertiesHandler = new HandoverElementHandler<>(PROPERTIES_ELEMENT); |
| // <properties> |
| private HandoverElementHandler<Group> groupPropertiesHandler = new HandoverElementHandler<Group>(PROPERTIES_ELEMENT) { |
| |
| @Override |
| public void end(String text) throws SAXException { |
| if (groupCallback != null && getParentObject().getParent() instanceof GraphDocument) { |
| final Group group = getParentObject(); |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| groupCallback.started(group); |
| } |
| }); |
| } |
| } |
| }; |
| // <property> |
| private ElementHandler<String, Properties.Provider> propertyHandler = new XMLParser.ElementHandler<String, Properties.Provider>(PROPERTY_ELEMENT, true) { |
| |
| @Override |
| public String start() throws SAXException { |
| return readRequiredAttribute(PROPERTY_NAME_PROPERTY); |
| } |
| |
| @Override |
| public void end(String text) { |
| getParentObject().getProperties().setProperty(getObject(), text.trim()); |
| } |
| }; |
| |
| public Parser(ReadableByteChannel channel) { |
| this(channel, null, null); |
| } |
| |
| public Parser(ReadableByteChannel channel, ParseMonitor monitor, GroupCallback groupCallback) { |
| |
| this.groupCallback = groupCallback; |
| this.monitor = monitor; |
| this.channel = channel; |
| |
| // Initialize dependencies |
| xmlDocument.addChild(topHandler); |
| topHandler.addChild(groupHandler); |
| |
| groupHandler.addChild(methodHandler); |
| groupHandler.addChild(graphHandler); |
| groupHandler.addChild(groupHandler); |
| |
| methodHandler.addChild(inlinedHandler); |
| methodHandler.addChild(bytecodesHandler); |
| |
| inlinedHandler.addChild(inlinedMethodHandler); |
| inlinedMethodHandler.addChild(bytecodesHandler); |
| inlinedMethodHandler.addChild(inlinedHandler); |
| |
| graphHandler.addChild(nodesHandler); |
| graphHandler.addChild(edgesHandler); |
| graphHandler.addChild(controlFlowHandler); |
| |
| controlFlowHandler.addChild(blockHandler); |
| |
| blockHandler.addChild(successorsHandler); |
| successorsHandler.addChild(successorHandler); |
| blockHandler.addChild(blockNodesHandler); |
| blockNodesHandler.addChild(blockNodeHandler); |
| |
| nodesHandler.addChild(nodeHandler); |
| nodesHandler.addChild(removeNodeHandler); |
| edgesHandler.addChild(edgeHandler); |
| edgesHandler.addChild(removeEdgeHandler); |
| |
| methodHandler.addChild(propertiesHandler); |
| inlinedMethodHandler.addChild(propertiesHandler); |
| topHandler.addChild(propertiesHandler); |
| groupHandler.addChild(groupPropertiesHandler); |
| graphHandler.addChild(propertiesHandler); |
| nodeHandler.addChild(propertiesHandler); |
| propertiesHandler.addChild(propertyHandler); |
| groupPropertiesHandler.addChild(propertyHandler); |
| } |
| |
| // Returns a new GraphDocument object deserialized from an XML input source. |
| @Override |
| public GraphDocument parse() throws IOException { |
| if (monitor != null) { |
| monitor.setState("Starting parsing"); |
| } |
| try { |
| XMLReader reader = createReader(); |
| reader.setContentHandler(new XMLParser(xmlDocument, monitor)); |
| reader.parse(new InputSource(Channels.newInputStream(channel))); |
| } catch (SAXException ex) { |
| if (!(ex instanceof SAXParseException) || !"XML document structures must start and end within the same entity.".equals(ex.getMessage())) { |
| throw new IOException(ex); |
| } |
| } |
| if (monitor != null) { |
| monitor.setState("Finished parsing"); |
| } |
| return graphDocument; |
| } |
| |
| private XMLReader createReader() throws SAXException { |
| try { |
| SAXParserFactory pfactory = SAXParserFactory.newInstance(); |
| pfactory.setValidating(true); |
| pfactory.setNamespaceAware(true); |
| |
| // Enable schema validation |
| SchemaFactory sfactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); |
| InputStream stream = Parser.class.getResourceAsStream("graphdocument.xsd"); |
| pfactory.setSchema(sfactory.newSchema(new Source[]{new StreamSource(stream)})); |
| |
| return pfactory.newSAXParser().getXMLReader(); |
| } catch (ParserConfigurationException ex) { |
| throw new SAXException(ex); |
| } |
| } |
| } |