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 import org.apache.struts2.jasper.compiler.Localizer;
22 import org.apache.struts2.jasper.security.SecurityUtil;
23
24 import javax.servlet.ServletResponse;
25 import javax.servlet.jsp.JspWriter;
26 import java.io.IOException;
27 import java.io.Writer;
28 import java.security.AccessController;
29 import java.security.PrivilegedAction;
30
31 /***
32 * Write text to a character-output stream, buffering characters so as
33 * to provide for the efficient writing of single characters, arrays,
34 * and strings.
35 * <p/>
36 * Provide support for discarding for the output that has been
37 * buffered.
38 * <p/>
39 * This needs revisiting when the buffering problems in the JSP spec
40 * are fixed -akv
41 *
42 * @author Anil K. Vijendran
43 */
44 public class JspWriterImpl extends JspWriter {
45
46 private Writer out;
47 private ServletResponse response;
48 private char cb[];
49 private int nextChar;
50 private boolean flushed = false;
51 private boolean closed = false;
52
53 public JspWriterImpl() {
54 super(Constants.DEFAULT_BUFFER_SIZE, true);
55 }
56
57 /***
58 * Create a buffered character-output stream that uses a default-sized
59 * output buffer.
60 *
61 * @param response A Servlet Response
62 */
63 public JspWriterImpl(ServletResponse response) {
64 this(response, Constants.DEFAULT_BUFFER_SIZE, true);
65 }
66
67 /***
68 * Create a new buffered character-output stream that uses an output
69 * buffer of the given size.
70 *
71 * @param response A Servlet Response
72 * @param sz Output-buffer size, a positive integer
73 * @throws IllegalArgumentException If sz is <= 0
74 */
75 public JspWriterImpl(ServletResponse response, int sz,
76 boolean autoFlush) {
77 super(sz, autoFlush);
78 if (sz < 0)
79 throw new IllegalArgumentException("Buffer size <= 0");
80 this.response = response;
81 cb = sz == 0 ? null : new char[sz];
82 nextChar = 0;
83 }
84
85 void init(ServletResponse response, int sz, boolean autoFlush) {
86 this.response = response;
87 if (sz > 0 && (cb == null || sz > cb.length))
88 cb = new char[sz];
89 nextChar = 0;
90 this.autoFlush = autoFlush;
91 this.bufferSize = sz;
92 }
93
94 /***
95 * Package-level access
96 */
97 void recycle() {
98 flushed = false;
99 closed = false;
100 out = null;
101 nextChar = 0;
102 response = null;
103 }
104
105 /***
106 * Flush the output buffer to the underlying character stream, without
107 * flushing the stream itself. This method is non-private only so that it
108 * may be invoked by PrintStream.
109 */
110 protected final void flushBuffer() throws IOException {
111 if (bufferSize == 0)
112 return;
113 flushed = true;
114 ensureOpen();
115 if (nextChar == 0)
116 return;
117 initOut();
118 out.write(cb, 0, nextChar);
119 nextChar = 0;
120 }
121
122 private void initOut() throws IOException {
123 if (out == null) {
124 out = response.getWriter();
125 }
126 }
127
128 private String getLocalizeMessage(final String message) {
129 if (SecurityUtil.isPackageProtectionEnabled()) {
130 return (String) AccessController.doPrivileged(new PrivilegedAction() {
131 public Object run() {
132 return Localizer.getMessage(message);
133 }
134 });
135 } else {
136 return Localizer.getMessage(message);
137 }
138 }
139
140 /***
141 * Discard the output buffer.
142 */
143 public final void clear() throws IOException {
144 if ((bufferSize == 0) && (out != null))
145
146 throw new IllegalStateException(
147 getLocalizeMessage("jsp.error.ise_on_clear"));
148 if (flushed)
149 throw new IOException(
150 getLocalizeMessage("jsp.error.attempt_to_clear_flushed_buffer"));
151 ensureOpen();
152 nextChar = 0;
153 }
154
155 public void clearBuffer() throws IOException {
156 if (bufferSize == 0)
157 throw new IllegalStateException(
158 getLocalizeMessage("jsp.error.ise_on_clear"));
159 ensureOpen();
160 nextChar = 0;
161 }
162
163 private final void bufferOverflow() throws IOException {
164 throw new IOException(getLocalizeMessage("jsp.error.overflow"));
165 }
166
167 /***
168 * Flush the stream.
169 */
170 public void flush() throws IOException {
171 flushBuffer();
172 if (out != null) {
173 out.flush();
174 }
175 }
176
177 /***
178 * Close the stream.
179 */
180 public void close() throws IOException {
181 if (response == null || closed)
182
183 return;
184 flush();
185 if (out != null)
186 out.close();
187 out = null;
188 closed = true;
189 }
190
191 /***
192 * @return the number of bytes unused in the buffer
193 */
194 public int getRemaining() {
195 return bufferSize - nextChar;
196 }
197
198 /***
199 * check to make sure that the stream has not been closed
200 */
201 private void ensureOpen() throws IOException {
202 if (response == null || closed)
203 throw new IOException("Stream closed");
204 }
205
206
207 /***
208 * Write a single character.
209 */
210 public void write(int c) throws IOException {
211 ensureOpen();
212 if (bufferSize == 0) {
213 initOut();
214 out.write(c);
215 } else {
216 if (nextChar >= bufferSize)
217 if (autoFlush)
218 flushBuffer();
219 else
220 bufferOverflow();
221 cb[nextChar++] = (char) c;
222 }
223 }
224
225 /***
226 * Our own little min method, to avoid loading java.lang.Math if we've run
227 * out of file descriptors and we're trying to print a stack trace.
228 */
229 private int min(int a, int b) {
230 if (a < b) return a;
231 return b;
232 }
233
234 /***
235 * Write a portion of an array of characters.
236 * <p/>
237 * <p> Ordinarily this method stores characters from the given array into
238 * this stream's buffer, flushing the buffer to the underlying stream as
239 * needed. If the requested length is at least as large as the buffer,
240 * however, then this method will flush the buffer and write the characters
241 * directly to the underlying stream. Thus redundant
242 * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
243 *
244 * @param cbuf A character array
245 * @param off Offset from which to start reading characters
246 * @param len Number of characters to write
247 */
248 public void write(char cbuf[], int off, int len)
249 throws IOException {
250 ensureOpen();
251
252 if (bufferSize == 0) {
253 initOut();
254 out.write(cbuf, off, len);
255 return;
256 }
257
258 if ((off < 0) || (off > cbuf.length) || (len < 0) ||
259 ((off + len) > cbuf.length) || ((off + len) < 0)) {
260 throw new IndexOutOfBoundsException();
261 } else if (len == 0) {
262 return;
263 }
264
265 if (len >= bufferSize) {
266
267
268
269 if (autoFlush)
270 flushBuffer();
271 else
272 bufferOverflow();
273 initOut();
274 out.write(cbuf, off, len);
275 return;
276 }
277
278 int b = off, t = off + len;
279 while (b < t) {
280 int d = min(bufferSize - nextChar, t - b);
281 System.arraycopy(cbuf, b, cb, nextChar, d);
282 b += d;
283 nextChar += d;
284 if (nextChar >= bufferSize)
285 if (autoFlush)
286 flushBuffer();
287 else
288 bufferOverflow();
289 }
290
291 }
292
293 /***
294 * Write an array of characters. This method cannot be inherited from the
295 * Writer class because it must suppress I/O exceptions.
296 */
297 public void write(char buf[]) throws IOException {
298 write(buf, 0, buf.length);
299 }
300
301 /***
302 * Write a portion of a String.
303 *
304 * @param s String to be written
305 * @param off Offset from which to start reading characters
306 * @param len Number of characters to be written
307 */
308 public void write(String s, int off, int len) throws IOException {
309 ensureOpen();
310 if (bufferSize == 0) {
311 initOut();
312 out.write(s, off, len);
313 return;
314 }
315 int b = off, t = off + len;
316 while (b < t) {
317 int d = min(bufferSize - nextChar, t - b);
318 s.getChars(b, b + d, cb, nextChar);
319 b += d;
320 nextChar += d;
321 if (nextChar >= bufferSize)
322 if (autoFlush)
323 flushBuffer();
324 else
325 bufferOverflow();
326 }
327 }
328
329 /***
330 * Write a string. This method cannot be inherited from the Writer class
331 * because it must suppress I/O exceptions.
332 */
333 public void write(String s) throws IOException {
334
335
336 if (s == null) {
337 write(s, 0, 0);
338 } else {
339 write(s, 0, s.length());
340 }
341 }
342
343
344 static String lineSeparator = System.getProperty("line.separator");
345
346 /***
347 * Write a line separator. The line separator string is defined by the
348 * system property <tt>line.separator</tt>, and is not necessarily a single
349 * newline ('\n') character.
350 *
351 * @throws IOException If an I/O error occurs
352 */
353
354 public void newLine() throws IOException {
355 write(lineSeparator);
356 }
357
358
359
360
361 /***
362 * Print a boolean value. The string produced by <code>{@link
363 * java.lang.String#valueOf(boolean)}</code> is translated into bytes
364 * according to the platform's default character encoding, and these bytes
365 * are written in exactly the manner of the <code>{@link
366 * #write(int)}</code> method.
367 *
368 * @param b The <code>boolean</code> to be printed
369 */
370 public void print(boolean b) throws IOException {
371 write(b ? "true" : "false");
372 }
373
374 /***
375 * Print a character. The character is translated into one or more bytes
376 * according to the platform's default character encoding, and these bytes
377 * are written in exactly the manner of the <code>{@link
378 * #write(int)}</code> method.
379 *
380 * @param c The <code>char</code> to be printed
381 */
382 public void print(char c) throws IOException {
383 write(String.valueOf(c));
384 }
385
386 /***
387 * Print an integer. The string produced by <code>{@link
388 * java.lang.String#valueOf(int)}</code> is translated into bytes according
389 * to the platform's default character encoding, and these bytes are
390 * written in exactly the manner of the <code>{@link #write(int)}</code>
391 * method.
392 *
393 * @param i The <code>int</code> to be printed
394 */
395 public void print(int i) throws IOException {
396 write(String.valueOf(i));
397 }
398
399 /***
400 * Print a long integer. The string produced by <code>{@link
401 * java.lang.String#valueOf(long)}</code> is translated into bytes
402 * according to the platform's default character encoding, and these bytes
403 * are written in exactly the manner of the <code>{@link #write(int)}</code>
404 * method.
405 *
406 * @param l The <code>long</code> to be printed
407 */
408 public void print(long l) throws IOException {
409 write(String.valueOf(l));
410 }
411
412 /***
413 * Print a floating-point number. The string produced by <code>{@link
414 * java.lang.String#valueOf(float)}</code> is translated into bytes
415 * according to the platform's default character encoding, and these bytes
416 * are written in exactly the manner of the <code>{@link #write(int)}</code>
417 * method.
418 *
419 * @param f The <code>float</code> to be printed
420 */
421 public void print(float f) throws IOException {
422 write(String.valueOf(f));
423 }
424
425 /***
426 * Print a double-precision floating-point number. The string produced by
427 * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
428 * bytes according to the platform's default character encoding, and these
429 * bytes are written in exactly the manner of the <code>{@link
430 * #write(int)}</code> method.
431 *
432 * @param d The <code>double</code> to be printed
433 */
434 public void print(double d) throws IOException {
435 write(String.valueOf(d));
436 }
437
438 /***
439 * Print an array of characters. The characters are converted into bytes
440 * according to the platform's default character encoding, and these bytes
441 * are written in exactly the manner of the <code>{@link #write(int)}</code>
442 * method.
443 *
444 * @param s The array of chars to be printed
445 * @throws NullPointerException If <code>s</code> is <code>null</code>
446 */
447 public void print(char s[]) throws IOException {
448 write(s);
449 }
450
451 /***
452 * Print a string. If the argument is <code>null</code> then the string
453 * <code>"null"</code> is printed. Otherwise, the string's characters are
454 * converted into bytes according to the platform's default character
455 * encoding, and these bytes are written in exactly the manner of the
456 * <code>{@link #write(int)}</code> method.
457 *
458 * @param s The <code>String</code> to be printed
459 */
460 public void print(String s) throws IOException {
461 if (s == null) {
462 s = "null";
463 }
464 write(s);
465 }
466
467 /***
468 * Print an object. The string produced by the <code>{@link
469 * java.lang.String#valueOf(Object)}</code> method is translated into bytes
470 * according to the platform's default character encoding, and these bytes
471 * are written in exactly the manner of the <code>{@link #write(int)}</code>
472 * method.
473 *
474 * @param obj The <code>Object</code> to be printed
475 */
476 public void print(Object obj) throws IOException {
477 write(String.valueOf(obj));
478 }
479
480
481
482 /***
483 * Terminate the current line by writing the line separator string. The
484 * line separator string is defined by the system property
485 * <code>line.separator</code>, and is not necessarily a single newline
486 * character (<code>'\n'</code>).
487 * <p/>
488 * Need to change this from PrintWriter because the default
489 * println() writes to the sink directly instead of through the
490 * write method...
491 */
492 public void println() throws IOException {
493 newLine();
494 }
495
496 /***
497 * Print a boolean value and then terminate the line. This method behaves
498 * as though it invokes <code>{@link #print(boolean)}</code> and then
499 * <code>{@link #println()}</code>.
500 */
501 public void println(boolean x) throws IOException {
502 print(x);
503 println();
504 }
505
506 /***
507 * Print a character and then terminate the line. This method behaves as
508 * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
509 * #println()}</code>.
510 */
511 public void println(char x) throws IOException {
512 print(x);
513 println();
514 }
515
516 /***
517 * Print an integer and then terminate the line. This method behaves as
518 * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
519 * #println()}</code>.
520 */
521 public void println(int x) throws IOException {
522 print(x);
523 println();
524 }
525
526 /***
527 * Print a long integer and then terminate the line. This method behaves
528 * as though it invokes <code>{@link #print(long)}</code> and then
529 * <code>{@link #println()}</code>.
530 */
531 public void println(long x) throws IOException {
532 print(x);
533 println();
534 }
535
536 /***
537 * Print a floating-point number and then terminate the line. This method
538 * behaves as though it invokes <code>{@link #print(float)}</code> and then
539 * <code>{@link #println()}</code>.
540 */
541 public void println(float x) throws IOException {
542 print(x);
543 println();
544 }
545
546 /***
547 * Print a double-precision floating-point number and then terminate the
548 * line. This method behaves as though it invokes <code>{@link
549 * #print(double)}</code> and then <code>{@link #println()}</code>.
550 */
551 public void println(double x) throws IOException {
552 print(x);
553 println();
554 }
555
556 /***
557 * Print an array of characters and then terminate the line. This method
558 * behaves as though it invokes <code>{@link #print(char[])}</code> and then
559 * <code>{@link #println()}</code>.
560 */
561 public void println(char x[]) throws IOException {
562 print(x);
563 println();
564 }
565
566 /***
567 * Print a String and then terminate the line. This method behaves as
568 * though it invokes <code>{@link #print(String)}</code> and then
569 * <code>{@link #println()}</code>.
570 */
571 public void println(String x) throws IOException {
572 print(x);
573 println();
574 }
575
576 /***
577 * Print an Object and then terminate the line. This method behaves as
578 * though it invokes <code>{@link #print(Object)}</code> and then
579 * <code>{@link #println()}</code>.
580 */
581 public void println(Object x) throws IOException {
582 print(x);
583 println();
584 }
585
586 }