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}