001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.io.output;
018
019import java.io.FilterWriter;
020import java.io.IOException;
021import java.io.Writer;
022
023import org.apache.commons.io.IOUtils;
024
025/**
026 * A Proxy stream which acts as expected, that is it passes the method calls on to the proxied stream and doesn't
027 * change which methods are being called. It is an alternative base class to FilterWriter to increase reusability,
028 * because FilterWriter changes the methods being called, such as {@code write(char[]) to write(char[], int, int)}
029 * and {@code write(String) to write(String, int, int)}.
030 */
031public class ProxyWriter extends FilterWriter {
032
033    /**
034     * Constructs a new ProxyWriter.
035     *
036     * @param proxy  the Writer to delegate to
037     */
038    public ProxyWriter(final Writer proxy) {
039        super(proxy);
040        // the proxy is stored in a protected superclass variable named 'out'
041    }
042
043    /**
044     * Invokes the delegate's <code>append(char)</code> method.
045     * @param c The character to write
046     * @return this writer
047     * @throws IOException if an I/O error occurs
048     * @since 2.0
049     */
050    @Override
051    public Writer append(final char c) throws IOException {
052        try {
053            beforeWrite(1);
054            out.append(c);
055            afterWrite(1);
056        } catch (final IOException e) {
057            handleIOException(e);
058        }
059        return this;
060    }
061
062    /**
063     * Invokes the delegate's <code>append(CharSequence, int, int)</code> method.
064     * @param csq The character sequence to write
065     * @param start The index of the first character to write
066     * @param end  The index of the first character to write (exclusive)
067     * @return this writer
068     * @throws IOException if an I/O error occurs
069     * @since 2.0
070     */
071    @Override
072    public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
073        try {
074            beforeWrite(end - start);
075            out.append(csq, start, end);
076            afterWrite(end - start);
077        } catch (final IOException e) {
078            handleIOException(e);
079        }
080        return this;
081    }
082
083    /**
084     * Invokes the delegate's <code>append(CharSequence)</code> method.
085     * @param csq The character sequence to write
086     * @return this writer
087     * @throws IOException if an I/O error occurs
088     * @since 2.0
089     */
090    @Override
091    public Writer append(final CharSequence csq) throws IOException {
092        try {
093            final int len = IOUtils.length(csq);
094            beforeWrite(len);
095            out.append(csq);
096            afterWrite(len);
097        } catch (final IOException e) {
098            handleIOException(e);
099        }
100        return this;
101    }
102
103    /**
104     * Invokes the delegate's <code>write(int)</code> method.
105     * @param c the character to write
106     * @throws IOException if an I/O error occurs
107     */
108    @Override
109    public void write(final int c) throws IOException {
110        try {
111            beforeWrite(1);
112            out.write(c);
113            afterWrite(1);
114        } catch (final IOException e) {
115            handleIOException(e);
116        }
117    }
118
119    /**
120     * Invokes the delegate's <code>write(char[])</code> method.
121     * @param cbuf the characters to write
122     * @throws IOException if an I/O error occurs
123     */
124    @Override
125    public void write(final char[] cbuf) throws IOException {
126        try {
127            final int len = IOUtils.length(cbuf);
128            beforeWrite(len);
129            out.write(cbuf);
130            afterWrite(len);
131        } catch (final IOException e) {
132            handleIOException(e);
133        }
134    }
135
136    /**
137     * Invokes the delegate's <code>write(char[], int, int)</code> method.
138     * @param cbuf the characters to write
139     * @param off The start offset
140     * @param len The number of characters to write
141     * @throws IOException if an I/O error occurs
142     */
143    @Override
144    public void write(final char[] cbuf, final int off, final int len) throws IOException {
145        try {
146            beforeWrite(len);
147            out.write(cbuf, off, len);
148            afterWrite(len);
149        } catch (final IOException e) {
150            handleIOException(e);
151        }
152    }
153
154    /**
155     * Invokes the delegate's <code>write(String)</code> method.
156     * @param str the string to write
157     * @throws IOException if an I/O error occurs
158     */
159    @Override
160    public void write(final String str) throws IOException {
161        try {
162            final int len = IOUtils.length(str);
163            beforeWrite(len);
164            out.write(str);
165            afterWrite(len);
166        } catch (final IOException e) {
167            handleIOException(e);
168        }
169    }
170
171    /**
172     * Invokes the delegate's <code>write(String)</code> method.
173     * @param str the string to write
174     * @param off The start offset
175     * @param len The number of characters to write
176     * @throws IOException if an I/O error occurs
177     */
178    @Override
179    public void write(final String str, final int off, final int len) throws IOException {
180        try {
181            beforeWrite(len);
182            out.write(str, off, len);
183            afterWrite(len);
184        } catch (final IOException e) {
185            handleIOException(e);
186        }
187    }
188
189    /**
190     * Invokes the delegate's <code>flush()</code> method.
191     * @throws IOException if an I/O error occurs
192     */
193    @Override
194    public void flush() throws IOException {
195        try {
196            out.flush();
197        } catch (final IOException e) {
198            handleIOException(e);
199        }
200    }
201
202    /**
203     * Invokes the delegate's <code>close()</code> method.
204     * @throws IOException if an I/O error occurs
205     */
206    @Override
207    public void close() throws IOException {
208        IOUtils.close(out, e -> handleIOException(e));
209    }
210
211    /**
212     * Invoked by the write methods before the call is proxied. The number
213     * of chars to be written (1 for the {@link #write(int)} method, buffer
214     * length for {@link #write(char[])}, etc.) is given as an argument.
215     * <p>
216     * Subclasses can override this method to add common pre-processing
217     * functionality without having to override all the write methods.
218     * The default implementation does nothing.
219     * </p>
220     *
221     * @since 2.0
222     * @param n number of chars to be written
223     * @throws IOException if the pre-processing fails
224     */
225    protected void beforeWrite(final int n) throws IOException {
226        // noop
227    }
228
229    /**
230     * Invoked by the write methods after the proxied call has returned
231     * successfully. The number of chars written (1 for the
232     * {@link #write(int)} method, buffer length for {@link #write(char[])},
233     * etc.) is given as an argument.
234     * <p>
235     * Subclasses can override this method to add common post-processing
236     * functionality without having to override all the write methods.
237     * The default implementation does nothing.
238     * </p>
239     *
240     * @since 2.0
241     * @param n number of chars written
242     * @throws IOException if the post-processing fails
243     */
244    protected void afterWrite(final int n) throws IOException {
245        // noop
246    }
247
248    /**
249     * Handle any IOExceptions thrown.
250     * <p>
251     * This method provides a point to implement custom exception
252     * handling. The default behavior is to re-throw the exception.
253     * </p>
254     *
255     * @param e The IOException thrown
256     * @throws IOException if an I/O error occurs
257     * @since 2.0
258     */
259    protected void handleIOException(final IOException e) throws IOException {
260        throw e;
261    }
262
263}