001: /*
002: * Input stream wrapper with a byte limit.
003: * Copyright (C) 2004 Stephen Ostermiller
004: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * See COPYING.TXT for details.
017: */
018: package com.Ostermiller.util;
019:
020: import java.io.*;
021:
022: /**
023: * An input stream wrapper that will read only a set number of bytes from the
024: * underlying stream.
025: *
026: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
027: * @since ostermillerutils 1.04.00
028: */
029: public class SizeLimitInputStream extends InputStream {
030:
031: /**
032: * The input stream that is being protected.
033: * All methods should be forwarded to it,
034: * after checking the size that has been read.
035: *
036: * @since ostermillerutils 1.04.00
037: */
038: protected InputStream in;
039:
040: /**
041: * The number of bytes to read at most from this
042: * Stream. Read methods should
043: * check to ensure that bytesRead never
044: * exceeds maxBytesToRead.
045: *
046: * @since ostermillerutils 1.04.00
047: */
048: protected long maxBytesToRead = 0;
049:
050: /**
051: * The number of bytes that have been read
052: * from this stream. Read methods should
053: * check to ensure that bytesRead never
054: * exceeds maxBytesToRead.
055: *
056: * @since ostermillerutils 1.04.00
057: */
058: protected long bytesRead = 0;
059:
060: /**
061: * The number of bytes that have been read
062: * from this stream since mark() was called.
063: *
064: * @since ostermillerutils 1.04.00
065: */
066: protected long bytesReadSinceMark = 0;
067:
068: /**
069: * The number of bytes the user has request
070: * to have been marked for reset.
071: *
072: * @since ostermillerutils 1.04.00
073: */
074: protected long markReadLimitBytes = -1;
075:
076: /**
077: * Get the number of bytes actually read
078: * from this stream.
079: *
080: * @return number of bytes that have already been taken from this stream.
081: *
082: * @since ostermillerutils 1.04.00
083: */
084: public long getBytesRead() {
085: return bytesRead;
086: }
087:
088: /**
089: * Get the maximum number of bytes left to read
090: * before the limit (set in the constructor) is reached.
091: *
092: * @return The number of bytes that (at a maximum) are left to be taken from this stream.
093: *
094: * @since ostermillerutils 1.04.00
095: */
096: public long getBytesLeft() {
097: return maxBytesToRead - bytesRead;
098: }
099:
100: /**
101: * Tell whether the number of bytes specified
102: * in the constructor have been read yet.
103: *
104: * @return true iff the specified number of bytes have all been read.
105: *
106: * @since ostermillerutils 1.04.00
107: */
108: public boolean allBytesRead() {
109: return getBytesLeft() == 0;
110: }
111:
112: /**
113: * Get the number of total bytes (including bytes already read)
114: * that can be read from this stream (as set in the constructor).
115: * @return Maximum bytes that can be read until the size limit runs out
116: *
117: * @since ostermillerutils 1.04.00
118: */
119: public long getMaxBytesToRead() {
120: return maxBytesToRead;
121: }
122:
123: /**
124: * Create a new size limit input stream from
125: * another stream given a size limit.
126: *
127: * @param in The input stream.
128: * @param maxBytesToRead the max number of bytes to allow to be read from the underlying stream.
129: *
130: * @since ostermillerutils 1.04.00
131: */
132: public SizeLimitInputStream(InputStream in, long maxBytesToRead) {
133: this .in = in;
134: this .maxBytesToRead = maxBytesToRead;
135: }
136:
137: /**
138: * {@inheritDoc}
139: */
140: @Override
141: public int read() throws IOException {
142: if (bytesRead >= maxBytesToRead) {
143: return -1;
144: }
145: int b = in.read();
146: if (b != -1) {
147: bytesRead++;
148: bytesReadSinceMark++;
149: }
150: return b;
151: }
152:
153: /**
154: * {@inheritDoc}
155: */
156: @Override
157: public int read(byte[] b) throws IOException {
158: return this .read(b, 0, b.length);
159: }
160:
161: /**
162: * {@inheritDoc}
163: */
164: @Override
165: public int read(byte[] b, int off, int len) throws IOException {
166: if (bytesRead >= maxBytesToRead) {
167: return -1;
168: }
169: long bytesLeft = getBytesLeft();
170: if (len > bytesLeft) {
171: len = (int) bytesLeft;
172: }
173: int bytesJustRead = in.read(b, off, len);
174: bytesRead += bytesJustRead;
175: bytesReadSinceMark += bytesJustRead;
176: return bytesJustRead;
177: }
178:
179: /**
180: * {@inheritDoc}
181: */
182: @Override
183: public long skip(long n) throws IOException {
184: if (bytesRead >= maxBytesToRead) {
185: return -1;
186: }
187: long bytesLeft = getBytesLeft();
188: if (n > bytesLeft) {
189: n = bytesLeft;
190: }
191: return in.skip(n);
192: }
193:
194: /**
195: * {@inheritDoc}
196: */
197: @Override
198: public int available() throws IOException {
199: int available = in.available();
200: long bytesLeft = getBytesLeft();
201: if (available > bytesLeft) {
202: available = (int) bytesLeft;
203: }
204: return available;
205: }
206:
207: /**
208: * Close this stream and underlying streams.
209: * Calling this method may make data on the
210: * underlying stream unavailable.
211: * <p>
212: * Consider wrapping this stream in a NoCloseStream
213: * so that clients can
214: * call close() with no effect.
215: *
216: * @since ostermillerutils 1.04.00
217: */
218: @Override
219: public void close() throws IOException {
220: in.close();
221: }
222:
223: /**
224: * {@inheritDoc}
225: */
226: @Override
227: public void mark(int readlimit) {
228: if (in.markSupported()) {
229: markReadLimitBytes = readlimit;
230: bytesReadSinceMark = 0;
231: in.mark(readlimit);
232: }
233: }
234:
235: /**
236: * {@inheritDoc}
237: */
238: @Override
239: public void reset() throws IOException {
240: if (in.markSupported()
241: && bytesReadSinceMark <= markReadLimitBytes) {
242: bytesRead -= bytesReadSinceMark;
243: in.reset();
244: bytesReadSinceMark = 0;
245: }
246: }
247:
248: /**
249: * {@inheritDoc}
250: */
251: @Override
252: public boolean markSupported() {
253: return in.markSupported();
254: }
255: }
|