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 * https://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 */ 017 018package org.apache.commons.io.build; 019 020import java.io.File; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.io.RandomAccessFile; 025import java.io.Reader; 026import java.io.Writer; 027import java.nio.channels.Channel; 028import java.nio.channels.ReadableByteChannel; 029import java.nio.charset.Charset; 030import java.nio.file.OpenOption; 031import java.nio.file.Path; 032import java.util.function.IntUnaryOperator; 033 034import org.apache.commons.io.Charsets; 035import org.apache.commons.io.IOUtils; 036import org.apache.commons.io.file.PathUtils; 037 038/** 039 * Abstracts <em>building</em> a typed instance of type {@code T} where {@code T} is unbounded. This class contains various properties like a buffer size, 040 * buffer size checker, a buffer size default, buffer size maximum, Charset, Charset default, default size checker, and open options. A subclass may use all, 041 * some, or none of these properties in building instances of {@code T}. 042 * 043 * @param <T> the type of instances to build. 044 * @param <B> the type of builder subclass. 045 * @since 2.12.0 046 */ 047public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>> extends AbstractOriginSupplier<T, B> { 048 049 private static final int DEFAULT_MAX_VALUE = Integer.MAX_VALUE; 050 051 private static final OpenOption[] DEFAULT_OPEN_OPTIONS = PathUtils.EMPTY_OPEN_OPTION_ARRAY; 052 053 /** 054 * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}). 055 */ 056 private int bufferSize = IOUtils.DEFAULT_BUFFER_SIZE; 057 058 /** 059 * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}). 060 */ 061 private int bufferSizeDefault = IOUtils.DEFAULT_BUFFER_SIZE; 062 063 /** 064 * The maximum buffer size. 065 */ 066 private int bufferSizeMax = DEFAULT_MAX_VALUE; 067 068 /** 069 * The Charset, defaults to {@link Charset#defaultCharset()}. 070 */ 071 private Charset charset = Charset.defaultCharset(); 072 073 /** 074 * The Charset, defaults to {@link Charset#defaultCharset()}. 075 */ 076 private Charset charsetDefault = Charset.defaultCharset(); 077 078 private OpenOption[] openOptions = DEFAULT_OPEN_OPTIONS; 079 080 /** 081 * The default checking behavior for a buffer size request. Throws a {@link IllegalArgumentException} by default. 082 */ 083 private final IntUnaryOperator defaultSizeChecker = size -> size > bufferSizeMax ? throwIae(size, bufferSizeMax) : size; 084 085 /** 086 * The checking behavior for a buffer size request. 087 */ 088 private IntUnaryOperator bufferSizeChecker = defaultSizeChecker; 089 090 /** 091 * Constructs a new instance for subclasses. 092 */ 093 public AbstractStreamBuilder() { 094 // empty 095 } 096 097 /** 098 * Applies the buffer size request. 099 * 100 * @param size the size request. 101 * @return the size to use, usually the input, or can throw an unchecked exception, like {@link IllegalArgumentException}. 102 */ 103 private int checkBufferSize(final int size) { 104 return bufferSizeChecker.applyAsInt(size); 105 } 106 107 /** 108 * Gets the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}). 109 * 110 * @return the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}). 111 */ 112 public int getBufferSize() { 113 return bufferSize; 114 } 115 116 /** 117 * Gets the buffer size default, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}). 118 * 119 * @return the buffer size default, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}). 120 */ 121 public int getBufferSizeDefault() { 122 return bufferSizeDefault; 123 } 124 125 /** 126 * Gets a Channel from the origin with OpenOption[]. 127 * 128 * @param channelType The channel type, not null. 129 * @return A channel of the specified type. 130 * @param <C> The channel type. 131 * @throws IllegalStateException if the {@code origin} is {@code null}. 132 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link ReadableByteChannel}. 133 * @throws IOException if an I/O error occurs. 134 * @see AbstractOrigin#getChannel 135 * @see #getOpenOptions() 136 * @since 2.21.0 137 */ 138 public <C extends Channel> C getChannel(final Class<C> channelType) throws IOException { 139 return checkOrigin().getChannel(channelType, getOpenOptions()); 140 } 141 142 /** 143 * Gets a CharSequence from the origin with a Charset. 144 * 145 * @return An input stream 146 * @throws IllegalStateException if the {@code origin} is {@code null}. 147 * @throws UnsupportedOperationException if the origin cannot be converted to a CharSequence. 148 * @throws IOException if an I/O error occurs. 149 * @see AbstractOrigin#getCharSequence(Charset) 150 * @since 2.13.0 151 */ 152 public CharSequence getCharSequence() throws IOException { 153 return checkOrigin().getCharSequence(getCharset()); 154 } 155 156 /** 157 * Gets the Charset, defaults to {@link Charset#defaultCharset()}. 158 * 159 * @return the Charset, defaults to {@link Charset#defaultCharset()}. 160 */ 161 public Charset getCharset() { 162 return charset; 163 } 164 165 /** 166 * Gets the Charset default, defaults to {@link Charset#defaultCharset()}. 167 * 168 * @return the Charset default, defaults to {@link Charset#defaultCharset()}. 169 */ 170 public Charset getCharsetDefault() { 171 return charsetDefault; 172 } 173 174 /** 175 * Gets a File from the origin. 176 * 177 * @return A File 178 * @throws IllegalStateException if the {@code origin} is {@code null}. 179 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link File}. 180 * @see AbstractOrigin#getPath() 181 * @since 2.18.0 182 */ 183 public File getFile() { 184 return checkOrigin().getFile(); 185 } 186 187 /** 188 * Gets an InputStream from the origin with OpenOption[]. 189 * 190 * @return An input stream 191 * @throws IllegalStateException if the {@code origin} is {@code null}. 192 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}. 193 * @throws IOException if an I/O error occurs. 194 * @see AbstractOrigin#getInputStream(OpenOption...) 195 * @see #getOpenOptions() 196 * @since 2.13.0 197 */ 198 public InputStream getInputStream() throws IOException { 199 return checkOrigin().getInputStream(getOpenOptions()); 200 } 201 202 /** 203 * Gets the OpenOption array. 204 * 205 * @return the OpenOption array. 206 */ 207 public OpenOption[] getOpenOptions() { 208 return openOptions; 209 } 210 211 /** 212 * Gets an OutputStream from the origin with OpenOption[]. 213 * 214 * @return An OutputStream 215 * @throws IllegalStateException if the {@code origin} is {@code null}. 216 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}. 217 * @throws IOException if an I/O error occurs. 218 * @see AbstractOrigin#getOutputStream(OpenOption...) 219 * @see #getOpenOptions() 220 * @since 2.13.0 221 */ 222 public OutputStream getOutputStream() throws IOException { 223 return checkOrigin().getOutputStream(getOpenOptions()); 224 } 225 226 /** 227 * Gets a Path from the origin. 228 * 229 * @return A Path 230 * @throws IllegalStateException if the {@code origin} is {@code null}. 231 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Path}. 232 * @see AbstractOrigin#getPath() 233 * @since 2.13.0 234 */ 235 public Path getPath() { 236 return checkOrigin().getPath(); 237 } 238 239 /** 240 * Gets a RandomAccessFile from the origin. 241 * 242 * @return A RandomAccessFile 243 * @throws IllegalStateException if the {@code origin} is {@code null}. 244 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link RandomAccessFile}. 245 * @throws IOException if an I/O error occurs. 246 * @since 2.18.0 247 */ 248 public RandomAccessFile getRandomAccessFile() throws IOException { 249 return checkOrigin().getRandomAccessFile(getOpenOptions()); 250 } 251 252 /** 253 * Gets a Reader from the origin with a Charset. 254 * 255 * @return A Reader 256 * @throws IllegalStateException if the {@code origin} is {@code null}. 257 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Reader}. 258 * @throws IOException if an I/O error occurs. 259 * @see AbstractOrigin#getReader(Charset) 260 * @see #getCharset() 261 * @since 2.16.0 262 */ 263 public Reader getReader() throws IOException { 264 return checkOrigin().getReader(getCharset()); 265 } 266 267 /** 268 * Gets a Writer from the origin with an OpenOption[]. 269 * 270 * @return An writer. 271 * @throws IllegalStateException if the {@code origin} is {@code null}. 272 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Writer}. 273 * @throws IOException if an I/O error occurs. 274 * @see AbstractOrigin#getOutputStream(OpenOption...) 275 * @see #getOpenOptions() 276 * @since 2.13.0 277 */ 278 public Writer getWriter() throws IOException { 279 return checkOrigin().getWriter(getCharset(), getOpenOptions()); 280 } 281 282 /** 283 * Sets the buffer size. Invalid input (bufferSize <= 0) resets the value to its default. 284 * <p> 285 * Subclasses may ignore this setting. 286 * </p> 287 * 288 * @param bufferSize the buffer size. 289 * @return {@code this} instance. 290 */ 291 public B setBufferSize(final int bufferSize) { 292 this.bufferSize = checkBufferSize(bufferSize > 0 ? bufferSize : bufferSizeDefault); 293 return asThis(); 294 } 295 296 /** 297 * Sets the buffer size. 298 * <p> 299 * Subclasses may ignore this setting. 300 * </p> 301 * 302 * @param bufferSize the buffer size, null resets to the default. 303 * @return {@code this} instance. 304 */ 305 public B setBufferSize(final Integer bufferSize) { 306 setBufferSize(bufferSize != null ? bufferSize : bufferSizeDefault); 307 return asThis(); 308 } 309 310 /** 311 * Sets the buffer size checker function. Throws a {@link IllegalArgumentException} by default. 312 * 313 * @param bufferSizeChecker the buffer size checker function. null resets to the default behavior. 314 * @return {@code this} instance. 315 * @since 2.14.0 316 */ 317 public B setBufferSizeChecker(final IntUnaryOperator bufferSizeChecker) { 318 this.bufferSizeChecker = bufferSizeChecker != null ? bufferSizeChecker : defaultSizeChecker; 319 return asThis(); 320 } 321 322 /** 323 * Sets the buffer size for subclasses to initialize. 324 * <p> 325 * Subclasses may ignore this setting. 326 * </p> 327 * 328 * @param bufferSizeDefault the buffer size, null resets to the default. 329 * @return {@code this} instance. 330 */ 331 protected B setBufferSizeDefault(final int bufferSizeDefault) { 332 this.bufferSizeDefault = bufferSizeDefault; 333 return asThis(); 334 } 335 336 /** 337 * The maximum buffer size checked by the buffer size checker. Values less or equal to 0, resets to the int max value. By default, if this value is 338 * exceeded, this methods throws an {@link IllegalArgumentException}. 339 * 340 * @param bufferSizeMax maximum buffer size checked by the buffer size checker. 341 * @return {@code this} instance. 342 * @since 2.14.0 343 */ 344 public B setBufferSizeMax(final int bufferSizeMax) { 345 this.bufferSizeMax = bufferSizeMax > 0 ? bufferSizeMax : DEFAULT_MAX_VALUE; 346 return asThis(); 347 } 348 349 /** 350 * Sets the Charset. 351 * <p> 352 * Subclasses may ignore this setting. 353 * </p> 354 * 355 * @param charset the Charset, null resets to the default. 356 * @return {@code this} instance. 357 */ 358 public B setCharset(final Charset charset) { 359 this.charset = Charsets.toCharset(charset, charsetDefault); 360 return asThis(); 361 } 362 363 /** 364 * Sets the Charset. 365 * <p> 366 * Subclasses may ignore this setting. 367 * </p> 368 * 369 * @param charset the Charset name, null resets to the default. 370 * @return {@code this} instance. 371 */ 372 public B setCharset(final String charset) { 373 return setCharset(Charsets.toCharset(charset, charsetDefault)); 374 } 375 376 /** 377 * Sets the Charset default for subclasses to initialize. 378 * <p> 379 * Subclasses may ignore this setting. 380 * </p> 381 * 382 * @param defaultCharset the Charset name, null resets to the default. 383 * @return {@code this} instance. 384 */ 385 protected B setCharsetDefault(final Charset defaultCharset) { 386 this.charsetDefault = defaultCharset; 387 return asThis(); 388 } 389 390 /** 391 * Sets the OpenOption[]. 392 * <p> 393 * Normally used with InputStream, OutputStream, and Writer. 394 * </p> 395 * <p> 396 * Subclasses may ignore this setting. 397 * </p> 398 * 399 * @param openOptions the OpenOption[] name, null resets to the default. 400 * @return {@code this} instance. 401 * @since 2.13.0 402 * @see #setInputStream(InputStream) 403 * @see #setOutputStream(OutputStream) 404 * @see #setWriter(Writer) 405 */ 406 public B setOpenOptions(final OpenOption... openOptions) { 407 this.openOptions = openOptions != null ? openOptions : DEFAULT_OPEN_OPTIONS; 408 return asThis(); 409 } 410 411 private int throwIae(final int size, final int max) { 412 throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max)); 413 } 414}