    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.io.output;
    018
    019    import java.io.File;
    020    import java.io.FileOutputStream;
    021    import java.io.IOException;
    022    import java.io.OutputStream;
    023    import java.io.OutputStreamWriter;
    024    import java.io.Writer;
    025    import java.nio.charset.Charset;
    026    import java.nio.charset.CharsetEncoder;
    027
    028    import org.apache.commons.io.FileUtils;
    029    import org.apache.commons.io.IOUtils;
    030
    031    /**
    032     * Writer of files that allows the encoding to be set.
    033     * <p>
    034     * This class provides a simple alternative to <code>FileWriter</code>
    035     * that allows an encoding to be set. Unfortunately, it cannot subclass
    036     * <code>FileWriter</code>.
    037     * <p>
    038     * By default, the file will be overwritten, but this may be changed to append.
    039     * <p>
    040     * The encoding must be specified using either the name of the {@link Charset},
    041     * the {@link Charset}, or a {@link CharsetEncoder}. If the default encoding
    042     * is required then use the {@link java.io.FileWriter} directly, rather than
    043     * this implementation.
    044     * <p>
    045     *
    046     *
    047     * @since Commons IO 1.4
    048     * @version $Id: FileWriterWithEncoding.java 659817 2008-05-24 13:23:10Z niallp $
    049     */
    050    public class FileWriterWithEncoding extends Writer {
    051        // Cannot extend ProxyWriter, as requires writer to be
    052        // known when super() is called
    053
    054        /** The writer to decorate. */
    055        private final Writer out;
    056
    057        /**
    058         * Constructs a FileWriterWithEncoding with a file encoding.
    059         *
    060         * @param filename  the name of the file to write to, not null
    061         * @param encoding  the encoding to use, not null
    062         * @throws NullPointerException if the file name or encoding is null
    063         * @throws IOException in case of an I/O error
    064         */
    065        public FileWriterWithEncoding(String filename, String encoding) throws IOException {
    066            this(new File(filename), encoding, false);
    067        }
    068
    069        /**
    070         * Constructs a FileWriterWithEncoding with a file encoding.
    071         *
    072         * @param filename  the name of the file to write to, not null
    073         * @param encoding  the encoding to use, not null
    074         * @param append  true if content should be appended, false to overwrite
    075         * @throws NullPointerException if the file name or encoding is null
    076         * @throws IOException in case of an I/O error
    077         */
    078        public FileWriterWithEncoding(String filename, String encoding, boolean append) throws IOException {
    079            this(new File(filename), encoding, append);
    080        }
    081
    082        /**
    083         * Constructs a FileWriterWithEncoding with a file encoding.
    084         *
    085         * @param filename  the name of the file to write to, not null
    086         * @param encoding  the encoding to use, not null
    087         * @throws NullPointerException if the file name or encoding is null
    088         * @throws IOException in case of an I/O error
    089         */
    090        public FileWriterWithEncoding(String filename, Charset encoding) throws IOException {
    091            this(new File(filename), encoding, false);
    092        }
    093
    094        /**
    095         * Constructs a FileWriterWithEncoding with a file encoding.
    096         *
    097         * @param filename  the name of the file to write to, not null
    098         * @param encoding  the encoding to use, not null
    099         * @param append  true if content should be appended, false to overwrite
    100         * @throws NullPointerException if the file name or encoding is null
    101         * @throws IOException in case of an I/O error
    102         */
    103        public FileWriterWithEncoding(String filename, Charset encoding, boolean append) throws IOException {
    104            this(new File(filename), encoding, append);
    105        }
    106
    107        /**
    108         * Constructs a FileWriterWithEncoding with a file encoding.
    109         *
    110         * @param filename  the name of the file to write to, not null
    111         * @param encoding  the encoding to use, not null
    112         * @throws NullPointerException if the file name or encoding is null
    113         * @throws IOException in case of an I/O error
    114         */
    115        public FileWriterWithEncoding(String filename, CharsetEncoder encoding) throws IOException {
    116            this(new File(filename), encoding, false);
    117        }
    118
    119        /**
    120         * Constructs a FileWriterWithEncoding with a file encoding.
    121         *
    122         * @param filename  the name of the file to write to, not null
    123         * @param encoding  the encoding to use, not null
    124         * @param append  true if content should be appended, false to overwrite
    125         * @throws NullPointerException if the file name or encoding is null
    126         * @throws IOException in case of an I/O error
    127         */
    128        public FileWriterWithEncoding(String filename, CharsetEncoder encoding, boolean append) throws IOException {
    129            this(new File(filename), encoding, append);
    130        }
    131
    132        /**
    133         * Constructs a FileWriterWithEncoding with a file encoding.
    134         *
    135         * @param file  the file to write to, not null
    136         * @param encoding  the encoding to use, not null
    137         * @throws NullPointerException if the file or encoding is null
    138         * @throws IOException in case of an I/O error
    139         */
    140        public FileWriterWithEncoding(File file, String encoding) throws IOException {
    141            this(file, encoding, false);
    142        }
    143
    144        /**
    145         * Constructs a FileWriterWithEncoding with a file encoding.
    146         *
    147         * @param file  the file to write to, not null
    148         * @param encoding  the encoding to use, not null
    149         * @param append  true if content should be appended, false to overwrite
    150         * @throws NullPointerException if the file or encoding is null
    151         * @throws IOException in case of an I/O error
    152         */
    153        public FileWriterWithEncoding(File file, String encoding, boolean append) throws IOException {
    154            super();
    155            this.out = initWriter(file, encoding, append);
    156        }
    157
    158        /**
    159         * Constructs a FileWriterWithEncoding with a file encoding.
    160         *
    161         * @param file  the file to write to, not null
    162         * @param encoding  the encoding to use, not null
    163         * @throws NullPointerException if the file or encoding is null
    164         * @throws IOException in case of an I/O error
    165         */
    166        public FileWriterWithEncoding(File file, Charset encoding) throws IOException {
    167            this(file, encoding, false);
    168        }
    169
    170        /**
    171         * Constructs a FileWriterWithEncoding with a file encoding.
    172         *
    173         * @param file  the file to write to, not null
    174         * @param encoding  the encoding to use, not null
    175         * @param append  true if content should be appended, false to overwrite
    176         * @throws NullPointerException if the file or encoding is null
    177         * @throws IOException in case of an I/O error
    178         */
    179        public FileWriterWithEncoding(File file, Charset encoding, boolean append) throws IOException {
    180            super();
    181            this.out = initWriter(file, encoding, append);
    182        }
    183
    184        /**
    185         * Constructs a FileWriterWithEncoding with a file encoding.
    186         *
    187         * @param file  the file to write to, not null
    188         * @param encoding  the encoding to use, not null
    189         * @throws NullPointerException if the file or encoding is null
    190         * @throws IOException in case of an I/O error
    191         */
    192        public FileWriterWithEncoding(File file, CharsetEncoder encoding) throws IOException {
    193            this(file, encoding, false);
    194        }
    195
    196        /**
    197         * Constructs a FileWriterWithEncoding with a file encoding.
    198         *
    199         * @param file  the file to write to, not null
    200         * @param encoding  the encoding to use, not null
    201         * @param append  true if content should be appended, false to overwrite
    202         * @throws NullPointerException if the file or encoding is null
    203         * @throws IOException in case of an I/O error
    204         */
    205        public FileWriterWithEncoding(File file, CharsetEncoder encoding, boolean append) throws IOException {
    206            super();
    207            this.out = initWriter(file, encoding, append);
    208        }
    209
    210        //-----------------------------------------------------------------------
    211        /**
    212         * Initialise the wrapped file writer.
    213         * Ensure that a cleanup occurs if the writer creation fails.
    214         *
    215         * @param file  the file to be accessed
    216         * @param encoding  the encoding to use - may be Charset, CharsetEncoder or String
    217         * @param append  true to append
    218         * @return the initialised writer
    219         * @throws NullPointerException if the file or encoding is null
    220         * @throws IOException if an error occurs
    221         */
    222         private static Writer initWriter(File file, Object encoding, boolean append) throws IOException {
    223            if (file == null) {
    224                throw new NullPointerException("File is missing");
    225            }
    226            if (encoding == null) {
    227                throw new NullPointerException("Encoding is missing");
    228            }
    229            boolean fileExistedAlready = file.exists();
    230            OutputStream stream = null;
    231            Writer writer = null;
    232            try {
    233                stream = new FileOutputStream(file, append);
    234                if (encoding instanceof Charset) {
    235                    writer = new OutputStreamWriter(stream, (Charset)encoding);
    236                } else if (encoding instanceof CharsetEncoder) {
    237                    writer = new OutputStreamWriter(stream, (CharsetEncoder)encoding);
    238                } else {
    239                    writer = new OutputStreamWriter(stream, (String)encoding);
    240                }
    241            } catch (IOException ex) {
    242                IOUtils.closeQuietly(writer);
    243                IOUtils.closeQuietly(stream);
    244                if (fileExistedAlready == false) {
    245                    FileUtils.deleteQuietly(file);
    246                }
    247                throw ex;
    248            } catch (RuntimeException ex) {
    249                IOUtils.closeQuietly(writer);
    250                IOUtils.closeQuietly(stream);
    251                if (fileExistedAlready == false) {
    252                    FileUtils.deleteQuietly(file);
    253                }
    254                throw ex;
    255            }
    256            return writer;
    257        }
    258
    259        //-----------------------------------------------------------------------
    260        /**
    261         * Write a character.
    262         * @param idx the character to write
    263         * @throws IOException if an I/O error occurs
    264         */
    265         @Override
    266        public void write(int idx) throws IOException {
    267            out.write(idx);
    268        }
    269
    270        /**
    271         * Write the characters from an array.
    272         * @param chr the characters to write
    273         * @throws IOException if an I/O error occurs
    274         */
    275         @Override
    276        public void write(char[] chr) throws IOException {
    277            out.write(chr);
    278        }
    279
    280        /**
    281         * Write the specified characters from an array.
    282         * @param chr the characters to write
    283         * @param st The start offset
    284         * @param end The number of characters to write
    285         * @throws IOException if an I/O error occurs
    286         */
    287         @Override
    288        public void write(char[] chr, int st, int end) throws IOException {
    289            out.write(chr, st, end);
    290        }
    291
    292        /**
    293         * Write the characters from a string.
    294         * @param str the string to write
    295         * @throws IOException if an I/O error occurs
    296         */
    297         @Override
    298        public void write(String str) throws IOException {
    299            out.write(str);
    300        }
    301
    302        /**
    303         * Write the specified characters from a string.
    304         * @param str the string to write
    305         * @param st The start offset
    306         * @param end The number of characters to write
    307         * @throws IOException if an I/O error occurs
    308         */
    309         @Override
    310        public void write(String str, int st, int end) throws IOException {
    311            out.write(str, st, end);
    312        }
    313
    314        /**
    315         * Flush the stream.
    316         * @throws IOException if an I/O error occurs
    317         */
    318         @Override
    319        public void flush() throws IOException {
    320            out.flush();
    321        }
    322
    323        /**
    324         * Close the stream.
    325         * @throws IOException if an I/O error occurs
    326         */
    327         @Override
    328        public void close() throws IOException {
    329            out.close();
    330        }
    331    }