FastPrintWriter.java revision db4e33f1f1d766afa3218a6bbdbb561e7962c854
1package com.android.internal.util;
2
3import java.io.File;
4import java.io.FileNotFoundException;
5import java.io.IOException;
6import java.io.OutputStream;
7import java.io.PrintWriter;
8import java.io.UnsupportedEncodingException;
9import java.io.Writer;
10import java.nio.ByteBuffer;
11import java.nio.CharBuffer;
12import java.nio.charset.Charset;
13import java.nio.charset.CharsetEncoder;
14import java.nio.charset.CoderResult;
15import java.nio.charset.CodingErrorAction;
16
17public class FastPrintWriter extends PrintWriter {
18    private static final int BUFFER_LEN = 8192;
19
20    private final char[] mText = new char[BUFFER_LEN];
21    private int mPos;
22
23    final private OutputStream mOutputStream;
24    private CharsetEncoder mCharset;
25    final private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN);
26
27    /**
28     * Constructs a new {@code PrintWriter} with {@code out} as its target
29     * stream. By default, the new print writer does not automatically flush its
30     * contents to the target stream when a newline is encountered.
31     *
32     * @param out
33     *            the target output stream.
34     * @throws NullPointerException
35     *             if {@code out} is {@code null}.
36     */
37    public FastPrintWriter(OutputStream out) {
38        super(out);
39        mOutputStream = out;
40        initDefaultEncoder();
41    }
42
43    /**
44     * Constructs a new {@code PrintWriter} with {@code out} as its target
45     * stream. The parameter {@code autoFlush} determines if the print writer
46     * automatically flushes its contents to the target stream when a newline is
47     * encountered.
48     *
49     * @param out
50     *            the target output stream.
51     * @param autoFlush
52     *            indicates whether contents are flushed upon encountering a
53     *            newline sequence.
54     * @throws NullPointerException
55     *             if {@code out} is {@code null}.
56     */
57    public FastPrintWriter(OutputStream out, boolean autoFlush) {
58        super(out, autoFlush);
59        mOutputStream = out;
60        initDefaultEncoder();
61    }
62
63    /**
64     * Constructs a new {@code PrintWriter} with {@code wr} as its target
65     * writer. By default, the new print writer does not automatically flush its
66     * contents to the target writer when a newline is encountered.
67     *
68     * @param wr
69     *            the target writer.
70     * @throws NullPointerException
71     *             if {@code wr} is {@code null}.
72     */
73    public FastPrintWriter(Writer wr) {
74        super(wr);
75        mOutputStream = null;
76        initDefaultEncoder();
77    }
78
79    /**
80     * Constructs a new {@code PrintWriter} with {@code out} as its target
81     * writer. The parameter {@code autoFlush} determines if the print writer
82     * automatically flushes its contents to the target writer when a newline is
83     * encountered.
84     *
85     * @param wr
86     *            the target writer.
87     * @param autoFlush
88     *            indicates whether to flush contents upon encountering a
89     *            newline sequence.
90     * @throws NullPointerException
91     *             if {@code out} is {@code null}.
92     */
93    public FastPrintWriter(Writer wr, boolean autoFlush) {
94        super(wr, autoFlush);
95        mOutputStream = null;
96        initDefaultEncoder();
97    }
98
99    /**
100     * Constructs a new {@code PrintWriter} with {@code file} as its target. The
101     * VM's default character set is used for character encoding.
102     * The print writer does not automatically flush its contents to the target
103     * file when a newline is encountered. The output to the file is buffered.
104     *
105     * @param file
106     *            the target file. If the file already exists, its contents are
107     *            removed, otherwise a new file is created.
108     * @throws java.io.FileNotFoundException
109     *             if an error occurs while opening or creating the target file.
110     */
111    public FastPrintWriter(File file) throws FileNotFoundException {
112        super(file);
113        mOutputStream = null;
114        initDefaultEncoder();
115    }
116
117    /**
118     * Constructs a new {@code PrintWriter} with {@code file} as its target. The
119     * character set named {@code csn} is used for character encoding.
120     * The print writer does not automatically flush its contents to the target
121     * file when a newline is encountered. The output to the file is buffered.
122     *
123     * @param file
124     *            the target file. If the file already exists, its contents are
125     *            removed, otherwise a new file is created.
126     * @param csn
127     *            the name of the character set used for character encoding.
128     * @throws FileNotFoundException
129     *             if an error occurs while opening or creating the target file.
130     * @throws NullPointerException
131     *             if {@code csn} is {@code null}.
132     * @throws java.io.UnsupportedEncodingException
133     *             if the encoding specified by {@code csn} is not supported.
134     */
135    public FastPrintWriter(File file, String csn) throws FileNotFoundException,
136            UnsupportedEncodingException {
137        super(file, csn);
138        mOutputStream = null;
139        initEncoder(csn);
140    }
141
142    /**
143     * Constructs a new {@code PrintWriter} with the file identified by {@code
144     * fileName} as its target. The VM's default character set is
145     * used for character encoding. The print writer does not automatically
146     * flush its contents to the target file when a newline is encountered. The
147     * output to the file is buffered.
148     *
149     * @param fileName
150     *            the target file's name. If the file already exists, its
151     *            contents are removed, otherwise a new file is created.
152     * @throws FileNotFoundException
153     *             if an error occurs while opening or creating the target file.
154     */
155    public FastPrintWriter(String fileName) throws FileNotFoundException {
156        super(fileName);
157        mOutputStream = null;
158        initDefaultEncoder();
159    }
160
161     /**
162     * Constructs a new {@code PrintWriter} with the file identified by {@code
163     * fileName} as its target. The character set named {@code csn} is used for
164     * character encoding. The print writer does not automatically flush its
165     * contents to the target file when a newline is encountered. The output to
166     * the file is buffered.
167     *
168     * @param fileName
169     *            the target file's name. If the file already exists, its
170     *            contents are removed, otherwise a new file is created.
171     * @param csn
172     *            the name of the character set used for character encoding.
173     * @throws FileNotFoundException
174     *             if an error occurs while opening or creating the target file.
175     * @throws NullPointerException
176     *             if {@code csn} is {@code null}.
177     * @throws UnsupportedEncodingException
178     *             if the encoding specified by {@code csn} is not supported.
179     */
180    public FastPrintWriter(String fileName, String csn)
181            throws FileNotFoundException, UnsupportedEncodingException {
182        super(fileName, csn);
183        mOutputStream = null;
184        initEncoder(csn);
185    }
186
187    private final void initEncoder(String csn) throws UnsupportedEncodingException {
188        try {
189            mCharset = Charset.forName(csn).newEncoder();
190        } catch (Exception e) {
191            throw new UnsupportedEncodingException(csn);
192        }
193        mCharset.onMalformedInput(CodingErrorAction.REPLACE);
194        mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
195    }
196
197    private final void initDefaultEncoder() {
198        mCharset = Charset.defaultCharset().newEncoder();
199        mCharset.onMalformedInput(CodingErrorAction.REPLACE);
200        mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
201    }
202
203    private void appendInner(char c) throws IOException {
204        int pos = mPos;
205        if (pos >= (BUFFER_LEN-1)) {
206            flush();
207            pos = mPos;
208        }
209        mText[pos] = c;
210        mPos = pos+1;
211    }
212
213    private void appendInner(String str, int i, final int length) throws IOException {
214        if (length > BUFFER_LEN) {
215            final int end = i + length;
216            while (i < end) {
217                int next = i + BUFFER_LEN;
218                appendInner(str, i, next<end ? BUFFER_LEN : (end-i));
219                i = next;
220            }
221            return;
222        }
223        int pos = mPos;
224        if ((pos+length) > BUFFER_LEN) {
225            flush();
226            pos = mPos;
227        }
228        str.getChars(i, i + length, mText, pos);
229        mPos = pos + length;
230    }
231
232    private void appendInner(char[] buf, int i, final int length) throws IOException {
233        if (length > BUFFER_LEN) {
234            final int end = i + length;
235            while (i < end) {
236                int next = i + BUFFER_LEN;
237                appendInner(buf, i, next < end ? BUFFER_LEN : (end - i));
238                i = next;
239            }
240            return;
241        }
242        int pos = mPos;
243        if ((pos+length) > BUFFER_LEN) {
244            flush();
245            pos = mPos;
246        }
247        System.arraycopy(buf, i, mText, pos, length);
248        mPos = pos + length;
249    }
250
251    private void flushBytesInner() throws IOException {
252        int position;
253        if ((position = mBytes.position()) > 0) {
254            mBytes.flip();
255            mOutputStream.write(mBytes.array(), 0, position);
256            mBytes.clear();
257        }
258    }
259
260    private void flushInner() throws IOException {
261        //Log.i("PackageManager", "flush mPos=" + mPos);
262        if (mPos > 0) {
263            if (mOutputStream != null) {
264                CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
265                CoderResult result = mCharset.encode(charBuffer, mBytes, true);
266                while (true) {
267                    if (result.isError()) {
268                        throw new IOException(result.toString());
269                    } else if (result.isOverflow()) {
270                        flushBytesInner();
271                        result = mCharset.encode(charBuffer, mBytes, true);
272                        continue;
273                    }
274                    break;
275                }
276                flushBytesInner();
277                mOutputStream.flush();
278            } else {
279                out.write(mText, 0, mPos);
280                out.flush();
281            }
282            mPos = 0;
283        }
284    }
285
286    /**
287     * Ensures that all pending data is sent out to the target. It also
288     * flushes the target. If an I/O error occurs, this writer's error
289     * state is set to {@code true}.
290     */
291    @Override
292    public void flush() {
293        try {
294            flushInner();
295        } catch (IOException e) {
296        }
297        super.flush();
298    }
299
300    /**
301     * Prints the string representation of the specified character array
302     * to the target.
303     *
304     * @param charArray
305     *            the character array to print to the target.
306     * @see #print(String)
307     */
308    public void print(char[] charArray) {
309        try {
310            appendInner(charArray, 0, charArray.length);
311        } catch (IOException e) {
312        }
313    }
314
315    /**
316     * Prints the string representation of the specified character to the
317     * target.
318     *
319     * @param ch
320     *            the character to print to the target.
321     * @see #print(String)
322     */
323    public void print(char ch) {
324        try {
325            appendInner(ch);
326        } catch (IOException e) {
327        }
328    }
329
330    /**
331     * Prints a string to the target. The string is converted to an array of
332     * bytes using the encoding chosen during the construction of this writer.
333     * The bytes are then written to the target with {@code write(int)}.
334     * <p>
335     * If an I/O error occurs, this writer's error flag is set to {@code true}.
336     *
337     * @param str
338     *            the string to print to the target.
339     * @see #write(int)
340     */
341    public void print(String str) {
342        if (str == null) {
343            str = String.valueOf((Object) null);
344        }
345        try {
346            appendInner(str, 0, str.length());
347        } catch (IOException e) {
348        }
349    }
350
351    /**
352     * Prints the string representation of the character array {@code chars} followed by a newline.
353     * Flushes this writer if the autoFlush flag is set to {@code true}.
354     */
355    public void println(char[] chars) {
356        print(chars);
357        println();
358    }
359
360    /**
361     * Prints the string representation of the char {@code c} followed by a newline.
362     * Flushes this writer if the autoFlush flag is set to {@code true}.
363     */
364    public void println(char c) {
365        print(c);
366        println();
367    }
368
369    /**
370     * Writes {@code count} characters from {@code buffer} starting at {@code
371     * offset} to the target.
372     * <p>
373     * This writer's error flag is set to {@code true} if this writer is closed
374     * or an I/O error occurs.
375     *
376     * @param buf
377     *            the buffer to write to the target.
378     * @param offset
379     *            the index of the first character in {@code buffer} to write.
380     * @param count
381     *            the number of characters in {@code buffer} to write.
382     * @throws IndexOutOfBoundsException
383     *             if {@code offset < 0} or {@code count < 0}, or if {@code
384     *             offset + count} is greater than the length of {@code buf}.
385     */
386    @Override
387    public void write(char[] buf, int offset, int count) {
388        try {
389            appendInner(buf, offset, count);
390        } catch (IOException e) {
391        }
392    }
393
394    /**
395     * Writes one character to the target. Only the two least significant bytes
396     * of the integer {@code oneChar} are written.
397     * <p>
398     * This writer's error flag is set to {@code true} if this writer is closed
399     * or an I/O error occurs.
400     *
401     * @param oneChar
402     *            the character to write to the target.
403     */
404    @Override
405    public void write(int oneChar) {
406        try {
407            appendInner((char) oneChar);
408        } catch (IOException e) {
409        }
410    }
411
412    /**
413     * Writes the characters from the specified string to the target.
414     *
415     * @param str
416     *            the non-null string containing the characters to write.
417     */
418    @Override
419    public void write(String str) {
420        try {
421            appendInner(str, 0, str.length());
422        } catch (IOException e) {
423        }
424    }
425
426    /**
427     * Writes {@code count} characters from {@code str} starting at {@code
428     * offset} to the target.
429     *
430     * @param str
431     *            the non-null string containing the characters to write.
432     * @param offset
433     *            the index of the first character in {@code str} to write.
434     * @param count
435     *            the number of characters from {@code str} to write.
436     * @throws IndexOutOfBoundsException
437     *             if {@code offset < 0} or {@code count < 0}, or if {@code
438     *             offset + count} is greater than the length of {@code str}.
439     */
440    @Override
441    public void write(String str, int offset, int count) {
442        try {
443            appendInner(str, offset, count);
444        } catch (IOException e) {
445        }
446    }
447
448    /**
449     * Appends a subsequence of the character sequence {@code csq} to the
450     * target. This method works the same way as {@code
451     * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code
452     * csq} is {@code null}, then the specified subsequence of the string "null"
453     * will be written to the target.
454     *
455     * @param csq
456     *            the character sequence appended to the target.
457     * @param start
458     *            the index of the first char in the character sequence appended
459     *            to the target.
460     * @param end
461     *            the index of the character following the last character of the
462     *            subsequence appended to the target.
463     * @return this writer.
464     * @throws StringIndexOutOfBoundsException
465     *             if {@code start > end}, {@code start < 0}, {@code end < 0} or
466     *             either {@code start} or {@code end} are greater or equal than
467     *             the length of {@code csq}.
468     */
469    @Override
470    public PrintWriter append(CharSequence csq, int start, int end) {
471        if (csq == null) {
472            csq = "null";
473        }
474        String output = csq.subSequence(start, end).toString();
475        write(output, 0, output.length());
476        return this;
477    }
478}
479