View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.shared.renderkit.html;
20  
21  import java.io.IOException;
22  import java.io.Writer;
23  import java.nio.charset.Charset;
24  import java.util.HashSet;
25  import java.util.Set;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.faces.FacesException;
30  import javax.faces.component.UIComponent;
31  import javax.faces.context.ResponseWriter;
32  
33  import org.apache.myfaces.shared.renderkit.ContentTypeUtils;
34  import org.apache.myfaces.shared.renderkit.RendererUtils;
35  import org.apache.myfaces.shared.renderkit.html.util.UnicodeEncoder;
36  import org.apache.myfaces.shared.util.CommentUtils;
37  import org.apache.myfaces.shared.util.StreamCharBuffer;
38  
39  /**
40   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
41   * @author Anton Koinov
42   * @version $Revision: 1305646 $ $Date: 2012-03-26 17:45:36 -0500 (Mon, 26 Mar 2012) $
43   */
44  public class HtmlResponseWriterImpl
45          extends ResponseWriter
46  {
47      //private static final Log log = LogFactory.getLog(HtmlResponseWriterImpl.class);
48      private static final Logger log = Logger.getLogger(HtmlResponseWriterImpl.class.getName());
49  
50      private static final String DEFAULT_CONTENT_TYPE = "text/html";
51      private static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
52      private static final String UTF8 = "UTF-8";
53  
54      private static final String APPLICATION_XML_CONTENT_TYPE = "application/xml";
55      private static final String TEXT_XML_CONTENT_TYPE = "text/xml";
56      
57      //private boolean _writeDummyForm = false;
58      //private Set _dummyFormParams = null;
59  
60      /**
61       * The writer used as output, or in other words, the one passed on the constructor
62       */
63      private Writer _outputWriter;
64      
65      /**
66       * The writer we are using to store data.
67       */
68      private Writer _currentWriter;
69      
70      /**
71       * The writer used to buffer script and style content
72       */
73      private StreamCharBuffer _buffer;
74      
75      private String _contentType;
76      
77      private String _writerContentTypeMode;
78      
79      /**
80       * This var prevents check if the contentType is for xhtml multiple times.
81       */
82      private boolean _isXhtmlContentType;
83      
84      /**
85       * Indicate the current response writer should not close automatically html elements
86       * and let the writer close them.
87       */
88      private boolean _useStraightXml;
89      
90      private String _characterEncoding;
91      private boolean _wrapScriptContentWithXmlCommentTag;
92      private boolean _isUTF8;
93      
94      private String _startElementName;
95      private Boolean _isInsideScript;
96      private Boolean _isStyle;
97      private Boolean _isTextArea;
98      private UIComponent _startElementUIComponent;
99      private boolean _startTagOpen;
100 
101     private boolean _cdataOpen;
102 
103     private static final Set<String> S_EMPTY_HTML_ELEMENTS = new HashSet<String>();
104 
105     private static final String CDATA_START = "<![CDATA[ \n";
106     private static final String CDATA_START_NO_LINE_RETURN = "<![CDATA[";
107     private static final String COMMENT_START = "<!--\n";
108     private static final String CDATA_COMMENT_START = "//<![CDATA[ \n";
109     private static final String CDATA_COMMENT_END = "\n//]]>";
110     private static final String CDATA_END = "\n]]>";
111     private static final String CDATA_END_NO_LINE_RETURN = "]]>";
112     private static final String COMMENT_COMMENT_END = "\n//-->";
113     private static final String COMMENT_END = "\n-->";
114 
115     static
116     {
117         S_EMPTY_HTML_ELEMENTS.add("area");
118         S_EMPTY_HTML_ELEMENTS.add("br");
119         S_EMPTY_HTML_ELEMENTS.add("base");
120         S_EMPTY_HTML_ELEMENTS.add("basefont");
121         S_EMPTY_HTML_ELEMENTS.add("col");
122         S_EMPTY_HTML_ELEMENTS.add("frame");
123         S_EMPTY_HTML_ELEMENTS.add("hr");
124         S_EMPTY_HTML_ELEMENTS.add("img");
125         S_EMPTY_HTML_ELEMENTS.add("input");
126         S_EMPTY_HTML_ELEMENTS.add("isindex");
127         S_EMPTY_HTML_ELEMENTS.add("link");
128         S_EMPTY_HTML_ELEMENTS.add("meta");
129         S_EMPTY_HTML_ELEMENTS.add("param");
130     }
131 
132     public HtmlResponseWriterImpl(Writer writer, String contentType, String characterEncoding)
133     {
134         this(writer,contentType,characterEncoding,true);
135     }
136 
137     public HtmlResponseWriterImpl(Writer writer, String contentType, String characterEncoding,
138             boolean wrapScriptContentWithXmlCommentTag)
139     {
140         this(writer,contentType, characterEncoding, wrapScriptContentWithXmlCommentTag, 
141                 contentType != null && HtmlRendererUtils.isXHTMLContentType(contentType) ? 
142                     ContentTypeUtils.XHTML_CONTENT_TYPE : ContentTypeUtils.HTML_CONTENT_TYPE);
143     }
144     
145     public HtmlResponseWriterImpl(Writer writer, String contentType, String characterEncoding,
146              boolean wrapScriptContentWithXmlCommentTag, String writerContentTypeMode)
147     throws FacesException
148     {
149         _outputWriter = writer;
150         //The current writer to be used is the one used as output 
151         _currentWriter = _outputWriter;
152         _wrapScriptContentWithXmlCommentTag = wrapScriptContentWithXmlCommentTag;
153         
154         _contentType = contentType;
155         if (_contentType == null)
156         {
157             if (log.isLoggable(Level.FINE))
158             {
159                 log.fine("No content type given, using default content type " + DEFAULT_CONTENT_TYPE);
160             }
161             _contentType = DEFAULT_CONTENT_TYPE;
162         }
163         _writerContentTypeMode = writerContentTypeMode;
164         _isXhtmlContentType = writerContentTypeMode.indexOf(ContentTypeUtils.XHTML_CONTENT_TYPE) != -1;
165         
166         _useStraightXml = _isXhtmlContentType && (_contentType.indexOf(APPLICATION_XML_CONTENT_TYPE) != -1 ||
167                           _contentType.indexOf(TEXT_XML_CONTENT_TYPE) != -1);
168 
169         if (characterEncoding == null)
170         {
171             if (log.isLoggable(Level.FINE))
172             {
173                 log.fine("No character encoding given, using default character encoding " +
174                         DEFAULT_CHARACTER_ENCODING);
175             }
176             _characterEncoding = DEFAULT_CHARACTER_ENCODING;
177         }
178         else
179         {
180             // canonize to uppercase, that's the standard format
181             _characterEncoding = characterEncoding.toUpperCase();
182             
183             // Check if encoding is valid by javadoc of RenderKit.createResponseWriter()
184             if (!Charset.isSupported(_characterEncoding))
185             {
186                 throw new IllegalArgumentException("Encoding "+_characterEncoding
187                         +" not supported by HtmlResponseWriterImpl");
188             }
189         }
190         _isUTF8 = UTF8.equals(_characterEncoding);
191     }
192 
193     public static boolean supportsContentType(String contentType)
194     {
195         String[] supportedContentTypes = HtmlRendererUtils.getSupportedContentTypes();
196 
197         for (int i = 0; i < supportedContentTypes.length; i++)
198         {
199             String supportedContentType = supportedContentTypes[i];
200 
201             if(supportedContentType.indexOf(contentType)!=-1)
202             {
203                 return true;
204             }
205         }
206         return false;
207     }
208 
209     public String getContentType()
210     {
211         return _contentType;
212     }
213     
214     public String getWriterContentTypeMode()
215     {
216         return _writerContentTypeMode;
217     }
218 
219     public String getCharacterEncoding()
220     {
221         return _characterEncoding;
222     }
223 
224     public void flush() throws IOException
225     {
226         // API doc says we should not flush the underlying writer
227         //_writer.flush();
228         // but rather clear any values buffered by this ResponseWriter:
229         closeStartTagIfNecessary();
230     }
231 
232     public void startDocument()
233     {
234         // do nothing
235     }
236 
237     public void endDocument() throws IOException
238     {
239         _currentWriter.flush();
240     }
241 
242     public void startElement(String name, UIComponent uiComponent) throws IOException
243     {
244         if (name == null)
245         {
246             throw new NullPointerException("elementName name must not be null");
247         }
248 
249         closeStartTagIfNecessary();
250         _currentWriter.write('<');
251         _currentWriter.write(name);
252 
253         resetStartedElement();
254 
255         _startElementName = name;
256         _startElementUIComponent = uiComponent;
257         _startTagOpen = true;
258         
259         // Each time we start a element, it is necessary to check <script> or <style>,
260         // because we need to buffer all content to post process it later when it reach its end
261         // according to the initialization properties used.
262         if(isScript(name))
263         {
264             // handle a <script> start
265             _isInsideScript = Boolean.TRUE;
266             _isStyle = Boolean.FALSE;
267             _isTextArea = Boolean.FALSE;
268         }
269         else if (isStyle(name))
270         {
271             _isInsideScript = Boolean.FALSE;
272             _isStyle = Boolean.TRUE;
273             _isTextArea = Boolean.FALSE;
274         }
275     }
276 
277     @Override
278     public void startCDATA() throws IOException
279     {
280         if (!_cdataOpen)
281         {
282             write(CDATA_START_NO_LINE_RETURN);
283             _cdataOpen = true;
284         }
285     }
286 
287     @Override
288     public void endCDATA() throws IOException
289     {
290         if (_cdataOpen)
291         {
292             write(CDATA_END_NO_LINE_RETURN);
293             _cdataOpen = false;
294         }
295     }
296 
297     private void closeStartTagIfNecessary() throws IOException
298     {
299         if (_startTagOpen)
300         {
301             if (!_useStraightXml && S_EMPTY_HTML_ELEMENTS.contains(_startElementName.toLowerCase()))
302             {
303                 _currentWriter.write(" />");
304                 // make null, this will cause NullPointer in some invalid element nestings
305                 // (better than doing nothing)
306                 resetStartedElement();
307             }
308             else
309             {
310                 _currentWriter.write('>');
311 
312                 /*
313                 if(isScript(_startElementName))
314                 {
315                     if(HtmlRendererUtils.isXHTMLContentType(_contentType))
316                     {
317                         if(HtmlRendererUtils.isAllowedCdataSection(FacesContext.getCurrentInstance()))
318                         {
319                             _currentWriter.write(CDATA_START);
320                         }
321                     }
322                     else
323                     {
324                         _currentWriter.write(COMMENT_START);
325                     }
326                 }*/
327                 if (isScript(_startElementName) && (_isXhtmlContentType || _wrapScriptContentWithXmlCommentTag))
328                 {
329                     //_bufferedWriter.reset();
330                     //_currentWriter = _bufferedWriter;
331                     getInternalBuffer(true);
332                     _currentWriter = getInternalBuffer().getWriter();
333                 }                
334                 if (isStyle(_startElementName) && _isXhtmlContentType)
335                 {
336                     //_bufferedWriter.reset();
337                     //_currentWriter = _bufferedWriter;
338                     getInternalBuffer(true);
339                     _currentWriter = getInternalBuffer().getWriter();
340                 }
341             }
342             _startTagOpen = false;
343         }
344     }
345 
346     private void resetStartedElement()
347     {
348         _startElementName = null;
349         _startElementUIComponent = null;
350         _isStyle = null;
351         _isTextArea = null;
352     }
353 
354     public void endElement(String name) throws IOException
355     {
356         if (name == null)
357         {
358             throw new NullPointerException("elementName name must not be null");
359         }
360 
361         if (log.isLoggable(Level.WARNING))
362         {
363             if (_startElementName != null &&
364                 !name.equals(_startElementName))
365             {
366                 log.warning("HTML nesting warning on closing " + name + ": element " + _startElementName +
367                         (_startElementUIComponent==null?"":(" rendered by component : "+
368                         RendererUtils.getPathToComponent(_startElementUIComponent)))+" not explicitly closed");
369             }
370         }
371 
372         if(_startTagOpen)
373         {
374 
375             // we will get here only if no text or attribute was written after the start element was opened
376             // now we close out the started tag - if it is an empty tag, this is then fully closed
377             closeStartTagIfNecessary();
378 
379             //tag was no empty tag - it has no accompanying end tag now.
380             if(_startElementName!=null)
381             {
382                 if (isScript() && (_isXhtmlContentType || _wrapScriptContentWithXmlCommentTag))
383                 {
384                     writeScriptContent();
385                     _currentWriter = _outputWriter;
386                 }
387                 else if (isStyle() && _isXhtmlContentType)
388                 {
389                     writeStyleContent();
390                     _currentWriter = _outputWriter;
391                 }
392 
393                 //write closing tag
394                 writeEndTag(name);
395             }
396         }
397         else
398         {
399             if (!_useStraightXml && S_EMPTY_HTML_ELEMENTS.contains(name.toLowerCase()))
400             {
401            /*
402            Should this be here?  It warns even when you have an x:htmlTag value="br", it should just close.
403 
404                 if (log.isWarnEnabled())
405                     log.warn("HTML nesting warning on closing " + name + 
406                         ": This element must not contain nested elements or text in HTML");
407                     */
408             }
409             else
410             {
411                 if (isScript() && (_isXhtmlContentType || _wrapScriptContentWithXmlCommentTag))
412                 {
413                     writeScriptContent();
414                     _currentWriter = _outputWriter;
415                 }
416                 else if (isStyle() && _isXhtmlContentType)
417                 {
418                     writeStyleContent();
419                     _currentWriter = _outputWriter;
420                 }
421                 writeEndTag(name);
422             }
423         }
424 
425         resetStartedElement();
426     }
427 
428 
429     
430     private void writeStyleContent() throws IOException
431     {
432         String content = getInternalBuffer().toString();
433         
434         if(_isXhtmlContentType)
435         {
436             // In xhtml, the content inside <style> tag is PCDATA, but
437             // in html the content is CDATA, so in order to preserve 
438             // compatibility we need to wrap the content inside proper
439             // CDATA tags.
440             // Since the response content type is xhtml, we can use
441             // simple CDATA without comments, but note we need to check
442             // when we are using any valid notation (simple CDATA, commented CDATA, xml comment) 
443             String trimmedContent = content.trim();
444             if (trimmedContent.startsWith(CommentUtils.CDATA_SIMPLE_START) && trimmedContent.endsWith(
445                     CommentUtils.CDATA_SIMPLE_END))
446             {
447                 _outputWriter.write(content);
448                 return;
449             }
450             else if (CommentUtils.isStartMatchWithCommentedCDATA(trimmedContent) && 
451                     CommentUtils.isEndMatchWithCommentedCDATA(trimmedContent))
452             {
453                 _outputWriter.write(content);
454                 return;
455             }
456             else if (trimmedContent.startsWith(CommentUtils.COMMENT_SIMPLE_START) && 
457                     trimmedContent.endsWith(CommentUtils.COMMENT_SIMPLE_END))
458             {
459                 //Use comment wrap is valid, but for xhtml it is preferred to use CDATA
460                 _outputWriter.write(CDATA_START);
461                 _outputWriter.write(trimmedContent.substring(4,trimmedContent.length()-3));
462                 _outputWriter.write(CDATA_END);
463                 return;
464             }
465             else
466             {
467                 _outputWriter.write(CDATA_START);
468                 _outputWriter.write(content);
469                 _outputWriter.write(CDATA_END);
470                 return;                
471             }
472         }
473         // If the response is handled as text/html, 
474         // it is not necessary to wrap with xml comment tag,
475         // so we can just write the content as is.
476         _outputWriter.write(content);
477     }
478     
479     private void writeScriptContent() throws IOException
480     {
481         String content = getInternalBuffer().toString();
482         String trimmedContent = null;
483         
484         if(_isXhtmlContentType)
485         {
486             trimmedContent = content.trim();
487             
488             if ( trimmedContent.startsWith(CommentUtils.COMMENT_SIMPLE_START) && 
489                     CommentUtils.isEndMatchtWithInlineCommentedXmlCommentTag(trimmedContent))
490             {
491                 // In xhtml use xml comment to wrap is invalid, so it is only required to remove the <!--
492                 // the ending //--> will be parsed as a comment, so it will not be a problem. Let it on the content.
493                 if (_cdataOpen)
494                 {
495                     _outputWriter.write("//\n");
496                 }
497                 else
498                 {
499                    _outputWriter.write(CDATA_COMMENT_START);
500                 }
501 
502                 _outputWriter.write(trimmedContent.substring(4));
503 
504                 if (_cdataOpen)
505                 {
506                     _outputWriter.write("\n");
507                 }
508                 else
509                 {
510                     _outputWriter.write(CDATA_COMMENT_END);
511                 }
512                 
513                 return;
514             }
515             else if (CommentUtils.isStartMatchWithCommentedCDATA(trimmedContent) && 
516                     CommentUtils.isEndMatchWithCommentedCDATA(trimmedContent))
517             {
518                 _outputWriter.write(content);
519                 return;
520             }
521             else if (CommentUtils.isStartMatchWithInlineCommentedCDATA(trimmedContent) && 
522                     CommentUtils.isEndMatchWithInlineCommentedCDATA(trimmedContent))
523             {
524                 _outputWriter.write(content);
525                 return;
526             }
527             else
528             {
529                 // <script> in xhtml has as content type PCDATA, but in html it is CDATA,
530                 // so we need to wrap here to prevent problems.
531                 if (_cdataOpen)
532                 {
533                     _outputWriter.write("//\n");
534                 }
535                 else
536                 {
537                    _outputWriter.write(CDATA_COMMENT_START);
538                 }
539 
540                 _outputWriter.write(content);
541 
542                 if (_cdataOpen)
543                 {
544                     _outputWriter.write("\n");
545                 }
546                 else
547                 {
548                     _outputWriter.write(CDATA_COMMENT_END);
549                 }
550 
551                 return;
552             }
553         }
554         else
555         {
556             if (_wrapScriptContentWithXmlCommentTag)
557             {
558                 trimmedContent = content.trim();
559                 
560                 if ( trimmedContent.startsWith(CommentUtils.COMMENT_SIMPLE_START) && 
561                         CommentUtils.isEndMatchtWithInlineCommentedXmlCommentTag(trimmedContent))
562                 {
563                     _outputWriter.write(content);
564                     return;
565                 }
566                 else if (CommentUtils.isStartMatchWithCommentedCDATA(trimmedContent) && 
567                         CommentUtils.isEndMatchWithCommentedCDATA(trimmedContent))
568                 {
569                     _outputWriter.write(content);
570                     return;
571                 }
572                 else if (CommentUtils.isStartMatchWithInlineCommentedCDATA(trimmedContent) && 
573                         CommentUtils.isEndMatchWithInlineCommentedCDATA(trimmedContent))
574                 {
575                     _outputWriter.write(content);
576                     return;
577                 }
578                 else
579                 {
580                     _outputWriter.write(COMMENT_START);
581                     _outputWriter.write(content);
582                     _outputWriter.write(COMMENT_COMMENT_END);
583                     return;
584                 }
585             }
586         }
587         
588         //If no return, just write everything
589         _outputWriter.write(content);
590     }
591     
592 
593     private void writeEndTag(String name)
594         throws IOException
595     {
596         /*
597         if(isScript(name)) 
598         {
599             if(HtmlRendererUtils.isXHTMLContentType(_contentType))
600             {
601                 if(HtmlRendererUtils.isAllowedCdataSection(FacesContext.getCurrentInstance()))
602                 {
603                     _currentWriter.write(CDATA_COMMENT_END);
604                 }
605             }
606             else
607             {
608                 _currentWriter.write(COMMENT_COMMENT_END);
609             }
610             
611             // reset _isInsideScript
612             _isInsideScript = Boolean.FALSE;
613         }*/
614         
615         if (isScript(name))
616         {
617             // reset _isInsideScript
618             _isInsideScript = Boolean.FALSE;
619         }
620         else if (isStyle(name))
621         {
622             _isStyle = Boolean.FALSE;
623         }
624 
625         _currentWriter.write("</");
626         _currentWriter.write(name);
627         _currentWriter.write('>');
628     }
629 
630     public void writeAttribute(String name, Object value, String componentPropertyName) throws IOException
631     {
632         if (name == null)
633         {
634             throw new NullPointerException("attributeName name must not be null");
635         }
636         if (!_startTagOpen)
637         {
638             throw new IllegalStateException("Must be called before the start element is closed (attribute '"
639                     + name + "')");
640         }
641 
642         if (value instanceof Boolean)
643         {
644             if (((Boolean)value).booleanValue())
645             {
646                 // name as value for XHTML compatibility
647                 _currentWriter.write(' ');
648                 _currentWriter.write(name);
649                 _currentWriter.write("=\"");
650                 _currentWriter.write(name);
651                 _currentWriter.write('"');
652             }
653         }
654         else
655         {
656             String strValue = (value==null)?"":value.toString();
657             _currentWriter.write(' ');
658             _currentWriter.write(name);
659             _currentWriter.write("=\"");
660             org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(_currentWriter,
661                     strValue, false, false, !_isUTF8);
662             _currentWriter.write('"');
663         }
664     }
665 
666     public void writeURIAttribute(String name, Object value, String componentPropertyName) throws IOException
667     {
668         if (name == null)
669         {
670             throw new NullPointerException("attributeName name must not be null");
671         }
672         if (!_startTagOpen)
673         {
674             throw new IllegalStateException("Must be called before the start element is closed (attribute '"
675                     + name + "')");
676         }
677 
678         String strValue = value.toString();
679         _currentWriter.write(' ');
680         _currentWriter.write(name);
681         _currentWriter.write("=\"");
682         if (strValue.toLowerCase().startsWith("javascript:"))
683         {
684             org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(_currentWriter,
685                     strValue, false, false, !_isUTF8);
686         }
687         else
688         {
689             /*
690             Todo: what is this section about? still needed?
691             client side state saving is now done via javascript...
692 
693             if (_startElementName.equalsIgnoreCase(HTML.ANCHOR_ELEM) && //Also support image and button urls?
694                 name.equalsIgnoreCase(HTML.HREF_ATTR) &&
695                 !strValue.startsWith("#"))
696             {
697                 FacesContext facesContext = FacesContext.getCurrentInstance();
698                 if (facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext))
699                 {
700                     // saving state in url depends on the work together
701                     // of 3 (theoretically) pluggable components:
702                     // ViewHandler, ResponseWriter and ViewTag
703                     // We should try to make this HtmlResponseWriterImpl able
704                     // to handle this alone!
705                     if (strValue.indexOf('?') < 0)
706                     {
707                         strValue = strValue + '?' + JspViewHandlerImpl.URL_STATE_MARKER;
708                     }
709                     else
710                     {
711                         strValue = strValue + '&' + JspViewHandlerImpl.URL_STATE_MARKER;
712                     }
713                 }
714             }
715             */
716             //_writer.write(strValue);
717             org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encodeURIAtributte(_currentWriter,
718                             strValue, _characterEncoding);
719         }
720         _currentWriter.write('"');
721     }
722 
723     public void writeComment(Object value) throws IOException
724     {
725         if (value == null)
726         {
727             throw new NullPointerException("comment name must not be null");
728         }
729 
730         closeStartTagIfNecessary();
731         _currentWriter.write("<!--");
732         _currentWriter.write(value.toString());    //TODO: Escaping: must not have "-->" inside!
733         _currentWriter.write("-->");
734     }
735 
736     public void writeText(Object value, String componentPropertyName) throws IOException
737     {
738         if (value == null)
739         {
740             throw new NullPointerException("Text must not be null.");
741         }
742 
743         closeStartTagIfNecessary();
744 
745         String strValue = value.toString();
746 
747         if (isScriptOrStyle())
748         {
749             // Don't bother encoding anything if chosen character encoding is UTF-8
750             if (_isUTF8)
751             {
752                 _currentWriter.write(strValue);
753             }
754             else
755             {
756                 UnicodeEncoder.encode(_currentWriter, strValue);
757             }
758         }
759         else
760         {
761             org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(_currentWriter,
762                       strValue, false, false, !_isUTF8);
763         }
764     }
765 
766     public void writeText(char cbuf[], int off, int len) throws IOException
767     {
768         if (cbuf == null)
769         {
770             throw new NullPointerException("cbuf name must not be null");
771         }
772         if (cbuf.length < off + len)
773         {
774             throw new IndexOutOfBoundsException((off + len) + " > " + cbuf.length);
775         }
776 
777         closeStartTagIfNecessary();
778 
779         if (isScriptOrStyle())
780         {
781             String strValue = new String(cbuf, off, len);
782             // Don't bother encoding anything if chosen character encoding is UTF-8
783             if (_isUTF8)
784             {
785                 _currentWriter.write(strValue);
786             }
787             else
788             {
789                 UnicodeEncoder.encode(_currentWriter, strValue);
790             }
791         }
792         else if (isTextarea())
793         {
794             // For textareas we must *not* map successive spaces to &nbsp or Newlines to <br/>
795             org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(
796                     cbuf, off, len, false, false, !_isUTF8, _currentWriter);
797         }
798         else
799         {
800             // We map successive spaces to &nbsp; and Newlines to <br/>
801             org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(
802                     cbuf, off, len, true, true, !_isUTF8, _currentWriter);
803         }
804     }
805 
806     private boolean isScriptOrStyle()
807     {
808         //initializeStartedTagInfo();
809 
810         return (_isStyle != null && _isStyle.booleanValue()) ||
811                 (_isInsideScript != null && _isInsideScript.booleanValue());
812     }
813     
814     /**
815      * Is the given element a script tag?
816      * @param element
817      * @return
818      */
819     private boolean isScript(String element)
820     {
821         return (HTML.SCRIPT_ELEM.equalsIgnoreCase(element));
822     }
823     
824     private boolean isScript()
825     {
826         return (_isInsideScript != null && _isInsideScript.booleanValue());
827     }
828     
829     private boolean isStyle(String element)
830     {
831         return (HTML.STYLE_ELEM.equalsIgnoreCase(element));
832     }
833     
834     private boolean isStyle()
835     {
836         return (_isStyle != null && _isStyle.booleanValue());
837     }
838 
839     private boolean isTextarea()
840     {
841         initializeStartedTagInfo();
842 
843         return _isTextArea != null && _isTextArea.booleanValue();
844     }
845 
846     private void initializeStartedTagInfo()
847     {
848         if(_startElementName != null)
849         {
850             /*
851             if(_isStyle == null)
852             {
853                 if(_startElementName.equalsIgnoreCase(org.apache.myfaces.shared.renderkit.html.HTML.STYLE_ELEM))
854                 {
855                     _isStyle = Boolean.TRUE;
856                     _isTextArea = Boolean.FALSE;
857                 }
858                 else
859                 {
860                     _isStyle = Boolean.FALSE;
861                 }
862             }*/
863 
864             if(_isTextArea == null)
865             {
866                 if(_startElementName.equalsIgnoreCase(HTML.TEXTAREA_ELEM))
867                 {
868                     _isTextArea = Boolean.TRUE;
869                 }
870                 else
871                 {
872                     _isTextArea = Boolean.FALSE;
873                 }
874             }
875         }
876     }
877 
878     public ResponseWriter cloneWithWriter(Writer writer)
879     {
880         HtmlResponseWriterImpl newWriter
881                 = new HtmlResponseWriterImpl(writer, getContentType(), getCharacterEncoding(), 
882                         _wrapScriptContentWithXmlCommentTag, _writerContentTypeMode);
883         //newWriter._writeDummyForm = _writeDummyForm;
884         //newWriter._dummyFormParams = _dummyFormParams;
885         return newWriter;
886     }
887 
888 
889     // Writer methods
890 
891     public void close() throws IOException
892     {
893         closeStartTagIfNecessary();
894         _currentWriter.close();
895     }
896 
897     public void write(char cbuf[], int off, int len) throws IOException
898     {
899         closeStartTagIfNecessary();
900         // Don't bother encoding anything if chosen character encoding is UTF-8
901         if (_isUTF8)
902         {
903             _currentWriter.write(cbuf, off, len);
904         }
905         else
906         {
907             UnicodeEncoder.encode(_currentWriter, cbuf, off, len);
908         }
909     }
910 
911     public void write(int c) throws IOException
912     {
913         closeStartTagIfNecessary();
914         _currentWriter.write(c);
915     }
916 
917     public void write(char cbuf[]) throws IOException
918     {
919         closeStartTagIfNecessary();
920         // Don't bother encoding anything if chosen character encoding is UTF-8
921         if (_isUTF8)
922         {
923             _currentWriter.write(cbuf);
924         }
925         else
926         {
927             UnicodeEncoder.encode(_currentWriter, cbuf, 0, cbuf.length);
928         }
929     }
930 
931     public void write(String str) throws IOException
932     {
933         closeStartTagIfNecessary();
934         // empty string commonly used to force the start tag to be closed.
935         // in such case, do not call down the writer chain
936         if (str.length() > 0)
937         {
938             // Don't bother encoding anything if chosen character encoding is UTF-8
939             if (_isUTF8)
940             {
941                 _currentWriter.write(str);
942             }
943             else
944             {
945                 UnicodeEncoder.encode(_currentWriter, str);
946             }
947         }
948     }
949 
950     public void write(String str, int off, int len) throws IOException
951     {
952         closeStartTagIfNecessary();
953         // Don't bother encoding anything if chosen character encoding is UTF-8
954         if (_isUTF8)
955         {
956             _currentWriter.write(str, off, len);
957         }
958         else
959         {
960             UnicodeEncoder.encode(_currentWriter, str, off, len);
961         }
962     }
963     
964     /**
965      * This method ignores the <code>UIComponent</code> provided and simply calls
966      * <code>writeText(Object,String)</code>
967      * @since 1.2
968      */
969     public void writeText(Object object, UIComponent component, String string) throws IOException
970     {
971         writeText(object,string);
972     }
973     
974     protected StreamCharBuffer getInternalBuffer()
975     {
976         return getInternalBuffer(false);
977     }
978     
979     protected StreamCharBuffer getInternalBuffer(boolean reset)
980     {
981         if (_buffer == null)
982         {
983             _buffer = new StreamCharBuffer(256, 100);
984         }
985         else if (reset)
986         {
987             _buffer.reset();
988         }
989         return _buffer;
990     }
991 }