001/* 002 * Copyright (c) 2009, 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.debug; 024 025import java.io.*; 026 027/** 028 * A utility for printing compiler debug and informational output to an output stream. 029 * 030 * A {@link LogStream} instance maintains an internal buffer that is flushed to the underlying 031 * output stream every time one of the {@code println} methods is invoked, or a newline character ( 032 * {@code '\n'}) is written. 033 * 034 * All of the {@code print} and {@code println} methods return the {code LogStream} instance on 035 * which they were invoked. This allows chaining of these calls to mitigate use of String 036 * concatenation by the caller. 037 * 038 * A {@code LogStream} maintains a current {@linkplain #indentationLevel() indentation} level. Each 039 * line of output written to this stream has {@code n} spaces prefixed to it where {@code n} is the 040 * value that would be returned by {@link #indentationLevel()} when the first character of a new 041 * line is written. 042 * 043 * A {@code LogStream} maintains a current {@linkplain #position() position} for the current line 044 * being written. This position can be advanced to a specified position by 045 * {@linkplain #fillTo(int, char) filling} this stream with a given character. 046 */ 047public class LogStream { 048 049 /** 050 * Null output stream that simply swallows any output sent to it. 051 */ 052 public static final LogStream SINK = new LogStream(); 053 054 private static final PrintStream SINK_PS = new PrintStream(new OutputStream() { 055 056 @Override 057 public void write(int b) throws IOException { 058 } 059 }); 060 061 private LogStream() { 062 this.ps = null; 063 this.lineBuffer = null; 064 } 065 066 /** 067 * The output stream to which this log stream writes. 068 */ 069 private final PrintStream ps; 070 071 private final StringBuilder lineBuffer; 072 private int indentationLevel; 073 private char indentation = ' '; 074 private boolean indentationDisabled; 075 076 public final PrintStream out() { 077 if (ps == null) { 078 return SINK_PS; 079 } 080 return ps; 081 } 082 083 /** 084 * The system dependent line separator. 085 */ 086 public static final String LINE_SEPARATOR = System.getProperty("line.separator"); 087 088 /** 089 * Creates a new log stream. 090 * 091 * @param os the underlying output stream to which prints are sent 092 */ 093 public LogStream(OutputStream os) { 094 ps = os instanceof PrintStream ? (PrintStream) os : new PrintStream(os); 095 lineBuffer = new StringBuilder(100); 096 } 097 098 /** 099 * Creates a new log stream that shares the same {@linkplain #ps output stream} as a given 100 * {@link LogStream}. 101 * 102 * @param log a LogStream whose output stream is shared with this one 103 */ 104 public LogStream(LogStream log) { 105 ps = log.ps; 106 lineBuffer = new StringBuilder(100); 107 } 108 109 /** 110 * Prepends {@link #indentation} to the current output line until its write position is equal to 111 * the current {@linkplain #indentationLevel()} level. 112 */ 113 private void indent() { 114 if (ps != null) { 115 if (!indentationDisabled && indentationLevel != 0) { 116 while (lineBuffer.length() < indentationLevel) { 117 lineBuffer.append(indentation); 118 } 119 } 120 } 121 } 122 123 private LogStream flushLine(boolean withNewline) { 124 if (ps != null) { 125 if (withNewline) { 126 lineBuffer.append(LINE_SEPARATOR); 127 } 128 ps.print(lineBuffer.toString()); 129 ps.flush(); 130 lineBuffer.setLength(0); 131 } 132 return this; 133 } 134 135 /** 136 * Flushes the stream. This is done by terminating the current line if it is not at position 0 137 * and then flushing the underlying output stream. 138 */ 139 public void flush() { 140 if (ps != null) { 141 if (lineBuffer.length() != 0) { 142 flushLine(false); 143 } 144 ps.flush(); 145 } 146 } 147 148 /** 149 * Gets the current column position of this log stream. 150 * 151 * @return the current column position of this log stream 152 */ 153 public int position() { 154 return lineBuffer == null ? 0 : lineBuffer.length(); 155 156 } 157 158 /** 159 * Gets the current indentation level for this log stream. 160 * 161 * @return the current indentation level for this log stream. 162 */ 163 public int indentationLevel() { 164 return indentationLevel; 165 } 166 167 /** 168 * Adjusts the current indentation level of this log stream. 169 * 170 * @param delta 171 */ 172 public void adjustIndentation(int delta) { 173 if (delta < 0) { 174 indentationLevel = Math.max(0, indentationLevel + delta); 175 } else { 176 indentationLevel += delta; 177 } 178 } 179 180 /** 181 * Gets the current indentation character of this log stream. 182 */ 183 public char indentation() { 184 return indentation; 185 } 186 187 public void disableIndentation() { 188 indentationDisabled = true; 189 } 190 191 public void enableIndentation() { 192 indentationDisabled = false; 193 } 194 195 /** 196 * Sets the character used for indentation. 197 */ 198 public void setIndentation(char c) { 199 indentation = c; 200 } 201 202 /** 203 * Advances this stream's {@linkplain #position() position} to a given position by repeatedly 204 * appending a given character as necessary. 205 * 206 * @param position the position to which this stream's position will be advanced 207 * @param filler the character used to pad the stream 208 */ 209 public LogStream fillTo(int position, char filler) { 210 if (ps != null) { 211 indent(); 212 while (lineBuffer.length() < position) { 213 lineBuffer.append(filler); 214 } 215 } 216 return this; 217 } 218 219 /** 220 * Writes a boolean value to this stream as {@code "true"} or {@code "false"}. 221 * 222 * @param b the value to be printed 223 * @return this {@link LogStream} instance 224 */ 225 public LogStream print(boolean b) { 226 if (ps != null) { 227 indent(); 228 lineBuffer.append(b); 229 } 230 return this; 231 } 232 233 /** 234 * Writes a boolean value to this stream followed by a {@linkplain #LINE_SEPARATOR line 235 * separator}. 236 * 237 * @param b the value to be printed 238 * @return this {@link LogStream} instance 239 */ 240 public LogStream println(boolean b) { 241 if (ps != null) { 242 indent(); 243 lineBuffer.append(b); 244 return flushLine(true); 245 } 246 return this; 247 } 248 249 /** 250 * Writes a character value to this stream. 251 * 252 * @param c the value to be printed 253 * @return this {@link LogStream} instance 254 */ 255 public LogStream print(char c) { 256 if (ps != null) { 257 indent(); 258 lineBuffer.append(c); 259 if (c == '\n') { 260 if (lineBuffer.indexOf(LINE_SEPARATOR, lineBuffer.length() - LINE_SEPARATOR.length()) != -1) { 261 flushLine(false); 262 } 263 } 264 } 265 return this; 266 } 267 268 /** 269 * Writes a character value to this stream followed by a {@linkplain #LINE_SEPARATOR line 270 * separator}. 271 * 272 * @param c the value to be printed 273 * @return this {@link LogStream} instance 274 */ 275 public LogStream println(char c) { 276 if (ps != null) { 277 indent(); 278 lineBuffer.append(c); 279 flushLine(true); 280 } 281 return this; 282 } 283 284 /** 285 * Prints an int value. 286 * 287 * @param i the value to be printed 288 * @return this {@link LogStream} instance 289 */ 290 public LogStream print(int i) { 291 if (ps != null) { 292 indent(); 293 lineBuffer.append(i); 294 } 295 return this; 296 } 297 298 /** 299 * Writes an int value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator}. 300 * 301 * @param i the value to be printed 302 * @return this {@link LogStream} instance 303 */ 304 public LogStream println(int i) { 305 if (ps != null) { 306 indent(); 307 lineBuffer.append(i); 308 return flushLine(true); 309 } 310 return this; 311 } 312 313 /** 314 * Writes a float value to this stream. 315 * 316 * @param f the value to be printed 317 * @return this {@link LogStream} instance 318 */ 319 public LogStream print(float f) { 320 if (ps != null) { 321 indent(); 322 lineBuffer.append(f); 323 } 324 return this; 325 } 326 327 /** 328 * Writes a float value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator} 329 * . 330 * 331 * @param f the value to be printed 332 * @return this {@link LogStream} instance 333 */ 334 public LogStream println(float f) { 335 if (ps != null) { 336 indent(); 337 lineBuffer.append(f); 338 return flushLine(true); 339 } 340 return this; 341 } 342 343 /** 344 * Writes a long value to this stream. 345 * 346 * @param l the value to be printed 347 * @return this {@link LogStream} instance 348 */ 349 public LogStream print(long l) { 350 if (ps != null) { 351 indent(); 352 lineBuffer.append(l); 353 } 354 return this; 355 } 356 357 /** 358 * Writes a long value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator}. 359 * 360 * @param l the value to be printed 361 * @return this {@link LogStream} instance 362 */ 363 public LogStream println(long l) { 364 if (ps != null) { 365 indent(); 366 lineBuffer.append(l); 367 return flushLine(true); 368 } 369 return this; 370 } 371 372 /** 373 * Writes a double value to this stream. 374 * 375 * @param d the value to be printed 376 * @return this {@link LogStream} instance 377 */ 378 public LogStream print(double d) { 379 if (ps != null) { 380 indent(); 381 lineBuffer.append(d); 382 } 383 return this; 384 } 385 386 /** 387 * Writes a double value to this stream followed by a {@linkplain #LINE_SEPARATOR line 388 * separator}. 389 * 390 * @param d the value to be printed 391 * @return this {@link LogStream} instance 392 */ 393 public LogStream println(double d) { 394 if (ps != null) { 395 indent(); 396 lineBuffer.append(d); 397 return flushLine(true); 398 } 399 return this; 400 } 401 402 /** 403 * Writes a {@code String} value to this stream. This method ensures that the 404 * {@linkplain #position() position} of this stream is updated correctly with respect to any 405 * {@linkplain #LINE_SEPARATOR line separators} present in {@code s}. 406 * 407 * @param s the value to be printed 408 * @return this {@link LogStream} instance 409 */ 410 public LogStream print(String s) { 411 if (ps != null) { 412 if (s == null) { 413 indent(); 414 lineBuffer.append(s); 415 return this; 416 } 417 418 int index = 0; 419 int next = s.indexOf(LINE_SEPARATOR, index); 420 while (index < s.length()) { 421 indent(); 422 if (next > index) { 423 lineBuffer.append(s.substring(index, next)); 424 flushLine(true); 425 index = next + LINE_SEPARATOR.length(); 426 next = s.indexOf(LINE_SEPARATOR, index); 427 } else { 428 lineBuffer.append(s.substring(index)); 429 break; 430 } 431 } 432 } 433 return this; 434 } 435 436 /** 437 * Writes a {@code String} value to this stream followed by a {@linkplain #LINE_SEPARATOR line 438 * separator}. 439 * 440 * @param s the value to be printed 441 * @return this {@link LogStream} instance 442 */ 443 public LogStream println(String s) { 444 if (ps != null) { 445 print(s); 446 flushLine(true); 447 } 448 return this; 449 } 450 451 /** 452 * Writes a formatted string to this stream. 453 * 454 * @param format a format string as described in {@link String#format(String, Object...)} 455 * @param args the arguments to be formatted 456 * @return this {@link LogStream} instance 457 */ 458 public LogStream printf(String format, Object... args) { 459 if (ps != null) { 460 print(String.format(format, args)); 461 } 462 return this; 463 } 464 465 /** 466 * Writes a {@linkplain #LINE_SEPARATOR line separator} to this stream. 467 * 468 * @return this {@code LogStream} instance 469 */ 470 public LogStream println() { 471 if (ps != null) { 472 indent(); 473 flushLine(true); 474 } 475 return this; 476 } 477}