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