001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import java.io.*; 016import java.util.*; 017 018/** 019 * Writer that can send output to multiple writers. 020 */ 021public class TeeWriter extends Writer { 022 private Writer[] writers = new Writer[0]; 023 private Map<String,Writer> writerMap; 024 025 /** 026 * Constructor. 027 * 028 * @param writers The list of writers. 029 */ 030 public TeeWriter(Writer...writers) { 031 this.writers = writers; 032 } 033 034 /** 035 * Constructor. 036 * 037 * @param writers The list of writers. 038 */ 039 public TeeWriter(Collection<Writer> writers) { 040 this.writers = writers.toArray(new Writer[writers.size()]); 041 } 042 043 /** 044 * Adds a writer to this tee writer. 045 * 046 * @param w The writer to add to this tee writer. 047 * @param close 048 * If <jk>false</jk>, then calling {@link #close()} on this tee writer will not filter to the specified writer. 049 * @return This object (for method chaining). 050 */ 051 public TeeWriter add(Writer w, boolean close) { 052 if (w == null) 053 return this; 054 if (! close) 055 w = new NoCloseWriter(w); 056 if (w == this) 057 throw new RuntimeException("Cannot add this writer to itself."); 058 for (Writer w2 : writers) 059 if (w2 == w) 060 throw new RuntimeException("Cannot add this writer again."); 061 if (w instanceof TeeWriter) { 062 for (Writer w2 : ((TeeWriter)w).writers) 063 add(w2, true); 064 } else { 065 writers = ArrayUtils.append(writers, w); 066 } 067 return this; 068 } 069 070 /** 071 * Same as {@link #add(Writer, boolean)} but associates the writer with an identifier so the writer can be retrieved 072 * through {@link #getWriter(String)}. 073 * 074 * @param id The ID to associate the writer with. 075 * @param w The writer to add. 076 * @param close Close the specified writer afterwards. 077 * @return This object (for method chaining). 078 */ 079 public TeeWriter add(String id, Writer w, boolean close) { 080 if (id != null) { 081 if (writerMap == null) 082 writerMap = new TreeMap<>(); 083 writerMap.put(id, w); 084 } 085 return add(w, close); 086 } 087 088 /** 089 * Returns the number of inner writers in this tee writer. 090 * 091 * @return The number of writers. 092 */ 093 public int size() { 094 return writers.length; 095 } 096 097 /** 098 * Returns the writer identified through the <c>id</c> parameter passed in through the 099 * {@link #add(String, Writer, boolean)} method. 100 * 101 * @param id The ID associated with the writer. 102 * @return The writer, or <jk>null</jk> if no identifier was specified when the writer was added. 103 */ 104 public Writer getWriter(String id) { 105 if (writerMap != null) 106 return writerMap.get(id); 107 return null; 108 } 109 110 @Override /* Writer */ 111 public void write(char[] cbuf, int off, int len) throws IOException { 112 for (Writer w : writers) 113 if (w != null) 114 w.write(cbuf, off, len); 115 } 116 117 @Override /* Writer */ 118 public void flush() throws IOException { 119 for (Writer w : writers) 120 if (w != null) 121 w.flush(); 122 } 123 124 @Override /* Writer */ 125 public void close() throws IOException { 126 IOException e = null; 127 for (Writer w : writers) { 128 if (w != null) { 129 try { 130 w.close(); 131 } catch (IOException e2) { 132 e = e2; 133 } 134 } 135 } 136 if (e != null) 137 throw e; 138 } 139}