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