001    /*
002     * Copyright (C) 2007 The Guava Authors
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.io;
018    
019    import com.google.common.annotations.Beta;
020    import com.google.common.base.Preconditions;
021    
022    import java.io.ByteArrayInputStream;
023    import java.io.ByteArrayOutputStream;
024    import java.io.DataInput;
025    import java.io.DataInputStream;
026    import java.io.DataOutput;
027    import java.io.DataOutputStream;
028    import java.io.EOFException;
029    import java.io.IOException;
030    import java.io.InputStream;
031    import java.io.OutputStream;
032    import java.nio.ByteBuffer;
033    import java.nio.channels.ReadableByteChannel;
034    import java.nio.channels.WritableByteChannel;
035    import java.security.MessageDigest;
036    import java.util.Arrays;
037    import java.util.zip.Checksum;
038    
039    /**
040     * Provides utility methods for working with byte arrays and I/O streams.
041     *
042     * <p>All method parameters must be non-null unless documented otherwise.
043     *
044     * @author Chris Nokleberg
045     * @since 1.0
046     */
047    @Beta
048    public final class ByteStreams {
049      private static final int BUF_SIZE = 0x1000; // 4K
050    
051      private ByteStreams() {}
052    
053      /**
054       * Returns a factory that will supply instances of
055       * {@link ByteArrayInputStream} that read from the given byte array.
056       *
057       * @param b the input buffer
058       * @return the factory
059       */
060      public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier(
061          byte[] b) {
062        return newInputStreamSupplier(b, 0, b.length);
063      }
064    
065      /**
066       * Returns a factory that will supply instances of
067       * {@link ByteArrayInputStream} that read from the given byte array.
068       *
069       * @param b the input buffer
070       * @param off the offset in the buffer of the first byte to read
071       * @param len the maximum number of bytes to read from the buffer
072       * @return the factory
073       */
074      public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier(
075          final byte[] b, final int off, final int len) {
076        return new InputSupplier<ByteArrayInputStream>() {
077          @Override
078          public ByteArrayInputStream getInput() {
079            return new ByteArrayInputStream(b, off, len);
080          }
081        };
082      }
083    
084      /**
085       * Writes a byte array to an output stream from the given supplier.
086       *
087       * @param from the bytes to write
088       * @param to the output supplier
089       * @throws IOException if an I/O error occurs
090       */
091      public static void write(byte[] from,
092          OutputSupplier<? extends OutputStream> to) throws IOException {
093        Preconditions.checkNotNull(from);
094        boolean threw = true;
095        OutputStream out = to.getOutput();
096        try {
097          out.write(from);
098          threw = false;
099        } finally {
100          Closeables.close(out, threw);
101        }
102      }
103    
104      /**
105       * Opens input and output streams from the given suppliers, copies all
106       * bytes from the input to the output, and closes the streams.
107       *
108       * @param from the input factory
109       * @param to the output factory
110       * @return the number of bytes copied
111       * @throws IOException if an I/O error occurs
112       */
113      public static long copy(InputSupplier<? extends InputStream> from,
114          OutputSupplier<? extends OutputStream> to) throws IOException {
115        boolean threw = true;
116        InputStream in = from.getInput();
117        try {
118          OutputStream out = to.getOutput();
119          try {
120            long count = copy(in, out);
121            threw = false;
122            return count;
123          } finally {
124            Closeables.close(out, threw);
125          }
126        } finally {
127          Closeables.close(in, threw);
128        }
129      }
130    
131      /**
132       * Opens an input stream from the supplier, copies all bytes from the
133       * input to the output, and closes the input stream. Does not close
134       * or flush the output stream.
135       *
136       * @param from the input factory
137       * @param to the output stream to write to
138       * @return the number of bytes copied
139       * @throws IOException if an I/O error occurs
140       */
141      public static long copy(InputSupplier<? extends InputStream> from,
142          OutputStream to) throws IOException {
143        boolean threw = true;
144        InputStream in = from.getInput();
145        try {
146          long count = copy(in, to);
147          threw = false;
148          return count;
149        } finally {
150          Closeables.close(in, threw);
151        }
152      }
153    
154      /**
155       * Opens an output stream from the supplier, copies all bytes from the input
156       * to the output, and closes the output stream. Does not close or flush the
157       * output stream.
158       *
159       * @param from the input stream to read from
160       * @param to the output factory
161       * @return the number of bytes copied
162       * @throws IOException if an I/O error occurs
163       * @since 10.0
164       */
165      public static long copy(InputStream from,
166          OutputSupplier<? extends OutputStream> to) throws IOException {
167        boolean threw = true;
168        OutputStream out = to.getOutput();
169        try {
170          long count = copy(from, out);
171          threw = false;
172          return count;
173        } finally {
174          Closeables.close(out, threw);
175        }
176      }
177    
178      /**
179       * Copies all bytes from the input stream to the output stream.
180       * Does not close or flush either stream.
181       *
182       * @param from the input stream to read from
183       * @param to the output stream to write to
184       * @return the number of bytes copied
185       * @throws IOException if an I/O error occurs
186       */
187      public static long copy(InputStream from, OutputStream to)
188          throws IOException {
189        byte[] buf = new byte[BUF_SIZE];
190        long total = 0;
191        while (true) {
192          int r = from.read(buf);
193          if (r == -1) {
194            break;
195          }
196          to.write(buf, 0, r);
197          total += r;
198        }
199        return total;
200      }
201    
202      /**
203       * Copies all bytes from the readable channel to the writable channel.
204       * Does not close or flush either channel.
205       *
206       * @param from the readable channel to read from
207       * @param to the writable channel to write to
208       * @return the number of bytes copied
209       * @throws IOException if an I/O error occurs
210       */
211      public static long copy(ReadableByteChannel from,
212          WritableByteChannel to) throws IOException {
213        ByteBuffer buf = ByteBuffer.allocate(BUF_SIZE);
214        long total = 0;
215        while (from.read(buf) != -1) {
216          buf.flip();
217          while (buf.hasRemaining()) {
218            total += to.write(buf);
219          }
220          buf.clear();
221        }
222        return total;
223      }
224    
225      /**
226       * Reads all bytes from an input stream into a byte array.
227       * Does not close the stream.
228       *
229       * @param in the input stream to read from
230       * @return a byte array containing all the bytes from the stream
231       * @throws IOException if an I/O error occurs
232       */
233      public static byte[] toByteArray(InputStream in) throws IOException {
234        ByteArrayOutputStream out = new ByteArrayOutputStream();
235        copy(in, out);
236        return out.toByteArray();
237      }
238    
239      /**
240       * Returns the data from a {@link InputStream} factory as a byte array.
241       *
242       * @param supplier the factory
243       * @throws IOException if an I/O error occurs
244       */
245      public static byte[] toByteArray(
246          InputSupplier<? extends InputStream> supplier) throws IOException {
247        boolean threw = true;
248        InputStream in = supplier.getInput();
249        try {
250          byte[] result = toByteArray(in);
251          threw = false;
252          return result;
253        } finally {
254          Closeables.close(in, threw);
255        }
256      }
257    
258      /**
259       * Returns a new {@link ByteArrayDataInput} instance to read from the {@code
260       * bytes} array from the beginning.
261       */
262      public static ByteArrayDataInput newDataInput(byte[] bytes) {
263        return new ByteArrayDataInputStream(bytes);
264      }
265    
266      /**
267       * Returns a new {@link ByteArrayDataInput} instance to read from the {@code
268       * bytes} array, starting at the given position.
269       *
270       * @throws IndexOutOfBoundsException if {@code start} is negative or greater
271       *     than the length of the array
272       */
273      public static ByteArrayDataInput newDataInput(byte[] bytes, int start) {
274        Preconditions.checkPositionIndex(start, bytes.length);
275        return new ByteArrayDataInputStream(bytes, start);
276      }
277    
278      private static class ByteArrayDataInputStream implements ByteArrayDataInput {
279        final DataInput input;
280    
281        ByteArrayDataInputStream(byte[] bytes) {
282          this.input = new DataInputStream(new ByteArrayInputStream(bytes));
283        }
284    
285        ByteArrayDataInputStream(byte[] bytes, int start) {
286          this.input = new DataInputStream(
287              new ByteArrayInputStream(bytes, start, bytes.length - start));
288        }
289    
290        @Override public void readFully(byte b[]) {
291          try {
292            input.readFully(b);
293          } catch (IOException e) {
294            throw new IllegalStateException(e);
295          }
296        }
297    
298        @Override public void readFully(byte b[], int off, int len) {
299          try {
300            input.readFully(b, off, len);
301          } catch (IOException e) {
302            throw new IllegalStateException(e);
303          }
304        }
305    
306        @Override public int skipBytes(int n) {
307          try {
308            return input.skipBytes(n);
309          } catch (IOException e) {
310            throw new IllegalStateException(e);
311          }
312        }
313    
314        @Override public boolean readBoolean() {
315          try {
316            return input.readBoolean();
317          } catch (IOException e) {
318            throw new IllegalStateException(e);
319          }
320        }
321    
322        @Override public byte readByte() {
323          try {
324            return input.readByte();
325          } catch (EOFException e) {
326            throw new IllegalStateException(e);
327          } catch (IOException impossible) {
328            throw new AssertionError(impossible);
329          }
330        }
331    
332        @Override public int readUnsignedByte() {
333          try {
334            return input.readUnsignedByte();
335          } catch (IOException e) {
336            throw new IllegalStateException(e);
337          }
338        }
339    
340        @Override public short readShort() {
341          try {
342            return input.readShort();
343          } catch (IOException e) {
344            throw new IllegalStateException(e);
345          }
346        }
347    
348        @Override public int readUnsignedShort() {
349          try {
350            return input.readUnsignedShort();
351          } catch (IOException e) {
352            throw new IllegalStateException(e);
353          }
354        }
355    
356        @Override public char readChar() {
357          try {
358            return input.readChar();
359          } catch (IOException e) {
360            throw new IllegalStateException(e);
361          }
362        }
363    
364        @Override public int readInt() {
365          try {
366            return input.readInt();
367          } catch (IOException e) {
368            throw new IllegalStateException(e);
369          }
370        }
371    
372        @Override public long readLong() {
373          try {
374            return input.readLong();
375          } catch (IOException e) {
376            throw new IllegalStateException(e);
377          }
378        }
379    
380        @Override public float readFloat() {
381          try {
382            return input.readFloat();
383          } catch (IOException e) {
384            throw new IllegalStateException(e);
385          }
386        }
387    
388        @Override public double readDouble() {
389          try {
390            return input.readDouble();
391          } catch (IOException e) {
392            throw new IllegalStateException(e);
393          }
394        }
395    
396        @Override public String readLine() {
397          try {
398            return input.readLine();
399          } catch (IOException e) {
400            throw new IllegalStateException(e);
401          }
402        }
403    
404        @Override public String readUTF() {
405          try {
406            return input.readUTF();
407          } catch (IOException e) {
408            throw new IllegalStateException(e);
409          }
410        }
411      }
412    
413      /**
414       * Returns a new {@link ByteArrayDataOutput} instance with a default size.
415       */
416      public static ByteArrayDataOutput newDataOutput() {
417        return new ByteArrayDataOutputStream();
418      }
419    
420      /**
421       * Returns a new {@link ByteArrayDataOutput} instance sized to hold
422       * {@code size} bytes before resizing.
423       *
424       * @throws IllegalArgumentException if {@code size} is negative
425       */
426      public static ByteArrayDataOutput newDataOutput(int size) {
427        Preconditions.checkArgument(size >= 0, "Invalid size: %s", size);
428        return new ByteArrayDataOutputStream(size);
429      }
430    
431      @SuppressWarnings("deprecation") // for writeBytes
432      private static class ByteArrayDataOutputStream
433          implements ByteArrayDataOutput {
434    
435        final DataOutput output;
436        final ByteArrayOutputStream byteArrayOutputSteam;
437    
438        ByteArrayDataOutputStream() {
439          this(new ByteArrayOutputStream());
440        }
441    
442        ByteArrayDataOutputStream(int size) {
443          this(new ByteArrayOutputStream(size));
444        }
445    
446        ByteArrayDataOutputStream(ByteArrayOutputStream byteArrayOutputSteam) {
447          this.byteArrayOutputSteam = byteArrayOutputSteam;
448          output = new DataOutputStream(byteArrayOutputSteam);
449        }
450    
451        @Override public void write(int b) {
452          try {
453            output.write(b);
454          } catch (IOException impossible) {
455            throw new AssertionError(impossible);
456          }
457        }
458    
459        @Override public void write(byte[] b) {
460          try {
461            output.write(b);
462          } catch (IOException impossible) {
463            throw new AssertionError(impossible);
464          }
465        }
466    
467        @Override public void write(byte[] b, int off, int len) {
468          try {
469            output.write(b, off, len);
470          } catch (IOException impossible) {
471            throw new AssertionError(impossible);
472          }
473        }
474    
475        @Override public void writeBoolean(boolean v) {
476          try {
477            output.writeBoolean(v);
478          } catch (IOException impossible) {
479            throw new AssertionError(impossible);
480          }
481        }
482    
483        @Override public void writeByte(int v) {
484          try {
485            output.writeByte(v);
486          } catch (IOException impossible) {
487            throw new AssertionError(impossible);
488          }
489        }
490    
491        @Override public void writeBytes(String s) {
492          try {
493            output.writeBytes(s);
494          } catch (IOException impossible) {
495            throw new AssertionError(impossible);
496          }
497        }
498    
499        @Override public void writeChar(int v) {
500          try {
501            output.writeChar(v);
502          } catch (IOException impossible) {
503            throw new AssertionError(impossible);
504          }
505        }
506    
507        @Override public void writeChars(String s) {
508          try {
509            output.writeChars(s);
510          } catch (IOException impossible) {
511            throw new AssertionError(impossible);
512          }
513        }
514    
515        @Override public void writeDouble(double v) {
516          try {
517            output.writeDouble(v);
518          } catch (IOException impossible) {
519            throw new AssertionError(impossible);
520          }
521        }
522    
523        @Override public void writeFloat(float v) {
524          try {
525            output.writeFloat(v);
526          } catch (IOException impossible) {
527            throw new AssertionError(impossible);
528          }
529        }
530    
531        @Override public void writeInt(int v) {
532          try {
533            output.writeInt(v);
534          } catch (IOException impossible) {
535            throw new AssertionError(impossible);
536          }
537        }
538    
539        @Override public void writeLong(long v) {
540          try {
541            output.writeLong(v);
542          } catch (IOException impossible) {
543            throw new AssertionError(impossible);
544          }
545        }
546    
547        @Override public void writeShort(int v) {
548          try {
549            output.writeShort(v);
550          } catch (IOException impossible) {
551            throw new AssertionError(impossible);
552          }
553        }
554    
555        @Override public void writeUTF(String s) {
556          try {
557            output.writeUTF(s);
558          } catch (IOException impossible) {
559            throw new AssertionError(impossible);
560          }
561        }
562    
563        @Override public byte[] toByteArray() {
564          return byteArrayOutputSteam.toByteArray();
565        }
566    
567      }
568    
569      // TODO(chrisn): Not all streams support skipping.
570      /** Returns the length of a supplied input stream, in bytes. */
571      public static long length(InputSupplier<? extends InputStream> supplier)
572          throws IOException {
573        long count = 0;
574        boolean threw = true;
575        InputStream in = supplier.getInput();
576        try {
577          while (true) {
578            // We skip only Integer.MAX_VALUE due to JDK overflow bugs.
579            long amt = in.skip(Integer.MAX_VALUE);
580            if (amt == 0) {
581              if (in.read() == -1) {
582                threw = false;
583                return count;
584              }
585              count++;
586            } else {
587              count += amt;
588            }
589          }
590        } finally {
591          Closeables.close(in, threw);
592        }
593      }
594    
595      /**
596       * Returns true if the supplied input streams contain the same bytes.
597       *
598       * @throws IOException if an I/O error occurs
599       */
600      public static boolean equal(InputSupplier<? extends InputStream> supplier1,
601          InputSupplier<? extends InputStream> supplier2) throws IOException {
602        byte[] buf1 = new byte[BUF_SIZE];
603        byte[] buf2 = new byte[BUF_SIZE];
604    
605        boolean threw = true;
606        InputStream in1 = supplier1.getInput();
607        try {
608          InputStream in2 = supplier2.getInput();
609          try {
610            while (true) {
611              int read1 = read(in1, buf1, 0, BUF_SIZE);
612              int read2 = read(in2, buf2, 0, BUF_SIZE);
613              if (read1 != read2 || !Arrays.equals(buf1, buf2)) {
614                threw = false;
615                return false;
616              } else if (read1 != BUF_SIZE) {
617                threw = false;
618                return true;
619              }
620            }
621          } finally {
622            Closeables.close(in2, threw);
623          }
624        } finally {
625          Closeables.close(in1, threw);
626        }
627      }
628    
629      /**
630       * Attempts to read enough bytes from the stream to fill the given byte array,
631       * with the same behavior as {@link DataInput#readFully(byte[])}.
632       * Does not close the stream.
633       *
634       * @param in the input stream to read from.
635       * @param b the buffer into which the data is read.
636       * @throws EOFException if this stream reaches the end before reading all
637       *     the bytes.
638       * @throws IOException if an I/O error occurs.
639       */
640      public static void readFully(InputStream in, byte[] b) throws IOException {
641        readFully(in, b, 0, b.length);
642      }
643    
644      /**
645       * Attempts to read {@code len} bytes from the stream into the given array
646       * starting at {@code off}, with the same behavior as
647       * {@link DataInput#readFully(byte[], int, int)}. Does not close the
648       * stream.
649       *
650       * @param in the input stream to read from.
651       * @param b the buffer into which the data is read.
652       * @param off an int specifying the offset into the data.
653       * @param len an int specifying the number of bytes to read.
654       * @throws EOFException if this stream reaches the end before reading all
655       *     the bytes.
656       * @throws IOException if an I/O error occurs.
657       */
658      public static void readFully(InputStream in, byte[] b, int off, int len)
659          throws IOException {
660        if (read(in, b, off, len) != len) {
661          throw new EOFException();
662        }
663      }
664    
665      /**
666       * Discards {@code n} bytes of data from the input stream. This method
667       * will block until the full amount has been skipped. Does not close the
668       * stream.
669       *
670       * @param in the input stream to read from
671       * @param n the number of bytes to skip
672       * @throws EOFException if this stream reaches the end before skipping all
673       *     the bytes
674       * @throws IOException if an I/O error occurs, or the stream does not
675       *     support skipping
676       */
677      public static void skipFully(InputStream in, long n) throws IOException {
678        while (n > 0) {
679          long amt = in.skip(n);
680          if (amt == 0) {
681            // Force a blocking read to avoid infinite loop
682            if (in.read() == -1) {
683              throw new EOFException();
684            }
685            n--;
686          } else {
687            n -= amt;
688          }
689        }
690      }
691    
692      /**
693       * Process the bytes of a supplied stream
694       *
695       * @param supplier the input stream factory
696       * @param processor the object to which to pass the bytes of the stream
697       * @return the result of the byte processor
698       * @throws IOException if an I/O error occurs
699       */
700      public static <T> T readBytes(InputSupplier<? extends InputStream> supplier,
701          ByteProcessor<T> processor) throws IOException {
702        byte[] buf = new byte[BUF_SIZE];
703        boolean threw = true;
704        InputStream in = supplier.getInput();
705        try {
706          int amt;
707          do {
708            amt = in.read(buf);
709            if (amt == -1) {
710              threw = false;
711              break;
712            }
713          } while (processor.processBytes(buf, 0, amt));
714          return processor.getResult();
715        } finally {
716          Closeables.close(in, threw);
717        }
718      }
719    
720      /**
721       * Computes and returns the checksum value for a supplied input stream.
722       * The checksum object is reset when this method returns successfully.
723       *
724       * @param supplier the input stream factory
725       * @param checksum the checksum object
726       * @return the result of {@link Checksum#getValue} after updating the
727       *     checksum object with all of the bytes in the stream
728       * @throws IOException if an I/O error occurs
729       */
730      public static long getChecksum(InputSupplier<? extends InputStream> supplier,
731          final Checksum checksum) throws IOException {
732        return readBytes(supplier, new ByteProcessor<Long>() {
733          @Override
734          public boolean processBytes(byte[] buf, int off, int len) {
735            checksum.update(buf, off, len);
736            return true;
737          }
738    
739          @Override
740          public Long getResult() {
741            long result = checksum.getValue();
742            checksum.reset();
743            return result;
744          }
745        });
746      }
747    
748      /**
749       * Computes and returns the digest value for a supplied input stream.
750       * The digest object is reset when this method returns successfully.
751       *
752       * @param supplier the input stream factory
753       * @param md the digest object
754       * @return the result of {@link MessageDigest#digest()} after updating the
755       *     digest object with all of the bytes in the stream
756       * @throws IOException if an I/O error occurs
757       */
758      public static byte[] getDigest(InputSupplier<? extends InputStream> supplier,
759          final MessageDigest md) throws IOException {
760        return readBytes(supplier, new ByteProcessor<byte[]>() {
761          @Override
762          public boolean processBytes(byte[] buf, int off, int len) {
763            md.update(buf, off, len);
764            return true;
765          }
766    
767          @Override
768          public byte[] getResult() {
769            return md.digest();
770          }
771        });
772      }
773    
774      /**
775       * Reads some bytes from an input stream and stores them into the buffer array
776       * {@code b}. This method blocks until {@code len} bytes of input data have
777       * been read into the array, or end of file is detected. The number of bytes
778       * read is returned, possibly zero. Does not close the stream.
779       *
780       * <p>A caller can detect EOF if the number of bytes read is less than
781       * {@code len}. All subsequent calls on the same stream will return zero.
782       *
783       * <p>If {@code b} is null, a {@code NullPointerException} is thrown. If
784       * {@code off} is negative, or {@code len} is negative, or {@code off+len} is
785       * greater than the length of the array {@code b}, then an
786       * {@code IndexOutOfBoundsException} is thrown. If {@code len} is zero, then
787       * no bytes are read. Otherwise, the first byte read is stored into element
788       * {@code b[off]}, the next one into {@code b[off+1]}, and so on. The number
789       * of bytes read is, at most, equal to {@code len}.
790       *
791       * @param in the input stream to read from
792       * @param b the buffer into which the data is read
793       * @param off an int specifying the offset into the data
794       * @param len an int specifying the number of bytes to read
795       * @return the number of bytes read
796       * @throws IOException if an I/O error occurs
797       */
798      public static int read(InputStream in, byte[] b, int off, int len)
799          throws IOException {
800        if (len < 0) {
801          throw new IndexOutOfBoundsException("len is negative");
802        }
803        int total = 0;
804        while (total < len) {
805          int result = in.read(b, off + total, len - total);
806          if (result == -1) {
807            break;
808          }
809          total += result;
810        }
811        return total;
812      }
813    
814      /**
815       * Returns an {@link InputSupplier} that returns input streams from the
816       * an underlying supplier, where each stream starts at the given
817       * offset and is limited to the specified number of bytes.
818       *
819       * @param supplier the supplier from which to get the raw streams
820       * @param offset the offset in bytes into the underlying stream where
821       *     the returned streams will start
822       * @param length the maximum length of the returned streams
823       * @throws IllegalArgumentException if offset or length are negative
824       */
825      public static InputSupplier<InputStream> slice(
826          final InputSupplier<? extends InputStream> supplier,
827          final long offset,
828          final long length) {
829        Preconditions.checkNotNull(supplier);
830        Preconditions.checkArgument(offset >= 0, "offset is negative");
831        Preconditions.checkArgument(length >= 0, "length is negative");
832        return new InputSupplier<InputStream>() {
833          @Override public InputStream getInput() throws IOException {
834            InputStream in = supplier.getInput();
835            if (offset > 0) {
836              try {
837                skipFully(in, offset);
838              } catch (IOException e) {
839                Closeables.closeQuietly(in);
840                throw e;
841              }
842            }
843            return new LimitInputStream(in, length);
844          }
845        };
846      }
847    
848      /**
849       * Joins multiple {@link InputStream} suppliers into a single supplier.
850       * Streams returned from the supplier will contain the concatenated data from
851       * the streams of the underlying suppliers.
852       *
853       * <p>Only one underlying input stream will be open at a time. Closing the
854       * joined stream will close the open underlying stream.
855       *
856       * <p>Reading from the joined stream will throw a {@link NullPointerException}
857       * if any of the suppliers are null or return null.
858       *
859       * @param suppliers the suppliers to concatenate
860       * @return a supplier that will return a stream containing the concatenated
861       *     stream data
862       */
863      public static InputSupplier<InputStream> join(
864          final Iterable<? extends InputSupplier<? extends InputStream>> suppliers) {
865        return new InputSupplier<InputStream>() {
866          @Override public InputStream getInput() throws IOException {
867            return new MultiInputStream(suppliers.iterator());
868          }
869        };
870      }
871    
872      /** Varargs form of {@link #join(Iterable)}. */
873      public static InputSupplier<InputStream> join(
874          InputSupplier<? extends InputStream>... suppliers) {
875        return join(Arrays.asList(suppliers));
876      }
877    }