1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.shared.renderkit.html;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.myfaces.shared.renderkit.RendererUtils;
24 import org.apache.myfaces.shared.renderkit.html.util.UnicodeEncoder;
25
26 import javax.faces.FacesException;
27 import javax.faces.component.UIComponent;
28 import javax.faces.context.FacesContext;
29 import javax.faces.context.ResponseWriter;
30 import java.io.IOException;
31 import java.io.UnsupportedEncodingException;
32 import java.io.Writer;
33 import java.util.HashSet;
34 import java.util.Set;
35
36 /***
37 * @author Manfred Geiler (latest modification by $Author: lu4242 $)
38 * @author Anton Koinov
39 * @version $Revision: 779412 $ $Date: 2009-05-27 21:36:25 -0500 (Wed, 27 May 2009) $
40 */
41 public class HtmlResponseWriterImpl
42 extends ResponseWriter
43 {
44 private static final Log log = LogFactory.getLog(HtmlResponseWriterImpl.class);
45
46 private static final String DEFAULT_CONTENT_TYPE = "text/html";
47 private static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
48 private static final String UTF8 = "UTF-8";
49
50 private boolean _writeDummyForm = false;
51 private Set _dummyFormParams = null;
52
53 private Writer _writer;
54 private String _contentType;
55 private String _characterEncoding;
56 private String _startElementName;
57 private Boolean _isScript;
58 private Boolean _isStyle;
59 private Boolean _isTextArea;
60 private UIComponent _startElementUIComponent;
61 private boolean _startTagOpen;
62
63 private static final Set s_emptyHtmlElements = new HashSet();
64
65 private static final String CDATA_START = "<![CDATA[ \n";
66 private static final String COMMENT_START = "<!--\n";
67 private static final String CDATA_COMMENT_END = "\n//]]>";
68 private static final String CDATA_END = "\n]]>";
69 private static final String COMMENT_COMMENT_END = "\n//-->";
70 private static final String COMMENT_END = "\n-->";
71
72 static
73 {
74 s_emptyHtmlElements.add("area");
75 s_emptyHtmlElements.add("br");
76 s_emptyHtmlElements.add("base");
77 s_emptyHtmlElements.add("basefont");
78 s_emptyHtmlElements.add("col");
79 s_emptyHtmlElements.add("frame");
80 s_emptyHtmlElements.add("hr");
81 s_emptyHtmlElements.add("img");
82 s_emptyHtmlElements.add("input");
83 s_emptyHtmlElements.add("isindex");
84 s_emptyHtmlElements.add("link");
85 s_emptyHtmlElements.add("meta");
86 s_emptyHtmlElements.add("param");
87 }
88
89 public HtmlResponseWriterImpl(Writer writer, String contentType, String characterEncoding)
90 throws FacesException
91 {
92 _writer = writer;
93 _contentType = contentType;
94 if (_contentType == null)
95 {
96 if (log.isDebugEnabled()) log.debug("No content type given, using default content type " + DEFAULT_CONTENT_TYPE);
97 _contentType = DEFAULT_CONTENT_TYPE;
98 }
99 if (characterEncoding == null)
100 {
101 if (log.isDebugEnabled()) log.debug("No character encoding given, using default character encoding " + DEFAULT_CHARACTER_ENCODING);
102 _characterEncoding = DEFAULT_CHARACTER_ENCODING;
103 }
104 else
105 {
106
107 try
108 {
109 new String("myfaces".getBytes(), characterEncoding);
110 }
111 catch (UnsupportedEncodingException e)
112 {
113 throw new IllegalArgumentException("Unsupported encoding: "+characterEncoding);
114 }
115
116
117 _characterEncoding = characterEncoding.toUpperCase();
118 }
119 }
120
121 public static boolean supportsContentType(String contentType)
122 {
123 String[] supportedContentTypes = HtmlRendererUtils.getSupportedContentTypes();
124
125 for (int i = 0; i < supportedContentTypes.length; i++)
126 {
127 String supportedContentType = supportedContentTypes[i];
128
129 if(supportedContentType.indexOf(contentType)!=-1)
130 return true;
131 }
132 return false;
133 }
134
135 public String getContentType()
136 {
137 return _contentType;
138 }
139
140 public String getCharacterEncoding()
141 {
142 return _characterEncoding;
143 }
144
145 public void flush() throws IOException
146 {
147
148
149
150 closeStartTagIfNecessary();
151 }
152
153 public void startDocument()
154 {
155
156 }
157
158 public void endDocument() throws IOException
159 {
160 _writer.flush();
161 }
162
163 public void startElement(String name, UIComponent uiComponent) throws IOException
164 {
165 if (name == null)
166 {
167 throw new NullPointerException("elementName name must not be null");
168 }
169
170 closeStartTagIfNecessary();
171 _writer.write('<');
172 _writer.write(name);
173
174 resetStartedElement();
175
176 _startElementName = name;
177 _startElementUIComponent = uiComponent;
178 _startTagOpen = true;
179 }
180
181 private void closeStartTagIfNecessary() throws IOException
182 {
183 if (_startTagOpen)
184 {
185 if (s_emptyHtmlElements.contains(_startElementName.toLowerCase()))
186 {
187 _writer.write(" />");
188
189
190 resetStartedElement();
191 }
192 else
193 {
194 _writer.write('>');
195
196 if(isScriptOrStyle())
197 {
198 if(HtmlRendererUtils.isXHTMLContentType(_contentType))
199 {
200 if(HtmlRendererUtils.isAllowedCdataSection(FacesContext.getCurrentInstance()))
201 {
202 _writer.write(CDATA_START);
203 }
204 }
205 else
206 {
207 _writer.write(COMMENT_START);
208 }
209 }
210 }
211 _startTagOpen = false;
212 }
213 }
214
215 private void resetStartedElement()
216 {
217 _startElementName = null;
218 _startElementUIComponent = null;
219 _isScript = null;
220 _isStyle = null;
221 _isTextArea = null;
222 }
223
224 public void endElement(String name) throws IOException
225 {
226 if (name == null)
227 {
228 throw new NullPointerException("elementName name must not be null");
229 }
230
231 if (log.isWarnEnabled())
232 {
233 if (_startElementName != null &&
234 !name.equals(_startElementName))
235 {
236 if (log.isWarnEnabled())
237 log.warn("HTML nesting warning on closing " + name + ": element " + _startElementName +
238 (_startElementUIComponent==null?"":(" rendered by component : "+
239 RendererUtils.getPathToComponent(_startElementUIComponent)))+" not explicitly closed");
240 }
241 }
242
243 if(_startTagOpen)
244 {
245
246
247
248 closeStartTagIfNecessary();
249
250
251 if(_startElementName!=null)
252 {
253
254 writeEndTag(name);
255 }
256 }
257 else
258 {
259 if (s_emptyHtmlElements.contains(name.toLowerCase()))
260 {
261
262
263
264
265
266
267 }
268 else
269 {
270 writeEndTag(name);
271 }
272 }
273
274 resetStartedElement();
275 }
276
277 private void writeEndTag(String name)
278 throws IOException
279 {
280 if(isScriptOrStyle())
281 {
282 if(HtmlRendererUtils.isXHTMLContentType(_contentType))
283 {
284 if(HtmlRendererUtils.isAllowedCdataSection(FacesContext.getCurrentInstance()))
285 {
286 if(isScript())
287 _writer.write(CDATA_COMMENT_END);
288 else
289 _writer.write(CDATA_END);
290 }
291 }
292 else
293 {
294 if(isScript())
295 _writer.write(COMMENT_COMMENT_END);
296 else
297 _writer.write(COMMENT_END);
298 }
299 }
300
301 _writer.write("</");
302 _writer.write(name);
303 _writer.write('>');
304 }
305
306 public void writeAttribute(String name, Object value, String componentPropertyName) throws IOException
307 {
308 if (name == null)
309 {
310 throw new NullPointerException("attributeName name must not be null");
311 }
312 if (!_startTagOpen)
313 {
314 throw new IllegalStateException("Must be called before the start element is closed (attribute '" + name + "')");
315 }
316
317 if (value instanceof Boolean)
318 {
319 if (((Boolean)value).booleanValue())
320 {
321
322 _writer.write(' ');
323 _writer.write(name);
324 _writer.write("=\"");
325 _writer.write(name);
326 _writer.write('"');
327 }
328 }
329 else
330 {
331 String strValue = (value==null)?"":value.toString();
332 _writer.write(' ');
333 _writer.write(name);
334 _writer.write("=\"");
335 _writer.write(org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(strValue, false, false, !UTF8.equals(_characterEncoding)));
336 _writer.write('"');
337 }
338 }
339
340 public void writeURIAttribute(String name, Object value, String componentPropertyName) throws IOException
341 {
342 if (name == null)
343 {
344 throw new NullPointerException("attributeName name must not be null");
345 }
346 if (!_startTagOpen)
347 {
348 throw new IllegalStateException("Must be called before the start element is closed (attribute '" + name + "')");
349 }
350
351 String strValue = value.toString();
352 _writer.write(' ');
353 _writer.write(name);
354 _writer.write("=\"");
355 if (strValue.toLowerCase().startsWith("javascript:"))
356 {
357 _writer.write(org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(strValue, false, false, !UTF8.equals(_characterEncoding)));
358 }
359 else
360 {
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389 _writer.write(org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encodeURIAtributte(strValue, _characterEncoding));
390 }
391 _writer.write('"');
392 }
393
394 public void writeComment(Object value) throws IOException
395 {
396 if (value == null)
397 {
398 throw new NullPointerException("comment name must not be null");
399 }
400
401 closeStartTagIfNecessary();
402 _writer.write("<!--");
403 _writer.write(value.toString());
404 _writer.write("-->");
405 }
406
407 public void writeText(Object value, String componentPropertyName) throws IOException
408 {
409 if (value == null)
410 {
411 throw new NullPointerException("Text must not be null.");
412 }
413
414 closeStartTagIfNecessary();
415
416 String strValue = value.toString();
417
418 if (isScriptOrStyle())
419 {
420
421 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
422 else _writer.write(UnicodeEncoder.encode(strValue) );
423 }
424 else
425 {
426 _writer.write(org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(strValue, false, false, !UTF8.equals(_characterEncoding)));
427 }
428 }
429
430 public void writeText(char cbuf[], int off, int len) throws IOException
431 {
432 if (cbuf == null)
433 {
434 throw new NullPointerException("cbuf name must not be null");
435 }
436 if (cbuf.length < off + len)
437 {
438 throw new IndexOutOfBoundsException((off + len) + " > " + cbuf.length);
439 }
440
441 closeStartTagIfNecessary();
442
443 if (isScriptOrStyle())
444 {
445 String strValue = new String(cbuf, off, len);
446
447 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
448 else _writer.write(UnicodeEncoder.encode(strValue) );
449 }
450 else if (isTextarea())
451 {
452
453 org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(cbuf, off, len, false, false, !UTF8.equals(_characterEncoding), _writer);
454 }
455 else
456 {
457
458 org.apache.myfaces.shared.renderkit.html.util.HTMLEncoder.encode(cbuf, off, len, true, true, !UTF8.equals(_characterEncoding), _writer);
459 }
460 }
461
462 private boolean isScriptOrStyle()
463 {
464 initializeStartedTagInfo();
465
466 return (_isStyle != null && _isStyle.booleanValue()) ||
467 (_isScript != null && _isScript.booleanValue());
468 }
469
470 private boolean isScript()
471 {
472 initializeStartedTagInfo();
473
474 return (_isScript != null && _isScript.booleanValue());
475 }
476
477 private boolean isTextarea()
478 {
479 initializeStartedTagInfo();
480
481 return _isTextArea != null && _isTextArea.booleanValue();
482 }
483
484 private void initializeStartedTagInfo()
485 {
486 if(_startElementName != null)
487 {
488 if(_isScript==null)
489 {
490 if(_startElementName.equalsIgnoreCase(HTML.SCRIPT_ELEM))
491 {
492 _isScript = Boolean.TRUE;
493 _isStyle = Boolean.FALSE;
494 _isTextArea = Boolean.FALSE;
495 }
496 else
497 {
498 _isScript = Boolean.FALSE;
499 }
500 }
501 if(_isStyle == null)
502 {
503 if(_startElementName.equalsIgnoreCase(org.apache.myfaces.shared.renderkit.html.HTML.STYLE_ELEM))
504 {
505 _isStyle = Boolean.TRUE;
506 _isTextArea = Boolean.FALSE;
507 }
508 else
509 {
510 _isStyle = Boolean.FALSE;
511 }
512 }
513
514 if(_isTextArea == null)
515 {
516 if(_startElementName.equalsIgnoreCase(HTML.TEXTAREA_ELEM))
517 {
518 _isTextArea = Boolean.TRUE;
519 }
520 else
521 {
522 _isTextArea = Boolean.FALSE;
523 }
524 }
525 }
526 }
527
528 public ResponseWriter cloneWithWriter(Writer writer)
529 {
530 HtmlResponseWriterImpl newWriter
531 = new HtmlResponseWriterImpl(writer, getContentType(), getCharacterEncoding());
532 newWriter._writeDummyForm = _writeDummyForm;
533 newWriter._dummyFormParams = _dummyFormParams;
534 return newWriter;
535 }
536
537
538
539
540 public void close() throws IOException
541 {
542 closeStartTagIfNecessary();
543 _writer.close();
544 }
545
546 public void write(char cbuf[], int off, int len) throws IOException
547 {
548 closeStartTagIfNecessary();
549 String strValue = new String(cbuf, off, len);
550
551 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
552 else _writer.write(UnicodeEncoder.encode(strValue) );
553 }
554
555 public void write(int c) throws IOException
556 {
557 closeStartTagIfNecessary();
558 _writer.write(c);
559 }
560
561 public void write(char cbuf[]) throws IOException
562 {
563 closeStartTagIfNecessary();
564 String strValue = new String(cbuf);
565
566 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
567 else _writer.write(UnicodeEncoder.encode(strValue) );
568 }
569
570 public void write(String str) throws IOException
571 {
572 closeStartTagIfNecessary();
573
574
575 if (str.length() > 0)
576 {
577
578 if (UTF8.equals(_characterEncoding)) _writer.write(str);
579 else _writer.write(UnicodeEncoder.encode(str) );
580 }
581 }
582
583 public void write(String str, int off, int len) throws IOException
584 {
585 closeStartTagIfNecessary();
586 String strValue = str.substring(off, off+len);
587
588 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
589 else _writer.write(UnicodeEncoder.encode(strValue) );
590 }
591
592 /***
593 * This method ignores the <code>UIComponent</code> provided and simply calls
594 * <code>writeText(Object,String)</code>
595 * @since 1.2
596 */
597 public void writeText(Object object, UIComponent component, String string) throws IOException
598 {
599 writeText(object,string);
600 }
601 }