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 byte array from the origin. 127 * 128 * @return A byte array. 129 * @throws IllegalStateException if the {@code origin} is {@code null}. 130 * @throws UnsupportedOperationException if the origin cannot be converted to a byte array. 131 * @throws IOException if an I/O error occurs. 132 * @see AbstractOrigin#getByteArray() 133 * @since 2.22.0 134 */ 135 public byte[] getByteArray() throws IOException { 136 return checkOrigin().getByteArray(); 137 } 138 139 /** 140 * Gets a Channel from the origin with OpenOption[]. 141 * 142 * @param channelType The channel type, not null. 143 * @return A channel of the specified type. 144 * @param <C> The channel type. 145 * @throws IllegalStateException if the {@code origin} is {@code null}. 146 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link ReadableByteChannel}. 147 * @throws IOException if an I/O error occurs. 148 * @see AbstractOrigin#getChannel 149 * @see #getOpenOptions() 150 * @since 2.21.0 151 */ 152 public <C extends Channel> C getChannel(final Class<C> channelType) throws IOException { 153 return checkOrigin().getChannel(channelType, getOpenOptions()); 154 } 155 156 /** 157 * Gets a CharSequence from the origin with a Charset. 158 * 159 * @return An input stream. 160 * @throws IllegalStateException if the {@code origin} is {@code null}. 161 * @throws UnsupportedOperationException if the origin cannot be converted to a CharSequence. 162 * @throws IOException if an I/O error occurs. 163 * @see AbstractOrigin#getCharSequence(Charset) 164 * @since 2.13.0 165 */ 166 public CharSequence getCharSequence() throws IOException { 167 return checkOrigin().getCharSequence(getCharset()); 168 } 169 170 /** 171 * Gets the Charset, defaults to {@link Charset#defaultCharset()}. 172 * 173 * @return the Charset, defaults to {@link Charset#defaultCharset()}. 174 */ 175 public Charset getCharset() { 176 return charset; 177 } 178 179 /** 180 * Gets the Charset default, defaults to {@link Charset#defaultCharset()}. 181 * 182 * @return the Charset default, defaults to {@link Charset#defaultCharset()}. 183 */ 184 public Charset getCharsetDefault() { 185 return charsetDefault; 186 } 187 188 /** 189 * Gets a File from the origin. 190 * 191 * @return A File. 192 * @throws IllegalStateException if the {@code origin} is {@code null}. 193 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link File}. 194 * @see AbstractOrigin#getPath() 195 * @since 2.18.0 196 */ 197 public File getFile() { 198 return checkOrigin().getFile(); 199 } 200 201 /** 202 * Gets an InputStream from the origin with OpenOption[]. 203 * 204 * @return An input stream. 205 * @throws IllegalStateException if the {@code origin} is {@code null}. 206 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}. 207 * @throws IOException if an I/O error occurs. 208 * @see AbstractOrigin#getInputStream(OpenOption...) 209 * @see #getOpenOptions() 210 * @since 2.13.0 211 */ 212 public InputStream getInputStream() throws IOException { 213 return checkOrigin().getInputStream(getOpenOptions()); 214 } 215 216 /** 217 * Gets the OpenOption array. 218 * 219 * @return the OpenOption array, this is not a defensive copy, modify at your own risk. 220 */ 221 public OpenOption[] getOpenOptions() { 222 return openOptions; 223 } 224 225 /** 226 * Gets an OutputStream from the origin with OpenOption[]. 227 * 228 * @return An OutputStream. 229 * @throws IllegalStateException if the {@code origin} is {@code null}. 230 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}. 231 * @throws IOException if an I/O error occurs. 232 * @see AbstractOrigin#getOutputStream(OpenOption...) 233 * @see #getOpenOptions() 234 * @since 2.13.0 235 */ 236 public OutputStream getOutputStream() throws IOException { 237 return checkOrigin().getOutputStream(getOpenOptions()); 238 } 239 240 /** 241 * Gets a Path from the origin. 242 * 243 * @return A Path. 244 * @throws IllegalStateException if the {@code origin} is {@code null}. 245 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Path}. 246 * @see AbstractOrigin#getPath() 247 * @since 2.13.0 248 */ 249 public Path getPath() { 250 return checkOrigin().getPath(); 251 } 252 253 /** 254 * Gets a RandomAccessFile from the origin. 255 * 256 * @return A RandomAccessFile. 257 * @throws IllegalStateException if the {@code origin} is {@code null}. 258 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link RandomAccessFile}. 259 * @throws IOException if an I/O error occurs. 260 * @since 2.18.0 261 */ 262 public RandomAccessFile getRandomAccessFile() throws IOException { 263 return checkOrigin().getRandomAccessFile(getOpenOptions()); 264 } 265 266 /** 267 * Gets a Reader from the origin with a Charset. 268 * 269 * @return A Reader. 270 * @throws IllegalStateException if the {@code origin} is {@code null}. 271 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Reader}. 272 * @throws IOException if an I/O error occurs. 273 * @see AbstractOrigin#getReader(Charset) 274 * @see #getCharset() 275 * @since 2.16.0 276 */ 277 public Reader getReader() throws IOException { 278 return checkOrigin().getReader(getCharset()); 279 } 280 281 /** 282 * Gets a Writer from the origin with an OpenOption[]. 283 * 284 * @return An writer. 285 * @throws IllegalStateException if the {@code origin} is {@code null}. 286 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Writer}. 287 * @throws IOException if an I/O error occurs. 288 * @see AbstractOrigin#getOutputStream(OpenOption...) 289 * @see #getOpenOptions() 290 * @since 2.13.0 291 */ 292 public Writer getWriter() throws IOException { 293 return checkOrigin().getWriter(getCharset(), getOpenOptions()); 294 } 295 296 /** 297 * Sets the buffer size. Invalid input (bufferSize <= 0) resets the value to its default. 298 * <p> 299 * Subclasses may ignore this setting. 300 * </p> 301 * 302 * @param bufferSize the buffer size, 0 resets to the default from {@link #getBufferSizeDefault()}. 303 * @return {@code this} instance. 304 */ 305 public B setBufferSize(final int bufferSize) { 306 this.bufferSize = checkBufferSize(bufferSize > 0 ? bufferSize : bufferSizeDefault); 307 return asThis(); 308 } 309 310 /** 311 * Sets the buffer size. 312 * <p> 313 * Subclasses may ignore this setting. 314 * </p> 315 * 316 * @param bufferSize the buffer size, null resets to the default from {@link #getBufferSizeDefault()}. 317 * @return {@code this} instance. 318 */ 319 public B setBufferSize(final Integer bufferSize) { 320 setBufferSize(bufferSize != null ? bufferSize : bufferSizeDefault); 321 return asThis(); 322 } 323 324 /** 325 * Sets the buffer size checker function. Throws a {@link IllegalArgumentException} by default. 326 * 327 * @param bufferSizeChecker the buffer size checker function. null resets to the default behavior. 328 * @return {@code this} instance. 329 * @since 2.14.0 330 */ 331 public B setBufferSizeChecker(final IntUnaryOperator bufferSizeChecker) { 332 this.bufferSizeChecker = bufferSizeChecker != null ? bufferSizeChecker : defaultSizeChecker; 333 return asThis(); 334 } 335 336 /** 337 * Sets the buffer size for subclasses to initialize. 338 * <p> 339 * Subclasses may ignore this setting. 340 * </p> 341 * 342 * @param bufferSizeDefault the buffer size, 0 resets to the default {@link IOUtils#DEFAULT_BUFFER_SIZE}. 343 * @return {@code this} instance. 344 */ 345 protected B setBufferSizeDefault(final int bufferSizeDefault) { 346 this.bufferSizeDefault = checkBufferSize(bufferSizeDefault > 0 ? bufferSizeDefault : IOUtils.DEFAULT_BUFFER_SIZE); 347 return asThis(); 348 } 349 350 /** 351 * 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 352 * exceeded, this methods throws an {@link IllegalArgumentException}. 353 * 354 * @param bufferSizeMax maximum buffer size checked by the buffer size checker. 355 * @return {@code this} instance. 356 * @since 2.14.0 357 */ 358 public B setBufferSizeMax(final int bufferSizeMax) { 359 this.bufferSizeMax = bufferSizeMax > 0 ? bufferSizeMax : DEFAULT_MAX_VALUE; 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, null resets to the default. 370 * @return {@code this} instance. 371 */ 372 public B setCharset(final Charset charset) { 373 this.charset = Charsets.toCharset(charset, charsetDefault); 374 return asThis(); 375 } 376 377 /** 378 * Sets the Charset. 379 * <p> 380 * Subclasses may ignore this setting. 381 * </p> 382 * 383 * @param charset the Charset name, null resets to the default. 384 * @return {@code this} instance. 385 */ 386 public B setCharset(final String charset) { 387 return setCharset(Charsets.toCharset(charset, charsetDefault)); 388 } 389 390 /** 391 * Sets the Charset default for subclasses to initialize. 392 * <p> 393 * Subclasses may ignore this setting. 394 * </p> 395 * 396 * @param defaultCharset the Charset name, null resets to the default. 397 * @return {@code this} instance. 398 */ 399 protected B setCharsetDefault(final Charset defaultCharset) { 400 this.charsetDefault = defaultCharset; 401 return asThis(); 402 } 403 404 /** 405 * Sets the OpenOption array. 406 * <p> 407 * Normally used with InputStream, OutputStream, and Writer. 408 * </p> 409 * <p> 410 * Subclasses may ignore this setting. 411 * </p> 412 * 413 * @param openOptions the OpenOption[] name, null resets to the default, a defensive copy is made. 414 * @return {@code this} instance. 415 * @since 2.13.0 416 * @see #setInputStream(InputStream) 417 * @see #setOutputStream(OutputStream) 418 * @see #setWriter(Writer) 419 */ 420 public B setOpenOptions(final OpenOption... openOptions) { 421 this.openOptions = openOptions != null ? openOptions.clone() : DEFAULT_OPEN_OPTIONS; 422 return asThis(); 423 } 424 425 private int throwIae(final int size, final int max) { 426 throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max)); 427 } 428}