KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > Ostermiller > util > ConcatInputStream


1 /*
2  * Copyright (C) 2004 Stephen Ostermiller
3  * http://ostermiller.org/contact.pl?regarding=Java+Utilities
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * See COPYING.TXT for details.
16  */

17 package com.Ostermiller.util;
18
19 import java.io.*;
20 import java.util.*;
21
22 /**
23  * An input stream which reads sequentially from multiple sources.
24  * More information about this class is available from <a target="_top" HREF=
25  * "http://ostermiller.org/utils/">ostermiller.org</a>.
26  *
27  * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
28  * @since ostermillerutils 1.04.00
29  */

30 public class ConcatInputStream extends InputStream {
31
32
33     /**
34      * Current index to inputStreamQueue
35      *
36      * @since ostermillerutils 1.04.01
37      */

38     private int inputStreamQueueIndex = 0;
39
40     /**
41      * Queue of inputStreams that have yet to be read from.
42      *
43      * @since ostermillerutils 1.04.01
44      */

45     private ArrayList<InputStream> inputStreamQueue = new ArrayList<InputStream>();
46
47     /**
48      * A cache of the current inputStream from the inputStreamQueue
49      * to avoid unneeded access to the queue which must
50      * be synchronized.
51      *
52      * @since ostermillerutils 1.04.01
53      */

54     private InputStream currentInputStream = null;
55
56     /**
57      * true iff the client may add more inputStreams.
58      *
59      * @since ostermillerutils 1.04.01
60      */

61     private boolean doneAddingInputStreams = false;
62
63     /**
64      * Causes the addInputStream method to throw IllegalStateException
65      * and read() methods to return -1 (end of stream)
66      * when there is no more available data.
67      * <p>
68      * Calling this method when this class is no longer accepting
69      * more inputStreams has no effect.
70      *
71      * @since ostermillerutils 1.04.01
72      */

73     public void lastInputStreamAdded(){
74         doneAddingInputStreams = true;
75     }
76
77     /**
78      * Add the given inputStream to the queue of inputStreams from which to
79      * concatenate data.
80      *
81      * @param in InputStream to add to the concatenation.
82      * @throws IllegalStateException if more inputStreams can't be added because lastInputStreamAdded() has been called, close() has been called, or a constructor with inputStream parameters was used.
83      *
84      * @since ostermillerutils 1.04.01
85      */

86     public void addInputStream(InputStream in){
87         synchronized(inputStreamQueue){
88             if (in == null) throw new NullPointerException JavaDoc();
89             if (closed) throw new IllegalStateException JavaDoc("ConcatInputStream has been closed");
90             if (doneAddingInputStreams) throw new IllegalStateException JavaDoc("Cannot add more inputStreams - the last inputStream has already been added.");
91             inputStreamQueue.add(in);
92         }
93     }
94
95     /**
96      * Add the given inputStream to the queue of inputStreams from which to
97      * concatenate data.
98      *
99      * @param in InputStream to add to the concatenation.
100      * @throws IllegalStateException if more inputStreams can't be added because lastInputStreamAdded() has been called, close() has been called, or a constructor with inputStream parameters was used.
101      * @throws NullPointerException the array of inputStreams, or any of the contents is null.
102      *
103      * @since ostermillerutils 1.04.01
104      */

105     public void addInputStreams(InputStream[] in){
106         for (int i=0; i<in.length; i++){
107             addInputStream(in[i]);
108         }
109     }
110
111     /**
112      * Gets the current inputStream, looking at the next
113      * one in the list if the current one is null.
114      *
115      * @since ostermillerutils 1.04.01
116      */

117     private InputStream getCurrentInputStream(){
118         if (currentInputStream == null && inputStreamQueueIndex < inputStreamQueue.size()){
119             synchronized(inputStreamQueue){
120                 // inputStream queue index is advanced only by the nextInputStream()
121
// method. Don't do it here.
122
currentInputStream = (InputStream)inputStreamQueue.get(inputStreamQueueIndex);
123             }
124         }
125         return currentInputStream;
126     }
127
128     /**
129      * Indicate that we are done with the current inputStream and we should
130      * advance to the next inputStream.
131      *
132      * @since ostermillerutils 1.04.01
133      */

134     private void advanceToNextInputStream(){
135         currentInputStream = null;
136         inputStreamQueueIndex++;
137     }
138
139     /**
140      * True iff this the close() method has been called on this stream.
141      *
142      * @since ostermillerutils 1.04.00
143      */

144     private boolean closed = false;
145
146
147     /**
148      * Create a new input stream that can dynamically accept new sources.
149      * <p>
150      * New sources should be added using the addInputStream() method.
151      * When all sources have been added the lastInputStreamAdded() should
152      * be called so that read methods can return -1 (end of stream).
153      * <p>
154      * Adding new sources may by interleaved with read calls.
155      *
156      * @since ostermillerutils 1.04.01
157      */

158     public ConcatInputStream(){
159     }
160
161     /**
162      * Create a new InputStream with one source.
163      *
164      * @param in InputStream to use as a source.
165      *
166      * @throws NullPointerException if in is null
167      *
168      * @since ostermillerutils 1.04.00
169      */

170     public ConcatInputStream(InputStream in){
171         addInputStream(in);
172         lastInputStreamAdded();
173     }
174
175     /**
176      * Create a new InputStream with two sources.
177      *
178      * @param in1 first InputStream to use as a source.
179      * @param in2 second InputStream to use as a source.
180      *
181      * @throws NullPointerException if either source is null.
182      *
183      * @since ostermillerutils 1.04.00
184      */

185     public ConcatInputStream(InputStream in1, InputStream in2){
186         addInputStream(in1);
187         addInputStream(in2);
188         lastInputStreamAdded();
189     }
190
191     /**
192      * Create a new InputStream with an arbitrary number of sources.
193      *
194      * @param in InputStreams to use as a sources.
195      *
196      * @throws NullPointerException if the input array on any element is null.
197      *
198      * @since ostermillerutils 1.04.00
199      */

200     public ConcatInputStream(InputStream[] in){
201         addInputStreams(in);
202         lastInputStreamAdded();
203     }
204
205     /**
206      * Reads the next byte of data from the underlying streams. The value byte is
207      * returned as an int in the range 0 to 255. If no byte is available because
208      * the end of the stream has been reached, the value -1 is returned. This method
209      * blocks until input data is available, the end of the stream is detected, or
210      * an exception is thrown.
211      * <p>
212      * If this class in not done accepting inputstreams and the end of the last known
213      * stream is reached, this method will block forever unless another thread
214      * adds an inputstream or interrupts.
215      *
216      * @return the next byte of data, or -1 if the end of the stream is reached.
217      *
218      * @throws IOException if an I/O error occurs.
219      */

220     public int read() throws IOException {
221         if (closed) throw new IOException("InputStream closed");
222         int r = -1;
223         while (r == -1){
224             InputStream in = getCurrentInputStream();
225             if (in == null){
226                 if (doneAddingInputStreams) return -1;
227                 try {
228                     Thread.sleep(100);
229                 } catch (InterruptedException JavaDoc iox){
230                     throw new IOException("Interrupted");
231                 }
232             } else {
233                 r = in.read();
234                 if (r == -1) advanceToNextInputStream();
235             }
236         }
237         return r;
238     }
239
240     /**
241      * Reads some number of bytes from the underlying streams and stores them into
242      * the buffer array b. The number of bytes actually read is returned as an
243      * integer. This method blocks until input data is available, end of file is
244      * detected, or an exception is thrown.
245      * <p>
246      * If the length of b is zero,
247      * then no bytes are read and 0 is returned; otherwise, there is an attempt
248      * to read at least one byte.
249      * <p>
250      * The read(b) method for class InputStream has the same effect as:<br>
251      * read(b, 0, b.length)
252      * <p>
253      * If this class in not done accepting inputstreams and the end of the last known
254      * stream is reached, this method will block forever unless another thread
255      * adds an inputstream or interrupts.
256      *
257      * @param b - Destination buffer
258      * @return The number of bytes read, or -1 if the end of the stream has been reached
259      *
260      * @throws IOException - If an I/O error occurs
261      * @throws NullPointerException - If b is null.
262      *
263      * @since ostermillerutils 1.04.00
264      */

265     public int read(byte[] b) throws IOException {
266         return read(b, 0, b.length);
267     }
268
269     /**
270      * Reads up to len bytes of data from the underlying streams into an array of bytes.
271      * An attempt is made to read as many as len bytes, but a smaller number may be read,
272      * possibly zero. The number of bytes actually read is returned as an integer.
273      * <p>
274      * If len is zero,
275      * then no bytes are read and 0 is returned; otherwise, there is an attempt
276      * to read at least one byte.
277      * <p>
278      * This method blocks until input data is available
279      * <p>
280      * If this class in not done accepting inputstreams and the end of the last known
281      * stream is reached, this method will block forever unless another thread
282      * adds an inputstream or interrupts.
283      *
284      * @param b Destination buffer
285      * @param off Offset at which to start storing bytes
286      * @param len Maximum number of bytes to read
287      * @return The number of bytes read, or -1 if the end of the stream has been reached
288      *
289      * @throws IOException - If an I/O error occurs
290      * @throws NullPointerException - If b is null.
291      * @throws IndexOutOfBoundsException - if len or offset are not possible.
292      */

293     public int read(byte[] b, int off, int len) throws IOException {
294         if (off < 0 || len < 0 || off + len > b.length) throw new IllegalArgumentException JavaDoc();
295         if (closed) throw new IOException("InputStream closed");
296         int r = -1;
297         while (r == -1){
298             InputStream in = getCurrentInputStream();
299             if (in == null){
300                 if (doneAddingInputStreams) return -1;
301                 try {
302                     Thread.sleep(100);
303                 } catch (InterruptedException JavaDoc iox){
304                     throw new IOException("Interrupted");
305                 }
306             } else {
307                 r = in.read(b, off, len);
308                 if (r == -1) advanceToNextInputStream();
309             }
310         }
311         return r;
312     }
313
314     /**
315      * Skips over and discards n bytes of data from this input stream. The skip method
316      * may, for a variety of reasons, end up skipping over some smaller number of bytes,
317      * possibly 0. This may result from any of a number of conditions; reaching end of
318      * file before n bytes have been skipped is only one possibility. The actual number
319      * of bytes skipped is returned. If n is negative, no bytes are skipped.
320      * <p>
321      * If this class in not done accepting inputstreams and the end of the last known
322      * stream is reached, this method will block forever unless another thread
323      * adds an inputstream or interrupts.
324      *
325      * @param n he number of characters to skip
326      * @return The number of characters actually skipped
327      *
328      * @throws IOException If an I/O error occurs
329      *
330      * @since ostermillerutils 1.04.00
331      */

332     public long skip(long n) throws IOException {
333         if (closed) throw new IOException("InputStream closed");
334         if (n <= 0) return 0;
335         long s = -1;
336         while (s <= 0){
337             InputStream in = getCurrentInputStream();
338             if (in == null){
339                 if (doneAddingInputStreams) return 0;
340                 try {
341                     Thread.sleep(100);
342                 } catch (InterruptedException JavaDoc iox){
343                     throw new IOException("Interrupted");
344                 }
345             } else {
346                 s = in.skip(n);
347                 // When nothing was skipped it is a bit of a puzzle.
348
// The most common cause is that the end of the underlying
349
// stream was reached. In which case calling skip on it
350
// will always return zero. If somebody were calling skip
351
// until it skipped everything they needed, there would
352
// be an infinite loop if we were to return zero here.
353
// If we get zero, let us try to read one character so
354
// we can see if we are at the end of the stream. If so,
355
// we will move to the next.
356
if (s <= 0) {
357                     // read() will advance to the next stream for us, so don't do it again
358
s = ((read()==-1)?-1:1);
359                 }
360             }
361
362         }
363         return s;
364     }
365
366     /**
367      * Returns the number of bytes that can be read (or skipped over) from this input
368      * stream without blocking by the next caller of a method for this input stream.
369      * The next caller might be the same thread or or another thread.
370      *
371      * @throws IOException If an I/O error occurs
372      *
373      * @since ostermillerutils 1.04.00
374      */

375     public int available() throws IOException {
376         if (closed) throw new IOException("InputStream closed");
377         InputStream in = getCurrentInputStream();
378         if (in == null) return 0;
379         return in.available();
380     }
381
382     /**
383      * Closes this input stream and releases any system resources associated with the stream.
384      *
385      * @since ostermillerutils 1.04.00
386      */

387     public void close() throws IOException {
388         if (closed) return;
389         for (Iterator i=inputStreamQueue.iterator(); i.hasNext();){
390             ((InputStream)i.next()).close();
391         }
392         closed = true;
393     }
394
395     /**
396      * Mark not supported
397      *
398      * @since ostermillerutils 1.04.00
399      */

400     public void mark(int readlimit){
401     }
402
403     /**
404      * Reset not supported.
405      *
406      * @throws IOException because reset is not supported.
407      *
408      * @since ostermillerutils 1.04.00
409      */

410     public void reset() throws IOException {
411         throw new IOException("Reset not supported");
412     }
413
414     /**
415      * Does not support mark.
416      *
417      * @return false
418      *
419      * @since ostermillerutils 1.04.00
420      */

421     public boolean markSupported(){
422         return false;
423     }
424 }
425
Popular Tags