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