View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.shared.renderkit.html;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.logging.Logger;
26  
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.UISelectBoolean;
29  import javax.faces.component.UISelectMany;
30  import javax.faces.component.UISelectOne;
31  import javax.faces.component.behavior.ClientBehavior;
32  import javax.faces.component.behavior.ClientBehaviorHolder;
33  import javax.faces.component.html.HtmlSelectBooleanCheckbox;
34  import javax.faces.component.html.HtmlSelectManyCheckbox;
35  import javax.faces.context.FacesContext;
36  import javax.faces.context.ResponseWriter;
37  import javax.faces.convert.Converter;
38  import javax.faces.convert.ConverterException;
39  import javax.faces.model.SelectItem;
40  import javax.faces.model.SelectItemGroup;
41  
42  import org.apache.myfaces.shared.renderkit.JSFAttr;
43  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
44  
45  public class HtmlCheckboxRendererBase extends HtmlRenderer
46  {
47      //private static final Log log = LogFactory
48      //        .getLog(HtmlCheckboxRendererBase.class);
49      private static final Logger log = Logger.getLogger(HtmlCheckboxRendererBase.class.getName());
50  
51      private static final String PAGE_DIRECTION = "pageDirection";
52  
53      private static final String LINE_DIRECTION = "lineDirection";
54  
55      private static final String EXTERNAL_TRUE_VALUE = "true";
56  
57      public void encodeEnd(FacesContext facesContext, UIComponent uiComponent)
58              throws IOException
59      {
60          org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, uiComponent, null);
61          
62          Map<String, List<ClientBehavior>> behaviors = null;
63          if (uiComponent instanceof ClientBehaviorHolder)
64          {
65              behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
66              if (!behaviors.isEmpty())
67              {
68                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
69              }
70          }
71          
72          if (uiComponent instanceof UISelectBoolean)
73          {
74              Boolean value = org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanValue( uiComponent );
75              boolean isChecked = value != null ? value.booleanValue() : false;
76              renderCheckbox(facesContext, uiComponent, EXTERNAL_TRUE_VALUE, false,isChecked, true, null); 
77                  //TODO: the selectBoolean is never disabled
78          }
79          else if (uiComponent instanceof UISelectMany)
80          {
81              renderCheckboxList(facesContext, (UISelectMany) uiComponent);
82          }
83          else
84          {
85              throw new IllegalArgumentException("Unsupported component class "
86                      + uiComponent.getClass().getName());
87          }
88      }
89  
90      public void renderCheckboxList(FacesContext facesContext,
91              UISelectMany selectMany) throws IOException
92      {
93  
94          String layout = getLayout(selectMany);
95          boolean pageDirectionLayout = false; //Default to lineDirection
96          if (layout != null)
97          {
98              if (layout.equals(PAGE_DIRECTION))
99              {
100                 pageDirectionLayout = true;
101             } 
102             else if (layout.equals(LINE_DIRECTION))
103             {
104                 pageDirectionLayout = false;
105             }
106             else
107             {
108                 log.severe("Wrong layout attribute for component "
109                         + selectMany.getClientId(facesContext) + ": " + layout);
110             }
111         }
112 
113         ResponseWriter writer = facesContext.getResponseWriter();
114 
115         writer.startElement(HTML.TABLE_ELEM, selectMany);
116         HtmlRendererUtils.renderHTMLAttributes(writer, selectMany,
117                 HTML.SELECT_TABLE_PASSTHROUGH_ATTRIBUTES);
118         
119         Map<String, List<ClientBehavior>> behaviors = null;
120         if (selectMany instanceof ClientBehaviorHolder)
121         {
122             behaviors = ((ClientBehaviorHolder) selectMany).getClientBehaviors();
123         }
124         
125         if (behaviors != null && !behaviors.isEmpty())
126         {
127             writer.writeAttribute(HTML.ID_ATTR, selectMany.getClientId(facesContext), null);
128         }
129         else
130         {
131             HtmlRendererUtils.writeIdIfNecessary(writer, selectMany, facesContext);
132         }        
133 
134         if (!pageDirectionLayout)
135         {
136             writer.startElement(HTML.TR_ELEM, null); //selectMany);
137         }
138         
139         Converter converter = getConverter(facesContext, selectMany);
140 
141         Set lookupSet = org.apache.myfaces.shared.renderkit.RendererUtils.getSubmittedValuesAsSet(
142                 facesContext, selectMany, converter, selectMany);
143         boolean useSubmittedValues = lookupSet != null;
144 
145         if (!useSubmittedValues)
146         {
147             lookupSet = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectedValuesAsSet(
148                     facesContext, selectMany, converter, selectMany);
149         }
150 
151         int itemNum = 0;
152 
153         
154         List<SelectItem> selectItemList = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(
155                 selectMany, facesContext);
156 
157         for (int i = 0; i < selectItemList.size(); i++)
158         {
159             SelectItem selectItem = (SelectItem) selectItemList.get(i);
160             
161             itemNum = renderGroupOrItemCheckbox(facesContext, selectMany, 
162                                                 selectItem, useSubmittedValues, lookupSet, 
163                                                 converter, pageDirectionLayout, itemNum);
164         }
165 
166         if (!pageDirectionLayout)
167         {
168             writer.endElement(HTML.TR_ELEM);
169         }
170         writer.endElement(HTML.TABLE_ELEM);
171     }
172 
173     protected String getLayout(UISelectMany selectMany)
174     {
175         if (selectMany instanceof HtmlSelectManyCheckbox)
176         {
177             return ((HtmlSelectManyCheckbox) selectMany).getLayout();
178         } 
179         
180         return (String) selectMany.getAttributes().get(JSFAttr.LAYOUT_ATTR);
181     }
182     
183     /**
184      * 
185      * @param facesContext
186      * @param uiComponent
187      * @param selectItem
188      * @param useSubmittedValues
189      * @param lookupSet
190      * @param converter
191      * @param pageDirectionLayout
192      * @param itemNum
193      * @return the itemNum for the next option
194      * @throws IOException
195      */
196     protected int renderGroupOrItemCheckbox(FacesContext facesContext,
197                                              UIComponent uiComponent, SelectItem selectItem,
198                                              boolean useSubmittedValues, Set lookupSet,
199                                              Converter converter, boolean pageDirectionLayout, 
200                                              Integer itemNum) throws IOException
201     {
202 
203         ResponseWriter writer = facesContext.getResponseWriter();
204 
205         boolean isSelectItemGroup = (selectItem instanceof SelectItemGroup);
206 
207         UISelectMany selectMany = (UISelectMany) uiComponent;
208 
209         if (isSelectItemGroup)
210         {
211             if (pageDirectionLayout)
212             {
213                 writer.startElement(HTML.TR_ELEM, null); // selectMany);
214             }
215 
216             writer.startElement(HTML.TD_ELEM, null); // selectMany);
217             if (selectItem.isEscape())
218             {
219                 writer.writeText(selectItem.getLabel(),HTML.LABEL_ATTR);
220             }
221             else
222             {
223                 writer.write(selectItem.getLabel());
224             }
225             writer.endElement(HTML.TD_ELEM);
226 
227             if (pageDirectionLayout)
228             {
229                 writer.endElement(HTML.TR_ELEM);
230                 writer.startElement(HTML.TR_ELEM, null); // selectMany);
231             }
232             writer.startElement(HTML.TD_ELEM, null); // selectMany);
233 
234             writer.startElement(HTML.TABLE_ELEM, null); // selectMany);
235             writer.writeAttribute(HTML.BORDER_ATTR, "0", null);
236             
237             if(!pageDirectionLayout)
238             {
239                 writer.startElement(HTML.TR_ELEM, null); // selectMany);
240             }
241 
242             SelectItemGroup group = (SelectItemGroup) selectItem;
243             SelectItem[] selectItems = group.getSelectItems();
244             
245             for (SelectItem groupSelectItem : selectItems)
246             {
247                 itemNum = renderGroupOrItemCheckbox(facesContext, selectMany, groupSelectItem, useSubmittedValues,
248                                                     lookupSet, converter, pageDirectionLayout, itemNum);
249             }
250 
251             if(!pageDirectionLayout)
252             {
253                 writer.endElement(HTML.TR_ELEM);
254             }
255             writer.endElement(HTML.TABLE_ELEM);
256             writer.endElement(HTML.TD_ELEM);
257 
258             if (pageDirectionLayout)
259             {
260                 writer.endElement(HTML.TR_ELEM);
261             }
262 
263         }
264         else
265         {
266             Object itemValue = selectItem.getValue(); // TODO : Check here for getSubmittedValue. 
267                                                       // Look at RendererUtils.getValue
268             String itemStrValue = org.apache.myfaces.shared.renderkit.RendererUtils.getConvertedStringValue(
269                     facesContext, selectMany, converter, itemValue);
270             
271             boolean checked = lookupSet.contains(itemStrValue);
272             
273             // IF the hideNoSelectionOption attribute of the component is true
274             // AND this selectItem is the "no selection option"
275             // AND there are currently selected items
276             // AND this item (the "no selection option") is not selected
277             if (HtmlRendererUtils.isHideNoSelectionOption(uiComponent) && selectItem.isNoSelectionOption() 
278                     && !lookupSet.isEmpty() && !checked)
279             {
280                 // do not render this selectItem
281                 return itemNum;
282             }
283 
284             writer.write("\t\t");
285             if (pageDirectionLayout)
286             {
287                 writer.startElement(HTML.TR_ELEM, null); // selectMany);
288             }
289             writer.startElement(HTML.TD_ELEM, null); // selectMany);
290 
291             boolean disabled = selectItem.isDisabled();
292 
293             String itemId = renderCheckbox(facesContext, selectMany, itemStrValue, disabled, checked, false, itemNum);
294 
295             // label element after the input
296             boolean componentDisabled = isDisabled(facesContext, selectMany);
297             boolean itemDisabled = (componentDisabled || disabled);
298 
299             HtmlRendererUtils.renderLabel(writer, selectMany, itemId, selectItem, itemDisabled, checked);
300 
301             writer.endElement(HTML.TD_ELEM);
302             if (pageDirectionLayout)
303             {
304                 writer.endElement(HTML.TR_ELEM);
305             }
306             
307             // we rendered one checkbox --> increment itemNum
308             itemNum++;
309         }
310         
311         return itemNum;
312     }
313 
314     @Deprecated
315     protected void renderCheckbox(FacesContext facesContext,
316             UIComponent uiComponent, String value, String label,
317             boolean disabled, boolean checked, boolean renderId) throws IOException
318     {
319         renderCheckbox(facesContext, uiComponent, value, disabled, checked, renderId, 0);
320     }
321 
322     /**
323      * Renders the input item
324      * @return the 'id' value of the rendered element
325      */
326     protected String renderCheckbox(FacesContext facesContext,
327             UIComponent uiComponent, String value, boolean disabled, boolean checked, 
328             boolean renderId, Integer itemNum) throws IOException
329     {
330         String clientId = uiComponent.getClientId(facesContext);
331 
332         String itemId = (itemNum == null)? null : clientId + 
333                 facesContext.getNamingContainerSeparatorChar() + itemNum;
334 
335         ResponseWriter writer = facesContext.getResponseWriter();
336 
337         writer.startElement(HTML.INPUT_ELEM, uiComponent);
338 
339         if (itemId != null)
340         {
341             writer.writeAttribute(HTML.ID_ATTR, itemId, null);
342         }
343         else if (renderId) 
344         {
345             writer.writeAttribute(HTML.ID_ATTR, clientId, null);
346         }
347         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_CHECKBOX, null);
348         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
349         
350         if (checked)
351         {
352             writer.writeAttribute(HTML.CHECKED_ATTR, 
353                     org.apache.myfaces.shared.renderkit.html.HTML.CHECKED_ATTR, null);
354         }
355         
356         if (disabled)
357         {
358             writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR, null);
359         }
360 
361         if ((value != null) && (value.length() > 0))
362         {
363             writer.writeAttribute(HTML.VALUE_ATTR, value, null);
364         }
365 
366         Map<String, List<ClientBehavior>> behaviors = null;
367         if (uiComponent instanceof UISelectBoolean)
368         {
369             if (uiComponent instanceof ClientBehaviorHolder)
370             {
371                 behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
372                 
373                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
374                 {
375                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
376                     CommonPropertyUtils.renderChangeEventProperty(writer, 
377                             commonPropertiesMarked, uiComponent);
378                     CommonPropertyUtils.renderEventProperties(writer, 
379                             commonPropertiesMarked, uiComponent);
380                     CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
381                             commonPropertiesMarked, uiComponent);
382                 }
383                 else
384                 {
385                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
386                     HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(
387                             facesContext, writer, uiComponent, itemId != null ? itemId : clientId,  behaviors);
388                     if (isCommonEventsOptimizationEnabled(facesContext))
389                     {
390                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
391                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
392                                 commonPropertiesMarked, commonEventsMarked, uiComponent,
393                                 itemId != null ? itemId : clientId, behaviors);
394                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
395                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, uiComponent, 
396                                 itemId != null ? itemId : clientId, behaviors);
397                     }
398                     else
399                     {
400                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, 
401                                 writer, uiComponent, behaviors);
402                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
403                                 facesContext, writer, uiComponent, 
404                                 itemId != null ? itemId : clientId, behaviors);
405                     }
406                 }
407                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
408                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
409             }
410             else
411             {
412                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
413                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
414             }
415         }
416         else
417         {
418             if (uiComponent instanceof ClientBehaviorHolder)
419             {
420                 behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
421                 
422                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
423                 {
424                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
425                     CommonPropertyUtils.renderChangeEventProperty(writer, 
426                             commonPropertiesMarked, uiComponent);
427                     CommonPropertyUtils.renderEventProperties(writer, 
428                             commonPropertiesMarked, uiComponent);
429                     CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
430                             commonPropertiesMarked, uiComponent);
431                 }
432                 else
433                 {
434                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
435                     HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(
436                             facesContext, writer, uiComponent, itemId != null ? itemId : clientId, behaviors);
437                     if (isCommonEventsOptimizationEnabled(facesContext))
438                     {
439                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
440                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
441                                 commonPropertiesMarked, commonEventsMarked, uiComponent, 
442                                 itemId != null ? itemId : clientId, behaviors);
443                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
444                             facesContext, writer, commonPropertiesMarked, commonEventsMarked,
445                             uiComponent, itemId != null ? itemId : clientId, behaviors);
446                     }
447                     else
448                     {
449                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer,
450                                 uiComponent, itemId != null ? itemId : clientId, behaviors);
451                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
452                                 facesContext, writer, uiComponent, itemId != null ? itemId : clientId, behaviors);
453                     }
454                 }
455                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, 
456                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE_AND_EVENTS);
457             }
458             else
459             {
460                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
461                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE);
462             }
463         }
464         if (isDisabled(facesContext, uiComponent))
465         {
466             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
467         }
468         
469         writer.endElement(HTML.INPUT_ELEM);
470 
471         return itemId;
472     }
473 
474     protected boolean isDisabled(FacesContext facesContext,
475             UIComponent component)
476     {
477         //TODO: overwrite in extended HtmlCheckboxRenderer and check for
478         // enabledOnUserRole
479         if (component instanceof HtmlSelectBooleanCheckbox)
480         {
481             return ((HtmlSelectBooleanCheckbox) component).isDisabled();
482         }
483         else if (component instanceof HtmlSelectManyCheckbox)
484         {
485             return ((HtmlSelectManyCheckbox) component).isDisabled();
486         }
487         else
488         {
489             return org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanAttribute(component,
490                     HTML.DISABLED_ATTR, false);
491         }
492     }
493 
494     public void decode(FacesContext facesContext, UIComponent component)
495     {
496         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, null);
497         if (component instanceof UISelectBoolean)
498         {
499             HtmlRendererUtils.decodeUISelectBoolean(facesContext, component);
500         }
501         else if (component instanceof UISelectMany)
502         {
503             HtmlRendererUtils.decodeUISelectMany(facesContext, component);
504         }
505         else
506         {
507             throw new IllegalArgumentException("Unsupported component class "
508                     + component.getClass().getName());
509         }
510         if (component instanceof ClientBehaviorHolder &&
511                 !HtmlRendererUtils.isDisabled(component))
512         {
513             HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
514         }
515     }
516 
517     public Object getConvertedValue(FacesContext facesContext,
518             UIComponent component, Object submittedValue)
519             throws ConverterException
520     {
521         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, null);
522         if (component instanceof UISelectBoolean)
523         {
524             return submittedValue;
525         }
526         else if (component instanceof UISelectMany)
527         {
528             return org.apache.myfaces.shared.renderkit.RendererUtils.getConvertedUISelectManyValue(facesContext,
529                     (UISelectMany) component, submittedValue);
530         }
531         else
532         {
533             throw new IllegalArgumentException("Unsupported component class "
534                     + component.getClass().getName());
535         }
536     }
537     
538     /**
539      * Gets the converter for the given component rendered by this renderer.
540      * @param facesContext
541      * @param component
542      * @return
543      */
544     protected Converter getConverter(FacesContext facesContext,
545             UIComponent component)
546     {
547         if (component instanceof UISelectMany)
548         {
549             return HtmlRendererUtils.findUISelectManyConverterFailsafe(facesContext, 
550                     (UISelectMany) component);
551         }
552         else if (component instanceof UISelectOne)
553         {
554             return HtmlRendererUtils.findUIOutputConverterFailSafe(facesContext, component);
555         }
556         return null;
557     }
558     
559 }