blob: 9bec10e5fa2b1d0a8993aa15d70d07a5c817049a [file] [log] [blame]
Dianne Hackborndb4e33f2013-04-01 17:28:16 -07001package com.android.internal.util;
2
Dianne Hackborndb4e33f2013-04-01 17:28:16 -07003import 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 {
Dianne Hackborne5a9c922013-06-24 15:54:52 -070016 private static Writer sDummyWriter = new Writer() {
17 @Override
18 public void close() throws IOException {
19 UnsupportedOperationException ex
20 = new UnsupportedOperationException("Shouldn't be here");
21 throw ex;
22 }
23
24 @Override
25 public void flush() throws IOException {
26 close();
27 }
28
29 @Override
30 public void write(char[] buf, int offset, int count) throws IOException {
31 close();
32 }
33 };
34
Dianne Hackborn8c841092013-06-24 13:46:13 -070035 private final int mBufferLen;
36 private final char[] mText;
Dianne Hackborndb4e33f2013-04-01 17:28:16 -070037 private int mPos;
38
39 final private OutputStream mOutputStream;
Dianne Hackborne5a9c922013-06-24 15:54:52 -070040 final private boolean mAutoFlush;
41 final private String mSeparator;
Dianne Hackborn8c841092013-06-24 13:46:13 -070042
43 final private Writer mWriter;
44
Dianne Hackborndb4e33f2013-04-01 17:28:16 -070045 private CharsetEncoder mCharset;
Dianne Hackborn8c841092013-06-24 13:46:13 -070046 final private ByteBuffer mBytes;
47
Dianne Hackborne5a9c922013-06-24 15:54:52 -070048 private boolean mIoError;
Dianne Hackborndb4e33f2013-04-01 17:28:16 -070049
50 /**
51 * Constructs a new {@code PrintWriter} with {@code out} as its target
52 * stream. By default, the new print writer does not automatically flush its
53 * contents to the target stream when a newline is encountered.
54 *
55 * @param out
56 * the target output stream.
57 * @throws NullPointerException
58 * if {@code out} is {@code null}.
59 */
60 public FastPrintWriter(OutputStream out) {
Dianne Hackborn8c841092013-06-24 13:46:13 -070061 this(out, false, 8192);
Dianne Hackborndb4e33f2013-04-01 17:28:16 -070062 }
63
64 /**
65 * Constructs a new {@code PrintWriter} with {@code out} as its target
66 * stream. The parameter {@code autoFlush} determines if the print writer
67 * automatically flushes its contents to the target stream when a newline is
68 * encountered.
69 *
70 * @param out
71 * the target output stream.
72 * @param autoFlush
73 * indicates whether contents are flushed upon encountering a
74 * newline sequence.
75 * @throws NullPointerException
76 * if {@code out} is {@code null}.
77 */
78 public FastPrintWriter(OutputStream out, boolean autoFlush) {
Dianne Hackborn8c841092013-06-24 13:46:13 -070079 this(out, autoFlush, 8192);
80 }
81
82 /**
83 * Constructs a new {@code PrintWriter} with {@code out} as its target
84 * stream and a custom buffer size. The parameter {@code autoFlush} determines
85 * if the print writer automatically flushes its contents to the target stream
86 * when a newline is encountered.
87 *
88 * @param out
89 * the target output stream.
90 * @param autoFlush
91 * indicates whether contents are flushed upon encountering a
92 * newline sequence.
93 * @param bufferLen
94 * specifies the size of the FastPrintWriter's internal buffer; the
95 * default is 8192.
96 * @throws NullPointerException
97 * if {@code out} is {@code null}.
98 */
99 public FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700100 super(sDummyWriter, autoFlush);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700101 if (out == null) {
102 throw new NullPointerException("out is null");
103 }
104 mBufferLen = bufferLen;
105 mText = new char[bufferLen];
106 mBytes = ByteBuffer.allocate(mBufferLen);
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700107 mOutputStream = out;
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700108 mWriter = null;
109 mAutoFlush = autoFlush;
110 mSeparator = System.lineSeparator();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700111 initDefaultEncoder();
112 }
113
114 /**
115 * Constructs a new {@code PrintWriter} with {@code wr} as its target
116 * writer. By default, the new print writer does not automatically flush its
117 * contents to the target writer when a newline is encountered.
118 *
Dianne Hackborn8c841092013-06-24 13:46:13 -0700119 * <p>NOTE: Unlike PrintWriter, this version will still do buffering inside of
120 * FastPrintWriter before sending data to the Writer. This means you must call
121 * flush() before retrieving any data from the Writer.</p>
122 *
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700123 * @param wr
124 * the target writer.
125 * @throws NullPointerException
126 * if {@code wr} is {@code null}.
127 */
128 public FastPrintWriter(Writer wr) {
Dianne Hackborn8c841092013-06-24 13:46:13 -0700129 this(wr, false, 8192);
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700130 }
131
132 /**
133 * Constructs a new {@code PrintWriter} with {@code out} as its target
134 * writer. The parameter {@code autoFlush} determines if the print writer
135 * automatically flushes its contents to the target writer when a newline is
136 * encountered.
137 *
138 * @param wr
139 * the target writer.
140 * @param autoFlush
141 * indicates whether to flush contents upon encountering a
142 * newline sequence.
143 * @throws NullPointerException
144 * if {@code out} is {@code null}.
145 */
146 public FastPrintWriter(Writer wr, boolean autoFlush) {
Dianne Hackborn8c841092013-06-24 13:46:13 -0700147 this(wr, autoFlush, 8192);
148 }
149
150 /**
151 * Constructs a new {@code PrintWriter} with {@code out} as its target
152 * writer and a custom buffer size. The parameter {@code autoFlush} determines
153 * if the print writer automatically flushes its contents to the target writer
154 * when a newline is encountered.
155 *
156 * @param wr
157 * the target writer.
158 * @param autoFlush
159 * indicates whether to flush contents upon encountering a
160 * newline sequence.
161 * @param bufferLen
162 * specifies the size of the FastPrintWriter's internal buffer; the
163 * default is 8192.
164 * @throws NullPointerException
165 * if {@code wr} is {@code null}.
166 */
167 public FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700168 super(sDummyWriter, autoFlush);
Dianne Hackborn8c841092013-06-24 13:46:13 -0700169 if (wr == null) {
170 throw new NullPointerException("wr is null");
171 }
172 mBufferLen = bufferLen;
173 mText = new char[bufferLen];
174 mBytes = null;
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700175 mOutputStream = null;
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700176 mWriter = wr;
177 mAutoFlush = autoFlush;
178 mSeparator = System.lineSeparator();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700179 initDefaultEncoder();
180 }
181
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700182 private final void initEncoder(String csn) throws UnsupportedEncodingException {
183 try {
184 mCharset = Charset.forName(csn).newEncoder();
185 } catch (Exception e) {
186 throw new UnsupportedEncodingException(csn);
187 }
188 mCharset.onMalformedInput(CodingErrorAction.REPLACE);
189 mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
190 }
191
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700192 /**
193 * Flushes this writer and returns the value of the error flag.
194 *
195 * @return {@code true} if either an {@code IOException} has been thrown
196 * previously or if {@code setError()} has been called;
197 * {@code false} otherwise.
198 * @see #setError()
199 */
200 public boolean checkError() {
201 flush();
202 synchronized (lock) {
203 return mIoError;
204 }
205 }
206
207 /**
208 * Sets the error state of the stream to false.
209 * @since 1.6
210 */
211 protected void clearError() {
212 synchronized (lock) {
213 mIoError = false;
214 }
215 }
216
217 /**
218 * Sets the error flag of this writer to true.
219 */
220 protected void setError() {
221 synchronized (lock) {
222 mIoError = true;
223 }
224 }
225
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700226 private final void initDefaultEncoder() {
227 mCharset = Charset.defaultCharset().newEncoder();
228 mCharset.onMalformedInput(CodingErrorAction.REPLACE);
229 mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
230 }
231
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700232 private void appendLocked(char c) throws IOException {
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700233 int pos = mPos;
Dianne Hackborn8c841092013-06-24 13:46:13 -0700234 if (pos >= (mBufferLen-1)) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700235 flushLocked();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700236 pos = mPos;
237 }
238 mText[pos] = c;
239 mPos = pos+1;
240 }
241
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700242 private void appendLocked(String str, int i, final int length) throws IOException {
Dianne Hackborn8c841092013-06-24 13:46:13 -0700243 final int BUFFER_LEN = mBufferLen;
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700244 if (length > BUFFER_LEN) {
245 final int end = i + length;
246 while (i < end) {
247 int next = i + BUFFER_LEN;
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700248 appendLocked(str, i, next < end ? BUFFER_LEN : (end - i));
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700249 i = next;
250 }
251 return;
252 }
253 int pos = mPos;
254 if ((pos+length) > BUFFER_LEN) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700255 flushLocked();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700256 pos = mPos;
257 }
258 str.getChars(i, i + length, mText, pos);
259 mPos = pos + length;
260 }
261
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700262 private void appendLocked(char[] buf, int i, final int length) throws IOException {
Dianne Hackborn8c841092013-06-24 13:46:13 -0700263 final int BUFFER_LEN = mBufferLen;
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700264 if (length > BUFFER_LEN) {
265 final int end = i + length;
266 while (i < end) {
267 int next = i + BUFFER_LEN;
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700268 appendLocked(buf, i, next < end ? BUFFER_LEN : (end - i));
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700269 i = next;
270 }
271 return;
272 }
273 int pos = mPos;
274 if ((pos+length) > BUFFER_LEN) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700275 flushLocked();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700276 pos = mPos;
277 }
278 System.arraycopy(buf, i, mText, pos, length);
279 mPos = pos + length;
280 }
281
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700282 private void flushBytesLocked() throws IOException {
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700283 int position;
284 if ((position = mBytes.position()) > 0) {
285 mBytes.flip();
286 mOutputStream.write(mBytes.array(), 0, position);
287 mBytes.clear();
288 }
289 }
290
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700291 private void flushLocked() throws IOException {
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700292 //Log.i("PackageManager", "flush mPos=" + mPos);
293 if (mPos > 0) {
294 if (mOutputStream != null) {
295 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
296 CoderResult result = mCharset.encode(charBuffer, mBytes, true);
297 while (true) {
298 if (result.isError()) {
299 throw new IOException(result.toString());
300 } else if (result.isOverflow()) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700301 flushBytesLocked();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700302 result = mCharset.encode(charBuffer, mBytes, true);
303 continue;
304 }
305 break;
306 }
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700307 flushBytesLocked();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700308 mOutputStream.flush();
309 } else {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700310 mWriter.write(mText, 0, mPos);
311 mWriter.flush();
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700312 }
313 mPos = 0;
314 }
315 }
316
317 /**
318 * Ensures that all pending data is sent out to the target. It also
319 * flushes the target. If an I/O error occurs, this writer's error
320 * state is set to {@code true}.
321 */
322 @Override
323 public void flush() {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700324 synchronized (lock) {
325 try {
326 flushLocked();
327 if (mOutputStream != null) {
328 mOutputStream.flush();
329 } else {
330 mWriter.flush();
331 }
332 } catch (IOException e) {
333 setError();
334 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700335 }
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700336 }
337
338 @Override
339 public void close() {
340 synchronized (lock) {
341 try {
342 flushLocked();
343 if (mOutputStream != null) {
344 mOutputStream.close();
345 } else {
346 mWriter.close();
347 }
348 } catch (IOException e) {
349 setError();
350 }
351 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700352 }
353
354 /**
355 * Prints the string representation of the specified character array
356 * to the target.
357 *
358 * @param charArray
359 * the character array to print to the target.
360 * @see #print(String)
361 */
362 public void print(char[] charArray) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700363 synchronized (lock) {
364 try {
365 appendLocked(charArray, 0, charArray.length);
366 } catch (IOException e) {
367 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700368 }
369 }
370
371 /**
372 * Prints the string representation of the specified character to the
373 * target.
374 *
375 * @param ch
376 * the character to print to the target.
377 * @see #print(String)
378 */
379 public void print(char ch) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700380 synchronized (lock) {
381 try {
382 appendLocked(ch);
383 } catch (IOException e) {
384 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700385 }
386 }
387
388 /**
389 * Prints a string to the target. The string is converted to an array of
390 * bytes using the encoding chosen during the construction of this writer.
391 * The bytes are then written to the target with {@code write(int)}.
392 * <p>
393 * If an I/O error occurs, this writer's error flag is set to {@code true}.
394 *
395 * @param str
396 * the string to print to the target.
397 * @see #write(int)
398 */
399 public void print(String str) {
400 if (str == null) {
401 str = String.valueOf((Object) null);
402 }
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700403 synchronized (lock) {
404 try {
405 appendLocked(str, 0, str.length());
406 } catch (IOException e) {
407 setError();
408 }
409 }
410 }
411
412
413 @Override
414 public void print(int inum) {
415 if (inum == 0) {
416 print("0");
417 } else {
418 super.print(inum);
419 }
420 }
421
422 @Override
423 public void print(long lnum) {
424 if (lnum == 0) {
425 print("0");
426 } else {
427 super.print(lnum);
428 }
429 }
430
431 /**
432 * Prints a newline. Flushes this writer if the autoFlush flag is set to {@code true}.
433 */
434 public void println() {
435 synchronized (lock) {
436 try {
437 appendLocked(mSeparator, 0, mSeparator.length());
438 if (mAutoFlush) {
439 flushLocked();
440 }
441 } catch (IOException e) {
442 setError();
443 }
444 }
445 }
446
447 @Override
448 public void println(int inum) {
449 if (inum == 0) {
450 println("0");
451 } else {
452 super.println(inum);
453 }
454 }
455
456 @Override
457 public void println(long lnum) {
458 if (lnum == 0) {
459 println("0");
460 } else {
Dianne Hackborn20cdcee2013-07-10 18:47:04 -0700461 super.println(lnum);
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700462 }
463 }
464
465 /**
466 * Prints the string representation of the character array {@code chars} followed by a newline.
467 * Flushes this writer if the autoFlush flag is set to {@code true}.
468 */
469 public void println(char[] chars) {
470 print(chars);
471 println();
472 }
473
474 /**
475 * Prints the string representation of the char {@code c} followed by a newline.
476 * Flushes this writer if the autoFlush flag is set to {@code true}.
477 */
478 public void println(char c) {
479 print(c);
480 println();
481 }
482
483 /**
484 * Writes {@code count} characters from {@code buffer} starting at {@code
485 * offset} to the target.
486 * <p>
487 * This writer's error flag is set to {@code true} if this writer is closed
488 * or an I/O error occurs.
489 *
490 * @param buf
491 * the buffer to write to the target.
492 * @param offset
493 * the index of the first character in {@code buffer} to write.
494 * @param count
495 * the number of characters in {@code buffer} to write.
496 * @throws IndexOutOfBoundsException
497 * if {@code offset < 0} or {@code count < 0}, or if {@code
498 * offset + count} is greater than the length of {@code buf}.
499 */
500 @Override
501 public void write(char[] buf, int offset, int count) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700502 synchronized (lock) {
503 try {
504 appendLocked(buf, offset, count);
505 } catch (IOException e) {
506 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700507 }
508 }
509
510 /**
511 * Writes one character to the target. Only the two least significant bytes
512 * of the integer {@code oneChar} are written.
513 * <p>
514 * This writer's error flag is set to {@code true} if this writer is closed
515 * or an I/O error occurs.
516 *
517 * @param oneChar
518 * the character to write to the target.
519 */
520 @Override
521 public void write(int oneChar) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700522 synchronized (lock) {
523 try {
524 appendLocked((char) oneChar);
525 } catch (IOException e) {
526 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700527 }
528 }
529
530 /**
531 * Writes the characters from the specified string to the target.
532 *
533 * @param str
534 * the non-null string containing the characters to write.
535 */
536 @Override
537 public void write(String str) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700538 synchronized (lock) {
539 try {
540 appendLocked(str, 0, str.length());
541 } catch (IOException e) {
542 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700543 }
544 }
545
546 /**
547 * Writes {@code count} characters from {@code str} starting at {@code
548 * offset} to the target.
549 *
550 * @param str
551 * the non-null string containing the characters to write.
552 * @param offset
553 * the index of the first character in {@code str} to write.
554 * @param count
555 * the number of characters from {@code str} to write.
556 * @throws IndexOutOfBoundsException
557 * if {@code offset < 0} or {@code count < 0}, or if {@code
558 * offset + count} is greater than the length of {@code str}.
559 */
560 @Override
561 public void write(String str, int offset, int count) {
Dianne Hackborne5a9c922013-06-24 15:54:52 -0700562 synchronized (lock) {
563 try {
564 appendLocked(str, offset, count);
565 } catch (IOException e) {
566 }
Dianne Hackborndb4e33f2013-04-01 17:28:16 -0700567 }
568 }
569
570 /**
571 * Appends a subsequence of the character sequence {@code csq} to the
572 * target. This method works the same way as {@code
573 * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code
574 * csq} is {@code null}, then the specified subsequence of the string "null"
575 * will be written to the target.
576 *
577 * @param csq
578 * the character sequence appended to the target.
579 * @param start
580 * the index of the first char in the character sequence appended
581 * to the target.
582 * @param end
583 * the index of the character following the last character of the
584 * subsequence appended to the target.
585 * @return this writer.
586 * @throws StringIndexOutOfBoundsException
587 * if {@code start > end}, {@code start < 0}, {@code end < 0} or
588 * either {@code start} or {@code end} are greater or equal than
589 * the length of {@code csq}.
590 */
591 @Override
592 public PrintWriter append(CharSequence csq, int start, int end) {
593 if (csq == null) {
594 csq = "null";
595 }
596 String output = csq.subSequence(start, end).toString();
597 write(output, 0, output.length());
598 return this;
599 }
600}