Home | History | Annotate | Download | only in io
      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("s1 == null");
     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("element is null");
     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("element is null");
    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 up to {@code byteCount} bytes from this sequence of input streams and
    150      * stores them in the byte array {@code buffer} starting at {@code byteOffset}.
    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 byteCount}, 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      * @throws IndexOutOfBoundsException
    166      *     if {@code byteOffset < 0 || byteCount < 0 || byteOffset + byteCount > buffer.length}.
    167      * @throws IOException
    168      *             if an I/O error occurs.
    169      * @throws NullPointerException
    170      *             if {@code buffer == null}.
    171      */
    172     @Override
    173     public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
    174         if (in == null) {
    175             return -1;
    176         }
    177         Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
    178         while (in != null) {
    179             int result = in.read(buffer, byteOffset, byteCount);
    180             if (result >= 0) {
    181                 return result;
    182             }
    183             nextStream();
    184         }
    185         return -1;
    186     }
    187 }
    188