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