Source code: org/apache/myfaces/renderkit/html/HtmlRendererUtils.java
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 java.io.IOException;
19 import java.util.*;
20
21 import javax.faces.FacesException;
22 import javax.faces.component.*;
23 import javax.faces.context.FacesContext;
24 import javax.faces.context.ResponseWriter;
25 import javax.faces.convert.Converter;
26 import javax.faces.model.SelectItem;
27 import javax.faces.model.SelectItemGroup;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.myfaces.config.MyfacesConfig;
32 import org.apache.myfaces.renderkit.RendererUtils;
33 import org.apache.myfaces.renderkit.JSFAttr;
34 import org.apache.myfaces.renderkit.html.util.DummyFormUtils;
35 import org.apache.myfaces.renderkit.html.util.HTMLEncoder;
36 import org.apache.myfaces.renderkit.html.util.JavascriptUtils;
37 import org.apache.myfaces.component.DisplayValueOnlyCapable;
38
39 /**
40 * @author Manfred Geiler (latest modification by $Author: mbr $)
41 * @version $Revision: 291881 $ $Date: 2005-09-27 05:59:11 -0400 (Tue, 27 Sep 2005) $
42 */
43 public final class HtmlRendererUtils {
44 private static final Log log = LogFactory.getLog(HtmlRendererUtils.class);
45
46 //private static final String[] EMPTY_STRING_ARRAY = new String[0];
47 private static final String LINE_SEPARATOR = System.getProperty(
48 "line.separator", "\r\n");
49
50 private static final String HIDDEN_COMMANDLINK_FIELD_NAME = "_link_hidden_";
51
52
53 private HtmlRendererUtils() {
54 // utility class, do not instantiate
55 }
56
57 /**
58 * X-CHECKED: tlddoc h:inputText
59 *
60 * @param facesContext
61 * @param component
62 */
63 public static void decodeUIInput(FacesContext facesContext,
64 UIComponent component) {
65 if (!(component instanceof EditableValueHolder)) {
66 throw new IllegalArgumentException("Component "
67 + component.getClientId(facesContext)
68 + " is not an EditableValueHolder");
69 }
70 Map paramMap = facesContext.getExternalContext()
71 .getRequestParameterMap();
72 String clientId = component.getClientId(facesContext);
73
74 if(isDisabledOrReadOnly(component))
75 return;
76
77 if(paramMap.containsKey(clientId))
78 {
79 ((EditableValueHolder) component).setSubmittedValue(paramMap
80 .get(clientId));
81 }
82 else
83 {
84 log.warn("There should always be a submitted value for an input if it is rendered, its form is submitted, and it is not disabled or read-only.");
85 }
86 }
87
88 /**
89 * X-CHECKED: tlddoc h:selectBooleanCheckbox
90 *
91 * @param facesContext
92 * @param component
93 */
94 public static void decodeUISelectBoolean(FacesContext facesContext,
95 UIComponent component) {
96 if (!(component instanceof EditableValueHolder)) {
97 throw new IllegalArgumentException("Component "
98 + component.getClientId(facesContext)
99 + " is not an EditableValueHolder");
100 }
101
102 if(isDisabledOrReadOnly(component))
103 return;
104
105 Map paramMap = facesContext.getExternalContext()
106 .getRequestParameterMap();
107 String clientId = component.getClientId(facesContext);
108 if (paramMap.containsKey(clientId)) {
109 String reqValue = (String) paramMap.get(clientId);
110 if ((reqValue.equalsIgnoreCase("on")
111 || reqValue.equalsIgnoreCase("yes") || reqValue
112 .equalsIgnoreCase("true"))) {
113 ((EditableValueHolder) component)
114 .setSubmittedValue(Boolean.TRUE);
115 } else {
116 ((EditableValueHolder) component)
117 .setSubmittedValue(Boolean.FALSE);
118 }
119 } else {
120 ((EditableValueHolder) component)
121 .setSubmittedValue(Boolean.FALSE);
122 }
123 }
124
125 public static boolean isDisabledOrReadOnly(UIComponent component)
126 {
127 return isDisplayValueOnly(component) ||
128 isTrue(component.getAttributes().get("disabled")) ||
129 isTrue(component.getAttributes().get("readOnly"));
130 }
131
132 private static boolean isTrue(Object obj)
133 {
134 if(!(obj instanceof Boolean))
135 return false;
136
137 return ((Boolean) obj).booleanValue();
138 }
139
140 /**
141 * X-CHECKED: tlddoc h:selectManyListbox
142 *
143 * @param facesContext
144 * @param component
145 */
146 public static void decodeUISelectMany(FacesContext facesContext,
147 UIComponent component) {
148 if (!(component instanceof EditableValueHolder)) {
149 throw new IllegalArgumentException("Component "
150 + component.getClientId(facesContext)
151 + " is not an EditableValueHolder");
152 }
153 Map paramValuesMap = facesContext.getExternalContext()
154 .getRequestParameterValuesMap();
155 String clientId = component.getClientId(facesContext);
156
157 if(isDisabledOrReadOnly(component))
158 return;
159
160 if (paramValuesMap.containsKey(clientId)) {
161 String[] reqValues = (String[]) paramValuesMap.get(clientId);
162 ((EditableValueHolder) component).setSubmittedValue(reqValues);
163 } else {
164 /* request parameter not found, nothing to decode - set submitted value to an empty array
165 as we should get here only if the component is on a submitted form, is rendered
166 and if the component is not readonly or has not been disabled.
167
168 So in fact, there must be component value at this location, but for listboxes, comboboxes etc.
169 the submitted value is not posted if no item is selected. */
170 ((EditableValueHolder) component).setSubmittedValue( new String[]{});
171 }
172 }
173
174 /**
175 * X-CHECKED: tlddoc h:selectManyListbox
176 *
177 * @param facesContext
178 * @param component
179 */
180 public static void decodeUISelectOne(FacesContext facesContext,
181 UIComponent component) {
182 if (!(component instanceof EditableValueHolder)) {
183 throw new IllegalArgumentException("Component "
184 + component.getClientId(facesContext)
185 + " is not an EditableValueHolder");
186 }
187
188 if(isDisabledOrReadOnly(component))
189 return;
190
191 Map paramMap = facesContext.getExternalContext()
192 .getRequestParameterMap();
193 String clientId = component.getClientId(facesContext);
194 if (paramMap.containsKey(clientId)) {
195 //request parameter found, set submitted value
196 ((EditableValueHolder) component).setSubmittedValue(paramMap
197 .get(clientId));
198 } else {
199 //see reason for this action at decodeUISelectMany
200 ((EditableValueHolder) component).setSubmittedValue( RendererUtils.NOTHING );
201 }
202 }
203
204 /*
205 * public static void renderCheckbox(FacesContext facesContext, UIComponent
206 * uiComponent, String value, String label, boolean checked) throws
207 * IOException { String clientId = uiComponent.getClientId(facesContext);
208 *
209 * ResponseWriter writer = facesContext.getResponseWriter();
210 *
211 * writer.startElement(HTML.INPUT_ELEM, uiComponent);
212 * writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_CHECKBOX, null);
213 * writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
214 * writer.writeAttribute(HTML.ID_ATTR, clientId, null);
215 *
216 * if (checked) { writer.writeAttribute(HTML.CHECKED_ATTR,
217 * HTML.CHECKED_ATTR, null); }
218 *
219 * if ((value != null) && (value.length() > 0)) {
220 * writer.writeAttribute(HTML.VALUE_ATTR, value, null); }
221 *
222 * renderHTMLAttributes(writer, uiComponent,
223 * HTML.INPUT_PASSTHROUGH_ATTRIBUTES); renderDisabledOnUserRole(writer,
224 * uiComponent, facesContext);
225 *
226 * if ((label != null) && (label.length() > 0)) {
227 * writer.write(HTML.NBSP_ENTITY); writer.writeText(label, null); }
228 *
229 * writer.endElement(HTML.INPUT_ELEM); }
230 */
231
232 public static void renderListbox(FacesContext facesContext,
233 UISelectOne selectOne, boolean disabled, int size)
234 throws IOException {
235 internalRenderSelect(facesContext, selectOne, disabled, size, false);
236 }
237
238 public static void renderListbox(FacesContext facesContext,
239 UISelectMany selectMany, boolean disabled, int size)
240 throws IOException {
241 internalRenderSelect(facesContext, selectMany, disabled, size, true);
242 }
243
244 public static void renderMenu(FacesContext facesContext,
245 UISelectOne selectOne, boolean disabled) throws IOException {
246 internalRenderSelect(facesContext, selectOne, disabled, 1, false);
247 }
248
249 public static void renderMenu(FacesContext facesContext,
250 UISelectMany selectMany, boolean disabled) throws IOException {
251 internalRenderSelect(facesContext, selectMany, disabled, 1, true);
252 }
253
254 private static void internalRenderSelect(FacesContext facesContext,
255 UIComponent uiComponent, boolean disabled, int size,
256 boolean selectMany) throws IOException {
257 ResponseWriter writer = facesContext.getResponseWriter();
258
259 writer.startElement(HTML.SELECT_ELEM, uiComponent);
260 HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
261 writer.writeAttribute(HTML.NAME_ATTR, uiComponent
262 .getClientId(facesContext), null);
263
264 List selectItemList;
265 Converter converter;
266 if (selectMany) {
267 writer.writeAttribute(HTML.MULTIPLE_ATTR, "true", null);
268 selectItemList = RendererUtils
269 .getSelectItemList((UISelectMany) uiComponent);
270 converter = findUISelectManyConverterFailsafe(facesContext, uiComponent);
271 } else {
272 selectItemList = RendererUtils
273 .getSelectItemList((UISelectOne) uiComponent);
274 converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
275 }
276
277 if (size == 0) {
278 //No size given (Listbox) --> size is number of select items
279 writer.writeAttribute(HTML.SIZE_ATTR, Integer
280 .toString(selectItemList.size()), null);
281 } else {
282 writer.writeAttribute(HTML.SIZE_ATTR, Integer.toString(size), null);
283 }
284 renderHTMLAttributes(writer, uiComponent,
285 HTML.SELECT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
286 if (disabled) {
287 writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
288 }
289
290 Set lookupSet = getSubmittedOrSelectedValuesAsSet(selectMany, uiComponent, facesContext, converter);
291
292 renderSelectOptions(facesContext, uiComponent, converter, lookupSet,
293 selectItemList);
294 // bug #970747: force separate end tag
295 writer.writeText("", null);
296 writer.endElement(HTML.SELECT_ELEM);
297 }
298
299 public static Set getSubmittedOrSelectedValuesAsSet(boolean selectMany, UIComponent uiComponent, FacesContext facesContext, Converter converter) {
300 Set lookupSet;
301
302 if (selectMany) {
303 UISelectMany uiSelectMany = (UISelectMany) uiComponent;
304 lookupSet = RendererUtils.getSubmittedValuesAsSet(facesContext, uiComponent, converter, uiSelectMany);
305 if (lookupSet == null)
306 {
307 lookupSet = RendererUtils.getSelectedValuesAsSet(facesContext, uiComponent, converter, uiSelectMany);
308 }
309 } else {
310 UISelectOne uiSelectOne = (UISelectOne) uiComponent;
311 Object lookup = uiSelectOne.getSubmittedValue();
312 if (lookup == null)
313 {
314 lookup = uiSelectOne.getValue();
315 String lookupString = RendererUtils.getConvertedStringValue(facesContext, uiComponent, converter, lookup);
316 lookupSet = Collections.singleton(lookupString);
317 }
318 else if(RendererUtils.NOTHING.equals(lookup))
319 {
320 lookupSet = Collections.EMPTY_SET;
321 }
322 else
323 {
324 lookupSet = Collections.singleton(lookup);
325 }
326 }
327 return lookupSet;
328 }
329
330 public static Converter findUISelectManyConverterFailsafe(FacesContext facesContext, UIComponent uiComponent) {
331 Converter converter;
332 try {
333 converter = RendererUtils.findUISelectManyConverter(
334 facesContext, (UISelectMany) uiComponent);
335 } catch (FacesException e) {
336 log.error("Error finding Converter for component with id "
337 + uiComponent.getClientId(facesContext));
338 converter = null;
339 }
340 return converter;
341 }
342
343 public static Converter findUIOutputConverterFailSafe(FacesContext facesContext, UIComponent uiComponent) {
344 Converter converter;
345 try {
346 converter = RendererUtils.findUIOutputConverter(facesContext,
347 (UISelectOne) uiComponent);
348 } catch (FacesException e) {
349 log.error("Error finding Converter for component with id "
350 + uiComponent.getClientId(facesContext));
351 converter = null;
352 }
353 return converter;
354 }
355
356 /**
357 * Renders the select options for a <code>UIComponent</code> that is
358 * rendered as an HTML select element.
359 *
360 * @param context
361 * the current <code>FacesContext</code>.
362 * @param component
363 * the <code>UIComponent</code> whose options need to be
364 * rendered.
365 * @param converter
366 * <code>component</code>'s converter
367 * @param lookupSet
368 * the <code>Set</code> to use to look up selected options
369 * @param selectItemList
370 * the <code>List</code> of <code>SelectItem</code> s to be
371 * rendered as HTML option elements.
372 * @throws IOException
373 */
374 public static void renderSelectOptions(FacesContext context,
375 UIComponent component, Converter converter, Set lookupSet,
376 List selectItemList) throws IOException {
377 ResponseWriter writer = context.getResponseWriter();
378
379 for (Iterator it = selectItemList.iterator(); it.hasNext();) {
380 SelectItem selectItem = (SelectItem) it.next();
381
382 if (selectItem instanceof SelectItemGroup) {
383 writer.startElement(HTML.OPTGROUP_ELEM, component);
384 writer.writeAttribute(HTML.LABEL_ATTR, selectItem.getLabel(),
385 null);
386 SelectItem[] selectItems = ((SelectItemGroup) selectItem)
387 .getSelectItems();
388 renderSelectOptions(context, component, converter, lookupSet,
389 Arrays.asList(selectItems));
390 writer.endElement(HTML.OPTGROUP_ELEM);
391 } else {
392 String itemStrValue = RendererUtils.getConvertedStringValue(context, component,
393 converter, selectItem);
394
395 writer.write("\t");
396 writer.startElement(HTML.OPTION_ELEM, component);
397 if (itemStrValue != null) {
398 writer.writeAttribute(HTML.VALUE_ATTR, itemStrValue, null);
399 }
400
401 if (lookupSet.contains(itemStrValue)) { //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings only when useSubmittedValue==true, else use the real item value Objects
402 writer.writeAttribute(HTML.SELECTED_ATTR,
403 HTML.SELECTED_ATTR, null);
404 }
405
406 boolean disabled = selectItem.isDisabled();
407 if (disabled) {
408 writer.writeAttribute(HTML.DISABLED_ATTR,
409 HTML.DISABLED_ATTR, null);
410 }
411
412 String labelClass = null;
413 boolean componentDisabled = isTrue(component.getAttributes().get("disabled"));
414
415 if (componentDisabled || disabled) {
416 labelClass = (String) component.getAttributes().get(JSFAttr.DISABLED_CLASS_ATTR);
417 } else {
418 labelClass = (String) component.getAttributes().get(JSFAttr.ENABLED_CLASS_ATTR);
419 }
420 if (labelClass != null) {
421 writer.writeAttribute("class", labelClass, "labelClass");
422 }
423
424
425 writer.writeText(selectItem.getLabel(), null);
426
427 writer.endElement(HTML.OPTION_ELEM);
428 }
429 }
430 }
431
432 /*
433 * public static void renderRadio(FacesContext facesContext, UIInput
434 * uiComponent, String value, String label, boolean checked) throws
435 * IOException { String clientId = uiComponent.getClientId(facesContext);
436 *
437 * ResponseWriter writer = facesContext.getResponseWriter();
438 *
439 * writer.startElement(HTML.INPUT_ELEM, uiComponent);
440 * writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_RADIO, null);
441 * writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
442 * writer.writeAttribute(HTML.ID_ATTR, clientId, null);
443 *
444 * if (checked) { writer.writeAttribute(HTML.CHECKED_ATTR,
445 * HTML.CHECKED_ATTR, null); }
446 *
447 * if ((value != null) && (value.length() > 0)) {
448 * writer.writeAttribute(HTML.VALUE_ATTR, value, null); }
449 *
450 * renderHTMLAttributes(writer, uiComponent,
451 * HTML.INPUT_PASSTHROUGH_ATTRIBUTES); renderDisabledOnUserRole(writer,
452 * uiComponent, facesContext);
453 *
454 * if ((label != null) && (label.length() > 0)) {
455 * writer.write(HTML.NBSP_ENTITY); writer.writeText(label, null); }
456 *
457 * writer.endElement(HTML.INPUT_ELEM); }
458 */
459
460 public static void writePrettyLineSeparator(FacesContext facesContext)
461 throws IOException {
462 if (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext())
463 .isPrettyHtml()) {
464 facesContext.getResponseWriter().write(LINE_SEPARATOR);
465 }
466 }
467
468 public static void writePrettyIndent(FacesContext facesContext)
469 throws IOException {
470 if (MyfacesConfig.getCurrentInstance(facesContext.getExternalContext())
471 .isPrettyHtml()) {
472 facesContext.getResponseWriter().write('\t');
473 }
474 }
475
476 /**
477 * @return true, if the attribute was written
478 * @throws java.io.IOException
479 */
480 public static boolean renderHTMLAttribute(ResponseWriter writer,
481 String componentProperty, String attrName, Object value)
482 throws IOException {
483 if (!RendererUtils.isDefaultAttributeValue(value)) {
484 // render JSF "styleClass" attribute as "class"
485 String htmlAttrName = attrName.equals(HTML.STYLE_CLASS_ATTR) ? HTML.CLASS_ATTR
486 : attrName;
487 writer.writeAttribute(htmlAttrName, value, componentProperty);
488 return true;
489 }
490
491 return false;
492 }
493
494 /**
495 * @return true, if the attribute was written
496 * @throws java.io.IOException
497 */
498 public static boolean renderHTMLAttribute(ResponseWriter writer,
499 UIComponent component, String componentProperty, String htmlAttrName)
500 throws IOException {
501 Object value = component.getAttributes().get(componentProperty);
502 return renderHTMLAttribute(writer, componentProperty, htmlAttrName,
503 value);
504 }
505
506 /**
507 * @return true, if an attribute was written
508 * @throws java.io.IOException
509 */
510 public static boolean renderHTMLAttributes(ResponseWriter writer,
511 UIComponent component, String[] attributes) throws IOException {
512 boolean somethingDone = false;
513 for (int i = 0, len = attributes.length; i < len; i++) {
514 String attrName = attributes[i];
515 if (renderHTMLAttribute(writer, component, attrName, attrName)) {
516 somethingDone = true;
517 }
518 }
519 return somethingDone;
520 }
521
522 public static boolean renderHTMLAttributeWithOptionalStartElement(
523 ResponseWriter writer, UIComponent component, String elementName,
524 String attrName, Object value, boolean startElementWritten)
525 throws IOException {
526 if (!RendererUtils.isDefaultAttributeValue(value)) {
527 if (!startElementWritten) {
528 writer.startElement(elementName, component);
529 startElementWritten = true;
530 }
531 renderHTMLAttribute(writer, attrName, attrName, value);
532 }
533 return startElementWritten;
534 }
535
536 public static boolean renderHTMLAttributesWithOptionalStartElement(
537 ResponseWriter writer, UIComponent component, String elementName,
538 String[] attributes) throws IOException {
539 boolean startElementWritten = false;
540 for (int i = 0, len = attributes.length; i < len; i++) {
541 String attrName = attributes[i];
542 Object value = component.getAttributes().get(attrName);
543 if (!RendererUtils.isDefaultAttributeValue(value)) {
544 if (!startElementWritten) {
545 writer.startElement(elementName, component);
546 startElementWritten = true;
547 }
548 renderHTMLAttribute(writer, attrName, attrName, value);
549 }
550 }
551 return startElementWritten;
552 }
553
554 public static boolean renderOptionalEndElement(ResponseWriter writer,
555 UIComponent component, String elementName, String[] attributes)
556 throws IOException {
557 boolean endElementNeeded = false;
558 for (int i = 0, len = attributes.length; i < len; i++) {
559 String attrName = attributes[i];
560 Object value = component.getAttributes().get(attrName);
561 if (!RendererUtils.isDefaultAttributeValue(value)) {
562 endElementNeeded = true;
563 break;
564 }
565 }
566 if (endElementNeeded) {
567 writer.endElement(elementName);
568 return true;
569 }
570
571 return false;
572 }
573
574 public static void writeIdIfNecessary(ResponseWriter writer, UIComponent component,
575 FacesContext facesContext)
576 throws IOException
577 {
578 if(component.getId()!=null && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
579 {
580 writer.writeAttribute(HTML.ID_ATTR, component.getClientId(facesContext),null);
581 }
582 }
583
584 public static void renderDisplayValueOnlyForSelects(FacesContext facesContext, UIComponent
585 uiComponent)
586 throws IOException
587 {
588 ResponseWriter writer = facesContext.getResponseWriter();
589
590 List selectItemList;
591 Converter converter;
592 boolean isSelectOne;
593 if (uiComponent instanceof UISelectMany) {
594 isSelectOne = false;
595 selectItemList = RendererUtils
596 .getSelectItemList((UISelectMany) uiComponent);
597 converter = findUISelectManyConverterFailsafe(facesContext, uiComponent);
598 } else {
599 isSelectOne = true;
600 selectItemList = RendererUtils
601 .getSelectItemList((UISelectOne) uiComponent);
602 converter = findUIOutputConverterFailSafe(facesContext, uiComponent);
603 }
604
605 writer.startElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM, uiComponent);
606 writeIdIfNecessary(writer, uiComponent, facesContext);
607
608 renderDisplayValueOnlyAttributes(uiComponent, writer);
609
610 Set lookupSet = getSubmittedOrSelectedValuesAsSet(
611 uiComponent instanceof UISelectMany,
612 uiComponent, facesContext, converter);
613
614 renderSelectOptionsAsText(facesContext, uiComponent, converter, lookupSet,
615 selectItemList, isSelectOne);
616
617 // bug #970747: force separate end tag
618 writer.writeText("", null);
619 writer.endElement(isSelectOne ? HTML.SPAN_ELEM : HTML.UL_ELEM);
620
621 }
622
623 public static void renderDisplayValueOnlyAttributes(UIComponent uiComponent, ResponseWriter writer) throws IOException {
624 if(!(uiComponent instanceof DisplayValueOnlyCapable))
625 {
626 log.error("Wrong type of uiComponent. needs DisplayValueOnlyCapable.");
627 renderHTMLAttributes(writer, uiComponent,
628 HTML.COMMON_PASSTROUGH_ATTRIBUTES);
629
630 return;
631 }
632
633 if(getDisplayValueOnlyStyle(uiComponent) != null || getDisplayValueOnlyStyleClass(uiComponent)!=null)
634 {
635 if(getDisplayValueOnlyStyle(uiComponent) != null )
636 {
637 writer.writeAttribute(HTML.STYLE_ATTR, getDisplayValueOnlyStyle(uiComponent), null);
638 }
639 else if(uiComponent.getAttributes().get("style")!=null)
640 {
641 writer.writeAttribute(HTML.STYLE_ATTR, uiComponent.getAttributes().get("style"), null);
642 }
643
644 if(getDisplayValueOnlyStyleClass(uiComponent) != null )
645 {
646 writer.writeAttribute(HTML.CLASS_ATTR, getDisplayValueOnlyStyleClass(uiComponent), null);
647 }
648 else if(uiComponent.getAttributes().get("styleClass")!=null)
649 {
650 writer.writeAttribute(HTML.CLASS_ATTR, uiComponent.getAttributes().get("styleClass"), null);
651 }
652
653 renderHTMLAttributes(writer, uiComponent,
654 HTML.COMMON_PASSTROUGH_ATTRIBUTES_WITHOUT_STYLE);
655 }
656 else
657 {
658 renderHTMLAttributes(writer, uiComponent,
659 HTML.COMMON_PASSTROUGH_ATTRIBUTES);
660 }
661 }
662
663 private static void renderSelectOptionsAsText(FacesContext context,
664 UIComponent component, Converter converter, Set lookupSet,
665 List selectItemList, boolean isSelectOne) throws IOException {
666 ResponseWriter writer = context.getResponseWriter();
667
668 for (Iterator it = selectItemList.iterator(); it.hasNext();) {
669 SelectItem selectItem = (SelectItem) it.next();
670
671 if (selectItem instanceof SelectItemGroup) {
672 SelectItem[] selectItems = ((SelectItemGroup) selectItem)
673 .getSelectItems();
674 renderSelectOptionsAsText(context, component, converter, lookupSet,
675 Arrays.asList(selectItems), isSelectOne);
676 } else {
677 String itemStrValue = RendererUtils.getConvertedStringValue(context, component,
678 converter, selectItem);
679
680 if (lookupSet.contains(itemStrValue)) { //TODO/FIX: we always compare the String vales, better fill lookupSet with Strings only when useSubmittedValue==true, else use the real item value Objects
681
682 if( ! isSelectOne )
683 writer.startElement(HTML.LI_ELEM, component);
684 writer.writeText(selectItem.getLabel(), null);
685 if( ! isSelectOne )
686 writer.endElement(HTML.LI_ELEM);
687
688 if( isSelectOne )
689 {
690 //take care of several choices with the same value; use only the first one
691 return;
692 }
693 }
694 }
695 }
696 }
697
698 public static String getDisplayValueOnlyStyleClass(UIComponent component) {
699
700 if(component instanceof DisplayValueOnlyCapable)
701 {
702 if(((DisplayValueOnlyCapable) component).getDisplayValueOnlyStyleClass()!=null)
703 return ((DisplayValueOnlyCapable) component).getDisplayValueOnlyStyleClass();
704
705 UIComponent parent=component;
706
707 while((parent = parent.getParent())!=null)
708 {
709 if(parent instanceof DisplayValueOnlyCapable &&
710 ((DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyleClass()!=null)
711 {
712 return ((DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyleClass();
713 }
714 }
715 }
716
717 return null;
718 }
719
720 public static String getDisplayValueOnlyStyle(UIComponent component) {
721
722 if(component instanceof DisplayValueOnlyCapable)
723 {
724 if(((DisplayValueOnlyCapable) component).getDisplayValueOnlyStyle()!=null)
725 return ((DisplayValueOnlyCapable) component).getDisplayValueOnlyStyle();
726
727 UIComponent parent=component;
728
729 while((parent = parent.getParent())!=null)
730 {
731 if(parent instanceof DisplayValueOnlyCapable &&
732 ((DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyle()!=null)
733 {
734 return ((DisplayValueOnlyCapable) parent).getDisplayValueOnlyStyle();
735 }
736 }
737 }
738
739 return null;
740 }
741
742 public static boolean isDisplayValueOnly(UIComponent component) {
743
744 if(component instanceof DisplayValueOnlyCapable)
745 {
746 if(((DisplayValueOnlyCapable) component).isSetDisplayValueOnly())
747 return ((DisplayValueOnlyCapable) component).isDisplayValueOnly();
748
749 UIComponent parent=component;
750
751 while((parent = parent.getParent())!=null)
752 {
753 if(parent instanceof DisplayValueOnlyCapable &&
754 ((DisplayValueOnlyCapable) parent).isSetDisplayValueOnly())
755 {
756 return ((DisplayValueOnlyCapable) parent).isDisplayValueOnly();
757 }
758 }
759 }
760
761 return false;
762 }
763
764 public static void renderDisplayValueOnly(FacesContext facesContext, UIInput input) throws IOException {
765 ResponseWriter writer = facesContext.getResponseWriter();
766 writer.startElement(HTML.SPAN_ELEM, input);
767
768 writeIdIfNecessary(writer, input, facesContext);
769
770 renderDisplayValueOnlyAttributes(input, writer);
771
772 String strValue = RendererUtils.getStringValue(facesContext, input);
773 writer.write( HTMLEncoder.encode(strValue, true, true) );
774
775 writer.endElement(HTML.SPAN_ELEM);
776 }
777
778 public static class LinkParameter {
779 private String _name;
780
781 private Object _value;
782
783 public String getName() {
784 return _name;
785 }
786
787 public void setName(String name) {
788 _name = name;
789 }
790
791 public Object getValue() {
792 return _value;
793 }
794
795 public void setValue(Object value) {
796 _value = value;
797 }
798
799 }
800
801 public static void renderHiddenCommandFormParams(ResponseWriter writer,
802 Set dummyFormParams) throws IOException {
803 for (Iterator it = dummyFormParams.iterator(); it.hasNext();) {
804 writer.startElement(HTML.INPUT_ELEM, null);
805 writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
806 writer.writeAttribute(HTML.NAME_ATTR, it.next(), null);
807 writer.endElement(HTML.INPUT_ELEM);
808 }
809 }
810
811 /**
812 * Render the javascript function that is called on a click on a commandLink
813 * to clear the hidden inputs. This is necessary because on a browser back,
814 * each hidden input still has it's old value (browser cache!) and therefore
815 * a new submit would cause the according action once more!
816 *
817 * @param writer
818 * @param formName
819 * @param dummyFormParams
820 * @param formTarget
821 * @throws IOException
822 */
823 public static void renderClearHiddenCommandFormParamsFunction(
824 ResponseWriter writer, String formName, Set dummyFormParams,
825 String formTarget) throws IOException {
826 //render the clear hidden inputs javascript function
827 String functionName = getClearHiddenCommandFormParamsFunctionName(formName);
828 writer.startElement(HTML.SCRIPT_ELEM, null);
829 writer.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
830
831 // Using writeComment instead of write with <!-- tag
832 StringBuffer script = new StringBuffer();
833 script.append("function ");
834 script.append(functionName);
835 script.append("() {");
836 if (dummyFormParams != null) {
837 script.append("\n var f = document.forms['");
838 script.append(formName);
839 script.append("'];");
840 for (Iterator it = dummyFormParams.iterator(); it.hasNext();) {
841 script.append("\n f.elements['");
842 script.append((String) it.next());
843 script.append("'].value='';");
844 }
845 }
846 // clear form target
847 script.append("\n f.target=");
848 if (formTarget == null || formTarget.length() == 0) {
849 //Normally one would think that setting target to null has the
850 //desired effect, but once again IE is different...
851 //Setting target to null causes IE to open a new window!
852 script.append("'';");
853 } else {
854 script.append("'");
855 script.append(formTarget);
856 script.append("';");
857 }
858 script.append("\n}");
859
860 //Just to be sure we call this clear method on each load.
861 //Otherwise in the case, that someone submits a form by pressing Enter
862 //within a text input, the hidden inputs won't be cleared!
863 script.append("\n");
864 script.append(functionName);
865 script.append("();");
866
867 writer.writeText(script.toString(),null);
868 writer.endElement(HTML.SCRIPT_ELEM);
869 }
870
871 /**
872 * Prefixes the given String with "clear_" and removes special characters
873 *
874 * @param formName
875 * @return String
876 */
877 public static String getClearHiddenCommandFormParamsFunctionName(
878 String formName) {
879 return "clear_"
880 + JavascriptUtils.getValidJavascriptName(formName, false);
881 }
882
883 public static String getFormName(UIComponent component, FacesContext context) {
884 //Find form
885 UIComponent parent = component.getParent();
886 while (parent != null && !(parent instanceof UIForm)) {
887 parent = parent.getParent();
888 }
889
890 if (parent != null) {
891 //link is nested inside a form
892 return ((UIForm) parent).getClientId(context);
893 }
894 //not nested in form, we must add a dummy form at the end of the
895 // document
896 return DummyFormUtils.DUMMY_FORM_NAME;
897 }
898
899 public static String getHiddenCommandLinkFieldName(String formName) {
900 return formName + NamingContainer.SEPARATOR_CHAR
901 + HIDDEN_COMMANDLINK_FIELD_NAME;
902 }
903
904 private static String HTML_CONTENT_TYPE = "text/html";
905 private static String ANY_CONTENT_TYPE = "*/*";
906
907 public static String DEFAULT_CHAR_ENCODING = "ISO-8859-1";
908 private static String XHTML_CONTENT_TYPE = "application/xhtml+xml";
909 private static String APPLICATION_XML_CONTENT_TYPE = "application/xml";
910 private static String TEXT_XML_CONTENT_TYPE = "text/xml";
911
912
913 public static String selectContentType(String contentTypeListString)
914 {
915 if (contentTypeListString == null)
916 {
917 FacesContext context = FacesContext.getCurrentInstance();
918
919 contentTypeListString = (String)
920 context.getExternalContext().getRequestHeaderMap().get("Accept");
921
922 if(contentTypeListString == null)
923 {
924 if (log.isDebugEnabled())
925 log.debug("No content type list given, creating HtmlResponseWriterImpl with default content type.");
926
927 contentTypeListString = HTML_CONTENT_TYPE;
928 }
929 }
930
931 List contentTypeList = splitContentTypeListString(contentTypeListString);
932 String[] supportedContentTypeArray = getSupportedContentTypes();
933
934 String selectedContentType = null;
935
936 for (int i = 0; i < supportedContentTypeArray.length; i++)
937 {
938 String supportedContentType = supportedContentTypeArray[i].trim();
939
940 for (int j = 0; j < contentTypeList.size(); j++)
941 {
942 String contentType = (String) contentTypeList.get(j);
943
944 if (contentType.indexOf(supportedContentType) != -1)
945 {
946 if (isHTMLContentType(contentType)) {
947 selectedContentType = HTML_CONTENT_TYPE;
948 }
949
950 else if (isXHTMLContentType(contentType)) {
951 selectedContentType = XHTML_CONTENT_TYPE;
952 }
953 break;
954 }
955 }
956 if (selectedContentType!=null)
957 {
958 break;
959 }
960 }
961
962 if(selectedContentType==null)
963 {
964 throw new IllegalArgumentException("ContentTypeList does not contain a supported content type: " +
965 contentTypeListString);
966 }
967 return selectedContentType;
968 }
969
970 public static String[] getSupportedContentTypes()
971 {
972 String[] supportedContentTypeArray = new String[]{HTML_CONTENT_TYPE,ANY_CONTENT_TYPE,
973 XHTML_CONTENT_TYPE,APPLICATION_XML_CONTENT_TYPE,TEXT_XML_CONTENT_TYPE};
974 return supportedContentTypeArray;
975 }
976
977 private static boolean isHTMLContentType(String contentType)
978 {
979 return contentType.indexOf(HTML_CONTENT_TYPE) != -1 ||
980 contentType.indexOf(ANY_CONTENT_TYPE) != -1;
981 }
982
983 public static boolean isXHTMLContentType(String contentType)
984 {
985 return contentType.indexOf(XHTML_CONTENT_TYPE) != -1 ||
986 contentType.indexOf(APPLICATION_XML_CONTENT_TYPE) != -1 ||
987 contentType.indexOf(TEXT_XML_CONTENT_TYPE) != -1;
988 }
989
990 private static List splitContentTypeListString(String contentTypeListString)
991 {
992 List contentTypeList = new ArrayList();
993
994 StringTokenizer st = new StringTokenizer(contentTypeListString, ",");
995 while (st.hasMoreTokens())
996 {
997 String contentType = st.nextToken().trim();
998
999 int semicolonIndex = contentType.indexOf(";");
1000
1001 if (semicolonIndex!=-1)
1002 {
1003 contentType = contentType.substring(0,semicolonIndex);
1004 }
1005
1006 contentTypeList.add(contentType);
1007 }
1008
1009 return contentTypeList;
1010 }
1011
1012
1013}