• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.internal.util;
2 
3 import android.util.Log;
4 import android.util.Printer;
5 
6 import java.io.IOException;
7 import java.io.OutputStream;
8 import java.io.PrintWriter;
9 import java.io.UnsupportedEncodingException;
10 import java.io.Writer;
11 import java.nio.ByteBuffer;
12 import java.nio.CharBuffer;
13 import java.nio.charset.Charset;
14 import java.nio.charset.CharsetEncoder;
15 import java.nio.charset.CoderResult;
16 import java.nio.charset.CodingErrorAction;
17 
18 public class FastPrintWriter extends PrintWriter {
19     private static class DummyWriter extends Writer {
20         @Override
close()21         public void close() throws IOException {
22             UnsupportedOperationException ex
23                     = new UnsupportedOperationException("Shouldn't be here");
24             throw ex;
25         }
26 
27         @Override
flush()28         public void flush() throws IOException {
29             close();
30         }
31 
32         @Override
write(char[] buf, int offset, int count)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      */
FastPrintWriter(OutputStream out)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      */
FastPrintWriter(OutputStream out, boolean autoFlush)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      */
FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen)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      */
FastPrintWriter(Writer wr)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      */
FastPrintWriter(Writer wr, boolean autoFlush)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      */
FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen)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      */
FastPrintWriter(Printer pr)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      */
FastPrintWriter(Printer pr, int bufferLen)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 
initEncoder(String csn)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      */
checkError()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      */
clearError()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      */
setError()269     protected void setError() {
270         synchronized (lock) {
271             mIoError = true;
272         }
273     }
274 
initDefaultEncoder()275     private final void initDefaultEncoder() {
276         mCharset = Charset.defaultCharset().newEncoder();
277         mCharset.onMalformedInput(CodingErrorAction.REPLACE);
278         mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
279     }
280 
appendLocked(char c)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 
appendLocked(String str, int i, final int length)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 
appendLocked(char[] buf, int i, final int length)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 
flushBytesLocked()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 
flushLocked()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 (!mIoError) {
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