package bboss.org.apache.velocity.io; /* * 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.IOException; import java.io.Writer; /** * Implementation of a fast Writer. It was originally taken from JspWriter * and modified to have less syncronization going on. * * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> * @author Anil K. Vijendran * @version $Id: VelocityWriter.java 463298 2006-10-12 16:10:32Z henning $ */ public final class VelocityWriter extends Writer { /** * constant indicating that the Writer is not buffering output */ public static final int NO_BUFFER = 0; /** * constant indicating that the Writer is buffered and is using the * implementation default buffer size */ public static final int DEFAULT_BUFFER = -1; /** * constant indicating that the Writer is buffered and is unbounded; * this is used in BodyContent */ public static final int UNBOUNDED_BUFFER = -2; private int bufferSize; private boolean autoFlush; private Writer writer; private char cb[]; private int nextChar; private static int defaultCharBufferSize = 8 * 1024; /** * Create a buffered character-output stream that uses a default-sized * output buffer. * * @param writer Writer to wrap around */ public VelocityWriter(Writer writer) { this(writer, defaultCharBufferSize, true); } /** * private constructor. */ private VelocityWriter(int bufferSize, boolean autoFlush) { this.bufferSize = bufferSize; this.autoFlush = autoFlush; } /** * This method returns the size of the buffer used by the JspWriter. * * @return the size of the buffer in bytes, or 0 is unbuffered. */ public int getBufferSize() { return bufferSize; } /** * This method indicates whether the JspWriter is autoFlushing. * * @return if this JspWriter is auto flushing or throwing IOExceptions on * buffer overflow conditions */ public boolean isAutoFlush() { return autoFlush; } /** * Create a new buffered character-output stream that uses an output * buffer of the given size. * * @param writer Writer to wrap around * @param sz Output-buffer size, a positive integer * @param autoFlush * * @exception IllegalArgumentException If sz is <= 0 */ public VelocityWriter(Writer writer, int sz, boolean autoFlush) { this(sz, autoFlush); if (sz < 0) throw new IllegalArgumentException("Buffer size <= 0"); this.writer = writer; cb = sz == 0 ? null : new char[sz]; nextChar = 0; } /** * Flush the output buffer to the underlying character stream, without * flushing the stream itself. This method is non-private only so that it * may be invoked by PrintStream. */ private final void flushBuffer() throws IOException { if (bufferSize == 0) return; if (nextChar == 0) return; writer.write(cb, 0, nextChar); nextChar = 0; } /** * Discard the output buffer. */ public final void clear() { nextChar = 0; } private final void bufferOverflow() throws IOException { throw new IOException("overflow"); } /** * Flush the stream. * @throws IOException * */ public final void flush() throws IOException { flushBuffer(); if (writer != null) { writer.flush(); } } /** * Close the stream. * @throws IOException * */ public final void close() throws IOException { if (writer == null) return; flush(); } /** * @return the number of bytes unused in the buffer */ public final int getRemaining() { return bufferSize - nextChar; } /** * Write a single character. * @param c * @throws IOException * */ public final void write(int c) throws IOException { if (bufferSize == 0) { writer.write(c); } else { if (nextChar >= bufferSize) if (autoFlush) flushBuffer(); else bufferOverflow(); cb[nextChar++] = (char) c; } } /** * Our own little min method, to avoid loading * <code>java.lang.Math</code> if we've run out of file * descriptors and we're trying to print a stack trace. */ private final int min(int a, int b) { return (a < b ? a : b); } /** * Write a portion of an array of characters. * * <p> Ordinarily this method stores characters from the given array into * this stream's buffer, flushing the buffer to the underlying stream as * needed. If the requested length is at least as large as the buffer, * however, then this method will flush the buffer and write the characters * directly to the underlying stream. Thus redundant * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily. * * @param cbuf A character array * @param off Offset from which to start reading characters * @param len Number of characters to write * @throws IOException * */ public final void write(char cbuf[], int off, int len) throws IOException { if (bufferSize == 0) { writer.write(cbuf, off, len); return; } if (len == 0) { return; } if (len >= bufferSize) { /* If the request length exceeds the size of the output buffer, flush the buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ if (autoFlush) flushBuffer(); else bufferOverflow(); writer.write(cbuf, off, len); return; } int b = off, t = off + len; while (b < t) { int d = min(bufferSize - nextChar, t - b); System.arraycopy(cbuf, b, cb, nextChar, d); b += d; nextChar += d; if (nextChar >= bufferSize) if (autoFlush) flushBuffer(); else bufferOverflow(); } } /** * Write an array of characters. This method cannot be inherited from the * Writer class because it must suppress I/O exceptions. * @param buf * @throws IOException */ public final void write(char buf[]) throws IOException { write(buf, 0, buf.length); } /** * Write a portion of a String. * * @param s String to be written * @param off Offset from which to start reading characters * @param len Number of characters to be written * @throws IOException * */ public final void write(String s, int off, int len) throws IOException { if (bufferSize == 0) { writer.write(s, off, len); return; } int b = off, t = off + len; while (b < t) { int d = min(bufferSize - nextChar, t - b); s.getChars(b, b + d, cb, nextChar); b += d; nextChar += d; if (nextChar >= bufferSize) if (autoFlush) flushBuffer(); else bufferOverflow(); } } /** * Write a string. This method cannot be inherited from the Writer class * because it must suppress I/O exceptions. * @param s * @throws IOException */ public final void write(String s) throws IOException { if (s != null) { write(s, 0, s.length()); } } /** * resets this class so that it can be reused * @param writer * */ public final void recycle(Writer writer) { this.writer = writer; clear(); } }