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