Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.os;
     18 
     19 import java.io.IOException;
     20 import java.io.OutputStream;
     21 import java.io.PrintStream;
     22 import java.nio.ByteBuffer;
     23 import java.nio.CharBuffer;
     24 import java.nio.charset.Charset;
     25 import java.nio.charset.CharsetDecoder;
     26 import java.nio.charset.CoderResult;
     27 import java.nio.charset.CodingErrorAction;
     28 import java.util.Formatter;
     29 import java.util.Locale;
     30 
     31 import com.android.internal.annotations.VisibleForTesting;
     32 
     33 /**
     34  * A print stream which logs output line by line.
     35  *
     36  * {@hide}
     37  */
     38 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     39 public abstract class LoggingPrintStream extends PrintStream {
     40 
     41     private final StringBuilder builder = new StringBuilder();
     42 
     43     /**
     44      * A buffer that is initialized when raw bytes are first written to this
     45      * stream. It may contain the leading bytes of multi-byte characters.
     46      * Between writes this buffer is always ready to receive data; ie. the
     47      * position is at the first unassigned byte and the limit is the capacity.
     48      */
     49     private ByteBuffer encodedBytes;
     50 
     51     /**
     52      * A buffer that is initialized when raw bytes are first written to this
     53      * stream. Between writes this buffer is always clear; ie. the position is
     54      * zero and the limit is the capacity.
     55      */
     56     private CharBuffer decodedChars;
     57 
     58     /**
     59      * Decodes bytes to characters using the system default charset. Initialized
     60      * when raw bytes are first written to this stream.
     61      */
     62     private CharsetDecoder decoder;
     63 
     64     protected LoggingPrintStream() {
     65         super(new OutputStream() {
     66             public void write(int oneByte) throws IOException {
     67                 throw new AssertionError();
     68             }
     69         });
     70     }
     71 
     72     /**
     73      * Logs the given line.
     74      */
     75     protected abstract void log(String line);
     76 
     77     @Override
     78     public synchronized void flush() {
     79         flush(true);
     80     }
     81 
     82     /**
     83      * Searches buffer for line breaks and logs a message for each one.
     84      *
     85      * @param completely true if the ending chars should be treated as a line
     86      *  even though they don't end in a line break
     87      */
     88     private void flush(boolean completely) {
     89         int length = builder.length();
     90 
     91         int start = 0;
     92         int nextBreak;
     93 
     94         // Log one line for each line break.
     95         while (start < length
     96                 && (nextBreak = builder.indexOf("\n", start)) != -1) {
     97             log(builder.substring(start, nextBreak));
     98             start = nextBreak + 1;
     99         }
    100 
    101         if (completely) {
    102             // Log the remainder of the buffer.
    103             if (start < length) {
    104                 log(builder.substring(start));
    105             }
    106             builder.setLength(0);
    107         } else {
    108             // Delete characters leading up to the next starting point.
    109             builder.delete(0, start);
    110         }
    111     }
    112 
    113     public void write(int oneByte) {
    114         write(new byte[] { (byte) oneByte }, 0, 1);
    115     }
    116 
    117     @Override
    118     public void write(byte[] buffer) {
    119         write(buffer, 0, buffer.length);
    120     }
    121 
    122     @Override
    123     public synchronized void write(byte bytes[], int start, int count) {
    124         if (decoder == null) {
    125             encodedBytes = ByteBuffer.allocate(80);
    126             decodedChars = CharBuffer.allocate(80);
    127             decoder = Charset.defaultCharset().newDecoder()
    128                     .onMalformedInput(CodingErrorAction.REPLACE)
    129                     .onUnmappableCharacter(CodingErrorAction.REPLACE);
    130         }
    131 
    132         int end = start + count;
    133         while (start < end) {
    134             // copy some bytes from the array to the long-lived buffer. This
    135             // way, if we end with a partial character we don't lose it.
    136             int numBytes = Math.min(encodedBytes.remaining(), end - start);
    137             encodedBytes.put(bytes, start, numBytes);
    138             start += numBytes;
    139 
    140             encodedBytes.flip();
    141             CoderResult coderResult;
    142             do {
    143                 // decode bytes from the byte buffer into the char buffer
    144                 coderResult = decoder.decode(encodedBytes, decodedChars, false);
    145 
    146                 // copy chars from the char buffer into our string builder
    147                 decodedChars.flip();
    148                 builder.append(decodedChars);
    149                 decodedChars.clear();
    150             } while (coderResult.isOverflow());
    151             encodedBytes.compact();
    152         }
    153         flush(false);
    154     }
    155 
    156     /** Always returns false. */
    157     @Override
    158     public boolean checkError() {
    159         return false;
    160     }
    161 
    162     /** Ignored. */
    163     @Override
    164     protected void setError() { /* ignored */ }
    165 
    166     /** Ignored. */
    167     @Override
    168     public void close() { /* ignored */ }
    169 
    170     @Override
    171     public PrintStream format(String format, Object... args) {
    172         return format(Locale.getDefault(), format, args);
    173     }
    174 
    175     @Override
    176     public PrintStream printf(String format, Object... args) {
    177         return format(format, args);
    178     }
    179 
    180     @Override
    181     public PrintStream printf(Locale l, String format, Object... args) {
    182         return format(l, format, args);
    183     }
    184 
    185     private final Formatter formatter = new Formatter(builder, null);
    186 
    187     @Override
    188     public synchronized PrintStream format(
    189             Locale l, String format, Object... args) {
    190         if (format == null) {
    191             throw new NullPointerException("format");
    192         }
    193 
    194         formatter.format(l, format, args);
    195         flush(false);
    196         return this;
    197     }
    198 
    199     @Override
    200     public synchronized void print(char[] charArray) {
    201         builder.append(charArray);
    202         flush(false);
    203     }
    204 
    205     @Override
    206     public synchronized void print(char ch) {
    207         builder.append(ch);
    208         if (ch == '\n') {
    209             flush(false);
    210         }
    211     }
    212 
    213     @Override
    214     public synchronized void print(double dnum) {
    215         builder.append(dnum);
    216     }
    217 
    218     @Override
    219     public synchronized void print(float fnum) {
    220         builder.append(fnum);
    221     }
    222 
    223     @Override
    224     public synchronized void print(int inum) {
    225         builder.append(inum);
    226     }
    227 
    228     @Override
    229     public synchronized void print(long lnum) {
    230         builder.append(lnum);
    231     }
    232 
    233     @Override
    234     public synchronized void print(Object obj) {
    235         builder.append(obj);
    236         flush(false);
    237     }
    238 
    239     @Override
    240     public synchronized void print(String str) {
    241         builder.append(str);
    242         flush(false);
    243     }
    244 
    245     @Override
    246     public synchronized void print(boolean bool) {
    247         builder.append(bool);
    248     }
    249 
    250     @Override
    251     public synchronized void println() {
    252         flush(true);
    253     }
    254 
    255     @Override
    256     public synchronized void println(char[] charArray) {
    257         builder.append(charArray);
    258         flush(true);
    259     }
    260 
    261     @Override
    262     public synchronized void println(char ch) {
    263         builder.append(ch);
    264         flush(true);
    265     }
    266 
    267     @Override
    268     public synchronized void println(double dnum) {
    269         builder.append(dnum);
    270         flush(true);
    271     }
    272 
    273     @Override
    274     public synchronized void println(float fnum) {
    275         builder.append(fnum);
    276         flush(true);
    277     }
    278 
    279     @Override
    280     public synchronized void println(int inum) {
    281         builder.append(inum);
    282         flush(true);
    283     }
    284 
    285     @Override
    286     public synchronized void println(long lnum) {
    287         builder.append(lnum);
    288         flush(true);
    289     }
    290 
    291     @Override
    292     public synchronized void println(Object obj) {
    293         builder.append(obj);
    294         flush(true);
    295     }
    296 
    297     @Override
    298     public synchronized void println(String s) {
    299         if (builder.length() == 0 && s != null) {
    300             // Optimization for a simple println.
    301             int length = s.length();
    302 
    303             int start = 0;
    304             int nextBreak;
    305 
    306             // Log one line for each line break.
    307             while (start < length
    308                     && (nextBreak = s.indexOf('\n', start)) != -1) {
    309                 log(s.substring(start, nextBreak));
    310                 start = nextBreak + 1;
    311             }
    312 
    313             if (start < length) {
    314                 log(s.substring(start));
    315             }
    316         } else {
    317             builder.append(s);
    318             flush(true);
    319         }
    320     }
    321 
    322     @Override
    323     public synchronized void println(boolean bool) {
    324         builder.append(bool);
    325         flush(true);
    326     }
    327 
    328     @Override
    329     public synchronized PrintStream append(char c) {
    330         print(c);
    331         return this;
    332     }
    333 
    334     @Override
    335     public synchronized PrintStream append(CharSequence csq) {
    336         builder.append(csq);
    337         flush(false);
    338         return this;
    339     }
    340 
    341     @Override
    342     public synchronized PrintStream append(
    343             CharSequence csq, int start, int end) {
    344         builder.append(csq, start, end);
    345         flush(false);
    346         return this;
    347     }
    348 }
    349