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.output;
18
19 import java.io.FilterWriter;
20 import java.io.IOException;
21 import java.io.Writer;
22
23 import org.apache.commons.io.IOUtils;
24
25 /**
26 * A writer proxy which delegates to the wrapped writer.
27 * <p>
28 * It is an alternative base class to FilterWriter to increase reusability, because FilterWriter changes the methods being called, such as
29 * {@code write(char[]) to write(char[], int, int)} and {@code write(String) to write(String, int, int)}.
30 * </p>
31 */
32 public class ProxyWriter extends FilterWriter {
33
34 /**
35 * Constructs a new ProxyWriter.
36 *
37 * @param delegate the Writer to delegate to.
38 */
39 public ProxyWriter(final Writer delegate) {
40 // the delegate is stored in a protected superclass variable named 'out'
41 super(delegate);
42 }
43
44 /**
45 * Invoked by the write methods after the proxied call has returned
46 * successfully. The number of chars written (1 for the
47 * {@link #write(int)} method, buffer length for {@link #write(char[])},
48 * etc.) is given as an argument.
49 * <p>
50 * Subclasses can override this method to add common post-processing
51 * functionality without having to override all the write methods.
52 * The default implementation does nothing.
53 * </p>
54 *
55 * @param n number of chars written.
56 * @throws IOException if the post-processing fails.
57 * @since 2.0
58 */
59 @SuppressWarnings("unused") // Possibly thrown from subclasses.
60 protected void afterWrite(final int n) throws IOException {
61 // noop
62 }
63
64 /**
65 * Invokes the delegate's {@code append(char)} method.
66 *
67 * @param c The character to write.
68 * @return this writer.
69 * @throws IOException if an I/O error occurs.
70 * @since 2.0
71 */
72 @Override
73 public Writer append(final char c) throws IOException {
74 try {
75 beforeWrite(1);
76 out.append(c);
77 afterWrite(1);
78 } catch (final IOException e) {
79 handleIOException(e);
80 }
81 return this;
82 }
83
84 /**
85 * Invokes the delegate's {@code append(CharSequence)} method.
86 *
87 * @param csq The character sequence to write.
88 * @return this writer.
89 * @throws IOException if an I/O error occurs.
90 * @since 2.0
91 */
92 @Override
93 public Writer append(final CharSequence csq) throws IOException {
94 try {
95 final int len = IOUtils.length(csq);
96 beforeWrite(len);
97 out.append(csq);
98 afterWrite(len);
99 } catch (final IOException e) {
100 handleIOException(e);
101 }
102 return this;
103 }
104
105 /**
106 * Invokes the delegate's {@code append(CharSequence, int, int)} method.
107 *
108 * @param csq The character sequence to write.
109 * @param start The index of the first character to write.
110 * @param end The index of the first character to write (exclusive).
111 * @return this writer.
112 * @throws IOException if an I/O error occurs.
113 * @since 2.0
114 */
115 @Override
116 public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
117 try {
118 beforeWrite(end - start);
119 out.append(csq, start, end);
120 afterWrite(end - start);
121 } catch (final IOException e) {
122 handleIOException(e);
123 }
124 return this;
125 }
126
127 /**
128 * Invoked by the write methods before the call is proxied. The number
129 * of chars to be written (1 for the {@link #write(int)} method, buffer
130 * length for {@link #write(char[])}, etc.) is given as an argument.
131 * <p>
132 * Subclasses can override this method to add common pre-processing
133 * functionality without having to override all the write methods.
134 * The default implementation does nothing.
135 * </p>
136 *
137 * @param n number of chars to be written.
138 * @throws IOException if the pre-processing fails.
139 * @since 2.0
140 */
141 @SuppressWarnings("unused") // Possibly thrown from subclasses.
142 protected void beforeWrite(final int n) throws IOException {
143 // noop
144 }
145
146 /**
147 * Invokes the delegate's {@code close()} method.
148 *
149 * @throws IOException if an I/O error occurs.
150 */
151 @Override
152 public void close() throws IOException {
153 IOUtils.close(out, this::handleIOException);
154 }
155
156 /**
157 * Invokes the delegate's {@code flush()} method.
158 *
159 * @throws IOException if an I/O error occurs.
160 */
161 @Override
162 public void flush() throws IOException {
163 try {
164 out.flush();
165 } catch (final IOException e) {
166 handleIOException(e);
167 }
168 }
169
170 /**
171 * Handles any IOExceptions thrown.
172 * <p>
173 * This method provides a point to implement custom exception
174 * handling. The default behavior is to re-throw the exception.
175 * </p>
176 *
177 * @param e The IOException thrown.
178 * @throws IOException if an I/O error occurs.
179 * @since 2.0
180 */
181 protected void handleIOException(final IOException e) throws IOException {
182 throw e;
183 }
184
185 /**
186 * Sets the underlying writer.
187 * <p>
188 * Use with caution.
189 * </p>
190 *
191 * @param out the underlying output writer.
192 * @return {@code this} instance.
193 * @since 2.22.0
194 */
195 public ProxyWriter setReference(final Writer out) {
196 this.out = out;
197 return this;
198 }
199
200 /**
201 * Unwraps this instance by returning the underlying {@link Writer}.
202 * <p>
203 * Use with caution.
204 * </p>
205 *
206 * @return the underlying {@link Writer}.
207 * @since 2.22.0
208 */
209 public Writer unwrap() {
210 return out;
211 }
212
213 /**
214 * Invokes the delegate's {@code write(char[])} method.
215 *
216 * @param cbuf the characters to write.
217 * @throws IOException if an I/O error occurs.
218 */
219 @Override
220 public void write(final char[] cbuf) throws IOException {
221 try {
222 final int len = IOUtils.length(cbuf);
223 beforeWrite(len);
224 out.write(cbuf);
225 afterWrite(len);
226 } catch (final IOException e) {
227 handleIOException(e);
228 }
229 }
230
231 /**
232 * Invokes the delegate's {@code write(char[], int, int)} method.
233 *
234 * @param cbuf the characters to write.
235 * @param off The start offset.
236 * @param len The number of characters to write.
237 * @throws IOException if an I/O error occurs.
238 */
239 @Override
240 public void write(final char[] cbuf, final int off, final int len) throws IOException {
241 try {
242 beforeWrite(len);
243 out.write(cbuf, off, len);
244 afterWrite(len);
245 } catch (final IOException e) {
246 handleIOException(e);
247 }
248 }
249
250 /**
251 * Invokes the delegate's {@code write(int)} method.
252 *
253 * @param c the character to write.
254 * @throws IOException if an I/O error occurs.
255 */
256 @Override
257 public void write(final int c) throws IOException {
258 try {
259 beforeWrite(1);
260 out.write(c);
261 afterWrite(1);
262 } catch (final IOException e) {
263 handleIOException(e);
264 }
265 }
266
267 /**
268 * Invokes the delegate's {@code write(String)} method.
269 *
270 * @param str the string to write.
271 * @throws IOException if an I/O error occurs.
272 */
273 @Override
274 public void write(final String str) throws IOException {
275 try {
276 final int len = IOUtils.length(str);
277 beforeWrite(len);
278 out.write(str);
279 afterWrite(len);
280 } catch (final IOException e) {
281 handleIOException(e);
282 }
283 }
284
285 /**
286 * Invokes the delegate's {@code write(String)} method.
287 *
288 * @param str the string to write.
289 * @param off The start offset.
290 * @param len The number of characters to write.
291 * @throws IOException if an I/O error occurs.
292 */
293 @Override
294 public void write(final String str, final int off, final int len) throws IOException {
295 try {
296 beforeWrite(len);
297 out.write(str, off, len);
298 afterWrite(len);
299 } catch (final IOException e) {
300 handleIOException(e);
301 }
302 }
303
304 }