    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;
    022import java.util.Collection;
    023
    024import org.apache.commons.io.IOUtils;
    025
    026/**
    027 * A Proxy stream collection which acts as expected, that is it passes the method calls on to the proxied streams and
    028 * doesn't change which methods are being called. It is an alternative base class to {@link FilterWriter} and
    029 * {@link FilterCollectionWriter} to increase reusability, because FilterWriter changes the methods being called, such
    030 * as {@code write(char[])} to {@code write(char[], int, int)} and {@code write(String)} to
    031 * {@code write(String, int, int)}. This is in contrast to {@link ProxyWriter} which is backed by a single
    032 * {@link Writer}.
    033 *
    034 * @since 2.7
    035 */
    036public class ProxyCollectionWriter extends FilterCollectionWriter {
    037
    038    /**
    039     * Constructs a new proxy collection writer.
    040     *
    041     * @param writers Writers object to provide the underlying targets.
    042     */
    043    public ProxyCollectionWriter(final Collection<Writer> writers) {
    044        super(writers);
    045    }
    046
    047    /**
    048     * Constructs a new proxy collection writer.
    049     *
    050     * @param writers Writers to provide the underlying targets.
    051     */
    052    public ProxyCollectionWriter(final Writer... writers) {
    053        super(writers);
    054    }
    055
    056    /**
    057     * Invoked by the write methods after the proxied call has returned successfully. The number of chars written (1 for
    058     * the {@link #write(int)} method, buffer length for {@link #write(char[])}, etc.) is given as an argument.
    059     * <p>
    060     * Subclasses can override this method to add common post-processing functionality without having to override all
    061     * the write methods. The default implementation does nothing.
    062     * </p>
    063     *
    064     * @param n number of chars written
    065     * @throws IOException if the post-processing fails
    066     */
    067    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    068    protected void afterWrite(final int n) throws IOException {
    069        // noop
    070    }
    071
    072    /**
    073     * Invokes the delegates' {@code append(char)} methods.
    074     *
    075     * @param c The character to write
    076     * @return this writer
    077     * @throws IOException if an I/O error occurs.
    078     * @since 2.0
    079     */
    080    @SuppressWarnings("resource") // Fluent API.
    081    @Override
    082    public Writer append(final char c) throws IOException {
    083        try {
    084            beforeWrite(1);
    085            super.append(c);
    086            afterWrite(1);
    087        } catch (final IOException e) {
    088            handleIOException(e);
    089        }
    090        return this;
    091    }
    092
    093    /**
    094     * Invokes the delegates' {@code append(CharSequence)} methods.
    095     *
    096     * @param csq The character sequence to write
    097     * @return this writer
    098     * @throws IOException if an I/O error occurs.
    099     */
    100    @SuppressWarnings("resource") // Fluent API.
    101    @Override
    102    public Writer append(final CharSequence csq) throws IOException {
    103        try {
    104            final int len = IOUtils.length(csq);
    105            beforeWrite(len);
    106            super.append(csq);
    107            afterWrite(len);
    108        } catch (final IOException e) {
    109            handleIOException(e);
    110        }
    111        return this;
    112    }
    113
    114    /**
    115     * Invokes the delegates' {@code append(CharSequence, int, int)} methods.
    116     *
    117     * @param csq   The character sequence to write
    118     * @param start The index of the first character to write
    119     * @param end   The index of the first character to write (exclusive)
    120     * @return this writer
    121     * @throws IOException if an I/O error occurs.
    122     */
    123    @SuppressWarnings("resource") // Fluent API.
    124    @Override
    125    public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
    126        try {
    127            beforeWrite(end - start);
    128            super.append(csq, start, end);
    129            afterWrite(end - start);
    130        } catch (final IOException e) {
    131            handleIOException(e);
    132        }
    133        return this;
    134    }
    135
    136    /**
    137     * Invoked by the write methods before the call is proxied. The number of chars to be written (1 for the
    138     * {@link #write(int)} method, buffer length for {@link #write(char[])}, etc.) is given as an argument.
    139     * <p>
    140     * Subclasses can override this method to add common pre-processing functionality without having to override all the
    141     * write methods. The default implementation does nothing.
    142     * </p>
    143     *
    144     * @param n number of chars to be written
    145     * @throws IOException if the pre-processing fails
    146     */
    147    @SuppressWarnings("unused") // Possibly thrown from subclasses.
    148    protected void beforeWrite(final int n) throws IOException {
    149        // noop
    150    }
    151
    152    /**
    153     * Invokes the delegate's {@code close()} method.
    154     *
    155     * @throws IOException if an I/O error occurs.
    156     */
    157    @Override
    158    public void close() throws IOException {
    159        try {
    160            super.close();
    161        } catch (final IOException e) {
    162            handleIOException(e);
    163        }
    164    }
    165
    166    /**
    167     * Invokes the delegate's {@code flush()} method.
    168     *
    169     * @throws IOException if an I/O error occurs.
    170     */
    171    @Override
    172    public void flush() throws IOException {
    173        try {
    174            super.flush();
    175        } catch (final IOException e) {
    176            handleIOException(e);
    177        }
    178    }
    179
    180    /**
    181     * Handle any IOExceptions thrown.
    182     * <p>
    183     * This method provides a point to implement custom exception handling. The default behavior is to re-throw the
    184     * exception.
    185     * </p>
    186     *
    187     * @param e The IOException thrown
    188     * @throws IOException if an I/O error occurs.
    189     */
    190    protected void handleIOException(final IOException e) throws IOException {
    191        throw e;
    192    }
    193
    194    /**
    195     * Invokes the delegate's {@code write(char[])} method.
    196     *
    197     * @param cbuf the characters to write
    198     * @throws IOException if an I/O error occurs.
    199     */
    200    @Override
    201    public void write(final char[] cbuf) throws IOException {
    202        try {
    203            final int len = IOUtils.length(cbuf);
    204            beforeWrite(len);
    205            super.write(cbuf);
    206            afterWrite(len);
    207        } catch (final IOException e) {
    208            handleIOException(e);
    209        }
    210    }
    211
    212    /**
    213     * Invokes the delegate's {@code write(char[], int, int)} method.
    214     *
    215     * @param cbuf the characters to write
    216     * @param off  The start offset
    217     * @param len  The number of characters to write
    218     * @throws IOException if an I/O error occurs.
    219     */
    220    @Override
    221    public void write(final char[] cbuf, final int off, final int len) throws IOException {
    222        try {
    223            beforeWrite(len);
    224            super.write(cbuf, off, len);
    225            afterWrite(len);
    226        } catch (final IOException e) {
    227            handleIOException(e);
    228        }
    229    }
    230
    231    /**
    232     * Invokes the delegate's {@code write(int)} method.
    233     *
    234     * @param c the character to write
    235     * @throws IOException if an I/O error occurs.
    236     */
    237    @Override
    238    public void write(final int c) throws IOException {
    239        try {
    240            beforeWrite(1);
    241            super.write(c);
    242            afterWrite(1);
    243        } catch (final IOException e) {
    244            handleIOException(e);
    245        }
    246    }
    247
    248    /**
    249     * Invokes the delegate's {@code write(String)} method.
    250     *
    251     * @param str the string to write
    252     * @throws IOException if an I/O error occurs.
    253     */
    254    @Override
    255    public void write(final String str) throws IOException {
    256        try {
    257            final int len = IOUtils.length(str);
    258            beforeWrite(len);
    259            super.write(str);
    260            afterWrite(len);
    261        } catch (final IOException e) {
    262            handleIOException(e);
    263        }
    264    }
    265
    266    /**
    267     * Invokes the delegate's {@code write(String)} method.
    268     *
    269     * @param str the string to write
    270     * @param off The start offset
    271     * @param len The number of characters to write
    272     * @throws IOException if an I/O error occurs.
    273     */
    274    @Override
    275    public void write(final String str, final int off, final int len) throws IOException {
    276        try {
    277            beforeWrite(len);
    278            super.write(str, off, len);
    279            afterWrite(len);
    280        } catch (final IOException e) {
    281            handleIOException(e);
    282        }
    283    }
    284
    285}