001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1999 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: *
054: */
055:
056: package com.sun.portal.providers.jsp.jasper3.jasper.runtime;
057:
058: import java.io.IOException;
059: import java.io.Writer;
060:
061: import javax.servlet.ServletResponse;
062: import javax.servlet.jsp.JspWriter;
063:
064: import com.sun.portal.providers.jsp.jasper3.jasper.Constants;
065:
066: /**
067: * Write text to a character-output stream, buffering characters so as
068: * to provide for the efficient writing of single characters, arrays,
069: * and strings.
070: *
071: * Provide support for discarding for the output that has been
072: * buffered.
073: *
074: * This needs revisiting when the buffering problems in the JSP spec
075: * are fixed -akv
076: *
077: * @author Anil K. Vijendran
078: */
079: public class JspWriterImpl extends JspWriter {
080:
081: protected Writer out;
082:
083: protected ServletResponse response;
084:
085: protected char cb[];
086: protected int nextChar;
087:
088: protected static int defaultCharBufferSize = Constants.DEFAULT_BUFFER_SIZE;
089:
090: protected boolean flushed = false;
091:
092: public JspWriterImpl() {
093: super (defaultCharBufferSize, true);
094: }
095:
096: /**
097: * Create a buffered character-output stream that uses a default-sized
098: * output buffer.
099: *
100: * @param response A Servlet Response
101: */
102: public JspWriterImpl(ServletResponse response) {
103: this (response, defaultCharBufferSize, true);
104: }
105:
106: /**
107: * Create a new buffered character-output stream that uses an output
108: * buffer of the given size.
109: *
110: * @param response A Servlet Response
111: * @param sz Output-buffer size, a positive integer
112: *
113: * @exception IllegalArgumentException If sz is <= 0
114: */
115: public JspWriterImpl(ServletResponse response, int sz,
116: boolean autoFlush) {
117: super (sz, autoFlush);
118: if (sz < 0)
119: throw new IllegalArgumentException("Buffer size <= 0");
120: this .response = response;
121: cb = sz == 0 ? null : new char[sz];
122: nextChar = 0;
123:
124: /* The default buffersize of the underlying stream is 8k.
125: To make sure the stream is not flushed before it needs to be,
126: we need to set the buffersize to the requested size
127: */
128: setBufferSize(sz);
129: }
130:
131: void init(ServletResponse response, int sz, boolean autoFlush) {
132: this .response = response;
133: if (sz > 0 && (cb == null || sz > cb.length))
134: cb = new char[sz];
135: nextChar = 0;
136: this .autoFlush = autoFlush;
137: this .bufferSize = sz;
138:
139: /* The default buffersize of the underlying stream is 8k.
140: To make sure the stream is not flushed before it needs to be,
141: we need to set the buffersize to the requested size
142: */
143: setBufferSize(sz);
144: }
145:
146: /**
147: * Set the underlying stream's buffer size to the requested
148: * size (From the page directive).
149: */
150: private void setBufferSize(int sz) {
151: if (sz > 0 && !response.isCommitted()) {
152: try {
153: response.setBufferSize(sz);
154: } catch (IllegalStateException ignore) {
155: /* If the control comes here, then the response is
156: already started (ie., some content is already written
157: to the response but not committed). Since, this
158: function is only called during JspWriterImpl's init
159: OR constructor; this call is necessarily due to a
160: FORWARDed request.
161:
162: For a forwarded request, as per spec, the response is
163: committed (necessarily flushed) before the bufstream
164: is popped.
165: Hence, it doesn't matter if the buffersize is not
166: set to the requested size.
167: */
168: }
169: }
170: }
171:
172: /**
173: * Flush the output buffer to the underlying character stream, without
174: * flushing the stream itself. This method is non-private only so that it
175: * may be invoked by PrintStream.
176: */
177: public final void flushBuffer() throws IOException {
178: synchronized (lock) {
179: if (bufferSize == 0)
180: return;
181: flushed = true;
182: ensureOpen();
183: if (nextChar == 0)
184: return;
185: initOut();
186: out.write(cb, 0, nextChar);
187: nextChar = 0;
188: }
189: }
190:
191: protected void initOut() throws IOException {
192: if (out == null) {
193: out = response.getWriter();
194: //System.out.println("JspWriterImpl: initOut: " + this + " " +out);
195: }
196: }
197:
198: /**
199: * Discard the output buffer.
200: */
201: public final void clear() throws IOException {
202: synchronized (lock) {
203: if (bufferSize == 0)
204: throw new IllegalStateException(Constants
205: .getString("jsp.error.ise_on_clear"));
206: if (flushed)
207: throw new IOException(
208: Constants
209: .getString("jsp.error.attempt_to_clear_flushed_buffer"));
210: ensureOpen();
211: nextChar = 0;
212: }
213: }
214:
215: public void clearBuffer() throws IOException {
216: synchronized (lock) {
217: if (bufferSize == 0)
218: throw new IllegalStateException(Constants
219: .getString("jsp.error.ise_on_clear"));
220: ensureOpen();
221: nextChar = 0;
222: }
223: }
224:
225: private final void bufferOverflow() throws IOException {
226: throw new IOException(Constants.getString("jsp.error.overflow"));
227: }
228:
229: /**
230: * Flush the stream.
231: *
232: */
233: public void flush() throws IOException {
234: synchronized (lock) {
235: flushBuffer();
236: if (out != null) {
237: out.flush();
238: // Also flush the response buffer.
239: response.flushBuffer();
240: }
241: }
242: }
243:
244: /**
245: * Close the stream.
246: *
247: */
248: public void close() throws IOException {
249: synchronized (lock) {
250: if (response == null)
251: return;
252: flush();
253: if (out != null)
254: out.close();
255: out = null;
256: // cb = null;
257: }
258: }
259:
260: /**
261: * @return the number of bytes unused in the buffer
262: */
263: public int getRemaining() {
264: return bufferSize - nextChar;
265: }
266:
267: /** check to make sure that the stream has not been closed */
268: protected void ensureOpen() throws IOException {
269: if (response == null)
270: throw new IOException("Stream closed");
271: }
272:
273: /**
274: * Write a single character.
275: *
276: */
277: public void write(int c) throws IOException {
278: synchronized (lock) {
279: ensureOpen();
280: if (bufferSize == 0) {
281: initOut();
282: out.write(c);
283: } else {
284: if (nextChar >= bufferSize)
285: if (autoFlush)
286: flushBuffer();
287: else
288: bufferOverflow();
289: cb[nextChar++] = (char) c;
290: }
291: }
292: }
293:
294: /**
295: * Our own little min method, to avoid loading java.lang.Math if we've run
296: * out of file descriptors and we're trying to print a stack trace.
297: */
298: private int min(int a, int b) {
299: if (a < b)
300: return a;
301: return b;
302: }
303:
304: /**
305: * Write a portion of an array of characters.
306: *
307: * <p> Ordinarily this method stores characters from the given array into
308: * this stream's buffer, flushing the buffer to the underlying stream as
309: * needed. If the requested length is at least as large as the buffer,
310: * however, then this method will flush the buffer and write the characters
311: * directly to the underlying stream. Thus redundant
312: * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
313: *
314: * @param cbuf A character array
315: * @param off Offset from which to start reading characters
316: * @param len Number of characters to write
317: *
318: */
319: public void write(char cbuf[], int off, int len) throws IOException {
320: synchronized (lock) {
321: ensureOpen();
322:
323: if (bufferSize == 0) {
324: initOut();
325: out.write(cbuf, off, len);
326: return;
327: }
328:
329: if ((off < 0) || (off > cbuf.length) || (len < 0)
330: || ((off + len) > cbuf.length) || ((off + len) < 0)) {
331: throw new IndexOutOfBoundsException();
332: } else if (len == 0) {
333: return;
334: }
335:
336: if (len >= bufferSize) {
337: /* If the request length exceeds the size of the output buffer,
338: flush the buffer and then write the data directly. In this
339: way buffered streams will cascade harmlessly. */
340: if (autoFlush)
341: flushBuffer();
342: else
343: bufferOverflow();
344: initOut();
345: out.write(cbuf, off, len);
346: return;
347: }
348:
349: int b = off, t = off + len;
350: while (b < t) {
351: int d = min(bufferSize - nextChar, t - b);
352: System.arraycopy(cbuf, b, cb, nextChar, d);
353: b += d;
354: nextChar += d;
355: if (nextChar >= bufferSize)
356: if (autoFlush)
357: flushBuffer();
358: else
359: bufferOverflow();
360: }
361: }
362: }
363:
364: /**
365: * Write an array of characters. This method cannot be inherited from the
366: * Writer class because it must suppress I/O exceptions.
367: */
368: public void write(char buf[]) throws IOException {
369: write(buf, 0, buf.length);
370: }
371:
372: /**
373: * Write a portion of a String.
374: *
375: * @param s String to be written
376: * @param off Offset from which to start reading characters
377: * @param len Number of characters to be written
378: *
379: */
380: public void write(String s, int off, int len) throws IOException {
381: synchronized (lock) {
382: ensureOpen();
383: if (bufferSize == 0) {
384: initOut();
385: out.write(s, off, len);
386: return;
387: }
388: int b = off, t = off + len;
389: while (b < t) {
390: int d = min(bufferSize - nextChar, t - b);
391: s.getChars(b, b + d, cb, nextChar);
392: b += d;
393: nextChar += d;
394: if (nextChar >= bufferSize)
395: if (autoFlush)
396: flushBuffer();
397: else
398: bufferOverflow();
399: }
400: }
401: }
402:
403: /**
404: * Write a string. This method cannot be inherited from the Writer class
405: * because it must suppress I/O exceptions.
406: */
407: public void write(String s) throws IOException {
408: write(s, 0, s.length());
409: }
410:
411: static String lineSeparator = System.getProperty("line.separator");
412:
413: /**
414: * Write a line separator. The line separator string is defined by the
415: * system property <tt>line.separator</tt>, and is not necessarily a single
416: * newline ('\n') character.
417: *
418: * @exception IOException If an I/O error occurs
419: */
420:
421: public void newLine() throws IOException {
422: synchronized (lock) {
423: write(lineSeparator);
424: }
425: }
426:
427: /* Methods that do not terminate lines */
428:
429: /**
430: * Print a boolean value. The string produced by <code>{@link
431: * java.lang.String#valueOf(boolean)}</code> is translated into bytes
432: * according to the platform's default character encoding, and these bytes
433: * are written in exactly the manner of the <code>{@link
434: * #write(int)}</code> method.
435: *
436: * @param b The <code>boolean</code> to be printed
437: */
438: public void print(boolean b) throws IOException {
439: write(b ? "true" : "false");
440: }
441:
442: /**
443: * Print a character. The character is translated into one or more bytes
444: * according to the platform's default character encoding, and these bytes
445: * are written in exactly the manner of the <code>{@link
446: * #write(int)}</code> method.
447: *
448: * @param c The <code>char</code> to be printed
449: */
450: public void print(char c) throws IOException {
451: write(String.valueOf(c));
452: }
453:
454: /**
455: * Print an integer. The string produced by <code>{@link
456: * java.lang.String#valueOf(int)}</code> is translated into bytes according
457: * to the platform's default character encoding, and these bytes are
458: * written in exactly the manner of the <code>{@link #write(int)}</code>
459: * method.
460: *
461: * @param i The <code>int</code> to be printed
462: * @see java.lang.Integer#toString(int)
463: */
464: public void print(int i) throws IOException {
465: write(String.valueOf(i));
466: }
467:
468: /**
469: * Print a long integer. The string produced by <code>{@link
470: * java.lang.String#valueOf(long)}</code> is translated into bytes
471: * according to the platform's default character encoding, and these bytes
472: * are written in exactly the manner of the <code>{@link #write(int)}</code>
473: * method.
474: *
475: * @param l The <code>long</code> to be printed
476: * @see java.lang.Long#toString(long)
477: */
478: public void print(long l) throws IOException {
479: write(String.valueOf(l));
480: }
481:
482: /**
483: * Print a floating-point number. The string produced by <code>{@link
484: * java.lang.String#valueOf(float)}</code> is translated into bytes
485: * according to the platform's default character encoding, and these bytes
486: * are written in exactly the manner of the <code>{@link #write(int)}</code>
487: * method.
488: *
489: * @param f The <code>float</code> to be printed
490: * @see java.lang.Float#toString(float)
491: */
492: public void print(float f) throws IOException {
493: write(String.valueOf(f));
494: }
495:
496: /**
497: * Print a double-precision floating-point number. The string produced by
498: * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
499: * bytes according to the platform's default character encoding, and these
500: * bytes are written in exactly the manner of the <code>{@link
501: * #write(int)}</code> method.
502: *
503: * @param d The <code>double</code> to be printed
504: * @see java.lang.Double#toString(double)
505: */
506: public void print(double d) throws IOException {
507: write(String.valueOf(d));
508: }
509:
510: /**
511: * Print an array of characters. The characters are converted into bytes
512: * according to the platform's default character encoding, and these bytes
513: * are written in exactly the manner of the <code>{@link #write(int)}</code>
514: * method.
515: *
516: * @param s The array of chars to be printed
517: *
518: * @throws NullPointerException If <code>s</code> is <code>null</code>
519: */
520: public void print(char s[]) throws IOException {
521: write(s);
522: }
523:
524: /**
525: * Print a string. If the argument is <code>null</code> then the string
526: * <code>"null"</code> is printed. Otherwise, the string's characters are
527: * converted into bytes according to the platform's default character
528: * encoding, and these bytes are written in exactly the manner of the
529: * <code>{@link #write(int)}</code> method.
530: *
531: * @param s The <code>String</code> to be printed
532: */
533: public void print(String s) throws IOException {
534: if (s == null) {
535: s = "null";
536: }
537: write(s);
538: }
539:
540: /**
541: * Print an object. The string produced by the <code>{@link
542: * java.lang.String#valueOf(Object)}</code> method is translated into bytes
543: * according to the platform's default character encoding, and these bytes
544: * are written in exactly the manner of the <code>{@link #write(int)}</code>
545: * method.
546: *
547: * @param obj The <code>Object</code> to be printed
548: * @see java.lang.Object#toString()
549: */
550: public void print(Object obj) throws IOException {
551: write(String.valueOf(obj));
552: }
553:
554: /* Methods that do terminate lines */
555:
556: /**
557: * Terminate the current line by writing the line separator string. The
558: * line separator string is defined by the system property
559: * <code>line.separator</code>, and is not necessarily a single newline
560: * character (<code>'\n'</code>).
561: *
562: * Need to change this from PrintWriter because the default
563: * println() writes to the sink directly instead of through the
564: * write method...
565: */
566: public void println() throws IOException {
567: newLine();
568: }
569:
570: /**
571: * Print a boolean value and then terminate the line. This method behaves
572: * as though it invokes <code>{@link #print(boolean)}</code> and then
573: * <code>{@link #println()}</code>.
574: */
575: public void println(boolean x) throws IOException {
576: synchronized (lock) {
577: print(x);
578: println();
579: }
580: }
581:
582: /**
583: * Print a character and then terminate the line. This method behaves as
584: * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
585: * #println()}</code>.
586: */
587: public void println(char x) throws IOException {
588: synchronized (lock) {
589: print(x);
590: println();
591: }
592: }
593:
594: /**
595: * Print an integer and then terminate the line. This method behaves as
596: * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
597: * #println()}</code>.
598: */
599: public void println(int x) throws IOException {
600: synchronized (lock) {
601: print(x);
602: println();
603: }
604: }
605:
606: /**
607: * Print a long integer and then terminate the line. This method behaves
608: * as though it invokes <code>{@link #print(long)}</code> and then
609: * <code>{@link #println()}</code>.
610: */
611: public void println(long x) throws IOException {
612: synchronized (lock) {
613: print(x);
614: println();
615: }
616: }
617:
618: /**
619: * Print a floating-point number and then terminate the line. This method
620: * behaves as though it invokes <code>{@link #print(float)}</code> and then
621: * <code>{@link #println()}</code>.
622: */
623: public void println(float x) throws IOException {
624: synchronized (lock) {
625: print(x);
626: println();
627: }
628: }
629:
630: /**
631: * Print a double-precision floating-point number and then terminate the
632: * line. This method behaves as though it invokes <code>{@link
633: * #print(double)}</code> and then <code>{@link #println()}</code>.
634: */
635: public void println(double x) throws IOException {
636: synchronized (lock) {
637: print(x);
638: println();
639: }
640: }
641:
642: /**
643: * Print an array of characters and then terminate the line. This method
644: * behaves as though it invokes <code>{@link #print(char[])}</code> and then
645: * <code>{@link #println()}</code>.
646: */
647: public void println(char x[]) throws IOException {
648: synchronized (lock) {
649: print(x);
650: println();
651: }
652: }
653:
654: /**
655: * Print a String and then terminate the line. This method behaves as
656: * though it invokes <code>{@link #print(String)}</code> and then
657: * <code>{@link #println()}</code>.
658: */
659: public void println(String x) throws IOException {
660: synchronized (lock) {
661: print(x);
662: println();
663: }
664: }
665:
666: /**
667: * Print an Object and then terminate the line. This method behaves as
668: * though it invokes <code>{@link #print(Object)}</code> and then
669: * <code>{@link #println()}</code>.
670: */
671: public void println(Object x) throws IOException {
672: synchronized (lock) {
673: print(x);
674: println();
675: }
676: }
677:
678: /** Package-level access
679: */
680: void recycle() {
681: flushed = false;
682: nextChar = 0;
683: out = null;
684: }
685: }
|