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