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