Java Tutorial/File/FilterInputStream

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

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