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