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)} 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)} 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)} 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)} 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[])} 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)} 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)} 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)} 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()} 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()} method.
204     * @throws IOException if an I/O error occurs.
205     */
206    @Override
207    public void close() throws IOException {
208        IOUtils.close(out, this::handleIOException);
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    @SuppressWarnings("unused") // Possibly thrown from subclasses.
226    protected void beforeWrite(final int n) throws IOException {
227        // noop
228    }
229
230    /**
231     * Invoked by the write methods after the proxied call has returned
232     * successfully. The number of chars written (1 for the
233     * {@link #write(int)} method, buffer length for {@link #write(char[])},
234     * etc.) is given as an argument.
235     * <p>
236     * Subclasses can override this method to add common post-processing
237     * functionality without having to override all the write methods.
238     * The default implementation does nothing.
239     * </p>
240     *
241     * @since 2.0
242     * @param n number of chars written
243     * @throws IOException if the post-processing fails
244     */
245    @SuppressWarnings("unused") // Possibly thrown from subclasses.
246    protected void afterWrite(final int n) throws IOException {
247        // noop
248    }
249
250    /**
251     * Handle any IOExceptions thrown.
252     * <p>
253     * This method provides a point to implement custom exception
254     * handling. The default behavior is to re-throw the exception.
255     * </p>
256     *
257     * @param e The IOException thrown
258     * @throws IOException if an I/O error occurs.
259     * @since 2.0
260     */
261    protected void handleIOException(final IOException e) throws IOException {
262        throw e;
263    }
264
265}