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 * https://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 }