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