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.icesoft.faces.webapp.parser;
057:
058: import com.icesoft.jasper.Constants;
059:
060: import javax.servlet.jsp.JspWriter;
061: import javax.servlet.jsp.tagext.BodyContent;
062: import java.io.CharArrayReader;
063: import java.io.IOException;
064: import java.io.Reader;
065: import java.io.Writer;
066:
067: /**
068: * Write text to a character-output stream, buffering characters so as to
069: * provide for the efficient writing of single characters, arrays, and strings.
070: * <p/>
071: * Provide support for discarding for the output that has been buffered.
072: *
073: * @author Rajiv Mordani
074: * @author Jan Luehe
075: */
076: public class BodyContentImpl extends BodyContent {
077:
078: private static final String LINE_SEPARATOR = System
079: .getProperty("line.separator");
080:
081: private char[] cb;
082: private int nextChar;
083: private boolean closed;
084:
085: // Enclosed writer to which any output is written
086: private Writer writer;
087:
088: // See comment in setWriter()
089: private int bufferSizeSave;
090:
091: /**
092: * Constructor.
093: */
094: public BodyContentImpl(JspWriter enclosingWriter) {
095: super (enclosingWriter);
096: bufferSize = Constants.DEFAULT_TAG_BUFFER_SIZE;
097: cb = new char[bufferSize];
098: nextChar = 0;
099: closed = false;
100: }
101:
102: /**
103: * Write a single character.
104: */
105: public void write(int c) throws IOException {
106: if (writer != null) {
107: writer.write(c);
108: } else {
109: ensureOpen();
110: if (nextChar >= bufferSize) {
111: reAllocBuff(1);
112: }
113: cb[nextChar++] = (char) c;
114: }
115: }
116:
117: /**
118: * Write a portion of an array of characters.
119: * <p/>
120: * <p> Ordinarily this method stores characters from the given array into
121: * this stream's buffer, flushing the buffer to the underlying stream as
122: * needed. If the requested length is at least as large as the buffer,
123: * however, then this method will flush the buffer and write the characters
124: * directly to the underlying stream. Thus redundant
125: * <code>DiscardableBufferedWriter</code>s will not copy data
126: * unnecessarily.
127: *
128: * @param cbuf A character array
129: * @param off Offset from which to start reading characters
130: * @param len Number of characters to write
131: */
132: public void write(char[] cbuf, int off, int len) throws IOException {
133: if (writer != null) {
134: writer.write(cbuf, off, len);
135: } else {
136: ensureOpen();
137:
138: if ((off < 0) || (off > cbuf.length) || (len < 0)
139: || ((off + len) > cbuf.length) || ((off + len) < 0)) {
140: throw new IndexOutOfBoundsException();
141: } else if (len == 0) {
142: return;
143: }
144:
145: if (len >= bufferSize - nextChar)
146: reAllocBuff(len);
147:
148: System.arraycopy(cbuf, off, cb, nextChar, len);
149: nextChar += len;
150: }
151: }
152:
153: /**
154: * Write an array of characters. This method cannot be inherited from the
155: * Writer class because it must suppress I/O exceptions.
156: */
157: public void write(char[] buf) throws IOException {
158: if (writer != null) {
159: writer.write(buf);
160: } else {
161: write(buf, 0, buf.length);
162: }
163: }
164:
165: /**
166: * Write a portion of a String.
167: *
168: * @param s String to be written
169: * @param off Offset from which to start reading characters
170: * @param len Number of characters to be written
171: */
172: public void write(String s, int off, int len) throws IOException {
173: if (writer != null) {
174: writer.write(s, off, len);
175: } else {
176: ensureOpen();
177: if (len >= bufferSize - nextChar)
178: reAllocBuff(len);
179:
180: s.getChars(off, off + len, cb, nextChar);
181: nextChar += len;
182: }
183: }
184:
185: /**
186: * Write a string. This method cannot be inherited from the Writer class
187: * because it must suppress I/O exceptions.
188: */
189: public void write(String s) throws IOException {
190: if (writer != null) {
191: writer.write(s);
192: } else {
193: write(s, 0, s.length());
194: }
195: }
196:
197: /**
198: * Write a line separator. The line separator string is defined by the
199: * system property <tt>line.separator</tt>, and is not necessarily a single
200: * newline ('\n') character.
201: *
202: * @throws IOException If an I/O error occurs
203: */
204: public void newLine() throws IOException {
205: if (writer != null) {
206: writer.write(LINE_SEPARATOR);
207: } else {
208: write(LINE_SEPARATOR);
209: }
210: }
211:
212: /**
213: * Print a boolean value. The string produced by <code>{@link
214: * java.lang.String#valueOf(boolean)}</code> is translated into bytes
215: * according to the platform's default character encoding, and these bytes
216: * are written in exactly the manner of the <code>{@link #write(int)}</code>
217: * method.
218: *
219: * @param b The <code>boolean</code> to be printed
220: * @throws IOException
221: */
222: public void print(boolean b) throws IOException {
223: if (writer != null) {
224: writer.write(b ? "true" : "false");
225: } else {
226: write(b ? "true" : "false");
227: }
228: }
229:
230: /**
231: * Print a character. The character is translated into one or more bytes
232: * according to the platform's default character encoding, and these bytes
233: * are written in exactly the manner of the <code>{@link #write(int)}</code>
234: * method.
235: *
236: * @param c The <code>char</code> to be printed
237: * @throws IOException
238: */
239: public void print(char c) throws IOException {
240: if (writer != null) {
241: writer.write(String.valueOf(c));
242: } else {
243: write(String.valueOf(c));
244: }
245: }
246:
247: /**
248: * Print an integer. The string produced by <code>{@link
249: * java.lang.String#valueOf(int)}</code> is translated into bytes according
250: * to the platform's default character encoding, and these bytes are written
251: * in exactly the manner of the <code>{@link #write(int)}</code> method.
252: *
253: * @param i The <code>int</code> to be printed
254: * @throws IOException
255: */
256: public void print(int i) throws IOException {
257: if (writer != null) {
258: writer.write(String.valueOf(i));
259: } else {
260: write(String.valueOf(i));
261: }
262: }
263:
264: /**
265: * Print a long integer. The string produced by <code>{@link
266: * java.lang.String#valueOf(long)}</code> is translated into bytes according
267: * to the platform's default character encoding, and these bytes are written
268: * in exactly the manner of the <code>{@link #write(int)}</code> method.
269: *
270: * @param l The <code>long</code> to be printed
271: * @throws IOException
272: */
273: public void print(long l) throws IOException {
274: if (writer != null) {
275: writer.write(String.valueOf(l));
276: } else {
277: write(String.valueOf(l));
278: }
279: }
280:
281: /**
282: * Print a floating-point number. The string produced by <code>{@link
283: * java.lang.String#valueOf(float)}</code> is translated 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 f The <code>float</code> to be printed
289: * @throws IOException
290: */
291: public void print(float f) throws IOException {
292: if (writer != null) {
293: writer.write(String.valueOf(f));
294: } else {
295: write(String.valueOf(f));
296: }
297: }
298:
299: /**
300: * Print a double-precision floating-point number. The string produced by
301: * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
302: * bytes according to the platform's default character encoding, and these
303: * bytes are written in exactly the manner of the <code>{@link
304: * #write(int)}</code> method.
305: *
306: * @param d The <code>double</code> to be printed
307: * @throws IOException
308: */
309: public void print(double d) throws IOException {
310: if (writer != null) {
311: writer.write(String.valueOf(d));
312: } else {
313: write(String.valueOf(d));
314: }
315: }
316:
317: /**
318: * Print an array of characters. The characters are converted into bytes
319: * according to the platform's default character encoding, and these bytes
320: * are written in exactly the manner of the <code>{@link #write(int)}</code>
321: * method.
322: *
323: * @param s The array of chars to be printed
324: * @throws NullPointerException If <code>s</code> is <code>null</code>
325: * @throws IOException
326: */
327: public void print(char[] s) throws IOException {
328: if (writer != null) {
329: writer.write(s);
330: } else {
331: write(s);
332: }
333: }
334:
335: /**
336: * Print a string. If the argument is <code>null</code> then the string
337: * <code>"null"</code> is printed. Otherwise, the string's characters are
338: * converted into bytes according to the platform's default character
339: * encoding, and these bytes are written in exactly the manner of the
340: * <code>{@link #write(int)}</code> method.
341: *
342: * @param s The <code>String</code> to be printed
343: * @throws IOException
344: */
345: public void print(String s) throws IOException {
346: if (s == null)
347: s = "null";
348: if (writer != null) {
349: writer.write(s);
350: } else {
351: write(s);
352: }
353: }
354:
355: /**
356: * Print an object. The string produced by the <code>{@link
357: * java.lang.String#valueOf(Object)}</code> method is translated into bytes
358: * according to the platform's default character encoding, and these bytes
359: * are written in exactly the manner of the <code>{@link #write(int)}</code>
360: * method.
361: *
362: * @param obj The <code>Object</code> to be printed
363: * @throws IOException
364: */
365: public void print(Object obj) throws IOException {
366: if (writer != null) {
367: writer.write(String.valueOf(obj));
368: } else {
369: write(String.valueOf(obj));
370: }
371: }
372:
373: /**
374: * Terminate the current line by writing the line separator string. The
375: * line separator string is defined by the system property
376: * <code>line.separator</code>, and is not necessarily a single newline
377: * character (<code>'\n'</code>).
378: *
379: * @throws IOException
380: */
381: public void println() throws IOException {
382: newLine();
383: }
384:
385: /**
386: * Print a boolean value and then terminate the line. This method behaves
387: * as though it invokes <code>{@link #print(boolean)}</code> and then
388: * <code>{@link #println()}</code>.
389: *
390: * @throws IOException
391: */
392: public void println(boolean x) throws IOException {
393: print(x);
394: println();
395: }
396:
397: /**
398: * Print a character and then terminate the line. This method behaves as
399: * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
400: * #println()}</code>.
401: *
402: * @throws IOException
403: */
404: public void println(char x) throws IOException {
405: print(x);
406: println();
407: }
408:
409: /**
410: * Print an integer and then terminate the line. This method behaves as
411: * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
412: * #println()}</code>.
413: *
414: * @throws IOException
415: */
416: public void println(int x) throws IOException {
417: print(x);
418: println();
419: }
420:
421: /**
422: * Print a long integer and then terminate the line. This method behaves as
423: * though it invokes <code>{@link #print(long)}</code> and then <code>{@link
424: * #println()}</code>.
425: *
426: * @throws IOException
427: */
428: public void println(long x) throws IOException {
429: print(x);
430: println();
431: }
432:
433: /**
434: * Print a floating-point number and then terminate the line. This method
435: * behaves as though it invokes <code>{@link #print(float)}</code> and then
436: * <code>{@link #println()}</code>.
437: *
438: * @throws IOException
439: */
440: public void println(float x) throws IOException {
441: print(x);
442: println();
443: }
444:
445: /**
446: * Print a double-precision floating-point number and then terminate the
447: * line. This method behaves as though it invokes <code>{@link
448: * #print(double)}</code> and then <code>{@link #println()}</code>.
449: *
450: * @throws IOException
451: */
452: public void println(double x) throws IOException {
453: print(x);
454: println();
455: }
456:
457: /**
458: * Print an array of characters and then terminate the line. This method
459: * behaves as though it invokes <code>{@link #print(char[])}</code> and then
460: * <code>{@link #println()}</code>.
461: *
462: * @throws IOException
463: */
464: public void println(char x[]) throws IOException {
465: print(x);
466: println();
467: }
468:
469: /**
470: * Print a String and then terminate the line. This method behaves as
471: * though it invokes <code>{@link #print(String)}</code> and then
472: * <code>{@link #println()}</code>.
473: *
474: * @throws IOException
475: */
476: public void println(String x) throws IOException {
477: print(x);
478: println();
479: }
480:
481: /**
482: * Print an Object and then terminate the line. This method behaves as
483: * though it invokes <code>{@link #print(Object)}</code> and then
484: * <code>{@link #println()}</code>.
485: *
486: * @throws IOException
487: */
488: public void println(Object x) throws IOException {
489: print(x);
490: println();
491: }
492:
493: /**
494: * Clear the contents of the buffer. If the buffer has been already been
495: * flushed then the clear operation shall throw an IOException to signal the
496: * fact that some data has already been irrevocably written to the client
497: * response stream.
498: *
499: * @throws IOException If an I/O error occurs
500: */
501: public void clear() throws IOException {
502: if (writer != null) {
503: throw new IOException();
504: } else {
505: nextChar = 0;
506: }
507: }
508:
509: /**
510: * Clears the current contents of the buffer. Unlike clear(), this mehtod
511: * will not throw an IOException if the buffer has already been flushed. It
512: * merely clears the current content of the buffer and returns.
513: *
514: * @throws IOException If an I/O error occurs
515: */
516: public void clearBuffer() throws IOException {
517: if (writer == null) {
518: this .clear();
519: }
520: }
521:
522: /**
523: * Close the stream, flushing it first. Once a stream has been closed,
524: * further write() or flush() invocations will cause an IOException to be
525: * thrown. Closing a previously-closed stream, however, has no effect.
526: *
527: * @throws IOException If an I/O error occurs
528: */
529: public void close() throws IOException {
530: if (writer != null) {
531: writer.close();
532: } else {
533: cb = null;
534: closed = true;
535: }
536: }
537:
538: /**
539: * @return the number of bytes unused in the buffer
540: */
541: public int getRemaining() {
542: return (writer == null) ? bufferSize - nextChar : 0;
543: }
544:
545: /**
546: * Return the value of this BodyJspWriter as a Reader. Note: this is after
547: * evaluation!! There are no scriptlets, etc in this stream.
548: *
549: * @return the value of this BodyJspWriter as a Reader
550: */
551: public Reader getReader() {
552: return (writer == null) ? new CharArrayReader(cb, 0, nextChar)
553: : null;
554: }
555:
556: /**
557: * Return the value of the BodyJspWriter as a String. Note: this is after
558: * evaluation!! There are no scriptlets, etc in this stream.
559: *
560: * @return the value of the BodyJspWriter as a String
561: */
562: public String getString() {
563: return (writer == null) ? new String(cb, 0, nextChar) : null;
564: }
565:
566: /**
567: * Write the contents of this BodyJspWriter into a Writer. Subclasses are
568: * likely to do interesting things with the implementation so some things
569: * are extra efficient.
570: *
571: * @param out The writer into which to place the contents of this body
572: * evaluation
573: */
574: public void writeOut(Writer out) throws IOException {
575: if (writer == null) {
576: out.write(cb, 0, nextChar);
577: // Flush not called as the writer passed could be a BodyContent and
578: // it doesn't allow to flush.
579: }
580: }
581:
582: /**
583: * Sets the writer to which all output is written.
584: */
585: void setWriter(Writer writer) {
586: this .writer = writer;
587: if (writer != null) {
588: // According to the spec, the JspWriter returned by
589: // JspContext.pushBody(java.io.Writer writer) must behave as
590: // though it were unbuffered. This means that its getBufferSize()
591: // must always return 0. The implementation of
592: // JspWriter.getBufferSize() returns the value of JspWriter's
593: // 'bufferSize' field, which is inherited by this class.
594: // Therefore, we simply save the current 'bufferSize' (so we can
595: // later restore it should this BodyContentImpl ever be reused by
596: // a call to PageContext.pushBody()) before setting it to 0.
597: if (bufferSize != 0) {
598: bufferSizeSave = bufferSize;
599: bufferSize = 0;
600: }
601: } else {
602: bufferSize = bufferSizeSave;
603: clearBody();
604: }
605: }
606:
607: private void ensureOpen() throws IOException {
608: if (closed)
609: throw new IOException("Stream closed");
610: }
611:
612: /**
613: * Reallocates buffer since the spec requires it to be unbounded.
614: */
615: private void reAllocBuff(int len) {
616:
617: if (bufferSize + len <= cb.length) {
618: bufferSize = cb.length;
619: return;
620: }
621:
622: if (len < cb.length) {
623: len = cb.length;
624: }
625:
626: bufferSize = cb.length + len;
627: char[] tmp = new char[bufferSize];
628:
629: System.arraycopy(cb, 0, tmp, 0, cb.length);
630: cb = tmp;
631: tmp = null;
632:
633: }
634:
635: }
|