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 }