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 * http://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.charset.Charset;
28 import java.nio.file.OpenOption;
29 import java.nio.file.Path;
30 import java.util.function.IntUnaryOperator;
31
32 import org.apache.commons.io.Charsets;
33 import org.apache.commons.io.IOUtils;
34 import org.apache.commons.io.file.PathUtils;
35
36 /**
37 * Abstracts building a typed instance of {@code T}.
38 *
39 * @param <T> the type of instances to build.
40 * @param <B> the type of builder subclass.
41 * @since 2.12.0
42 */
43 public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>> extends AbstractOriginSupplier<T, B> {
44
45 private static final int DEFAULT_MAX_VALUE = Integer.MAX_VALUE;
46
47 private static final OpenOption[] DEFAULT_OPEN_OPTIONS = PathUtils.EMPTY_OPEN_OPTION_ARRAY;
48
49 /**
50 * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
51 */
52 private int bufferSize = IOUtils.DEFAULT_BUFFER_SIZE;
53
54 /**
55 * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
56 */
57 private int bufferSizeDefault = IOUtils.DEFAULT_BUFFER_SIZE;
58
59 /**
60 * The maximum buffer size.
61 */
62 private int bufferSizeMax = DEFAULT_MAX_VALUE;
63
64 /**
65 * The Charset, defaults to {@link Charset#defaultCharset()}.
66 */
67 private Charset charset = Charset.defaultCharset();
68
69 /**
70 * The Charset, defaults to {@link Charset#defaultCharset()}.
71 */
72 private Charset charsetDefault = Charset.defaultCharset();
73
74 private OpenOption[] openOptions = DEFAULT_OPEN_OPTIONS;
75
76 /**
77 * The default checking behavior for a buffer size request. Throws a {@link IllegalArgumentException} by default.
78 */
79 private final IntUnaryOperator defaultSizeChecker = size -> size > bufferSizeMax ? throwIae(size, bufferSizeMax) : size;
80
81 /**
82 * The checking behavior for a buffer size request.
83 */
84 private IntUnaryOperator bufferSizeChecker = defaultSizeChecker;
85
86 /**
87 * Constructs a new instance for subclasses.
88 */
89 public AbstractStreamBuilder() {
90 // empty
91 }
92
93 /**
94 * Applies the buffer size request.
95 *
96 * @param size the size request.
97 * @return the size to use, usually the input, or can throw an unchecked exception, like {@link IllegalArgumentException}.
98 */
99 private int checkBufferSize(final int size) {
100 return bufferSizeChecker.applyAsInt(size);
101 }
102
103 /**
104 * Gets the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
105 *
106 * @return the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
107 */
108 public int getBufferSize() {
109 return bufferSize;
110 }
111
112 /**
113 * Gets the buffer size default, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
114 *
115 * @return the buffer size default, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
116 */
117 public int getBufferSizeDefault() {
118 return bufferSizeDefault;
119 }
120
121 /**
122 * Gets a CharSequence from the origin with a Charset.
123 *
124 * @return An input stream
125 * @throws IllegalStateException if the {@code origin} is {@code null}.
126 * @throws UnsupportedOperationException if the origin cannot be converted to a CharSequence.
127 * @throws IOException if an I/O error occurs.
128 * @see AbstractOrigin#getCharSequence(Charset)
129 * @since 2.13.0
130 */
131 public CharSequence getCharSequence() throws IOException {
132 return checkOrigin().getCharSequence(getCharset());
133 }
134
135 /**
136 * Gets the Charset, defaults to {@link Charset#defaultCharset()}.
137 *
138 * @return the Charset, defaults to {@link Charset#defaultCharset()}.
139 */
140 public Charset getCharset() {
141 return charset;
142 }
143
144 /**
145 * Gets the Charset default, defaults to {@link Charset#defaultCharset()}.
146 *
147 * @return the Charset default, defaults to {@link Charset#defaultCharset()}.
148 */
149 public Charset getCharsetDefault() {
150 return charsetDefault;
151 }
152
153 /**
154 * Gets a File from the origin.
155 *
156 * @return A File
157 * @throws IllegalStateException if the {@code origin} is {@code null}.
158 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link File}.
159 * @see AbstractOrigin#getPath()
160 * @since 2.18.0
161 */
162 public File getFile() {
163 return checkOrigin().getFile();
164 }
165
166 /**
167 * Gets an InputStream from the origin with OpenOption[].
168 *
169 * @return An input stream
170 * @throws IllegalStateException if the {@code origin} is {@code null}.
171 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}.
172 * @throws IOException if an I/O error occurs.
173 * @see AbstractOrigin#getInputStream(OpenOption...)
174 * @see #getOpenOptions()
175 * @since 2.13.0
176 */
177 public InputStream getInputStream() throws IOException {
178 return checkOrigin().getInputStream(getOpenOptions());
179 }
180
181 /**
182 * Gets the OpenOption array.
183 *
184 * @return the OpenOption array.
185 */
186 public OpenOption[] getOpenOptions() {
187 return openOptions;
188 }
189
190 /**
191 * Gets an OutputStream from the origin with OpenOption[].
192 *
193 * @return An OutputStream
194 * @throws IllegalStateException if the {@code origin} is {@code null}.
195 * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}.
196 * @throws IOException if an I/O error occurs.
197 * @see AbstractOrigin#getOutputStream(OpenOption...)
198 * @see #getOpenOptions()
199 * @since 2.13.0
200 */
201 public OutputStream getOutputStream() throws IOException {
202 return checkOrigin().getOutputStream(getOpenOptions());
203 }
204
205 /**
206 * Gets a Path from the origin.
207 *
208 * @return A Path
209 * @throws IllegalStateException if the {@code origin} is {@code null}.
210 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Path}.
211 * @see AbstractOrigin#getPath()
212 * @since 2.13.0
213 */
214 public Path getPath() {
215 return checkOrigin().getPath();
216 }
217
218 /**
219 * Gets a RandomAccessFile from the origin.
220 *
221 * @return A RandomAccessFile
222 * @throws IllegalStateException if the {@code origin} is {@code null}.
223 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link RandomAccessFile}.
224 * @throws IOException if an I/O error occurs.
225 * @since 2.18.0
226 */
227 public RandomAccessFile getRandomAccessFile() throws IOException {
228 return checkOrigin().getRandomAccessFile(getOpenOptions());
229 }
230
231 /**
232 * Gets a Reader from the origin with a Charset.
233 *
234 * @return A Reader
235 * @throws IllegalStateException if the {@code origin} is {@code null}.
236 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Reader}.
237 * @throws IOException if an I/O error occurs.
238 * @see AbstractOrigin#getReader(Charset)
239 * @see #getCharset()
240 * @since 2.16.0
241 */
242 public Reader getReader() throws IOException {
243 return checkOrigin().getReader(getCharset());
244 }
245
246 /**
247 * Gets a Writer from the origin with an OpenOption[].
248 *
249 * @return An writer.
250 * @throws IllegalStateException if the {@code origin} is {@code null}.
251 * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Writer}.
252 * @throws IOException if an I/O error occurs.
253 * @see AbstractOrigin#getOutputStream(OpenOption...)
254 * @see #getOpenOptions()
255 * @since 2.13.0
256 */
257 public Writer getWriter() throws IOException {
258 return checkOrigin().getWriter(getCharset(), getOpenOptions());
259 }
260
261 /**
262 * Sets the buffer size. Invalid input (bufferSize <= 0) resets the value to its default.
263 * <p>
264 * Subclasses may ignore this setting.
265 * </p>
266 *
267 * @param bufferSize the buffer size.
268 * @return {@code this} instance.
269 */
270 public B setBufferSize(final int bufferSize) {
271 this.bufferSize = checkBufferSize(bufferSize > 0 ? bufferSize : bufferSizeDefault);
272 return asThis();
273 }
274
275 /**
276 * Sets the buffer size.
277 * <p>
278 * Subclasses may ignore this setting.
279 * </p>
280 *
281 * @param bufferSize the buffer size, null resets to the default.
282 * @return {@code this} instance.
283 */
284 public B setBufferSize(final Integer bufferSize) {
285 setBufferSize(bufferSize != null ? bufferSize : bufferSizeDefault);
286 return asThis();
287 }
288
289 /**
290 * Sets the buffer size checker function. Throws a {@link IllegalArgumentException} by default.
291 *
292 * @param bufferSizeChecker the buffer size checker function. null resets to the default behavior.
293 * @return {@code this} instance.
294 * @since 2.14.0
295 */
296 public B setBufferSizeChecker(final IntUnaryOperator bufferSizeChecker) {
297 this.bufferSizeChecker = bufferSizeChecker != null ? bufferSizeChecker : defaultSizeChecker;
298 return asThis();
299 }
300
301 /**
302 * Sets the buffer size for subclasses to initialize.
303 * <p>
304 * Subclasses may ignore this setting.
305 * </p>
306 *
307 * @param bufferSizeDefault the buffer size, null resets to the default.
308 * @return {@code this} instance.
309 */
310 protected B setBufferSizeDefault(final int bufferSizeDefault) {
311 this.bufferSizeDefault = bufferSizeDefault;
312 return asThis();
313 }
314
315 /**
316 * 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
317 * exceeded, this methods throws an {@link IllegalArgumentException}.
318 *
319 * @param bufferSizeMax maximum buffer size checked by the buffer size checker.
320 * @return {@code this} instance.
321 * @since 2.14.0
322 */
323 public B setBufferSizeMax(final int bufferSizeMax) {
324 this.bufferSizeMax = bufferSizeMax > 0 ? bufferSizeMax : DEFAULT_MAX_VALUE;
325 return asThis();
326 }
327
328 /**
329 * Sets the Charset.
330 * <p>
331 * Subclasses may ignore this setting.
332 * </p>
333 *
334 * @param charset the Charset, null resets to the default.
335 * @return {@code this} instance.
336 */
337 public B setCharset(final Charset charset) {
338 this.charset = Charsets.toCharset(charset, charsetDefault);
339 return asThis();
340 }
341
342 /**
343 * Sets the Charset.
344 * <p>
345 * Subclasses may ignore this setting.
346 * </p>
347 *
348 * @param charset the Charset name, null resets to the default.
349 * @return {@code this} instance.
350 */
351 public B setCharset(final String charset) {
352 return setCharset(Charsets.toCharset(charset, charsetDefault));
353 }
354
355 /**
356 * Sets the Charset default for subclasses to initialize.
357 * <p>
358 * Subclasses may ignore this setting.
359 * </p>
360 *
361 * @param defaultCharset the Charset name, null resets to the default.
362 * @return {@code this} instance.
363 */
364 protected B setCharsetDefault(final Charset defaultCharset) {
365 this.charsetDefault = defaultCharset;
366 return asThis();
367 }
368
369 /**
370 * Sets the OpenOption[].
371 * <p>
372 * Normally used with InputStream, OutputStream, and Writer.
373 * </p>
374 * <p>
375 * Subclasses may ignore this setting.
376 * </p>
377 *
378 * @param openOptions the OpenOption[] name, null resets to the default.
379 * @return {@code this} instance.
380 * @since 2.13.0
381 * @see #setInputStream(InputStream)
382 * @see #setOutputStream(OutputStream)
383 * @see #setWriter(Writer)
384 */
385 public B setOpenOptions(final OpenOption... openOptions) {
386 this.openOptions = openOptions != null ? openOptions : DEFAULT_OPEN_OPTIONS;
387 return asThis();
388 }
389
390 private int throwIae(final int size, final int max) {
391 throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max));
392 }
393 }