1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.shared_orchestra.renderkit.html;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.myfaces.shared_orchestra.renderkit.RendererUtils;
24 import org.apache.myfaces.shared_orchestra.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
38
39
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 void setContentType(java.lang.String contentType) {
141 _contentType = contentType;
142 }
143
144 public String getCharacterEncoding()
145 {
146 return _characterEncoding;
147 }
148
149 public void flush() throws IOException
150 {
151
152
153
154 closeStartTagIfNecessary();
155 }
156
157 public void startDocument()
158 {
159
160 }
161
162 public void endDocument() throws IOException
163 {
164 _writer.flush();
165 }
166
167 public void startElement(String name, UIComponent uiComponent) throws IOException
168 {
169 if (name == null)
170 {
171 throw new NullPointerException("elementName name must not be null");
172 }
173
174 closeStartTagIfNecessary();
175 _writer.write('<');
176 _writer.write(name);
177
178 resetStartedElement();
179
180 _startElementName = name;
181 _startElementUIComponent = uiComponent;
182 _startTagOpen = true;
183 }
184
185 private void closeStartTagIfNecessary() throws IOException
186 {
187 if (_startTagOpen)
188 {
189 if (s_emptyHtmlElements.contains(_startElementName.toLowerCase()))
190 {
191 _writer.write(" />");
192
193
194 resetStartedElement();
195 }
196 else
197 {
198 _writer.write('>');
199
200 if(isScriptOrStyle())
201 {
202 if(HtmlRendererUtils.isXHTMLContentType(_contentType))
203 {
204 if(HtmlRendererUtils.isAllowedCdataSection(FacesContext.getCurrentInstance()))
205 {
206 _writer.write(CDATA_START);
207 }
208 }
209 else
210 {
211 _writer.write(COMMENT_START);
212 }
213 }
214 }
215 _startTagOpen = false;
216 }
217 }
218
219 private void resetStartedElement()
220 {
221 _startElementName = null;
222 _startElementUIComponent = null;
223 _isScript = null;
224 _isStyle = null;
225 _isTextArea = null;
226 }
227
228 public void endElement(String name) throws IOException
229 {
230 if (name == null)
231 {
232 throw new NullPointerException("elementName name must not be null");
233 }
234
235 if (log.isWarnEnabled())
236 {
237 if (_startElementName != null &&
238 !name.equals(_startElementName))
239 {
240 if (log.isWarnEnabled())
241 log.warn("HTML nesting warning on closing " + name + ": element " + _startElementName +
242 (_startElementUIComponent==null?"":(" rendered by component : "+
243 RendererUtils.getPathToComponent(_startElementUIComponent)))+" not explicitly closed");
244 }
245 }
246
247 if(_startTagOpen)
248 {
249
250
251
252 closeStartTagIfNecessary();
253
254
255 if(_startElementName!=null)
256 {
257
258 writeEndTag(name);
259 }
260 }
261 else
262 {
263 if (s_emptyHtmlElements.contains(name.toLowerCase()))
264 {
265
266
267
268
269
270
271 }
272 else
273 {
274 writeEndTag(name);
275 }
276 }
277
278 resetStartedElement();
279 }
280
281 private void writeEndTag(String name)
282 throws IOException
283 {
284 if(isScriptOrStyle())
285 {
286 if(HtmlRendererUtils.isXHTMLContentType(_contentType))
287 {
288 if(HtmlRendererUtils.isAllowedCdataSection(FacesContext.getCurrentInstance()))
289 {
290 if(isScript())
291 _writer.write(CDATA_COMMENT_END);
292 else
293 _writer.write(CDATA_END);
294 }
295 }
296 else
297 {
298 if(isScript())
299 _writer.write(COMMENT_COMMENT_END);
300 else
301 _writer.write(COMMENT_END);
302 }
303 }
304
305 _writer.write("</");
306 _writer.write(name);
307 _writer.write('>');
308 }
309
310 public void writeAttribute(String name, Object value, String componentPropertyName) throws IOException
311 {
312 if (name == null)
313 {
314 throw new NullPointerException("attributeName name must not be null");
315 }
316 if (!_startTagOpen)
317 {
318 throw new IllegalStateException("Must be called before the start element is closed (attribute '" + name + "')");
319 }
320
321 if (value instanceof Boolean)
322 {
323 if (((Boolean)value).booleanValue())
324 {
325
326 _writer.write(' ');
327 _writer.write(name);
328 _writer.write("=\"");
329 _writer.write(name);
330 _writer.write('"');
331 }
332 }
333 else
334 {
335 String strValue = (value==null)?"":value.toString();
336 _writer.write(' ');
337 _writer.write(name);
338 _writer.write("=\"");
339 _writer.write(org.apache.myfaces.shared_orchestra.renderkit.html.util.HTMLEncoder.encode(strValue, false, false, !UTF8.equals(_characterEncoding)));
340 _writer.write('"');
341 }
342 }
343
344 public void writeURIAttribute(String name, Object value, String componentPropertyName) throws IOException
345 {
346 if (name == null)
347 {
348 throw new NullPointerException("attributeName name must not be null");
349 }
350 if (!_startTagOpen)
351 {
352 throw new IllegalStateException("Must be called before the start element is closed (attribute '" + name + "')");
353 }
354
355 String strValue = value.toString();
356 _writer.write(' ');
357 _writer.write(name);
358 _writer.write("=\"");
359 if (strValue.toLowerCase().startsWith("javascript:"))
360 {
361 _writer.write(org.apache.myfaces.shared_orchestra.renderkit.html.util.HTMLEncoder.encode(strValue, false, false, !UTF8.equals(_characterEncoding)));
362 }
363 else
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
390
391
392
393 _writer.write(org.apache.myfaces.shared_orchestra.renderkit.html.util.HTMLEncoder.encodeURIAtributte(strValue, _characterEncoding));
394 }
395 _writer.write('"');
396 }
397
398 public void writeComment(Object value) throws IOException
399 {
400 if (value == null)
401 {
402 throw new NullPointerException("comment name must not be null");
403 }
404
405 closeStartTagIfNecessary();
406 _writer.write("<!--");
407 _writer.write(value.toString());
408 _writer.write("-->");
409 }
410
411 public void writeText(Object value, String componentPropertyName) throws IOException
412 {
413 if (value == null)
414 {
415 throw new NullPointerException("Text must not be null.");
416 }
417
418 closeStartTagIfNecessary();
419
420 String strValue = value.toString();
421
422 if (isScriptOrStyle())
423 {
424
425 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
426 else _writer.write(UnicodeEncoder.encode(strValue) );
427 }
428 else
429 {
430 _writer.write(org.apache.myfaces.shared_orchestra.renderkit.html.util.HTMLEncoder.encode(strValue, false, false, !UTF8.equals(_characterEncoding)));
431 }
432 }
433
434 public void writeText(char cbuf[], int off, int len) throws IOException
435 {
436 if (cbuf == null)
437 {
438 throw new NullPointerException("cbuf name must not be null");
439 }
440 if (cbuf.length < off + len)
441 {
442 throw new IndexOutOfBoundsException((off + len) + " > " + cbuf.length);
443 }
444
445 closeStartTagIfNecessary();
446
447 if (isScriptOrStyle())
448 {
449 String strValue = new String(cbuf, off, len);
450
451 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
452 else _writer.write(UnicodeEncoder.encode(strValue) );
453 }
454 else if (isTextarea())
455 {
456
457
458 String strValue = new String(cbuf, off, len);
459 _writer.write(org.apache.myfaces.shared_orchestra.renderkit.html.util.HTMLEncoder.encode(strValue, false, false, !UTF8.equals(_characterEncoding)));
460 }
461 else
462 {
463
464
465 String strValue = new String(cbuf, off, len);
466 _writer.write(org.apache.myfaces.shared_orchestra.renderkit.html.util.HTMLEncoder.encode(strValue, true, true, !UTF8.equals(_characterEncoding)));
467 }
468 }
469
470 private boolean isScriptOrStyle()
471 {
472 initializeStartedTagInfo();
473
474 return (_isStyle != null && _isStyle.booleanValue()) ||
475 (_isScript != null && _isScript.booleanValue());
476 }
477
478 private boolean isScript()
479 {
480 initializeStartedTagInfo();
481
482 return (_isScript != null && _isScript.booleanValue());
483 }
484
485 private boolean isTextarea()
486 {
487 initializeStartedTagInfo();
488
489 return _isTextArea != null && _isTextArea.booleanValue();
490 }
491
492 private void initializeStartedTagInfo()
493 {
494 if(_startElementName != null)
495 {
496 if(_isScript==null)
497 {
498 if(_startElementName.equalsIgnoreCase(HTML.SCRIPT_ELEM))
499 {
500 _isScript = Boolean.TRUE;
501 _isStyle = Boolean.FALSE;
502 _isTextArea = Boolean.FALSE;
503 }
504 else
505 {
506 _isScript = Boolean.FALSE;
507 }
508 }
509 if(_isStyle == null)
510 {
511 if(_startElementName.equalsIgnoreCase(org.apache.myfaces.shared_orchestra.renderkit.html.HTML.STYLE_ELEM))
512 {
513 _isStyle = Boolean.TRUE;
514 _isTextArea = Boolean.FALSE;
515 }
516 else
517 {
518 _isStyle = Boolean.FALSE;
519 }
520 }
521
522 if(_isTextArea == null)
523 {
524 if(_startElementName.equalsIgnoreCase(HTML.TEXTAREA_ELEM))
525 {
526 _isTextArea = Boolean.TRUE;
527 }
528 else
529 {
530 _isTextArea = Boolean.FALSE;
531 }
532 }
533 }
534 }
535
536 public ResponseWriter cloneWithWriter(Writer writer)
537 {
538 HtmlResponseWriterImpl newWriter
539 = new HtmlResponseWriterImpl(writer, getContentType(), getCharacterEncoding());
540 newWriter._writeDummyForm = _writeDummyForm;
541 newWriter._dummyFormParams = _dummyFormParams;
542 return newWriter;
543 }
544
545
546
547
548 public void close() throws IOException
549 {
550 closeStartTagIfNecessary();
551 _writer.close();
552 }
553
554 public void write(char cbuf[], int off, int len) throws IOException
555 {
556 closeStartTagIfNecessary();
557 String strValue = new String(cbuf, off, len);
558
559 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
560 else _writer.write(UnicodeEncoder.encode(strValue) );
561 }
562
563 public void write(int c) throws IOException
564 {
565 closeStartTagIfNecessary();
566 _writer.write(c);
567 }
568
569 public void write(char cbuf[]) throws IOException
570 {
571 closeStartTagIfNecessary();
572 String strValue = new String(cbuf);
573
574 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
575 else _writer.write(UnicodeEncoder.encode(strValue) );
576 }
577
578 public void write(String str) throws IOException
579 {
580 closeStartTagIfNecessary();
581
582
583 if (str.length() > 0)
584 {
585
586 if (UTF8.equals(_characterEncoding)) _writer.write(str);
587 else _writer.write(UnicodeEncoder.encode(str) );
588 }
589 }
590
591 public void write(String str, int off, int len) throws IOException
592 {
593 closeStartTagIfNecessary();
594 String strValue = str.substring(off, off+len);
595
596 if (UTF8.equals(_characterEncoding)) _writer.write(strValue);
597 else _writer.write(UnicodeEncoder.encode(strValue) );
598 }
599 }