    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.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}