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 /** 077 * Adds an indexed exception to the list. 078 * 079 * @param causeList The target list. 080 * @param i The index. 081 * @param e The cause. 082 * @return the given list or a new list on null input. 083 */ 084 private List<Exception> add(List<Exception> causeList, final int i, final IOException e) { 085 if (causeList == null) { 086 causeList = new ArrayList<>(); 087 } 088 causeList.add(new IOIndexedException(i, e)); 089 return causeList; 090 } 091 092 @Override 093 public Writer append(final char c) throws IOException { 094 List<Exception> causeList = null; 095 int i = 0; 096 for (final Writer w : writers) { 097 if (w != null) { 098 try { 099 w.append(c); 100 } catch (final IOException e) { 101 causeList = add(causeList, i, e); 102 } 103 } 104 i++; 105 } 106 if (notEmpty(causeList)) { 107 throw new IOExceptionList("append", causeList); 108 } 109 return this; 110 } 111 112 @Override 113 public Writer append(final CharSequence csq) throws IOException { 114 List<Exception> causeList = null; 115 int i = 0; 116 for (final Writer w : writers) { 117 if (w != null) { 118 try { 119 w.append(csq); 120 } catch (final IOException e) { 121 causeList = add(causeList, i, e); 122 } 123 } 124 i++; 125 } 126 if (notEmpty(causeList)) { 127 throw new IOExceptionList("append", causeList); 128 } 129 return this; 130 } 131 132 @Override 133 public Writer append(final CharSequence csq, final int start, final int end) throws IOException { 134 135 List<Exception> causeList = null; 136 int i = 0; 137 for (final Writer w : writers) { 138 if (w != null) { 139 try { 140 w.append(csq, start, end); 141 } catch (final IOException e) { 142 causeList = add(causeList, i, e); 143 } 144 } 145 i++; 146 } 147 if (notEmpty(causeList)) { 148 throw new IOExceptionList("append", causeList); 149 } 150 return this; 151 } 152 153 @Override 154 public void close() throws IOException { 155 List<Exception> causeList = null; 156 int i = 0; 157 for (final Writer w : writers) { 158 if (w != null) { 159 try { 160 w.close(); 161 } catch (final IOException e) { 162 causeList = add(causeList, i, e); 163 } 164 } 165 i++; 166 } 167 if (notEmpty(causeList)) { 168 throw new IOExceptionList("close", causeList); 169 } 170 171 } 172 173 /** 174 * Flushes the stream. 175 * 176 * @exception IOException If an I/O error occurs 177 */ 178 @Override 179 public void flush() throws IOException { 180 List<Exception> causeList = null; 181 int i = 0; 182 for (final Writer w : writers) { 183 if (w != null) { 184 try { 185 w.flush(); 186 } catch (final IOException e) { 187 causeList = add(causeList, i, e); 188 } 189 } 190 i++; 191 } 192 if (notEmpty(causeList)) { 193 throw new IOExceptionList("flush", causeList); 194 } 195 196 } 197 198 /** 199 * Tests if the given list is empty in a null-safe manner. 200 * 201 * @param causeList the list to test. 202 * @return true if empty or null. 203 */ 204 private boolean notEmpty(final List<Exception> causeList) { 205 return causeList != null && !causeList.isEmpty(); 206 } 207 208 @Override 209 public void write(final char[] cbuf) throws IOException { 210 List<Exception> causeList = null; 211 int i = 0; 212 for (final Writer w : writers) { 213 if (w != null) { 214 try { 215 w.write(cbuf); 216 } catch (final IOException e) { 217 causeList = add(causeList, i, e); 218 } 219 } 220 i++; 221 } 222 if (notEmpty(causeList)) { 223 throw new IOExceptionList("write", causeList); 224 } 225 } 226 227 /** 228 * Writes a portion of an array of characters. 229 * 230 * @param cbuf Buffer of characters to be written 231 * @param off Offset from which to start reading characters 232 * @param len Number of characters to be written 233 * 234 * @exception IOException If an I/O error occurs 235 */ 236 @Override 237 public void write(final char[] cbuf, final int off, final int len) throws IOException { 238 List<Exception> causeList = null; 239 int i = 0; 240 for (final Writer w : writers) { 241 if (w != null) { 242 try { 243 w.write(cbuf, off, len); 244 } catch (final IOException e) { 245 causeList = add(causeList, i, e); 246 } 247 } 248 i++; 249 } 250 if (notEmpty(causeList)) { 251 throw new IOExceptionList("write", causeList); 252 } 253 } 254 255 /** 256 * Writes a single character. 257 * 258 * @exception IOException If an I/O error occurs 259 */ 260 @Override 261 public void write(final int c) throws IOException { 262 List<Exception> causeList = null; 263 int i = 0; 264 for (final Writer w : writers) { 265 if (w != null) { 266 try { 267 w.write(c); 268 } catch (final IOException e) { 269 causeList = add(causeList, i, e); 270 } 271 } 272 i++; 273 } 274 if (notEmpty(causeList)) { 275 throw new IOExceptionList("write", causeList); 276 } 277 } 278 279 @Override 280 public void write(final String str) throws IOException { 281 List<Exception> causeList = null; 282 int i = 0; 283 for (final Writer w : writers) { 284 if (w != null) { 285 try { 286 w.write(str); 287 } catch (final IOException e) { 288 causeList = add(causeList, i, e); 289 } 290 } 291 i++; 292 } 293 if (notEmpty(causeList)) { 294 throw new IOExceptionList("write", causeList); 295 } 296 297 } 298 299 /** 300 * Writes a portion of a string. 301 * 302 * @param str String to be written 303 * @param off Offset from which to start reading characters 304 * @param len Number of characters to be written 305 * 306 * @exception IOException If an I/O error occurs 307 */ 308 @Override 309 public void write(final String str, final int off, final int len) throws IOException { 310 List<Exception> causeList = null; 311 int i = 0; 312 for (final Writer w : writers) { 313 if (w != null) { 314 try { 315 w.write(str, off, len); 316 } catch (final IOException e) { 317 causeList = add(causeList, i, e); 318 } 319 } 320 i++; 321 } 322 if (notEmpty(causeList)) { 323 throw new IOExceptionList("write", causeList); 324 } 325 326 } 327 328}