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 1.4 048 * @version $Id: FileWriterWithEncoding.java 1304052 2012-03-22 20:55:29Z ggregory $ 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 }