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