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