View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.io.output;
19  
20  import java.io.FilterWriter;
21  import java.io.IOException;
22  import java.io.Writer;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Objects;
27  import java.util.stream.Stream;
28  
29  import org.apache.commons.io.IOExceptionList;
30  import org.apache.commons.io.IOIndexedException;
31  import org.apache.commons.io.function.IOConsumer;
32  
33  /**
34   * Abstract class for writing filtered character streams to a {@link Collection} of writers. This is in contrast to
35   * {@link FilterWriter} which is backed by a single {@link Writer}.
36   * <p>
37   * This abstract class provides default methods that pass all requests to the contained writers. Subclasses should
38   * likely override some of these methods.
39   * </p>
40   * <p>
41   * The class {@link Writer} defines method signatures with {@code throws} {@link IOException}, which in this class are
42   * actually {@link IOExceptionList} containing a list of {@link IOIndexedException}.
43   * </p>
44   *
45   * @since 2.7
46   */
47  public class FilterCollectionWriter extends Writer {
48  
49      /**
50       * Empty and immutable collection of writers.
51       */
52      protected final Collection<Writer> EMPTY_WRITERS = Collections.emptyList();
53  
54      /**
55       * The underlying writers.
56       */
57      protected final Collection<Writer> writers;
58  
59      /**
60       * Constructs a new filtered collection writer.
61       *
62       * @param writers Writers to provide the underlying targets.
63       */
64      protected FilterCollectionWriter(final Collection<Writer> writers) {
65          this.writers = writers == null ? EMPTY_WRITERS : writers;
66      }
67  
68      /**
69       * Constructs a new filtered collection writer.
70       *
71       * @param writers Writers to provide the underlying targets.
72       */
73      protected FilterCollectionWriter(final Writer... writers) {
74          this.writers = writers == null ? EMPTY_WRITERS : Arrays.asList(writers);
75      }
76  
77      @Override
78      public Writer append(final char c) throws IOException {
79          return forAllWriters(w -> w.append(c));
80      }
81  
82      @Override
83      public Writer append(final CharSequence csq) throws IOException {
84          return forAllWriters(w -> w.append(csq));
85      }
86  
87      @Override
88      public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
89          return forAllWriters(w -> w.append(csq, start, end));
90      }
91  
92      @SuppressWarnings("resource") // no allocation
93      @Override
94      public void close() throws IOException {
95          forAllWriters(Writer::close);
96      }
97  
98      /**
99       * Flushes the stream.
100      *
101      * @throws IOException If an I/O error occurs
102      */
103     @SuppressWarnings("resource") // no allocation
104     @Override
105     public void flush() throws IOException {
106         forAllWriters(Writer::flush);
107     }
108 
109     private FilterCollectionWriter forAllWriters(final IOConsumer<Writer> action) throws IOExceptionList {
110         IOConsumer.forAll(action, writers());
111         return this;
112     }
113 
114     @SuppressWarnings("resource") // no allocation
115     @Override
116     public void write(final char[] cbuf) throws IOException {
117         forAllWriters(w -> w.write(cbuf));
118     }
119 
120     /**
121      * Writes a portion of an array of characters.
122      *
123      * @param cbuf Buffer of characters to be written
124      * @param off  Offset from which to start reading characters
125      * @param len  Number of characters to be written
126      * @throws IOException If an I/O error occurs
127      */
128     @SuppressWarnings("resource") // no allocation
129     @Override
130     public void write(final char[] cbuf, final int off, final int len) throws IOException {
131         forAllWriters(w -> w.write(cbuf, off, len));
132     }
133 
134     /**
135      * Writes a single character.
136      *
137      * @throws IOException If an I/O error occurs
138      */
139     @SuppressWarnings("resource") // no allocation
140     @Override
141     public void write(final int c) throws IOException {
142         forAllWriters(w -> w.write(c));
143     }
144 
145     @SuppressWarnings("resource") // no allocation
146     @Override
147     public void write(final String str) throws IOException {
148         forAllWriters(w -> w.write(str));
149     }
150 
151     /**
152      * Writes a portion of a string.
153      *
154      * @param str String to be written
155      * @param off Offset from which to start reading characters
156      * @param len Number of characters to be written
157      * @throws IOException If an I/O error occurs
158      */
159     @SuppressWarnings("resource") // no allocation
160     @Override
161     public void write(final String str, final int off, final int len) throws IOException {
162         forAllWriters(w -> w.write(str, off, len));
163     }
164 
165     private Stream<Writer> writers() {
166         return writers.stream().filter(Objects::nonNull);
167     }
168 
169 }