    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 */
    017
    018package org.apache.commons.io.output;
    019
    020import java.io.FilterWriter;
    021import java.io.IOException;
    022import java.io.Writer;
    023import java.util.ArrayList;
    024import java.util.Arrays;
    025import java.util.Collection;
    026import java.util.Collections;
    027import java.util.List;
    028
    029import org.apache.commons.io.IOExceptionList;
    030import org.apache.commons.io.IOIndexedException;
    031
    032/**
    033 * Abstract class for writing filtered character streams to a {@link Collection} of writers. This is in contrast to
    034 * {@link FilterWriter} which is backed by a single {@link Writer}.
    035 * <p>
    036 * This abstract class provides default methods that pass all requests to the contained writers. Subclasses should
    037 * likely override some of these methods.
    038 * </p>
    039 * <p>
    040 * The class {@link Writer} defines method signatures with {@code throws} {@link IOException}, which in this class are
    041 * actually {@link IOExceptionList} containing a list of {@link IOIndexedException}.
    042 * </p>
    043 *
    044 * @since 2.7
    045 */
    046public class FilterCollectionWriter extends Writer {
    047
    048    /**
    049     * Empty and immutable collection of writers.
    050     */
    051    protected final Collection<Writer> EMPTY_WRITERS = Collections.emptyList();
    052
    053    /**
    054     * The underlying writers.
    055     */
    056    protected final Collection<Writer> writers;
    057
    058    /**
    059     * Creates a new filtered collection writer.
    060     *
    061     * @param writers Writers to provide the underlying targets.
    062     */
    063    protected FilterCollectionWriter(final Collection<Writer> writers) {
    064        this.writers = writers == null ? EMPTY_WRITERS : writers;
    065    }
    066
    067    /**
    068     * Creates a new filtered collection writer.
    069     *
    070     * @param writers Writers to provide the underlying targets.
    071     */
    072    protected FilterCollectionWriter(final Writer... writers) {
    073        this.writers = writers == null ? EMPTY_WRITERS : Arrays.asList(writers);
    074    }
    075
    076    @Override
    077    public Writer append(final char c) throws IOException {
    078        final List<Exception> causeList = new ArrayList<>();
    079        int i = 0;
    080        for (final Writer w : writers) {
    081            if (w != null) {
    082                try {
    083                    w.append(c);
    084                } catch (final IOException e) {
    085                    causeList.add(new IOIndexedException(i, e));
    086                }
    087            }
    088            i++;
    089        }
    090        if (!causeList.isEmpty()) {
    091            throw new IOExceptionList(causeList);
    092        }
    093        return this;
    094    }
    095
    096    @Override
    097    public Writer append(final CharSequence csq) throws IOException {
    098        final List<Exception> causeList = new ArrayList<>();
    099        int i = 0;
    100        for (final Writer w : writers) {
    101            if (w != null) {
    102                try {
    103                    w.append(csq);
    104                } catch (final IOException e) {
    105                    causeList.add(new IOIndexedException(i, e));
    106                }
    107            }
    108            i++;
    109        }
    110        if (!causeList.isEmpty()) {
    111            throw new IOExceptionList(causeList);
    112        }
    113        return this;
    114    }
    115
    116    @Override
    117    public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
    118
    119        final List<Exception> causeList = new ArrayList<>();
    120        int i = 0;
    121        for (final Writer w : writers) {
    122            if (w != null) {
    123                try {
    124                    w.append(csq, start, end);
    125                } catch (final IOException e) {
    126                    causeList.add(new IOIndexedException(i, e));
    127                }
    128            }
    129            i++;
    130        }
    131        if (!causeList.isEmpty()) {
    132            throw new IOExceptionList(causeList);
    133        }
    134        return this;
    135    }
    136
    137    @Override
    138    public void close() throws IOException {
    139        final List<Exception> causeList = new ArrayList<>();
    140        int i = 0;
    141        for (final Writer w : writers) {
    142            if (w != null) {
    143                try {
    144                    w.close();
    145                } catch (final IOException e) {
    146                    causeList.add(new IOIndexedException(i, e));
    147                }
    148            }
    149            i++;
    150        }
    151        if (!causeList.isEmpty()) {
    152            throw new IOExceptionList(causeList);
    153        }
    154
    155    }
    156
    157    /**
    158     * Flushes the stream.
    159     *
    160     * @exception IOException If an I/O error occurs
    161     */
    162    @Override
    163    public void flush() throws IOException {
    164        final List<Exception> causeList = new ArrayList<>();
    165        int i = 0;
    166        for (final Writer w : writers) {
    167            if (w != null) {
    168                try {
    169                    w.flush();
    170                } catch (final IOException e) {
    171                    causeList.add(new IOIndexedException(i, e));
    172                }
    173            }
    174            i++;
    175        }
    176        if (!causeList.isEmpty()) {
    177            throw new IOExceptionList(causeList);
    178        }
    179
    180    }
    181
    182    /**
    183     * Writes a portion of an array of characters.
    184     *
    185     * @param cbuf Buffer of characters to be written
    186     * @param off  Offset from which to start reading characters
    187     * @param len  Number of characters to be written
    188     *
    189     * @exception IOException If an I/O error occurs
    190     */
    191    @Override
    192    public void write(final char[] cbuf, final int off, final int len) throws IOException {
    193        final List<Exception> causeList = new ArrayList<>();
    194        int i = 0;
    195        for (final Writer w : writers) {
    196            if (w != null) {
    197                try {
    198                    w.write(cbuf, off, len);
    199                } catch (final IOException e) {
    200                    causeList.add(new IOIndexedException(i, e));
    201                }
    202            }
    203            i++;
    204        }
    205        if (!causeList.isEmpty()) {
    206            throw new IOExceptionList(causeList);
    207        }
    208    }
    209
    210    @Override
    211    public void write(final char[] cbuf) throws IOException {
    212        final List<Exception> causeList = new ArrayList<>();
    213        int i = 0;
    214        for (final Writer w : writers) {
    215            if (w != null) {
    216                try {
    217                    w.write(cbuf);
    218                } catch (final IOException e) {
    219                    causeList.add(new IOIndexedException(i, e));
    220                }
    221            }
    222            i++;
    223        }
    224        if (!causeList.isEmpty()) {
    225            throw new IOExceptionList(causeList);
    226        }
    227    }
    228
    229    /**
    230     * Writes a single character.
    231     *
    232     * @exception IOException If an I/O error occurs
    233     */
    234    @Override
    235    public void write(final int c) throws IOException {
    236        final List<Exception> causeList = new ArrayList<>();
    237        int i = 0;
    238        for (final Writer w : writers) {
    239            if (w != null) {
    240                try {
    241                    w.write(c);
    242                } catch (final IOException e) {
    243                    causeList.add(new IOIndexedException(i, e));
    244                }
    245            }
    246            i++;
    247        }
    248        if (!causeList.isEmpty()) {
    249            throw new IOExceptionList(causeList);
    250        }
    251    }
    252
    253    @Override
    254    public void write(final String str) throws IOException {
    255        final List<Exception> causeList = new ArrayList<>();
    256        int i = 0;
    257        for (final Writer w : writers) {
    258            if (w != null) {
    259                try {
    260                    w.write(str);
    261                } catch (final IOException e) {
    262                    causeList.add(new IOIndexedException(i, e));
    263                }
    264            }
    265            i++;
    266        }
    267        if (!causeList.isEmpty()) {
    268            throw new IOExceptionList(causeList);
    269        }
    270
    271    }
    272
    273    /**
    274     * Writes a portion of a string.
    275     *
    276     * @param str String to be written
    277     * @param off Offset from which to start reading characters
    278     * @param len Number of characters to be written
    279     *
    280     * @exception IOException If an I/O error occurs
    281     */
    282    @Override
    283    public void write(final String str, final int off, final int len) throws IOException {
    284        final List<Exception> causeList = new ArrayList<>();
    285        int i = 0;
    286        for (final Writer w : writers) {
    287            if (w != null) {
    288                try {
    289                    w.write(str, off, len);
    290                } catch (final IOException e) {
    291                    causeList.add(new IOIndexedException(i, e));
    292                }
    293            }
    294            i++;
    295        }
    296        if (!causeList.isEmpty()) {
    297            throw new IOExceptionList(causeList);
    298        }
    299
    300    }
    301
    302}