Java Tutorial/File/FilterInputStream
Содержание
An limited-data-size input stream
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* An input stream, which limits its data size. This stream is
* used, if the content length is unknown.
*/
public abstract class LimitedInputStream
extends FilterInputStream {
/**
* The maximum size of an item, in bytes.
*/
private long sizeMax;
/**
* The current number of bytes.
*/
private long count;
/**
* Whether this stream is already closed.
*/
private boolean closed;
/**
* Creates a new instance.
* @param pIn The input stream, which shall be limited.
* @param pSizeMax The limit; no more than this number of bytes
* shall be returned by the source stream.
*/
public LimitedInputStream(InputStream pIn, long pSizeMax) {
super(pIn);
sizeMax = pSizeMax;
}
/**
* Called to indicate, that the input streams limit has
* been exceeded.
* @param pSizeMax The input streams limit, in bytes.
* @param pCount The actual number of bytes.
* @throws IOException The called method is expected
* to raise an IOException.
*/
protected abstract void raiseError(long pSizeMax, long pCount)
throws IOException;
/** Called to check, whether the input streams
* limit is reached.
* @throws IOException The given limit is exceeded.
*/
private void checkLimit() throws IOException {
if (count > sizeMax) {
raiseError(sizeMax, count);
}
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned. This method blocks until input data
* is available, the end of the stream is detected, or an exception
* is thrown.
*
* This method
* simply performs <code>in.read()</code> and returns the result.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public int read() throws IOException {
int res = super.read();
if (res != -1) {
count++;
checkLimit();
}
return res;
}
/**
* Reads up to <code>len</code> bytes of data from this input stream
* into an array of bytes. If <code>len</code> is not zero, the method
* blocks until some input is available; otherwise, no
* bytes are read and <code>0</code> is returned.
*
* This method simply performs <code>in.read(b, off, len)</code>
* and returns the result.
*
* @param b the buffer into which the data is read.
* @param off The start offset in the destination array
* <code>b</code>.
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception NullPointerException If <code>b</code> is <code>null</code>.
* @exception IndexOutOfBoundsException If <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public int read(byte[] b, int off, int len) throws IOException {
int res = super.read(b, off, len);
if (res > 0) {
count += res;
checkLimit();
}
return res;
}
/**
* Returns, whether this stream is already closed.
* @return True, if the stream is closed, otherwise false.
* @throws IOException An I/O error occurred.
*/
public boolean isClosed() throws IOException {
return closed;
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
* This
* method simply performs <code>in.close()</code>.
*
* @exception IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in
*/
public void close() throws IOException {
closed = true;
super.close();
}
}
A trace of the data that is being retrieved from an input stream
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the "License"). You may not use this file except
* in compliance with the License.
*
* You can obtain a copy of the license at
* glassfish/bootstrap/legal/CDDLv1.0.txt or
* https://glassfish.dev.java.net/public/CDDLv1.0.html.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* HEADER in each file and include the License file at
* glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
* add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your
* own identifying information: Portions Copyright [yyyy]
* [name of copyright owner]
*/
/*
* @(#)TraceInputStream.java 1.7 05/08/29
*
* Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
*/
//Revised from sun mail util
import java.io.*;
/**
* This class is a FilterInputStream that writes the bytes
* being read from the given input stream into the given output
* stream. This class is typically used to provide a trace of
* the data that is being retrieved from an input stream.
*
* @author John Mani
*/
public class TraceInputStream extends FilterInputStream {
private boolean trace = false;
private boolean quote = false;
private OutputStream traceOut;
/**
* Creates an input stream filter built on top of the specified
* input stream.
*
* @param in the underlying input stream.
* @param out the trace stream
*/
public TraceInputStream(InputStream in, OutputStream traceOut) {
super(in);
this.traceOut = traceOut;
}
/**
* Set trace mode.
* @param trace the trace mode
*/
public void setTrace(boolean trace) {
this.trace = trace;
}
/**
* Set quote mode.
* @param quote the quote mode
*/
public void setQuote(boolean quote) {
this.quote = quote;
}
/**
* Reads the next byte of data from this input stream. Returns
* <code>-1</code> if no data is available. Writes out the read
* byte into the trace stream, if trace mode is <code>true</code>
*/
public int read() throws IOException {
int b = in.read();
if (trace && b != -1) {
if (quote)
writeByte(b);
else
traceOut.write(b);
}
return b;
}
/**
* Reads up to <code>len</code> bytes of data from this input stream
* into an array of bytes. Returns <code>-1</code> if no more data
* is available. Writes out the read bytes into the trace stream, if
* trace mode is <code>true</code>
*/
public int read(byte b[], int off, int len) throws IOException {
int count = in.read(b, off, len);
if (trace && count != -1) {
if (quote) {
for (int i = 0; i < count; i++)
writeByte(b[off + i]);
} else
traceOut.write(b, off, count);
}
return count;
}
/**
* Write a byte in a way that every byte value is printable ASCII.
*/
private final void writeByte(int b) throws IOException {
b &= 0xff;
if (b > 0x7f) {
traceOut.write("M");
traceOut.write("-");
b &= 0x7f;
}
if (b == "\r") {
traceOut.write("\\");
traceOut.write("r");
} else if (b == "\n") {
traceOut.write("\\");
traceOut.write("n");
traceOut.write("\n");
} else if (b == "\t") {
traceOut.write("\\");
traceOut.write("t");
} else if (b < " ") {
traceOut.write("^");
traceOut.write("@" + b);
} else {
traceOut.write(b);
}
}
}
introduce a protocol for reading arbitrary length data in a uniform way
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.DataInputStream;
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
/**
* This input stream works in conjunction with the WrappedOutputStream
* to introduce a protocol for reading arbitrary length data in a
* uniform way.
*
* <strong>Note:</strong> See the javadoc for WrappedOutputStream for
* more information.
*
* @see WrappedOutputStream
*
* @author Andy Clark, IBM
*
* @version $Id: WrappedInputStream.java 447688 2006-09-19 02:39:49Z mrglavas $
*/
public class WrappedInputStream
extends FilterInputStream {
//
// Data
//
/** Bytes left on input stream for current packet. */
protected int fPacketCount;
/**
* Data input stream. This stream is used to input the block sizes
* from the data stream that are written by the WrappedOutputStream.
*
* <strong>Note:</strong> The data input stream is only used for
* reading the byte count for performance reasons. We avoid the
* method indirection for reading the byte data.
*/
protected DataInputStream fDataInputStream;
/** To mark that the stream is "closed". */
protected boolean fClosed;
//
// Constructors
//
/** Constructs a wrapper for the given an input stream. */
public WrappedInputStream(InputStream stream) {
super(stream);
fDataInputStream = new DataInputStream(stream);
} // <init>(InputStream)
//
// InputStream methods
//
/** Reads a single byte. */
public int read() throws IOException {
// ignore, if already closed
if (fClosed) {
return -1;
}
// read packet header
if (fPacketCount == 0) {
fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
if (fPacketCount == 0) {
fClosed = true;
return -1;
}
}
// read a byte from the packet
fPacketCount--;
return super.in.read();
} // read():int
/**
* Reads a block of bytes and returns the total number of bytes read.
*/
public int read(byte[] b, int offset, int length) throws IOException {
// ignore, if already closed
if (fClosed) {
return -1;
}
// read packet header
if (fPacketCount == 0) {
fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
if (fPacketCount == 0) {
fClosed = true;
return -1;
}
}
// read bytes from packet
if (length > fPacketCount) {
length = fPacketCount;
}
int count = super.in.read(b, offset, length);
if (count == -1) {
// NOTE: This condition should not happen. The end of
// the stream should always be designated by a
// byte count header of 0. -Ac
fClosed = true;
return -1;
}
fPacketCount -= count;
// return total bytes read
return count;
} // read(byte[],int,int):int
/** Skips the specified number of bytes from the input stream. */
public long skip(long n) throws IOException {
if (!fClosed) {
// NOTE: This should be rewritten to be more efficient. -Ac
for (long i = 0; i < n; i++) {
int b = read();
if (b == -1) {
return i + 1;
}
}
return n;
}
return 0;
} // skip(long):long
/**
* Closes the input stream. This method will search for the end of
* the wrapped input, positioning the stream at after the end packet.
*
* <strong>Note:</strong> This method does not close the underlying
* input stream.
*/
public void close() throws IOException {
if (!fClosed) {
fClosed = true;
do {
super.in.skip(fPacketCount);
fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;
} while (fPacketCount > 0);
}
} // close()
} // class WrappedInputStream
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.DataOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* This output stream works in conjunction with the WrappedInputStream
* to introduce a protocol for sending arbitrary length data in a
* uniform way. This output stream allows variable length data to be
* inserted into an existing output stream so that it can be read by
* an input stream without reading too many bytes (in case of buffering
* by the input stream).
*
* This output stream is used like any normal output stream. The protocol
* is introduced by the WrappedOutputStream and does not need to be known
* by the user of this class. However, for those that are interested, the
* method is described below.
*
* The output stream writes the requested bytes as packets of binary
* information. The packet consists of a header and payload. The header
* is two bytes of a single unsigned short (written in network order)
* that specifies the length of bytes in the payload. A header value of
* 0 indicates that the stream is "closed".
*
* <strong>Note:</strong> For this wrapped output stream to be used,
* the application <strong>must</strong> call <code>close()</code>
* to end the output.
*
* @see WrappedInputStream
*
* @author Andy Clark, IBM
*
* @version $Id: WrappedOutputStream.java 447688 2006-09-19 02:39:49Z mrglavas $
*/
public class WrappedOutputStream
extends FilterOutputStream {
//
// Constants
//
/** Default buffer size (1024). */
public static final int DEFAULT_BUFFER_SIZE = 1024;
//
// Data
//
/** Buffer. */
protected byte[] fBuffer;
/** Buffer position. */
protected int fPosition;
/**
* Data output stream. This stream is used to output the block sizes
* into the data stream that are read by the WrappedInputStream.
*
* <strong>Note:</strong> The data output stream is only used for
* writing the byte count for performance reasons. We avoid the
* method indirection for writing the byte data.
*/
protected DataOutputStream fDataOutputStream;
//
// Constructors
//
/** Constructs a wrapper for the given output stream. */
public WrappedOutputStream(OutputStream stream) {
this(stream, DEFAULT_BUFFER_SIZE);
} // <init>(OutputStream)
/**
* Constructs a wrapper for the given output stream with the
* given buffer size.
*/
public WrappedOutputStream(OutputStream stream, int bufferSize) {
super(stream);
fBuffer = new byte[bufferSize];
fDataOutputStream = new DataOutputStream(stream);
} // <init>(OutputStream)
//
// OutputStream methods
//
/**
* Writes a single byte to the output.
*
* <strong>Note:</strong> Single bytes written to the output stream
* will be buffered
*/
public void write(int b) throws IOException {
fBuffer[fPosition++] = (byte)b;
if (fPosition == fBuffer.length) {
fPosition = 0;
fDataOutputStream.writeInt(fBuffer.length);
super.out.write(fBuffer, 0, fBuffer.length);
}
} // write(int)
/** Writes an array of bytes to the output. */
public void write(byte[] b, int offset, int length)
throws IOException {
// flush existing buffer
if (fPosition > 0) {
flush0();
}
// write header followed by actual bytes
fDataOutputStream.writeInt(length);
super.out.write(b, offset, length);
} // write(byte[])
/**
* Flushes the output buffer, writing all bytes currently in
* the buffer to the output.
*/
public void flush() throws IOException {
flush0();
super.out.flush();
} // flush()
/**
* Closes the output stream. This method <strong>must</strong> be
* called when done writing all data to the output stream.
*
* <strong>Note:</strong> This method does <em>not</em> close the
* actual output stream, only makes the input stream see the stream
* closed. Do not write bytes after closing the output stream.
*/
public void close() throws IOException {
flush0();
fDataOutputStream.writeInt(0);
super.out.flush();
} // close()
//
// Protected methods
//
/**
* Flushes the output buffer, writing all bytes currently in
* the buffer to the output. This method does not call the
* flush() method of the output stream; it merely writes the
* remaining bytes in the buffer.
*/
public void flush0() throws IOException {
int length = fPosition;
fPosition = 0;
if (length > 0) {
fDataOutputStream.writeInt(length);
super.out.write(fBuffer, 0, length);
}
} // flush0()
} // class WrappedOutputStream
Reads from an underlying InputStream up to a defined number of bytes or the end of the underlying stream
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id: SubInputStream.java 604883 2007-12-17 14:36:37Z jeremias $ */
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* This class is a FilterInputStream descendant that reads from an underlying InputStream
* up to a defined number of bytes or the end of the underlying stream. Closing this InputStream
* will not result in the underlying InputStream to be closed, too.
*
* This InputStream can be used to read chunks from a larger file of which the length is
* known in advance.
*/
public class SubInputStream extends FilterInputStream {
/** Indicates the number of bytes remaining to be read from the underlying InputStream. */
private long bytesToRead;
/**
* Indicates whether the underlying stream should be closed when the {@link #close()} method
* is called.
*/
private boolean closeUnderlying = false;
/**
* Creates a new SubInputStream.
* @param in the InputStream to read from
* @param maxLen the maximum number of bytes to read from the underlying InputStream until
* the end-of-file is signalled.
* @param closeUnderlying true if the underlying stream should be closed when the
* {@link #close()} method is called.
*/
public SubInputStream(InputStream in, long maxLen, boolean closeUnderlying) {
super(in);
this.bytesToRead = maxLen;
this.closeUnderlying = closeUnderlying;
}
/**
* Creates a new SubInputStream. The underlying stream is not closed, when close() is called.
* @param in the InputStream to read from
* @param maxLen the maximum number of bytes to read from the underlying InputStream until
* the end-of-file is signalled.
*/
public SubInputStream(InputStream in, long maxLen) {
this(in, maxLen, false);
}
/** {@inheritDoc} */
public int read() throws IOException {
if (bytesToRead > 0) {
int result = super.read();
if (result >= 0) {
bytesToRead--;
return result;
} else {
return -1;
}
} else {
return -1;
}
}
/** {@inheritDoc} */
public int read(byte[] b, int off, int len) throws IOException {
if (bytesToRead == 0) {
return -1;
}
int effRead = (int)Math.min(bytesToRead, len);
//cast to int is safe because len can never be bigger than Integer.MAX_VALUE
int result = super.read(b, off, effRead);
if (result >= 0) {
bytesToRead -= result;
}
return result;
}
/** {@inheritDoc} */
public long skip(long n) throws IOException {
long effRead = Math.min(bytesToRead, n);
long result = super.skip(effRead);
bytesToRead -= result;
return result;
}
/** {@inheritDoc} */
public void close() throws IOException {
this.bytesToRead = 0;
if (this.closeUnderlying) {
super.close();
}
}
}
//////////////////////////////////
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id: SubInputStreamTestCase.java 604883 2007-12-17 14:36:37Z jeremias $ */
package org.apache.xmlgraphics.util.io;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import junit.framework.TestCase;
/**
* Test case for SubInputStream.
*/
public class SubInputStreamTestCase extends TestCase {
/**
* Main constructor.
* @param name the test case"s name
* @see junit.framework.TestCase#TestCase(String)
*/
public SubInputStreamTestCase(String name) {
super(name);
}
/**
* Tests SubInputStream.
* @throws Exception if an error occurs
*/
public void testMain() throws Exception {
//Initialize test data
byte[] data = new byte[256];
for (int i = 0; i < data.length; i++) {
data[i] = (byte)(i & 0xff);
}
int v, c;
byte[] buf;
String s;
SubInputStream subin = new SubInputStream(new ByteArrayInputStream(data), 10);
v = subin.read();
assertEquals(0, v);
v = subin.read();
assertEquals(1, v);
buf = new byte[4];
c = subin.read(buf);
assertEquals(4, c);
s = new String(buf, "US-ASCII");
assertEquals("\u0002\u0003\u0004\u0005", s);
Arrays.fill(buf, (byte)0);
c = subin.read(buf, 2, 2);
assertEquals(2, c);
s = new String(buf, "US-ASCII");
assertEquals("\u0000\u0000\u0006\u0007", s);
Arrays.fill(buf, (byte)0);
c = subin.read(buf);
assertEquals(2, c);
s = new String(buf, "US-ASCII");
assertEquals("\u0008\u0009\u0000\u0000", s);
}
}