    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}