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/BodyContentImpl.java $
078: //$Author: Dan $
079: //$Revision: 20 $
080: //$Modtime: 2/20/03 1:31p $
081: /////////////////////////
082: import java.io.IOException;
083: import java.io.Writer;
084: import java.io.Reader;
085: import java.io.CharArrayReader;
086:
087: import javax.servlet.jsp.JspWriter;
088: import javax.servlet.jsp.tagext.BodyContent;
089: import java.util.*;
090:
091: import com.salmonllc.jsp.*;
092:
093: public class BodyContentImpl extends BodyContent {
094:
095: private static Stack _unused = new Stack();
096: private static BodyContentImpl _first, _last;
097:
098: private char[] _cb;
099: protected int _bufferSize = 8192;
100: private int _nextChar;
101: static String _lineSeparator = System.getProperty("line.separator");
102: private BodyContentImpl _next, _prior;
103: private JspWriter _enclosingWriter = null;
104: private boolean _noConv = false;
105: private boolean _dreamWeaverConv = false;
106: private String _prefix, _tagName, _origTag;
107: private boolean _printContent = true;
108:
109: public BodyContentImpl(JspWriter writer) {
110: super (writer);
111: _enclosingWriter = writer;
112: _cb = new char[_bufferSize];
113: _printContent = false;
114: _nextChar = 0;
115: bufferSize = _bufferSize;
116: }
117:
118: public BodyContentImpl(JspWriter writer, boolean printContent) {
119: this (writer);
120: _printContent = printContent;
121: }
122:
123: /**
124: * Clear the contents of the buffer. If the buffer has been already
125: * been flushed then the clear operation shall throw an IOException
126: * to signal the fact that some data has already been irrevocably
127: * written to the client response stream.
128: *
129: * @throws IOException If an I/O error occurs
130: */
131:
132: public void clear() throws IOException {
133: synchronized (lock) {
134: _nextChar = 0;
135: _dreamWeaverConv = false;
136: }
137: }
138:
139: public void cleaBody() throws IOException {
140: this .clear();
141: }
142:
143: /**
144: * Clears the current contents of the buffer. Unlike clear(), this
145: * mehtod will not throw an IOException if the buffer has already been
146: * flushed. It merely clears the current content of the buffer and
147: * returns.
148: *
149: * @throws IOException If an I/O error occurs
150: */
151:
152: public void clearBuffer() throws IOException {
153: this .clear();
154: }
155:
156: /**
157: * This method clears the flag that tells the bodycontent object whether or not to convert to DreamWeaver
158: */
159: public void clearDreamWeaverConv() {
160: _dreamWeaverConv = false;
161: }
162:
163: /**
164: * Close the stream, flushing it first. Once a stream has been closed,
165: * further write() or flush() invocations will cause an IOException to be
166: * thrown. Closing a previously-closed stream, however, has no effect.
167: *
168: * @exception IOException If an I/O error occurs
169: */
170:
171: public void close() throws IOException {
172: synchronized (lock) {
173: _cb = null;
174: }
175: }
176:
177: /**
178: * Free's a body content object back to the body content pool
179: */
180: public static synchronized void freeBodyContent(BodyContentImpl o) {
181: BodyContentImpl next = o.getNext();
182: BodyContentImpl prior = o.getPrior();
183:
184: if (next != null)
185: next.setPrior(prior);
186:
187: if (prior != null)
188: prior.setNext(next);
189:
190: if (o == _last)
191: _last = prior;
192:
193: if (o == _first)
194: _first = next;
195:
196: o.setNext(null);
197: o.setPrior(null);
198:
199: _unused.push(o);
200: }
201:
202: /**
203: * Returns a body content object from the pool.
204: */
205: public static synchronized BodyContentImpl getBodyContent(
206: JspWriter w, boolean printContent) {
207: BodyContentImpl ret = null;
208:
209: if (_unused.isEmpty()) {
210: ret = new BodyContentImpl(w, printContent);
211: } else {
212: ret = (BodyContentImpl) _unused.pop();
213: ret.setEnclosingWriter(w);
214: ret.reset();
215: ret.setPrintContent(printContent);
216: }
217:
218: if (_first == null)
219: _first = ret;
220: if (_last != null)
221: _last.setNext(ret);
222:
223: ret.setPrior(_last);
224: ret.setNext(null);
225: _last = ret;
226:
227: return ret;
228: }
229:
230: /**
231: * Returns the parent JSPWriter of this body content
232: */
233: public JspWriter getEnclosingWriter() {
234: return _enclosingWriter;
235: }
236:
237: public BodyContentImpl getNext() {
238: return _next;
239: }
240:
241: /**
242: * Count the number of used and unused body content object in the pool
243: */
244: public static final int getPoolCount() {
245: return getPoolUnusedCount() + getPoolUsedCount();
246: }
247:
248: /**
249: * Count the number of unused body content object in the pool
250: */
251: public static final int getPoolUnusedCount() {
252: return _unused.size();
253: }
254:
255: /**
256: * Count the number of used body content object in the pool
257: */
258: public static final int getPoolUsedCount() {
259: int count = 0;
260: if (_first != null) {
261: count++;
262: BodyContentImpl next = _first.getNext();
263: while (next != null) {
264: count++;
265: next = next.getNext();
266: }
267: }
268: return count;
269: }
270:
271: public BodyContentImpl getPrior() {
272: return _prior;
273: }
274:
275: /**
276: * Return the value of this BodyJspWriter as a Reader.
277: * Note: this is after evaluation!! There are no scriptlets,
278: * etc in this stream.
279: *
280: * @return the value of this BodyJspWriter as a Reader
281: */
282: public Reader getReader() {
283: return new CharArrayReader(_cb, 0, _nextChar);
284: }
285:
286: /**
287: * @return the number of bytes unused in the buffer
288: */
289:
290: public int getRemaining() {
291: return bufferSize - _nextChar;
292: }
293:
294: /**
295: * Return the value of the BodyJspWriter as a String.
296: * Note: this is after evaluation!! There are no scriptlets,
297: * etc in this stream.
298: *
299: * @return the value of the BodyJspWriter as a String
300: */
301: public String getString() {
302: if (_dreamWeaverConv && !_noConv) {
303: _noConv = false;
304: return TagWriter.dreamWeaverConv(_prefix, _tagName,
305: _origTag, new String(_cb, 0, _nextChar));
306: } else {
307: _noConv = false;
308: return new String(_cb, 0, _nextChar);
309: }
310: }
311:
312: /**
313: * Write a line separator. The line separator string is defined by the
314: * system property <tt>line.separator</tt>, and is not necessarily a single
315: * newline ('\n') character.
316: *
317: * @exception IOException If an I/O error occurs
318: */
319:
320: public void newLine() throws IOException {
321: synchronized (lock) {
322: write(_lineSeparator);
323: }
324: }
325:
326: /**
327: * Print an array of characters. The characters are converted into bytes
328: * according to the platform's default character encoding, and these bytes
329: * are written in exactly the manner of the <code>{@link #write(int)}</code>
330: * method.
331: *
332: * @param s The array of chars to be printed
333: *
334: * @throws NullPointerException If <code>s</code> is <code>null</code>
335: * @throws java.io.IOException
336: */
337:
338: public void print(char s[]) throws IOException {
339: write(s);
340: }
341:
342: /**
343: * Print a character. The character is translated into one or more bytes
344: * according to the platform's default character encoding, and these bytes
345: * are written in exactly the manner of the <code>{@link
346: * #write(int)}</code> method.
347: *
348: * @param c The <code>char</code> to be printed
349: * @throws java.io.IOException
350: */
351:
352: public void print(char c) throws IOException {
353: write(String.valueOf(c));
354: }
355:
356: /**
357: * Print a double-precision floating-point number. The string produced by
358: * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
359: * bytes according to the platform's default character encoding, and these
360: * bytes are written in exactly the manner of the <code>{@link
361: * #write(int)}</code> method.
362: *
363: * @param d The <code>double</code> to be printed
364: * @see java.lang.Double#toString(double)
365: * @throws java.io.IOException
366: */
367:
368: public void print(double d) throws IOException {
369: write(String.valueOf(d));
370: }
371:
372: /**
373: * Print a floating-point number. The string produced by <code>{@link
374: * java.lang.String#valueOf(float)}</code> is translated into bytes
375: * according to the platform's default character encoding, and these bytes
376: * are written in exactly the manner of the <code>{@link #write(int)}</code>
377: * method.
378: *
379: * @param f The <code>float</code> to be printed
380: * @see java.lang.Float#toString(float)
381: * @throws java.io.IOException
382: */
383:
384: public void print(float f) throws IOException {
385: write(String.valueOf(f));
386: }
387:
388: /**
389: * Print an integer. The string produced by <code>{@link
390: * java.lang.String#valueOf(int)}</code> is translated into bytes according
391: * to the platform's default character encoding, and these bytes are
392: * written in exactly the manner of the <code>{@link #write(int)}</code>
393: * method.
394: *
395: * @param i The <code>int</code> to be printed
396: * @see java.lang.Integer#toString(int)
397: * @throws java.io.IOException
398: */
399:
400: public void print(int i) throws IOException {
401: write(String.valueOf(i));
402: }
403:
404: /**
405: * Print a long integer. The string produced by <code>{@link
406: * java.lang.String#valueOf(long)}</code> is translated into bytes
407: * according to the platform's default character encoding, and these bytes
408: * are written in exactly the manner of the <code>{@link #write(int)}</code>
409: * method.
410: *
411: * @param l The <code>long</code> to be printed
412: * @see java.lang.Long#toString(long)
413: * @throws java.io.IOException
414: */
415:
416: public void print(long l) throws IOException {
417: write(String.valueOf(l));
418: }
419:
420: /**
421: * Print an object. The string produced by the <code>{@link
422: * java.lang.String#valueOf(Object)}</code> method is translated into bytes
423: * according to the platform's default character encoding, and these bytes
424: * are written in exactly the manner of the <code>{@link #write(int)}</code>
425: * method.
426: *
427: * @param obj The <code>Object</code> to be printed
428: * @see java.lang.Object#toString()
429: * @throws java.io.IOException
430: */
431:
432: public void print(Object obj) throws IOException {
433: write(String.valueOf(obj));
434: }
435:
436: /**
437: * Print a string. If the argument is <code>null</code> then the string
438: * <code>"null"</code> is printed. Otherwise, the string's characters are
439: * converted into bytes according to the platform's default character
440: * encoding, and these bytes are written in exactly the manner of the
441: * <code>{@link #write(int)}</code> method.
442: *
443: * @param s The <code>String</code> to be printed
444: * @throws java.io.IOException
445: */
446:
447: public void print(String s) throws IOException {
448: if (s == null) {
449: s = "null";
450: }
451: write(s);
452: }
453:
454: /**
455: * Print a boolean value. The string produced by <code>{@link
456: * java.lang.String#valueOf(boolean)}</code> is translated into bytes
457: * according to the platform's default character encoding, and these bytes
458: * are written in exactly the manner of the <code>{@link
459: * #write(int)}</code> method.
460: *
461: * @param b The <code>boolean</code> to be printed
462: * @throws java.io.IOException
463: */
464:
465: public void print(boolean b) throws IOException {
466: write(b ? "true" : "false");
467: }
468:
469: /**
470: * Terminate the current line by writing the line separator string. The
471: * line separator string is defined by the system property
472: * <code>line.separator</code>, and is not necessarily a single newline
473: * character (<code>'\n'</code>).
474: * @throws java.io.IOException
475: */
476:
477: public void println() throws IOException {
478: newLine();
479: }
480:
481: /**
482: * Print an array of characters and then terminate the line. This method
483: * behaves as though it invokes <code>{@link #print(char[])}</code> and then
484: * <code>{@link #println()}</code>.
485: * @throws java.io.IOException
486: */
487:
488: public void println(char x[]) throws IOException {
489: synchronized (lock) {
490: print(x);
491: println();
492: }
493: }
494:
495: /**
496: * Print a character and then terminate the line. This method behaves as
497: * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
498: * #println()}</code>.
499: * @throws java.io.IOException
500: */
501:
502: public void println(char x) throws IOException {
503: synchronized (lock) {
504: print(x);
505: println();
506: }
507: }
508:
509: /**
510: * Print a double-precision floating-point number and then terminate the
511: * line. This method behaves as though it invokes <code>{@link
512: * #print(double)}</code> and then <code>{@link #println()}</code>.
513: * @throws java.io.IOException
514: */
515:
516: public void println(double x) throws IOException {
517: synchronized (lock) {
518: print(x);
519: println();
520: }
521: }
522:
523: /**
524: * Print a floating-point number and then terminate the line. This method
525: * behaves as though it invokes <code>{@link #print(float)}</code> and then
526: * <code>{@link #println()}</code>.
527: * @throws java.io.IOException
528: */
529:
530: public void println(float x) throws IOException {
531: synchronized (lock) {
532: print(x);
533: println();
534: }
535: }
536:
537: /**
538: * Print an integer and then terminate the line. This method behaves as
539: * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
540: * #println()}</code>.
541: * @throws java.io.IOException
542: */
543:
544: public void println(int x) throws IOException {
545: synchronized (lock) {
546: print(x);
547: println();
548: }
549: }
550:
551: /**
552: * Print a long integer and then terminate the line. This method behaves
553: * as though it invokes <code>{@link #print(long)}</code> and then
554: * <code>{@link #println()}</code>.
555: * @throws java.io.IOException
556: */
557:
558: public void println(long x) throws IOException {
559: synchronized (lock) {
560: print(x);
561: println();
562: }
563: }
564:
565: /**
566: * Print an Object and then terminate the line. This method behaves as
567: * though it invokes <code>{@link #print(Object)}</code> and then
568: * <code>{@link #println()}</code>.
569: * @throws java.io.IOException
570: */
571:
572: public void println(Object x) throws IOException {
573: synchronized (lock) {
574: print(x);
575: println();
576: }
577: }
578:
579: /**
580: * Print a String and then terminate the line. This method behaves as
581: * though it invokes <code>{@link #print(String)}</code> and then
582: * <code>{@link #println()}</code>.
583: * @throws java.io.IOException
584: */
585:
586: public void println(String x) throws IOException {
587: synchronized (lock) {
588: print(x);
589: println();
590: }
591: }
592:
593: /**
594: * Print a boolean value and then terminate the line. This method behaves
595: * as though it invokes <code>{@link #print(boolean)}</code> and then
596: * <code>{@link #println()}</code>.
597: * @throws java.io.IOException
598: */
599:
600: public void println(boolean x) throws IOException {
601: synchronized (lock) {
602: print(x);
603: println();
604: }
605: }
606:
607: /**
608: * Print a string. If the argument is <code>null</code> then the string
609: * <code>"null"</code> is printed. Otherwise, the string's characters are
610: * converted into bytes according to the platform's default character
611: * encoding, and these bytes are written in exactly the manner of the
612: * <code>{@link #write(int)}</code> method.
613: *
614: * @param s The <code>String</code> to be printed
615: * @throws java.io.IOException
616: */
617:
618: public void printNoConv(String s) throws IOException {
619: _noConv = true;
620: if (s == null) {
621: s = "null";
622: }
623: write(s);
624: }
625:
626: private void reAllocBuff(int len) {
627: char[] tmp = null;
628:
629: if (len <= bufferSize) {
630: bufferSize *= 2;
631: } else {
632: bufferSize += len;
633: }
634: tmp = new char[bufferSize];
635: System.arraycopy(_cb, 0, tmp, 0, _cb.length);
636: _cb = tmp;
637: tmp = null;
638: }
639:
640: private void reset() {
641: _nextChar = 0;
642: _noConv = false;
643: _dreamWeaverConv = false;
644: _prefix = null;
645: _tagName = null;
646: _origTag = null;
647: _printContent = true;
648: }
649:
650: public void setDreamWeaverConv(String prefix, String tag,
651: String origTag) {
652: _dreamWeaverConv = true;
653: _prefix = prefix;
654: _tagName = tag;
655: _origTag = origTag;
656: }
657:
658: /**
659: * Sets the parent JSP Writer for this body content
660: */
661: public void setEnclosingWriter(JspWriter w) {
662: _enclosingWriter = w;
663: }
664:
665: public void setNext(BodyContentImpl next) {
666: _next = next;
667: }
668:
669: public void setOrigTag(String origTag) {
670: _origTag = origTag;
671: getString();
672: }
673:
674: /**
675: * Sets whether or not this body content will print it's output or discard it
676: */
677: public void setPrintContent(boolean printContent) {
678: _printContent = printContent;
679: }
680:
681: public void setPrior(BodyContentImpl prior) {
682: _prior = prior;
683: }
684:
685: /**
686: * Returns the string representation of the contents of the class
687: */
688: public String toString() {
689: return getString();
690: }
691:
692: /**
693: * Write an array of characters. This method cannot be inherited from the
694: * Writer class because it must suppress I/O exceptions.
695: */
696: public void write(char buf[]) throws IOException {
697: write(buf, 0, buf.length);
698: }
699:
700: /**
701: * Write a portion of an array of characters.
702: *
703: * <p> Ordinarily this method stores characters from the given array into
704: * this stream's buffer, flushing the buffer to the underlying stream as
705: * needed. If the requested length is at least as large as the buffer,
706: * however, then this method will flush the buffer and write the characters
707: * directly to the underlying stream. Thus redundant
708: * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
709: *
710: * @param cbuf A character array
711: * @param off Offset from which to start reading characters
712: * @param len Number of characters to write
713: *
714: */
715: public void write(char cbuf[], int off, int len) throws IOException {
716: synchronized (lock) {
717: if (!_printContent)
718: return;
719:
720: if ((off < 0) || (off > cbuf.length) || (len < 0)
721: || ((off + len) > cbuf.length) || ((off + len) < 0)) {
722: throw new IndexOutOfBoundsException();
723: } else if (len == 0) {
724: return;
725: }
726:
727: if (len >= bufferSize - _nextChar)
728: reAllocBuff(len);
729:
730: System.arraycopy(cbuf, off, _cb, _nextChar, len);
731: _nextChar += len;
732: }
733: }
734:
735: /**
736: * Write a single character.
737: *
738: */
739: public void write(int c) throws IOException {
740: synchronized (lock) {
741: if (!_printContent)
742: return;
743:
744: if (_nextChar >= bufferSize) {
745: reAllocBuff(0);
746: }
747: _cb[_nextChar++] = (char) c;
748: }
749: }
750:
751: /**
752: * Write a string. This method cannot be inherited from the Writer class
753: * because it must suppress I/O exceptions.
754: */
755: public void write(String s) throws IOException {
756: write(s, 0, s.length());
757: }
758:
759: /**
760: * Write a portion of a String.
761: *
762: * @param s String to be written
763: * @param off Offset from which to start reading characters
764: * @param len Number of characters to be written
765: *
766: */
767: public void write(String s, int off, int len) throws IOException {
768: synchronized (lock) {
769: if (!_printContent)
770: return;
771:
772: if (len >= bufferSize - _nextChar)
773: reAllocBuff(len);
774:
775: s.getChars(off, off + len, _cb, _nextChar);
776: _nextChar += len;
777: }
778: }
779:
780: /**
781: * Write the contents of this BodyJspWriter into a Writer.
782: * Subclasses are likely to do interesting things with the
783: * implementation so some things are extra efficient.
784: *
785: * @param out The writer into which to place the contents of
786: * this body evaluation
787: */
788: public void writeOut(Writer out) throws IOException {
789: if (!_printContent)
790: return;
791:
792: if (_dreamWeaverConv && !_noConv)
793: out.write(TagWriter.dreamWeaverConv(_prefix, _tagName,
794: _origTag, new String(_cb, 0, _nextChar)));
795: else
796: out.write(_cb, 0, _nextChar);
797:
798: _noConv = false;
799: // Flush not called as the writer passed could be a BodyContent and
800: // it doesn't allow to flush.
801: }
802: }
|