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.SequenceInputStream; 024import java.util.Arrays; 025import java.util.Iterator; 026import java.util.Objects; 027 028import org.apache.commons.io.function.Uncheck; 029 030/** 031 * Provides the contents of multiple {@link Reader}s in sequence. 032 * <p> 033 * Like {@link SequenceInputStream} but for {@link Reader} arguments. 034 * </p> 035 * 036 * @since 2.7 037 */ 038public class SequenceReader extends Reader { 039 040 private Reader reader; 041 private final Iterator<? extends Reader> readers; 042 043 /** 044 * Constructs a new instance with readers 045 * 046 * @param readers the readers to read 047 */ 048 public SequenceReader(final Iterable<? extends Reader> readers) { 049 this.readers = Objects.requireNonNull(readers, "readers").iterator(); 050 this.reader = Uncheck.get(this::nextReader); 051 } 052 053 /** 054 * Constructs a new instance with readers 055 * 056 * @param readers the readers to read 057 */ 058 public SequenceReader(final Reader... readers) { 059 this(Arrays.asList(readers)); 060 } 061 062 /* 063 * (non-Javadoc) 064 * 065 * @see java.io.Reader#close() 066 */ 067 @Override 068 public void close() throws IOException { 069 do { // NOPMD 070 // empty 071 } while (nextReader() != null); 072 } 073 074 /** 075 * Returns the next available reader or null if done. 076 * 077 * @return the next available reader or null. 078 * @throws IOException IOException If an I/O error occurs. 079 */ 080 private Reader nextReader() throws IOException { 081 if (reader != null) { 082 reader.close(); 083 } 084 if (readers.hasNext()) { 085 reader = readers.next(); 086 } else { 087 reader = null; 088 } 089 return reader; 090 } 091 092 /* 093 * (non-Javadoc) 094 * 095 * @see java.io.Reader#read(char[], int, int) 096 */ 097 @Override 098 public int read() throws IOException { 099 int c = EOF; 100 while (reader != null) { 101 c = reader.read(); 102 if (c != EOF) { 103 break; 104 } 105 nextReader(); 106 } 107 return c; 108 } 109 110 /* 111 * (non-Javadoc) 112 * 113 * @see java.io.Reader#read() 114 */ 115 @Override 116 public int read(final char[] cbuf, int off, int len) throws IOException { 117 Objects.requireNonNull(cbuf, "cbuf"); 118 if (len < 0 || off < 0 || off + len > cbuf.length) { 119 throw new IndexOutOfBoundsException("Array Size=" + cbuf.length + ", offset=" + off + ", length=" + len); 120 } 121 int count = 0; 122 while (reader != null) { 123 final int readLen = reader.read(cbuf, off, len); 124 if (readLen == EOF) { 125 nextReader(); 126 } else { 127 count += readLen; 128 off += readLen; 129 len -= readLen; 130 if (len <= 0) { 131 break; 132 } 133 } 134 } 135 if (count > 0) { 136 return count; 137 } 138 return EOF; 139 } 140}