    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}