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 */ 017package org.apache.commons.io.output; 018 019import java.io.File; 020import java.io.FileOutputStream; 021import java.io.IOException; 022import java.io.OutputStream; 023import java.io.OutputStreamWriter; 024import java.io.Writer; 025import java.nio.charset.Charset; 026import java.nio.charset.CharsetEncoder; 027 028import org.apache.commons.io.FileUtils; 029import 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 1686747 2015-06-21 18:44:49Z krosenvold $ 049 */ 050public 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(final String filename, final 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(final String filename, final String encoding, final boolean append) 079 throws IOException { 080 this(new File(filename), encoding, append); 081 } 082 083 /** 084 * Constructs a FileWriterWithEncoding with a file encoding. 085 * 086 * @param filename the name of the file to write to, not null 087 * @param encoding the encoding to use, not null 088 * @throws NullPointerException if the file name or encoding is null 089 * @throws IOException in case of an I/O error 090 */ 091 public FileWriterWithEncoding(final String filename, final Charset encoding) throws IOException { 092 this(new File(filename), encoding, false); 093 } 094 095 /** 096 * Constructs a FileWriterWithEncoding with a file encoding. 097 * 098 * @param filename the name of the file to write to, not null 099 * @param encoding the encoding to use, not null 100 * @param append true if content should be appended, false to overwrite 101 * @throws NullPointerException if the file name or encoding is null 102 * @throws IOException in case of an I/O error 103 */ 104 public FileWriterWithEncoding(final String filename, final Charset encoding, final boolean append) 105 throws IOException { 106 this(new File(filename), encoding, append); 107 } 108 109 /** 110 * Constructs a FileWriterWithEncoding with a file encoding. 111 * 112 * @param filename the name of the file to write to, not null 113 * @param encoding the encoding to use, not null 114 * @throws NullPointerException if the file name or encoding is null 115 * @throws IOException in case of an I/O error 116 */ 117 public FileWriterWithEncoding(final String filename, final CharsetEncoder encoding) throws IOException { 118 this(new File(filename), encoding, false); 119 } 120 121 /** 122 * Constructs a FileWriterWithEncoding with a file encoding. 123 * 124 * @param filename the name of the file to write to, not null 125 * @param encoding the encoding to use, not null 126 * @param append true if content should be appended, false to overwrite 127 * @throws NullPointerException if the file name or encoding is null 128 * @throws IOException in case of an I/O error 129 */ 130 public FileWriterWithEncoding(final String filename, final CharsetEncoder encoding, final boolean append) 131 throws IOException { 132 this(new File(filename), encoding, append); 133 } 134 135 /** 136 * Constructs a FileWriterWithEncoding with a file encoding. 137 * 138 * @param file the file to write to, not null 139 * @param encoding the encoding to use, not null 140 * @throws NullPointerException if the file or encoding is null 141 * @throws IOException in case of an I/O error 142 */ 143 public FileWriterWithEncoding(final File file, final String encoding) throws IOException { 144 this(file, encoding, false); 145 } 146 147 /** 148 * Constructs a FileWriterWithEncoding with a file encoding. 149 * 150 * @param file the file to write to, not null 151 * @param encoding the encoding to use, not null 152 * @param append true if content should be appended, false to overwrite 153 * @throws NullPointerException if the file or encoding is null 154 * @throws IOException in case of an I/O error 155 */ 156 public FileWriterWithEncoding(final File file, final String encoding, final boolean append) throws IOException { 157 super(); 158 this.out = initWriter(file, encoding, append); 159 } 160 161 /** 162 * Constructs a FileWriterWithEncoding with a file encoding. 163 * 164 * @param file the file to write to, not null 165 * @param encoding the encoding to use, not null 166 * @throws NullPointerException if the file or encoding is null 167 * @throws IOException in case of an I/O error 168 */ 169 public FileWriterWithEncoding(final File file, final Charset encoding) throws IOException { 170 this(file, encoding, false); 171 } 172 173 /** 174 * Constructs a FileWriterWithEncoding with a file encoding. 175 * 176 * @param file the file to write to, not null 177 * @param encoding the encoding to use, not null 178 * @param append true if content should be appended, false to overwrite 179 * @throws NullPointerException if the file or encoding is null 180 * @throws IOException in case of an I/O error 181 */ 182 public FileWriterWithEncoding(final File file, final Charset encoding, final boolean append) throws IOException { 183 super(); 184 this.out = initWriter(file, encoding, append); 185 } 186 187 /** 188 * Constructs a FileWriterWithEncoding with a file encoding. 189 * 190 * @param file the file to write to, not null 191 * @param encoding the encoding to use, not null 192 * @throws NullPointerException if the file or encoding is null 193 * @throws IOException in case of an I/O error 194 */ 195 public FileWriterWithEncoding(final File file, final CharsetEncoder encoding) throws IOException { 196 this(file, encoding, false); 197 } 198 199 /** 200 * Constructs a FileWriterWithEncoding with a file encoding. 201 * 202 * @param file the file to write to, not null 203 * @param encoding the encoding to use, not null 204 * @param append true if content should be appended, false to overwrite 205 * @throws NullPointerException if the file or encoding is null 206 * @throws IOException in case of an I/O error 207 */ 208 public FileWriterWithEncoding(final File file, final CharsetEncoder encoding, final boolean append) 209 throws IOException { 210 super(); 211 this.out = initWriter(file, encoding, append); 212 } 213 214 //----------------------------------------------------------------------- 215 /** 216 * Initialise the wrapped file writer. 217 * Ensure that a cleanup occurs if the writer creation fails. 218 * 219 * @param file the file to be accessed 220 * @param encoding the encoding to use - may be Charset, CharsetEncoder or String 221 * @param append true to append 222 * @return the initialised writer 223 * @throws NullPointerException if the file or encoding is null 224 * @throws IOException if an error occurs 225 */ 226 private static Writer initWriter(final File file, final Object encoding, final boolean append) throws IOException { 227 if (file == null) { 228 throw new NullPointerException("File is missing"); 229 } 230 if (encoding == null) { 231 throw new NullPointerException("Encoding is missing"); 232 } 233 final boolean fileExistedAlready = file.exists(); 234 OutputStream stream = null; 235 Writer writer = null; 236 try { 237 stream = new FileOutputStream(file, append); 238 if (encoding instanceof Charset) { 239 writer = new OutputStreamWriter(stream, (Charset)encoding); 240 } else if (encoding instanceof CharsetEncoder) { 241 writer = new OutputStreamWriter(stream, (CharsetEncoder)encoding); 242 } else { 243 writer = new OutputStreamWriter(stream, (String)encoding); 244 } 245 } catch (final IOException ex) { 246 IOUtils.closeQuietly(writer); 247 IOUtils.closeQuietly(stream); 248 if (fileExistedAlready == false) { 249 FileUtils.deleteQuietly(file); 250 } 251 throw ex; 252 } catch (final RuntimeException ex) { 253 IOUtils.closeQuietly(writer); 254 IOUtils.closeQuietly(stream); 255 if (fileExistedAlready == false) { 256 FileUtils.deleteQuietly(file); 257 } 258 throw ex; 259 } 260 return writer; 261 } 262 263 //----------------------------------------------------------------------- 264 /** 265 * Write a character. 266 * @param idx the character to write 267 * @throws IOException if an I/O error occurs 268 */ 269 @Override 270 public void write(final int idx) throws IOException { 271 out.write(idx); 272 } 273 274 /** 275 * Write the characters from an array. 276 * @param chr the characters to write 277 * @throws IOException if an I/O error occurs 278 */ 279 @Override 280 public void write(final char[] chr) throws IOException { 281 out.write(chr); 282 } 283 284 /** 285 * Write the specified characters from an array. 286 * @param chr the characters to write 287 * @param st The start offset 288 * @param end The number of characters to write 289 * @throws IOException if an I/O error occurs 290 */ 291 @Override 292 public void write(final char[] chr, final int st, final int end) throws IOException { 293 out.write(chr, st, end); 294 } 295 296 /** 297 * Write the characters from a string. 298 * @param str the string to write 299 * @throws IOException if an I/O error occurs 300 */ 301 @Override 302 public void write(final String str) throws IOException { 303 out.write(str); 304 } 305 306 /** 307 * Write the specified characters from a string. 308 * @param str the string to write 309 * @param st The start offset 310 * @param end The number of characters to write 311 * @throws IOException if an I/O error occurs 312 */ 313 @Override 314 public void write(final String str, final int st, final int end) throws IOException { 315 out.write(str, st, end); 316 } 317 318 /** 319 * Flush the stream. 320 * @throws IOException if an I/O error occurs 321 */ 322 @Override 323 public void flush() throws IOException { 324 out.flush(); 325 } 326 327 /** 328 * Close the stream. 329 * @throws IOException if an I/O error occurs 330 */ 331 @Override 332 public void close() throws IOException { 333 out.close(); 334 } 335}