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    /**
158     * Gets the OpenOption.
159     *
160     * @return the OpenOption.
161     */
162    protected OpenOption[] getOpenOptions() {
163        return openOptions;
164    }
165
166    /**
167     * Gets an OutputStream from the origin with open options.
168     *
169     * @return An OutputStream
170     * @throws IOException                   if an I/O error occurs.
171     * @throws UnsupportedOperationException if the origin cannot be converted to an OutputStream.
172     * @throws IllegalStateException         if the {@code origin} is {@code null}.
173     * @see AbstractOrigin#getOutputStream(OpenOption...)
174     * @since 2.13.0
175     */
176    protected OutputStream getOutputStream() throws IOException {
177        return checkOrigin().getOutputStream(getOpenOptions());
178    }
179
180    /**
181     * Gets a Path from the origin.
182     *
183     * @return A Path
184     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
185     * @throws IllegalStateException         if the {@code origin} is {@code null}.
186     * @see AbstractOrigin#getPath()
187     * @since 2.13.0
188     */
189    protected Path getPath() {
190        return checkOrigin().getPath();
191    }
192
193    /**
194     * Gets an writer from the origin with open options.
195     *
196     * @return An writer.
197     * @throws IOException                   if an I/O error occurs.
198     * @throws UnsupportedOperationException if the origin cannot be converted to a Writer.
199     * @throws IllegalStateException         if the {@code origin} is {@code null}.
200     * @see AbstractOrigin#getOutputStream(OpenOption...)
201     * @since 2.13.0
202     */
203    protected Writer getWriter() throws IOException {
204        return checkOrigin().getWriter(getCharset(), getOpenOptions());
205    }
206
207    /**
208     * Sets the buffer size. Invalid input (bufferSize &lt;= 0) resets the value to its default.
209     * <p>
210     * Subclasses may ignore this setting.
211     * </p>
212     *
213     * @param bufferSize the buffer size.
214     * @return this.
215     */
216    public B setBufferSize(final int bufferSize) {
217        this.bufferSize = checkBufferSize(bufferSize > 0 ? bufferSize : bufferSizeDefault);
218        return asThis();
219    }
220
221    /**
222     * Sets the buffer size.
223     * <p>
224     * Subclasses may ignore this setting.
225     * </p>
226     *
227     * @param bufferSize the buffer size, null resets to the default.
228     * @return this.
229     */
230    public B setBufferSize(final Integer bufferSize) {
231        setBufferSize(bufferSize != null ? bufferSize : bufferSizeDefault);
232        return asThis();
233    }
234
235    /**
236     * Sets the buffer size checker function. Throws a {@link IllegalArgumentException} by default.
237     *
238     * @param bufferSizeChecker the buffer size checker function. null resets to the default behavior.
239     * @return this
240     * @since 2.14.0
241     */
242    public B setBufferSizeChecker(final IntUnaryOperator bufferSizeChecker) {
243        this.bufferSizeChecker = bufferSizeChecker != null ? bufferSizeChecker : defaultSizeChecker;
244        return asThis();
245    }
246
247    /**
248     * Sets the buffer size for subclasses to initialize.
249     * <p>
250     * Subclasses may ignore this setting.
251     * </p>
252     *
253     * @param bufferSizeDefault the buffer size, null resets to the default.
254     * @return this.
255     */
256    protected B setBufferSizeDefault(final int bufferSizeDefault) {
257        this.bufferSizeDefault = bufferSizeDefault;
258        return asThis();
259    }
260
261    /**
262     * 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
263     * exceeded, this methods throws an {@link IllegalArgumentException}.
264     *
265     * @param bufferSizeMax maximum buffer size checked by the buffer size checker.
266     * @return this.
267     * @since 2.14.0
268     */
269    public B setBufferSizeMax(final int bufferSizeMax) {
270        this.bufferSizeMax = bufferSizeMax > 0 ? bufferSizeMax : DEFAULT_MAX_VALUE;
271        return asThis();
272    }
273
274    /**
275     * Sets the Charset.
276     * <p>
277     * Subclasses may ignore this setting.
278     * </p>
279     *
280     * @param charset the Charset, null resets to the default.
281     * @return this.
282     */
283    public B setCharset(final Charset charset) {
284        this.charset = Charsets.toCharset(charset, charsetDefault);
285        return asThis();
286    }
287
288    /**
289     * Sets the Charset.
290     * <p>
291     * Subclasses may ignore this setting.
292     * </p>
293     *
294     * @param charset the Charset name, null resets to the default.
295     * @return this.
296     */
297    public B setCharset(final String charset) {
298        return setCharset(Charsets.toCharset(charset, charsetDefault));
299    }
300
301    /**
302     * Sets the Charset default for subclasses to initialize.
303     * <p>
304     * Subclasses may ignore this setting.
305     * </p>
306     *
307     * @param defaultCharset the Charset name, null resets to the default.
308     * @return this.
309     */
310    protected B setCharsetDefault(final Charset defaultCharset) {
311        this.charsetDefault = defaultCharset;
312        return asThis();
313    }
314
315    /**
316     * Sets the OpenOption[].
317     * <p>
318     * Normally used with InputStream, OutputStream, and Writer.
319     * </p>
320     * <p>
321     * Subclasses may ignore this setting.
322     * </p>
323     *
324     * @param openOptions the OpenOption[] name, null resets to the default.
325     * @return this.
326     * @since 2.13.0
327     * @see #setInputStream(InputStream)
328     * @see #setOutputStream(OutputStream)
329     * @see #setWriter(Writer)
330     */
331    public B setOpenOptions(final OpenOption... openOptions) {
332        this.openOptions = openOptions != null ? openOptions : DEFAULT_OPEN_OPTIONS;
333        return asThis();
334    }
335
336    private int throwIae(final int size, final int max) {
337        throw new IllegalArgumentException(String.format("Request %,d exceeds maximum %,d", size, max));
338    }
339}