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 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 }