001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.jasper.runtime;
019:
020: import java.io.IOException;
021: import java.io.Writer;
022: import java.security.AccessController;
023: import java.security.PrivilegedAction;
024:
025: import javax.servlet.ServletResponse;
026: import javax.servlet.jsp.JspWriter;
027:
028: import org.apache.jasper.Constants;
029: import org.apache.jasper.compiler.Localizer;
030: import org.apache.jasper.security.SecurityUtil;
031:
032: /**
033: * Write text to a character-output stream, buffering characters so as
034: * to provide for the efficient writing of single characters, arrays,
035: * and strings.
036: *
037: * Provide support for discarding for the output that has been
038: * buffered.
039: *
040: * This needs revisiting when the buffering problems in the JSP spec
041: * are fixed -akv
042: *
043: * @author Anil K. Vijendran
044: */
045: public class JspWriterImpl extends JspWriter {
046:
047: private Writer out;
048: private ServletResponse response;
049: private char cb[];
050: private int nextChar;
051: private boolean flushed = false;
052: private boolean closed = false;
053:
054: public JspWriterImpl() {
055: super (Constants.DEFAULT_BUFFER_SIZE, true);
056: }
057:
058: /**
059: * Create a buffered character-output stream that uses a default-sized
060: * output buffer.
061: *
062: * @param response A Servlet Response
063: */
064: public JspWriterImpl(ServletResponse response) {
065: this (response, Constants.DEFAULT_BUFFER_SIZE, true);
066: }
067:
068: /**
069: * Create a new buffered character-output stream that uses an output
070: * buffer of the given size.
071: *
072: * @param response A Servlet Response
073: * @param sz Output-buffer size, a positive integer
074: *
075: * @exception IllegalArgumentException If sz is <= 0
076: */
077: public JspWriterImpl(ServletResponse response, int sz,
078: boolean autoFlush) {
079: super (sz, autoFlush);
080: if (sz < 0)
081: throw new IllegalArgumentException("Buffer size <= 0");
082: this .response = response;
083: cb = sz == 0 ? null : new char[sz];
084: nextChar = 0;
085: }
086:
087: void init(ServletResponse response, int sz, boolean autoFlush) {
088: this .response = response;
089: if (sz > 0 && (cb == null || sz > cb.length))
090: cb = new char[sz];
091: nextChar = 0;
092: this .autoFlush = autoFlush;
093: this .bufferSize = sz;
094: }
095:
096: /** Package-level access
097: */
098: void recycle() {
099: flushed = false;
100: closed = false;
101: out = null;
102: nextChar = 0;
103: response = null;
104: }
105:
106: /**
107: * Flush the output buffer to the underlying character stream, without
108: * flushing the stream itself. This method is non-private only so that it
109: * may be invoked by PrintStream.
110: */
111: protected final void flushBuffer() throws IOException {
112: if (bufferSize == 0)
113: return;
114: flushed = true;
115: ensureOpen();
116: if (nextChar == 0)
117: return;
118: initOut();
119: out.write(cb, 0, nextChar);
120: nextChar = 0;
121: }
122:
123: private void initOut() throws IOException {
124: if (out == null) {
125: out = response.getWriter();
126: }
127: }
128:
129: private String getLocalizeMessage(final String message) {
130: if (SecurityUtil.isPackageProtectionEnabled()) {
131: return (String) AccessController
132: .doPrivileged(new PrivilegedAction() {
133: public Object run() {
134: return Localizer.getMessage(message);
135: }
136: });
137: } else {
138: return Localizer.getMessage(message);
139: }
140: }
141:
142: /**
143: * Discard the output buffer.
144: */
145: public final void clear() throws IOException {
146: if ((bufferSize == 0) && (out != null))
147: // clear() is illegal after any unbuffered output (JSP.5.5)
148: throw new IllegalStateException(
149: getLocalizeMessage("jsp.error.ise_on_clear"));
150: if (flushed)
151: throw new IOException(
152: getLocalizeMessage("jsp.error.attempt_to_clear_flushed_buffer"));
153: ensureOpen();
154: nextChar = 0;
155: }
156:
157: public void clearBuffer() throws IOException {
158: if (bufferSize == 0)
159: throw new IllegalStateException(
160: getLocalizeMessage("jsp.error.ise_on_clear"));
161: ensureOpen();
162: nextChar = 0;
163: }
164:
165: private final void bufferOverflow() throws IOException {
166: throw new IOException(getLocalizeMessage("jsp.error.overflow"));
167: }
168:
169: /**
170: * Flush the stream.
171: *
172: */
173: public void flush() throws IOException {
174: flushBuffer();
175: if (out != null) {
176: out.flush();
177: }
178: }
179:
180: /**
181: * Close the stream.
182: *
183: */
184: public void close() throws IOException {
185: if (response == null || closed)
186: // multiple calls to close is OK
187: return;
188: flush();
189: if (out != null)
190: out.close();
191: out = null;
192: closed = true;
193: }
194:
195: /**
196: * @return the number of bytes unused in the buffer
197: */
198: public int getRemaining() {
199: return bufferSize - nextChar;
200: }
201:
202: /** check to make sure that the stream has not been closed */
203: private void ensureOpen() throws IOException {
204: if (response == null || closed)
205: throw new IOException("Stream closed");
206: }
207:
208: /**
209: * Write a single character.
210: */
211: public void write(int c) throws IOException {
212: ensureOpen();
213: if (bufferSize == 0) {
214: initOut();
215: out.write(c);
216: } else {
217: if (nextChar >= bufferSize)
218: if (autoFlush)
219: flushBuffer();
220: else
221: bufferOverflow();
222: cb[nextChar++] = (char) c;
223: }
224: }
225:
226: /**
227: * Our own little min method, to avoid loading java.lang.Math if we've run
228: * out of file descriptors and we're trying to print a stack trace.
229: */
230: private int min(int a, int b) {
231: if (a < b)
232: return a;
233: return b;
234: }
235:
236: /**
237: * Write a portion of an array of characters.
238: *
239: * <p> Ordinarily this method stores characters from the given array into
240: * this stream's buffer, flushing the buffer to the underlying stream as
241: * needed. If the requested length is at least as large as the buffer,
242: * however, then this method will flush the buffer and write the characters
243: * directly to the underlying stream. Thus redundant
244: * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
245: *
246: * @param cbuf A character array
247: * @param off Offset from which to start reading characters
248: * @param len Number of characters to write
249: */
250: public void write(char cbuf[], int off, int len) throws IOException {
251: ensureOpen();
252:
253: if (bufferSize == 0) {
254: initOut();
255: out.write(cbuf, off, len);
256: return;
257: }
258:
259: if ((off < 0) || (off > cbuf.length) || (len < 0)
260: || ((off + len) > cbuf.length) || ((off + len) < 0)) {
261: throw new IndexOutOfBoundsException();
262: } else if (len == 0) {
263: return;
264: }
265:
266: if (len >= bufferSize) {
267: /* If the request length exceeds the size of the output buffer,
268: flush the buffer and then write the data directly. In this
269: way buffered streams will cascade harmlessly. */
270: if (autoFlush)
271: flushBuffer();
272: else
273: bufferOverflow();
274: initOut();
275: out.write(cbuf, off, len);
276: return;
277: }
278:
279: int b = off, t = off + len;
280: while (b < t) {
281: int d = min(bufferSize - nextChar, t - b);
282: System.arraycopy(cbuf, b, cb, nextChar, d);
283: b += d;
284: nextChar += d;
285: if (nextChar >= bufferSize)
286: if (autoFlush)
287: flushBuffer();
288: else
289: bufferOverflow();
290: }
291:
292: }
293:
294: /**
295: * Write an array of characters. This method cannot be inherited from the
296: * Writer class because it must suppress I/O exceptions.
297: */
298: public void write(char buf[]) throws IOException {
299: write(buf, 0, buf.length);
300: }
301:
302: /**
303: * Write a portion of a String.
304: *
305: * @param s String to be written
306: * @param off Offset from which to start reading characters
307: * @param len Number of characters to be written
308: */
309: public void write(String s, int off, int len) throws IOException {
310: ensureOpen();
311: if (bufferSize == 0) {
312: initOut();
313: out.write(s, off, len);
314: return;
315: }
316: int b = off, t = off + len;
317: while (b < t) {
318: int d = min(bufferSize - nextChar, t - b);
319: s.getChars(b, b + d, cb, nextChar);
320: b += d;
321: nextChar += d;
322: if (nextChar >= bufferSize)
323: if (autoFlush)
324: flushBuffer();
325: else
326: bufferOverflow();
327: }
328: }
329:
330: /**
331: * Write a string. This method cannot be inherited from the Writer class
332: * because it must suppress I/O exceptions.
333: */
334: public void write(String s) throws IOException {
335: // Simple fix for Bugzilla 35410
336: // Calling the other write function so as to init the buffer anyways
337: if (s == null) {
338: write(s, 0, 0);
339: } else {
340: write(s, 0, s.length());
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: * @exception IOException If an I/O error occurs
352: */
353:
354: public void newLine() throws IOException {
355: write(lineSeparator);
356: }
357:
358: /* Methods that do not terminate lines */
359:
360: /**
361: * Print a boolean value. The string produced by <code>{@link
362: * java.lang.String#valueOf(boolean)}</code> is translated into bytes
363: * according to the platform's default character encoding, and these bytes
364: * are written in exactly the manner of the <code>{@link
365: * #write(int)}</code> method.
366: *
367: * @param b The <code>boolean</code> to be printed
368: */
369: public void print(boolean b) throws IOException {
370: write(b ? "true" : "false");
371: }
372:
373: /**
374: * Print a character. The character is translated into one or more bytes
375: * according to the platform's default character encoding, and these bytes
376: * are written in exactly the manner of the <code>{@link
377: * #write(int)}</code> method.
378: *
379: * @param c The <code>char</code> to be printed
380: */
381: public void print(char c) throws IOException {
382: write(String.valueOf(c));
383: }
384:
385: /**
386: * Print an integer. The string produced by <code>{@link
387: * java.lang.String#valueOf(int)}</code> is translated into bytes according
388: * to the platform's default character encoding, and these bytes are
389: * written in exactly the manner of the <code>{@link #write(int)}</code>
390: * method.
391: *
392: * @param i The <code>int</code> to be printed
393: */
394: public void print(int i) throws IOException {
395: write(String.valueOf(i));
396: }
397:
398: /**
399: * Print a long integer. The string produced by <code>{@link
400: * java.lang.String#valueOf(long)}</code> is translated into bytes
401: * according to the platform's default character encoding, and these bytes
402: * are written in exactly the manner of the <code>{@link #write(int)}</code>
403: * method.
404: *
405: * @param l The <code>long</code> to be printed
406: */
407: public void print(long l) throws IOException {
408: write(String.valueOf(l));
409: }
410:
411: /**
412: * Print a floating-point number. The string produced by <code>{@link
413: * java.lang.String#valueOf(float)}</code> is translated into bytes
414: * according to the platform's default character encoding, and these bytes
415: * are written in exactly the manner of the <code>{@link #write(int)}</code>
416: * method.
417: *
418: * @param f The <code>float</code> to be printed
419: */
420: public void print(float f) throws IOException {
421: write(String.valueOf(f));
422: }
423:
424: /**
425: * Print a double-precision floating-point number. The string produced by
426: * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
427: * bytes according to the platform's default character encoding, and these
428: * bytes are written in exactly the manner of the <code>{@link
429: * #write(int)}</code> method.
430: *
431: * @param d The <code>double</code> to be printed
432: */
433: public void print(double d) throws IOException {
434: write(String.valueOf(d));
435: }
436:
437: /**
438: * Print an array of characters. The characters are converted into bytes
439: * according to the platform's default character encoding, and these bytes
440: * are written in exactly the manner of the <code>{@link #write(int)}</code>
441: * method.
442: *
443: * @param s The array of chars to be printed
444: *
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: /* Methods that do terminate lines */
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: *
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: }
|