    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()</code> 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[])</code> 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)</code> 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)</code> 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()</code> 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()</code> 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)</code> 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()</code> 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()</code> 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    protected void beforeRead(final int n) throws IOException {
    201        // no-op
    202    }
    203
    204    /**
    205     * Invoked by the read methods after the proxied call has returned
    206     * successfully. The number of bytes returned to the caller (or -1 if
    207     * the end of stream was reached) is given as an argument.
    208     * <p>
    209     * Subclasses can override this method to add common post-processing
    210     * functionality without having to override all the read methods.
    211     * The default implementation does nothing.
    212     * <p>
    213     * Note this method is <em>not</em> called from {@link #skip(long)} or
    214     * {@link #reset()}. You need to explicitly override those methods if
    215     * you want to add post-processing steps also to them.
    216     *
    217     * @since 2.0
    218     * @param n number of bytes read, or -1 if the end of stream was reached
    219     * @throws IOException if the post-processing fails
    220     */
    221    protected void afterRead(final int n) throws IOException {
    222        // no-op
    223    }
    224
    225    /**
    226     * Handle any IOExceptions thrown.
    227     * <p>
    228     * This method provides a point to implement custom exception
    229     * handling. The default behavior is to re-throw the exception.
    230     * @param e The IOException thrown
    231     * @throws IOException if an I/O error occurs
    232     * @since 2.0
    233     */
    234    protected void handleIOException(final IOException e) throws IOException {
    235        throw e;
    236    }
    237
    238}