    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}