• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LoggingPrintStream()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      */
log(String line)72     protected abstract void log(String line);
73 
74     @Override
flush()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      */
flush(boolean completely)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 
write(int oneByte)110     public void write(int oneByte) {
111         write(new byte[] { (byte) oneByte }, 0, 1);
112     }
113 
114     @Override
write(byte[] buffer)115     public void write(byte[] buffer) {
116         write(buffer, 0, buffer.length);
117     }
118 
119     @Override
write(byte bytes[], int start, int count)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
checkError()155     public boolean checkError() {
156         return false;
157     }
158 
159     /** Ignored. */
160     @Override
setError()161     protected void setError() { /* ignored */ }
162 
163     /** Ignored. */
164     @Override
close()165     public void close() { /* ignored */ }
166 
167     @Override
format(String format, Object... args)168     public PrintStream format(String format, Object... args) {
169         return format(Locale.getDefault(), format, args);
170     }
171 
172     @Override
printf(String format, Object... args)173     public PrintStream printf(String format, Object... args) {
174         return format(format, args);
175     }
176 
177     @Override
printf(Locale l, String format, Object... args)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
format( Locale l, String format, Object... args)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
print(char[] charArray)197     public synchronized void print(char[] charArray) {
198         builder.append(charArray);
199         flush(false);
200     }
201 
202     @Override
print(char ch)203     public synchronized void print(char ch) {
204         builder.append(ch);
205         if (ch == '\n') {
206             flush(false);
207         }
208     }
209 
210     @Override
print(double dnum)211     public synchronized void print(double dnum) {
212         builder.append(dnum);
213     }
214 
215     @Override
print(float fnum)216     public synchronized void print(float fnum) {
217         builder.append(fnum);
218     }
219 
220     @Override
print(int inum)221     public synchronized void print(int inum) {
222         builder.append(inum);
223     }
224 
225     @Override
print(long lnum)226     public synchronized void print(long lnum) {
227         builder.append(lnum);
228     }
229 
230     @Override
print(Object obj)231     public synchronized void print(Object obj) {
232         builder.append(obj);
233         flush(false);
234     }
235 
236     @Override
print(String str)237     public synchronized void print(String str) {
238         builder.append(str);
239         flush(false);
240     }
241 
242     @Override
print(boolean bool)243     public synchronized void print(boolean bool) {
244         builder.append(bool);
245     }
246 
247     @Override
println()248     public synchronized void println() {
249         flush(true);
250     }
251 
252     @Override
println(char[] charArray)253     public synchronized void println(char[] charArray) {
254         builder.append(charArray);
255         flush(true);
256     }
257 
258     @Override
println(char ch)259     public synchronized void println(char ch) {
260         builder.append(ch);
261         flush(true);
262     }
263 
264     @Override
println(double dnum)265     public synchronized void println(double dnum) {
266         builder.append(dnum);
267         flush(true);
268     }
269 
270     @Override
println(float fnum)271     public synchronized void println(float fnum) {
272         builder.append(fnum);
273         flush(true);
274     }
275 
276     @Override
println(int inum)277     public synchronized void println(int inum) {
278         builder.append(inum);
279         flush(true);
280     }
281 
282     @Override
println(long lnum)283     public synchronized void println(long lnum) {
284         builder.append(lnum);
285         flush(true);
286     }
287 
288     @Override
println(Object obj)289     public synchronized void println(Object obj) {
290         builder.append(obj);
291         flush(true);
292     }
293 
294     @Override
println(String s)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
println(boolean bool)320     public synchronized void println(boolean bool) {
321         builder.append(bool);
322         flush(true);
323     }
324 
325     @Override
append(char c)326     public synchronized PrintStream append(char c) {
327         print(c);
328         return this;
329     }
330 
331     @Override
append(CharSequence csq)332     public synchronized PrintStream append(CharSequence csq) {
333         builder.append(csq);
334         flush(false);
335         return this;
336     }
337 
338     @Override
append( CharSequence csq, int start, int end)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