blob: afd470ae0789a276453b17575ea4513d6246eece [file] [log] [blame]
/*
* 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);
}
}
}