You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UnionInputStream.java 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * Copyright (C) 2009, 2013 Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.util.io;
  44. import java.io.IOException;
  45. import java.io.InputStream;
  46. import java.util.Iterator;
  47. import java.util.LinkedList;
  48. /**
  49. * An InputStream which reads from one or more InputStreams.
  50. * <p>
  51. * This stream may enter into an EOF state, returning -1 from any of the read
  52. * methods, and then later successfully read additional bytes if a new
  53. * InputStream is added after reaching EOF.
  54. * <p>
  55. * Currently this stream does not support the mark/reset APIs. If mark and later
  56. * reset functionality is needed the caller should wrap this stream with a
  57. * {@link java.io.BufferedInputStream}.
  58. * */
  59. public class UnionInputStream extends InputStream {
  60. private static final InputStream EOF = new InputStream() {
  61. @Override
  62. public int read() throws IOException {
  63. return -1;
  64. }
  65. };
  66. private final LinkedList<InputStream> streams = new LinkedList<InputStream>();
  67. /** Create an empty InputStream that is currently at EOF state. */
  68. public UnionInputStream() {
  69. // Do nothing.
  70. }
  71. /**
  72. * Create an InputStream that is a union of the individual streams.
  73. * <p>
  74. * As each stream reaches EOF, it will be automatically closed before bytes
  75. * from the next stream are read.
  76. *
  77. * @param inputStreams
  78. * streams to be pushed onto this stream.
  79. */
  80. public UnionInputStream(InputStream... inputStreams) {
  81. for (InputStream i : inputStreams)
  82. add(i);
  83. }
  84. private InputStream head() {
  85. return streams.isEmpty() ? EOF : streams.getFirst();
  86. }
  87. private void pop() throws IOException {
  88. if (!streams.isEmpty())
  89. streams.removeFirst().close();
  90. }
  91. /**
  92. * Add the given InputStream onto the end of the stream queue.
  93. * <p>
  94. * When the stream reaches EOF it will be automatically closed.
  95. *
  96. * @param in
  97. * the stream to add; must not be null.
  98. */
  99. public void add(final InputStream in) {
  100. streams.add(in);
  101. }
  102. /**
  103. * Returns true if there are no more InputStreams in the stream queue.
  104. * <p>
  105. * If this method returns {@code true} then all read methods will signal EOF
  106. * by returning -1, until another InputStream has been pushed into the queue
  107. * with {@link #add(InputStream)}.
  108. *
  109. * @return true if there are no more streams to read from.
  110. */
  111. public boolean isEmpty() {
  112. return streams.isEmpty();
  113. }
  114. @Override
  115. public int read() throws IOException {
  116. for (;;) {
  117. final InputStream in = head();
  118. final int r = in.read();
  119. if (0 <= r)
  120. return r;
  121. else if (in == EOF)
  122. return -1;
  123. else
  124. pop();
  125. }
  126. }
  127. @Override
  128. public int read(byte[] b, int off, int len) throws IOException {
  129. if (len == 0)
  130. return 0;
  131. for (;;) {
  132. final InputStream in = head();
  133. final int n = in.read(b, off, len);
  134. if (0 < n)
  135. return n;
  136. else if (in == EOF)
  137. return -1;
  138. else
  139. pop();
  140. }
  141. }
  142. @Override
  143. public int available() throws IOException {
  144. return head().available();
  145. }
  146. @Override
  147. public long skip(final long count) throws IOException {
  148. long skipped = 0;
  149. long cnt = count;
  150. while (0 < cnt) {
  151. final InputStream in = head();
  152. final long n = in.skip(cnt);
  153. if (0 < n) {
  154. skipped += n;
  155. cnt -= n;
  156. } else if (in == EOF) {
  157. return skipped;
  158. } else {
  159. // Is this stream at EOF? We can't tell from skip alone.
  160. // Read one byte to test for EOF, discard it if we aren't
  161. // yet at EOF.
  162. //
  163. final int r = in.read();
  164. if (r < 0) {
  165. pop();
  166. if (0 < skipped)
  167. break;
  168. } else {
  169. skipped += 1;
  170. cnt -= 1;
  171. }
  172. }
  173. }
  174. return skipped;
  175. }
  176. @Override
  177. public void close() throws IOException {
  178. IOException err = null;
  179. for (Iterator<InputStream> i = streams.iterator(); i.hasNext();) {
  180. try {
  181. i.next().close();
  182. } catch (IOException closeError) {
  183. err = closeError;
  184. }
  185. i.remove();
  186. }
  187. if (err != null)
  188. throw err;
  189. }
  190. }