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