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 611634 2008-01-13 20:35:00Z 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        public void write(int idx) throws IOException {
266            out.write(idx);
267        }
268    
269        /**
270         * Write the characters from an array.
271         * @param chr the characters to write
272         * @throws IOException if an I/O error occurs
273         */
274        public void write(char[] chr) throws IOException {
275            out.write(chr);
276        }
277    
278        /**
279         * Write the specified characters from an array.
280         * @param chr the characters to write
281         * @param st The start offset
282         * @param end The number of characters to write
283         * @throws IOException if an I/O error occurs
284         */
285        public void write(char[] chr, int st, int end) throws IOException {
286            out.write(chr, st, end);
287        }
288    
289        /**
290         * Write the characters from a string.
291         * @param str the string to write
292         * @throws IOException if an I/O error occurs
293         */
294        public void write(String str) throws IOException {
295            out.write(str);
296        }
297    
298        /**
299         * Write the specified characters from a string.
300         * @param str the string to write
301         * @param st The start offset
302         * @param end The number of characters to write
303         * @throws IOException if an I/O error occurs
304         */
305        public void write(String str, int st, int end) throws IOException {
306            out.write(str, st, end);
307        }
308    
309        /**
310         * Flush the stream.
311         * @throws IOException if an I/O error occurs
312         */
313        public void flush() throws IOException {
314            out.flush();
315        }
316    
317        /**
318         * Close the stream.
319         * @throws IOException if an I/O error occurs
320         */
321        public void close() throws IOException {
322            out.close();
323        }
324    }