    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.Reader;
    025import java.io.Writer;
    026import java.net.URI;
    027import java.nio.charset.Charset;
    028import java.nio.file.Files;
    029import java.nio.file.OpenOption;
    030import java.nio.file.Path;
    031import java.nio.file.Paths;
    032import java.util.Objects;
    033
    034/**
    035 * Abstracts the origin of data for builders like a {@link File}, {@link Path}, {@link Reader}, {@link Writer}, {@link InputStream}, {@link OutputStream}, and
    036 * {@link URI}.
    037 * <p>
    038 * Some methods may throw {@link UnsupportedOperationException} if that method is not implemented in a concrete subclass, see {@link #getFile()} and
    039 * {@link #getPath()}.
    040 * </p>
    041 *
    042 * @param <T> the type of instances to build.
    043 * @param <B> the type of builder subclass.
    044 * @since 2.12.0
    045 */
    046public abstract class AbstractOrigin<T, B extends AbstractOrigin<T, B>> extends AbstractSupplier<T, B> {
    047
    048    /**
    049     * A {@link File} origin.
    050     */
    051    public static class ByteArrayOrigin extends AbstractOrigin<byte[], ByteArrayOrigin> {
    052
    053        /**
    054         * Constructs a new instance for the given origin.
    055         *
    056         * @param origin The origin.
    057         */
    058        public ByteArrayOrigin(final byte[] origin) {
    059            super(origin);
    060        }
    061
    062        @Override
    063        public byte[] getByteArray() {
    064            // No conversion
    065            return get();
    066        }
    067
    068    }
    069
    070    /**
    071     * A {@link File} origin.
    072     * <p>
    073     * Starting from this origin, you can get a byte array, a file, an input stream, an output stream, a path, a reader, and a writer.
    074     * </p>
    075     */
    076    public static class FileOrigin extends AbstractOrigin<File, FileOrigin> {
    077
    078        /**
    079         * Constructs a new instance for the given origin.
    080         *
    081         * @param origin The origin.
    082         */
    083        public FileOrigin(final File origin) {
    084            super(origin);
    085        }
    086
    087        @Override
    088        public File getFile() {
    089            // No conversion
    090            return get();
    091        }
    092
    093        @Override
    094        public Path getPath() {
    095            return get().toPath();
    096        }
    097
    098    }
    099
    100    /**
    101     * An {@link InputStream} origin.
    102     * <p>
    103     * This origin cannot provide other aspects.
    104     * </p>
    105     */
    106    public static class InputStreamOrigin extends AbstractOrigin<InputStream, InputStreamOrigin> {
    107
    108        /**
    109         * Constructs a new instance for the given origin.
    110         *
    111         * @param origin The origin.
    112         */
    113        public InputStreamOrigin(final InputStream origin) {
    114            super(origin);
    115        }
    116
    117        @Override
    118        public InputStream getInputStream(final OpenOption... options) {
    119            // No conversion
    120            return get();
    121        }
    122
    123    }
    124
    125    /**
    126     * An {@link OutputStream} origin.
    127     * <p>
    128     * This origin cannot provide other aspects.
    129     * </p>
    130     */
    131    public static class OutputStreamOrigin extends AbstractOrigin<OutputStream, OutputStreamOrigin> {
    132
    133        /**
    134         * Constructs a new instance for the given origin.
    135         *
    136         * @param origin The origin.
    137         */
    138        public OutputStreamOrigin(final OutputStream origin) {
    139            super(origin);
    140        }
    141
    142        @Override
    143        public OutputStream getOutputStream(final OpenOption... options) {
    144            // No conversion
    145            return get();
    146        }
    147
    148    }
    149
    150    /**
    151     * A {@link Path} origin.
    152     * <p>
    153     * Starting from this origin, you can get a byte array, a file, an input stream, an output stream, a path, a reader, and a writer.
    154     * </p>
    155     */
    156    public static class PathOrigin extends AbstractOrigin<Path, PathOrigin> {
    157
    158        /**
    159         * Constructs a new instance for the given origin.
    160         *
    161         * @param origin The origin.
    162         */
    163        public PathOrigin(final Path origin) {
    164            super(origin);
    165        }
    166
    167        @Override
    168        public File getFile() {
    169            return get().toFile();
    170        }
    171
    172        @Override
    173        public Path getPath() {
    174            // No conversion
    175            return get();
    176        }
    177
    178    }
    179
    180    /**
    181     * An {@link Reader} origin.
    182     * <p>
    183     * This origin cannot provide other aspects.
    184     * </p>
    185     */
    186    public static class ReaderOrigin extends AbstractOrigin<Reader, ReaderOrigin> {
    187
    188        /**
    189         * Constructs a new instance for the given origin.
    190         *
    191         * @param origin The origin.
    192         */
    193        public ReaderOrigin(final Reader origin) {
    194            super(origin);
    195        }
    196
    197        @Override
    198        public Reader getReader(final Charset charset) throws IOException {
    199            // No conversion
    200            return get();
    201        }
    202    }
    203
    204    /**
    205     * A {@link URI} origin.
    206     */
    207    public static class URIOrigin extends AbstractOrigin<URI, URIOrigin> {
    208
    209        /**
    210         * Constructs a new instance for the given origin.
    211         *
    212         * @param origin The origin.
    213         */
    214        public URIOrigin(final URI origin) {
    215            super(origin);
    216        }
    217
    218        @Override
    219        public File getFile() {
    220            return getPath().toFile();
    221        }
    222
    223        @Override
    224        public Path getPath() {
    225            return Paths.get(get());
    226        }
    227
    228    }
    229
    230    /**
    231     * An {@link Writer} origin.
    232     * <p>
    233     * This origin cannot provide other aspects.
    234     * </p>
    235     */
    236    public static class WriterOrigin extends AbstractOrigin<Writer, WriterOrigin> {
    237
    238        /**
    239         * Constructs a new instance for the given origin.
    240         *
    241         * @param origin The origin.
    242         */
    243        public WriterOrigin(final Writer origin) {
    244            super(origin);
    245        }
    246
    247        @Override
    248        public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
    249            // No conversion
    250            return get();
    251        }
    252    }
    253
    254    /**
    255     * The non-null origin.
    256     */
    257    final T origin;
    258
    259    /**
    260     * Constructs a new instance for a subclass.
    261     *
    262     * @param origin The origin.
    263     */
    264    protected AbstractOrigin(final T origin) {
    265        this.origin = Objects.requireNonNull(origin, "origin");
    266    }
    267
    268    /**
    269     * Gets the origin.
    270     *
    271     * @return the origin.
    272     */
    273    @Override
    274    public T get() {
    275        return origin;
    276    }
    277
    278    /**
    279     * Gets this origin as a byte array, if possible.
    280     *
    281     * @return this origin as a byte array, if possible.
    282     * @throws IOException if an I/O error occurs.
    283     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
    284     */
    285    public byte[] getByteArray() throws IOException {
    286        return Files.readAllBytes(getPath());
    287    }
    288
    289    /**
    290     * Gets this origin as a Path, if possible.
    291     *
    292     * @return this origin as a Path, if possible.
    293     * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
    294     */
    295    public File getFile() {
    296        throw new UnsupportedOperationException(origin.toString());
    297    }
    298
    299    /**
    300     * Gets this origin as an InputStream, if possible.
    301     *
    302     * @param options options specifying how the file is opened
    303     * @return this origin as an InputStream, if possible.
    304     * @throws IOException if an I/O error occurs.
    305     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
    306     */
    307    public InputStream getInputStream(final OpenOption... options) throws IOException {
    308        return Files.newInputStream(getPath(), options);
    309    }
    310
    311    /**
    312     * Gets this origin as an OutputStream, if possible.
    313     *
    314     * @param options options specifying how the file is opened
    315     * @return this origin as an OutputStream, if possible.
    316     * @throws IOException if an I/O error occurs.
    317     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
    318     */
    319    public OutputStream getOutputStream(final OpenOption... options) throws IOException {
    320        return Files.newOutputStream(getPath(), options);
    321    }
    322
    323    /**
    324     * Gets this origin as a Path, if possible.
    325     *
    326     * @return this origin as a Path, if possible.
    327     * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
    328     */
    329    public Path getPath() {
    330        throw new UnsupportedOperationException(origin.toString());
    331    }
    332
    333    /**
    334     * Gets a new Reader on the origin, buffered by default.
    335     *
    336     * @param charset the charset to use for decoding
    337     * @return a new Reader on the origin.
    338     * @throws IOException if an I/O error occurs opening the file.
    339     */
    340    public Reader getReader(final Charset charset) throws IOException {
    341        return Files.newBufferedReader(getPath(), charset);
    342    }
    343
    344    /**
    345     * Gets a new Writer on the origin, buffered by default.
    346     *
    347     * @param charset the charset to use for encoding
    348     * @param options options specifying how the file is opened
    349     * @return a new Writer on the origin.
    350     * @throws IOException if an I/O error occurs opening or creating the file.
    351     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
    352     */
    353    public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
    354        return Files.newBufferedWriter(getPath(), charset, options);
    355    }
    356
    357    @Override
    358    public String toString() {
    359        return origin.toString();
    360    }
    361}