blob: d27874cd3be224a8295e6a7bf62a79c3a6565835 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package com.android.internal.os;
18
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import java.io.IOException;
Jesse Wilsonf41de2a2009-12-17 17:38:33 -080020import java.io.OutputStream;
21import java.io.PrintStream;
22import java.nio.ByteBuffer;
23import java.nio.CharBuffer;
24import java.nio.charset.Charset;
25import java.nio.charset.CharsetDecoder;
26import java.nio.charset.CoderResult;
27import java.nio.charset.CodingErrorAction;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import java.util.Formatter;
Jesse Wilsonf41de2a2009-12-17 17:38:33 -080029import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
Andreas Gampee2456d62017-10-10 09:09:26 -070031import com.android.internal.annotations.VisibleForTesting;
32
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033/**
34 * A print stream which logs output line by line.
35 *
36 * {@hide}
37 */
Andreas Gampee2456d62017-10-10 09:09:26 -070038@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
39public abstract class LoggingPrintStream extends PrintStream {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
41 private final StringBuilder builder = new StringBuilder();
42
Jesse Wilsonf41de2a2009-12-17 17:38:33 -080043 /**
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 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
Jesse Wilsonf41de2a2009-12-17 17:38:33 -0800113 public void write(int oneByte) {
114 write(new byte[] { (byte) oneByte }, 0, 1);
115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 @Override
Jesse Wilsonf41de2a2009-12-17 17:38:33 -0800118 public void write(byte[] buffer) {
119 write(buffer, 0, buffer.length);
120 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 @Override
Jesse Wilsonf41de2a2009-12-17 17:38:33 -0800123 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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155
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) {
Narayan Kamathb0c7c912013-12-09 11:09:21 +0000299 if (builder.length() == 0 && s != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 // 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}