KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > myfaces > renderkit > html > HtmlResponseWriterImpl


1 /*
2  * Copyright 2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.myfaces.renderkit.html;
17
18 import org.apache.myfaces.config.MyfacesConfig;
19 import org.apache.myfaces.renderkit.html.util.DummyFormResponseWriter;
20 import org.apache.myfaces.renderkit.html.util.DummyFormUtils;
21 import org.apache.myfaces.renderkit.html.util.HTMLEncoder;
22 import org.apache.myfaces.renderkit.html.util.JavascriptUtils;
23 import org.apache.myfaces.renderkit.html.util.UnicodeEncoder;
24 import org.apache.myfaces.renderkit.RendererUtils;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import javax.faces.component.UIComponent;
29 import javax.faces.context.FacesContext;
30 import javax.faces.context.ResponseWriter;
31 import java.io.IOException JavaDoc;
32 import java.io.Writer JavaDoc;
33 import java.util.HashSet JavaDoc;
34 import java.util.Set JavaDoc;
35
36 /**
37  * @author Manfred Geiler (latest modification by $Author: svieujot $)
38  * @author Anton Koinov
39  * @version $Revision: 1.1 $ $Date: 2005/02/01 16:51:48 $
40  * $Log: HtmlResponseWriterImpl.java,v $
41  * Revision 1.1 2005/02/01 16:51:48 svieujot
42  * Move the HtmlResponseWriterImpl to the shared sources directory.
43  *
44  * Revision 1.36 2005/01/31 17:43:11 svieujot
45  * Bugfix for HtmlResponseWriterImpl.write(str, offset, length).
46  *
47  * Revision 1.35 2005/01/31 17:03:54 svieujot
48  * Resynchronize the HtmlResponseWriterImpl from the renderkit, and from the x:buffer component.
49  *
50  * Revision 1.34 2005/01/29 10:04:25 matzew
51  * MYFACES-91 patch by Jason Hoo. Thanks
52  *
53  * Revision 1.33 2005/01/19 13:18:04 mmarinschek
54  * better logging of component information
55  *
56  * Revision 1.32 2005/01/04 15:41:06 svieujot
57  * new x:buffer component.
58  *
59  * Revision 1.31 2004/12/27 04:11:11 mmarinschek
60  * Data Table stores the state of facets of children; script tag is rendered with type attribute instead of language attribute, popup works better as a column in a data table
61  *
62  * Revision 1.30 2004/12/17 22:06:32 grantsmith
63  * Jira MYFACES-57: Changed logging levels to DEBUG from INFO
64  *
65  * Revision 1.29 2004/10/24 23:30:35 oros
66  * do not convert newline to <br> and space to &nbps; as this is not required by the spec
67  *
68  * Revision 1.28 2004/10/13 11:51:00 matze
69  * renamed packages to org.apache
70  *
71  * Revision 1.27 2004/10/05 08:49:15 manolito
72  * #1038697 h:selectOneRadio generates malformed XHTML
73  *
74  * Revision 1.26 2004/10/05 08:32:23 manolito
75  * #1038716 Empty h:selectManyCheckbox generates malformed HTML
76  *
77  * Revision 1.25 2004/09/09 13:15:44 manolito
78  * For textareas we must *not* map successive spaces to nbsp
79  *
80  * Revision 1.24 2004/09/08 15:23:10 manolito
81  * Autoscroll feature
82  *
83  * Revision 1.23 2004/09/08 09:30:01 manolito
84  * moved javascript detection to ResponseWriter
85  *
86  * Revision 1.22 2004/08/20 00:13:55 dave0000
87  * remove unused constant
88  *
89  * Revision 1.21 2004/08/18 17:56:58 manolito
90  * no newline to <br/> mapping for TEXTAREA elements
91  *
92  * Revision 1.20 2004/08/18 16:13:06 manolito
93  * writeText method in HtmlResponseWriterImpl now encodes Newlines and successive spaces
94  *
95  * Revision 1.19 2004/07/01 22:05:06 mwessendorf
96  * ASF switch
97  *
98  * Revision 1.18 2004/04/29 14:59:42 manolito
99  * writeURIAttribute no longer adds state saving url parameters
100  *
101  */

