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}