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 */
038public abstract class ProxyInputStream extends FilterInputStream {
039
040    /**
041     * Constructs a new ProxyInputStream.
042     *
043     * @param proxy  the InputStream to delegate to
044     */
045    public ProxyInputStream(final InputStream proxy) {
046        super(proxy);
047        // the proxy is stored in a protected superclass variable named 'in'
048    }
049
050    /**
051     * Invokes the delegate's <code>read()</code> method.
052     * @return the byte read or -1 if the end of stream
053     * @throws IOException if an I/O error occurs
054     */
055    @Override
056    public int read() throws IOException {
057        try {
058            beforeRead(1);
059            final int b = in.read();
060            afterRead(b != EOF ? 1 : EOF);
061            return b;
062        } catch (final IOException e) {
063            handleIOException(e);
064            return EOF;
065        }
066    }
067
068    /**
069     * Invokes the delegate's <code>read(byte[])</code> method.
070     * @param bts the buffer to read the bytes into
071     * @return the number of bytes read or EOF if the end of stream
072     * @throws IOException if an I/O error occurs
073     */
074    @Override
075    public int read(final byte[] bts) throws IOException {
076        try {
077            beforeRead(bts != null ? bts.length : 0);
078            final int n = in.read(bts);
079            afterRead(n);
080            return n;
081        } catch (final IOException e) {
082            handleIOException(e);
083            return EOF;
084        }
085    }
086
087    /**
088     * Invokes the delegate's <code>read(byte[], int, int)</code> method.
089     * @param bts the buffer to read the bytes into
090     * @param off The start offset
091     * @param len The number of bytes to read
092     * @return the number of bytes read or -1 if the end of stream
093     * @throws IOException if an I/O error occurs
094     */
095    @Override
096    public int read(final byte[] bts, final int off, final int len) throws IOException {
097        try {
098            beforeRead(len);
099            final int n = in.read(bts, off, len);
100            afterRead(n);
101            return n;
102        } catch (final IOException e) {
103            handleIOException(e);
104            return EOF;
105        }
106    }
107
108    /**
109     * Invokes the delegate's <code>skip(long)</code> method.
110     * @param ln the number of bytes to skip
111     * @return the actual number of bytes skipped
112     * @throws IOException if an I/O error occurs
113     */
114    @Override
115    public long skip(final long ln) throws IOException {
116        try {
117            return in.skip(ln);
118        } catch (final IOException e) {
119            handleIOException(e);
120            return 0;
121        }
122    }
123
124    /**
125     * Invokes the delegate's <code>available()</code> method.
126     * @return the number of available bytes
127     * @throws IOException if an I/O error occurs
128     */
129    @Override
130    public int available() throws IOException {
131        try {
132            return super.available();
133        } catch (final IOException e) {
134            handleIOException(e);
135            return 0;
136        }
137    }
138
139    /**
140     * Invokes the delegate's <code>close()</code> method.
141     * @throws IOException if an I/O error occurs
142     */
143    @Override
144    public void close() throws IOException {
145        try {
146            in.close();
147        } catch (final IOException e) {
148            handleIOException(e);
149        }
150    }
151
152    /**
153     * Invokes the delegate's <code>mark(int)</code> method.
154     * @param readlimit read ahead limit
155     */
156    @Override
157    public synchronized void mark(final int readlimit) {
158        in.mark(readlimit);
159    }
160
161    /**
162     * Invokes the delegate's <code>reset()</code> method.
163     * @throws IOException if an I/O error occurs
164     */
165    @Override
166    public synchronized void reset() throws IOException {
167        try {
168            in.reset();
169        } catch (final IOException e) {
170            handleIOException(e);
171        }
172    }
173
174    /**
175     * Invokes the delegate's <code>markSupported()</code> method.
176     * @return true if mark is supported, otherwise false
177     */
178    @Override
179    public boolean markSupported() {
180        return in.markSupported();
181    }
182
183    /**
184     * Invoked by the read methods before the call is proxied. The number
185     * of bytes that the caller wanted to read (1 for the {@link #read()}
186     * method, buffer length for {@link #read(byte[])}, etc.) is given as
187     * an argument.
188     * <p>
189     * Subclasses can override this method to add common pre-processing
190     * functionality without having to override all the read methods.
191     * The default implementation does nothing.
192     * <p>
193     * Note this method is <em>not</em> called from {@link #skip(long)} or
194     * {@link #reset()}. You need to explicitly override those methods if
195     * you want to add pre-processing steps also to them.
196     *
197     * @since 2.0
198     * @param n number of bytes that the caller asked to be read
199     * @throws IOException if the pre-processing fails
200     */
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    protected void afterRead(final int n) throws IOException {
223        // no-op
224    }
225
226    /**
227     * Handle any IOExceptions thrown.
228     * <p>
229     * This method provides a point to implement custom exception
230     * handling. The default behaviour is to re-throw the exception.
231     * @param e The IOException thrown
232     * @throws IOException if an I/O error occurs
233     * @since 2.0
234     */
235    protected void handleIOException(final IOException e) throws IOException {
236        throw e;
237    }
238
239}