view graal/com.oracle.max.criutils/src/com/oracle/max/criutils/LogStream.java @ 5318:b5cd7bc05695

Method entry counters: Enable the flag to collect an execution profile of compiled methods and their callers. This allows to, e.g., detect methods that should be inlined because they are called frequently.
author Christian Wimmer <Christian.Wimmer@Oracle.com>
date Fri, 27 Apr 2012 12:56:39 -0700
parents e233f5660da4
children
line wrap: on
line source

/*
 * 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 com.oracle.max.criutils;

import java.io.*;

/**
 * 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) {
                    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;
    }
}