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