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