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