/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.os;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Formatter;
import java.util.Locale;

import com.android.internal.annotations.VisibleForTesting;

A print stream which logs output line by line. {@hide}
/** * A print stream which logs output line by line. * * {@hide} */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public abstract class LoggingPrintStream extends PrintStream { private final StringBuilder builder = new StringBuilder();
A buffer that is initialized when raw bytes are first written to this stream. It may contain the leading bytes of multi-byte characters. Between writes this buffer is always ready to receive data; ie. the position is at the first unassigned byte and the limit is the capacity.
/** * A buffer that is initialized when raw bytes are first written to this * stream. It may contain the leading bytes of multi-byte characters. * Between writes this buffer is always ready to receive data; ie. the * position is at the first unassigned byte and the limit is the capacity. */
private ByteBuffer encodedBytes;
A buffer that is initialized when raw bytes are first written to this stream. Between writes this buffer is always clear; ie. the position is zero and the limit is the capacity.
/** * A buffer that is initialized when raw bytes are first written to this * stream. Between writes this buffer is always clear; ie. the position is * zero and the limit is the capacity. */
private CharBuffer decodedChars;
Decodes bytes to characters using the system default charset. Initialized when raw bytes are first written to this stream.
/** * Decodes bytes to characters using the system default charset. Initialized * when raw bytes are first written to this stream. */
private CharsetDecoder decoder; protected LoggingPrintStream() { super(new OutputStream() { public void write(int oneByte) throws IOException { throw new AssertionError(); } }); }
Logs the given line.
/** * Logs the given line. */
protected abstract void log(String line); @Override public synchronized void flush() { flush(true); }
Searches buffer for line breaks and logs a message for each one.
Params:
  • completely – true if the ending chars should be treated as a line even though they don't end in a line break
/** * Searches buffer for line breaks and logs a message for each one. * * @param completely true if the ending chars should be treated as a line * even though they don't end in a line break */
private void flush(boolean completely) { int length = builder.length(); int start = 0; int nextBreak; // Log one line for each line break. while (start < length && (nextBreak = builder.indexOf("\n", start)) != -1) { log(builder.substring(start, nextBreak)); start = nextBreak + 1; } if (completely) { // Log the remainder of the buffer. if (start < length) { log(builder.substring(start)); } builder.setLength(0); } else { // Delete characters leading up to the next starting point. builder.delete(0, start); } } public void write(int oneByte) { write(new byte[] { (byte) oneByte }, 0, 1); } @Override public void write(byte[] buffer) { write(buffer, 0, buffer.length); } @Override public synchronized void write(byte bytes[], int start, int count) { if (decoder == null) { encodedBytes = ByteBuffer.allocate(80); decodedChars = CharBuffer.allocate(80); decoder = Charset.defaultCharset().newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); } int end = start + count; while (start < end) { // copy some bytes from the array to the long-lived buffer. This // way, if we end with a partial character we don't lose it. int numBytes = Math.min(encodedBytes.remaining(), end - start); encodedBytes.put(bytes, start, numBytes); start += numBytes; encodedBytes.flip(); CoderResult coderResult; do { // decode bytes from the byte buffer into the char buffer coderResult = decoder.decode(encodedBytes, decodedChars, false); // copy chars from the char buffer into our string builder decodedChars.flip(); builder.append(decodedChars); decodedChars.clear(); } while (coderResult.isOverflow()); encodedBytes.compact(); } flush(false); }
Always returns false.
/** Always returns false. */
@Override public boolean checkError() { return false; }
Ignored.
/** Ignored. */
@Override protected void setError() { /* ignored */ }
Ignored.
/** Ignored. */
@Override public void close() { /* ignored */ } @Override public PrintStream format(String format, Object... args) { return format(Locale.getDefault(), format, args); } @Override public PrintStream printf(String format, Object... args) { return format(format, args); } @Override public PrintStream printf(Locale l, String format, Object... args) { return format(l, format, args); } private final Formatter formatter = new Formatter(builder, null); @Override public synchronized PrintStream format( Locale l, String format, Object... args) { if (format == null) { throw new NullPointerException("format"); } formatter.format(l, format, args); flush(false); return this; } @Override public synchronized void print(char[] charArray) { builder.append(charArray); flush(false); } @Override public synchronized void print(char ch) { builder.append(ch); if (ch == '\n') { flush(false); } } @Override public synchronized void print(double dnum) { builder.append(dnum); } @Override public synchronized void print(float fnum) { builder.append(fnum); } @Override public synchronized void print(int inum) { builder.append(inum); } @Override public synchronized void print(long lnum) { builder.append(lnum); } @Override public synchronized void print(Object obj) { builder.append(obj); flush(false); } @Override public synchronized void print(String str) { builder.append(str); flush(false); } @Override public synchronized void print(boolean bool) { builder.append(bool); } @Override public synchronized void println() { flush(true); } @Override public synchronized void println(char[] charArray) { builder.append(charArray); flush(true); } @Override public synchronized void println(char ch) { builder.append(ch); flush(true); } @Override public synchronized void println(double dnum) { builder.append(dnum); flush(true); } @Override public synchronized void println(float fnum) { builder.append(fnum); flush(true); } @Override public synchronized void println(int inum) { builder.append(inum); flush(true); } @Override public synchronized void println(long lnum) { builder.append(lnum); flush(true); } @Override public synchronized void println(Object obj) { builder.append(obj); flush(true); } @Override public synchronized void println(String s) { if (builder.length() == 0 && s != null) { // Optimization for a simple println. int length = s.length(); int start = 0; int nextBreak; // Log one line for each line break. while (start < length && (nextBreak = s.indexOf('\n', start)) != -1) { log(s.substring(start, nextBreak)); start = nextBreak + 1; } if (start < length) { log(s.substring(start)); } } else { builder.append(s); flush(true); } } @Override public synchronized void println(boolean bool) { builder.append(bool); flush(true); } @Override public synchronized PrintStream append(char c) { print(c); return this; } @Override public synchronized PrintStream append(CharSequence csq) { builder.append(csq); flush(false); return this; } @Override public synchronized PrintStream append( CharSequence csq, int start, int end) { builder.append(csq, start, end); flush(false); return this; } }