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 */
017package org.apache.commons.io.input;
018
019import static org.apache.commons.io.IOUtils.EOF;
020
021import java.io.FilterInputStream;
022import java.io.IOException;
023import java.io.InputStream;
024
025import org.apache.commons.io.IOUtils;
026
027/**
028 * A Proxy stream which acts as expected, that is it passes the method
029 * calls on to the proxied stream and doesn't change which methods are
030 * being called.
031 * <p>
032 * It is an alternative base class to FilterInputStream
033 * to increase reusability, because FilterInputStream changes the
034 * methods being called, such as read(byte[]) to read(byte[], int, int).
035 * </p>
036 * <p>
037 * See the protected methods for ways in which a subclass can easily decorate
038 * a stream with custom pre-, post- or error processing functionality.
039 * </p>
040 */
041public abstract class ProxyInputStream extends FilterInputStream {
042
043    /**
044     * Constructs a new ProxyInputStream.
045     *
046     * @param proxy  the InputStream to delegate to
047     */
048    public ProxyInputStream(final InputStream proxy) {
049        super(proxy);
050        // the proxy is stored in a protected superclass variable named 'in'
051    }
052
053    /**
054     * Invokes the delegate's {@code read()} method.
055     * @return the byte read or -1 if the end of stream
056     * @throws IOException if an I/O error occurs.
057     */
058    @Override
059    public int read() throws IOException {
060        try {
061            beforeRead(1);
062            final int b = in.read();
063            afterRead(b != EOF ? 1 : EOF);
064            return b;
065        } catch (final IOException e) {
066            handleIOException(e);
067            return EOF;
068        }
069    }
070
071    /**
072     * Invokes the delegate's {@code read(byte[])} method.
073     * @param bts the buffer to read the bytes into
074     * @return the number of bytes read or EOF if the end of stream
075     * @throws IOException if an I/O error occurs.
076     */
077    @Override
078    public int read(final byte[] bts) throws IOException {
079        try {
080            beforeRead(IOUtils.length(bts));
081            final int n = in.read(bts);
082            afterRead(n);
083            return n;
084        } catch (final IOException e) {
085            handleIOException(e);
086            return EOF;
087        }
088    }
089
090    /**
091     * Invokes the delegate's {@code read(byte[], int, int)} method.
092     * @param bts the buffer to read the bytes into
093     * @param off The start offset
094     * @param len The number of bytes to read
095     * @return the number of bytes read or -1 if the end of stream
096     * @throws IOException if an I/O error occurs.
097     */
098    @Override
099    public int read(final byte[] bts, final int off, final int len) throws IOException {
100        try {
101            beforeRead(len);
102            final int n = in.read(bts, off, len);
103            afterRead(n);
104            return n;
105        } catch (final IOException e) {
106            handleIOException(e);
107            return EOF;
108        }
109    }
110
111    /**
112     * Invokes the delegate's {@code skip(long)} method.
113     * @param ln the number of bytes to skip
114     * @return the actual number of bytes skipped
115     * @throws IOException if an I/O error occurs.
116     */
117    @Override
118    public long skip(final long ln) throws IOException {
119        try {
120            return in.skip(ln);
121        } catch (final IOException e) {
122            handleIOException(e);
123            return 0;
124        }
125    }
126
127    /**
128     * Invokes the delegate's {@code available()} method.
129     * @return the number of available bytes
130     * @throws IOException if an I/O error occurs.
131     */
132    @Override
133    public int available() throws IOException {
134        try {
135            return super.available();
136        } catch (final IOException e) {
137            handleIOException(e);
138            return 0;
139        }
140    }
141
142    /**
143     * Invokes the delegate's {@code close()} method.
144     * @throws IOException if an I/O error occurs.
145     */
146    @Override
147    public void close() throws IOException {
148        IOUtils.close(in, this::handleIOException);
149    }
150
151    /**
152     * Invokes the delegate's {@code mark(int)} method.
153     * @param readlimit read ahead limit
154     */
155    @Override
156    public synchronized void mark(final int readlimit) {
157        in.mark(readlimit);
158    }
159
160    /**
161     * Invokes the delegate's {@code reset()} method.
162     * @throws IOException if an I/O error occurs.
163     */
164    @Override
165    public synchronized void reset() throws IOException {
166        try {
167            in.reset();
168        } catch (final IOException e) {
169            handleIOException(e);
170        }
171    }
172
173    /**
174     * Invokes the delegate's {@code markSupported()} method.
175     * @return true if mark is supported, otherwise false
176     */
177    @Override
178    public boolean markSupported() {
179        return in.markSupported();
180    }
181
182    /**
183     * Invoked by the read methods before the call is proxied. The number
184     * of bytes that the caller wanted to read (1 for the {@link #read()}
185     * method, buffer length for {@link #read(byte[])}, etc.) is given as
186     * an argument.
187     * <p>
188     * Subclasses can override this method to add common pre-processing
189     * functionality without having to override all the read methods.
190     * The default implementation does nothing.
191     * <p>
192     * Note this method is <em>not</em> called from {@link #skip(long)} or
193     * {@link #reset()}. You need to explicitly override those methods if
194     * you want to add pre-processing steps also to them.
195     *
196     * @since 2.0
197     * @param n number of bytes that the caller asked to be read
198     * @throws IOException if the pre-processing fails
199     */
200    @SuppressWarnings("unused") // Possibly thrown from subclasses.
201    protected void beforeRead(final int n) throws IOException {
202        // no-op
203    }
204
205    /**
206     * Invoked by the read methods after the proxied call has returned
207     * successfully. The number of bytes returned to the caller (or -1 if
208     * the end of stream was reached) is given as an argument.
209     * <p>
210     * Subclasses can override this method to add common post-processing
211     * functionality without having to override all the read methods.
212     * The default implementation does nothing.
213     * <p>
214     * Note this method is <em>not</em> called from {@link #skip(long)} or
215     * {@link #reset()}. You need to explicitly override those methods if
216     * you want to add post-processing steps also to them.
217     *
218     * @since 2.0
219     * @param n number of bytes read, or -1 if the end of stream was reached
220     * @throws IOException if the post-processing fails
221     */
222    @SuppressWarnings("unused") // Possibly thrown from subclasses.
223    protected void afterRead(final int n) throws IOException {
224        // no-op
225    }
226
227    /**
228     * Handle any IOExceptions thrown.
229     * <p>
230     * This method provides a point to implement custom exception
231     * handling. The default behavior is to re-throw the exception.
232     * @param e The IOException thrown
233     * @throws IOException if an I/O error occurs.
234     * @since 2.0
235     */
236    protected void handleIOException(final IOException e) throws IOException {
237        throw e;
238    }
239
240}