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 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 delegate the Reader to delegate to
44 */
45 public ProxyReader(final Reader delegate) {
46 // the delegate is stored in a protected superclass variable named 'in'
47 super(delegate);
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 * @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 * 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 * @param n number of chars that the caller asked to be read
87 * @throws IOException if the pre-processing fails
88 * @since 2.0
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 readAheadLimit read ahead limit
124 * @throws IOException if an I/O error occurs.
125 */
126 @Override
127 public synchronized void mark(final int readAheadLimit) throws IOException {
128 try {
129 in.mark(readAheadLimit);
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 }