001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.jasper.runtime;
018:
019: import java.io.IOException;
020: import java.io.Writer;
021: import java.security.AccessController;
022: import java.security.PrivilegedAction;
023:
024: import javax.servlet.ServletResponse;
025: import javax.servlet.jsp.JspWriter;
026:
027: import org.apache.jasper.Constants;
028: import org.apache.jasper.compiler.Localizer;
029:
030: /**
031: * Write text to a character-output stream, buffering characters so as
032: * to provide for the efficient writing of single characters, arrays,
033: * and strings.
034: *
035: * Provide support for discarding for the output that has been
036: * buffered.
037: *
038: * This needs revisiting when the buffering problems in the JSP spec
039: * are fixed -akv
040: *
041: * @author Anil K. Vijendran
042: */
043: public class JspWriterImpl extends JspWriter {
044:
045: private Writer out;
046: private ServletResponse response;
047: private char cb[];
048: private int nextChar;
049: private boolean flushed = false;
050: private boolean closed = false;
051:
052: public JspWriterImpl() {
053: super (Constants.DEFAULT_BUFFER_SIZE, true);
054: }
055:
056: /**
057: * Create a buffered character-output stream that uses a default-sized
058: * output buffer.
059: *
060: * @param response A Servlet Response
061: */
062: public JspWriterImpl(ServletResponse response) {
063: this (response, Constants.DEFAULT_BUFFER_SIZE, true);
064: }
065:
066: /**
067: * Create a new buffered character-output stream that uses an output
068: * buffer of the given size.
069: *
070: * @param response A Servlet Response
071: * @param sz Output-buffer size, a positive integer
072: *
073: * @exception IllegalArgumentException If sz is <= 0
074: */
075: public JspWriterImpl(ServletResponse response, int sz,
076: boolean autoFlush) {
077: super (sz, autoFlush);
078: if (sz < 0)
079: throw new IllegalArgumentException("Buffer size <= 0");
080: this .response = response;
081: cb = sz == 0 ? null : new char[sz];
082: nextChar = 0;
083: }
084:
085: void init(ServletResponse response, int sz, boolean autoFlush) {
086: this .response = response;
087: if (sz > 0 && (cb == null || sz > cb.length))
088: cb = new char[sz];
089: nextChar = 0;
090: this .autoFlush = autoFlush;
091: this .bufferSize = sz;
092: }
093:
094: /** Package-level access
095: */
096: void recycle() {
097: flushed = false;
098: closed = false;
099: out = null;
100: nextChar = 0;
101: }
102:
103: /**
104: * Flush the output buffer to the underlying character stream, without
105: * flushing the stream itself. This method is non-private only so that it
106: * may be invoked by PrintStream.
107: */
108: protected final void flushBuffer() throws IOException {
109: if (bufferSize == 0)
110: return;
111: flushed = true;
112: ensureOpen();
113: if (nextChar == 0)
114: return;
115: initOut();
116: out.write(cb, 0, nextChar);
117: nextChar = 0;
118: }
119:
120: private void initOut() throws IOException {
121: if (out == null) {
122: out = response.getWriter();
123: }
124: }
125:
126: private String getLocalizeMessage(final String message) {
127: if (System.getSecurityManager() != null) {
128: return (String) AccessController
129: .doPrivileged(new PrivilegedAction() {
130: public Object run() {
131: return Localizer.getMessage(message);
132: }
133: });
134: } else {
135: return Localizer.getMessage(message);
136: }
137: }
138:
139: /**
140: * Discard the output buffer.
141: */
142: public final void clear() throws IOException {
143: if ((bufferSize == 0) && (out != null))
144: // clear() is illegal after any unbuffered output (JSP.5.5)
145: throw new IllegalStateException(
146: getLocalizeMessage("jsp.error.ise_on_clear"));
147: if (flushed)
148: throw new IOException(
149: getLocalizeMessage("jsp.error.attempt_to_clear_flushed_buffer"));
150: ensureOpen();
151: nextChar = 0;
152: }
153:
154: public void clearBuffer() throws IOException {
155: if (bufferSize == 0)
156: throw new IllegalStateException(
157: getLocalizeMessage("jsp.error.ise_on_clear"));
158: ensureOpen();
159: nextChar = 0;
160: }
161:
162: private final void bufferOverflow() throws IOException {
163: throw new IOException(getLocalizeMessage("jsp.error.overflow"));
164: }
165:
166: /**
167: * Flush the stream.
168: *
169: */
170: public void flush() throws IOException {
171: flushBuffer();
172: if (out != null) {
173: out.flush();
174: }
175: }
176:
177: /**
178: * Close the stream.
179: *
180: */
181: public void close() throws IOException {
182: if (response == null || closed)
183: // multiple calls to close is OK
184: return;
185: flush();
186: if (out != null)
187: out.close();
188: out = null;
189: closed = true;
190: // cb = null;
191: }
192:
193: /**
194: * @return the number of bytes unused in the buffer
195: */
196: public int getRemaining() {
197: return bufferSize - nextChar;
198: }
199:
200: /** check to make sure that the stream has not been closed */
201: private void ensureOpen() throws IOException {
202: if (response == null || closed)
203: throw new IOException("Stream closed");
204: }
205:
206: /**
207: * Write a single character.
208: */
209: public void write(int c) throws IOException {
210: ensureOpen();
211: if (bufferSize == 0) {
212: initOut();
213: out.write(c);
214: } else {
215: if (nextChar >= bufferSize)
216: if (autoFlush)
217: flushBuffer();
218: else
219: bufferOverflow();
220: cb[nextChar++] = (char) c;
221: }
222: }
223:
224: /**
225: * Our own little min method, to avoid loading java.lang.Math if we've run
226: * out of file descriptors and we're trying to print a stack trace.
227: */
228: private int min(int a, int b) {
229: if (a < b)
230: return a;
231: return b;
232: }
233:
234: /**
235: * Write a portion of an array of characters.
236: *
237: * <p> Ordinarily this method stores characters from the given array into
238: * this stream's buffer, flushing the buffer to the underlying stream as
239: * needed. If the requested length is at least as large as the buffer,
240: * however, then this method will flush the buffer and write the characters
241: * directly to the underlying stream. Thus redundant
242: * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
243: *
244: * @param cbuf A character array
245: * @param off Offset from which to start reading characters
246: * @param len Number of characters to write
247: */
248: public void write(char cbuf[], int off, int len) throws IOException {
249: ensureOpen();
250:
251: if (bufferSize == 0) {
252: initOut();
253: out.write(cbuf, off, len);
254: return;
255: }
256:
257: if ((off < 0) || (off > cbuf.length) || (len < 0)
258: || ((off + len) > cbuf.length) || ((off + len) < 0)) {
259: throw new IndexOutOfBoundsException();
260: } else if (len == 0) {
261: return;
262: }
263:
264: if (len >= bufferSize) {
265: /* If the request length exceeds the size of the output buffer,
266: flush the buffer and then write the data directly. In this
267: way buffered streams will cascade harmlessly. */
268: if (autoFlush)
269: flushBuffer();
270: else
271: bufferOverflow();
272: initOut();
273: out.write(cbuf, off, len);
274: return;
275: }
276:
277: int b = off, t = off + len;
278: while (b < t) {
279: int d = min(bufferSize - nextChar, t - b);
280: System.arraycopy(cbuf, b, cb, nextChar, d);
281: b += d;
282: nextChar += d;
283: if (nextChar >= bufferSize)
284: if (autoFlush)
285: flushBuffer();
286: else
287: bufferOverflow();
288: }
289:
290: }
291:
292: /**
293: * Write an array of characters. This method cannot be inherited from the
294: * Writer class because it must suppress I/O exceptions.
295: */
296: public void write(char buf[]) throws IOException {
297: write(buf, 0, buf.length);
298: }
299:
300: /**
301: * Write a portion of a String.
302: *
303: * @param s String to be written
304: * @param off Offset from which to start reading characters
305: * @param len Number of characters to be written
306: */
307: public void write(String s, int off, int len) throws IOException {
308: ensureOpen();
309: if (bufferSize == 0) {
310: initOut();
311: out.write(s, off, len);
312: return;
313: }
314: int b = off, t = off + len;
315: while (b < t) {
316: int d = min(bufferSize - nextChar, t - b);
317: s.getChars(b, b + d, cb, nextChar);
318: b += d;
319: nextChar += d;
320: if (nextChar >= bufferSize)
321: if (autoFlush)
322: flushBuffer();
323: else
324: bufferOverflow();
325: }
326: }
327:
328: /**
329: * Write a string. This method cannot be inherited from the Writer class
330: * because it must suppress I/O exceptions.
331: */
332: public void write(String s) throws IOException {
333: write(s, 0, s.length());
334: }
335:
336: static String lineSeparator = System.getProperty("line.separator");
337:
338: /**
339: * Write a line separator. The line separator string is defined by the
340: * system property <tt>line.separator</tt>, and is not necessarily a single
341: * newline ('\n') character.
342: *
343: * @exception IOException If an I/O error occurs
344: */
345:
346: public void newLine() throws IOException {
347: write(lineSeparator);
348: }
349:
350: /* Methods that do not terminate lines */
351:
352: /**
353: * Print a boolean value. The string produced by <code>{@link
354: * java.lang.String#valueOf(boolean)}</code> is translated into bytes
355: * according to the platform's default character encoding, and these bytes
356: * are written in exactly the manner of the <code>{@link
357: * #write(int)}</code> method.
358: *
359: * @param b The <code>boolean</code> to be printed
360: */
361: public void print(boolean b) throws IOException {
362: write(b ? "true" : "false");
363: }
364:
365: /**
366: * Print a character. The character is translated into one or more bytes
367: * according to the platform's default character encoding, and these bytes
368: * are written in exactly the manner of the <code>{@link
369: * #write(int)}</code> method.
370: *
371: * @param c The <code>char</code> to be printed
372: */
373: public void print(char c) throws IOException {
374: write(String.valueOf(c));
375: }
376:
377: /**
378: * Print an integer. The string produced by <code>{@link
379: * java.lang.String#valueOf(int)}</code> is translated into bytes according
380: * to the platform's default character encoding, and these bytes are
381: * written in exactly the manner of the <code>{@link #write(int)}</code>
382: * method.
383: *
384: * @param i The <code>int</code> to be printed
385: */
386: public void print(int i) throws IOException {
387: write(String.valueOf(i));
388: }
389:
390: /**
391: * Print a long integer. The string produced by <code>{@link
392: * java.lang.String#valueOf(long)}</code> is translated into bytes
393: * according to the platform's default character encoding, and these bytes
394: * are written in exactly the manner of the <code>{@link #write(int)}</code>
395: * method.
396: *
397: * @param l The <code>long</code> to be printed
398: */
399: public void print(long l) throws IOException {
400: write(String.valueOf(l));
401: }
402:
403: /**
404: * Print a floating-point number. The string produced by <code>{@link
405: * java.lang.String#valueOf(float)}</code> is translated into bytes
406: * according to the platform's default character encoding, and these bytes
407: * are written in exactly the manner of the <code>{@link #write(int)}</code>
408: * method.
409: *
410: * @param f The <code>float</code> to be printed
411: */
412: public void print(float f) throws IOException {
413: write(String.valueOf(f));
414: }
415:
416: /**
417: * Print a double-precision floating-point number. The string produced by
418: * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
419: * bytes according to the platform's default character encoding, and these
420: * bytes are written in exactly the manner of the <code>{@link
421: * #write(int)}</code> method.
422: *
423: * @param d The <code>double</code> to be printed
424: */
425: public void print(double d) throws IOException {
426: write(String.valueOf(d));
427: }
428:
429: /**
430: * Print an array of characters. The characters are converted into bytes
431: * according to the platform's default character encoding, and these bytes
432: * are written in exactly the manner of the <code>{@link #write(int)}</code>
433: * method.
434: *
435: * @param s The array of chars to be printed
436: *
437: * @throws NullPointerException If <code>s</code> is <code>null</code>
438: */
439: public void print(char s[]) throws IOException {
440: write(s);
441: }
442:
443: /**
444: * Print a string. If the argument is <code>null</code> then the string
445: * <code>"null"</code> is printed. Otherwise, the string's characters are
446: * converted into bytes according to the platform's default character
447: * encoding, and these bytes are written in exactly the manner of the
448: * <code>{@link #write(int)}</code> method.
449: *
450: * @param s The <code>String</code> to be printed
451: */
452: public void print(String s) throws IOException {
453: if (s == null) {
454: s = "null";
455: }
456: write(s);
457: }
458:
459: /**
460: * Print an object. The string produced by the <code>{@link
461: * java.lang.String#valueOf(Object)}</code> method is translated into bytes
462: * according to the platform's default character encoding, and these bytes
463: * are written in exactly the manner of the <code>{@link #write(int)}</code>
464: * method.
465: *
466: * @param obj The <code>Object</code> to be printed
467: */
468: public void print(Object obj) throws IOException {
469: write(String.valueOf(obj));
470: }
471:
472: /* Methods that do terminate lines */
473:
474: /**
475: * Terminate the current line by writing the line separator string. The
476: * line separator string is defined by the system property
477: * <code>line.separator</code>, and is not necessarily a single newline
478: * character (<code>'\n'</code>).
479: *
480: * Need to change this from PrintWriter because the default
481: * println() writes to the sink directly instead of through the
482: * write method...
483: */
484: public void println() throws IOException {
485: newLine();
486: }
487:
488: /**
489: * Print a boolean value and then terminate the line. This method behaves
490: * as though it invokes <code>{@link #print(boolean)}</code> and then
491: * <code>{@link #println()}</code>.
492: */
493: public void println(boolean x) throws IOException {
494: print(x);
495: println();
496: }
497:
498: /**
499: * Print a character and then terminate the line. This method behaves as
500: * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
501: * #println()}</code>.
502: */
503: public void println(char x) throws IOException {
504: print(x);
505: println();
506: }
507:
508: /**
509: * Print an integer and then terminate the line. This method behaves as
510: * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
511: * #println()}</code>.
512: */
513: public void println(int x) throws IOException {
514: print(x);
515: println();
516: }
517:
518: /**
519: * Print a long integer and then terminate the line. This method behaves
520: * as though it invokes <code>{@link #print(long)}</code> and then
521: * <code>{@link #println()}</code>.
522: */
523: public void println(long x) throws IOException {
524: print(x);
525: println();
526: }
527:
528: /**
529: * Print a floating-point number and then terminate the line. This method
530: * behaves as though it invokes <code>{@link #print(float)}</code> and then
531: * <code>{@link #println()}</code>.
532: */
533: public void println(float x) throws IOException {
534: print(x);
535: println();
536: }
537:
538: /**
539: * Print a double-precision floating-point number and then terminate the
540: * line. This method behaves as though it invokes <code>{@link
541: * #print(double)}</code> and then <code>{@link #println()}</code>.
542: */
543: public void println(double x) throws IOException {
544: print(x);
545: println();
546: }
547:
548: /**
549: * Print an array of characters and then terminate the line. This method
550: * behaves as though it invokes <code>{@link #print(char[])}</code> and then
551: * <code>{@link #println()}</code>.
552: */
553: public void println(char x[]) throws IOException {
554: print(x);
555: println();
556: }
557:
558: /**
559: * Print a String and then terminate the line. This method behaves as
560: * though it invokes <code>{@link #print(String)}</code> and then
561: * <code>{@link #println()}</code>.
562: */
563: public void println(String x) throws IOException {
564: print(x);
565: println();
566: }
567:
568: /**
569: * Print an Object and then terminate the line. This method behaves as
570: * though it invokes <code>{@link #print(Object)}</code> and then
571: * <code>{@link #println()}</code>.
572: */
573: public void println(Object x) throws IOException {
574: print(x);
575: println();
576: }
577:
578: }
|