001: package org.apache.velocity.io;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.IOException;
023: import java.io.Writer;
024:
025: /**
026: * Implementation of a fast Writer. It was originally taken from JspWriter
027: * and modified to have less syncronization going on.
028: *
029: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
030: * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
031: * @author Anil K. Vijendran
032: * @version $Id: VelocityWriter.java 463298 2006-10-12 16:10:32Z henning $
033: */
034: public final class VelocityWriter extends Writer {
035: /**
036: * constant indicating that the Writer is not buffering output
037: */
038: public static final int NO_BUFFER = 0;
039:
040: /**
041: * constant indicating that the Writer is buffered and is using the
042: * implementation default buffer size
043: */
044: public static final int DEFAULT_BUFFER = -1;
045:
046: /**
047: * constant indicating that the Writer is buffered and is unbounded;
048: * this is used in BodyContent
049: */
050: public static final int UNBOUNDED_BUFFER = -2;
051:
052: private int bufferSize;
053: private boolean autoFlush;
054:
055: private Writer writer;
056:
057: private char cb[];
058: private int nextChar;
059:
060: private static int defaultCharBufferSize = 8 * 1024;
061:
062: /**
063: * Create a buffered character-output stream that uses a default-sized
064: * output buffer.
065: *
066: * @param writer Writer to wrap around
067: */
068: public VelocityWriter(Writer writer) {
069: this (writer, defaultCharBufferSize, true);
070: }
071:
072: /**
073: * private constructor.
074: */
075: private VelocityWriter(int bufferSize, boolean autoFlush) {
076: this .bufferSize = bufferSize;
077: this .autoFlush = autoFlush;
078: }
079:
080: /**
081: * This method returns the size of the buffer used by the JspWriter.
082: *
083: * @return the size of the buffer in bytes, or 0 is unbuffered.
084: */
085: public int getBufferSize() {
086: return bufferSize;
087: }
088:
089: /**
090: * This method indicates whether the JspWriter is autoFlushing.
091: *
092: * @return if this JspWriter is auto flushing or throwing IOExceptions on
093: * buffer overflow conditions
094: */
095: public boolean isAutoFlush() {
096: return autoFlush;
097: }
098:
099: /**
100: * Create a new buffered character-output stream that uses an output
101: * buffer of the given size.
102: *
103: * @param writer Writer to wrap around
104: * @param sz Output-buffer size, a positive integer
105: * @param autoFlush
106: *
107: * @exception IllegalArgumentException If sz is <= 0
108: */
109: public VelocityWriter(Writer writer, int sz, boolean autoFlush) {
110: this (sz, autoFlush);
111: if (sz < 0)
112: throw new IllegalArgumentException("Buffer size <= 0");
113: this .writer = writer;
114: cb = sz == 0 ? null : new char[sz];
115: nextChar = 0;
116: }
117:
118: /**
119: * Flush the output buffer to the underlying character stream, without
120: * flushing the stream itself. This method is non-private only so that it
121: * may be invoked by PrintStream.
122: */
123: private final void flushBuffer() throws IOException {
124: if (bufferSize == 0)
125: return;
126: if (nextChar == 0)
127: return;
128: writer.write(cb, 0, nextChar);
129: nextChar = 0;
130: }
131:
132: /**
133: * Discard the output buffer.
134: */
135: public final void clear() {
136: nextChar = 0;
137: }
138:
139: private final void bufferOverflow() throws IOException {
140: throw new IOException("overflow");
141: }
142:
143: /**
144: * Flush the stream.
145: * @throws IOException
146: *
147: */
148: public final void flush() throws IOException {
149: flushBuffer();
150: if (writer != null) {
151: writer.flush();
152: }
153: }
154:
155: /**
156: * Close the stream.
157: * @throws IOException
158: *
159: */
160: public final void close() throws IOException {
161: if (writer == null)
162: return;
163: flush();
164: }
165:
166: /**
167: * @return the number of bytes unused in the buffer
168: */
169: public final int getRemaining() {
170: return bufferSize - nextChar;
171: }
172:
173: /**
174: * Write a single character.
175: * @param c
176: * @throws IOException
177: *
178: */
179: public final void write(int c) throws IOException {
180: if (bufferSize == 0) {
181: writer.write(c);
182: } else {
183: if (nextChar >= bufferSize)
184: if (autoFlush)
185: flushBuffer();
186: else
187: bufferOverflow();
188: cb[nextChar++] = (char) c;
189: }
190: }
191:
192: /**
193: * Our own little min method, to avoid loading
194: * <code>java.lang.Math</code> if we've run out of file
195: * descriptors and we're trying to print a stack trace.
196: */
197: private final int min(int a, int b) {
198: return (a < b ? a : b);
199: }
200:
201: /**
202: * Write a portion of an array of characters.
203: *
204: * <p> Ordinarily this method stores characters from the given array into
205: * this stream's buffer, flushing the buffer to the underlying stream as
206: * needed. If the requested length is at least as large as the buffer,
207: * however, then this method will flush the buffer and write the characters
208: * directly to the underlying stream. Thus redundant
209: * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
210: *
211: * @param cbuf A character array
212: * @param off Offset from which to start reading characters
213: * @param len Number of characters to write
214: * @throws IOException
215: *
216: */
217: public final void write(char cbuf[], int off, int len)
218: throws IOException {
219: if (bufferSize == 0) {
220: writer.write(cbuf, off, len);
221: return;
222: }
223:
224: if (len == 0) {
225: return;
226: }
227:
228: if (len >= bufferSize) {
229: /* If the request length exceeds the size of the output buffer,
230: flush the buffer and then write the data directly. In this
231: way buffered streams will cascade harmlessly. */
232: if (autoFlush)
233: flushBuffer();
234: else
235: bufferOverflow();
236: writer.write(cbuf, off, len);
237: return;
238: }
239:
240: int b = off, t = off + len;
241: while (b < t) {
242: int d = min(bufferSize - nextChar, t - b);
243: System.arraycopy(cbuf, b, cb, nextChar, d);
244: b += d;
245: nextChar += d;
246: if (nextChar >= bufferSize)
247: if (autoFlush)
248: flushBuffer();
249: else
250: bufferOverflow();
251: }
252: }
253:
254: /**
255: * Write an array of characters. This method cannot be inherited from the
256: * Writer class because it must suppress I/O exceptions.
257: * @param buf
258: * @throws IOException
259: */
260: public final void write(char buf[]) throws IOException {
261: write(buf, 0, buf.length);
262: }
263:
264: /**
265: * Write a portion of a String.
266: *
267: * @param s String to be written
268: * @param off Offset from which to start reading characters
269: * @param len Number of characters to be written
270: * @throws IOException
271: *
272: */
273: public final void write(String s, int off, int len)
274: throws IOException {
275: if (bufferSize == 0) {
276: writer.write(s, off, len);
277: return;
278: }
279: int b = off, t = off + len;
280: while (b < t) {
281: int d = min(bufferSize - nextChar, t - b);
282: s.getChars(b, b + d, cb, nextChar);
283: b += d;
284: nextChar += d;
285: if (nextChar >= bufferSize)
286: if (autoFlush)
287: flushBuffer();
288: else
289: bufferOverflow();
290: }
291: }
292:
293: /**
294: * Write a string. This method cannot be inherited from the Writer class
295: * because it must suppress I/O exceptions.
296: * @param s
297: * @throws IOException
298: */
299: public final void write(String s) throws IOException {
300: if (s != null) {
301: write(s, 0, s.length());
302: }
303: }
304:
305: /**
306: * resets this class so that it can be reused
307: * @param writer
308: *
309: */
310: public final void recycle(Writer writer) {
311: this.writer = writer;
312: clear();
313: }
314: }
|