    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}