]> sjero.net Git - linphone/blob - p2pproxy/dependencies-src/jxse-src-2.5/api/src/net/jxta/util/LimitInputStream.java
8c738c75666547065d0aafc3128a60ffb1b15da3
[linphone] / p2pproxy / dependencies-src / jxse-src-2.5 / api / src / net / jxta / util / LimitInputStream.java
1 /*
2  * Copyright (c) 2001-2007 Sun Microsystems, Inc.  All rights reserved.
3  *  
4  *  The Sun Project JXTA(TM) Software License
5  *  
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions are met:
8  *  
9  *  1. Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *  
12  *  2. Redistributions in binary form must reproduce the above copyright notice, 
13  *     this list of conditions and the following disclaimer in the documentation 
14  *     and/or other materials provided with the distribution.
15  *  
16  *  3. The end-user documentation included with the redistribution, if any, must 
17  *     include the following acknowledgment: "This product includes software 
18  *     developed by Sun Microsystems, Inc. for JXTA(TM) technology." 
19  *     Alternately, this acknowledgment may appear in the software itself, if 
20  *     and wherever such third-party acknowledgments normally appear.
21  *  
22  *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must 
23  *     not be used to endorse or promote products derived from this software 
24  *     without prior written permission. For written permission, please contact 
25  *     Project JXTA at http://www.jxta.org.
26  *  
27  *  5. Products derived from this software may not be called "JXTA", nor may 
28  *     "JXTA" appear in their name, without prior written permission of Sun.
29  *  
30  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
32  *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN 
33  *  MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
34  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
35  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
36  *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
37  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
38  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
39  *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *  
41  *  JXTA is a registered trademark of Sun Microsystems, Inc. in the United 
42  *  States and other countries.
43  *  
44  *  Please see the license information page at :
45  *  <http://www.jxta.org/project/www/license.html> for instructions on use of 
46  *  the license in source files.
47  *  
48  *  ====================================================================
49  *  
50  *  This software consists of voluntary contributions made by many individuals 
51  *  on behalf of Project JXTA. For more information on Project JXTA, please see 
52  *  http://www.jxta.org.
53  *  
54  *  This license is based on the BSD license adopted by the Apache Foundation. 
55  */
56
57 package net.jxta.util;
58
59
60 import java.io.ByteArrayInputStream;
61 import java.io.FilterInputStream;
62 import java.io.InputStream;
63
64 import java.io.IOException;
65
66 import java.util.logging.Logger;
67 import java.util.logging.Level;
68 import net.jxta.logging.Logging;
69
70
71 /**
72  *  Implements a bounds on the number of bytes which may be read from an
73  *  InputStream. {link LimitInputStream.close() close()} does not close the
74  *  underlying stream.
75  **/
76 public class LimitInputStream extends FilterInputStream {
77     
78     /*
79      *  Log4J Catagory
80      */
81     private static final Logger LOG = Logger.getLogger(LimitInputStream.class.getName());
82     
83     private transient long limit;
84     
85     private transient long read;
86     
87     private transient long mark;
88     
89     private transient boolean fatalUnderflow;
90     
91     private transient boolean alreadycounting;
92     
93     /**
94      * Creates a new instance of LimitInputStream
95      *
96      *    @param  in  the stream which will be limited.
97      *    @param  limit   the number of bytes which can be read from the stream.
98      **/
99     public LimitInputStream(InputStream in, long limit) {
100         this(in, limit, false);
101     }
102     
103     /**
104      * Creates a new instance of LimitInputStream
105      *
106      *    @param  in  the stream which will be limited.
107      *    @param  limit   the number of bytes which can be read from the stream.
108      *    @param  underflowThrows if the underlying stream EOFs before limit then
109      *    an IOException will be thrown.
110      **/
111     public LimitInputStream(InputStream in, long limit, boolean underflowThrows) {
112         super(in);
113         this.limit = limit;
114         this.mark = -1;
115         this.read = 0;
116         this.fatalUnderflow = underflowThrows;
117         this.alreadycounting = false;
118     }
119     
120     /**
121      * {@inheritDoc}
122      *
123      *  <p/>Debugging toString.
124      **/
125     @Override
126     public String toString() {
127         if (null == in) {
128             return "closed/" + super.toString();
129         } else {
130             if (in instanceof ByteArrayInputStream) {
131                 // ByteArrayInputStream.toString() prints the entire stream! 
132                 return in.getClass().getName() + "@" + System.identityHashCode(in) + "/" + super.toString() + ":" + read + "-"
133                         + limit;
134             } else {
135                 return in.toString() + "/" + super.toString() + ":" + read + "-" + limit;
136             }
137         }
138     }
139     
140     /**
141      * Closes this input stream and releases any system resources
142      * associated with the stream.
143      * <p/>
144      * This method simply forgets the underlying stream.
145      *
146      * @exception  IOException  if an I/O error occurs.
147      * @see        java.io.FilterInputStream#in
148      */
149     @Override
150     public void close() throws IOException {
151         in = null;
152     }
153     
154     /**
155      * Returns the number of bytes that can be read from this input
156      * stream without blocking.
157      * <p>
158      * This method
159      * simply performs <code>in.available(n)</code> and
160      * returns the result.
161      *
162      * @return     the number of bytes that can be read from the input stream
163      *             without blocking.
164      * @exception  IOException  if an I/O error occurs.
165      * @see        java.io.FilterInputStream#in
166      */
167     @Override
168     public int available() throws IOException {
169         if (null == in) {
170             throw new IOException("Stream has been closed.");
171         }
172         
173         return (int) Math.min(super.available(), (limit - read));
174     }
175     
176     /**
177      * Marks the current position in this input stream. A subsequent
178      * call to the <code>reset</code> method repositions this stream at
179      * the last marked position so that subsequent reads re-read the same bytes.
180      * <p>
181      * The <code>readlimit</code> argument tells this input stream to
182      * allow that many bytes to be read before the mark position gets
183      * invalidated.
184      * <p>
185      * This method simply performs <code>in.mark(readlimit)</code>.
186      *
187      * @param   readlimit   the maximum limit of bytes that can be read before
188      *                      the mark position becomes invalid.
189      * @see     java.io.FilterInputStream#in
190      * @see     java.io.FilterInputStream#reset()
191      */
192     @Override
193     public void mark(int readlimit) {
194         if (null == in) {
195             return;
196         } // don't throw exception to be consistent with other impls.
197         
198         super.mark(readlimit);
199         mark = read;
200     }
201     
202     /**
203      * Repositions this stream to the position at the time the
204      * <code>mark</code> method was last called on this input stream.
205      * <p>
206      * This method simply performs <code>in.reset()</code>.
207      * <p>
208      * Stream marks are intended to be used in
209      * situations where you need to read ahead a little to see what's in
210      * the stream. Often this is most easily done by invoking some
211      * general parser. If the stream is of the type handled by the
212      * parse, it just chugs along happily. If the stream is not of
213      * that type, the parser should toss an exception when it fails.
214      * If this happens within readlimit bytes, it allows the outer
215      * code to reset the stream and try another parser.
216      *
217      * @exception  IOException  if the stream has not been marked or if the
218      *               mark has been invalidated.
219      * @see        java.io.FilterInputStream#in
220      * @see        java.io.FilterInputStream#mark(int)
221      */
222     @Override
223     public void reset() throws IOException {
224         if (null == in) {
225             throw new IOException("Stream has been closed.");
226         }
227         
228         if (-1 == mark) {
229             throw new IOException("reset() without mark(), or I dont know where mark is");
230         }
231         
232         super.reset();
233         
234         read = mark;
235     }
236     
237     /**
238      * Skips over and discards <code>n</code> bytes of data from the
239      * input stream. The <code>skip</code> method may, for a variety of
240      * reasons, end up skipping over some smaller number of bytes,
241      * possibly <code>0</code>. The actual number of bytes skipped is
242      * returned.
243      * <p>
244      * This method
245      * simply performs <code>in.skip(n)</code>.
246      *
247      * @param      n   the number of bytes to be skipped.
248      * @return     the actual number of bytes skipped.
249      * @exception  IOException  if an I/O error occurs.
250      */
251     @Override
252     public synchronized long skip(long n) throws IOException {
253         if (null == in) {
254             throw new IOException("Stream has been closed.");
255         }
256         
257         long skipLen = Math.min(n, (limit - read));
258         
259         boolean wascounting = alreadycounting;
260
261         alreadycounting = true;
262         long result = super.skip(skipLen);
263
264         alreadycounting = wascounting;
265         
266         if ((-1 != result) && !alreadycounting) {
267             read += result;
268         }
269         
270         return result;
271     }
272     
273     /**
274      * Reads the next byte of data from this input stream. The value
275      * byte is returned as an <code>int</code> in the range
276      * <code>0</code> to <code>255</code>. If no byte is available
277      * because the end of the stream has been reached, the value
278      * <code>-1</code> is returned. This method blocks until input data
279      * is available, the end of the stream is detected, or an exception
280      * is thrown.
281      * <p>
282      * This method
283      * simply performs <code>in.read()</code> and returns the result.
284      *
285      * @return     the next byte of data, or <code>-1</code> if the end of the
286      *             stream is reached.
287      * @exception  IOException  if an I/O error occurs.
288      * @see        java.io.FilterInputStream#in
289      */
290     @Override
291     public synchronized int read() throws IOException {
292         if (null == in) {
293             throw new IOException("Stream has been closed.");
294         }
295         
296         if (read >= limit) {
297             return -1;
298         }
299         
300         boolean wascounting = alreadycounting;
301
302         alreadycounting = true;
303         int result = super.read();
304
305         alreadycounting = wascounting;
306         
307         if (!alreadycounting) {
308             if (-1 != result) {
309                 read++;
310             } else {
311                 if (fatalUnderflow && (read != limit)) {
312                     IOException failed = new IOException(
313                             "Underflow in read, stream EOFed at " + read + " before limit of " + limit);
314
315                     if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
316                         LOG.log(Level.WARNING, failed.getMessage(), failed);
317                     }
318                     throw failed;
319                 }
320             }
321         }
322         
323         return result;
324     }
325     
326     /**
327      * Reads up to <code>len</code> bytes of data from this input stream
328      * into an array of bytes. This method blocks until some input is
329      * available.
330      * <p>
331      * This method simply performs <code>in.read(b, off, len)</code>
332      * and returns the result.
333      *
334      * @param      b     the buffer into which the data is read.
335      * @param      off   the start offset of the data.
336      * @param      len   the maximum number of bytes read.
337      * @return     the total number of bytes read into the buffer, or
338      *             <code>-1</code> if there is no more data because the end of
339      *             the stream has been reached.
340      * @exception  IOException  if an I/O error occurs.
341      * @see        java.io.FilterInputStream#in
342      */
343     @Override
344     public synchronized int read(byte[] b, int off, int len) throws IOException {
345         if (null == in) {
346             throw new IOException("Stream has been closed.");
347         }
348         
349         if (read >= limit) {
350             return -1;
351         }
352         
353         int readLen = (int) Math.min(len, limit - read);
354         
355         boolean wascounting = alreadycounting;
356
357         alreadycounting = true;
358         int result = super.read(b, off, readLen);
359
360         alreadycounting = wascounting;
361         
362         if (!alreadycounting) {
363             if (-1 != result) {
364                 read += result;
365             } else {
366                 if (fatalUnderflow && (read != limit)) {
367                     IOException failed = new IOException(
368                             "Underflow while tring to read " + readLen + ", stream EOFed at " + read + " before limit of " + limit);
369
370                     if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
371                         LOG.log(Level.WARNING, failed.getMessage(), failed);
372                     }
373                     throw failed;
374
375                 }
376             }
377         }
378         
379         return result;
380     }
381 }