/* * Copyright (c) 2011, 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 org.graalvm.compiler.printer; import java.io.BufferedOutputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Map; import java.util.Map.Entry; /** * Elementary, generic generator of Ideal Graph Visualizer input for use in printers for specific * data structures. */ class BasicIdealGraphPrinter { /** * Edge between two nodes. */ protected static class Edge { final String from; final int fromIndex; final String to; final int toIndex; final String label; public Edge(String from, int fromIndex, String to, int toIndex, String label) { assert (from != null && to != null); this.from = from; this.fromIndex = fromIndex; this.to = to; this.toIndex = toIndex; this.label = label; } @Override public int hashCode() { int h = from.hashCode() ^ to.hashCode(); h = 3 * h + fromIndex; h = 5 * h + toIndex; if (label != null) { h ^= label.hashCode(); } return h; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof Edge) { Edge other = (Edge) obj; return from.equals(other.from) && fromIndex == other.fromIndex && to.equals(other.to) && toIndex == other.toIndex && (label == other.label || (label != null && label.equals(other.label))); } return false; } } private final PrintStream stream; /** * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream. */ protected BasicIdealGraphPrinter(OutputStream stream) { try { OutputStream buffered; if (stream instanceof BufferedOutputStream) { buffered = stream; } else { buffered = new BufferedOutputStream(stream, 256 * 1024); } this.stream = new PrintStream(buffered, false, Charset.defaultCharset().name()); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Flushes any buffered output. */ protected void flush() { stream.flush(); } /** * Starts a new graph document. */ protected void begin() { stream.println(""); } protected void beginGroup() { stream.println(""); } protected void beginMethod(String name, String shortName, int bci) { stream.printf(" %n", escape(name), escape(shortName), bci); } protected void beginBytecodes() { stream.println(" \n"); } protected void printBytecodes(String disassembly) { beginBytecodes(); stream.println(disassembly); endBytecodes(); } protected void endMethod() { stream.println(" "); } protected void beginGraph(String title) { stream.printf(" %n", escape(title)); } protected void beginProperties() { stream.print(""); } protected void printProperty(String name, String value) { stream.printf("

%s

", escape(name), escape(value)); } protected void endProperties() { stream.print("
"); } protected void printProperties(Map properties) { beginProperties(); for (Entry entry : properties.entrySet()) { printProperty(entry.getKey(), entry.getValue()); } endProperties(); } protected void beginNodes() { stream.println(" "); } protected void beginNode(String id) { stream.printf(" ", escape(id)); } protected void endNode() { stream.println(" "); } protected void printNode(String id, Map properties) { beginNode(id); if (properties != null) { printProperties(properties); } endNode(); } protected void endNodes() { stream.println(" "); } protected void beginEdges() { stream.println(" "); } protected void printEdge(Edge edge) { stream.printf(" %n", escape(edge.from), edge.fromIndex, escape(edge.to), edge.toIndex, escape(edge.label)); } protected void endEdges() { stream.println(" "); } protected void beginControlFlow() { stream.println(" "); } protected void beginBlock(String name) { stream.printf(" %n", escape(name)); } protected void beginSuccessors() { stream.println(" "); } protected void printSuccessor(String name) { stream.printf(" %n", escape(name)); } protected void endSuccessors() { stream.println(" "); } protected void beginBlockNodes() { stream.println(" "); } protected void printBlockNode(String nodeId) { stream.printf(" %n", escape(nodeId)); } protected void endBlockNodes() { stream.println(" "); } protected void endBlock() { stream.println(" "); } protected void endControlFlow() { stream.println(" "); } protected void endGraph() { stream.println("
"); } /** * Ends the current group. */ public void endGroup() { stream.println("
"); } /** * Finishes the graph document and flushes the output stream. */ protected void end() { stream.println("
"); flush(); } public void close() { end(); stream.close(); } public boolean isValid() { return !stream.checkError(); } private static String escape(String s) { StringBuilder str = null; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '&': case '<': case '>': case '"': case '\'': if (str == null) { str = new StringBuilder(); str.append(s, 0, i); } switch (c) { case '&': str.append("&"); break; case '<': str.append("<"); break; case '>': str.append(">"); break; case '"': str.append("""); break; case '\'': str.append("'"); break; default: assert false; } break; case '\u0000': case '\u0001': case '\u0002': case '\u0003': case '\u0004': case '\u0005': case '\u0006': case '\u0007': case '\u0008': case '\u000b': case '\u000c': case '\u000e': case '\u000f': case '\u0010': case '\u0011': case '\u0012': case '\u0013': case '\u0014': case '\u0015': case '\u0016': case '\u0017': case '\u0018': case '\u0019': case '\u001a': case '\u001b': case '\u001c': case '\u001d': case '\u001e': case '\u001f': if (str == null) { str = new StringBuilder(); str.append(s, 0, i); } str.append("'0x").append(Integer.toHexString(c)); break; default: if (str != null) { str.append(c); } break; } } if (str == null) { return s; } else { return str.toString(); } } }