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 java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.EnumSet;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.logging.Logger;
30 import javax.faces.FacesException;
31
32 import javax.faces.component.UIComponent;
33 import javax.faces.component.UIInput;
34 import javax.faces.component.UISelectOne;
35 import javax.faces.component.behavior.ClientBehavior;
36 import javax.faces.component.behavior.ClientBehaviorHolder;
37 import javax.faces.component.html.HtmlSelectOneRadio;
38 import javax.faces.component.visit.VisitCallback;
39 import javax.faces.component.visit.VisitContext;
40 import javax.faces.component.visit.VisitHint;
41 import javax.faces.component.visit.VisitResult;
42 import javax.faces.context.FacesContext;
43 import javax.faces.context.ResponseWriter;
44 import javax.faces.convert.Converter;
45 import javax.faces.convert.ConverterException;
46 import javax.faces.model.SelectItem;
47 import javax.faces.model.SelectItemGroup;
48
49 import org.apache.myfaces.shared.renderkit.JSFAttr;
50 import org.apache.myfaces.shared.renderkit.RendererUtils;
51 import org.apache.myfaces.shared.renderkit.html.util.FormInfo;
52 import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
53
54 public class HtmlRadioRendererBase
55 extends HtmlRenderer
56 {
57 private static final Logger log = Logger.getLogger(HtmlRadioRendererBase.class.getName());
58
59 private static final String PAGE_DIRECTION = "pageDirection";
60 private static final String LINE_DIRECTION = "lineDirection";
61
62 private static final Set<VisitHint> FIND_SELECT_LIST_HINTS =
63 Collections.unmodifiableSet(EnumSet.of(VisitHint.SKIP_UNRENDERED, VisitHint.SKIP_ITERATION));
64
65 private Map<String, UISelectOne> groupFirst = new HashMap<String, UISelectOne>();
66
67 @Override
68 public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
69 {
70 RendererUtils.checkParamValidity(facesContext, uiComponent, UISelectOne.class);
71
72 UISelectOne selectOne = (UISelectOne)uiComponent;
73
74 String layout = getLayout(selectOne);
75
76 boolean pageDirectionLayout = false;
77 if (layout != null)
78 {
79 if (layout.equals(PAGE_DIRECTION))
80 {
81 pageDirectionLayout = true;
82 }
83 else if (layout.equals(LINE_DIRECTION))
84 {
85 pageDirectionLayout = false;
86 }
87 else
88 {
89 log.severe("Wrong layout attribute for component " +
90 selectOne.getClientId(facesContext) + ": " + layout);
91 }
92 }
93
94 ResponseWriter writer = facesContext.getResponseWriter();
95
96 Map<String, List<ClientBehavior>> behaviors = null;
97 if (uiComponent instanceof ClientBehaviorHolder)
98 {
99 behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
100 if (!behaviors.isEmpty())
101 {
102 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, writer);
103 }
104 }
105
106 String group = selectOne instanceof HtmlSelectOneRadio ? ((HtmlSelectOneRadio) selectOne).getGroup() : null;
107 if (group != null && !group.isEmpty())
108 {
109 if (!groupFirst.containsKey(group))
110 {
111 groupFirst.put(group, selectOne);
112 }
113
114 List selectItemList = RendererUtils.getSelectItemList(selectOne, facesContext);
115 if (selectItemList != null && !selectItemList.isEmpty())
116 {
117 Converter converter = HtmlRendererUtils.findUIOutputConverterFailSafe(facesContext, selectOne);
118 Object currentValue = null;
119
120
121
122
123
124 if (selectOne.getValueExpression("value") != null)
125 {
126 currentValue = RendererUtils.getStringFromSubmittedValueOrLocalValueReturnNull(
127 facesContext, selectOne);
128 }
129 else
130 {
131 currentValue = RendererUtils.getStringFromSubmittedValueOrLocalValueReturnNull(
132 facesContext, groupFirst.get(group));
133 }
134 SelectItem selectItem = (SelectItem) selectItemList.get(0);
135 renderGroupOrItemRadio(facesContext, selectOne,
136 selectItem, currentValue,
137 converter, pageDirectionLayout, group, 0);
138 }
139 else
140 {
141
142 FormInfo formInfo = RendererUtils.findNestingForm(uiComponent, facesContext);
143 GetSelectItemListCallback callback = new GetSelectItemListCallback(selectOne, group);
144 formInfo.getForm().visitTree(
145 VisitContext.createVisitContext(facesContext, null, FIND_SELECT_LIST_HINTS),
146 callback);
147 renderGroupOrItemRadio(facesContext, selectOne, callback.getSelectItem(),
148 callback.getCurrentValue(), callback.getConverter(),
149 pageDirectionLayout, group, callback.getIndex());
150 }
151 }
152 else
153 {
154
155 writer.startElement(HTML.TABLE_ELEM, selectOne);
156 HtmlRendererUtils.renderHTMLAttributes(writer, selectOne,
157 HTML.SELECT_TABLE_PASSTHROUGH_ATTRIBUTES);
158
159 if (behaviors != null && !behaviors.isEmpty())
160 {
161 writer.writeAttribute(HTML.ID_ATTR, selectOne.getClientId(facesContext), null);
162 }
163 else
164 {
165 HtmlRendererUtils.writeIdIfNecessary(writer, selectOne, facesContext);
166 }
167
168 if (!pageDirectionLayout)
169 {
170 writer.startElement(HTML.TR_ELEM, null);
171 }
172
173 List selectItemList = RendererUtils.getSelectItemList(selectOne, facesContext);
174 Converter converter = HtmlRendererUtils.findUIOutputConverterFailSafe(facesContext, selectOne);
175 Object currentValue = RendererUtils.getStringFromSubmittedValueOrLocalValueReturnNull(
176 facesContext, selectOne);
177
178 int itemNum = 0;
179
180 for (int i = 0; i < selectItemList.size(); i++)
181 {
182 SelectItem selectItem = (SelectItem) selectItemList.get(i);
183
184 itemNum = renderGroupOrItemRadio(facesContext, selectOne,
185 selectItem, currentValue,
186 converter, pageDirectionLayout, itemNum);
187 }
188
189 if (!pageDirectionLayout)
190 {
191 writer.endElement(HTML.TR_ELEM);
192 }
193 writer.endElement(HTML.TABLE_ELEM);
194 }
195 }
196
197
198 protected String getLayout(UIComponent selectOne)
199 {
200 if (selectOne instanceof HtmlSelectOneRadio)
201 {
202 return ((HtmlSelectOneRadio)selectOne).getLayout();
203 }
204
205 return (String)selectOne.getAttributes().get(JSFAttr.LAYOUT_ATTR);
206 }
207
208
209 protected String getStyleClass(UISelectOne selectOne)
210 {
211 if (selectOne instanceof HtmlSelectOneRadio)
212 {
213 return ((HtmlSelectOneRadio)selectOne).getStyleClass();
214 }
215
216 return (String)selectOne.getAttributes().get(JSFAttr.STYLE_CLASS_ATTR);
217 }
218
219 protected int renderGroupOrItemRadio(FacesContext facesContext,
220 UIComponent uiComponent, SelectItem selectItem,
221 Object currentValue,
222 Converter converter, boolean pageDirectionLayout,
223 Integer itemNum) throws IOException
224 {
225 return renderGroupOrItemRadio(facesContext, uiComponent, selectItem,
226 currentValue,converter, pageDirectionLayout, null, itemNum);
227 }
228
229
230
231
232
233 protected int renderGroupOrItemRadio(FacesContext facesContext,
234 UIComponent uiComponent, SelectItem selectItem,
235 Object currentValue,
236 Converter converter, boolean pageDirectionLayout, String group,
237 Integer itemNum) throws IOException
238 {
239
240 ResponseWriter writer = facesContext.getResponseWriter();
241
242 boolean isSelectItemGroup = (selectItem instanceof SelectItemGroup);
243
244
245
246
247
248 UISelectOne selectOne = (UISelectOne)uiComponent;
249
250 if (isSelectItemGroup)
251 {
252 if (pageDirectionLayout)
253 {
254 writer.startElement(HTML.TR_ELEM, null);
255 }
256
257 writer.startElement(HTML.TD_ELEM, null);
258 if (selectItem.isEscape())
259 {
260 writer.writeText(selectItem.getLabel(),HTML.LABEL_ATTR);
261 }
262 else
263 {
264 writer.write(selectItem.getLabel());
265 }
266 writer.endElement(HTML.TD_ELEM);
267
268 if (pageDirectionLayout)
269 {
270 writer.endElement(HTML.TR_ELEM);
271 writer.startElement(HTML.TR_ELEM, null);
272 }
273 writer.startElement(HTML.TD_ELEM, null);
274
275 writer.startElement(HTML.TABLE_ELEM, null);
276 writer.writeAttribute(HTML.BORDER_ATTR, "0", null);
277
278 if(!pageDirectionLayout)
279 {
280 writer.startElement(HTML.TR_ELEM, null);
281 }
282
283 SelectItemGroup selectItemGroup = (SelectItemGroup) selectItem;
284 SelectItem[] selectItems = selectItemGroup.getSelectItems();
285
286 for (SelectItem groupSelectItem : selectItems)
287 {
288 itemNum = renderGroupOrItemRadio(facesContext, selectOne, groupSelectItem, currentValue,
289 converter, pageDirectionLayout, itemNum);
290 }
291
292 if(!pageDirectionLayout)
293 {
294 writer.endElement(HTML.TR_ELEM);
295 }
296 writer.endElement(HTML.TABLE_ELEM);
297 writer.endElement(HTML.TD_ELEM);
298
299 if (pageDirectionLayout)
300 {
301 writer.endElement(HTML.TR_ELEM);
302 }
303
304 }
305 else
306 {
307 String itemStrValue = RendererUtils.getConvertedStringValue(
308 facesContext, selectOne, converter, selectItem.getValue());
309 boolean itemChecked = (itemStrValue == null) ?
310 itemStrValue == currentValue :
311 "".equals(itemStrValue) ?
312 (currentValue == null || itemStrValue.equals(currentValue)) :
313 itemStrValue.equals(currentValue);
314
315
316
317
318
319 if (HtmlRendererUtils.isHideNoSelectionOption(uiComponent) && selectItem.isNoSelectionOption()
320 && currentValue != null && !"".equals(currentValue) && !itemChecked)
321 {
322
323 return itemNum;
324 }
325
326
327 boolean renderGroupId = false;
328 if (group != null && !group.isEmpty())
329 {
330
331 renderGroupId = true;
332 }
333 else
334 {
335 if (pageDirectionLayout)
336 {
337 writer.startElement(HTML.TR_ELEM, null);
338 }
339 writer.startElement(HTML.TD_ELEM, null);
340 }
341
342 boolean itemDisabled = selectItem.isDisabled();
343
344 String itemId = renderRadio(facesContext, selectOne, itemStrValue, itemDisabled,
345 itemChecked, renderGroupId, renderGroupId ? null : itemNum);
346
347
348 boolean componentDisabled = isDisabled(facesContext, selectOne);
349 boolean disabled = (componentDisabled || itemDisabled);
350
351 HtmlRendererUtils.renderLabel(writer, selectOne,
352 renderGroupId ? selectOne.getClientId(facesContext) : itemId,
353 selectItem, disabled);
354
355 if (group != null && group.length() > 0)
356 {
357
358 }
359 else
360 {
361 writer.endElement(HTML.TD_ELEM);
362 if (pageDirectionLayout)
363 {
364 writer.endElement(HTML.TR_ELEM);
365 }
366 }
367
368
369 itemNum++;
370 }
371 return itemNum;
372 }
373
374 @Deprecated
375 protected void renderRadio(FacesContext facesContext,
376 UIComponent uiComponent,
377 String value,
378 String label,
379 boolean disabled,
380 boolean checked, boolean renderId)
381 throws IOException
382 {
383 renderRadio(facesContext, (UIInput) uiComponent, value, disabled, checked, renderId, 0);
384 }
385
386
387
388
389
390 protected String renderRadio(FacesContext facesContext,
391 UIInput uiComponent,
392 String value,
393 boolean disabled,
394 boolean checked,
395 boolean renderId,
396 Integer itemNum)
397 throws IOException
398 {
399 String clientId = uiComponent.getClientId(facesContext);
400
401 String itemId = (itemNum == null) ? null : clientId +
402 facesContext.getNamingContainerSeparatorChar() + itemNum;
403
404 ResponseWriter writer = facesContext.getResponseWriter();
405
406 writer.startElement(HTML.INPUT_ELEM, uiComponent);
407
408 if (itemId != null)
409 {
410 writer.writeAttribute(HTML.ID_ATTR, itemId, null);
411 }
412 else if (renderId)
413 {
414 writer.writeAttribute(HTML.ID_ATTR, clientId, null);
415 }
416 writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_RADIO, null);
417
418 String group = uiComponent instanceof HtmlSelectOneRadio ? ((HtmlSelectOneRadio) uiComponent).getGroup() : null;
419 if (group != null && !group.isEmpty())
420 {
421 FormInfo formInfo = RendererUtils.findNestingForm(uiComponent, facesContext);
422 writer.writeAttribute(HTML.NAME_ATTR, formInfo.getFormName()+
423 facesContext.getNamingContainerSeparatorChar() + group, null);
424 }
425 else
426 {
427 writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
428 }
429
430 if (disabled)
431 {
432 writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR, null);
433 }
434
435 if (checked)
436 {
437 writer.writeAttribute(HTML.CHECKED_ATTR, HTML.CHECKED_ATTR, null);
438 }
439
440 if (group != null && group.length() > 0)
441 {
442 if (value != null)
443 {
444 writer.writeAttribute(HTML.VALUE_ATTR,
445 clientId + facesContext.getNamingContainerSeparatorChar() +value, null);
446 }
447 else
448 {
449 writer.writeAttribute(HTML.VALUE_ATTR,
450 clientId + facesContext.getNamingContainerSeparatorChar() + "", null);
451 }
452 }
453 else
454 {
455 if (value != null)
456 {
457 writer.writeAttribute(HTML.VALUE_ATTR, value, null);
458 }
459 else
460 {
461 writer.writeAttribute(HTML.VALUE_ATTR, "", null);
462 }
463 }
464
465 Map<String, List<ClientBehavior>> behaviors = null;
466 if (uiComponent instanceof ClientBehaviorHolder)
467 {
468 behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
469
470 long commonPropertiesMarked = 0L;
471 if (isCommonPropertiesOptimizationEnabled(facesContext))
472 {
473 commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
474 }
475 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
476 {
477 CommonPropertyUtils.renderChangeEventProperty(writer,
478 commonPropertiesMarked, uiComponent);
479 CommonPropertyUtils.renderEventProperties(writer,
480 commonPropertiesMarked, uiComponent);
481 CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer,
482 commonPropertiesMarked, uiComponent);
483 }
484 else
485 {
486 HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(facesContext, writer, uiComponent,
487 itemId != null ? itemId : clientId, behaviors);
488 if (isCommonEventsOptimizationEnabled(facesContext))
489 {
490 Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
491 CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer,
492 commonPropertiesMarked, commonEventsMarked, uiComponent,
493 itemId != null ? itemId : clientId, behaviors);
494 CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
495 facesContext, writer, commonPropertiesMarked, commonEventsMarked, uiComponent,
496 itemId != null ? itemId : clientId, behaviors);
497 }
498 else
499 {
500 HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, uiComponent,
501 itemId != null ? itemId : clientId, behaviors);
502 HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
503 facesContext, writer, uiComponent,
504 itemId != null ? itemId : clientId, behaviors);
505 }
506 }
507 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
508 HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE_AND_EVENTS);
509 }
510 else
511 {
512 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
513 HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE);
514 }
515
516 if (isDisabled(facesContext, uiComponent))
517 {
518 writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
519 }
520
521 writer.endElement(HTML.INPUT_ELEM);
522
523 return itemId;
524 }
525
526
527 protected boolean isDisabled(FacesContext facesContext, UIComponent uiComponent)
528 {
529
530 if (uiComponent instanceof HtmlSelectOneRadio)
531 {
532 return ((HtmlSelectOneRadio)uiComponent).isDisabled();
533 }
534
535 return RendererUtils.getBooleanAttribute(uiComponent, HTML.DISABLED_ATTR, false);
536 }
537
538 @Override
539 public void decode(FacesContext facesContext, UIComponent uiComponent)
540 {
541 RendererUtils.checkParamValidity(facesContext, uiComponent, null);
542 if (uiComponent instanceof UIInput)
543 {
544 HtmlRendererUtils.decodeUISelectOne(facesContext, uiComponent);
545 }
546 if (uiComponent instanceof ClientBehaviorHolder &&
547 !HtmlRendererUtils.isDisabled(uiComponent))
548 {
549 HtmlRendererUtils.decodeClientBehaviors(facesContext, uiComponent);
550 }
551 }
552
553 @Override
554 public Object getConvertedValue(FacesContext facesContext, UIComponent uiComponent, Object submittedValue)
555 throws ConverterException
556 {
557 RendererUtils.checkParamValidity(facesContext, uiComponent, UISelectOne.class);
558 return RendererUtils.getConvertedUISelectOneValue(facesContext,
559 (UISelectOne)uiComponent, submittedValue);
560 }
561
562 private static class GetSelectItemListCallback implements VisitCallback
563 {
564 private final UISelectOne selectOneRadio;
565 private final String group;
566
567 private final List<UISelectOne> selectOneRadios;
568
569 private int index;
570 private SelectItem selectItem;
571 private Converter converter;
572 private Object currentValue;
573
574 public GetSelectItemListCallback(UISelectOne selectOneRadio, String group)
575 {
576 this.selectOneRadio = selectOneRadio;
577 this.group = group;
578
579 this.selectOneRadios = new ArrayList<>();
580 }
581
582 @Override
583 public VisitResult visit(VisitContext context, UIComponent target)
584 {
585 if (target instanceof UISelectOne)
586 {
587 UISelectOne targetSelectOneRadio = ((UISelectOne) target);
588 String targetGroup = targetSelectOneRadio.getGroup();
589 if (group.equals(targetGroup))
590 {
591 selectOneRadios.add(targetSelectOneRadio);
592
593
594 index = selectOneRadios.indexOf(selectOneRadio);
595 if (index != -1)
596 {
597 UISelectOne first = selectOneRadios.get(0);
598
599
600
601 List<SelectItem> selectItemList = RendererUtils.getSelectItemList(first,
602 context.getFacesContext());
603 if (selectItemList == null || selectItemList.isEmpty())
604 {
605 throw new FacesException("UISelectOne with id=\"" + first.getId()
606 + "\" and group=\"" + group + "\" does not have any UISelectItems!");
607 }
608
609
610 selectItem = selectItemList.get(index);
611 converter = HtmlRendererUtils.findUIOutputConverterFailSafe(context.getFacesContext(), first);
612 currentValue = RendererUtils.getStringFromSubmittedValueOrLocalValueReturnNull(
613 context.getFacesContext(), first);
614
615 return VisitResult.COMPLETE;
616 }
617
618 return VisitResult.REJECT;
619 }
620 else
621 {
622 return VisitResult.REJECT;
623 }
624 }
625
626 return VisitResult.ACCEPT;
627 }
628
629 public int getIndex()
630 {
631 return index;
632 }
633
634 public SelectItem getSelectItem()
635 {
636 return selectItem;
637 }
638
639 public Converter getConverter()
640 {
641 return converter;
642 }
643
644 public Object getCurrentValue()
645 {
646 return currentValue;
647 }
648 }
649
650 }