    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}