001/* 002 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.printer; 024 025import java.io.*; 026import java.nio.charset.*; 027import java.util.*; 028import java.util.Map.Entry; 029 030/** 031 * Elementary, generic generator of Ideal Graph Visualizer input for use in printers for specific 032 * data structures. 033 */ 034class BasicIdealGraphPrinter { 035 036 /** 037 * Edge between two nodes. 038 */ 039 protected static class Edge { 040 041 final String from; 042 final int fromIndex; 043 final String to; 044 final int toIndex; 045 final String label; 046 047 public Edge(String from, int fromIndex, String to, int toIndex, String label) { 048 assert (from != null && to != null); 049 this.from = from; 050 this.fromIndex = fromIndex; 051 this.to = to; 052 this.toIndex = toIndex; 053 this.label = label; 054 } 055 056 @Override 057 public int hashCode() { 058 int h = from.hashCode() ^ to.hashCode(); 059 h = 3 * h + fromIndex; 060 h = 5 * h + toIndex; 061 if (label != null) { 062 h ^= label.hashCode(); 063 } 064 return h; 065 } 066 067 @Override 068 public boolean equals(Object obj) { 069 if (obj == this) { 070 return true; 071 } 072 if (obj instanceof Edge) { 073 Edge other = (Edge) obj; 074 return from.equals(other.from) && fromIndex == other.fromIndex && to.equals(other.to) && toIndex == other.toIndex && 075 (label == other.label || (label != null && label.equals(other.label))); 076 } 077 return false; 078 } 079 } 080 081 private final PrintStream stream; 082 083 /** 084 * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream. 085 */ 086 protected BasicIdealGraphPrinter(OutputStream stream) { 087 try { 088 OutputStream buffered; 089 if (stream instanceof BufferedOutputStream) { 090 buffered = stream; 091 } else { 092 buffered = new BufferedOutputStream(stream, 256 * 1024); 093 } 094 this.stream = new PrintStream(buffered, false, Charset.defaultCharset().name()); 095 } catch (UnsupportedEncodingException e) { 096 throw new RuntimeException(e); 097 } 098 } 099 100 /** 101 * Flushes any buffered output. 102 */ 103 protected void flush() { 104 stream.flush(); 105 } 106 107 /** 108 * Starts a new graph document. 109 */ 110 protected void begin() { 111 stream.println("<graphDocument>"); 112 } 113 114 protected void beginGroup() { 115 stream.println("<group>"); 116 } 117 118 protected void beginMethod(String name, String shortName, int bci) { 119 stream.printf(" <method name='%s' shortName='%s' bci='%d'>%n", escape(name), escape(shortName), bci); 120 } 121 122 protected void beginBytecodes() { 123 stream.println(" <bytecodes>\n<![CDATA["); 124 } 125 126 protected void printBytecode(int bci, String mnemonic, int[] extra) { 127 stream.print(bci); 128 stream.print(' '); 129 stream.print(mnemonic); 130 if (extra != null) { 131 for (int b : extra) { 132 stream.print(' '); 133 stream.print(b); 134 } 135 } 136 stream.println(); 137 } 138 139 protected void endBytecodes() { 140 stream.println(" ]]></bytecodes>"); 141 } 142 143 protected void printBytecodes(String disassembly) { 144 beginBytecodes(); 145 stream.println(disassembly); 146 endBytecodes(); 147 } 148 149 protected void endMethod() { 150 stream.println(" </method>"); 151 } 152 153 protected void beginGraph(String title) { 154 stream.printf(" <graph name='%s'>%n", escape(title)); 155 } 156 157 protected void beginProperties() { 158 stream.print("<properties>"); 159 } 160 161 protected void printProperty(String name, String value) { 162 stream.printf("<p name='%s'>%s</p>", escape(name), escape(value)); 163 } 164 165 protected void endProperties() { 166 stream.print("</properties>"); 167 } 168 169 protected void printProperties(Map<String, String> properties) { 170 beginProperties(); 171 for (Entry<String, String> entry : properties.entrySet()) { 172 printProperty(entry.getKey(), entry.getValue()); 173 } 174 endProperties(); 175 } 176 177 protected void beginNodes() { 178 stream.println(" <nodes>"); 179 } 180 181 protected void beginNode(String id) { 182 stream.printf(" <node id='%s'>", escape(id)); 183 } 184 185 protected void endNode() { 186 stream.println(" </node>"); 187 } 188 189 protected void printNode(String id, Map<String, String> properties) { 190 beginNode(id); 191 if (properties != null) { 192 printProperties(properties); 193 } 194 endNode(); 195 } 196 197 protected void endNodes() { 198 stream.println(" </nodes>"); 199 } 200 201 protected void beginEdges() { 202 stream.println(" <edges>"); 203 } 204 205 protected void printEdge(Edge edge) { 206 stream.printf(" <edge from='%s' fromIndex='%d' to='%s' toIndex='%d' label='%s' />%n", escape(edge.from), edge.fromIndex, escape(edge.to), edge.toIndex, escape(edge.label)); 207 } 208 209 protected void endEdges() { 210 stream.println(" </edges>"); 211 } 212 213 protected void beginControlFlow() { 214 stream.println(" <controlFlow>"); 215 } 216 217 protected void beginBlock(String name) { 218 stream.printf(" <block name='%s'>%n", escape(name)); 219 } 220 221 protected void beginSuccessors() { 222 stream.println(" <successors>"); 223 } 224 225 protected void printSuccessor(String name) { 226 stream.printf(" <successor name='%s'/>%n", escape(name)); 227 } 228 229 protected void endSuccessors() { 230 stream.println(" </successors>"); 231 } 232 233 protected void beginBlockNodes() { 234 stream.println(" <nodes>"); 235 } 236 237 protected void printBlockNode(String nodeId) { 238 stream.printf(" <node id='%s'/>%n", escape(nodeId)); 239 } 240 241 protected void endBlockNodes() { 242 stream.println(" </nodes>"); 243 } 244 245 protected void endBlock() { 246 stream.println(" </block>"); 247 } 248 249 protected void endControlFlow() { 250 stream.println(" </controlFlow>"); 251 } 252 253 protected void endGraph() { 254 stream.println(" </graph>"); 255 } 256 257 /** 258 * Ends the current group. 259 */ 260 public void endGroup() { 261 stream.println("</group>"); 262 } 263 264 /** 265 * Finishes the graph document and flushes the output stream. 266 */ 267 protected void end() { 268 stream.println("</graphDocument>"); 269 flush(); 270 } 271 272 public void close() { 273 end(); 274 stream.close(); 275 } 276 277 public boolean isValid() { 278 return !stream.checkError(); 279 } 280 281 private static String escape(String s) { 282 StringBuilder str = null; 283 for (int i = 0; i < s.length(); i++) { 284 char c = s.charAt(i); 285 switch (c) { 286 case '&': 287 case '<': 288 case '>': 289 case '"': 290 case '\'': 291 if (str == null) { 292 str = new StringBuilder(); 293 str.append(s, 0, i); 294 } 295 switch (c) { 296 case '&': 297 str.append("&"); 298 break; 299 case '<': 300 str.append("<"); 301 break; 302 case '>': 303 str.append(">"); 304 break; 305 case '"': 306 str.append("""); 307 break; 308 case '\'': 309 str.append("'"); 310 break; 311 default: 312 assert false; 313 } 314 break; 315 case '\u0000': 316 case '\u0001': 317 case '\u0002': 318 case '\u0003': 319 case '\u0004': 320 case '\u0005': 321 case '\u0006': 322 case '\u0007': 323 case '\u0008': 324 case '\u000b': 325 case '\u000c': 326 case '\u000e': 327 case '\u000f': 328 case '\u0010': 329 case '\u0011': 330 case '\u0012': 331 case '\u0013': 332 case '\u0014': 333 case '\u0015': 334 case '\u0016': 335 case '\u0017': 336 case '\u0018': 337 case '\u0019': 338 case '\u001a': 339 case '\u001b': 340 case '\u001c': 341 case '\u001d': 342 case '\u001e': 343 case '\u001f': 344 if (str == null) { 345 str = new StringBuilder(); 346 str.append(s, 0, i); 347 } 348 str.append("'0x").append(Integer.toHexString(c)); 349 break; 350 default: 351 if (str != null) { 352 str.append(c); 353 } 354 break; 355 } 356 } 357 if (str == null) { 358 return s; 359 } else { 360 return str.toString(); 361 } 362 } 363}