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.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 }