Home | History | Annotate | Download | only in io
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.io;
     19 
     20 import java.util.Arrays;
     21 import libcore.util.SneakyThrow;
     22 
     23 /**
     24  * Wraps an existing {@link Writer} and <em>buffers</em> the output. Expensive
     25  * interaction with the underlying reader is minimized, since most (smaller)
     26  * requests can be satisfied by accessing the buffer alone. The drawback is that
     27  * some extra space is required to hold the buffer and that copying takes place
     28  * when filling that buffer, but this is usually outweighed by the performance
     29  * benefits.
     30  *
     31  * <p/>A typical application pattern for the class looks like this:<p/>
     32  *
     33  * <pre>
     34  * BufferedWriter buf = new BufferedWriter(new FileWriter(&quot;file.java&quot;));
     35  * </pre>
     36  *
     37  * @see BufferedReader
     38  */
     39 public class BufferedWriter extends Writer {
     40 
     41     private Writer out;
     42 
     43     private char[] buf;
     44 
     45     private int pos;
     46 
     47     /**
     48      * Constructs a new {@code BufferedWriter}, providing {@code out} with a buffer
     49      * of 8192 chars.
     50      *
     51      * @param out the {@code Writer} the buffer writes to.
     52      */
     53     public BufferedWriter(Writer out) {
     54         this(out, 8192);
     55     }
     56 
     57     /**
     58      * Constructs a new {@code BufferedWriter}, providing {@code out} with {@code size} chars
     59      * of buffer.
     60      *
     61      * @param out the {@code OutputStream} the buffer writes to.
     62      * @param size the size of buffer in chars.
     63      * @throws IllegalArgumentException if {@code size <= 0}.
     64      */
     65     public BufferedWriter(Writer out, int size) {
     66         super(out);
     67         if (size <= 0) {
     68             throw new IllegalArgumentException("size <= 0");
     69         }
     70         this.out = out;
     71         this.buf = new char[size];
     72     }
     73 
     74     /**
     75      * Closes this writer. The contents of the buffer are flushed, the target
     76      * writer is closed, and the buffer is released. Only the first invocation
     77      * of close has any effect.
     78      *
     79      * @throws IOException
     80      *             if an error occurs while closing this writer.
     81      */
     82     @Override
     83     public void close() throws IOException {
     84         synchronized (lock) {
     85             if (isClosed()) {
     86                 return;
     87             }
     88 
     89             Throwable thrown = null;
     90             try {
     91                 flushInternal();
     92             } catch (Throwable e) {
     93                 thrown = e;
     94             }
     95             buf = null;
     96 
     97             try {
     98                 out.close();
     99             } catch (Throwable e) {
    100                 if (thrown == null) {
    101                     thrown = e;
    102                 }
    103             }
    104             out = null;
    105 
    106             if (thrown != null) {
    107                 SneakyThrow.sneakyThrow(thrown);
    108             }
    109         }
    110     }
    111 
    112     /**
    113      * Flushes this writer. The contents of the buffer are committed to the
    114      * target writer and it is then flushed.
    115      *
    116      * @throws IOException
    117      *             if an error occurs while flushing this writer.
    118      */
    119     @Override
    120     public void flush() throws IOException {
    121         synchronized (lock) {
    122             checkNotClosed();
    123             flushInternal();
    124             out.flush();
    125         }
    126     }
    127 
    128     private void checkNotClosed() throws IOException {
    129         if (isClosed()) {
    130             throw new IOException("BufferedWriter is closed");
    131         }
    132     }
    133 
    134     /**
    135      * Flushes the internal buffer.
    136      */
    137     private void flushInternal() throws IOException {
    138         if (pos > 0) {
    139             out.write(buf, 0, pos);
    140         }
    141         pos = 0;
    142     }
    143 
    144     /**
    145      * Indicates whether this writer is closed.
    146      *
    147      * @return {@code true} if this writer is closed, {@code false} otherwise.
    148      */
    149     private boolean isClosed() {
    150         return out == null;
    151     }
    152 
    153     /**
    154      * Writes a newline to this writer. On Android, this is {@code "\n"}.
    155      * The target writer may or may not be flushed when a newline is written.
    156      *
    157      * @throws IOException
    158      *             if an error occurs attempting to write to this writer.
    159      */
    160     public void newLine() throws IOException {
    161         write(System.lineSeparator());
    162     }
    163 
    164     /**
    165      * Writes {@code count} characters starting at {@code offset} in
    166      * {@code buffer} to this writer. If {@code count} is greater than this
    167      * writer's buffer, then the buffer is flushed and the characters are
    168      * written directly to the target writer.
    169      *
    170      * @param buffer
    171      *            the array containing characters to write.
    172      * @param offset
    173      *            the start position in {@code buffer} for retrieving characters.
    174      * @param count
    175      *            the maximum number of characters to write.
    176      * @throws IndexOutOfBoundsException
    177      *             if {@code offset < 0} or {@code count < 0}, or if
    178      *             {@code offset + count} is greater than the size of
    179      *             {@code buffer}.
    180      * @throws IOException
    181      *             if this writer is closed or another I/O error occurs.
    182      */
    183     @Override
    184     public void write(char[] buffer, int offset, int count) throws IOException {
    185         synchronized (lock) {
    186             checkNotClosed();
    187             if (buffer == null) {
    188                 throw new NullPointerException("buffer == null");
    189             }
    190             Arrays.checkOffsetAndCount(buffer.length, offset, count);
    191             if (pos == 0 && count >= this.buf.length) {
    192                 out.write(buffer, offset, count);
    193                 return;
    194             }
    195             int available = this.buf.length - pos;
    196             if (count < available) {
    197                 available = count;
    198             }
    199             if (available > 0) {
    200                 System.arraycopy(buffer, offset, this.buf, pos, available);
    201                 pos += available;
    202             }
    203             if (pos == this.buf.length) {
    204                 out.write(this.buf, 0, this.buf.length);
    205                 pos = 0;
    206                 if (count > available) {
    207                     offset += available;
    208                     available = count - available;
    209                     if (available >= this.buf.length) {
    210                         out.write(buffer, offset, available);
    211                         return;
    212                     }
    213 
    214                     System.arraycopy(buffer, offset, this.buf, pos, available);
    215                     pos += available;
    216                 }
    217             }
    218         }
    219     }
    220 
    221     /**
    222      * Writes the character {@code oneChar} to this writer. If the buffer
    223      * gets full by writing this character, this writer is flushed. Only the
    224      * lower two bytes of the integer {@code oneChar} are written.
    225      *
    226      * @param oneChar
    227      *            the character to write.
    228      * @throws IOException
    229      *             if this writer is closed or another I/O error occurs.
    230      */
    231     @Override
    232     public void write(int oneChar) throws IOException {
    233         synchronized (lock) {
    234             checkNotClosed();
    235             if (pos >= buf.length) {
    236                 out.write(buf, 0, buf.length);
    237                 pos = 0;
    238             }
    239             buf[pos++] = (char) oneChar;
    240         }
    241     }
    242 
    243     /**
    244      * Writes {@code count} characters starting at {@code offset} in {@code str}
    245      * to this writer. If {@code count} is greater than this writer's buffer,
    246      * then this writer is flushed and the remaining characters are written
    247      * directly to the target writer. If count is negative no characters are
    248      * written to the buffer. This differs from the behavior of the superclass.
    249      *
    250      * @param str
    251      *            the non-null String containing characters to write.
    252      * @param offset
    253      *            the start position in {@code str} for retrieving characters.
    254      * @param count
    255      *            maximum number of characters to write.
    256      * @throws IOException
    257      *             if this writer has already been closed or another I/O error
    258      *             occurs.
    259      * @throws IndexOutOfBoundsException
    260      *             if {@code offset < 0} or {@code offset + count} is greater
    261      *             than the length of {@code str}.
    262      */
    263     @Override
    264     public void write(String str, int offset, int count) throws IOException {
    265         synchronized (lock) {
    266             checkNotClosed();
    267             if (count <= 0) {
    268                 return;
    269             }
    270             if (offset < 0 || offset > str.length() - count) {
    271                 throw new StringIndexOutOfBoundsException(str, offset, count);
    272             }
    273             if (pos == 0 && count >= buf.length) {
    274                 char[] chars = new char[count];
    275                 str.getChars(offset, offset + count, chars, 0);
    276                 out.write(chars, 0, count);
    277                 return;
    278             }
    279             int available = buf.length - pos;
    280             if (count < available) {
    281                 available = count;
    282             }
    283             if (available > 0) {
    284                 str.getChars(offset, offset + available, buf, pos);
    285                 pos += available;
    286             }
    287             if (pos == buf.length) {
    288                 out.write(this.buf, 0, this.buf.length);
    289                 pos = 0;
    290                 if (count > available) {
    291                     offset += available;
    292                     available = count - available;
    293                     if (available >= buf.length) {
    294                         char[] chars = new char[count];
    295                         str.getChars(offset, offset + available, chars, 0);
    296                         out.write(chars, 0, available);
    297                         return;
    298                     }
    299                     str.getChars(offset, offset + available, buf, pos);
    300                     pos += available;
    301                 }
    302             }
    303         }
    304     }
    305 }
    306