1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.io.input;
18
19 import static org.apache.commons.io.IOUtils.EOF;
20
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.io.SequenceInputStream;
24 import java.util.Arrays;
25 import java.util.Iterator;
26 import java.util.Objects;
27
28 import org.apache.commons.io.IOUtils;
29 import org.apache.commons.io.function.Uncheck;
30
31 /**
32 * Provides the contents of multiple {@link Reader}s in sequence.
33 * <p>
34 * Like {@link SequenceInputStream} but for {@link Reader} arguments.
35 * </p>
36 *
37 * @since 2.7
38 */
39 public class SequenceReader extends Reader {
40
41 private Reader reader;
42 private final Iterator<? extends Reader> readers;
43
44 /**
45 * Constructs a new instance with readers
46 *
47 * @param readers the readers to read
48 */
49 public SequenceReader(final Iterable<? extends Reader> readers) {
50 this.readers = Objects.requireNonNull(readers, "readers").iterator();
51 this.reader = Uncheck.get(this::nextReader);
52 }
53
54 /**
55 * Constructs a new instance with readers
56 *
57 * @param readers the readers to read
58 */
59 public SequenceReader(final Reader... readers) {
60 this(Arrays.asList(readers));
61 }
62
63 /*
64 * (non-Javadoc)
65 *
66 * @see Reader#close()
67 */
68 @Override
69 public void close() throws IOException {
70 do { // NOPMD
71 // empty
72 } while (nextReader() != null);
73 }
74
75 /**
76 * Returns the next available reader or null if done.
77 *
78 * @return the next available reader or null.
79 * @throws IOException IOException If an I/O error occurs.
80 */
81 private Reader nextReader() throws IOException {
82 if (reader != null) {
83 reader.close();
84 }
85 if (readers.hasNext()) {
86 reader = readers.next();
87 } else {
88 reader = null;
89 }
90 return reader;
91 }
92
93 /*
94 * (non-Javadoc)
95 *
96 * @see Reader#read(char[], int, int)
97 */
98 @Override
99 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 }