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.UINamingContainer;
30  import javax.faces.component.UISelectBoolean;
31  import javax.faces.component.UISelectMany;
32  import javax.faces.component.UISelectOne;
33  import javax.faces.component.behavior.ClientBehavior;
34  import javax.faces.component.behavior.ClientBehaviorHolder;
35  import javax.faces.component.html.HtmlSelectBooleanCheckbox;
36  import javax.faces.component.html.HtmlSelectManyCheckbox;
37  import javax.faces.context.FacesContext;
38  import javax.faces.context.ResponseWriter;
39  import javax.faces.convert.Converter;
40  import javax.faces.convert.ConverterException;
41  import javax.faces.model.SelectItem;
42  import javax.faces.model.SelectItemGroup;
43  
44  import org.apache.myfaces.shared.renderkit.JSFAttr;
45  import org.apache.myfaces.shared.renderkit.html.util.JavascriptUtils;
46  import org.apache.myfaces.shared.renderkit.html.util.ResourceUtils;
47  
48  /**
49   * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
50   * @author Anton Koinov
51   * @version $Revision: 1237537 $ $Date: 2012-01-29 22:41:18 -0500 (Sun, 29 Jan 2012) $
52   */
53  public class HtmlCheckboxRendererBase extends HtmlRenderer
54  {
55      //private static final Log log = LogFactory
56      //        .getLog(HtmlCheckboxRendererBase.class);
57      private static final Logger log = Logger.getLogger(HtmlCheckboxRendererBase.class.getName());
58  
59      private static final String PAGE_DIRECTION = "pageDirection";
60  
61      private static final String LINE_DIRECTION = "lineDirection";
62  
63      private static final String EXTERNAL_TRUE_VALUE = "true";
64  
65      public void encodeEnd(FacesContext facesContext, UIComponent uiComponent)
66              throws IOException
67      {
68          org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, uiComponent, null);
69          
70          Map<String, List<ClientBehavior>> behaviors = null;
71          if (uiComponent instanceof ClientBehaviorHolder)
72          {
73              behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
74              if (!behaviors.isEmpty())
75              {
76                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
77              }
78          }
79          
80          if (uiComponent instanceof UISelectBoolean)
81          {
82              Boolean value = org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanValue( uiComponent );
83              boolean isChecked = value != null ? value.booleanValue() : false;
84              renderCheckbox(facesContext, uiComponent, EXTERNAL_TRUE_VALUE, false,isChecked, true, null); 
85                  //TODO: the selectBoolean is never disabled
86          }
87          else if (uiComponent instanceof UISelectMany)
88          {
89              renderCheckboxList(facesContext, (UISelectMany) uiComponent);
90          }
91          else
92          {
93              throw new IllegalArgumentException("Unsupported component class "
94                      + uiComponent.getClass().getName());
95          }
96      }
97  
98      public void renderCheckboxList(FacesContext facesContext,
99              UISelectMany selectMany) throws IOException
100     {
101 
102         String layout = getLayout(selectMany);
103         boolean pageDirectionLayout = false; //Default to lineDirection
104         if (layout != null)
105         {
106             if (layout.equals(PAGE_DIRECTION))
107             {
108                 pageDirectionLayout = true;
109             } 
110             else if (layout.equals(LINE_DIRECTION))
111             {
112                 pageDirectionLayout = false;
113             }
114             else
115             {
116                 log.severe("Wrong layout attribute for component "
117                         + selectMany.getClientId(facesContext) + ": " + layout);
118             }
119         }
120 
121         ResponseWriter writer = facesContext.getResponseWriter();
122 
123         writer.startElement(HTML.TABLE_ELEM, selectMany);
124         HtmlRendererUtils.renderHTMLAttributes(writer, selectMany,
125                 HTML.SELECT_TABLE_PASSTHROUGH_ATTRIBUTES);
126         
127         Map<String, List<ClientBehavior>> behaviors = null;
128         if (selectMany instanceof ClientBehaviorHolder)
129         {
130             behaviors = ((ClientBehaviorHolder) selectMany).getClientBehaviors();
131         }
132         
133         if (behaviors != null && !behaviors.isEmpty())
134         {
135             writer.writeAttribute(HTML.ID_ATTR, selectMany.getClientId(facesContext), null);
136         }
137         else
138         {
139             HtmlRendererUtils.writeIdIfNecessary(writer, selectMany, facesContext);
140         }        
141 
142         if (!pageDirectionLayout)
143         {
144             writer.startElement(HTML.TR_ELEM, selectMany);
145         }
146         
147         Converter converter = getConverter(facesContext, selectMany);
148 
149         Set lookupSet = org.apache.myfaces.shared.renderkit.RendererUtils.getSubmittedValuesAsSet(
150                 facesContext, selectMany, converter, selectMany);
151         boolean useSubmittedValues = lookupSet != null;
152 
153         if (!useSubmittedValues)
154         {
155             lookupSet = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectedValuesAsSet(
156                     facesContext, selectMany, converter, selectMany);
157         }
158 
159         int itemNum = 0;
160 
161         for (Iterator it = org.apache.myfaces.shared.renderkit.RendererUtils.getSelectItemList(
162                 selectMany, facesContext)
163                 .iterator(); it.hasNext();)
164         {
165             SelectItem selectItem = (SelectItem) it.next();
166             
167             itemNum = renderGroupOrItemCheckbox(facesContext, selectMany, 
168                                                 selectItem, useSubmittedValues, lookupSet, 
169                                                 converter, pageDirectionLayout, itemNum);
170         }
171 
172         if (!pageDirectionLayout)
173         {
174             writer.endElement(HTML.TR_ELEM);
175         }
176         writer.endElement(HTML.TABLE_ELEM);
177     }
178 
179     protected String getLayout(UISelectMany selectMany)
180     {
181         if (selectMany instanceof HtmlSelectManyCheckbox)
182         {
183             return ((HtmlSelectManyCheckbox) selectMany).getLayout();
184         } 
185         
186         return (String) selectMany.getAttributes().get(JSFAttr.LAYOUT_ATTR);
187     }
188     
189     /**
190      * 
191      * @param facesContext
192      * @param uiComponent
193      * @param selectItem
194      * @param useSubmittedValues
195      * @param lookupSet
196      * @param converter
197      * @param pageDirectionLayout
198      * @param itemNum
199      * @return the itemNum for the next option
200      * @throws IOException
201      */
202     protected int renderGroupOrItemCheckbox(FacesContext facesContext,
203                                              UIComponent uiComponent, SelectItem selectItem,
204                                              boolean useSubmittedValues, Set lookupSet,
205                                              Converter converter, boolean pageDirectionLayout, 
206                                              Integer itemNum) throws IOException
207     {
208 
209         ResponseWriter writer = facesContext.getResponseWriter();
210 
211         boolean isSelectItemGroup = (selectItem instanceof SelectItemGroup);
212 
213         UISelectMany selectMany = (UISelectMany) uiComponent;
214 
215         if (isSelectItemGroup)
216         {
217             if (pageDirectionLayout)
218             {
219                 writer.startElement(HTML.TR_ELEM, selectMany);
220             }
221 
222             writer.startElement(HTML.TD_ELEM, selectMany);
223             writer.write(selectItem.getLabel());
224             writer.endElement(HTML.TD_ELEM);
225 
226             if (pageDirectionLayout)
227             {
228                 writer.endElement(HTML.TR_ELEM);
229                 writer.startElement(HTML.TR_ELEM, selectMany);
230             }
231             writer.startElement(HTML.TD_ELEM, selectMany);
232 
233             writer.startElement(HTML.TABLE_ELEM, selectMany);
234             writer.writeAttribute(HTML.BORDER_ATTR, "0", null);
235             
236             if(!pageDirectionLayout)
237             {
238                 writer.startElement(HTML.TR_ELEM, 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, selectMany);
287             }
288             writer.startElement(HTML.TD_ELEM, 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                 UINamingContainer.getSeparatorChar(facesContext) + 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 && JavascriptUtils.isJavascriptAllowed(
369                     facesContext.getExternalContext()))
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, behaviors);
388                     if (isCommonEventsOptimizationEnabled(facesContext))
389                     {
390                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
391                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
392                                 commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
393                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
394                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
395                     }
396                     else
397                     {
398                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, 
399                                 writer, uiComponent, behaviors);
400                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
401                                 facesContext, writer, uiComponent, behaviors);
402                     }
403                 }
404                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
405                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
406             }
407             else
408             {
409                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
410                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
411             }
412         }
413         else
414         {
415             if (uiComponent instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
416                     facesContext.getExternalContext()))
417             {
418                 behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
419                 
420                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
421                 {
422                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
423                     CommonPropertyUtils.renderChangeEventProperty(writer, 
424                             commonPropertiesMarked, uiComponent);
425                     CommonPropertyUtils.renderEventProperties(writer, 
426                             commonPropertiesMarked, uiComponent);
427                     CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
428                             commonPropertiesMarked, uiComponent);
429                 }
430                 else
431                 {
432                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
433                     HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(
434                             facesContext, writer, uiComponent, behaviors);
435                     if (isCommonEventsOptimizationEnabled(facesContext))
436                     {
437                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
438                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
439                                 commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
440                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
441                             facesContext, writer, commonPropertiesMarked, commonEventsMarked,
442                             uiComponent, behaviors);
443                     }
444                     else
445                     {
446                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer,
447                                 uiComponent, behaviors);
448                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
449                                 facesContext, writer, uiComponent, behaviors);
450                     }
451                 }
452                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, 
453                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE_AND_EVENTS);
454             }
455             else
456             {
457                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
458                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE);
459             }
460         }
461         if (isDisabled(facesContext, uiComponent))
462         {
463             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
464         }
465         
466         writer.endElement(HTML.INPUT_ELEM);
467 
468         return itemId;
469     }
470 
471     protected boolean isDisabled(FacesContext facesContext,
472             UIComponent component)
473     {
474         //TODO: overwrite in extended HtmlCheckboxRenderer and check for
475         // enabledOnUserRole
476         if (component instanceof HtmlSelectBooleanCheckbox)
477         {
478             return ((HtmlSelectBooleanCheckbox) component).isDisabled();
479         }
480         else if (component instanceof HtmlSelectManyCheckbox)
481         {
482             return ((HtmlSelectManyCheckbox) component).isDisabled();
483         }
484         else
485         {
486             return org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanAttribute(component,
487                     HTML.DISABLED_ATTR, false);
488         }
489     }
490 
491     public void decode(FacesContext facesContext, UIComponent component)
492     {
493         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, null);
494         if (component instanceof UISelectBoolean)
495         {
496             HtmlRendererUtils.decodeUISelectBoolean(facesContext, component);
497         }
498         else if (component instanceof UISelectMany)
499         {
500             HtmlRendererUtils.decodeUISelectMany(facesContext, component);
501         }
502         else
503         {
504             throw new IllegalArgumentException("Unsupported component class "
505                     + component.getClass().getName());
506         }
507         if (component instanceof ClientBehaviorHolder &&
508                 !HtmlRendererUtils.isDisabled(component))
509         {
510             HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
511         }
512     }
513 
514     public Object getConvertedValue(FacesContext facesContext,
515             UIComponent component, Object submittedValue)
516             throws ConverterException
517     {
518         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, null);
519         if (component instanceof UISelectBoolean)
520         {
521             return submittedValue;
522         }
523         else if (component instanceof UISelectMany)
524         {
525             return org.apache.myfaces.shared.renderkit.RendererUtils.getConvertedUISelectManyValue(facesContext,
526                     (UISelectMany) component, submittedValue);
527         }
528         else
529         {
530             throw new IllegalArgumentException("Unsupported component class "
531                     + component.getClass().getName());
532         }
533     }
534     
535     /**
536      * Gets the converter for the given component rendered by this renderer.
537      * @param facesContext
538      * @param component
539      * @return
540      */
541     protected Converter getConverter(FacesContext facesContext,
542             UIComponent component)
543     {
544         if (component instanceof UISelectMany)
545         {
546             return HtmlRendererUtils.findUISelectManyConverterFailsafe(facesContext, 
547                     (UISelectMany) component);
548         }
549         else if (component instanceof UISelectOne)
550         {
551             return HtmlRendererUtils.findUIOutputConverterFailSafe(facesContext, component);
552         }
553         return null;
554     }
555     
556 }