View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io.input;
18  
19  import static org.apache.commons.io.IOUtils.EOF;
20  
21  import java.io.FilterReader;
22  import java.io.IOException;
23  import java.io.Reader;
24  import java.nio.CharBuffer;
25  
26  import org.apache.commons.io.IOUtils;
27  
28  /**
29   * A Proxy stream which acts as expected, that is it passes the method
30   * calls on to the proxied stream and doesn't change which methods are
31   * being called.
32   * <p>
33   * It is an alternative base class to FilterReader
34   * to increase reusability, because FilterReader changes the
35   * methods being called, such as read(char[]) to read(char[], int, int).
36   * </p>
37   */
38  public abstract class ProxyReader extends FilterReader {
39  
40      /**
41       * Constructs a new ProxyReader.
42       *
43       * @param proxy  the Reader to delegate to
44       */
45      public ProxyReader(final Reader proxy) {
46          super(proxy);
47          // the proxy is stored in a protected superclass variable named 'in'
48      }
49  
50      /**
51       * Invoked by the read methods after the proxied call has returned
52       * successfully. The number of chars returned to the caller (or -1 if
53       * the end of stream was reached) is given as an argument.
54       * <p>
55       * Subclasses can override this method to add common post-processing
56       * functionality without having to override all the read methods.
57       * The default implementation does nothing.
58       * <p>
59       * Note this method is <em>not</em> called from {@link #skip(long)} or
60       * {@link #reset()}. You need to explicitly override those methods if
61       * you want to add post-processing steps also to them.
62       *
63       * @since 2.0
64       * @param n number of chars read, or -1 if the end of stream was reached
65       * @throws IOException if the post-processing fails
66       */
67      @SuppressWarnings("unused") // Possibly thrown from subclasses.
68      protected void afterRead(final int n) throws IOException {
69          // noop
70      }
71  
72      /**
73       * Invoked by the read methods before the call is proxied. The number
74       * of chars that the caller wanted to read (1 for the {@link #read()}
75       * method, buffer length for {@link #read(char[])}, etc.) is given as
76       * an argument.
77       * <p>
78       * Subclasses can override this method to add common pre-processing
79       * functionality without having to override all the read methods.
80       * The default implementation does nothing.
81       * <p>
82       * Note this method is <em>not</em> called from {@link #skip(long)} or
83       * {@link #reset()}. You need to explicitly override those methods if
84       * you want to add pre-processing steps also to them.
85       *
86       * @since 2.0
87       * @param n number of chars that the caller asked to be read
88       * @throws IOException if the pre-processing fails
89       */
90      @SuppressWarnings("unused") // Possibly thrown from subclasses.
91      protected void beforeRead(final int n) throws IOException {
92          // noop
93      }
94  
95      /**
96       * Invokes the delegate's {@code close()} method.
97       * @throws IOException if an I/O error occurs.
98       */
99      @Override
100     public void close() throws IOException {
101         try {
102             in.close();
103         } catch (final IOException e) {
104             handleIOException(e);
105         }
106     }
107 
108     /**
109      * Handle any IOExceptions thrown.
110      * <p>
111      * This method provides a point to implement custom exception
112      * handling. The default behavior is to re-throw the exception.
113      * @param e The IOException thrown
114      * @throws IOException if an I/O error occurs.
115      * @since 2.0
116      */
117     protected void handleIOException(final IOException e) throws IOException {
118         throw e;
119     }
120 
121     /**
122      * Invokes the delegate's {@code mark(int)} method.
123      * @param idx read ahead limit
124      * @throws IOException if an I/O error occurs.
125      */
126     @Override
127     public synchronized void mark(final int idx) throws IOException {
128         try {
129             in.mark(idx);
130         } catch (final IOException e) {
131             handleIOException(e);
132         }
133     }
134 
135     /**
136      * Invokes the delegate's {@code markSupported()} method.
137      * @return true if mark is supported, otherwise false
138      */
139     @Override
140     public boolean markSupported() {
141         return in.markSupported();
142     }
143 
144     /**
145      * Invokes the delegate's {@code read()} method.
146      * @return the character read or -1 if the end of stream
147      * @throws IOException if an I/O error occurs.
148      */
149     @Override
150     public int read() throws IOException {
151         try {
152             beforeRead(1);
153             final int c = in.read();
154             afterRead(c != EOF ? 1 : EOF);
155             return c;
156         } catch (final IOException e) {
157             handleIOException(e);
158             return EOF;
159         }
160     }
161 
162     /**
163      * Invokes the delegate's {@code read(char[])} method.
164      * @param chr the buffer to read the characters into
165      * @return the number of characters read or -1 if the end of stream
166      * @throws IOException if an I/O error occurs.
167      */
168     @Override
169     public int read(final char[] chr) throws IOException {
170         try {
171             beforeRead(IOUtils.length(chr));
172             final int n = in.read(chr);
173             afterRead(n);
174             return n;
175         } catch (final IOException e) {
176             handleIOException(e);
177             return EOF;
178         }
179     }
180 
181     /**
182      * Invokes the delegate's {@code read(char[], int, int)} method.
183      * @param chr the buffer to read the characters into
184      * @param st The start offset
185      * @param len The number of bytes to read
186      * @return the number of characters read or -1 if the end of stream
187      * @throws IOException if an I/O error occurs.
188      */
189     @Override
190     public int read(final char[] chr, final int st, final int len) throws IOException {
191         try {
192             beforeRead(len);
193             final int n = in.read(chr, st, len);
194             afterRead(n);
195             return n;
196         } catch (final IOException e) {
197             handleIOException(e);
198             return EOF;
199         }
200     }
201 
202     /**
203      * Invokes the delegate's {@code read(CharBuffer)} method.
204      * @param target the char buffer to read the characters into
205      * @return the number of characters read or -1 if the end of stream
206      * @throws IOException if an I/O error occurs.
207      * @since 2.0
208      */
209     @Override
210     public int read(final CharBuffer target) throws IOException {
211         try {
212             beforeRead(IOUtils.length(target));
213             final int n = in.read(target);
214             afterRead(n);
215             return n;
216         } catch (final IOException e) {
217             handleIOException(e);
218             return EOF;
219         }
220     }
221 
222     /**
223      * Invokes the delegate's {@code ready()} method.
224      * @return true if the stream is ready to be read
225      * @throws IOException if an I/O error occurs.
226      */
227     @Override
228     public boolean ready() throws IOException {
229         try {
230             return in.ready();
231         } catch (final IOException e) {
232             handleIOException(e);
233             return false;
234         }
235     }
236 
237     /**
238      * Invokes the delegate's {@code reset()} method.
239      * @throws IOException if an I/O error occurs.
240      */
241     @Override
242     public synchronized void reset() throws IOException {
243         try {
244             in.reset();
245         } catch (final IOException e) {
246             handleIOException(e);
247         }
248     }
249 
250     /**
251      * Invokes the delegate's {@code skip(long)} method.
252      * @param ln the number of bytes to skip
253      * @return the number of bytes to skipped or EOF if the end of stream
254      * @throws IOException if an I/O error occurs.
255      */
256     @Override
257     public long skip(final long ln) throws IOException {
258         try {
259             return in.skip(ln);
260         } catch (final IOException e) {
261             handleIOException(e);
262             return 0;
263         }
264     }
265 
266 }