Java Tutorial/File/FilterInputStream

Материал из Java эксперт
Перейти к: навигация, поиск

An limited-data-size input stream

   <source lang="java">

/*

* 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 int in the range
    * 0 to 255. If no byte is available
    * because the end of the stream has been reached, the value
    * -1 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 in.read() and returns the result.
    *
    * @return     the next byte of data, or -1 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 len bytes of data from this input stream
    * into an array of bytes. If len is not zero, the method
    * blocks until some input is available; otherwise, no
    * bytes are read and 0 is returned.
    * 
    * This method simply performs in.read(b, off, len)
    * and returns the result.
    *
    * @param      b     the buffer into which the data is read.
    * @param      off   The start offset in the destination array
    *                   b.
    * @param      len   the maximum number of bytes read.
    * @return     the total number of bytes read into the buffer, or
    *             -1 if there is no more data because the end of
    *             the stream has been reached.
    * @exception  NullPointerException If b is null.
    * @exception  IndexOutOfBoundsException If off is negative,
    * len is negative, or len is greater than
    * b.length - off
    * @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 in.close().
    *
    * @exception  IOException  if an I/O error occurs.
    * @see        java.io.FilterInputStream#in
    */
   public void close() throws IOException {
       closed = true;
       super.close();
   }

}</source>





A trace of the data that is being retrieved from an input stream

   <source lang="java">

/*

* 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
    * -1 if no data is available. Writes out the read
    * byte into the trace stream, if trace mode is true
    */
   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 len bytes of data from this input stream
    * into an array of bytes. Returns -1 if no more data
    * is available. Writes out the read bytes into the trace stream, if 
    * trace mode is true
    */
   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);
 }
   }

}</source>





introduce a protocol for reading arbitrary length data in a uniform way

   <source lang="java">

/*

* 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.
* 
* Note: 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.
    * 
    * Note: 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.
    * 
    * Note: 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".
* 
* Note: For this wrapped output stream to be used,
* the application must call close()
* 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.
    * 
    * Note: 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. 
    * 
    * Note: 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 must be
    * called when done writing all data to the output stream.
    * 
    * Note: This method does not 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</source>





Reads from an underlying InputStream up to a defined number of bytes or the end of the underlying stream

   <source lang="java">

/*

* 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);
   }
   

}</source>