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