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