1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.jasper.runtime;
19
20 import java.io.CharArrayReader;
21 import java.io.IOException;
22 import java.io.Reader;
23 import java.io.Writer;
24
25 import javax.servlet.jsp.JspWriter;
26 import javax.servlet.jsp.tagext.BodyContent;
27
28 import org.apache.jasper.Constants;
29
30 /**
31 * Write text to a character-output stream, buffering characters so as
32 * to provide for the efficient writing of single characters, arrays,
33 * and strings.
34 *
35 * Provide support for discarding for the output that has been buffered.
36 *
37 * @author Rajiv Mordani
38 * @author Jan Luehe
39 */
40 public class BodyContentImpl extends BodyContent {
41
42 private static final String LINE_SEPARATOR =
43 System.getProperty("line.separator");
44 private static final boolean LIMIT_BUFFER =
45 Boolean.valueOf(System.getProperty("org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER", "false")).booleanValue();
46
47 private char[] cb;
48 private int nextChar;
49 private boolean closed;
50
51 // Enclosed writer to which any output is written
52 private Writer writer;
53
54 // See comment in setWriter()
55 private int bufferSizeSave;
56
57 /**
58 * Constructor.
59 */
60 public BodyContentImpl(JspWriter enclosingWriter) {
61 super(enclosingWriter);
62 bufferSize = Constants.DEFAULT_TAG_BUFFER_SIZE;
63 cb = new char[bufferSize];
64 nextChar = 0;
65 closed = false;
66 }
67
68 /**
69 * Write a single character.
70 */
71 public void write(int c) throws IOException {
72 if (writer != null) {
73 writer.write(c);
74 } else {
75 ensureOpen();
76 if (nextChar >= bufferSize) {
77 reAllocBuff (1);
78 }
79 cb[nextChar++] = (char) c;
80 }
81 }
82
83 /**
84 * Write a portion of an array of characters.
85 *
86 * <p> Ordinarily this method stores characters from the given array into
87 * this stream's buffer, flushing the buffer to the underlying stream as
88 * needed. If the requested length is at least as large as the buffer,
89 * however, then this method will flush the buffer and write the characters
90 * directly to the underlying stream. Thus redundant
91 * <code>DiscardableBufferedWriter</code>s will not copy data
92 * unnecessarily.
93 *
94 * @param cbuf A character array
95 * @param off Offset from which to start reading characters
96 * @param len Number of characters to write
97 */
98 public void write(char[] cbuf, int off, int len) throws IOException {
99 if (writer != null) {
100 writer.write(cbuf, off, len);
101 } else {
102 ensureOpen();
103
104 if ((off < 0) || (off > cbuf.length) || (len < 0) ||
105 ((off + len) > cbuf.length) || ((off + len) < 0)) {
106 throw new IndexOutOfBoundsException();
107 } else if (len == 0) {
108 return;
109 }
110
111 if (len >= bufferSize - nextChar)
112 reAllocBuff (len);
113
114 System.arraycopy(cbuf, off, cb, nextChar, len);
115 nextChar+=len;
116 }
117 }
118
119 /**
120 * Write an array of characters. This method cannot be inherited from the
121 * Writer class because it must suppress I/O exceptions.
122 */
123 public void write(char[] buf) throws IOException {
124 if (writer != null) {
125 writer.write(buf);
126 } else {
127 write(buf, 0, buf.length);
128 }
129 }
130
131 /**
132 * Write a portion of a String.
133 *
134 * @param s String to be written
135 * @param off Offset from which to start reading characters
136 * @param len Number of characters to be written
137 */
138 public void write(String s, int off, int len) throws IOException {
139 if (writer != null) {
140 writer.write(s, off, len);
141 } else {
142 ensureOpen();
143 if (len >= bufferSize - nextChar)
144 reAllocBuff(len);
145
146 s.getChars(off, off + len, cb, nextChar);
147 nextChar += len;
148 }
149 }
150
151 /**
152 * Write a string. This method cannot be inherited from the Writer class
153 * because it must suppress I/O exceptions.
154 */
155 public void write(String s) throws IOException {
156 if (writer != null) {
157 writer.write(s);
158 } else {
159 write(s, 0, s.length());
160 }
161 }
162
163 /**
164 * Write a line separator. The line separator string is defined by the
165 * system property <tt>line.separator</tt>, and is not necessarily a single
166 * newline ('\n') character.
167 *
168 * @throws IOException If an I/O error occurs
169 */
170 public void newLine() throws IOException {
171 if (writer != null) {
172 writer.write(LINE_SEPARATOR);
173 } else {
174 write(LINE_SEPARATOR);
175 }
176 }
177
178 /**
179 * Print a boolean value. The string produced by <code>{@link
180 * java.lang.String#valueOf(boolean)}</code> is translated into bytes
181 * according to the platform's default character encoding, and these bytes
182 * are written in exactly the manner of the <code>{@link
183 * #write(int)}</code> method.
184 *
185 * @param b The <code>boolean</code> to be printed
186 * @throws IOException
187 */
188 public void print(boolean b) throws IOException {
189 if (writer != null) {
190 writer.write(b ? "true" : "false");
191 } else {
192 write(b ? "true" : "false");
193 }
194 }
195
196 /**
197 * Print a character. The character is translated into one or more bytes
198 * according to the platform's default character encoding, and these bytes
199 * are written in exactly the manner of the <code>{@link
200 * #write(int)}</code> method.
201 *
202 * @param c The <code>char</code> to be printed
203 * @throws IOException
204 */
205 public void print(char c) throws IOException {
206 if (writer != null) {
207 writer.write(String.valueOf(c));
208 } else {
209 write(String.valueOf(c));
210 }
211 }
212
213 /**
214 * Print an integer. The string produced by <code>{@link
215 * java.lang.String#valueOf(int)}</code> is translated into bytes according
216 * to the platform's default character encoding, and these bytes are
217 * written in exactly the manner of the <code>{@link #write(int)}</code>
218 * method.
219 *
220 * @param i The <code>int</code> to be printed
221 * @throws IOException
222 */
223 public void print(int i) throws IOException {
224 if (writer != null) {
225 writer.write(String.valueOf(i));
226 } else {
227 write(String.valueOf(i));
228 }
229 }
230
231 /**
232 * Print a long integer. The string produced by <code>{@link
233 * java.lang.String#valueOf(long)}</code> is translated into bytes
234 * according to the platform's default character encoding, and these bytes
235 * are written in exactly the manner of the
236 * <code>{@link #write(int)}</code> method.
237 *
238 * @param l The <code>long</code> to be printed
239 * @throws IOException
240 */
241 public void print(long l) throws IOException {
242 if (writer != null) {
243 writer.write(String.valueOf(l));
244 } else {
245 write(String.valueOf(l));
246 }
247 }
248
249 /**
250 * Print a floating-point number. The string produced by <code>{@link
251 * java.lang.String#valueOf(float)}</code> is translated into bytes
252 * according to the platform's default character encoding, and these bytes
253 * are written in exactly the manner of the
254 * <code>{@link #write(int)}</code> method.
255 *
256 * @param f The <code>float</code> to be printed
257 * @throws IOException
258 */
259 public void print(float f) throws IOException {
260 if (writer != null) {
261 writer.write(String.valueOf(f));
262 } else {
263 write(String.valueOf(f));
264 }
265 }
266
267 /**
268 * Print a double-precision floating-point number. The string produced by
269 * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
270 * bytes according to the platform's default character encoding, and these
271 * bytes are written in exactly the manner of the <code>{@link
272 * #write(int)}</code> method.
273 *
274 * @param d The <code>double</code> to be printed
275 * @throws IOException
276 */
277 public void print(double d) throws IOException {
278 if (writer != null) {
279 writer.write(String.valueOf(d));
280 } else {
281 write(String.valueOf(d));
282 }
283 }
284
285 /**
286 * Print an array of characters. The characters are converted into bytes
287 * according to the platform's default character encoding, and these bytes
288 * are written in exactly the manner of the
289 * <code>{@link #write(int)}</code> method.
290 *
291 * @param s The array of chars to be printed
292 *
293 * @throws NullPointerException If <code>s</code> is <code>null</code>
294 * @throws IOException
295 */
296 public void print(char[] s) throws IOException {
297 if (writer != null) {
298 writer.write(s);
299 } else {
300 write(s);
301 }
302 }
303
304 /**
305 * Print a string. If the argument is <code>null</code> then the string
306 * <code>"null"</code> is printed. Otherwise, the string's characters are
307 * converted into bytes according to the platform's default character
308 * encoding, and these bytes are written in exactly the manner of the
309 * <code>{@link #write(int)}</code> method.
310 *
311 * @param s The <code>String</code> to be printed
312 * @throws IOException
313 */
314 public void print(String s) throws IOException {
315 if (s == null) s = "null";
316 if (writer != null) {
317 writer.write(s);
318 } else {
319 write(s);
320 }
321 }
322
323 /**
324 * Print an object. The string produced by the <code>{@link
325 * java.lang.String#valueOf(Object)}</code> method is translated into bytes
326 * according to the platform's default character encoding, and these bytes
327 * are written in exactly the manner of the
328 * <code>{@link #write(int)}</code> method.
329 *
330 * @param obj The <code>Object</code> to be printed
331 * @throws IOException
332 */
333 public void print(Object obj) throws IOException {
334 if (writer != null) {
335 writer.write(String.valueOf(obj));
336 } else {
337 write(String.valueOf(obj));
338 }
339 }
340
341 /**
342 * Terminate the current line by writing the line separator string. The
343 * line separator string is defined by the system property
344 * <code>line.separator</code>, and is not necessarily a single newline
345 * character (<code>'\n'</code>).
346 *
347 * @throws IOException
348 */
349 public void println() throws IOException {
350 newLine();
351 }
352
353 /**
354 * Print a boolean value and then terminate the line. This method behaves
355 * as though it invokes <code>{@link #print(boolean)}</code> and then
356 * <code>{@link #println()}</code>.
357 *
358 * @throws IOException
359 */
360 public void println(boolean x) throws IOException {
361 print(x);
362 println();
363 }
364
365 /**
366 * Print a character and then terminate the line. This method behaves as
367 * though it invokes <code>{@link #print(char)}</code> and then
368 * <code>{@link #println()}</code>.
369 *
370 * @throws IOException
371 */
372 public void println(char x) throws IOException {
373 print(x);
374 println();
375 }
376
377 /**
378 * Print an integer and then terminate the line. This method behaves as
379 * though it invokes <code>{@link #print(int)}</code> and then
380 * <code>{@link #println()}</code>.
381 *
382 * @throws IOException
383 */
384 public void println(int x) throws IOException {
385 print(x);
386 println();
387 }
388
389 /**
390 * Print a long integer and then terminate the line. This method behaves
391 * as though it invokes <code>{@link #print(long)}</code> and then
392 * <code>{@link #println()}</code>.
393 *
394 * @throws IOException
395 */
396 public void println(long x) throws IOException {
397 print(x);
398 println();
399 }
400
401 /**
402 * Print a floating-point number and then terminate the line. This method
403 * behaves as though it invokes <code>{@link #print(float)}</code> and then
404 * <code>{@link #println()}</code>.
405 *
406 * @throws IOException
407 */
408 public void println(float x) throws IOException {
409 print(x);
410 println();
411 }
412
413 /**
414 * Print a double-precision floating-point number and then terminate the
415 * line. This method behaves as though it invokes <code>{@link
416 * #print(double)}</code> and then <code>{@link #println()}</code>.
417 *
418 * @throws IOException
419 */
420 public void println(double x) throws IOException{
421 print(x);
422 println();
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
428 * then <code>{@link #println()}</code>.
429 *
430 * @throws IOException
431 */
432 public void println(char x[]) throws IOException {
433 print(x);
434 println();
435 }
436
437 /**
438 * Print a String and then terminate the line. This method behaves as
439 * though it invokes <code>{@link #print(String)}</code> and then
440 * <code>{@link #println()}</code>.
441 *
442 * @throws IOException
443 */
444 public void println(String x) throws IOException {
445 print(x);
446 println();
447 }
448
449 /**
450 * Print an Object and then terminate the line. This method behaves as
451 * though it invokes <code>{@link #print(Object)}</code> and then
452 * <code>{@link #println()}</code>.
453 *
454 * @throws IOException
455 */
456 public void println(Object x) throws IOException {
457 print(x);
458 println();
459 }
460
461 /**
462 * Clear the contents of the buffer. If the buffer has been already
463 * been flushed then the clear operation shall throw an IOException
464 * to signal the fact that some data has already been irrevocably
465 * written to the client response stream.
466 *
467 * @throws IOException If an I/O error occurs
468 */
469 public void clear() throws IOException {
470 if (writer != null) {
471 throw new IOException();
472 } else {
473 nextChar = 0;
474 if (LIMIT_BUFFER && (cb.length > Constants.DEFAULT_TAG_BUFFER_SIZE)) {
475 bufferSize = Constants.DEFAULT_TAG_BUFFER_SIZE;
476 cb = new char[bufferSize];
477 }
478 }
479 }
480
481 /**
482 * Clears the current contents of the buffer. Unlike clear(), this
483 * mehtod will not throw an IOException if the buffer has already been
484 * flushed. It merely clears the current content of the buffer and
485 * returns.
486 *
487 * @throws IOException If an I/O error occurs
488 */
489 public void clearBuffer() throws IOException {
490 if (writer == null) {
491 this.clear();
492 }
493 }
494
495 /**
496 * Close the stream, flushing it first. Once a stream has been closed,
497 * further write() or flush() invocations will cause an IOException to be
498 * thrown. Closing a previously-closed stream, however, has no effect.
499 *
500 * @throws IOException If an I/O error occurs
501 */
502 public void close() throws IOException {
503 if (writer != null) {
504 writer.close();
505 } else {
506 closed = true;
507 }
508 }
509
510 /**
511 * @return the number of bytes unused in the buffer
512 */
513 public int getRemaining() {
514 return (writer == null) ? bufferSize-nextChar : 0;
515 }
516
517 /**
518 * Return the value of this BodyJspWriter as a Reader.
519 * Note: this is after evaluation!! There are no scriptlets,
520 * etc in this stream.
521 *
522 * @return the value of this BodyJspWriter as a Reader
523 */
524 public Reader getReader() {
525 return (writer == null) ? new CharArrayReader (cb, 0, nextChar) : null;
526 }
527
528 /**
529 * Return the value of the BodyJspWriter as a String.
530 * Note: this is after evaluation!! There are no scriptlets,
531 * etc in this stream.
532 *
533 * @return the value of the BodyJspWriter as a String
534 */
535 public String getString() {
536 return (writer == null) ? new String(cb, 0, nextChar) : null;
537 }
538
539 /**
540 * Write the contents of this BodyJspWriter into a Writer.
541 * Subclasses are likely to do interesting things with the
542 * implementation so some things are extra efficient.
543 *
544 * @param out The writer into which to place the contents of this body
545 * evaluation
546 */
547 public void writeOut(Writer out) throws IOException {
548 if (writer == null) {
549 out.write(cb, 0, nextChar);
550 // Flush not called as the writer passed could be a BodyContent and
551 // it doesn't allow to flush.
552 }
553 }
554
555 /**
556 * Sets the writer to which all output is written.
557 */
558 void setWriter(Writer writer) {
559 this.writer = writer;
560 closed = false;
561 if (writer != null) {
562 // According to the spec, the JspWriter returned by
563 // JspContext.pushBody(java.io.Writer writer) must behave as
564 // though it were unbuffered. This means that its getBufferSize()
565 // must always return 0. The implementation of
566 // JspWriter.getBufferSize() returns the value of JspWriter's
567 // 'bufferSize' field, which is inherited by this class.
568 // Therefore, we simply save the current 'bufferSize' (so we can
569 // later restore it should this BodyContentImpl ever be reused by
570 // a call to PageContext.pushBody()) before setting it to 0.
571 if (bufferSize != 0) {
572 bufferSizeSave = bufferSize;
573 bufferSize = 0;
574 }
575 } else {
576 bufferSize = bufferSizeSave;
577 clearBody();
578 }
579 }
580
581 private void ensureOpen() throws IOException {
582 if (closed) throw new IOException("Stream closed");
583 }
584
585 /**
586 * Reallocates buffer since the spec requires it to be unbounded.
587 */
588 private void reAllocBuff(int len) {
589
590 if (bufferSize + len <= cb.length) {
591 bufferSize = cb.length;
592 return;
593 }
594
595 if (len < cb.length) {
596 len = cb.length;
597 }
598
599 bufferSize = cb.length + len;
600 char[] tmp = new char[bufferSize];
601
602 System.arraycopy(cb, 0, tmp, 0, cb.length);
603 cb = tmp;
604 tmp = null;
605
606 }
607
608
609 }