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