1 /* 2 * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.io; 27 28 import java.io.InputStream; 29 import java.util.Enumeration; 30 import java.util.Vector; 31 32 /** 33 * A <code>SequenceInputStream</code> represents 34 * the logical concatenation of other input 35 * streams. It starts out with an ordered 36 * collection of input streams and reads from 37 * the first one until end of file is reached, 38 * whereupon it reads from the second one, 39 * and so on, until end of file is reached 40 * on the last of the contained input streams. 41 * 42 * @author Author van Hoff 43 * @since 1.0 44 */ 45 public 46 class SequenceInputStream extends InputStream { 47 Enumeration<? extends InputStream> e; 48 InputStream in; 49 50 /** 51 * Initializes a newly created <code>SequenceInputStream</code> 52 * by remembering the argument, which must 53 * be an <code>Enumeration</code> that produces 54 * objects whose run-time type is <code>InputStream</code>. 55 * The input streams that are produced by 56 * the enumeration will be read, in order, 57 * to provide the bytes to be read from this 58 * <code>SequenceInputStream</code>. After 59 * each input stream from the enumeration 60 * is exhausted, it is closed by calling its 61 * <code>close</code> method. 62 * 63 * @param e an enumeration of input streams. 64 * @see java.util.Enumeration 65 */ 66 public SequenceInputStream(Enumeration<? extends InputStream> e) { 67 this.e = e; 68 peekNextStream(); 69 } 70 71 /** 72 * Initializes a newly 73 * created <code>SequenceInputStream</code> 74 * by remembering the two arguments, which 75 * will be read in order, first <code>s1</code> 76 * and then <code>s2</code>, to provide the 77 * bytes to be read from this <code>SequenceInputStream</code>. 78 * 79 * @param s1 the first input stream to read. 80 * @param s2 the second input stream to read. 81 */ 82 public SequenceInputStream(InputStream s1, InputStream s2) { 83 Vector<InputStream> v = new Vector<>(2); 84 v.addElement(s1); 85 v.addElement(s2); 86 e = v.elements(); 87 peekNextStream(); 88 } 89 90 /** 91 * Continues reading in the next stream if an EOF is reached. 92 */ 93 final void nextStream() throws IOException { 94 try { 95 if (in != null) { 96 in.close(); 97 } 98 } finally { 99 peekNextStream(); 100 } 101 } 102 103 private void peekNextStream() { 104 if (e.hasMoreElements()) { 105 in = (InputStream) e.nextElement(); 106 if (in == null) 107 throw new NullPointerException(); 108 } else { 109 in = null; 110 } 111 } 112 113 /** 114 * Returns an estimate of the number of bytes that can be read (or 115 * skipped over) from the current underlying input stream without 116 * blocking by the next invocation of a method for the current 117 * underlying input stream. The next invocation might be 118 * the same thread or another thread. A single read or skip of this 119 * many bytes will not block, but may read or skip fewer bytes. 120 * <p> 121 * This method simply calls {@code available} of the current underlying 122 * input stream and returns the result. 123 * 124 * @return an estimate of the number of bytes that can be read (or 125 * skipped over) from the current underlying input stream 126 * without blocking or {@code 0} if this input stream 127 * has been closed by invoking its {@link #close()} method 128 * @exception IOException if an I/O error occurs. 129 * 130 * @since 1.1 131 */ 132 public int available() throws IOException { 133 if (in == null) { 134 return 0; // no way to signal EOF from available() 135 } 136 return in.available(); 137 } 138 139 /** 140 * Reads the next byte of data from this input stream. The byte is 141 * returned as an <code>int</code> in the range <code>0</code> to 142 * <code>255</code>. If no byte is available because the end of the 143 * stream has been reached, the value <code>-1</code> is returned. 144 * This method blocks until input data is available, the end of the 145 * stream is detected, or an exception is thrown. 146 * <p> 147 * This method 148 * tries to read one character from the current substream. If it 149 * reaches the end of the stream, it calls the <code>close</code> 150 * method of the current substream and begins reading from the next 151 * substream. 152 * 153 * @return the next byte of data, or <code>-1</code> if the end of the 154 * stream is reached. 155 * @exception IOException if an I/O error occurs. 156 */ 157 public int read() throws IOException { 158 while (in != null) { 159 int c = in.read(); 160 if (c != -1) { 161 return c; 162 } 163 nextStream(); 164 } 165 return -1; 166 } 167 168 /** 169 * Reads up to <code>len</code> bytes of data from this input stream 170 * into an array of bytes. If <code>len</code> is not zero, the method 171 * blocks until at least 1 byte of input is available; otherwise, no 172 * bytes are read and <code>0</code> is returned. 173 * <p> 174 * The <code>read</code> method of <code>SequenceInputStream</code> 175 * tries to read the data from the current substream. If it fails to 176 * read any characters because the substream has reached the end of 177 * the stream, it calls the <code>close</code> method of the current 178 * substream and begins reading from the next substream. 179 * 180 * @param b the buffer into which the data is read. 181 * @param off the start offset in array <code>b</code> 182 * at which the data is written. 183 * @param len the maximum number of bytes read. 184 * @return int the number of bytes read. 185 * @exception NullPointerException If <code>b</code> is <code>null</code>. 186 * @exception IndexOutOfBoundsException If <code>off</code> is negative, 187 * <code>len</code> is negative, or <code>len</code> is greater than 188 * <code>b.length - off</code> 189 * @exception IOException if an I/O error occurs. 190 */ 191 public int read(byte b[], int off, int len) throws IOException { 192 if (in == null) { 193 return -1; 194 } else if (b == null) { 195 throw new NullPointerException(); 196 } else if (off < 0 || len < 0 || len > b.length - off) { 197 throw new IndexOutOfBoundsException(); 198 } else if (len == 0) { 199 return 0; 200 } 201 do { 202 int n = in.read(b, off, len); 203 if (n > 0) { 204 return n; 205 } 206 nextStream(); 207 } while (in != null); 208 return -1; 209 } 210 211 /** 212 * Closes this input stream and releases any system resources 213 * associated with the stream. 214 * A closed <code>SequenceInputStream</code> 215 * cannot perform input operations and cannot 216 * be reopened. 217 * <p> 218 * If this stream was created 219 * from an enumeration, all remaining elements 220 * are requested from the enumeration and closed 221 * before the <code>close</code> method returns. 222 * 223 * @exception IOException if an I/O error occurs. 224 */ 225 public void close() throws IOException { 226 IOException ioe = null; 227 do { 228 try { 229 nextStream(); 230 } catch (IOException e) { 231 if (ioe == null) { 232 ioe = e; 233 } else { 234 ioe.addSuppressed(e); 235 } 236 } 237 } while (in != null); 238 if (ioe != null) { 239 throw ioe; 240 } 241 } 242 }