    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.FilterReader;
    022import java.io.IOException;
    023import java.io.Reader;
    024import java.nio.CharBuffer;
    025
    026import org.apache.commons.io.IOUtils;
    027
    028/**
    029 * A Proxy stream which acts as expected, that is it passes the method
    030 * calls on to the proxied stream and doesn't change which methods are
    031 * being called.
    032 * <p>
    033 * It is an alternative base class to FilterReader
    034 * to increase reusability, because FilterReader changes the
    035 * methods being called, such as read(char[]) to read(char[], int, int).
    036 * </p>
    037 */
    038public abstract class ProxyReader extends FilterReader {
    039
    040    /**
    041     * Constructs a new ProxyReader.
    042     *
    043     * @param proxy  the Reader to delegate to
    044     */
    045    public ProxyReader(final Reader proxy) {
    046        super(proxy);
    047        // the proxy is stored in a protected superclass variable named 'in'
    048    }
    049
    050    /**
    051     * Invokes the delegate's {@code read()} method.
    052     * @return the character read or -1 if the end of stream
    053     * @throws IOException if an I/O error occurs.
    054     */
    055    @Override
    056    public int read() throws IOException {
    057        try {
    058            beforeRead(1);
    059            final int c = in.read();
    060            afterRead(c != EOF ? 1 : EOF);
    061            return c;
    062        } catch (final IOException e) {
    063            handleIOException(e);
    064            return EOF;
    065        }
    066    }
    067
    068    /**
    069     * Invokes the delegate's {@code read(char[])} method.
    070     * @param chr the buffer to read the characters into
    071     * @return the number of characters read or -1 if the end of stream
    072     * @throws IOException if an I/O error occurs.
    073     */
    074    @Override
    075    public int read(final char[] chr) throws IOException {
    076        try {
    077            beforeRead(IOUtils.length(chr));
    078            final int n = in.read(chr);
    079            afterRead(n);
    080            return n;
    081        } catch (final IOException e) {
    082            handleIOException(e);
    083            return EOF;
    084        }
    085    }
    086
    087    /**
    088     * Invokes the delegate's {@code read(char[], int, int)} method.
    089     * @param chr the buffer to read the characters into
    090     * @param st The start offset
    091     * @param len The number of bytes to read
    092     * @return the number of characters read or -1 if the end of stream
    093     * @throws IOException if an I/O error occurs.
    094     */
    095    @Override
    096    public int read(final char[] chr, final int st, final int len) throws IOException {
    097        try {
    098            beforeRead(len);
    099            final int n = in.read(chr, st, len);
    100            afterRead(n);
    101            return n;
    102        } catch (final IOException e) {
    103            handleIOException(e);
    104            return EOF;
    105        }
    106    }
    107
    108    /**
    109     * Invokes the delegate's {@code read(CharBuffer)} method.
    110     * @param target the char buffer to read the characters into
    111     * @return the number of characters read or -1 if the end of stream
    112     * @throws IOException if an I/O error occurs.
    113     * @since 2.0
    114     */
    115    @Override
    116    public int read(final CharBuffer target) throws IOException {
    117        try {
    118            beforeRead(IOUtils.length(target));
    119            final int n = in.read(target);
    120            afterRead(n);
    121            return n;
    122        } catch (final IOException e) {
    123            handleIOException(e);
    124            return EOF;
    125        }
    126    }
    127
    128    /**
    129     * Invokes the delegate's {@code skip(long)} method.
    130     * @param ln the number of bytes to skip
    131     * @return the number of bytes to skipped or EOF if the end of stream
    132     * @throws IOException if an I/O error occurs.
    133     */
    134    @Override
    135    public long skip(final long ln) throws IOException {
    136        try {
    137            return in.skip(ln);
    138        } catch (final IOException e) {
    139            handleIOException(e);
    140            return 0;
    141        }
    142    }
    143
    144    /**
    145     * Invokes the delegate's {@code ready()} method.
    146     * @return true if the stream is ready to be read
    147     * @throws IOException if an I/O error occurs.
    148     */
    149    @Override
    150    public boolean ready() throws IOException {
    151        try {
    152            return in.ready();
    153        } catch (final IOException e) {
    154            handleIOException(e);
    155            return false;
    156        }
    157    }
    158
    159    /**
    160     * Invokes the delegate's {@code close()} method.
    161     * @throws IOException if an I/O error occurs.
    162     */
    163    @Override
    164    public void close() throws IOException {
    165        try {
    166            in.close();
    167        } catch (final IOException e) {
    168            handleIOException(e);
    169        }
    170    }
    171
    172    /**
    173     * Invokes the delegate's {@code mark(int)} method.
    174     * @param idx read ahead limit
    175     * @throws IOException if an I/O error occurs.
    176     */
    177    @Override
    178    public synchronized void mark(final int idx) throws IOException {
    179        try {
    180            in.mark(idx);
    181        } catch (final IOException e) {
    182            handleIOException(e);
    183        }
    184    }
    185
    186    /**
    187     * Invokes the delegate's {@code reset()} method.
    188     * @throws IOException if an I/O error occurs.
    189     */
    190    @Override
    191    public synchronized void reset() throws IOException {
    192        try {
    193            in.reset();
    194        } catch (final IOException e) {
    195            handleIOException(e);
    196        }
    197    }
    198
    199    /**
    200     * Invokes the delegate's {@code markSupported()} method.
    201     * @return true if mark is supported, otherwise false
    202     */
    203    @Override
    204    public boolean markSupported() {
    205        return in.markSupported();
    206    }
    207
    208    /**
    209     * Invoked by the read methods before the call is proxied. The number
    210     * of chars that the caller wanted to read (1 for the {@link #read()}
    211     * method, buffer length for {@link #read(char[])}, etc.) is given as
    212     * an argument.
    213     * <p>
    214     * Subclasses can override this method to add common pre-processing
    215     * functionality without having to override all the read methods.
    216     * The default implementation does nothing.
    217     * <p>
    218     * Note this method is <em>not</em> called from {@link #skip(long)} or
    219     * {@link #reset()}. You need to explicitly override those methods if
    220     * you want to add pre-processing steps also to them.
    221     *
    222     * @since 2.0
    223     * @param n number of chars that the caller asked to be read
    224     * @throws IOException if the pre-processing fails
    225     */
    226    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    227    protected void beforeRead(final int n) throws IOException {
    228        // noop
    229    }
    230
    231    /**
    232     * Invoked by the read methods after the proxied call has returned
    233     * successfully. The number of chars returned to the caller (or -1 if
    234     * the end of stream was reached) is given as an argument.
    235     * <p>
    236     * Subclasses can override this method to add common post-processing
    237     * functionality without having to override all the read methods.
    238     * The default implementation does nothing.
    239     * <p>
    240     * Note this method is <em>not</em> called from {@link #skip(long)} or
    241     * {@link #reset()}. You need to explicitly override those methods if
    242     * you want to add post-processing steps also to them.
    243     *
    244     * @since 2.0
    245     * @param n number of chars read, or -1 if the end of stream was reached
    246     * @throws IOException if the post-processing fails
    247     */
    248    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    249    protected void afterRead(final int n) throws IOException {
    250        // noop
    251    }
    252
    253    /**
    254     * Handle any IOExceptions thrown.
    255     * <p>
    256     * This method provides a point to implement custom exception
    257     * handling. The default behavior is to re-throw the exception.
    258     * @param e The IOException thrown
    259     * @throws IOException if an I/O error occurs.
    260     * @since 2.0
    261     */
    262    protected void handleIOException(final IOException e) throws IOException {
    263        throw e;
    264    }
    265
    266}