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: 1393891 $ $Date: 2012-10-03 21:55:02 -0500 (Wed, 03 Oct 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             if (selectItem.isEscape())
224             {
225                 writer.writeText(selectItem.getLabel(),HTML.LABEL_ATTR);
226             }
227             else
228             {
229                 writer.write(selectItem.getLabel());
230             }
231             writer.endElement(HTML.TD_ELEM);
232 
233             if (pageDirectionLayout)
234             {
235                 writer.endElement(HTML.TR_ELEM);
236                 writer.startElement(HTML.TR_ELEM, selectMany);
237             }
238             writer.startElement(HTML.TD_ELEM, selectMany);
239 
240             writer.startElement(HTML.TABLE_ELEM, selectMany);
241             writer.writeAttribute(HTML.BORDER_ATTR, "0", null);
242             
243             if(!pageDirectionLayout)
244             {
245                 writer.startElement(HTML.TR_ELEM, selectMany);
246             }
247 
248             SelectItemGroup group = (SelectItemGroup) selectItem;
249             SelectItem[] selectItems = group.getSelectItems();
250             
251             for (SelectItem groupSelectItem : selectItems)
252             {
253                 itemNum = renderGroupOrItemCheckbox(facesContext, selectMany, groupSelectItem, useSubmittedValues,
254                                                     lookupSet, converter, pageDirectionLayout, itemNum);
255             }
256 
257             if(!pageDirectionLayout)
258             {
259                 writer.endElement(HTML.TR_ELEM);
260             }
261             writer.endElement(HTML.TABLE_ELEM);
262             writer.endElement(HTML.TD_ELEM);
263 
264             if (pageDirectionLayout)
265             {
266                 writer.endElement(HTML.TR_ELEM);
267             }
268 
269         }
270         else
271         {
272             Object itemValue = selectItem.getValue(); // TODO : Check here for getSubmittedValue. 
273                                                       // Look at RendererUtils.getValue
274             String itemStrValue = org.apache.myfaces.shared.renderkit.RendererUtils.getConvertedStringValue(
275                     facesContext, selectMany, converter, itemValue);
276             
277             boolean checked = lookupSet.contains(itemStrValue);
278             
279             // IF the hideNoSelectionOption attribute of the component is true
280             // AND this selectItem is the "no selection option"
281             // AND there are currently selected items
282             // AND this item (the "no selection option") is not selected
283             if (HtmlRendererUtils.isHideNoSelectionOption(uiComponent) && selectItem.isNoSelectionOption() 
284                     && lookupSet.size() != 0 && !checked)
285             {
286                 // do not render this selectItem
287                 return itemNum;
288             }
289 
290             writer.write("\t\t");
291             if (pageDirectionLayout)
292             {
293                 writer.startElement(HTML.TR_ELEM, selectMany);
294             }
295             writer.startElement(HTML.TD_ELEM, selectMany);
296 
297             boolean disabled = selectItem.isDisabled();
298 
299             String itemId = renderCheckbox(facesContext, selectMany, itemStrValue, disabled, checked, false, itemNum);
300 
301             // label element after the input
302             boolean componentDisabled = isDisabled(facesContext, selectMany);
303             boolean itemDisabled = (componentDisabled || disabled);
304 
305             HtmlRendererUtils.renderLabel(writer, selectMany, itemId, selectItem, itemDisabled, checked);
306 
307             writer.endElement(HTML.TD_ELEM);
308             if (pageDirectionLayout)
309             {
310                 writer.endElement(HTML.TR_ELEM);
311             }
312             
313             // we rendered one checkbox --> increment itemNum
314             itemNum++;
315         }
316         
317         return itemNum;
318     }
319 
320     @Deprecated
321     protected void renderCheckbox(FacesContext facesContext,
322             UIComponent uiComponent, String value, String label,
323             boolean disabled, boolean checked, boolean renderId) throws IOException
324     {
325         renderCheckbox(facesContext, uiComponent, value, disabled, checked, renderId, 0);
326     }
327 
328     /**
329      * Renders the input item
330      * @return the 'id' value of the rendered element
331      */
332     protected String renderCheckbox(FacesContext facesContext,
333             UIComponent uiComponent, String value, boolean disabled, boolean checked, 
334             boolean renderId, Integer itemNum) throws IOException
335     {
336         String clientId = uiComponent.getClientId(facesContext);
337 
338         String itemId = (itemNum == null)? null : clientId + 
339                 UINamingContainer.getSeparatorChar(facesContext) + itemNum;
340 
341         ResponseWriter writer = facesContext.getResponseWriter();
342 
343         writer.startElement(HTML.INPUT_ELEM, uiComponent);
344 
345         if (itemId != null)
346         {
347             writer.writeAttribute(HTML.ID_ATTR, itemId, null);
348         }
349         else if (renderId) 
350         {
351             writer.writeAttribute(HTML.ID_ATTR, clientId, null);
352         }
353         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_CHECKBOX, null);
354         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
355         
356         if (checked)
357         {
358             writer.writeAttribute(HTML.CHECKED_ATTR, 
359                     org.apache.myfaces.shared.renderkit.html.HTML.CHECKED_ATTR, null);
360         }
361         
362         if (disabled)
363         {
364             writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR, null);
365         }
366 
367         if ((value != null) && (value.length() > 0))
368         {
369             writer.writeAttribute(HTML.VALUE_ATTR, value, null);
370         }
371 
372         Map<String, List<ClientBehavior>> behaviors = null;
373         if (uiComponent instanceof UISelectBoolean)
374         {
375             if (uiComponent instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
376                     facesContext.getExternalContext()))
377             {
378                 behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
379                 
380                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
381                 {
382                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
383                     CommonPropertyUtils.renderChangeEventProperty(writer, 
384                             commonPropertiesMarked, uiComponent);
385                     CommonPropertyUtils.renderEventProperties(writer, 
386                             commonPropertiesMarked, uiComponent);
387                     CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
388                             commonPropertiesMarked, uiComponent);
389                 }
390                 else
391                 {
392                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
393                     HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(
394                             facesContext, writer, uiComponent, behaviors);
395                     if (isCommonEventsOptimizationEnabled(facesContext))
396                     {
397                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
398                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
399                                 commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
400                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
401                             facesContext, writer, commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
402                     }
403                     else
404                     {
405                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, 
406                                 writer, uiComponent, behaviors);
407                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
408                                 facesContext, writer, uiComponent, behaviors);
409                     }
410                 }
411                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
412                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
413             }
414             else
415             {
416                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
417                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
418             }
419         }
420         else
421         {
422             if (uiComponent instanceof ClientBehaviorHolder && JavascriptUtils.isJavascriptAllowed(
423                     facesContext.getExternalContext()))
424             {
425                 behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
426                 
427                 if (behaviors.isEmpty() && isCommonPropertiesOptimizationEnabled(facesContext))
428                 {
429                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
430                     CommonPropertyUtils.renderChangeEventProperty(writer, 
431                             commonPropertiesMarked, uiComponent);
432                     CommonPropertyUtils.renderEventProperties(writer, 
433                             commonPropertiesMarked, uiComponent);
434                     CommonPropertyUtils.renderFieldEventPropertiesWithoutOnchange(writer, 
435                             commonPropertiesMarked, uiComponent);
436                 }
437                 else
438                 {
439                     long commonPropertiesMarked = CommonPropertyUtils.getCommonPropertiesMarked(uiComponent);
440                     HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(
441                             facesContext, writer, uiComponent, behaviors);
442                     if (isCommonEventsOptimizationEnabled(facesContext))
443                     {
444                         Long commonEventsMarked = CommonEventUtils.getCommonEventsMarked(uiComponent);
445                         CommonEventUtils.renderBehaviorizedEventHandlers(facesContext, writer, 
446                                 commonPropertiesMarked, commonEventsMarked, uiComponent, behaviors);
447                         CommonEventUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
448                             facesContext, writer, commonPropertiesMarked, commonEventsMarked,
449                             uiComponent, behaviors);
450                     }
451                     else
452                     {
453                         HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer,
454                                 uiComponent, behaviors);
455                         HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(
456                                 facesContext, writer, uiComponent, behaviors);
457                     }
458                 }
459                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, 
460                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE_AND_EVENTS);
461             }
462             else
463             {
464                 HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
465                         HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE);
466             }
467         }
468         if (isDisabled(facesContext, uiComponent))
469         {
470             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
471         }
472         
473         writer.endElement(HTML.INPUT_ELEM);
474 
475         return itemId;
476     }
477 
478     protected boolean isDisabled(FacesContext facesContext,
479             UIComponent component)
480     {
481         //TODO: overwrite in extended HtmlCheckboxRenderer and check for
482         // enabledOnUserRole
483         if (component instanceof HtmlSelectBooleanCheckbox)
484         {
485             return ((HtmlSelectBooleanCheckbox) component).isDisabled();
486         }
487         else if (component instanceof HtmlSelectManyCheckbox)
488         {
489             return ((HtmlSelectManyCheckbox) component).isDisabled();
490         }
491         else
492         {
493             return org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanAttribute(component,
494                     HTML.DISABLED_ATTR, false);
495         }
496     }
497 
498     public void decode(FacesContext facesContext, UIComponent component)
499     {
500         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, null);
501         if (component instanceof UISelectBoolean)
502         {
503             HtmlRendererUtils.decodeUISelectBoolean(facesContext, component);
504         }
505         else if (component instanceof UISelectMany)
506         {
507             HtmlRendererUtils.decodeUISelectMany(facesContext, component);
508         }
509         else
510         {
511             throw new IllegalArgumentException("Unsupported component class "
512                     + component.getClass().getName());
513         }
514         if (component instanceof ClientBehaviorHolder &&
515                 !HtmlRendererUtils.isDisabled(component))
516         {
517             HtmlRendererUtils.decodeClientBehaviors(facesContext, component);
518         }
519     }
520 
521     public Object getConvertedValue(FacesContext facesContext,
522             UIComponent component, Object submittedValue)
523             throws ConverterException
524     {
525         org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, component, null);
526         if (component instanceof UISelectBoolean)
527         {
528             return submittedValue;
529         }
530         else if (component instanceof UISelectMany)
531         {
532             return org.apache.myfaces.shared.renderkit.RendererUtils.getConvertedUISelectManyValue(facesContext,
533                     (UISelectMany) component, submittedValue);
534         }
535         else
536         {
537             throw new IllegalArgumentException("Unsupported component class "
538                     + component.getClass().getName());
539         }
540     }
541     
542     /**
543      * Gets the converter for the given component rendered by this renderer.
544      * @param facesContext
545      * @param component
546      * @return
547      */
548     protected Converter getConverter(FacesContext facesContext,
549             UIComponent component)
550     {
551         if (component instanceof UISelectMany)
552         {
553             return HtmlRendererUtils.findUISelectManyConverterFailsafe(facesContext, 
554                     (UISelectMany) component);
555         }
556         else if (component instanceof UISelectOne)
557         {
558             return HtmlRendererUtils.findUIOutputConverterFailSafe(facesContext, component);
559         }
560         return null;
561     }
562     
563 }