1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.io.build;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.io.RandomAccessFile;
25 import java.io.Reader;
26 import java.io.Writer;
27 import java.nio.channels.Channel;
28 import java.nio.channels.ReadableByteChannel;
29 import java.nio.charset.Charset;
30 import java.nio.file.OpenOption;
31 import java.nio.file.Path;
32 import java.util.function.IntUnaryOperator;
33
34 import org.apache.commons.io.Charsets;
35 import org.apache.commons.io.IOUtils;
36 import org.apache.commons.io.file.PathUtils;
37
38 /**
39 * 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,
40 * buffer size checker, a buffer size default, buffer size maximum, Charset, Charset default, default size checker, and open options. A subclass may use all,
41 * some, or none of these properties in building instances of {@code T}.
42 *
43 * @param <T> the type of instances to build.
44 * @param <B> the type of builder subclass.
45 * @since 2.12.0
46 */
47 public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>> extends AbstractOriginSupplier<T, B> {
48
49 private static final int DEFAULT_MAX_VALUE = Integer.MAX_VALUE;
50
51 private static final OpenOption[] DEFAULT_OPEN_OPTIONS = PathUtils.EMPTY_OPEN_OPTION_ARRAY;
52
53 /**
54 * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
55 */
56 private int bufferSize = IOUtils.DEFAULT_BUFFER_SIZE;
57
58 /**
59 * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
60 */
61 private int bufferSizeDefault = IOUtils.DEFAULT_BUFFER_SIZE;
62
63 /**
64 * The maximum buffer size.
65 */
66 private int bufferSizeMax = DEFAULT_MAX_VALUE;
67
68 /**
69 * The Charset, defaults to {@link Charset#defaultCharset()}.
70 */
71 private Charset charset = Charset.defaultCharset();
72
73 /**
74 * The Charset, defaults to {@link Charset#defaultCharset()}.
75 */
76 private Charset charsetDefault = Charset.defaultCharset();
77
78 private OpenOption[] openOptions = DEFAULT_OPEN_OPTIONS;
79
80 /**
81 * The default checking behavior for a buffer size request. Throws a {@link IllegalArgumentException} by default.
82 */
83 private final IntUnaryOperator defaultSizeChecker = size -> size > bufferSizeMax ? throwIae(size, bufferSizeMax) : size;
84
85 /**
86 * The checking behavior for a buffer size request.
87 */
88 private IntUnaryOperator bufferSizeChecker = defaultSizeChecker;
89
90 /**
91 * Constructs a new instance for subclasses.
92 */
93 public AbstractStreamBuilder() {
94 // empty
95 }
96
97 /**
98 * Applies the buffer size request.
99 *
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 }