| package 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$ |
| */ |
| 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(); |
| } |
| } |