On 29.05.2008 00:06, Joerg Heinicke wrote:

Only issue I want to solve before the release is the BufferedOutputStream issue. I planned to do it this weekend.

Done. Please review the file attached. It's still completely untested. At the moment I need some sleep ;) I will write junit tests for it this week and eventually commit it.

Joerg
/*
 * 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.
 */
package org.apache.cocoon.util;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * This class is similar to the [EMAIL PROTECTED] 
java.io.BufferedOutputStream}. In
 * addition it provides an increasing buffer, the possibility to reset the
 * buffer and it counts the number of bytes written to the output stream.
 * 
 * @author <a href="mailto:[EMAIL PROTECTED]">Carsten Ziegeler</a>
 * @version CVS $Id: BufferedOutputStream.java 433543 2006-08-22 06:22:54Z 
crossley $
 * @since   2.1
 */
public class BufferedOutputStream extends FilterOutputStream {

    private byte buffer[];

    private int count;
    private int totalCount;

    private final int flushBufferSize;

    /**
     * Creates a new buffered output stream to write data to the 
     * specified underlying output stream with a default 8192-byte
     * buffer size.
     *
     * @param   out   the underlying output stream.
     */
    public BufferedOutputStream(final OutputStream out) {
        this(out, 8192, 32768);
    }

    /**
     * Creates a new buffered output stream to write data to the specified
     * underlying output stream with the specified buffer sizes.
     *
     * @param out    the underlying output stream.
     * @param initialBufferSize  the initial buffer size. Must be greater than 
0.
     *                           Will be limited to the flush buffer size.
     * @param flushBufferSize  the buffer size when the stream is flushed. Must
     *                         be greater than 0 or -1 meaning the stream never
     *                         flushes itself.
     */
    public BufferedOutputStream(final OutputStream out,
                                final int initialBufferSize,
                                final int flushBufferSize) {
        super(out);
        if (initialBufferSize <= 0) {
            throw new IllegalArgumentException("Initial buffer size <= 0");
        }
        if (flushBufferSize <= 0 && flushBufferSize != -1) {
            throw new IllegalArgumentException("Flush buffer size <= 0 && != 
-1");
        }
        this.buffer =
            new byte[initialBufferSize > flushBufferSize ? flushBufferSize : 
initialBufferSize];
        this.flushBufferSize = flushBufferSize;
    }

    /**
     * Writes the specified byte to this buffered output stream. 
     *
     * @param      b   the byte to be written.
     * @exception  IOException  if an I/O error occurs.
     */
    public void write(int b) throws IOException {
        if (this.count == this.buffer.length) {
            // No need to check return value, can NEVER be 0.
            this.increaseBuffer(1);
        }

        this.buffer[this.count++] = (byte)b;
        this.totalCount++;

        if (this.count == this.flushBufferSize) {
            flush();
        }
    }

    /**
     * Writes <code>len</code> bytes from the specified byte array 
     * starting at offset <code>off</code> to this buffered output stream.
     *
     * <p> Ordinarily this method stores bytes from the given array into this
     * stream's buffer, flushing the buffer to the underlying output stream as
     * needed.  If the requested length is at least as large as this stream's
     * buffer, however, then this method will flush the buffer and write the
     * bytes directly to the underlying output stream.  Thus redundant
     * <code>BufferedOutputStream</code>s will not copy data unnecessarily.
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs.
     */
    public void write(final byte[] b, final int off, final int len) throws 
IOException {
        if (len > this.buffer.length - this.count) {
            int actualIncrease = this.increaseBuffer(len);
            if (actualIncrease < len) {
                // Needs to be written in chunks by recursive calls to this 
method.
                write(b, off, actualIncrease);
                write(b, off + actualIncrease, len - actualIncrease);
                return;
            }
        }

        System.arraycopy(b, off, this.buffer, this.count, len);
        this.count += len;
        this.totalCount += len;

        if (this.count == this.flushBufferSize) {
            flush();
        }
    }

    /**
     * Flushes this buffered output stream. 
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void flush() throws IOException {
        writeBuffer();
        this.out.flush();
    }

    /**
     * Closes this buffered output stream.
     * Flush before closing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void close() throws IOException {
        flush();
        super.close();
    }

    /**
     * Write the buffer
     */
    private void writeBuffer() throws IOException {
        if (this.count > 0) {
            this.out.write(this.buffer, 0, this.count);
            this.clearBuffer();
        }
    }

    /**
     * Increase the buffer by at least as many bytes as specified via the
     * parameter, but not exceeding the flushBufferSize. The actual increase is
     * returned.
     * 
     * @return  increase in buffer size.
     */
    private int increaseBuffer(final int increase) {
        int oldLength = this.buffer.length;
        if (oldLength == this.flushBufferSize) {
            // fast way out
            return 0;
        }

        int newLength = oldLength;
        int actualIncrease;
        do {
            newLength = newLength * 2;
            if (this.flushBufferSize > 0 && newLength >= this.flushBufferSize) {
                newLength = this.flushBufferSize;
                actualIncrease = newLength - oldLength;
                break;
            }
            actualIncrease = newLength - oldLength;
        } while (actualIncrease < increase);

        // Because of the "fast way out" above at this point there should 
always be an increase.
        byte[] newBuffer = new byte[newLength];
        if (this.count > 0) {
            System.arraycopy(this.buffer, 0, newBuffer, 0, this.count);
        }
        this.buffer = newBuffer;
        return actualIncrease;
    }

    /**
     * Clear/reset the buffer
     * @deprecated Public access is deprecated. Use [EMAIL PROTECTED] #reset()} 
instead.
     */
    public void clearBuffer() {
        this.count = 0;
    }

    /**
     * Reset the BufferedOutputStream to the last [EMAIL PROTECTED] #flush()}.
     */
    public void reset() {
        this.clearBuffer();
    }

    /**
     * Return the size of the current buffer
     */
    public int getCount() {
        return this.totalCount;
    }

}

Reply via email to