102 public class HtmlResponseWriterImpl
103         extends ResponseWriter
104         implements DummyFormResponseWriter
105 {
106     private static final Log log = LogFactory.getLog(HtmlResponseWriterImpl.class);
107
108     private static final String JavaDoc DEFAULT_CONTENT_TYPE = "text/html";
109     private static final String JavaDoc DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
110
111     private boolean _writeDummyForm = false;
112     private Set JavaDoc _dummyFormParams = null;
113
114     private Writer _writer;
115     private String JavaDoc _contentType;
116     private String JavaDoc _characterEncoding;
117     private String JavaDoc _startElementName;
118     private UIComponent _startElementUIComponent;
119     private boolean _startTagOpen;
120
121     private static final Set JavaDoc s_emptyHtmlElements = new HashSet JavaDoc();
122
123     static
124     {
125         s_emptyHtmlElements.add("area");
126         s_emptyHtmlElements.add("br");
127         s_emptyHtmlElements.add("base");
128         s_emptyHtmlElements.add("basefont");
129         s_emptyHtmlElements.add("col");
130         s_emptyHtmlElements.add("frame");
131         s_emptyHtmlElements.add("hr");
132         s_emptyHtmlElements.add("img");
133         s_emptyHtmlElements.add("input");
134         s_emptyHtmlElements.add("isindex");
135         s_emptyHtmlElements.add("link");
136         s_emptyHtmlElements.add("meta");
137         s_emptyHtmlElements.add("param");
138     }
139
140     public HtmlResponseWriterImpl(Writer writer, String JavaDoc contentType, String JavaDoc characterEncoding)
141     {
142         _writer = writer;
143         _contentType = contentType;
144         if (_contentType == null)
145         {
146             if (log.isInfoEnabled()) log.debug("No content type given, using default content type " + DEFAULT_CONTENT_TYPE);
147             _contentType = DEFAULT_CONTENT_TYPE;
148         }
149         _characterEncoding = characterEncoding;
150         if (_characterEncoding == null)
151         {
152             if (log.isInfoEnabled()) log.debug("No character encoding given, using default character encoding " + DEFAULT_CHARACTER_ENCODING);
153             _characterEncoding = DEFAULT_CHARACTER_ENCODING;
154         }
155     }
156
157     public static boolean supportsContentType(String JavaDoc contentType)
158     {
159         //return SUPPORTED_CONTENT_TYPES.contains(contentType); //TODO: Match according to Section 14.1 of RFC 2616
160
return true;
161     }
162
163     public String JavaDoc getContentType()
164     {
165         return _contentType;
166     }
167
168     public String JavaDoc getCharacterEncoding()
169     {
170         return _characterEncoding;
171     }
172
173     public void flush() throws IOException JavaDoc
174     {
175         // API doc says we should not flush the underlying writer
176
//_writer.flush();
177
// but rather clear any values buffered by this ResponseWriter:
178
closeStartTagIfNecessary();
179     }
180
181     public void startDocument()
182     {
183         // do nothing
184
}
185
186     public void endDocument() throws IOException JavaDoc
187     {
188         flush();
189         if (_writeDummyForm)
190         {
191             DummyFormUtils.writeDummyForm(this, _dummyFormParams);
192         }
193
194         FacesContext facesContext = FacesContext.getCurrentInstance();
195         MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext());
196         if (myfacesConfig.isDetectJavascript())
197         {
198             if (! JavascriptUtils.isJavascriptDetected(facesContext.getExternalContext()))
199             {
200                 write("<script type=\"text/javascript\">\n<!--\ndocument.location.replace('" + facesContext.getApplication().getViewHandler().getResourceURL(facesContext, "/_javascriptDetector_") + "?goto=" + facesContext.getApplication().getViewHandler().getActionURL(facesContext, facesContext.getViewRoot().getViewId()) +"');\n//-->\n</script>");
201             }
202         }
203
204         if (myfacesConfig.isAutoScroll())
205         {
206             JavascriptUtils.renderAutoScrollFunction(facesContext, this);
207         }
208
209         _writer.flush();
210     }
211
212     public void startElement(String JavaDoc name, UIComponent uiComponent) throws IOException JavaDoc
213     {
214         if (name == null)
215         {
216             throw new NullPointerException JavaDoc("elementName name must not be null");
217         }
218
219         closeStartTagIfNecessary();
220         _writer.write('<');
221         _writer.write(name);
222         _startElementName = name;
223         _startElementUIComponent = uiComponent;
224         _startTagOpen = true;
225     }
226
227     private void closeStartTagIfNecessary() throws IOException JavaDoc
228     {
229         if (_startTagOpen)
230         {
231             if (s_emptyHtmlElements.contains(_startElementName.toLowerCase()))
232             {
233                 _writer.write("/>");
234                 // make null, this will cause NullPointer in some invalid element nestings
235
// (better than doing nothing)
236
_startElementName = null;
237             }
238             else
239             {
240                 _writer.write('>');
241             }
242             _startTagOpen = false;
243         }
244     }
245
246     public void endElement(String JavaDoc name) throws IOException JavaDoc
247     {
248         if (name == null)
249         {
250             throw new NullPointerException JavaDoc("elementName name must not be null");
251         }
252
253         if (log.isWarnEnabled())
254         {
255             if (_startElementName != null &&
256                 !name.equals(_startElementName))
257             {
258                 if (log.isWarnEnabled())
259                     log.warn("HTML nesting warning on closing " + name + ": element " + _startElementName +
260                             (_startElementUIComponent==null?"":(" rendered by component : "+
261                             RendererUtils.getPathToComponent(_startElementUIComponent)))+" not explicitly closed");
262             }
263         }
264
265         if(_startTagOpen)
266         {
267             // we will get here only if no text or attribute was written after the start element was opened
268
if (s_emptyHtmlElements.contains(name.toLowerCase()))
269             {
270                 _writer.write("/>");
271             }
272             else
273             {
274                 _writer.write("></");
275                 _writer.write(name);
276                 _writer.write('>');
277             }
278             _startTagOpen = false;
279         }
280         else
281         {
282             if (s_emptyHtmlElements.contains(name.toLowerCase()))
283             {
284                 if (log.isWarnEnabled())
285                     log.warn("HTML nesting warning on closing " + name + ": This element must not contain nested elements or text in HTML");
286             }
287             else
288             {
289                 _writer.write("</");
290                 _writer.write(name);
291                 _writer.write('>');
292             }
293         }
294
295         _startElementName = null;
296         _startElementUIComponent = null;
297     }
298
299     public void writeAttribute(String JavaDoc name, Object JavaDoc value, String JavaDoc componentPropertyName) throws IOException JavaDoc
300     {
301         if (name == null)
302         {
303             throw new NullPointerException JavaDoc("attributeName name must not be null");
304         }
305         if (!_startTagOpen)
306         {
307             throw new IllegalStateException JavaDoc("Must be called before the start element is closed (attribute '" + name + "')");
308         }
309
310         if (value instanceof Boolean JavaDoc)
311         {
312             if (((Boolean JavaDoc)value).booleanValue())
313             {
314                 // name as value for XHTML compatibility
315
_writer.write(' ');
316                 _writer.write(name);
317                 _writer.write("=\"");
318                 _writer.write(name);
319                 _writer.write('"');
320             }
321         }
322         else
323         {
324             String JavaDoc strValue = value.toString(); //TODO: Use converter for value
325
_writer.write(' ');
326             _writer.write(name);
327             _writer.write("=\"");
328             _writer.write(HTMLEncoder.encode(strValue, false, false));
329             _writer.write('"');
330         }
331     }
332
333     public void writeURIAttribute(String JavaDoc name, Object JavaDoc value, String JavaDoc componentPropertyName) throws IOException JavaDoc
334     {
335         if (name == null)
336         {
337             throw new NullPointerException JavaDoc("attributeName name must not be null");
338         }
339         if (!_startTagOpen)
340         {
341             throw new IllegalStateException JavaDoc("Must be called before the start element is closed (attribute '" + name + "')");
342         }
343
344         String JavaDoc strValue = value.toString(); //TODO: Use converter for value?
345
_writer.write(' ');
346         _writer.write(name);
347         _writer.write("=\"");
348         if (strValue.toLowerCase().startsWith("javascript:"))
349         {
350             _writer.write(HTMLEncoder.encode(strValue, false, false));
351         }
352         else
353         {
354             /*
355             if (_startElementName.equalsIgnoreCase(HTML.ANCHOR_ELEM) && //TODO: Also support image and button urls ?
356                 name.equalsIgnoreCase(HTML.HREF_ATTR) &&
357                 !strValue.startsWith("#"))
358             {
359                 FacesContext facesContext = FacesContext.getCurrentInstance();
360                 if (facesContext.getApplication().getStateManager().isSavingStateInClient(facesContext))
361                 {
362                     //TODO/HACK: saving state in url depends on the work together
363                     // of 3 (theoretically) pluggable components:
364                     // ViewHandler, ResponseWriter and ViewTag
365                     // We should try to make this HtmlResponseWriterImpl able
366                     // to handle this alone!
367                     if (strValue.indexOf('?') < 0)
368                     {
369                         strValue = strValue + '?' + JspViewHandlerImpl.URL_STATE_MARKER;
370                     }
371                     else
372                     {
373                         strValue = strValue + '&' + JspViewHandlerImpl.URL_STATE_MARKER;
374                     }
375                 }
376             }
377             */

378             _writer.write(strValue);
379         }
380         _writer.write('"');
381     }
382
383     public void writeComment(Object JavaDoc value) throws IOException JavaDoc
384     {
385         if (value == null)
386         {
387             throw new NullPointerException JavaDoc("comment name must not be null");
388         }
389
390         closeStartTagIfNecessary();
391         _writer.write("<!--");
392         _writer.write(value.toString()); //TODO: Escaping: must not have "-->" inside!
393
_writer.write("-->");
394     }
395
396     public void writeText(Object JavaDoc value, String JavaDoc componentPropertyName) throws IOException JavaDoc
397     {
398         if (value == null)
399         {
400             throw new NullPointerException JavaDoc("text name must not be null");
401         }
402
403         closeStartTagIfNecessary();
404         if(value == null)
405             return;
406
407         String JavaDoc strValue = value.toString(); //TODO: Use converter for value?
408

409         if (isScriptOrStyle())
410         {
411             _writer.write(UnicodeEncoder.encode(strValue, false, false));
412         }
413         else
414         {
415             _writer.write(HTMLEncoder.encode(strValue, false, false));
416         }
417     }
418
419     public void writeText(char cbuf[], int off, int len) throws IOException JavaDoc
420     {
421         if (cbuf == null)
422         {
423             throw new NullPointerException JavaDoc("cbuf name must not be null");
424         }
425         if (cbuf.length < off + len)
426         {
427             throw new IndexOutOfBoundsException JavaDoc((off + len) + " > " + cbuf.length);
428         }
429
430         closeStartTagIfNecessary();
431
432         if (isScriptOrStyle())
433         {
434             String JavaDoc strValue = new String JavaDoc(cbuf, off, len);
435             _writer.write(UnicodeEncoder.encode(strValue, false, false));
436         }
437         else if (isTextarea())
438         {
439             // For textareas we must *not* map successive spaces to &nbsp or Newlines to <br/>
440
// TODO: Make HTMLEncoder support char arrays directly
441
String JavaDoc strValue = new String JavaDoc(cbuf, off, len);
442             _writer.write(HTMLEncoder.encode(strValue, false, false));
443         }
444         else
445         {
446             // We map successive spaces to &nbsp; and Newlines to <br/>
447
// TODO: Make HTMLEncoder support char arrays directly
448
String JavaDoc strValue = new String JavaDoc(cbuf, off, len);
449             _writer.write(HTMLEncoder.encode(strValue, true, true));
450         }
451     }
452
453     private boolean isScriptOrStyle()
454     {
455         return _startElementName != null &&
456                (_startElementName.equalsIgnoreCase(HTML.SCRIPT_ELEM) ||
457                 _startElementName.equalsIgnoreCase(HTML.STYLE_ELEM));
458     }
459
460     private boolean isTextarea()
461     {
462         return _startElementName != null &&
463                (_startElementName.equalsIgnoreCase(HTML.TEXTAREA_ELEM));
464     }
465
466
467     public ResponseWriter cloneWithWriter(Writer writer)
468     {
469         HtmlResponseWriterImpl newWriter
470                 = new HtmlResponseWriterImpl(writer, getContentType(), getCharacterEncoding());
471         newWriter._writeDummyForm = _writeDummyForm;
472         newWriter._dummyFormParams = _dummyFormParams;
473         return newWriter;
474     }
475
476
477     // Writer methods
478

