All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.lib.ClosableCharArrayWriter Maven / Gradle / Ivy

There is a newer version: 2.7.4
Show newest version
/* Copyright (c) 2001-2019, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb.lib;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Writer;

/**
 * @todo - finer-grained synchronization to reduce average
 * potential monitor contention
 */

/**
 * Provides Closable semantics ordinarily missing in a
 * {@link java.io.CharArrayWriter}. 

* * Accumulates output in a character array that automatically grows as needed.

* * Data is retrieved using toCharArray(), toCharArrayReader() * and toString().

* * {@link #close() Closing} a ClosableCharArrayWriter prevents * further write operations, but all other operations will succeed until after * the first invocation of {@link #free() free()}.

* * Freeing a ClosableCharArrayWriter closes the writer and * releases its internal buffer, preventing successful invocation of all * operations, with the exception of size(), close(), * isClosed(), free() and isFreed().

* * This class is especially useful when an accumulating writer must be * handed off to an extenal client under contract that the writer should * exhibit true Closable behaviour, both in response to internally tracked * events and to client invocation of the Writer.close() method. * * @author Campbell Burnet (campbell-burnet@users dot sourceforge.net) * @version 1.8.x * @since 1.8.x */ public class ClosableCharArrayWriter extends Writer { /** * Data buffer. */ protected char[] buf; /** * # of valid characters in buffer. */ protected int count; /** * Whether this writer is closed. */ protected boolean closed; /** * Whether this writer is freed. */ protected boolean freed; /** * Creates a new writer.

* * The buffer capacity is initially 32 characters, although its size * automatically increases when necessary. */ public ClosableCharArrayWriter() { this(32); } /** * Creates a new writer with a buffer capacity of the specified * size, in characters. * * @param size the initial size. * @exception IllegalArgumentException if size is negative. */ public ClosableCharArrayWriter(int size) throws IllegalArgumentException { if (size < 0) { throw new IllegalArgumentException("Negative initial size: " + size); // NOI18N } buf = new char[size]; } /** * Writes the specified single character. * * @param c the single character to be written. * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #close() closed}. */ public synchronized void write(int c) throws IOException { checkClosed(); int newcount = count + 1; if (newcount > buf.length) { buf = copyOf(buf, Math.max(buf.length << 1, newcount)); } buf[count] = (char) c; count = newcount; } /** * Writes the designated portion of the designated character array

. * * @param c the source character sequence. * @param off the start offset in the source character sequence. * @param len the number of characters to write. * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #close() closed}. */ public synchronized void write(char[] c, int off, int len) throws IOException { checkClosed(); if ((off < 0) || (off > c.length) || (len < 0) || ((off + len) > c.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } int newcount = count + len; if (newcount > buf.length) { buf = copyOf(buf, Math.max(buf.length << 1, newcount)); } System.arraycopy(c, off, buf, count, len); count = newcount; } /** * Efficiently writes the designated portion of the designated string.

* * The operation occurs as if by calling * str.getChars(off, off + len, buf, count).

* * @param str the string from which to write * @param off the start offset in the string. * @param len the number of characters to write. * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #close() closed}. */ public synchronized void write(String str, int off, int len) throws IOException { checkClosed(); int strlen = str.length(); if ((off < 0) || (off > strlen) || (len < 0) || ((off + len) > strlen) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } int newcount = count + len; if (newcount > buf.length) { buf = copyOf(buf, Math.max(buf.length << 1, newcount)); } str.getChars(off, off + len, buf, count); count = newcount; } /** * By default, does nothing.

* * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #close() closed}. */ public void flush() throws IOException { checkClosed(); } /** * Writes the complete contents of this writer's buffered data to the * specified writer.

* * The operation occurs as if by calling out.write(buf, 0, count). * * @param out the writer to which to write the data. * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #free() freed}. */ public synchronized void writeTo(Writer out) throws IOException { checkFreed(); if (count > 0) { out.write(buf, 0, count); } } /** * Returns the current capacity of this writer's data buffer. * * @return the current capacity (the length of the internal * data array) * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #free() freed}. */ public synchronized int capacity() throws IOException { checkFreed(); return buf.length; } /** * Resets the count field of this writer to zero, so that all * currently accumulated output is effectively discarded. Further write * operations will reuse the allocated buffer space. * * @see #count * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this output stream has been {@link #close() closed}. */ public synchronized void reset() throws IOException { checkClosed(); count = 0; } /** * Attempts to reduce this writer's buffer capacity to its current size.

* * If the buffer is larger than necessary to hold its current sequence of * characters, then it may be resized to become more space efficient. * Calling this method may, but is not required to, affect the value * returned by a subsequent call to the {@link #capacity()} method. */ public synchronized void trimToSize() throws IOException { checkFreed(); if (buf.length > count) { buf = copyOf(buf, count); } } /** * Creates a newly allocated character array. Its size is the current * size of this writer and the valid contents of the buffer * have been copied into it. * * @return the current contents of this writer, as a character array. * @see #size() * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #free() freed}. */ public synchronized char[] toCharArray() throws IOException { checkFreed(); return copyOf(buf, count); } /** * Returns the current size of this writer's accumulated character data. * * @return the value of the count field, which is the number * of valid characters accumulated in this writer. * @see #count * @throws java.io.IOException never */ public synchronized int size() throws IOException { return count; } /** * Sets the size of this writer's accumulated character data.

* * @param newSize the new size of this writer's accumulated data * @throws ArrayIndexOutOfBoundsException if new size is negative */ public synchronized void setSize(int newSize) { if (newSize < 0) { throw new ArrayIndexOutOfBoundsException(newSize); } else if (newSize > buf.length) { buf = copyOf(buf, Math.max(buf.length << 1, newSize)); } count = newSize; } /** * Performs an efficient (zero-copy) conversion of the character data * accumulated in this writer to a reader.

* * To ensure the integrity of the resulting reader, {@link #free() * free} is invoked upon this writer as a side-effect. * * @return a reader representing this writer's accumulated * character data * @throws java.io.IOException if an I/O error occurs. * In particular, an IOException may be thrown * if this writer has been {@link #free() freed}. */ public synchronized CharArrayReader toCharArrayReader() throws IOException { checkFreed(); CharArrayReader reader = new CharArrayReader(buf, 0, count); //System.out.println("toCharArrayReader::buf.length: " + buf.length); free(); return reader; } /** * Converts this writer's accumulated data into a string. * * @return String constructed from this writer's accumulated data * @throws RuntimeException may be thrown if this writer has been * {@link #free() freed}. */ public synchronized String toString() { try { checkFreed(); } catch (IOException ex) { throw new RuntimeException(ex.toString()); } return new String(buf, 0, count); } /** * Closes this object for further writing.

* * Other operations may continue to succeed until after the first invocation * of {@link #free() free()}.

* * @throws java.io.IOException if an I/O error occurs (default: never) */ public synchronized void close() throws IOException { closed = true; } /** * @return true if this writer is closed, else false */ public synchronized boolean isClosed() { return closed; } /** * Closes this object and releases the underlying buffer for * garbage collection.

* * @throws java.io.IOException if an I/O error occurs while closing * this writer (default: never). */ public synchronized void free() throws IOException { closed = true; freed = true; buf = null; count = 0; } /** * @return true if this writer is freed; else false. */ public synchronized boolean isFreed() { return freed; } /** * @throws java.io.IOException if this writer is closed. */ protected synchronized void checkClosed() throws IOException { if (closed) { throw new IOException("writer is closed."); // NOI18N } } /** * @throws java.io.IOException if this writer is freed. */ protected synchronized void checkFreed() throws IOException { if (freed) { throw new IOException("write buffer is freed."); // NOI18N } } /** * Retrieves a copy of original with the given * newLength.

* * @param original the object to copy * @param newLength the length of the copy * @return copy of original with the given newLength */ protected char[] copyOf(char[] original, int newLength) { char[] copy = new char[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } }