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    *      https://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 reader proxy which delegates to the wrapped reader.
30   * <p>
31   * It is an alternative base class to FilterReader
32   * to increase reusability, because FilterReader changes the
33   * methods being called, such as read(char[]) to read(char[], int, int).
34   * </p>
35   */
36  public abstract class ProxyReader extends FilterReader {
37  
38      /**
39       * Constructs a new ProxyReader.
40       *
41       * @param delegate  the Reader to delegate to.
42       */
43      public ProxyReader(final Reader delegate) {
44          // the delegate is stored in a protected superclass variable named 'in'
45          super(delegate);
46      }
47  
48      /**
49       * Invoked by the read methods after the proxied call has returned
50       * successfully. The number of chars returned to the caller (or -1 if
51       * the end of stream was reached) is given as an argument.
52       * <p>
53       * Subclasses can override this method to add common post-processing
54       * functionality without having to override all the read methods.
55       * The default implementation does nothing.
56       * </p>
57       * <p>
58       * Note this method is <em>not</em> called from {@link #skip(long)} or
59       * {@link #reset()}. You need to explicitly override those methods if
60       * you want to add post-processing steps also to them.
61       * </p>
62       *
63       * @param n number of chars read, or -1 if the end of stream was reached.
64       * @throws IOException if the post-processing fails.
65       * @since 2.0
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       * <p>
83       * Note this method is <em>not</em> called from {@link #skip(long)} or
84       * {@link #reset()}. You need to explicitly override those methods if
85       * you want to add pre-processing steps also to them.
86       * </p>
87       *
88       * @param n number of chars that the caller asked to be read.
89       * @throws IOException if the pre-processing fails.
90       * @since 2.0
91       */
92      @SuppressWarnings("unused") // Possibly thrown from subclasses.
93      protected void beforeRead(final int n) throws IOException {
94          // noop
95      }
96  
97      /**
98       * Invokes the delegate's {@code close()} method.
99       *
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 }