FastPrintWriter.java revision e5a9c92377e035b24f50f9f66f4cdfd9cf79c2dd
1package com.android.internal.util; 2 3import java.io.IOException; 4import java.io.OutputStream; 5import java.io.PrintWriter; 6import java.io.UnsupportedEncodingException; 7import java.io.Writer; 8import java.nio.ByteBuffer; 9import java.nio.CharBuffer; 10import java.nio.charset.Charset; 11import java.nio.charset.CharsetEncoder; 12import java.nio.charset.CoderResult; 13import java.nio.charset.CodingErrorAction; 14 15public class FastPrintWriter extends PrintWriter { 16 private static final int BUFFER_LEN = 8192; 17 18 private static Writer sDummyWriter = new Writer() { 19 @Override 20 public void close() throws IOException { 21 UnsupportedOperationException ex 22 = new UnsupportedOperationException("Shouldn't be here"); 23 throw ex; 24 } 25 26 @Override 27 public void flush() throws IOException { 28 close(); 29 } 30 31 @Override 32 public void write(char[] buf, int offset, int count) throws IOException { 33 close(); 34 } 35 }; 36 37 private final char[] mText = new char[BUFFER_LEN]; 38 private int mPos; 39 40 final private OutputStream mOutputStream; 41 final private Writer mWriter; 42 final private boolean mAutoFlush; 43 final private String mSeparator; 44 private CharsetEncoder mCharset; 45 final private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN); 46 private boolean mIoError; 47 48 /** 49 * Constructs a new {@code PrintWriter} with {@code out} as its target 50 * stream. By default, the new print writer does not automatically flush its 51 * contents to the target stream when a newline is encountered. 52 * 53 * @param out 54 * the target output stream. 55 * @throws NullPointerException 56 * if {@code out} is {@code null}. 57 */ 58 public FastPrintWriter(OutputStream out) { 59 super(sDummyWriter); 60 mOutputStream = out; 61 mWriter = null; 62 mAutoFlush = false; 63 mSeparator = System.lineSeparator(); 64 initDefaultEncoder(); 65 } 66 67 /** 68 * Constructs a new {@code PrintWriter} with {@code out} as its target 69 * stream. The parameter {@code autoFlush} determines if the print writer 70 * automatically flushes its contents to the target stream when a newline is 71 * encountered. 72 * 73 * @param out 74 * the target output stream. 75 * @param autoFlush 76 * indicates whether contents are flushed upon encountering a 77 * newline sequence. 78 * @throws NullPointerException 79 * if {@code out} is {@code null}. 80 */ 81 public FastPrintWriter(OutputStream out, boolean autoFlush) { 82 super(sDummyWriter, autoFlush); 83 mOutputStream = out; 84 mWriter = null; 85 mAutoFlush = autoFlush; 86 mSeparator = System.lineSeparator(); 87 initDefaultEncoder(); 88 } 89 90 /** 91 * Constructs a new {@code PrintWriter} with {@code wr} as its target 92 * writer. By default, the new print writer does not automatically flush its 93 * contents to the target writer when a newline is encountered. 94 * 95 * @param wr 96 * the target writer. 97 * @throws NullPointerException 98 * if {@code wr} is {@code null}. 99 */ 100 public FastPrintWriter(Writer wr) { 101 super(sDummyWriter); 102 mOutputStream = null; 103 mWriter = wr; 104 mAutoFlush = false; 105 mSeparator = System.lineSeparator(); 106 initDefaultEncoder(); 107 } 108 109 /** 110 * Constructs a new {@code PrintWriter} with {@code out} as its target 111 * writer. The parameter {@code autoFlush} determines if the print writer 112 * automatically flushes its contents to the target writer when a newline is 113 * encountered. 114 * 115 * @param wr 116 * the target writer. 117 * @param autoFlush 118 * indicates whether to flush contents upon encountering a 119 * newline sequence. 120 * @throws NullPointerException 121 * if {@code out} is {@code null}. 122 */ 123 public FastPrintWriter(Writer wr, boolean autoFlush) { 124 super(sDummyWriter, autoFlush); 125 mOutputStream = null; 126 mWriter = wr; 127 mAutoFlush = autoFlush; 128 mSeparator = System.lineSeparator(); 129 initDefaultEncoder(); 130 } 131 132 private final void initEncoder(String csn) throws UnsupportedEncodingException { 133 try { 134 mCharset = Charset.forName(csn).newEncoder(); 135 } catch (Exception e) { 136 throw new UnsupportedEncodingException(csn); 137 } 138 mCharset.onMalformedInput(CodingErrorAction.REPLACE); 139 mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE); 140 } 141 142 /** 143 * Flushes this writer and returns the value of the error flag. 144 * 145 * @return {@code true} if either an {@code IOException} has been thrown 146 * previously or if {@code setError()} has been called; 147 * {@code false} otherwise. 148 * @see #setError() 149 */ 150 public boolean checkError() { 151 flush(); 152 synchronized (lock) { 153 return mIoError; 154 } 155 } 156 157 /** 158 * Sets the error state of the stream to false. 159 * @since 1.6 160 */ 161 protected void clearError() { 162 synchronized (lock) { 163 mIoError = false; 164 } 165 } 166 167 /** 168 * Sets the error flag of this writer to true. 169 */ 170 protected void setError() { 171 synchronized (lock) { 172 mIoError = true; 173 } 174 } 175 176 private final void initDefaultEncoder() { 177 mCharset = Charset.defaultCharset().newEncoder(); 178 mCharset.onMalformedInput(CodingErrorAction.REPLACE); 179 mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE); 180 } 181 182 private void appendLocked(char c) throws IOException { 183 int pos = mPos; 184 if (pos >= (BUFFER_LEN-1)) { 185 flushLocked(); 186 pos = mPos; 187 } 188 mText[pos] = c; 189 mPos = pos+1; 190 } 191 192 private void appendLocked(String str, int i, final int length) throws IOException { 193 if (length > BUFFER_LEN) { 194 final int end = i + length; 195 while (i < end) { 196 int next = i + BUFFER_LEN; 197 appendLocked(str, i, next < end ? BUFFER_LEN : (end - i)); 198 i = next; 199 } 200 return; 201 } 202 int pos = mPos; 203 if ((pos+length) > BUFFER_LEN) { 204 flushLocked(); 205 pos = mPos; 206 } 207 str.getChars(i, i + length, mText, pos); 208 mPos = pos + length; 209 } 210 211 private void appendLocked(char[] buf, int i, final int length) throws IOException { 212 if (length > BUFFER_LEN) { 213 final int end = i + length; 214 while (i < end) { 215 int next = i + BUFFER_LEN; 216 appendLocked(buf, i, next < end ? BUFFER_LEN : (end - i)); 217 i = next; 218 } 219 return; 220 } 221 int pos = mPos; 222 if ((pos+length) > BUFFER_LEN) { 223 flushLocked(); 224 pos = mPos; 225 } 226 System.arraycopy(buf, i, mText, pos, length); 227 mPos = pos + length; 228 } 229 230 private void flushBytesLocked() throws IOException { 231 int position; 232 if ((position = mBytes.position()) > 0) { 233 mBytes.flip(); 234 mOutputStream.write(mBytes.array(), 0, position); 235 mBytes.clear(); 236 } 237 } 238 239 private void flushLocked() throws IOException { 240 //Log.i("PackageManager", "flush mPos=" + mPos); 241 if (mPos > 0) { 242 if (mOutputStream != null) { 243 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); 244 CoderResult result = mCharset.encode(charBuffer, mBytes, true); 245 while (true) { 246 if (result.isError()) { 247 throw new IOException(result.toString()); 248 } else if (result.isOverflow()) { 249 flushBytesLocked(); 250 result = mCharset.encode(charBuffer, mBytes, true); 251 continue; 252 } 253 break; 254 } 255 flushBytesLocked(); 256 mOutputStream.flush(); 257 } else { 258 mWriter.write(mText, 0, mPos); 259 mWriter.flush(); 260 } 261 mPos = 0; 262 } 263 } 264 265 /** 266 * Ensures that all pending data is sent out to the target. It also 267 * flushes the target. If an I/O error occurs, this writer's error 268 * state is set to {@code true}. 269 */ 270 @Override 271 public void flush() { 272 synchronized (lock) { 273 try { 274 flushLocked(); 275 if (mOutputStream != null) { 276 mOutputStream.flush(); 277 } else { 278 mWriter.flush(); 279 } 280 } catch (IOException e) { 281 setError(); 282 } 283 } 284 } 285 286 @Override 287 public void close() { 288 synchronized (lock) { 289 try { 290 flushLocked(); 291 if (mOutputStream != null) { 292 mOutputStream.close(); 293 } else { 294 mWriter.close(); 295 } 296 } catch (IOException e) { 297 setError(); 298 } 299 } 300 } 301 302 /** 303 * Prints the string representation of the specified character array 304 * to the target. 305 * 306 * @param charArray 307 * the character array to print to the target. 308 * @see #print(String) 309 */ 310 public void print(char[] charArray) { 311 synchronized (lock) { 312 try { 313 appendLocked(charArray, 0, charArray.length); 314 } catch (IOException e) { 315 } 316 } 317 } 318 319 /** 320 * Prints the string representation of the specified character to the 321 * target. 322 * 323 * @param ch 324 * the character to print to the target. 325 * @see #print(String) 326 */ 327 public void print(char ch) { 328 synchronized (lock) { 329 try { 330 appendLocked(ch); 331 } catch (IOException e) { 332 } 333 } 334 } 335 336 /** 337 * Prints a string to the target. The string is converted to an array of 338 * bytes using the encoding chosen during the construction of this writer. 339 * The bytes are then written to the target with {@code write(int)}. 340 * <p> 341 * If an I/O error occurs, this writer's error flag is set to {@code true}. 342 * 343 * @param str 344 * the string to print to the target. 345 * @see #write(int) 346 */ 347 public void print(String str) { 348 if (str == null) { 349 str = String.valueOf((Object) null); 350 } 351 synchronized (lock) { 352 try { 353 appendLocked(str, 0, str.length()); 354 } catch (IOException e) { 355 setError(); 356 } 357 } 358 } 359 360 361 @Override 362 public void print(int inum) { 363 if (inum == 0) { 364 print("0"); 365 } else { 366 super.print(inum); 367 } 368 } 369 370 @Override 371 public void print(long lnum) { 372 if (lnum == 0) { 373 print("0"); 374 } else { 375 super.print(lnum); 376 } 377 } 378 379 /** 380 * Prints a newline. Flushes this writer if the autoFlush flag is set to {@code true}. 381 */ 382 public void println() { 383 synchronized (lock) { 384 try { 385 appendLocked(mSeparator, 0, mSeparator.length()); 386 if (mAutoFlush) { 387 flushLocked(); 388 } 389 } catch (IOException e) { 390 setError(); 391 } 392 } 393 } 394 395 @Override 396 public void println(int inum) { 397 if (inum == 0) { 398 println("0"); 399 } else { 400 super.println(inum); 401 } 402 } 403 404 @Override 405 public void println(long lnum) { 406 if (lnum == 0) { 407 println("0"); 408 } else { 409 super.print(lnum); 410 } 411 } 412 413 /** 414 * Prints the string representation of the character array {@code chars} followed by a newline. 415 * Flushes this writer if the autoFlush flag is set to {@code true}. 416 */ 417 public void println(char[] chars) { 418 print(chars); 419 println(); 420 } 421 422 /** 423 * Prints the string representation of the char {@code c} followed by a newline. 424 * Flushes this writer if the autoFlush flag is set to {@code true}. 425 */ 426 public void println(char c) { 427 print(c); 428 println(); 429 } 430 431 /** 432 * Writes {@code count} characters from {@code buffer} starting at {@code 433 * offset} to the target. 434 * <p> 435 * This writer's error flag is set to {@code true} if this writer is closed 436 * or an I/O error occurs. 437 * 438 * @param buf 439 * the buffer to write to the target. 440 * @param offset 441 * the index of the first character in {@code buffer} to write. 442 * @param count 443 * the number of characters in {@code buffer} to write. 444 * @throws IndexOutOfBoundsException 445 * if {@code offset < 0} or {@code count < 0}, or if {@code 446 * offset + count} is greater than the length of {@code buf}. 447 */ 448 @Override 449 public void write(char[] buf, int offset, int count) { 450 synchronized (lock) { 451 try { 452 appendLocked(buf, offset, count); 453 } catch (IOException e) { 454 } 455 } 456 } 457 458 /** 459 * Writes one character to the target. Only the two least significant bytes 460 * of the integer {@code oneChar} are written. 461 * <p> 462 * This writer's error flag is set to {@code true} if this writer is closed 463 * or an I/O error occurs. 464 * 465 * @param oneChar 466 * the character to write to the target. 467 */ 468 @Override 469 public void write(int oneChar) { 470 synchronized (lock) { 471 try { 472 appendLocked((char) oneChar); 473 } catch (IOException e) { 474 } 475 } 476 } 477 478 /** 479 * Writes the characters from the specified string to the target. 480 * 481 * @param str 482 * the non-null string containing the characters to write. 483 */ 484 @Override 485 public void write(String str) { 486 synchronized (lock) { 487 try { 488 appendLocked(str, 0, str.length()); 489 } catch (IOException e) { 490 } 491 } 492 } 493 494 /** 495 * Writes {@code count} characters from {@code str} starting at {@code 496 * offset} to the target. 497 * 498 * @param str 499 * the non-null string containing the characters to write. 500 * @param offset 501 * the index of the first character in {@code str} to write. 502 * @param count 503 * the number of characters from {@code str} to write. 504 * @throws IndexOutOfBoundsException 505 * if {@code offset < 0} or {@code count < 0}, or if {@code 506 * offset + count} is greater than the length of {@code str}. 507 */ 508 @Override 509 public void write(String str, int offset, int count) { 510 synchronized (lock) { 511 try { 512 appendLocked(str, offset, count); 513 } catch (IOException e) { 514 } 515 } 516 } 517 518 /** 519 * Appends a subsequence of the character sequence {@code csq} to the 520 * target. This method works the same way as {@code 521 * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code 522 * csq} is {@code null}, then the specified subsequence of the string "null" 523 * will be written to the target. 524 * 525 * @param csq 526 * the character sequence appended to the target. 527 * @param start 528 * the index of the first char in the character sequence appended 529 * to the target. 530 * @param end 531 * the index of the character following the last character of the 532 * subsequence appended to the target. 533 * @return this writer. 534 * @throws StringIndexOutOfBoundsException 535 * if {@code start > end}, {@code start < 0}, {@code end < 0} or 536 * either {@code start} or {@code end} are greater or equal than 537 * the length of {@code csq}. 538 */ 539 @Override 540 public PrintWriter append(CharSequence csq, int start, int end) { 541 if (csq == null) { 542 csq = "null"; 543 } 544 String output = csq.subSequence(start, end).toString(); 545 write(output, 0, output.length()); 546 return this; 547 } 548} 549