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