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 */ 017package org.apache.commons.io.input; 018 019import static org.apache.commons.io.IOUtils.EOF; 020 021import java.io.IOException; 022import java.io.Reader; 023import java.io.Writer; 024import java.nio.CharBuffer; 025 026/** 027 * Reader proxy that transparently writes a copy of all characters read from the proxied reader to a given Reader. Using 028 * {@link #skip(long)} or {@link #mark(int)}/{@link #reset()} on the reader will result on some characters from the 029 * reader being skipped or duplicated in the writer. 030 * <p> 031 * The proxied reader is closed when the {@link #close()} method is called on this proxy. You may configure whether the 032 * reader closes the writer. 033 * </p> 034 * 035 * @since 2.7 036 */ 037public class TeeReader extends ProxyReader { 038 039 /** 040 * The writer that will receive a copy of all characters read from the proxied reader. 041 */ 042 private final Writer branch; 043 044 /** 045 * Flag for closing the associated writer when this reader is closed. 046 */ 047 private final boolean closeBranch; 048 049 /** 050 * Creates a TeeReader that proxies the given {@link Reader} and copies all read characters to the given 051 * {@link Writer}. The given writer will not be closed when this reader gets closed. 052 * 053 * @param input reader to be proxied 054 * @param branch writer that will receive a copy of all characters read 055 */ 056 public TeeReader(final Reader input, final Writer branch) { 057 this(input, branch, false); 058 } 059 060 /** 061 * Creates a TeeReader that proxies the given {@link Reader} and copies all read characters to the given 062 * {@link Writer}. The given writer will be closed when this reader gets closed if the closeBranch parameter is 063 * {@code true}. 064 * 065 * @param input reader to be proxied 066 * @param branch writer that will receive a copy of all characters read 067 * @param closeBranch flag for closing also the writer when this reader is closed 068 */ 069 public TeeReader(final Reader input, final Writer branch, final boolean closeBranch) { 070 super(input); 071 this.branch = branch; 072 this.closeBranch = closeBranch; 073 } 074 075 /** 076 * Closes the proxied reader and, if so configured, the associated writer. An exception thrown from the reader will 077 * not prevent closing of the writer. 078 * 079 * @throws IOException if either the reader or writer could not be closed 080 */ 081 @Override 082 public void close() throws IOException { 083 try { 084 super.close(); 085 } finally { 086 if (closeBranch) { 087 branch.close(); 088 } 089 } 090 } 091 092 /** 093 * Reads a single chracter from the proxied reader and writes it to the associated writer. 094 * 095 * @return next character from the reader, or -1 if the reader has ended 096 * @throws IOException if the reader could not be read (or written) 097 */ 098 @Override 099 public int read() throws IOException { 100 final int ch = super.read(); 101 if (ch != EOF) { 102 branch.write(ch); 103 } 104 return ch; 105 } 106 107 /** 108 * Reads characters from the proxied reader and writes the read characters to the associated writer. 109 * 110 * @param chr character buffer 111 * @return number of characters read, or -1 if the reader has ended 112 * @throws IOException if the reader could not be read (or written) 113 */ 114 @Override 115 public int read(final char[] chr) throws IOException { 116 final int n = super.read(chr); 117 if (n != EOF) { 118 branch.write(chr, 0, n); 119 } 120 return n; 121 } 122 123 /** 124 * Reads characters from the proxied reader and writes the read characters to the associated writer. 125 * 126 * @param chr character buffer 127 * @param st start offset within the buffer 128 * @param end maximum number of characters to read 129 * @return number of characters read, or -1 if the reader has ended 130 * @throws IOException if the reader could not be read (or written) 131 */ 132 @Override 133 public int read(final char[] chr, final int st, final int end) throws IOException { 134 final int n = super.read(chr, st, end); 135 if (n != EOF) { 136 branch.write(chr, st, n); 137 } 138 return n; 139 } 140 141 /** 142 * Reads characters from the proxied reader and writes the read characters to the associated writer. 143 * 144 * @param target character buffer 145 * @return number of characters read, or -1 if the reader has ended 146 * @throws IOException if the reader could not be read (or written) 147 */ 148 @Override 149 public int read(final CharBuffer target) throws IOException { 150 final int originalPosition = target.position(); 151 final int n = super.read(target); 152 if (n != EOF) { 153 // Appending can only be done after resetting the CharBuffer to the 154 // right position and limit. 155 final int newPosition = target.position(); 156 final int newLimit = target.limit(); 157 try { 158 target.position(originalPosition).limit(newPosition); 159 branch.append(target); 160 } finally { 161 // Reset the CharBuffer as if the appending never happened. 162 target.position(newPosition).limit(newLimit); 163 } 164 } 165 return n; 166 } 167 168}