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.juneau.commons.io; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020 021import java.io.*; 022import java.nio.charset.*; 023 024/** 025 * A fluent builder for creating {@link Writer} instances for writing to files with configurable options. 026 * 027 * <p> 028 * This builder provides a convenient way to create file writers with custom character encodings, 029 * append mode, and buffering options. It's particularly useful when you need to write files with 030 * specific encodings or control whether to append to existing files. 031 * 032 * <h5 class='section'>Features:</h5> 033 * <ul class='spaced-list'> 034 * <li>Fluent API - all methods return <c>this</c> for method chaining 035 * <li>Character encoding support - specify custom charset for file writing 036 * <li>Append mode - optionally append to existing files instead of overwriting 037 * <li>Buffering support - optional buffering for improved performance 038 * <li>Multiple file specification methods - accept File or String path 039 * </ul> 040 * 041 * <h5 class='section'>Use Cases:</h5> 042 * <ul class='spaced-list'> 043 * <li>Writing files with specific character encodings (UTF-8, ISO-8859-1, etc.) 044 * <li>Appending to log files or data files 045 * <li>Creating writers with consistent encoding across an application 046 * <li>Writing files where buffering improves performance 047 * </ul> 048 * 049 * <h5 class='section'>Usage:</h5> 050 * <p class='bjava'> 051 * <jc>// Basic usage</jc> 052 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 053 * .file(<js>"/path/to/file.txt"</js>) 054 * .charset(<js>"UTF-8"</js>) 055 * .build(); 056 * 057 * <jc>// Append mode</jc> 058 * Writer <jv>logWriter</jv> = FileWriterBuilder.<jsm>create</jsm>() 059 * .file(<js>"app.log"</js>) 060 * .append() 061 * .build(); 062 * 063 * <jc>// With buffering</jc> 064 * Writer <jv>bufferedWriter</jv> = FileWriterBuilder.<jsm>create</jsm>() 065 * .file(<js>"output.txt"</js>) 066 * .buffered() 067 * .charset(StandardCharsets.UTF_8) 068 * .build(); 069 * </p> 070 * 071 * <h5 class='section'>Character Encoding:</h5> 072 * <p> 073 * By default, the builder uses the system's default charset ({@link Charset#defaultCharset()}). 074 * You can specify a custom charset using {@link #charset(Charset)} or {@link #charset(String)}. 075 * This is important when writing files that need to be read with a specific encoding. 076 * 077 * <h5 class='section'>See Also:</h5><ul> 078 * <li class='jc'>{@link FileReaderBuilder} - Builder for file readers 079 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsIO">I/O Package</a> 080 * </ul> 081 */ 082public class FileWriterBuilder { 083 084 /** 085 * Creates a new builder. 086 * 087 * <h5 class='section'>Example:</h5> 088 * <p class='bjava'> 089 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 090 * .file(<js>"output.txt"</js>) 091 * .build(); 092 * </p> 093 * 094 * @return A new builder instance. 095 */ 096 public static FileWriterBuilder create() { 097 return new FileWriterBuilder(); 098 } 099 100 /** 101 * Creates a new builder initialized with the specified file. 102 * 103 * <h5 class='section'>Example:</h5> 104 * <p class='bjava'> 105 * File <jv>file</jv> = <jk>new</jk> File(<js>"output.txt"</js>); 106 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>(<jv>file</jv>) 107 * .charset(<js>"UTF-8"</js>) 108 * .build(); 109 * </p> 110 * 111 * @param file The file to write to. 112 * @return A new builder instance initialized with the specified file. 113 */ 114 public static FileWriterBuilder create(File file) { 115 return new FileWriterBuilder().file(file); 116 } 117 118 /** 119 * Creates a new builder initialized with the specified file path. 120 * 121 * <h5 class='section'>Example:</h5> 122 * <p class='bjava'> 123 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>(<js>"/path/to/output.txt"</js>) 124 * .append() 125 * .build(); 126 * </p> 127 * 128 * @param path The file path to write to. 129 * @return A new builder instance initialized with the specified path. 130 */ 131 public static FileWriterBuilder create(String path) { 132 return new FileWriterBuilder().file(path); 133 } 134 135 private File file; 136 137 private Charset cs = Charset.defaultCharset(); 138 139 private boolean append, buffered; 140 141 /** 142 * Enables append mode, which appends to the file instead of overwriting it. 143 * 144 * <p> 145 * When append mode is enabled, data written to the file will be appended to the end of the 146 * existing file content rather than overwriting it. This is useful for log files or data 147 * files where you want to preserve existing content. 148 * 149 * <h5 class='section'>Example:</h5> 150 * <p class='bjava'> 151 * <jc>// Append to log file</jc> 152 * Writer <jv>logWriter</jv> = FileWriterBuilder.<jsm>create</jsm>() 153 * .file(<js>"app.log"</js>) 154 * .append() 155 * .build(); 156 * <jv>logWriter</jv>.write(<js>"New log entry\n"</js>); <jc>// Appends to existing content</jc> 157 * </p> 158 * 159 * @return This object for method chaining. 160 */ 161 public FileWriterBuilder append() { 162 this.append = true; 163 return this; 164 } 165 166 /** 167 * Enables buffering for improved write performance. 168 * 169 * <p> 170 * When buffering is enabled, the writer wraps the underlying output stream with a 171 * {@link BufferedOutputStream}, which can significantly improve performance for multiple 172 * small writes by batching them together. 173 * 174 * <h5 class='section'>Example:</h5> 175 * <p class='bjava'> 176 * <jc>// Buffered writer for better performance</jc> 177 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 178 * .file(<js>"output.txt"</js>) 179 * .buffered() 180 * .build(); 181 * </p> 182 * 183 * @return This object for method chaining. 184 */ 185 public FileWriterBuilder buffered() { 186 this.buffered = true; 187 return this; 188 } 189 190 /** 191 * Creates a new {@link Writer} for writing to the configured file. 192 * 193 * <p> 194 * The writer is created with the specified character encoding, append mode, and buffering 195 * options. The file will be created if it doesn't exist (unless in append mode, where the 196 * file must exist or be creatable). 197 * 198 * <h5 class='section'>Example:</h5> 199 * <p class='bjava'> 200 * <jk>try</jk> (Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 201 * .file(<js>"output.txt"</js>) 202 * .charset(<js>"UTF-8"</js>) 203 * .buffered() 204 * .build()) { 205 * <jv>writer</jv>.write(<js>"Hello World"</js>); 206 * } 207 * </p> 208 * 209 * @return A new {@link Writer} for writing to the file. 210 * @throws FileNotFoundException If the file could not be created or opened for writing. 211 */ 212 public Writer build() throws FileNotFoundException { 213 assertArgNotNull("file", file); 214 var os = (OutputStream)new FileOutputStream(file, append); 215 if (buffered) 216 os = new BufferedOutputStream(os); 217 return new OutputStreamWriter(os, cs != null ? cs : Charset.defaultCharset()); 218 } 219 220 /** 221 * Sets the character encoding for writing to the file. 222 * 223 * <p> 224 * If not specified, the system's default charset ({@link Charset#defaultCharset()}) is used. 225 * Specifying the encoding is important when writing files that need to be read with a specific 226 * character encoding. 227 * 228 * <h5 class='section'>Example:</h5> 229 * <p class='bjava'> 230 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 231 * .file(<js>"data.txt"</js>) 232 * .charset(StandardCharsets.UTF_8) 233 * .build(); 234 * </p> 235 * 236 * @param cs The character encoding to use. The default is {@link Charset#defaultCharset()}. 237 * @return This object for method chaining. 238 */ 239 public FileWriterBuilder charset(Charset cs) { 240 this.cs = cs; 241 return this; 242 } 243 244 /** 245 * Sets the character encoding for writing to the file by charset name. 246 * 247 * <p> 248 * This is a convenience method that accepts a charset name string and converts it to a 249 * {@link Charset} using {@link Charset#forName(String)}. 250 * 251 * <h5 class='section'>Example:</h5> 252 * <p class='bjava'> 253 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 254 * .file(<js>"data.txt"</js>) 255 * .charset(<js>"UTF-8"</js>) 256 * .build(); 257 * </p> 258 * 259 * @param cs The character encoding name (e.g., <js>"UTF-8"</js>, <js>"ISO-8859-1"</js>). 260 * The default is {@link Charset#defaultCharset()}. 261 * Must not be <jk>null</jk>. 262 * @return This object for method chaining. 263 */ 264 public FileWriterBuilder charset(String cs) { 265 this.cs = Charset.forName(assertArgNotNull("cs", cs)); 266 return this; 267 } 268 269 /** 270 * Sets the file to write to. 271 * 272 * <h5 class='section'>Example:</h5> 273 * <p class='bjava'> 274 * File <jv>f</jv> = <jk>new</jk> File(<js>"output.txt"</js>); 275 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 276 * .file(<jv>f</jv>) 277 * .build(); 278 * </p> 279 * 280 * @param value The file to write to. 281 * @return This object for method chaining. 282 */ 283 public FileWriterBuilder file(File value) { 284 file = value; 285 return this; 286 } 287 288 /** 289 * Sets the file path to write to. 290 * 291 * <h5 class='section'>Example:</h5> 292 * <p class='bjava'> 293 * Writer <jv>writer</jv> = FileWriterBuilder.<jsm>create</jsm>() 294 * .file(<js>"/path/to/output.txt"</js>) 295 * .build(); 296 * </p> 297 * 298 * @param path The file path to write to. 299 * @return This object for method chaining. 300 */ 301 public FileWriterBuilder file(String path) { 302 this.file = new File(path); 303 return this; 304 } 305}