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 <code>id</code> 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}