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