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}