View Javadoc
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 &lt;= 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 }