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 *      http://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.charset.Charset;
028import java.nio.file.OpenOption;
029import java.nio.file.Path;
030import java.util.function.IntUnaryOperator;
031
032import org.apache.commons.io.Charsets;
033import org.apache.commons.io.IOUtils;
034import org.apache.commons.io.file.PathUtils;
035
036/**
037 * Abstracts building a typed instance of {@code T}.
038 *
039 * @param <T> the type of instances to build.
040 * @param <B> the type of builder subclass.
041 * @since 2.12.0
042 */
043public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>> extends AbstractOriginSupplier<T, B> {
044
045    private static final int DEFAULT_MAX_VALUE = Integer.MAX_VALUE;
046
047    private static final OpenOption[] DEFAULT_OPEN_OPTIONS = PathUtils.EMPTY_OPEN_OPTION_ARRAY;
048
049    /**
050     * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
051     */
052    private int bufferSize = IOUtils.DEFAULT_BUFFER_SIZE;
053
054    /**
055     * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
056     */
057    private int bufferSizeDefault = IOUtils.DEFAULT_BUFFER_SIZE;
058
059    /**
060     * The maximum buffer size.
061     */
062    private int bufferSizeMax = DEFAULT_MAX_VALUE;
063
064    /**
065     * The Charset, defaults to {@link Charset#defaultCharset()}.
066     */
067    private Charset charset = Charset.defaultCharset();
068
069    /**
070     * The Charset, defaults to {@link Charset#defaultCharset()}.
071     */
072    private Charset charsetDefault = Charset.defaultCharset();
073
074    private OpenOption[] openOptions = DEFAULT_OPEN_OPTIONS;
075
076    /**
077     * The default checking behavior for a buffer size request. Throws a {@link IllegalArgumentException} by default.
078     */
079    private final IntUnaryOperator defaultSizeChecker = size -> size > bufferSizeMax ? throwIae(size, bufferSizeMax) : size;
080
081    /**
082     * The checking behavior for a buffer size request.
083     */
084    private IntUnaryOperator bufferSizeChecker = defaultSizeChecker;
085
086    /**
087     * Applies the buffer size request.
088     *
089     * @param size the size request.
090     * @return the size to use, usually the input, or can throw an unchecked exception, like {@link IllegalArgumentException}.
091     */
092    private int checkBufferSize(final int size) {
093        return bufferSizeChecker.applyAsInt(size);
094    }
095
096    /**
097     * Gets the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
098     *
099     * @return the buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
100     */
101    public int getBufferSize() {
102        return bufferSize;
103    }
104
105    /**
106     * Gets the buffer size default, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
107     *
108     * @return the buffer size default, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
109     */
110    public int getBufferSizeDefault() {
111        return bufferSizeDefault;
112    }
113
114    /**
115     * Gets a CharSequence from the origin with a Charset.
116     *
117     * @return An input stream
118     * @throws IllegalStateException         if the {@code origin} is {@code null}.
119     * @throws UnsupportedOperationException if the origin cannot be converted to a CharSequence.
120     * @throws IOException                   if an I/O error occurs.
121     * @see AbstractOrigin#getCharSequence(Charset)
122     * @since 2.13.0
123     */
124    public CharSequence getCharSequence() throws IOException {
125        return checkOrigin().getCharSequence(getCharset());
126    }
127
128    /**
129     * Gets the Charset, defaults to {@link Charset#defaultCharset()}.
130     *
131     * @return the Charset, defaults to {@link Charset#defaultCharset()}.
132     */
133    public Charset getCharset() {
134        return charset;
135    }
136
137    /**
138     * Gets the Charset default, defaults to {@link Charset#defaultCharset()}.
139     *
140     * @return the Charset default, defaults to {@link Charset#defaultCharset()}.
141     */
142    public Charset getCharsetDefault() {
143        return charsetDefault;
144    }
145
146    /**
147     * Gets a File from the origin.
148     *
149     * @return A File
150     * @throws IllegalStateException         if the {@code origin} is {@code null}.
151     * @throws UnsupportedOperationException if the origin cannot be converted to a {@link File}.
152     * @see AbstractOrigin#getPath()
153     * @since 2.18.0
154     */
155    public File getFile() {
156        return checkOrigin().getFile();
157    }
158
159    /**
160     * Gets an InputStream from the origin with OpenOption[].
161     *
162     * @return An input stream
163     * @throws IllegalStateException         if the {@code origin} is {@code null}.
164     * @throws UnsupportedOperationException if the origin cannot be converted to an {@link InputStream}.
165     * @throws IOException                   if an I/O error occurs.
166     * @see AbstractOrigin#getInputStream(OpenOption...)
167     * @see #getOpenOptions()
168     * @since 2.13.0
169     */
170    public InputStream getInputStream() throws IOException {
171        return checkOrigin().getInputStream(getOpenOptions());
172    }
173
174    /**
175     * Gets the OpenOption array.
176     *
177     * @return the OpenOption array.
178     */
179    public OpenOption[] getOpenOptions() {
180        return openOptions;
181    }
182
183    /**
184     * Gets an OutputStream from the origin with OpenOption[].
185     *
186     * @return An OutputStream
187     * @throws IllegalStateException         if the {@code origin} is {@code null}.
188     * @throws UnsupportedOperationException if the origin cannot be converted to an {@link OutputStream}.
189     * @throws IOException                   if an I/O error occurs.
190     * @see AbstractOrigin#getOutputStream(OpenOption...)
191     * @see #getOpenOptions()
192     * @since 2.13.0
193     */
194    public OutputStream getOutputStream() throws IOException {
195        return checkOrigin().getOutputStream(getOpenOptions());
196    }
197
198    /**
199     * Gets a Path from the origin.
200     *
201     * @return A Path
202     * @throws IllegalStateException         if the {@code origin} is {@code null}.
203     * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Path}.
204     * @see AbstractOrigin#getPath()
205     * @since 2.13.0
206     */
207    public Path getPath() {
208        return checkOrigin().getPath();
209    }
210
211    /**
212     * Gets a RandomAccessFile from the origin.
213     *
214     * @return A RandomAccessFile
215     * @throws IllegalStateException         if the {@code origin} is {@code null}.
216     * @throws UnsupportedOperationException if the origin cannot be converted to a {@link RandomAccessFile}.
217     * @throws IOException                   if an I/O error occurs.
218     * @since 2.18.0
219     */
220    public RandomAccessFile getRandomAccessFile() throws IOException {
221        return checkOrigin().getRandomAccessFile(getOpenOptions());
222    }
223
224    /**
225     * Gets a Reader from the origin with a Charset.
226     *
227     * @return A Reader
228     * @throws IllegalStateException         if the {@code origin} is {@code null}.
229     * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Reader}.
230     * @throws IOException                   if an I/O error occurs.
231     * @see AbstractOrigin#getReader(Charset)
232     * @see #getCharset()
233     * @since 2.16.0
234     */
235    public Reader getReader() throws IOException {
236        return checkOrigin().getReader(getCharset());
237    }
238
239    /**
240     * Gets a Writer from the origin with an OpenOption[].
241     *
242     * @return An writer.
243     * @throws IllegalStateException         if the {@code origin} is {@code null}.
244     * @throws UnsupportedOperationException if the origin cannot be converted to a {@link Writer}.
245     * @throws IOException                   if an I/O error occurs.
246     * @see AbstractOrigin#getOutputStream(OpenOption...)
247     * @see #getOpenOptions()
248     * @since 2.13.0
249     */
250    public Writer getWriter() throws IOException {
251        return checkOrigin().getWriter(getCharset(), getOpenOptions());
252    }
253
254    /**
255     * Sets the buffer size. Invalid input (bufferSize &lt;= 0) resets the value to its default.
256     * <p>
257     * Subclasses may ignore this setting.
258     * </p>
259     *
260     * @param bufferSize the buffer size.
261     * @return {@code this} instance.
262     */
263    public B setBufferSize(final int bufferSize) {
264        this.bufferSize = checkBufferSize(bufferSize > 0 ? bufferSize : bufferSizeDefault);
265        return asThis();
266    }
267
268    /**
269     * Sets the buffer size.
270     * <p>
271     * Subclasses may ignore this setting.
272     * </p>
273     *
274     * @param bufferSize the buffer size, null resets to the default.
275     * @return {@code this} instance.
276     */
277    public B setBufferSize(final Integer bufferSize) {
278        setBufferSize(bufferSize != null ? bufferSize : bufferSizeDefault);
279        return asThis();
280    }
281
282    /**
283     * Sets the buffer size checker function. Throws a {@link IllegalArgumentException} by default.
284     *
285     * @param bufferSizeChecker the buffer size checker function. null resets to the default behavior.
286     * @return {@code this} instance.
287     * @since 2.14.0
288     */
289    public B setBufferSizeChecker(final IntUnaryOperator bufferSizeChecker) {
290        this.bufferSizeChecker = bufferSizeChecker != null ? bufferSizeChecker : defaultSizeChecker;
291        return asThis();
292    }
293
294    /**
295     * Sets the buffer size for subclasses to initialize.
296     * <p>
297     * Subclasses may ignore this setting.
298     * </p>
299     *
300     * @param bufferSizeDefault the buffer size, null resets to the default.
301     * @return {@code this} instance.
302     */
303    protected B setBufferSizeDefault(final int bufferSizeDefault) {
304        this.bufferSizeDefault = bufferSizeDefault;
305        return asThis();
306    }
307
308    /**
309     * 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
310     * exceeded, this methods throws an {@link IllegalArgumentException}.
311     *
312     * @param bufferSizeMax maximum buffer size checked by the buffer size checker.
313     * @return {@code this} instance.
314     * @since 2.14.0
315     */
316    public B setBufferSizeMax(final int bufferSizeMax) {
317        this.bufferSizeMax = bufferSizeMax > 0 ? bufferSizeMax : DEFAULT_MAX_VALUE;
318        return asThis();
319    }
320
321    /**
322     * Sets the Charset.
323     * <p>
324     * Subclasses may ignore this setting.
325     * </p>
326     *
327     * @param charset the Charset, null resets to the default.
328     * @return {@code this} instance.
329     */
330    public B setCharset(final Charset charset) {
331        this.charset = Charsets.toCharset(charset, charsetDefault);
332        return asThis();
333    }
334
335    /**
336     * Sets the Charset.
337     * <p>
338     * Subclasses may ignore this setting.
339     * </p>
340     *
341     * @param charset the Charset name, null resets to the default.
342     * @return {@code this} instance.
343     */
344    public B setCharset(final String charset) {
345        return setCharset(Charsets.toCharset(charset, charsetDefault));
346    }
347
348    /**
349     * Sets the Charset default for subclasses to initialize.
350     * <p>
351     * Subclasses may ignore this setting.
352     * </p>
353     *
354     * @param defaultCharset the Charset name, null resets to the default.
355     * @return {@code this} instance.
356     */
357    protected B setCharsetDefault(final Charset defaultCharset) {
358        this.charsetDefault = defaultCharset;
359        return asThis();
360    }
361
362    /**
363     * Sets the OpenOption[].
364     * <p>
365     * Normally used with InputStream, OutputStream, and Writer.
366     * </p>
367     * <p>
368     * Subclasses may ignore this setting.
369     * </p>
370     *
371     * @param openOptions the OpenOption[] name, null resets to the default.
372     * @return {@code this} instance.
373     * @since 2.13.0
374     * @see #setInputStream(InputStream)
375     * @see #setOutputStream(OutputStream)
376     * @see #setWriter(Writer)
377     */
378    public B setOpenOptions(final OpenOption... openOptions) {
379        this.openOptions = openOptions != null ? openOptions : DEFAULT_OPEN_OPTIONS;
380        return asThis();
381    }
382
383    private int throwIae(final int size, final int max) {
384        throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max));
385    }
386}