1 /* 2 * Copyright (c) 2009, 2011, 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 package org.graalvm.compiler.debug; 24 25 import java.io.IOException; 26 import java.io.OutputStream; 27 import java.io.PrintStream; 28 29 /** 30 * A utility for printing compiler debug and informational output to an output stream. 31 * 32 * A {@link LogStream} instance maintains an internal buffer that is flushed to the underlying 33 * output stream every time one of the {@code println} methods is invoked, or a newline character ( 34 * {@code '\n'}) is written. 35 * 36 * All of the {@code print} and {@code println} methods return the {code LogStream} instance on 37 * which they were invoked. This allows chaining of these calls to mitigate use of String 38 * concatenation by the caller. 39 * 40 * A {@code LogStream} maintains a current {@linkplain #indentationLevel() indentation} level. Each 41 * line of output written to this stream has {@code n} spaces prefixed to it where {@code n} is the 42 * value that would be returned by {@link #indentationLevel()} when the first character of a new 43 * line is written. 44 * 45 * A {@code LogStream} maintains a current {@linkplain #position() position} for the current line 46 * being written. This position can be advanced to a specified position by 47 * {@linkplain #fillTo(int, char) filling} this stream with a given character. 48 */ 49 public class LogStream { 50 51 /** 52 * Null output stream that simply swallows any output sent to it. 53 */ 54 public static final LogStream SINK = new LogStream(); 55 56 private static final PrintStream SINK_PS = new PrintStream(new OutputStream() { 57 58 @Override 59 public void write(int b) throws IOException { 60 } 61 }); 62 63 private LogStream() { 64 this.ps = null; 65 this.lineBuffer = null; 66 } 67 68 /** 69 * The output stream to which this log stream writes. 70 */ 71 private final PrintStream ps; 72 73 private final StringBuilder lineBuffer; 74 private int indentationLevel; 75 private char indentation = ' '; 76 private boolean indentationDisabled; 77 78 public final PrintStream out() { 79 if (ps == null) { 80 return SINK_PS; 81 } 82 return ps; 83 } 84 85 /** 86 * The system dependent line separator. 87 */ 88 public static final String LINE_SEPARATOR = System.getProperty("line.separator"); 89 90 /** 91 * Creates a new log stream. 92 * 93 * @param os the underlying output stream to which prints are sent 94 */ 95 public LogStream(OutputStream os) { 96 ps = os instanceof PrintStream ? (PrintStream) os : new PrintStream(os); 97 lineBuffer = new StringBuilder(100); 98 } 99 100 /** 101 * Creates a new log stream that shares the same {@linkplain #ps output stream} as a given 102 * {@link LogStream}. 103 * 104 * @param log a LogStream whose output stream is shared with this one 105 */ 106 public LogStream(LogStream log) { 107 ps = log.ps; 108 lineBuffer = new StringBuilder(100); 109 } 110 111 /** 112 * Prepends {@link #indentation} to the current output line until its write position is equal to 113 * the current {@linkplain #indentationLevel()} level. 114 */ 115 private void indent() { 116 if (ps != null) { 117 if (!indentationDisabled && indentationLevel != 0) { 118 while (lineBuffer.length() < indentationLevel) { 119 lineBuffer.append(indentation); 120 } 121 } 122 } 123 } 124 125 private LogStream flushLine(boolean withNewline) { 126 if (ps != null) { 127 if (withNewline) { 128 lineBuffer.append(LINE_SEPARATOR); 129 } 130 ps.print(lineBuffer.toString()); 131 ps.flush(); 132 lineBuffer.setLength(0); 133 } 134 return this; 135 } 136 137 /** 138 * Flushes the stream. This is done by terminating the current line if it is not at position 0 139 * and then flushing the underlying output stream. 140 */ 141 public void flush() { 142 if (ps != null) { 143 if (lineBuffer.length() != 0) { 144 flushLine(false); 145 } 146 ps.flush(); 147 } 148 } 149 150 /** 151 * Gets the current column position of this log stream. 152 * 153 * @return the current column position of this log stream 154 */ 155 public int position() { 156 return lineBuffer == null ? 0 : lineBuffer.length(); 157 158 } 159 160 /** 161 * Gets the current indentation level for this log stream. 162 * 163 * @return the current indentation level for this log stream. 164 */ 165 public int indentationLevel() { 166 return indentationLevel; 167 } 168 169 /** 170 * Adjusts the current indentation level of this log stream. 171 * 172 * @param delta 173 */ 174 public void adjustIndentation(int delta) { 175 if (delta < 0) { 176 indentationLevel = Math.max(0, indentationLevel + delta); 177 } else { 178 indentationLevel += delta; 179 } 180 } 181 182 /** 183 * Gets the current indentation character of this log stream. 184 */ 185 public char indentation() { 186 return indentation; 187 } 188 189 public void disableIndentation() { 190 indentationDisabled = true; 191 } 192 193 public void enableIndentation() { 194 indentationDisabled = false; 195 } 196 197 /** 198 * Sets the character used for indentation. 199 */ 200 public void setIndentation(char c) { 201 indentation = c; 202 } 203 204 /** 205 * Advances this stream's {@linkplain #position() position} to a given position by repeatedly 206 * appending a given character as necessary. 207 * 208 * @param position the position to which this stream's position will be advanced 209 * @param filler the character used to pad the stream 210 */ 211 public LogStream fillTo(int position, char filler) { 212 if (ps != null) { 213 indent(); 214 while (lineBuffer.length() < position) { 215 lineBuffer.append(filler); 216 } 217 } 218 return this; 219 } 220 221 /** 222 * Writes a boolean value to this stream as {@code "true"} or {@code "false"}. 223 * 224 * @param b the value to be printed 225 * @return this {@link LogStream} instance 226 */ 227 public LogStream print(boolean b) { 228 if (ps != null) { 229 indent(); 230 lineBuffer.append(b); 231 } 232 return this; 233 } 234 235 /** 236 * Writes a boolean value to this stream followed by a {@linkplain #LINE_SEPARATOR line 237 * separator}. 238 * 239 * @param b the value to be printed 240 * @return this {@link LogStream} instance 241 */ 242 public LogStream println(boolean b) { 243 if (ps != null) { 244 indent(); 245 lineBuffer.append(b); 246 return flushLine(true); 247 } 248 return this; 249 } 250 251 /** 252 * Writes a character value to this stream. 253 * 254 * @param c the value to be printed 255 * @return this {@link LogStream} instance 256 */ 257 public LogStream print(char c) { 258 if (ps != null) { 259 indent(); 260 lineBuffer.append(c); 261 if (c == '\n') { 262 if (lineBuffer.indexOf(LINE_SEPARATOR, lineBuffer.length() - LINE_SEPARATOR.length()) != -1) { 263 flushLine(false); 264 } 265 } 266 } 267 return this; 268 } 269 270 /** 271 * Writes a character value to this stream followed by a {@linkplain #LINE_SEPARATOR line 272 * separator}. 273 * 274 * @param c the value to be printed 275 * @return this {@link LogStream} instance 276 */ 277 public LogStream println(char c) { 278 if (ps != null) { 279 indent(); 280 lineBuffer.append(c); 281 flushLine(true); 282 } 283 return this; 284 } 285 286 /** 287 * Prints an int value. 288 * 289 * @param i the value to be printed 290 * @return this {@link LogStream} instance 291 */ 292 public LogStream print(int i) { 293 if (ps != null) { 294 indent(); 295 lineBuffer.append(i); 296 } 297 return this; 298 } 299 300 /** 301 * Writes an int value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator}. 302 * 303 * @param i the value to be printed 304 * @return this {@link LogStream} instance 305 */ 306 public LogStream println(int i) { 307 if (ps != null) { 308 indent(); 309 lineBuffer.append(i); 310 return flushLine(true); 311 } 312 return this; 313 } 314 315 /** 316 * Writes a float value to this stream. 317 * 318 * @param f the value to be printed 319 * @return this {@link LogStream} instance 320 */ 321 public LogStream print(float f) { 322 if (ps != null) { 323 indent(); 324 lineBuffer.append(f); 325 } 326 return this; 327 } 328 329 /** 330 * Writes a float value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator} 331 * . 332 * 333 * @param f the value to be printed 334 * @return this {@link LogStream} instance 335 */ 336 public LogStream println(float f) { 337 if (ps != null) { 338 indent(); 339 lineBuffer.append(f); 340 return flushLine(true); 341 } 342 return this; 343 } 344 345 /** 346 * Writes a long value to this stream. 347 * 348 * @param l the value to be printed 349 * @return this {@link LogStream} instance 350 */ 351 public LogStream print(long l) { 352 if (ps != null) { 353 indent(); 354 lineBuffer.append(l); 355 } 356 return this; 357 } 358 359 /** 360 * Writes a long value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator}. 361 * 362 * @param l the value to be printed 363 * @return this {@link LogStream} instance 364 */ 365 public LogStream println(long l) { 366 if (ps != null) { 367 indent(); 368 lineBuffer.append(l); 369 return flushLine(true); 370 } 371 return this; 372 } 373 374 /** 375 * Writes a double value to this stream. 376 * 377 * @param d the value to be printed 378 * @return this {@link LogStream} instance 379 */ 380 public LogStream print(double d) { 381 if (ps != null) { 382 indent(); 383 lineBuffer.append(d); 384 } 385 return this; 386 } 387 388 /** 389 * Writes a double value to this stream followed by a {@linkplain #LINE_SEPARATOR line 390 * separator}. 391 * 392 * @param d the value to be printed 393 * @return this {@link LogStream} instance 394 */ 395 public LogStream println(double d) { 396 if (ps != null) { 397 indent(); 398 lineBuffer.append(d); 399 return flushLine(true); 400 } 401 return this; 402 } 403 404 /** 405 * Writes a {@code String} value to this stream. This method ensures that the 406 * {@linkplain #position() position} of this stream is updated correctly with respect to any 407 * {@linkplain #LINE_SEPARATOR line separators} present in {@code s}. 408 * 409 * @param s the value to be printed 410 * @return this {@link LogStream} instance 411 */ 412 public LogStream print(String s) { 413 if (ps != null) { 414 if (s == null) { 415 indent(); 416 lineBuffer.append(s); 417 return this; 418 } 419 420 int index = 0; 421 int next = s.indexOf(LINE_SEPARATOR, index); 422 while (index < s.length()) { 423 indent(); 424 if (next > index || next == 0) { 425 lineBuffer.append(s.substring(index, next)); 426 flushLine(true); 427 index = next + LINE_SEPARATOR.length(); 428 next = s.indexOf(LINE_SEPARATOR, index); 429 } else { 430 lineBuffer.append(s.substring(index)); 431 break; 432 } 433 } 434 } 435 return this; 436 } 437 438 /** 439 * Writes a {@code String} value to this stream followed by a {@linkplain #LINE_SEPARATOR line 440 * separator}. 441 * 442 * @param s the value to be printed 443 * @return this {@link LogStream} instance 444 */ 445 public LogStream println(String s) { 446 if (ps != null) { 447 print(s); 448 flushLine(true); 449 } 450 return this; 451 } 452 453 /** 454 * Writes a formatted string to this stream. 455 * 456 * @param format a format string as described in {@link String#format(String, Object...)} 457 * @param args the arguments to be formatted 458 * @return this {@link LogStream} instance 459 */ 460 public LogStream printf(String format, Object... args) { 461 if (ps != null) { 462 print(String.format(format, args)); 463 } 464 return this; 465 } 466 467 /** 468 * Writes a {@linkplain #LINE_SEPARATOR line separator} to this stream. 469 * 470 * @return this {@code LogStream} instance 471 */ 472 public LogStream println() { 473 if (ps != null) { 474 indent(); 475 flushLine(true); 476 } 477 return this; 478 } 479 }