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