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