blob: f7b94205c59b6c25f98c4818aa4e96c0eb59020f [file] [log] [blame]
/*
* Copyright (c) 2009, 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.debug;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
/**
* A utility for printing compiler debug and informational output to an output stream.
*
* A {@link LogStream} instance maintains an internal buffer that is flushed to the underlying
* output stream every time one of the {@code println} methods is invoked, or a newline character (
* {@code '\n'}) is written.
*
* All of the {@code print} and {@code println} methods return the {code LogStream} instance on
* which they were invoked. This allows chaining of these calls to mitigate use of String
* concatenation by the caller.
*
* A {@code LogStream} maintains a current {@linkplain #indentationLevel() indentation} level. Each
* line of output written to this stream has {@code n} spaces prefixed to it where {@code n} is the
* value that would be returned by {@link #indentationLevel()} when the first character of a new
* line is written.
*
* A {@code LogStream} maintains a current {@linkplain #position() position} for the current line
* being written. This position can be advanced to a specified position by
* {@linkplain #fillTo(int, char) filling} this stream with a given character.
*/
public class LogStream {
/**
* Null output stream that simply swallows any output sent to it.
*/
public static final LogStream SINK = new LogStream();
private static final PrintStream SINK_PS = new PrintStream(new OutputStream() {
@Override
public void write(int b) throws IOException {
}
});
private LogStream() {
this.ps = null;
this.lineBuffer = null;
}
/**
* The output stream to which this log stream writes.
*/
private final PrintStream ps;
private final StringBuilder lineBuffer;
private int indentationLevel;
private char indentation = ' ';
private boolean indentationDisabled;
public final PrintStream out() {
if (ps == null) {
return SINK_PS;
}
return ps;
}
/**
* The system dependent line separator.
*/
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* Creates a new log stream.
*
* @param os the underlying output stream to which prints are sent
*/
public LogStream(OutputStream os) {
ps = os instanceof PrintStream ? (PrintStream) os : new PrintStream(os);
lineBuffer = new StringBuilder(100);
}
/**
* Creates a new log stream that shares the same {@linkplain #ps output stream} as a given
* {@link LogStream}.
*
* @param log a LogStream whose output stream is shared with this one
*/
public LogStream(LogStream log) {
ps = log.ps;
lineBuffer = new StringBuilder(100);
}
/**
* Prepends {@link #indentation} to the current output line until its write position is equal to
* the current {@linkplain #indentationLevel()} level.
*/
private void indent() {
if (ps != null) {
if (!indentationDisabled && indentationLevel != 0) {
while (lineBuffer.length() < indentationLevel) {
lineBuffer.append(indentation);
}
}
}
}
private LogStream flushLine(boolean withNewline) {
if (ps != null) {
if (withNewline) {
lineBuffer.append(LINE_SEPARATOR);
}
ps.print(lineBuffer.toString());
ps.flush();
lineBuffer.setLength(0);
}
return this;
}
/**
* Flushes the stream. This is done by terminating the current line if it is not at position 0
* and then flushing the underlying output stream.
*/
public void flush() {
if (ps != null) {
if (lineBuffer.length() != 0) {
flushLine(false);
}
ps.flush();
}
}
/**
* Gets the current column position of this log stream.
*
* @return the current column position of this log stream
*/
public int position() {
return lineBuffer == null ? 0 : lineBuffer.length();
}
/**
* Gets the current indentation level for this log stream.
*
* @return the current indentation level for this log stream.
*/
public int indentationLevel() {
return indentationLevel;
}
/**
* Adjusts the current indentation level of this log stream.
*
* @param delta
*/
public void adjustIndentation(int delta) {
if (delta < 0) {
indentationLevel = Math.max(0, indentationLevel + delta);
} else {
indentationLevel += delta;
}
}
/**
* Gets the current indentation character of this log stream.
*/
public char indentation() {
return indentation;
}
public void disableIndentation() {
indentationDisabled = true;
}
public void enableIndentation() {
indentationDisabled = false;
}
/**
* Sets the character used for indentation.
*/
public void setIndentation(char c) {
indentation = c;
}
/**
* Advances this stream's {@linkplain #position() position} to a given position by repeatedly
* appending a given character as necessary.
*
* @param position the position to which this stream's position will be advanced
* @param filler the character used to pad the stream
*/
public LogStream fillTo(int position, char filler) {
if (ps != null) {
indent();
while (lineBuffer.length() < position) {
lineBuffer.append(filler);
}
}
return this;
}
/**
* Writes a boolean value to this stream as {@code "true"} or {@code "false"}.
*
* @param b the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream print(boolean b) {
if (ps != null) {
indent();
lineBuffer.append(b);
}
return this;
}
/**
* Writes a boolean value to this stream followed by a {@linkplain #LINE_SEPARATOR line
* separator}.
*
* @param b the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream println(boolean b) {
if (ps != null) {
indent();
lineBuffer.append(b);
return flushLine(true);
}
return this;
}
/**
* Writes a character value to this stream.
*
* @param c the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream print(char c) {
if (ps != null) {
indent();
lineBuffer.append(c);
if (c == '\n') {
if (lineBuffer.indexOf(LINE_SEPARATOR, lineBuffer.length() - LINE_SEPARATOR.length()) != -1) {
flushLine(false);
}
}
}
return this;
}
/**
* Writes a character value to this stream followed by a {@linkplain #LINE_SEPARATOR line
* separator}.
*
* @param c the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream println(char c) {
if (ps != null) {
indent();
lineBuffer.append(c);
flushLine(true);
}
return this;
}
/**
* Prints an int value.
*
* @param i the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream print(int i) {
if (ps != null) {
indent();
lineBuffer.append(i);
}
return this;
}
/**
* Writes an int value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator}.
*
* @param i the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream println(int i) {
if (ps != null) {
indent();
lineBuffer.append(i);
return flushLine(true);
}
return this;
}
/**
* Writes a float value to this stream.
*
* @param f the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream print(float f) {
if (ps != null) {
indent();
lineBuffer.append(f);
}
return this;
}
/**
* Writes a float value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator}
* .
*
* @param f the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream println(float f) {
if (ps != null) {
indent();
lineBuffer.append(f);
return flushLine(true);
}
return this;
}
/**
* Writes a long value to this stream.
*
* @param l the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream print(long l) {
if (ps != null) {
indent();
lineBuffer.append(l);
}
return this;
}
/**
* Writes a long value to this stream followed by a {@linkplain #LINE_SEPARATOR line separator}.
*
* @param l the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream println(long l) {
if (ps != null) {
indent();
lineBuffer.append(l);
return flushLine(true);
}
return this;
}
/**
* Writes a double value to this stream.
*
* @param d the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream print(double d) {
if (ps != null) {
indent();
lineBuffer.append(d);
}
return this;
}
/**
* Writes a double value to this stream followed by a {@linkplain #LINE_SEPARATOR line
* separator}.
*
* @param d the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream println(double d) {
if (ps != null) {
indent();
lineBuffer.append(d);
return flushLine(true);
}
return this;
}
/**
* Writes a {@code String} value to this stream. This method ensures that the
* {@linkplain #position() position} of this stream is updated correctly with respect to any
* {@linkplain #LINE_SEPARATOR line separators} present in {@code s}.
*
* @param s the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream print(String s) {
if (ps != null) {
if (s == null) {
indent();
lineBuffer.append(s);
return this;
}
int index = 0;
int next = s.indexOf(LINE_SEPARATOR, index);
while (index < s.length()) {
indent();
if (next > index || next == 0) {
lineBuffer.append(s.substring(index, next));
flushLine(true);
index = next + LINE_SEPARATOR.length();
next = s.indexOf(LINE_SEPARATOR, index);
} else {
lineBuffer.append(s.substring(index));
break;
}
}
}
return this;
}
/**
* Writes a {@code String} value to this stream followed by a {@linkplain #LINE_SEPARATOR line
* separator}.
*
* @param s the value to be printed
* @return this {@link LogStream} instance
*/
public LogStream println(String s) {
if (ps != null) {
print(s);
flushLine(true);
}
return this;
}
/**
* Writes a formatted string to this stream.
*
* @param format a format string as described in {@link String#format(String, Object...)}
* @param args the arguments to be formatted
* @return this {@link LogStream} instance
*/
public LogStream printf(String format, Object... args) {
if (ps != null) {
print(String.format(format, args));
}
return this;
}
/**
* Writes a {@linkplain #LINE_SEPARATOR line separator} to this stream.
*
* @return this {@code LogStream} instance
*/
public LogStream println() {
if (ps != null) {
indent();
flushLine(true);
}
return this;
}
}