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; 029 030/** 031 * Writer of files that allows the encoding to be set. 032 * <p> 033 * This class provides a simple alternative to <code>FileWriter</code> 034 * that allows an encoding to be set. Unfortunately, it cannot subclass 035 * <code>FileWriter</code>. 036 * <p> 037 * By default, the file will be overwritten, but this may be changed to append. 038 * <p> 039 * The encoding must be specified using either the name of the {@link Charset}, 040 * the {@link Charset}, or a {@link CharsetEncoder}. If the default encoding 041 * is required then use the {@link java.io.FileWriter} directly, rather than 042 * this implementation. 043 * <p> 044 * 045 * 046 * @since 1.4 047 * @version $Id$ 048 */ 049public class FileWriterWithEncoding extends Writer { 050 // Cannot extend ProxyWriter, as requires writer to be 051 // known when super() is called 052 053 /** The writer to decorate. */ 054 private final Writer out; 055 056 /** 057 * Constructs a FileWriterWithEncoding with a file encoding. 058 * 059 * @param filename the name of the file to write to, not null 060 * @param encoding the encoding to use, not null 061 * @throws NullPointerException if the file name or encoding is null 062 * @throws IOException in case of an I/O error 063 */ 064 public FileWriterWithEncoding(final String filename, final String encoding) throws IOException { 065 this(new File(filename), encoding, false); 066 } 067 068 /** 069 * Constructs a FileWriterWithEncoding with a file encoding. 070 * 071 * @param filename the name of the file to write to, not null 072 * @param encoding the encoding to use, not null 073 * @param append true if content should be appended, false to overwrite 074 * @throws NullPointerException if the file name or encoding is null 075 * @throws IOException in case of an I/O error 076 */ 077 public FileWriterWithEncoding(final String filename, final String encoding, final boolean append) 078 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(final String filename, final 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(final String filename, final Charset encoding, final boolean append) 104 throws IOException { 105 this(new File(filename), encoding, append); 106 } 107 108 /** 109 * Constructs a FileWriterWithEncoding with a file encoding. 110 * 111 * @param filename the name of the file to write to, not null 112 * @param encoding the encoding to use, not null 113 * @throws NullPointerException if the file name or encoding is null 114 * @throws IOException in case of an I/O error 115 */ 116 public FileWriterWithEncoding(final String filename, final CharsetEncoder encoding) throws IOException { 117 this(new File(filename), encoding, false); 118 } 119 120 /** 121 * Constructs a FileWriterWithEncoding with a file encoding. 122 * 123 * @param filename the name of the file to write to, not null 124 * @param encoding the encoding to use, not null 125 * @param append true if content should be appended, false to overwrite 126 * @throws NullPointerException if the file name or encoding is null 127 * @throws IOException in case of an I/O error 128 */ 129 public FileWriterWithEncoding(final String filename, final CharsetEncoder encoding, final boolean append) 130 throws IOException { 131 this(new File(filename), encoding, append); 132 } 133 134 /** 135 * Constructs a FileWriterWithEncoding with a file encoding. 136 * 137 * @param file the file to write to, not null 138 * @param encoding the encoding to use, not null 139 * @throws NullPointerException if the file or encoding is null 140 * @throws IOException in case of an I/O error 141 */ 142 public FileWriterWithEncoding(final File file, final String encoding) throws IOException { 143 this(file, encoding, false); 144 } 145 146 /** 147 * Constructs a FileWriterWithEncoding with a file encoding. 148 * 149 * @param file the file to write to, not null 150 * @param encoding the encoding to use, not null 151 * @param append true if content should be appended, false to overwrite 152 * @throws NullPointerException if the file or encoding is null 153 * @throws IOException in case of an I/O error 154 */ 155 public FileWriterWithEncoding(final File file, final String encoding, final boolean append) throws IOException { 156 super(); 157 this.out = initWriter(file, encoding, append); 158 } 159 160 /** 161 * Constructs a FileWriterWithEncoding with a file encoding. 162 * 163 * @param file the file to write to, not null 164 * @param encoding the encoding to use, not null 165 * @throws NullPointerException if the file or encoding is null 166 * @throws IOException in case of an I/O error 167 */ 168 public FileWriterWithEncoding(final File file, final Charset encoding) throws IOException { 169 this(file, encoding, false); 170 } 171 172 /** 173 * Constructs a FileWriterWithEncoding with a file encoding. 174 * 175 * @param file the file to write to, not null 176 * @param encoding the encoding to use, not null 177 * @param append true if content should be appended, false to overwrite 178 * @throws NullPointerException if the file or encoding is null 179 * @throws IOException in case of an I/O error 180 */ 181 public FileWriterWithEncoding(final File file, final Charset encoding, final boolean append) throws IOException { 182 super(); 183 this.out = initWriter(file, encoding, append); 184 } 185 186 /** 187 * Constructs a FileWriterWithEncoding with a file encoding. 188 * 189 * @param file the file to write to, not null 190 * @param encoding the encoding to use, not null 191 * @throws NullPointerException if the file or encoding is null 192 * @throws IOException in case of an I/O error 193 */ 194 public FileWriterWithEncoding(final File file, final CharsetEncoder encoding) throws IOException { 195 this(file, encoding, false); 196 } 197 198 /** 199 * Constructs a FileWriterWithEncoding with a file encoding. 200 * 201 * @param file the file to write to, not null 202 * @param encoding the encoding to use, not null 203 * @param append true if content should be appended, false to overwrite 204 * @throws NullPointerException if the file or encoding is null 205 * @throws IOException in case of an I/O error 206 */ 207 public FileWriterWithEncoding(final File file, final CharsetEncoder encoding, final boolean append) 208 throws IOException { 209 super(); 210 this.out = initWriter(file, encoding, append); 211 } 212 213 //----------------------------------------------------------------------- 214 /** 215 * Initialise the wrapped file writer. 216 * Ensure that a cleanup occurs if the writer creation fails. 217 * 218 * @param file the file to be accessed 219 * @param encoding the encoding to use - may be Charset, CharsetEncoder or String 220 * @param append true to append 221 * @return the initialised writer 222 * @throws NullPointerException if the file or encoding is null 223 * @throws IOException if an error occurs 224 */ 225 private static Writer initWriter(final File file, final Object encoding, final boolean append) throws IOException { 226 if (file == null) { 227 throw new NullPointerException("File is missing"); 228 } 229 if (encoding == null) { 230 throw new NullPointerException("Encoding is missing"); 231 } 232 OutputStream stream = null; 233 final boolean fileExistedAlready = file.exists(); 234 try { 235 stream = new FileOutputStream(file, append); 236 if (encoding instanceof Charset) { 237 return new OutputStreamWriter(stream, (Charset)encoding); 238 } else if (encoding instanceof CharsetEncoder) { 239 return new OutputStreamWriter(stream, (CharsetEncoder)encoding); 240 } else { 241 return new OutputStreamWriter(stream, (String)encoding); 242 } 243 } catch (final IOException | RuntimeException ex) { 244 try { 245 if (stream != null) { 246 stream.close(); 247 } 248 } catch (final IOException e) { 249 ex.addSuppressed(e); 250 } 251 if (fileExistedAlready == false) { 252 FileUtils.deleteQuietly(file); 253 } 254 throw ex; 255 } 256 } 257 258 //----------------------------------------------------------------------- 259 /** 260 * Write a character. 261 * @param idx the character to write 262 * @throws IOException if an I/O error occurs 263 */ 264 @Override 265 public void write(final 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 @Override 275 public void write(final char[] chr) throws IOException { 276 out.write(chr); 277 } 278 279 /** 280 * Write the specified characters from an array. 281 * @param chr the characters to write 282 * @param st The start offset 283 * @param end The number of characters to write 284 * @throws IOException if an I/O error occurs 285 */ 286 @Override 287 public void write(final char[] chr, final int st, final int end) throws IOException { 288 out.write(chr, st, end); 289 } 290 291 /** 292 * Write the characters from a string. 293 * @param str the string to write 294 * @throws IOException if an I/O error occurs 295 */ 296 @Override 297 public void write(final String str) throws IOException { 298 out.write(str); 299 } 300 301 /** 302 * Write the specified characters from a string. 303 * @param str the string to write 304 * @param st The start offset 305 * @param end The number of characters to write 306 * @throws IOException if an I/O error occurs 307 */ 308 @Override 309 public void write(final String str, final int st, final int end) throws IOException { 310 out.write(str, st, end); 311 } 312 313 /** 314 * Flush the stream. 315 * @throws IOException if an I/O error occurs 316 */ 317 @Override 318 public void flush() throws IOException { 319 out.flush(); 320 } 321 322 /** 323 * Close the stream. 324 * @throws IOException if an I/O error occurs 325 */ 326 @Override 327 public void close() throws IOException { 328 out.close(); 329 } 330}