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