FastPrintWriter.java revision 8c84109b9fbbf473b225707a38261ff5f99d95fb
1b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratopackage com.android.internal.util;
2b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
3b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.io.IOException;
4b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.io.OutputStream;
5b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.io.PrintWriter;
6b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.io.UnsupportedEncodingException;
7b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.io.Writer;
8b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.nio.ByteBuffer;
9b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.nio.CharBuffer;
10b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.nio.charset.Charset;
11b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.nio.charset.CharsetEncoder;
12b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.nio.charset.CoderResult;
13b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratoimport java.nio.charset.CodingErrorAction;
14b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
15b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onoratopublic class FastPrintWriter extends PrintWriter {
16b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    private static Writer sDummyWriter = new Writer() {
17290bb011c5c1a9ba1f2116810b06cf52a9c36b3eJoe Onorato        @Override
18290bb011c5c1a9ba1f2116810b06cf52a9c36b3eJoe Onorato        public void close() throws IOException {
19290bb011c5c1a9ba1f2116810b06cf52a9c36b3eJoe Onorato            UnsupportedOperationException ex
20b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato                    = new UnsupportedOperationException("Shouldn't be here");
21ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe            throw ex;
22b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        }
23b13b9bdad2baf6ad1ec2e56b6b7598fa20f55fc4Mathias Agopian
24b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        @Override
25b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        public void flush() throws IOException {
26b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato            close();
27b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        }
2858b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat
2906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        @Override
3006290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        public void write(char[] buf, int offset, int count) throws IOException {
3158b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat            close();
3206290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        }
3306290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    };
3406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
3558b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat    private final int mBufferLen;
3606290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    private final char[] mText;
3706290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    private int mPos;
3806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
3906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    final private OutputStream mOutputStream;
4058b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat    final private boolean mAutoFlush;
4158b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat    final private String mSeparator;
4223ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato
43b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    final private Writer mWriter;
44b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato
45b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    private CharsetEncoder mCharset;
46b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    final private ByteBuffer mBytes;
47a3804cf77f0edd93f6247a055cdafb856b117eecElliott Hughes
48a3804cf77f0edd93f6247a055cdafb856b117eecElliott Hughes    private boolean mIoError;
49d2110dbce071a236b6176de344ca797b737542ebJoe Onorato
50b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    /**
51b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * Constructs a new {@code PrintWriter} with {@code out} as its target
52b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * stream. By default, the new print writer does not automatically flush its
53b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * contents to the target stream when a newline is encountered.
54b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     *
55b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * @param out
56b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     *            the target output stream.
5723ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     * @throws NullPointerException
5823ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     *             if {@code out} is {@code null}.
5923ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     */
6023ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato    public FastPrintWriter(OutputStream out) {
6123ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato        this(out, false, 8192);
6223ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato    }
6323ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato
64b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    /**
65b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * Constructs a new {@code PrintWriter} with {@code out} as its target
66b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * stream. The parameter {@code autoFlush} determines if the print writer
67b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * automatically flushes its contents to the target stream when a newline is
68b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * encountered.
6923ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     *
7023ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     * @param out
7123ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     *            the target output stream.
7223ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     * @param autoFlush
7323ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato     *            indicates whether contents are flushed upon encountering a
74b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     *            newline sequence.
7558b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat     * @throws NullPointerException
76b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     *             if {@code out} is {@code null}.
77b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     */
7806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    public FastPrintWriter(OutputStream out, boolean autoFlush) {
7958b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat        this(out, autoFlush, 8192);
8058b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat    }
8106290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato
8206290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    /**
8306290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     * Constructs a new {@code PrintWriter} with {@code out} as its target
8406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     * stream and a custom buffer size. The parameter {@code autoFlush} determines
8506290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     * if the print writer automatically flushes its contents to the target stream
8606290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     * when a newline is encountered.
8706290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     *
8806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     * @param out
8906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     *            the target output stream.
9006290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     * @param autoFlush
9106290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     *            indicates whether contents are flushed upon encountering a
9206290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     *            newline sequence.
9358b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat     * @param bufferLen
9406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     *            specifies the size of the FastPrintWriter's internal buffer; the
9506290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     *            default is 8192.
9658b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat     * @throws NullPointerException
9758b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat     *             if {@code out} is {@code null}.
9806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     */
9906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato    public FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen) {
10006290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        super(sDummyWriter, autoFlush);
10106290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        if (out == null) {
102a3804cf77f0edd93f6247a055cdafb856b117eecElliott Hughes            throw new NullPointerException("out is null");
10306290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        }
10406290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        mBufferLen = bufferLen;
10506290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        mText = new char[bufferLen];
10658b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat        mBytes = ByteBuffer.allocate(mBufferLen);
10706290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        mOutputStream = out;
10806290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato        mWriter = null;
109b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato        mAutoFlush = autoFlush;
11058b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat        mSeparator = System.lineSeparator();
11158b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat        initDefaultEncoder();
112b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato    }
11358b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat
11423ecae3bbb60c5af940f3a22170d75eb6ac05b69Joe Onorato    /**
11558b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat     * Constructs a new {@code PrintWriter} with {@code wr} as its target
11658b8b24256bdc2b613b7fda9151845ed9898a4c7Ashok Bhat     * writer. By default, the new print writer does not automatically flush its
117b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * contents to the target writer when a newline is encountered.
118b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     *
11906290a4bb9b280fa14a2bbeb2d3ceb09396a78c3Joe Onorato     * <p>NOTE: Unlike PrintWriter, this version will still do buffering inside of
120b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * FastPrintWriter before sending data to the Writer.  This means you must call
121ed6b9dff563c5e22f040ff37e12c0d771e0478aeAndreas Gampe     * flush() before retrieving any data from the Writer.</p>
122b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     *
123b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * @param wr
124b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     *            the target writer.
125b1a7ffef3a0007b6991b8338460f6aac8cbb11e8Joe Onorato     * @throws NullPointerException
126     *             if {@code wr} is {@code null}.
127     */
128    public FastPrintWriter(Writer wr) {
129        this(wr, false, 8192);
130    }
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) {
147        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) {
168        super(sDummyWriter, autoFlush);
169        if (wr == null) {
170            throw new NullPointerException("wr is null");
171        }
172        mBufferLen = bufferLen;
173        mText = new char[bufferLen];
174        mBytes = null;
175        mOutputStream = null;
176        mWriter = wr;
177        mAutoFlush = autoFlush;
178        mSeparator = System.lineSeparator();
179        initDefaultEncoder();
180    }
181
182    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
192    /**
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
226    private final void initDefaultEncoder() {
227        mCharset = Charset.defaultCharset().newEncoder();
228        mCharset.onMalformedInput(CodingErrorAction.REPLACE);
229        mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE);
230    }
231
232    private void appendLocked(char c) throws IOException {
233        int pos = mPos;
234        if (pos >= (mBufferLen-1)) {
235            flushLocked();
236            pos = mPos;
237        }
238        mText[pos] = c;
239        mPos = pos+1;
240    }
241
242    private void appendLocked(String str, int i, final int length) throws IOException {
243        final int BUFFER_LEN = mBufferLen;
244        if (length > BUFFER_LEN) {
245            final int end = i + length;
246            while (i < end) {
247                int next = i + BUFFER_LEN;
248                appendLocked(str, i, next < end ? BUFFER_LEN : (end - i));
249                i = next;
250            }
251            return;
252        }
253        int pos = mPos;
254        if ((pos+length) > BUFFER_LEN) {
255            flushLocked();
256            pos = mPos;
257        }
258        str.getChars(i, i + length, mText, pos);
259        mPos = pos + length;
260    }
261
262    private void appendLocked(char[] buf, int i, final int length) throws IOException {
263        final int BUFFER_LEN = mBufferLen;
264        if (length > BUFFER_LEN) {
265            final int end = i + length;
266            while (i < end) {
267                int next = i + BUFFER_LEN;
268                appendLocked(buf, i, next < end ? BUFFER_LEN : (end - i));
269                i = next;
270            }
271            return;
272        }
273        int pos = mPos;
274        if ((pos+length) > BUFFER_LEN) {
275            flushLocked();
276            pos = mPos;
277        }
278        System.arraycopy(buf, i, mText, pos, length);
279        mPos = pos + length;
280    }
281
282    private void flushBytesLocked() throws IOException {
283        int position;
284        if ((position = mBytes.position()) > 0) {
285            mBytes.flip();
286            mOutputStream.write(mBytes.array(), 0, position);
287            mBytes.clear();
288        }
289    }
290
291    private void flushLocked() throws IOException {
292        //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()) {
301                        flushBytesLocked();
302                        result = mCharset.encode(charBuffer, mBytes, true);
303                        continue;
304                    }
305                    break;
306                }
307                flushBytesLocked();
308                mOutputStream.flush();
309            } else {
310                mWriter.write(mText, 0, mPos);
311                mWriter.flush();
312            }
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() {
324        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            }
335        }
336    }
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        }
352    }
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) {
363        synchronized (lock) {
364            try {
365                appendLocked(charArray, 0, charArray.length);
366            } catch (IOException e) {
367            }
368        }
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) {
380        synchronized (lock) {
381            try {
382                appendLocked(ch);
383            } catch (IOException e) {
384            }
385        }
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        }
403        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 {
461            super.print(lnum);
462        }
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) {
502        synchronized (lock) {
503            try {
504                appendLocked(buf, offset, count);
505            } catch (IOException e) {
506            }
507        }
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) {
522        synchronized (lock) {
523            try {
524                appendLocked((char) oneChar);
525            } catch (IOException e) {
526            }
527        }
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) {
538        synchronized (lock) {
539            try {
540                appendLocked(str, 0, str.length());
541            } catch (IOException e) {
542            }
543        }
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) {
562        synchronized (lock) {
563            try {
564                appendLocked(str, offset, count);
565            } catch (IOException e) {
566            }
567        }
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}
601