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 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26
27 import javax.servlet.jsp.JspWriter;
28 import javax.servlet.jsp.tagext.BodyContent;
29
30 import org.apache.jasper.Constants;
31 import org.apache.jasper.compiler.Localizer;
32
33 /**
34  * Write text to a character-output stream, buffering characters so as
35  * to provide for the efficient writing of single characters, arrays,
36  * and strings.
37  *
38  * Provide support for discarding for the output that has been buffered.
39  *
40  * @author Rajiv Mordani
41  * @author Jan Luehe
42  */

43 public class BodyContentImpl extends BodyContent {
44
45     private static final boolean LIMIT_BUFFER;
46     private static final int TAG_BUFFER_SIZE;
47
48     static {
49         if (System.getSecurityManager() == null) {
50             LIMIT_BUFFER = Boolean.parseBoolean(System.getProperty(
51                     "org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER""false"));
52             TAG_BUFFER_SIZE = Integer.getInteger(
53                     "org.apache.jasper.runtime.BodyContentImpl.BUFFER_SIZE",
54                     Constants.DEFAULT_TAG_BUFFER_SIZE).intValue();
55         } else {
56             LIMIT_BUFFER = AccessController.doPrivileged(
57                     new PrivilegedAction<Boolean>() {
58                         @Override
59                         public Boolean run() {
60                             return Boolean.valueOf(System.getProperty(
61                                     "org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER",
62                                     "false"));
63                         }
64                     }
65             ).booleanValue();
66             TAG_BUFFER_SIZE = AccessController.doPrivileged(
67                     new PrivilegedAction<Integer>() {
68                         @Override
69                         public Integer run() {
70                             return Integer.getInteger(
71                                     "org.apache.jasper.runtime.BodyContentImpl.BUFFER_SIZE",
72                                     Constants.DEFAULT_TAG_BUFFER_SIZE);
73                         }
74                     }
75             ).intValue();
76         }
77     }
78
79
80     private char[] cb;
81     private int nextChar;
82     private boolean closed;
83
84     /**
85      * Enclosed writer to which any output is written
86      */

87     private Writer writer;
88
89     /**
90      * Constructor.
91      * @param enclosingWriter The wrapped writer
92      */

93     public BodyContentImpl(JspWriter enclosingWriter) {
94         super(enclosingWriter);
95         cb = new char[TAG_BUFFER_SIZE];
96         bufferSize = cb.length;
97         nextChar = 0;
98         closed = false;
99     }
100
101     /**
102      * Write a single character.
103      * @param c The char to write
104      * @throws IOException Error writing to wrapped writer
105      */

106     @Override
107     public void write(int c) throws IOException {
108         if (writer != null) {
109             writer.write(c);
110         } else {
111             ensureOpen();
112             if (nextChar >= bufferSize) {
113                 reAllocBuff (1);
114             }
115             cb[nextChar++] = (char) c;
116         }
117     }
118
119     /**
120      * Write a portion of an array of characters.
121      *
122      * <p> Ordinarily this method stores characters from the given array into
123      * this stream's buffer, flushing the buffer to the underlying stream as
124      * needed.  If the requested length is at least as large as the buffer,
125      * however, then this method will flush the buffer and write the characters
126      * directly to the underlying stream.  Thus redundant
127      * <code>DiscardableBufferedWriter</code>s will not copy data
128      * unnecessarily.
129      *
130      * @param cbuf A character array
131      * @param off Offset from which to start reading characters
132      * @param len Number of characters to write
133      * @throws IOException Error writing to wrapped writer
134      */

135     @Override
136     public void write(char[] cbuf, int off, int len) throws IOException {
137         if (writer != null) {
138             writer.write(cbuf, off, len);
139         } else {
140             ensureOpen();
141
142             if ((off < 0) || (off > cbuf.length) || (len < 0) ||
143                     ((off + len) > cbuf.length) || ((off + len) < 0)) {
144                 throw new IndexOutOfBoundsException();
145             } else if (len == 0) {
146                 return;
147             }
148
149             if (len >= bufferSize - nextChar)
150                 reAllocBuff (len);
151
152             System.arraycopy(cbuf, off, cb, nextChar, len);
153             nextChar+=len;
154         }
155     }
156
157     /**
158      * Write an array of characters.  This method cannot be inherited from the
159      * Writer class because it must suppress I/O exceptions.
160      * @param buf Content to write
161      * @throws IOException Error writing to wrapped writer
162      */

163     @Override
164     public void write(char[] buf) throws IOException {
165         if (writer != null) {
166             writer.write(buf);
167         } else {
168             write(buf, 0, buf.length);
169         }
170     }
171
172     /**
173      * Write a portion of a String.
174      *
175      * @param s String to be written
176      * @param off Offset from which to start reading characters
177      * @param len Number of characters to be written
178      * @throws IOException Error writing to wrapped writer
179      */

180     @Override
181     public void write(String s, int off, int len) throws IOException {
182         if (writer != null) {
183             writer.write(s, off, len);
184         } else {
185             ensureOpen();
186             if (len >= bufferSize - nextChar)
187                 reAllocBuff(len);
188
189             s.getChars(off, off + len, cb, nextChar);
190             nextChar += len;
191         }
192     }
193
194     /**
195      * Write a string.  This method cannot be inherited from the Writer class
196      * because it must suppress I/O exceptions.
197      * @param s String to be written
198      * @throws IOException Error writing to wrapped writer
199      */

200     @Override
201     public void write(String s) throws IOException {
202         if (writer != null) {
203             writer.write(s);
204         } else {
205             write(s, 0, s.length());
206         }
207     }
208
209     /**
210      * Write a line separator.  The line separator string is defined by the
211      * system property <code>line.separator</code>, and is not necessarily a
212      * single newline ('\n') character.
213      *
214      * @throws IOException Error writing to wrapped writer
215      */

216     @Override
217     public void newLine() throws IOException {
218         if (writer != null) {
219             writer.write(System.lineSeparator());
220         } else {
221             write(System.lineSeparator());
222         }
223     }
224
225     /**
226      * Print a boolean value.  The string produced by <code>{@link
227      * java.lang.String#valueOf(boolean)}</code> is translated into bytes
228      * according to the platform's default character encoding, and these bytes
229      * are written in exactly the manner of the <code>{@link
230      * #write(int)}</code> method.
231      *
232      * @param b The <code>boolean</code> to be printed
233      * @throws IOException Error writing to wrapped writer
234      */

235     @Override
236     public void print(boolean b) throws IOException {
237         if (writer != null) {
238             writer.write(b ? "true" : "false");
239         } else {
240             write(b ? "true" : "false");
241         }
242     }
243
244     /**
245      * Print a character.  The character is translated into one or more bytes
246      * according to the platform's default character encoding, and these bytes
247      * are written in exactly the manner of the <code>{@link
248      * #write(int)}</code> method.
249      *
250      * @param c The <code>char</code> to be printed
251      * @throws IOException Error writing to wrapped writer
252      */

253     @Override
254     public void print(char c) throws IOException {
255         if (writer != null) {
256             writer.write(String.valueOf(c));
257         } else {
258             write(String.valueOf(c));
259         }
260     }
261
262     /**
263      * Print an integer.  The string produced by <code>{@link
264      * java.lang.String#valueOf(int)}</code> is translated into bytes according
265      * to the platform's default character encoding, and these bytes are
266      * written in exactly the manner of the <code>{@link #write(int)}</code>
267      * method.
268      *
269      * @param i The <code>int</code> to be printed
270      * @throws IOException Error writing to wrapped writer
271      */

272     @Override
273     public void print(int i) throws IOException {
274         if (writer != null) {
275             writer.write(String.valueOf(i));
276         } else {
277             write(String.valueOf(i));
278         }
279     }
280
281     /**
282      * Print a long integer.  The string produced by <code>{@link
283      * java.lang.String#valueOf(long)}</code> is translated into bytes
284      * according to the platform's default character encoding, and these bytes
285      * are written in exactly the manner of the
286      * <code>{@link #write(int)}</code> method.
287      *
288      * @param l The <code>long</code> to be printed
289      * @throws IOException Error writing to wrapped writer
290      */

291     @Override
292     public void print(long l) throws IOException {
293         if (writer != null) {
294             writer.write(String.valueOf(l));
295         } else {
296             write(String.valueOf(l));
297         }
298     }
299
300     /**
301      * Print a floating-point number.  The string produced by <code>{@link
302      * java.lang.String#valueOf(float)}</code> is translated into bytes
303      * according to the platform's default character encoding, and these bytes
304      * are written in exactly the manner of the
305      * <code>{@link #write(int)}</code> method.
306      *
307      * @param f The <code>float</code> to be printed
308      * @throws IOException Error writing to wrapped writer
309      */

310     @Override
311     public void print(float f) throws IOException {
312         if (writer != null) {
313             writer.write(String.valueOf(f));
314         } else {
315             write(String.valueOf(f));
316         }
317     }
318
319     /**
320      * Print a double-precision floating-point number.  The string produced by
321      * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
322      * bytes according to the platform's default character encoding, and these
323      * bytes are written in exactly the manner of the <code>{@link
324      * #write(int)}</code> method.
325      *
326      * @param d The <code>double</code> to be printed
327      * @throws IOException Error writing to wrapped writer
328      */

329     @Override
330     public void print(double d) throws IOException {
331         if (writer != null) {
332             writer.write(String.valueOf(d));
333         } else {
334             write(String.valueOf(d));
335         }
336     }
337
338     /**
339      * Print an array of characters.  The characters are converted into bytes
340      * according to the platform's default character encoding, and these bytes
341      * are written in exactly the manner of the
342      * <code>{@link #write(int)}</code> method.
343      *
344      * @param s The array of chars to be printed
345      *
346      * @throws NullPointerException If <code>s</code> is <code>null</code>
347      * @throws IOException Error writing to wrapped writer
348      */

349     @Override
350     public void print(char[] s) throws IOException {
351         if (writer != null) {
352             writer.write(s);
353         } else {
354             write(s);
355         }
356     }
357
358     /**
359      * Print a string.  If the argument is <code>null</code> then the string
360      * <code>"null"</code> is printed.  Otherwise, the string's characters are
361      * converted into bytes according to the platform's default character
362      * encoding, and these bytes are written in exactly the manner of the
363      * <code>{@link #write(int)}</code> method.
364      *
365      * @param s The <code>String</code> to be printed
366      * @throws IOException Error writing to wrapped writer
367      */

368     @Override
369     public void print(String s) throws IOException {
370         if (s == null) s = "null";
371         if (writer != null) {
372             writer.write(s);
373         } else {
374             write(s);
375         }
376     }
377
378     /**
379      * Print an object.  The string produced by the <code>{@link
380      * java.lang.String#valueOf(Object)}</code> method is translated into bytes
381      * according to the platform's default character encoding, and these bytes
382      * are written in exactly the manner of the
383      * <code>{@link #write(int)}</code> method.
384      *
385      * @param obj The <code>Object</code> to be printed
386      * @throws IOException Error writing to wrapped writer
387      */

388     @Override
389     public void print(Object obj) throws IOException {
390         if (writer != null) {
391             writer.write(String.valueOf(obj));
392         } else {
393             write(String.valueOf(obj));
394         }
395     }
396
397     /**
398      * Terminate the current line by writing the line separator string.  The
399      * line separator string is defined by the system property
400      * <code>line.separator</code>, and is not necessarily a single newline
401      * character (<code>'\n'</code>).
402      *
403      * @throws IOException Error writing to wrapped writer
404      */

405     @Override
406     public void println() throws IOException {
407         newLine();
408     }
409
410     /**
411      * Print a boolean value and then terminate the line.  This method behaves
412      * as though it invokes <code>{@link #print(boolean)}</code> and then
413      * <code>{@link #println()}</code>.
414      *
415      * @param x The <code>boolean</code> to be printed
416      * @throws IOException Error writing to wrapped writer
417      */

418     @Override
419     public void println(boolean x) throws IOException {
420         print(x);
421         println();
422     }
423
424     /**
425      * Print a character and then terminate the line.  This method behaves as
426      * though it invokes <code>{@link #print(char)}</code> and then
427      * <code>{@link #println()}</code>.
428      *
429      * @param x The <code>char</code> to be printed
430      * @throws IOException Error writing to wrapped writer
431      */

432     @Override
433     public void println(char x) throws IOException {
434         print(x);
435         println();
436     }
437
438     /**
439      * Print an integer and then terminate the line.  This method behaves as
440      * though it invokes <code>{@link #print(int)}</code> and then
441      * <code>{@link #println()}</code>.
442      *
443      * @param x The <code>int</code> to be printed
444      * @throws IOException Error writing to wrapped writer
445      */

446     @Override
447     public void println(int x) throws IOException {
448         print(x);
449         println();
450     }
451
452     /**
453      * Print a long integer and then terminate the line.  This method behaves
454      * as though it invokes <code>{@link #print(long)}</code> and then
455      * <code>{@link #println()}</code>.
456      *
457      * @param x The <code>long</code> to be printed
458      * @throws IOException Error writing to wrapped writer
459      */

460     @Override
461     public void println(long x) throws IOException {
462         print(x);
463         println();
464     }
465
466     /**
467      * Print a floating-point number and then terminate the line.  This method
468      * behaves as though it invokes <code>{@link #print(float)}</code> and then
469      * <code>{@link #println()}</code>.
470      *
471      * @param x The <code>float</code> to be printed
472      * @throws IOException Error writing to wrapped writer
473      */

474     @Override
475     public void println(float x) throws IOException {
476         print(x);
477         println();
478     }
479
480     /**
481      * Print a double-precision floating-point number and then terminate the
482      * line.  This method behaves as though it invokes <code>{@link
483      * #print(double)}</code> and then <code>{@link #println()}</code>.
484      *
485      * @param x The <code>double</code> to be printed
486      * @throws IOException Error writing to wrapped writer
487      */

488     @Override
489     public void println(double x) throws IOException{
490         print(x);
491         println();
492     }
493
494     /**
495      * Print an array of characters and then terminate the line.  This method
496      * behaves as though it invokes <code>{@link #print(char[])}</code> and
497      * then <code>{@link #println()}</code>.
498      *
499      * @param x The <code>char</code> array to be printed
500      * @throws IOException Error writing to wrapped writer
501      */

502     @Override
503     public void println(char x[]) throws IOException {
504         print(x);
505         println();
506     }
507
508     /**
509      * Print a String and then terminate the line.  This method behaves as
510      * though it invokes <code>{@link #print(String)}</code> and then
511      * <code>{@link #println()}</code>.
512      *
513      * @param x The string to be printed
514      * @throws IOException Error writing to wrapped writer
515      */

516     @Override
517     public void println(String x) throws IOException {
518         print(x);
519         println();
520     }
521
522     /**
523      * Print an Object and then terminate the line.  This method behaves as
524      * though it invokes <code>{@link #print(Object)}</code> and then
525      * <code>{@link #println()}</code>.
526      *
527      * @param x The object to be printed
528      * @throws IOException Error writing to wrapped writer
529      */

530     @Override
531     public void println(Object x) throws IOException {
532         print(x);
533         println();
534     }
535
536     /**
537      * Clear the contents of the buffer. If the buffer has been already
538      * been flushed then the clear operation shall throw an IOException
539      * to signal the fact that some data has already been irrevocably
540      * written to the client response stream.
541      *
542      * @throws IOException If there is no wrapped writer
543      */

544     @Override
545     public void clear() throws IOException {
546         if (writer != null) {
547             throw new IOException();
548         } else {
549             nextChar = 0;
550             if (LIMIT_BUFFER && (cb.length > TAG_BUFFER_SIZE)) {
551                 cb = new char[TAG_BUFFER_SIZE];
552                 bufferSize = cb.length;
553             }
554         }
555     }
556
557     /**
558      * Clears the current contents of the buffer. Unlike clear(), this
559      * method will not throw an IOException if the buffer has already been
560      * flushed. It merely clears the current content of the buffer and
561      * returns.
562      *
563      * @throws IOException Should not happen
564      */

565     @Override
566     public void clearBuffer() throws IOException {
567         if (writer == null) {
568             this.clear();
569         }
570     }
571
572     /**
573      * Close the stream, flushing it first.  Once a stream has been closed,
574      * further write() or flush() invocations will cause an IOException to be
575      * thrown.  Closing a previously-closed stream, however, has no effect.
576      *
577      * @throws IOException Error writing to wrapped writer
578      */

579     @Override
580     public void close() throws IOException {
581         if (writer != null) {
582             writer.close();
583         } else {
584             closed = true;
585         }
586     }
587
588     /**
589      * This method returns the size of the buffer used by the JspWriter.
590      *
591      * @return the size of the buffer in bytes, or 0 is unbuffered.
592      */

593     @Override
594     public int getBufferSize() {
595         // According to the spec, the JspWriter returned by
596         // JspContext.pushBody(java.io.Writer writer) must behave as
597         // though it were unbuffered. This means that its getBufferSize()
598         // must always return 0.
599         return (writer == null) ? bufferSize : 0;
600     }
601
602     /**
603      * @return the number of bytes unused in the buffer
604      */

605     @Override
606     public int getRemaining() {
607         return (writer == null) ? bufferSize-nextChar : 0;
608     }
609
610     /**
611      * Return the value of this BodyJspWriter as a Reader.
612      * Note: this is after evaluation!!  There are no scriptlets,
613      * etc in this stream.
614      *
615      * @return the value of this BodyJspWriter as a Reader
616      */

617     @Override
618     public Reader getReader() {
619         return (writer == null) ? new CharArrayReader (cb, 0, nextChar) : null;
620     }
621
622     /**
623      * Return the value of the BodyJspWriter as a String.
624      * Note: this is after evaluation!!  There are no scriptlets,
625      * etc in this stream.
626      *
627      * @return the value of the BodyJspWriter as a String
628      */

629     @Override
630     public String getString() {
631         return (writer == null) ? new String(cb, 0, nextChar) : null;
632     }
633
634     /**
635      * Write the contents of this BodyJspWriter into a Writer.
636      * Subclasses are likely to do interesting things with the
637      * implementation so some things are extra efficient.
638      *
639      * @param out The writer into which to place the contents of this body
640      * evaluation
641      * @throws IOException Error writing to writer
642      */

643     @Override
644     public void writeOut(Writer out) throws IOException {
645         if (writer == null) {
646             out.write(cb, 0, nextChar);
647             // Flush not called as the writer passed could be a BodyContent and
648             // it doesn't allow to flush.
649         }
650     }
651
652     /**
653      * Sets the writer to which all output is written.
654      */

655     void setWriter(Writer writer) {
656         this.writer = writer;
657         closed = false;
658         if (writer == null) {
659             clearBody();
660         }
661     }
662
663     /**
664      * This method shall "reset" the internal state of a BodyContentImpl,
665      * releasing all internal references, and preparing it for potential
666      * reuse by a later invocation of {@link PageContextImpl#pushBody(Writer)}.
667      *
668      * <p>Note, that BodyContentImpl instances are usually owned by a
669      * PageContextImpl instance, and PageContextImpl instances are recycled
670      * and reused.
671      *
672      * @see PageContextImpl#release()
673      */

674     protected void recycle() {
675         this.writer = null;
676         try {
677             this.clear();
678         } catch (IOException ex) {
679             // ignore
680         }
681     }
682
683     private void ensureOpen() throws IOException {
684         if (closed) {
685             throw new IOException(Localizer.getMessage("jsp.error.stream.closed"));
686         }
687     }
688
689     /**
690      * Reallocates buffer since the spec requires it to be unbounded.
691      */

692     private void reAllocBuff(int len) {
693
694         if (bufferSize + len <= cb.length) {
695             bufferSize = cb.length;
696             return;
697         }
698
699         if (len < cb.length) {
700             len = cb.length;
701         }
702
703         char[] tmp = new char[cb.length + len];
704         System.arraycopy(cb, 0, tmp, 0, cb.length);
705         cb = tmp;
706         bufferSize = cb.length;
707     }
708
709
710 }
711