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.IOException;
21 import java.io.Writer;
22
23 import javax.servlet.ServletResponse;
24 import javax.servlet.jsp.JspWriter;
25
26 import org.apache.jasper.Constants;
27 import org.apache.jasper.compiler.Localizer;
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  *
34  * Provide support for discarding for the output that has been
35  * buffered.
36  *
37  * This needs revisiting when the buffering problems in the JSP spec
38  * are fixed -akv
39  *
40  * @author Anil K. Vijendran
41  */

42 public class JspWriterImpl extends JspWriter {
43
44     private Writer out;
45     private ServletResponse response;
46     private char cb[];
47     private int nextChar;
48     private boolean flushed = false;
49     private boolean closed = false;
50
51     public JspWriterImpl() {
52         super( Constants.DEFAULT_BUFFER_SIZE, true );
53     }
54
55     /**
56      * Create a new buffered character-output stream that uses an output
57      * buffer of the given size.
58      *
59      * @param  response A Servlet Response
60      * @param  sz       Output-buffer size, a positive integer
61      * @param autoFlush <code>true</code> to automatically flush on buffer
62      *  full, <code>false</code> to throw an overflow exception in that case
63      * @exception  IllegalArgumentException  If sz is &lt;= 0
64      */

65     public JspWriterImpl(ServletResponse response, int sz,
66             boolean autoFlush) {
67         super(sz, autoFlush);
68         if (sz < 0)
69             throw new IllegalArgumentException(Localizer.getMessage("jsp.error.negativeBufferSize"));
70         this.response = response;
71         cb = sz == 0 ? null : new char[sz];
72         nextChar = 0;
73     }
74
75     void init( ServletResponse response, int sz, boolean autoFlush ) {
76         this.response= response;
77         if( sz > 0 && ( cb == null || sz > cb.length ) )
78             cb=new char[sz];
79         nextChar = 0;
80         this.autoFlush=autoFlush;
81         this.bufferSize=sz;
82     }
83
84     /**
85      * Package-level access
86      */

87     void recycle() {
88         flushed = false;
89         closed = false;
90         out = null;
91         nextChar = 0;
92         response = null;
93     }
94
95     /**
96      * Flush the output buffer to the underlying character stream, without
97      * flushing the stream itself.  This method is non-private only so that it
98      * may be invoked by PrintStream.
99      * @throws IOException Error writing buffered data
100      */

101     protected final void flushBuffer() throws IOException {
102         if (bufferSize == 0)
103             return;
104         flushed = true;
105         ensureOpen();
106         if (nextChar == 0)
107             return;
108         initOut();
109         out.write(cb, 0, nextChar);
110         nextChar = 0;
111     }
112
113     private void initOut() throws IOException {
114         if (out == null) {
115             out = response.getWriter();
116         }
117     }
118
119     /**
120      * Discard the output buffer.
121      */

122     @Override
123     public final void clear() throws IOException {
124         if ((bufferSize == 0) && (out != null))
125             // clear() is illegal after any unbuffered output (JSP.5.5)
126             throw new IllegalStateException(
127                     Localizer.getMessage("jsp.error.ise_on_clear"));
128         if (flushed)
129             throw new IOException(
130                     Localizer.getMessage("jsp.error.attempt_to_clear_flushed_buffer"));
131         ensureOpen();
132         nextChar = 0;
133     }
134
135     @Override
136     public void clearBuffer() throws IOException {
137         if (bufferSize == 0)
138             throw new IllegalStateException(
139                     Localizer.getMessage("jsp.error.ise_on_clear"));
140         ensureOpen();
141         nextChar = 0;
142     }
143
144     private final void bufferOverflow() throws IOException {
145         throw new IOException(Localizer.getMessage("jsp.error.overflow"));
146     }
147
148     /**
149      * Flush the stream.
150      *
151      */

152     @Override
153     public void flush()  throws IOException {
154         flushBuffer();
155         if (out != null) {
156             out.flush();
157         }
158     }
159
160     /**
161      * Close the stream.
162      *
163      */

164     @Override
165     public void close() throws IOException {
166         if (response == null || closed)
167             // multiple calls to close is OK
168             return;
169         flush();
170         if (out != null)
171             out.close();
172         out = null;
173         closed = true;
174     }
175
176     /**
177      * @return the number of bytes unused in the buffer
178      */

179     @Override
180     public int getRemaining() {
181         return bufferSize - nextChar;
182     }
183
184     /** check to make sure that the stream has not been closed */
185     private void ensureOpen() throws IOException {
186         if (response == null || closed)
187             throw new IOException(Localizer.getMessage("jsp.error.stream.closed"));
188     }
189
190
191     /**
192      * Write a single character.
193      */

194     @Override
195     public void write(int c) throws IOException {
196         ensureOpen();
197         if (bufferSize == 0) {
198             initOut();
199             out.write(c);
200         }
201         else {
202             if (nextChar >= bufferSize)
203                 if (autoFlush)
204                     flushBuffer();
205                 else
206                     bufferOverflow();
207             cb[nextChar++] = (char) c;
208         }
209     }
210
211     /**
212      * Our own little min method, to avoid loading java.lang.Math if we've run
213      * out of file descriptors and we're trying to print a stack trace.
214      */

215     private static int min(int a, int b) {
216         if (a < b) return a;
217         return b;
218     }
219
220     /**
221      * Write a portion of an array of characters.
222      *
223      * <p> Ordinarily this method stores characters from the given array into
224      * this stream's buffer, flushing the buffer to the underlying stream as
225      * needed.  If the requested length is at least as large as the buffer,
226      * however, then this method will flush the buffer and write the characters
227      * directly to the underlying stream.  Thus redundant
228      * <code>DiscardableBufferedWriter</code>s will not copy data unnecessarily.
229      *
230      * @param  cbuf  A character array
231      * @param  off   Offset from which to start reading characters
232      * @param  len   Number of characters to write
233      */

234     @Override
235     public void write(char cbuf[], int off, int len)
236     throws IOException
237     {
238         ensureOpen();
239
240         if (bufferSize == 0) {
241             initOut();
242             out.write(cbuf, off, len);
243             return;
244         }
245
246         if ((off < 0) || (off > cbuf.length) || (len < 0) ||
247                 ((off + len) > cbuf.length) || ((off + len) < 0)) {
248             throw new IndexOutOfBoundsException();
249         } else if (len == 0) {
250             return;
251         }
252
253         if (len >= bufferSize) {
254             /* If the request length exceeds the size of the output buffer,
255              flush the buffer and then write the data directly.  In this
256              way buffered streams will cascade harmlessly. */

257             if (autoFlush)
258                 flushBuffer();
259             else
260                 bufferOverflow();
261             initOut();
262             out.write(cbuf, off, len);
263             return;
264         }
265
266         int b = off, t = off + len;
267         while (b < t) {
268             int d = min(bufferSize - nextChar, t - b);
269             System.arraycopy(cbuf, b, cb, nextChar, d);
270             b += d;
271             nextChar += d;
272             if (nextChar >= bufferSize)
273                 if (autoFlush)
274                     flushBuffer();
275                 else
276                     bufferOverflow();
277         }
278
279     }
280
281     /**
282      * Write an array of characters.  This method cannot be inherited from the
283      * Writer class because it must suppress I/O exceptions.
284      */

285     @Override
286     public void write(char buf[]) throws IOException {
287         write(buf, 0, buf.length);
288     }
289
290     /**
291      * Write a portion of a String.
292      *
293      * @param  s     String to be written
294      * @param  off   Offset from which to start reading characters
295      * @param  len   Number of characters to be written
296      */

297     @Override
298     public void write(String s, int off, int len) throws IOException {
299         ensureOpen();
300         if (bufferSize == 0) {
301             initOut();
302             out.write(s, off, len);
303             return;
304         }
305         int b = off, t = off + len;
306         while (b < t) {
307             int d = min(bufferSize - nextChar, t - b);
308             s.getChars(b, b + d, cb, nextChar);
309             b += d;
310             nextChar += d;
311             if (nextChar >= bufferSize)
312                 if (autoFlush)
313                     flushBuffer();
314                 else
315                     bufferOverflow();
316         }
317     }
318
319
320     /**
321      * Write a line separator.  The line separator string is defined by the
322      * system property <code>line.separator</code>, and is not necessarily a
323      * single newline ('\n') character.
324      *
325      * @exception  IOException  If an I/O error occurs
326      */

327
328     @Override
329     public void newLine() throws IOException {
330         write(System.lineSeparator());
331     }
332
333
334     /* Methods that do not terminate lines */
335
336     /**
337      * Print a boolean value.  The string produced by <code>{@link
338      * java.lang.String#valueOf(boolean)}</code> is translated into bytes
339      * according to the platform's default character encoding, and these bytes
340      * are written in exactly the manner of the <code>{@link
341      * #write(int)}</code> method.
342      *
343      * @param      b   The <code>boolean</code> to be printed
344      */

345     @Override
346     public void print(boolean b) throws IOException {
347         write(b ? "true" : "false");
348     }
349
350     /**
351      * Print a character.  The character is translated into one or more bytes
352      * according to the platform's default character encoding, and these bytes
353      * are written in exactly the manner of the <code>{@link
354      * #write(int)}</code> method.
355      *
356      * @param      c   The <code>char</code> to be printed
357      */

358     @Override
359     public void print(char c) throws IOException {
360         write(String.valueOf(c));
361     }
362
363     /**
364      * Print an integer.  The string produced by <code>{@link
365      * java.lang.String#valueOf(int)}</code> is translated into bytes according
366      * to the platform's default character encoding, and these bytes are
367      * written in exactly the manner of the <code>{@link #write(int)}</code>
368      * method.
369      *
370      * @param      i   The <code>int</code> to be printed
371      */

372     @Override
373     public void print(int i) throws IOException {
374         write(String.valueOf(i));
375     }
376
377     /**
378      * Print a long integer.  The string produced by <code>{@link
379      * java.lang.String#valueOf(long)}</code> is translated into bytes
380      * according to the platform's default character encoding, and these bytes
381      * are written in exactly the manner of the <code>{@link #write(int)}</code>
382      * method.
383      *
384      * @param      l   The <code>long</code> to be printed
385      */

386     @Override
387     public void print(long l) throws IOException {
388         write(String.valueOf(l));
389     }
390
391     /**
392      * Print a floating-point number.  The string produced by <code>{@link
393      * java.lang.String#valueOf(float)}</code> is translated into bytes
394      * according to the platform's default character encoding, and these bytes
395      * are written in exactly the manner of the <code>{@link #write(int)}</code>
396      * method.
397      *
398      * @param      f   The <code>float</code> to be printed
399      */

400     @Override
401     public void print(float f) throws IOException {
402         write(String.valueOf(f));
403     }
404
405     /**
406      * Print a double-precision floating-point number.  The string produced by
407      * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
408      * bytes according to the platform's default character encoding, and these
409      * bytes are written in exactly the manner of the <code>{@link
410      * #write(int)}</code> method.
411      *
412      * @param      d   The <code>double</code> to be printed
413      */

414     @Override
415     public void print(double d) throws IOException {
416         write(String.valueOf(d));
417     }
418
419     /**
420      * Print an array of characters.  The characters are converted into bytes
421      * according to the platform's default character encoding, and these bytes
422      * are written in exactly the manner of the <code>{@link #write(int)}</code>
423      * method.
424      *
425      * @param      s   The array of chars to be printed
426      *
427      * @throws  NullPointerException  If <code>s</code> is <code>null</code>
428      */

429     @Override
430     public void print(char s[]) throws IOException {
431         write(s);
432     }
433
434     /**
435      * Print a string.  If the argument is <code>null</code> then the string
436      * <code>"null"</code> is printed.  Otherwise, the string's characters are
437      * converted into bytes according to the platform's default character
438      * encoding, and these bytes are written in exactly the manner of the
439      * <code>{@link #write(int)}</code> method.
440      *
441      * @param      s   The <code>String</code> to be printed
442      */

443     @Override
444     public void print(String s) throws IOException {
445         if (s == null) {
446             s = "null";
447         }
448         write(s);
449     }
450
451     /**
452      * Print an object.  The string produced by the <code>{@link
453      * java.lang.String#valueOf(Object)}</code> method is translated into bytes
454      * according to the platform's default character encoding, and these bytes
455      * are written in exactly the manner of the <code>{@link #write(int)}</code>
456      * method.
457      *
458      * @param      obj   The <code>Object</code> to be printed
459      */

460     @Override
461     public void print(Object obj) throws IOException {
462         write(String.valueOf(obj));
463     }
464
465     /* Methods that do terminate lines */
466
467     /**
468      * Terminate the current line by writing the line separator string.  The
469      * line separator string is defined by the system property
470      * <code>line.separator</code>, and is not necessarily a single newline
471      * character (<code>'\n'</code>).
472      *
473      * Need to change this from PrintWriter because the default
474      * println() writes  to the sink directly instead of through the
475      * write method...
476      */

477     @Override
478     public void println() throws IOException {
479         newLine();
480     }
481
482     /**
483      * Print a boolean value and then terminate the line.  This method behaves
484      * as though it invokes <code>{@link #print(boolean)}</code> and then
485      * <code>{@link #println()}</code>.
486      */

487     @Override
488     public void println(boolean x) throws IOException {
489         print(x);
490         println();
491     }
492
493     /**
494      * Print a character and then terminate the line.  This method behaves as
495      * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
496      * #println()}</code>.
497      */

498     @Override
499     public void println(char x) throws IOException {
500         print(x);
501         println();
502     }
503
504     /**
505      * Print an integer and then terminate the line.  This method behaves as
506      * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
507      * #println()}</code>.
508      */

509     @Override
510     public void println(int x) throws IOException {
511         print(x);
512         println();
513     }
514
515     /**
516      * Print a long integer and then terminate the line.  This method behaves
517      * as though it invokes <code>{@link #print(long)}</code> and then
518      * <code>{@link #println()}</code>.
519      */

520     @Override
521     public void println(long x) throws IOException {
522         print(x);
523         println();
524     }
525
526     /**
527      * Print a floating-point number and then terminate the line.  This method
528      * behaves as though it invokes <code>{@link #print(float)}</code> and then
529      * <code>{@link #println()}</code>.
530      */

531     @Override
532     public void println(float x) throws IOException {
533         print(x);
534         println();
535     }
536
537     /**
538      * Print a double-precision floating-point number and then terminate the
539      * line.  This method behaves as though it invokes <code>{@link
540      * #print(double)}</code> and then <code>{@link #println()}</code>.
541      */

542     @Override
543     public void println(double x) throws IOException {
544         print(x);
545         println();
546     }
547
548     /**
549      * Print an array of characters and then terminate the line.  This method
550      * behaves as though it invokes <code>{@link #print(char[])}</code> and then
551      * <code>{@link #println()}</code>.
552      */

553     @Override
554     public void println(char x[]) throws IOException {
555         print(x);
556         println();
557     }
558
559     /**
560      * Print a String and then terminate the line.  This method behaves as
561      * though it invokes <code>{@link #print(String)}</code> and then
562      * <code>{@link #println()}</code>.
563      */

564     @Override
565     public void println(String x) throws IOException {
566         print(x);
567         println();
568     }
569
570     /**
571      * Print an Object and then terminate the line.  This method behaves as
572      * though it invokes <code>{@link #print(Object)}</code> and then
573      * <code>{@link #println()}</code>.
574      */

575     @Override
576     public void println(Object x) throws IOException {
577         print(x);
578         println();
579     }
580
581 }
582