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