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