1/*
2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24package com.sun.hotspot.igv.data.serialization;
25
26import com.sun.hotspot.igv.data.*;
27import com.sun.hotspot.igv.data.serialization.XMLParser.ElementHandler;
28import com.sun.hotspot.igv.data.serialization.XMLParser.HandoverElementHandler;
29import com.sun.hotspot.igv.data.serialization.XMLParser.TopElementHandler;
30import com.sun.hotspot.igv.data.services.GroupCallback;
31import java.io.IOException;
32import java.io.InputStream;
33import java.nio.channels.Channels;
34import java.nio.channels.ReadableByteChannel;
35import java.util.ArrayList;
36import java.util.HashMap;
37import java.util.Map;
38import javax.swing.SwingUtilities;
39import javax.xml.parsers.ParserConfigurationException;
40import javax.xml.parsers.SAXParserFactory;
41import javax.xml.transform.Source;
42import javax.xml.transform.stream.StreamSource;
43import javax.xml.validation.SchemaFactory;
44import org.xml.sax.InputSource;
45import org.xml.sax.SAXException;
46import org.xml.sax.SAXParseException;
47import org.xml.sax.XMLReader;
48
49/**
50 *
51 * @author Thomas Wuerthinger
52 */
53public class Parser implements GraphParser {
54
55    public static final String INDENT = "  ";
56    public static final String TOP_ELEMENT = "graphDocument";
57    public static final String GROUP_ELEMENT = "group";
58    public static final String GRAPH_ELEMENT = "graph";
59    public static final String ROOT_ELEMENT = "graphDocument";
60    public static final String PROPERTIES_ELEMENT = "properties";
61    public static final String EDGES_ELEMENT = "edges";
62    public static final String PROPERTY_ELEMENT = "p";
63    public static final String EDGE_ELEMENT = "edge";
64    public static final String NODE_ELEMENT = "node";
65    public static final String NODES_ELEMENT = "nodes";
66    public static final String REMOVE_EDGE_ELEMENT = "removeEdge";
67    public static final String REMOVE_NODE_ELEMENT = "removeNode";
68    public static final String METHOD_NAME_PROPERTY = "name";
69    public static final String GROUP_NAME_PROPERTY = "name";
70    public static final String METHOD_IS_PUBLIC_PROPERTY = "public";
71    public static final String METHOD_IS_STATIC_PROPERTY = "static";
72    public static final String TRUE_VALUE = "true";
73    public static final String NODE_NAME_PROPERTY = "name";
74    public static final String EDGE_NAME_PROPERTY = "name";
75    public static final String NODE_ID_PROPERTY = "id";
76    public static final String FROM_PROPERTY = "from";
77    public static final String TO_PROPERTY = "to";
78    public static final String TYPE_PROPERTY = "type";
79    public static final String PROPERTY_NAME_PROPERTY = "name";
80    public static final String GRAPH_NAME_PROPERTY = "name";
81    public static final String FROM_INDEX_PROPERTY = "fromIndex";
82    public static final String TO_INDEX_PROPERTY = "toIndex";
83    public static final String TO_INDEX_ALT_PROPERTY = "index";
84    public static final String LABEL_PROPERTY = "label";
85    public static final String METHOD_ELEMENT = "method";
86    public static final String INLINE_ELEMENT = "inline";
87    public static final String BYTECODES_ELEMENT = "bytecodes";
88    public static final String METHOD_BCI_PROPERTY = "bci";
89    public static final String METHOD_SHORT_NAME_PROPERTY = "shortName";
90    public static final String CONTROL_FLOW_ELEMENT = "controlFlow";
91    public static final String BLOCK_NAME_PROPERTY = "name";
92    public static final String BLOCK_ELEMENT = "block";
93    public static final String SUCCESSORS_ELEMENT = "successors";
94    public static final String SUCCESSOR_ELEMENT = "successor";
95    public static final String ASSEMBLY_ELEMENT = "assembly";
96    public static final String DIFFERENCE_PROPERTY = "difference";
97    private TopElementHandler<GraphDocument> xmlDocument = new TopElementHandler<>();
98    private Map<Group, Boolean> differenceEncoding = new HashMap<>();
99    private Map<Group, InputGraph> lastParsedGraph = new HashMap<>();
100    private GroupCallback groupCallback;
101    private HashMap<String, Integer> idCache = new HashMap<>();
102    private ArrayList<Pair<String, String>> blockConnections = new ArrayList<>();
103    private int maxId = 0;
104    private GraphDocument graphDocument;
105    private final ParseMonitor monitor;
106    private final ReadableByteChannel channel;
107
108    private int lookupID(String i) {
109        try {
110            return Integer.parseInt(i);
111        } catch (NumberFormatException nfe) {
112            // ignore
113        }
114        Integer id = idCache.get(i);
115        if (id == null) {
116            id = maxId++;
117            idCache.put(i, id);
118        }
119        return id.intValue();
120    }
121
122    // <graphDocument>
123    private ElementHandler<GraphDocument, Object> topHandler = new ElementHandler<GraphDocument, Object>(TOP_ELEMENT) {
124
125        @Override
126        protected GraphDocument start() throws SAXException {
127            graphDocument = new GraphDocument();
128            return graphDocument;
129        }
130    };
131    // <group>
132    private ElementHandler<Group, Folder> groupHandler = new XMLParser.ElementHandler<Group, Folder>(GROUP_ELEMENT) {
133
134        @Override
135        protected Group start() throws SAXException {
136            final Group group = new Group(this.getParentObject());
137
138            String differenceProperty = this.readAttribute(DIFFERENCE_PROPERTY);
139            Parser.this.differenceEncoding.put(group, (differenceProperty != null && (differenceProperty.equals("1") || differenceProperty.equals("true"))));
140
141            ParseMonitor monitor = getMonitor();
142            if (monitor != null) {
143                monitor.setState(group.getName());
144            }
145
146            final Folder parent = getParentObject();
147            if (groupCallback == null || parent instanceof Group) {
148                SwingUtilities.invokeLater(new Runnable(){
149                    @Override
150                    public void run() {
151                        parent.addElement(group);
152                    }
153                });
154            }
155
156            return group;
157        }
158
159        @Override
160        protected void end(String text) throws SAXException {
161        }
162    };
163    // <method>
164    private ElementHandler<InputMethod, Group> methodHandler = new XMLParser.ElementHandler<InputMethod, Group>(METHOD_ELEMENT) {
165
166        @Override
167        protected InputMethod start() throws SAXException {
168
169            InputMethod method = parseMethod(this, getParentObject());
170            getParentObject().setMethod(method);
171            return method;
172        }
173    };
174
175    private InputMethod parseMethod(XMLParser.ElementHandler<?,?> handler, Group group) throws SAXException {
176        String s = handler.readRequiredAttribute(METHOD_BCI_PROPERTY);
177        int bci = 0;
178        try {
179            bci = Integer.parseInt(s);
180        } catch (NumberFormatException e) {
181            throw new SAXException(e);
182        }
183        InputMethod method = new InputMethod(group, handler.readRequiredAttribute(METHOD_NAME_PROPERTY), handler.readRequiredAttribute(METHOD_SHORT_NAME_PROPERTY), bci);
184        return method;
185    }
186    // <bytecodes>
187    private HandoverElementHandler<InputMethod> bytecodesHandler = new XMLParser.HandoverElementHandler<InputMethod>(BYTECODES_ELEMENT, true) {
188
189        @Override
190        protected void end(String text) throws SAXException {
191            getParentObject().setBytecodes(text);
192        }
193    };
194    // <inlined>
195    private HandoverElementHandler<InputMethod> inlinedHandler = new XMLParser.HandoverElementHandler<>(INLINE_ELEMENT);
196    // <inlined><method>
197    private ElementHandler<InputMethod, InputMethod> inlinedMethodHandler = new XMLParser.ElementHandler<InputMethod, InputMethod>(METHOD_ELEMENT) {
198
199        @Override
200        protected InputMethod start() throws SAXException {
201            InputMethod method = parseMethod(this, getParentObject().getGroup());
202            getParentObject().addInlined(method);
203            return method;
204        }
205    };
206    // <graph>
207    private ElementHandler<InputGraph, Group> graphHandler = new XMLParser.ElementHandler<InputGraph, Group>(GRAPH_ELEMENT) {
208
209        @Override
210        protected InputGraph start() throws SAXException {
211            String name = readAttribute(GRAPH_NAME_PROPERTY);
212            InputGraph curGraph = new InputGraph(name);
213            if (differenceEncoding.get(getParentObject())) {
214                InputGraph previous = lastParsedGraph.get(getParentObject());
215                lastParsedGraph.put(getParentObject(), curGraph);
216                if (previous != null) {
217                    for (InputNode n : previous.getNodes()) {
218                        curGraph.addNode(n);
219                    }
220                    for (InputEdge e : previous.getEdges()) {
221                        curGraph.addEdge(e);
222                    }
223                }
224            }
225            ParseMonitor monitor = getMonitor();
226            if (monitor != null) {
227                monitor.updateProgress();
228            }
229            return curGraph;
230        }
231
232        @Override
233        protected void end(String text) throws SAXException {
234            // NOTE: Some graphs intentionally don't provide blocks. Instead
235            //       they later generate the blocks from other information such
236            //       as node properties (example: ServerCompilerScheduler).
237            //       Thus, we shouldn't assign nodes that don't belong to any
238            //       block to some artificial block below unless blocks are
239            //       defined and nodes are assigned to them.
240
241            final InputGraph graph = getObject();
242            final Group parent = getParentObject();
243            if (graph.getBlocks().size() > 0) {
244                boolean blocksContainNodes = false;
245                for (InputBlock b : graph.getBlocks()) {
246                    if (b.getNodes().size() > 0) {
247                        blocksContainNodes = true;
248                        break;
249                    }
250                }
251
252                if (!blocksContainNodes) {
253                    graph.clearBlocks();
254                    blockConnections.clear();
255                } else {
256                    // Blocks and their nodes defined: add other nodes to an
257                    //  artificial "no block" block
258                    InputBlock noBlock = null;
259                    for (InputNode n : graph.getNodes()) {
260                        if (graph.getBlock(n) == null) {
261                            if (noBlock == null) {
262                                noBlock = graph.addBlock("(no block)");
263                            }
264
265                            noBlock.addNode(n.getId());
266                        }
267
268                        assert graph.getBlock(n) != null;
269                    }
270                }
271            }
272
273            // Resolve block successors
274            for (Pair<String, String> p : blockConnections) {
275                final InputBlock left = graph.getBlock(p.getLeft());
276                assert left != null;
277                final InputBlock right = graph.getBlock(p.getRight());
278                assert right != null;
279                graph.addBlockEdge(left, right);
280            }
281            blockConnections.clear();
282
283            SwingUtilities.invokeLater(new Runnable(){
284
285                @Override
286                public void run() {
287                    // Add to group
288                    parent.addElement(graph);
289                }
290            });
291        }
292    };
293    // <nodes>
294    private HandoverElementHandler<InputGraph> nodesHandler = new HandoverElementHandler<>(NODES_ELEMENT);
295    // <controlFlow>
296    private HandoverElementHandler<InputGraph> controlFlowHandler = new HandoverElementHandler<>(CONTROL_FLOW_ELEMENT);
297    // <block>
298    private ElementHandler<InputBlock, InputGraph> blockHandler = new ElementHandler<InputBlock, InputGraph>(BLOCK_ELEMENT) {
299
300        @Override
301        protected InputBlock start() throws SAXException {
302            InputGraph graph = getParentObject();
303            String name = readRequiredAttribute(BLOCK_NAME_PROPERTY);
304            InputBlock b = graph.addBlock(name);
305            for (InputNode n : b.getNodes()) {
306                assert graph.getBlock(n).equals(b);
307            }
308            return b;
309        }
310    };
311    // <nodes>
312    private HandoverElementHandler<InputBlock> blockNodesHandler = new HandoverElementHandler<>(NODES_ELEMENT);
313    // <node>
314    private ElementHandler<InputBlock, InputBlock> blockNodeHandler = new ElementHandler<InputBlock, InputBlock>(NODE_ELEMENT) {
315
316        @Override
317        protected InputBlock start() throws SAXException {
318            String s = readRequiredAttribute(NODE_ID_PROPERTY);
319
320            int id = 0;
321            try {
322                id = lookupID(s);
323            } catch (NumberFormatException e) {
324                throw new SAXException(e);
325            }
326            getParentObject().addNode(id);
327            return getParentObject();
328        }
329    };
330    // <successors>
331    private HandoverElementHandler<InputBlock> successorsHandler = new HandoverElementHandler<>(SUCCESSORS_ELEMENT);
332    // <successor>
333    private ElementHandler<InputBlock, InputBlock> successorHandler = new ElementHandler<InputBlock, InputBlock>(SUCCESSOR_ELEMENT) {
334
335        @Override
336        protected InputBlock start() throws SAXException {
337            String name = readRequiredAttribute(BLOCK_NAME_PROPERTY);
338            blockConnections.add(new Pair<>(getParentObject().getName(), name));
339            return getParentObject();
340        }
341    };
342    // <node>
343    private ElementHandler<InputNode, InputGraph> nodeHandler = new ElementHandler<InputNode, InputGraph>(NODE_ELEMENT) {
344
345        @Override
346        protected InputNode start() throws SAXException {
347            String s = readRequiredAttribute(NODE_ID_PROPERTY);
348            int id = 0;
349            try {
350                id = lookupID(s);
351            } catch (NumberFormatException e) {
352                throw new SAXException(e);
353            }
354            InputNode node = new InputNode(id);
355            getParentObject().addNode(node);
356            return node;
357        }
358    };
359    // <removeNode>
360    private ElementHandler<InputNode, InputGraph> removeNodeHandler = new ElementHandler<InputNode, InputGraph>(REMOVE_NODE_ELEMENT) {
361
362        @Override
363        protected InputNode start() throws SAXException {
364            String s = readRequiredAttribute(NODE_ID_PROPERTY);
365            int id = 0;
366            try {
367                id = lookupID(s);
368            } catch (NumberFormatException e) {
369                throw new SAXException(e);
370            }
371            return getParentObject().removeNode(id);
372        }
373    };
374    // <graph>
375    private HandoverElementHandler<InputGraph> edgesHandler = new HandoverElementHandler<>(EDGES_ELEMENT);
376
377    // Local class for edge elements
378    private class EdgeElementHandler extends ElementHandler<InputEdge, InputGraph> {
379
380        public EdgeElementHandler(String name) {
381            super(name);
382        }
383
384        @Override
385        protected InputEdge start() throws SAXException {
386            int fromIndex = 0;
387            int toIndex = 0;
388            int from = -1;
389            int to = -1;
390            String label = null;
391            String type = null;
392
393            try {
394                String fromIndexString = readAttribute(FROM_INDEX_PROPERTY);
395                if (fromIndexString != null) {
396                    fromIndex = Integer.parseInt(fromIndexString);
397                }
398
399                String toIndexString = readAttribute(TO_INDEX_PROPERTY);
400                if (toIndexString == null) {
401                    toIndexString = readAttribute(TO_INDEX_ALT_PROPERTY);
402                }
403                if (toIndexString != null) {
404                    toIndex = Integer.parseInt(toIndexString);
405                }
406
407                label = readAttribute(LABEL_PROPERTY);
408                type = readAttribute(TYPE_PROPERTY);
409
410                from = lookupID(readRequiredAttribute(FROM_PROPERTY));
411                to = lookupID(readRequiredAttribute(TO_PROPERTY));
412            } catch (NumberFormatException e) {
413                throw new SAXException(e);
414            }
415
416            InputEdge conn = new InputEdge((char) fromIndex, (char) toIndex, from, to, label, type == null ? "" : type);
417            return start(conn);
418        }
419
420        protected InputEdge start(InputEdge conn) throws SAXException {
421            return conn;
422        }
423    }
424    // <edge>
425    private EdgeElementHandler edgeHandler = new EdgeElementHandler(EDGE_ELEMENT) {
426
427        @Override
428        protected InputEdge start(InputEdge conn) throws SAXException {
429            getParentObject().addEdge(conn);
430            return conn;
431        }
432    };
433    // <removeEdge>
434    private EdgeElementHandler removeEdgeHandler = new EdgeElementHandler(REMOVE_EDGE_ELEMENT) {
435
436        @Override
437        protected InputEdge start(InputEdge conn) throws SAXException {
438            getParentObject().removeEdge(conn);
439            return conn;
440        }
441    };
442    // <properties>
443    private HandoverElementHandler<Properties.Provider> propertiesHandler = new HandoverElementHandler<>(PROPERTIES_ELEMENT);
444    // <properties>
445    private HandoverElementHandler<Group> groupPropertiesHandler = new HandoverElementHandler<Group>(PROPERTIES_ELEMENT) {
446
447        @Override
448        public void end(String text) throws SAXException {
449            if (groupCallback != null && getParentObject().getParent() instanceof GraphDocument) {
450                final Group group = getParentObject();
451                SwingUtilities.invokeLater(new Runnable() {
452                    @Override
453                    public void run() {
454                        groupCallback.started(group);
455                    }
456                });
457            }
458        }
459    };
460    // <property>
461    private ElementHandler<String, Properties.Provider> propertyHandler = new XMLParser.ElementHandler<String, Properties.Provider>(PROPERTY_ELEMENT, true) {
462
463        @Override
464        public String start() throws SAXException {
465            return readRequiredAttribute(PROPERTY_NAME_PROPERTY);
466         }
467
468        @Override
469        public void end(String text) {
470            getParentObject().getProperties().setProperty(getObject(), text.trim());
471        }
472    };
473
474    public Parser(ReadableByteChannel channel) {
475        this(channel, null, null);
476    }
477
478    public Parser(ReadableByteChannel channel, ParseMonitor monitor, GroupCallback groupCallback) {
479
480        this.groupCallback = groupCallback;
481        this.monitor = monitor;
482        this.channel = channel;
483
484        // Initialize dependencies
485        xmlDocument.addChild(topHandler);
486        topHandler.addChild(groupHandler);
487
488        groupHandler.addChild(methodHandler);
489        groupHandler.addChild(graphHandler);
490        groupHandler.addChild(groupHandler);
491
492        methodHandler.addChild(inlinedHandler);
493        methodHandler.addChild(bytecodesHandler);
494
495        inlinedHandler.addChild(inlinedMethodHandler);
496        inlinedMethodHandler.addChild(bytecodesHandler);
497        inlinedMethodHandler.addChild(inlinedHandler);
498
499        graphHandler.addChild(nodesHandler);
500        graphHandler.addChild(edgesHandler);
501        graphHandler.addChild(controlFlowHandler);
502
503        controlFlowHandler.addChild(blockHandler);
504
505        blockHandler.addChild(successorsHandler);
506        successorsHandler.addChild(successorHandler);
507        blockHandler.addChild(blockNodesHandler);
508        blockNodesHandler.addChild(blockNodeHandler);
509
510        nodesHandler.addChild(nodeHandler);
511        nodesHandler.addChild(removeNodeHandler);
512        edgesHandler.addChild(edgeHandler);
513        edgesHandler.addChild(removeEdgeHandler);
514
515        methodHandler.addChild(propertiesHandler);
516        inlinedMethodHandler.addChild(propertiesHandler);
517        topHandler.addChild(propertiesHandler);
518        groupHandler.addChild(groupPropertiesHandler);
519        graphHandler.addChild(propertiesHandler);
520        nodeHandler.addChild(propertiesHandler);
521        propertiesHandler.addChild(propertyHandler);
522        groupPropertiesHandler.addChild(propertyHandler);
523    }
524
525    // Returns a new GraphDocument object deserialized from an XML input source.
526    @Override
527    public GraphDocument parse() throws IOException {
528        if (monitor != null) {
529            monitor.setState("Starting parsing");
530        }
531        try {
532            XMLReader reader = createReader();
533            reader.setContentHandler(new XMLParser(xmlDocument, monitor));
534            reader.parse(new InputSource(Channels.newInputStream(channel)));
535        } catch (SAXException ex) {
536            if (!(ex instanceof SAXParseException) || !"XML document structures must start and end within the same entity.".equals(ex.getMessage())) {
537                throw new IOException(ex);
538            }
539        }
540        if (monitor != null) {
541            monitor.setState("Finished parsing");
542        }
543        return graphDocument;
544    }
545
546    private XMLReader createReader() throws SAXException {
547        try {
548            SAXParserFactory pfactory = SAXParserFactory.newInstance();
549            pfactory.setValidating(true);
550            pfactory.setNamespaceAware(true);
551
552            // Enable schema validation
553            SchemaFactory sfactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
554            InputStream stream = Parser.class.getResourceAsStream("graphdocument.xsd");
555            pfactory.setSchema(sfactory.newSchema(new Source[]{new StreamSource(stream)}));
556
557            return pfactory.newSAXParser().getXMLReader();
558        } catch (ParserConfigurationException ex) {
559            throw new SAXException(ex);
560        }
561    }
562}
563