479     public void close() throws IOException JavaDoc
480     {
481         if (_startTagOpen)
482         {
483             // we will get here only if no text was written after the start element was opened
484
_writer.write(" />");
485         }
486         _writer.close();
487     }
488
489     public void write(char cbuf[], int off, int len) throws IOException JavaDoc
490     {
491         closeStartTagIfNecessary();
492         String JavaDoc strValue = new String JavaDoc(cbuf, off, len);
493         _writer.write(UnicodeEncoder.encode(strValue, false, false));
494     }
495
496     public void write(int c) throws IOException JavaDoc
497     {
498         closeStartTagIfNecessary();
499         _writer.write(c);
500     }
501
502     public void write(char cbuf[]) throws IOException JavaDoc
503     {
504         closeStartTagIfNecessary();
505         String JavaDoc strValue = new String JavaDoc(cbuf);
506         _writer.write(UnicodeEncoder.encode(strValue, false, false));
507     }
508
509     public void write(String JavaDoc str) throws IOException JavaDoc
510     {
511         closeStartTagIfNecessary();
512         // empty string commonly used to force the start tag to be closed.
513
// in such case, do not call down the writer chain
514
if (str.length() > 0)
515         {
516             _writer.write(UnicodeEncoder.encode(str, false, false));
517         }
518     }
519
520     public void write(String JavaDoc str, int off, int len) throws IOException JavaDoc
521     {
522         closeStartTagIfNecessary();
523         String JavaDoc strValue = str.substring(off, off+len);
524         _writer.write(UnicodeEncoder.encode(strValue, false, false));
525     }
526
527     // DummyFormResponseWriter support
528

529     public void setWriteDummyForm(boolean writeDummyForm)
530     {
531         _writeDummyForm = writeDummyForm;
532     }
533
534     public String JavaDoc getDummyFormName()
535     {
536         return DummyFormUtils.DUMMY_FORM_NAME;
537     }
538
539     public void addDummyFormParameter(String JavaDoc paramName)
540     {
541         if (_dummyFormParams == null)
542         {
543             _dummyFormParams = new HashSet JavaDoc();
544         }
545         _dummyFormParams.add(paramName);
546     }
547     
548     public Set JavaDoc getDummyFormParams()
549     {
550         return _dummyFormParams;
551     }
552 }
553
Popular Tags