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.input;
019
020import java.io.FilterInputStream;
021import java.io.IOException;
022import java.io.InputStream;
023
024import org.apache.commons.io.build.AbstractStreamBuilder;
025
026/**
027 * An unsynchronized version of {@link FilterInputStream}, not thread-safe.
028 * <p>
029 * Wraps an existing {@link InputStream} and performs some transformation on the input data while it is being read. Transformations can be anything from a
030 * simple byte-wise filtering input data to an on-the-fly compression or decompression of the underlying stream. Input streams that wrap another input stream
031 * and provide some additional functionality on top of it usually inherit from this class.
032 * </p>
033 * <p>
034 * To build an instance, see {@link Builder}.
035 * </p>
036 * <p>
037 * Provenance: Apache Harmony and modified.
038 * </p>
039 *
040 * @see FilterInputStream
041 * @since 2.12.0
042 */
043//@NotThreadSafe
044public class UnsynchronizedFilterInputStream extends InputStream {
045
046    /**
047     * Builds a new {@link UnsynchronizedFilterInputStream} instance.
048     * <p>
049     * Using File IO:
050     * </p>
051     * <pre>{@code
052     * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder()
053     *   .setFile(file)
054     *   .get();}
055     * </pre>
056     * <p>
057     * Using NIO Path:
058     * </p>
059     * <pre>{@code
060     * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder()
061     *   .setPath(path)
062     *   .get();}
063     * </pre>
064     */
065    public static class Builder extends AbstractStreamBuilder<UnsynchronizedFilterInputStream, Builder> {
066
067        /**
068         * Constructs a new instance.
069         * <p>
070         * This builder use the aspect InputStream and OpenOption[].
071         * </p>
072         * <p>
073         * You must provide an origin that can be converted to an InputStream by this builder, otherwise, this call will throw an
074         * {@link UnsupportedOperationException}.
075         * </p>
076         *
077         * @return a new instance.
078         * @throws UnsupportedOperationException if the origin cannot provide an InputStream.
079         * @see #getInputStream()
080         */
081        @SuppressWarnings("resource") // Caller closes.
082        @Override
083        public UnsynchronizedFilterInputStream get() throws IOException {
084            return new UnsynchronizedFilterInputStream(getInputStream());
085        }
086
087    }
088
089    /**
090     * Constructs a new {@link Builder}.
091     *
092     * @return a new {@link Builder}.
093     */
094    public static Builder builder() {
095        return new Builder();
096    }
097
098    /**
099     * The source input stream that is filtered.
100     */
101    protected volatile InputStream inputStream;
102
103    /**
104     * Constructs a new {@code FilterInputStream} with the specified input stream as source.
105     *
106     * @param inputStream the non-null InputStream to filter reads on.
107     */
108    UnsynchronizedFilterInputStream(final InputStream inputStream) {
109        this.inputStream = inputStream;
110    }
111
112    /**
113     * Returns the number of bytes that are available before this stream will block.
114     *
115     * @return the number of bytes available before blocking.
116     * @throws IOException if an error occurs in this stream.
117     */
118    @Override
119    public int available() throws IOException {
120        return inputStream.available();
121    }
122
123    /**
124     * Closes this stream. This implementation closes the filtered stream.
125     *
126     * @throws IOException if an error occurs while closing this stream.
127     */
128    @Override
129    public void close() throws IOException {
130        inputStream.close();
131    }
132
133    /**
134     * Sets a mark position in this stream. The parameter {@code readlimit} indicates how many bytes can be read before the mark is invalidated. Sending
135     * {@code reset()} will reposition this stream back to the marked position, provided that {@code readlimit} has not been surpassed.
136     * <p>
137     * This implementation sets a mark in the filtered stream.
138     *
139     * @param readlimit the number of bytes that can be read from this stream before the mark is invalidated.
140     * @see #markSupported()
141     * @see #reset()
142     */
143    @SuppressWarnings("sync-override") // by design.
144    @Override
145    public void mark(final int readlimit) {
146        inputStream.mark(readlimit);
147    }
148
149    /**
150     * Indicates whether this stream supports {@code mark()} and {@code reset()}. This implementation returns whether or not the filtered stream supports
151     * marking.
152     *
153     * @return {@code true} if {@code mark()} and {@code reset()} are supported, {@code false} otherwise.
154     * @see #mark(int)
155     * @see #reset()
156     * @see #skip(long)
157     */
158    @Override
159    public boolean markSupported() {
160        return inputStream.markSupported();
161    }
162
163    /**
164     * Reads a single byte from the filtered stream and returns it as an integer in the range from 0 to 255. Returns -1 if the end of this stream has been
165     * reached.
166     *
167     * @return the byte read or -1 if the end of the filtered stream has been reached.
168     * @throws IOException if the stream is closed or another IOException occurs.
169     */
170    @Override
171    public int read() throws IOException {
172        return inputStream.read();
173    }
174
175    /**
176     * Reads bytes from this stream and stores them in the byte array {@code buffer}. Returns the number of bytes actually read or -1 if no bytes were read and
177     * the end of this stream was encountered. This implementation reads bytes from the filtered stream.
178     *
179     * @param buffer the byte array in which to store the read bytes.
180     * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading.
181     * @throws IOException if this stream is closed or another IOException occurs.
182     */
183    @Override
184    public int read(final byte[] buffer) throws IOException {
185        return read(buffer, 0, buffer.length);
186    }
187
188    /**
189     * Reads at most {@code count} bytes from this stream and stores them in the byte array {@code buffer} starting at {@code offset}. Returns the number of
190     * bytes actually read or -1 if no bytes have been read and the end of this stream has been reached. This implementation reads bytes from the filtered
191     * stream.
192     *
193     * @param buffer the byte array in which to store the bytes read.
194     * @param offset the initial position in {@code buffer} to store the bytes read from this stream.
195     * @param count  the maximum number of bytes to store in {@code buffer}.
196     * @return the number of bytes actually read or -1 if the end of the filtered stream has been reached while reading.
197     * @throws IOException if this stream is closed or another I/O error occurs.
198     */
199    @Override
200    public int read(final byte[] buffer, final int offset, final int count) throws IOException {
201        return inputStream.read(buffer, offset, count);
202    }
203
204    /**
205     * Resets this stream to the last marked location. This implementation resets the target stream.
206     *
207     * @throws IOException if this stream is already closed, no mark has been set or the mark is no longer valid because more than {@code readlimit} bytes have
208     *                     been read since setting the mark.
209     * @see #mark(int)
210     * @see #markSupported()
211     */
212    @SuppressWarnings("sync-override") // by design.
213    @Override
214    public void reset() throws IOException {
215        inputStream.reset();
216    }
217
218    /**
219     * Skips {@code count} number of bytes in this stream. Subsequent {@code read()}'s will not return these bytes unless {@code reset()} is used. This
220     * implementation skips {@code count} number of bytes in the filtered stream.
221     *
222     * @param count the number of bytes to skip.
223     * @return the number of bytes actually skipped.
224     * @throws IOException if this stream is closed or another IOException occurs.
225     * @see #mark(int)
226     * @see #reset()
227     */
228    @Override
229    public long skip(final long count) throws IOException {
230        return inputStream.skip(count);
231    }
232}