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