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 * http://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 18 package java.io; 19 20 import java.util.Arrays; 21 import java.util.Enumeration; 22 import java.util.Vector; 23 24 /** 25 * Concatenates two or more existing {@link InputStream}s. Reads are taken from 26 * the first stream until it ends, then the next stream is used, until the last 27 * stream returns end of file. 28 */ 29 public class SequenceInputStream extends InputStream { 30 /** 31 * An enumeration which will return types of InputStream. 32 */ 33 private Enumeration<? extends InputStream> e; 34 35 /** 36 * The current input stream. 37 */ 38 private InputStream in; 39 40 /** 41 * Constructs a new {@code SequenceInputStream} using the two streams 42 * {@code s1} and {@code s2} as the sequence of streams to read from. 43 * 44 * @param s1 45 * the first stream to get bytes from. 46 * @param s2 47 * the second stream to get bytes from. 48 * @throws NullPointerException 49 * if {@code s1} is {@code null}. 50 */ 51 public SequenceInputStream(InputStream s1, InputStream s2) { 52 if (s1 == null) { 53 throw new NullPointerException(); 54 } 55 Vector<InputStream> inVector = new Vector<InputStream>(1); 56 inVector.addElement(s2); 57 e = inVector.elements(); 58 in = s1; 59 } 60 61 /** 62 * Constructs a new SequenceInputStream using the elements returned from 63 * Enumeration {@code e} as the stream sequence. The instances returned by 64 * {@code e.nextElement()} must be of type {@link InputStream}. 65 * 66 * @param e 67 * the enumeration of {@code InputStreams} to get bytes from. 68 * @throws NullPointerException 69 * if any of the elements in {@code e} is {@code null}. 70 */ 71 public SequenceInputStream(Enumeration<? extends InputStream> e) { 72 this.e = e; 73 if (e.hasMoreElements()) { 74 in = e.nextElement(); 75 if (in == null) { 76 throw new NullPointerException(); 77 } 78 } 79 } 80 81 @Override 82 public int available() throws IOException { 83 if (e != null && in != null) { 84 return in.available(); 85 } 86 return 0; 87 } 88 89 /** 90 * Closes all streams in this sequence of input stream. 91 * 92 * @throws IOException 93 * if an error occurs while closing any of the input streams. 94 */ 95 @Override 96 public void close() throws IOException { 97 while (in != null) { 98 nextStream(); 99 } 100 e = null; 101 } 102 103 /** 104 * Sets up the next InputStream or leaves it alone if there are none left. 105 * 106 * @throws IOException 107 */ 108 private void nextStream() throws IOException { 109 if (in != null) { 110 in.close(); 111 } 112 if (e.hasMoreElements()) { 113 in = e.nextElement(); 114 if (in == null) { 115 throw new NullPointerException(); 116 } 117 } else { 118 in = null; 119 } 120 } 121 122 /** 123 * Reads a single byte from this sequence of input streams and returns it as 124 * an integer in the range from 0 to 255. It tries to read from the current 125 * stream first; if the end of this stream has been reached, it reads from 126 * the next one. Blocks until one byte has been read, the end of the last 127 * input stream in the sequence has been reached, or an exception is thrown. 128 * 129 * @return the byte read or -1 if either the end of the last stream in the 130 * sequence has been reached or this input stream sequence is 131 * closed. 132 * @throws IOException 133 * if an error occurs while reading the current source input 134 * stream. 135 */ 136 @Override 137 public int read() throws IOException { 138 while (in != null) { 139 int result = in.read(); 140 if (result >= 0) { 141 return result; 142 } 143 nextStream(); 144 } 145 return -1; 146 } 147 148 /** 149 * Reads at most {@code count} bytes from this sequence of input streams and 150 * stores them in the byte array {@code buffer} starting at {@code offset}. 151 * Blocks only until at least 1 byte has been read, the end of the stream 152 * has been reached, or an exception is thrown. 153 * <p> 154 * This SequenceInputStream shows the same behavior as other InputStreams. 155 * To do this it will read only as many bytes as a call to read on the 156 * current substream returns. If that call does not return as many bytes as 157 * requested by {@code count}, it will not retry to read more on its own 158 * because subsequent reads might block. This would violate the rule that 159 * it will only block until at least one byte has been read. 160 * <p> 161 * If a substream has already reached the end when this call is made, it 162 * will close that substream and start with the next one. If there are no 163 * more substreams it will return -1. 164 * 165 * @param buffer 166 * the array in which to store the bytes read. 167 * @param offset 168 * the initial position in {@code buffer} to store the bytes read 169 * from this stream. 170 * @param count 171 * the maximum number of bytes to store in {@code buffer}. 172 * @return the number of bytes actually read; -1 if this sequence of streams 173 * is closed or if the end of the last stream in the sequence has 174 * been reached. 175 * @throws IndexOutOfBoundsException 176 * if {@code offset < 0} or {@code count < 0}, or if {@code 177 * offset + count} is greater than the size of {@code buffer}. 178 * @throws IOException 179 * if an I/O error occurs. 180 * @throws NullPointerException 181 * if {@code buffer} is {@code null}. 182 */ 183 @Override 184 public int read(byte[] buffer, int offset, int count) throws IOException { 185 if (in == null) { 186 return -1; 187 } 188 Arrays.checkOffsetAndCount(buffer.length, offset, count); 189 while (in != null) { 190 int result = in.read(buffer, offset, count); 191 if (result >= 0) { 192 return result; 193 } 194 nextStream(); 195 } 196 return -1; 197 } 198 } 199