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