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