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} 036 * that allows an encoding to be set. Unfortunately, it cannot subclass 037 * {@code FileWriter}. 038 * </p> 039 * <p> 040 * By default, the file will be overwritten, but this may be changed to append. 041 * </p> 042 * <p> 043 * The encoding must be specified using either the name of the {@link Charset}, 044 * the {@link Charset}, or a {@link CharsetEncoder}. If the default encoding 045 * is required then use the {@link java.io.FileWriter} directly, rather than 046 * this implementation. 047 * </p> 048 * 049 * @since 1.4 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 this.out = initWriter(file, charsetName, 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 charset 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 charset) throws IOException { 170 this(file, charset, 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 name of the requested charset, 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 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 charsetEncoder 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 charsetEncoder) throws IOException { 195 this(file, charsetEncoder, 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 charsetEncoder 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 charsetEncoder, final boolean append) 208 throws IOException { 209 this.out = initWriter(file, charsetEncoder, append); 210 } 211 212 //----------------------------------------------------------------------- 213 /** 214 * Initialize the wrapped file writer. 215 * Ensure that a cleanup occurs if the writer creation fails. 216 * 217 * @param file the file to be accessed 218 * @param encoding the encoding to use - may be Charset, CharsetEncoder or String 219 * @param append true to append 220 * @return the initialized writer 221 * @throws NullPointerException if the file or encoding is null 222 * @throws IOException if an error occurs 223 */ 224 private static Writer initWriter(final File file, final Object encoding, final boolean append) throws IOException { 225 Objects.requireNonNull(file, "file"); 226 Objects.requireNonNull(encoding, "encoding"); 227 OutputStream stream = null; 228 final boolean fileExistedAlready = file.exists(); 229 try { 230 stream = new FileOutputStream(file, append); 231 if (encoding instanceof Charset) { 232 return new OutputStreamWriter(stream, (Charset)encoding); 233 } 234 if (encoding instanceof CharsetEncoder) { 235 return new OutputStreamWriter(stream, (CharsetEncoder)encoding); 236 } 237 return new OutputStreamWriter(stream, (String)encoding); 238 } catch (final IOException | RuntimeException ex) { 239 try { 240 IOUtils.close(stream); 241 } catch (final IOException e) { 242 ex.addSuppressed(e); 243 } 244 if (!fileExistedAlready) { 245 FileUtils.deleteQuietly(file); 246 } 247 throw ex; 248 } 249 } 250 251 //----------------------------------------------------------------------- 252 /** 253 * Write a character. 254 * @param idx the character to write 255 * @throws IOException if an I/O error occurs. 256 */ 257 @Override 258 public void write(final int idx) throws IOException { 259 out.write(idx); 260 } 261 262 /** 263 * Write the characters from an array. 264 * @param chr the characters to write 265 * @throws IOException if an I/O error occurs. 266 */ 267 @Override 268 public void write(final char[] chr) throws IOException { 269 out.write(chr); 270 } 271 272 /** 273 * Write the specified characters from an array. 274 * @param chr the characters to write 275 * @param st The start offset 276 * @param end The number of characters to write 277 * @throws IOException if an I/O error occurs. 278 */ 279 @Override 280 public void write(final char[] chr, final int st, final int end) throws IOException { 281 out.write(chr, st, end); 282 } 283 284 /** 285 * Write the characters from a string. 286 * @param str the string to write 287 * @throws IOException if an I/O error occurs. 288 */ 289 @Override 290 public void write(final String str) throws IOException { 291 out.write(str); 292 } 293 294 /** 295 * Write the specified characters from a string. 296 * @param str the string to write 297 * @param st The start offset 298 * @param end The number of characters to write 299 * @throws IOException if an I/O error occurs. 300 */ 301 @Override 302 public void write(final String str, final int st, final int end) throws IOException { 303 out.write(str, st, end); 304 } 305 306 /** 307 * Flush the stream. 308 * @throws IOException if an I/O error occurs. 309 */ 310 @Override 311 public void flush() throws IOException { 312 out.flush(); 313 } 314 315 /** 316 * Close the stream. 317 * @throws IOException if an I/O error occurs. 318 */ 319 @Override 320 public void close() throws IOException { 321 out.close(); 322 } 323}