    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}