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