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    *      http://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 proxy  the Writer to delegate to
37       */
38      public ProxyWriter(final Writer proxy) {
39          super(proxy);
40          // the proxy is stored in a protected superclass variable named 'out'
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       * @since 2.0
55       * @param n number of chars written
56       * @throws IOException if the post-processing fails
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      * @since 2.0
134      * @param n number of chars to be written
135      * @throws IOException if the pre-processing fails
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 }