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.renderkit.html.ext;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.myfaces.component.UserRoleUtils;
24  import org.apache.myfaces.shared_tomahawk.component.DisplayValueOnlyCapable;
25  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
26  import org.apache.myfaces.component.html.ext.HtmlSelectManyCheckbox;
27  import org.apache.myfaces.custom.checkbox.HtmlCheckbox;
28  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
29  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
30  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlCheckboxRendererBase;
31  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
32  
33  import javax.faces.FacesException;
34  import javax.faces.component.NamingContainer;
35  import javax.faces.component.UIComponent;
36  import javax.faces.component.UISelectMany;
37  import javax.faces.component.UISelectBoolean;
38  import javax.faces.context.FacesContext;
39  import javax.faces.context.ResponseWriter;
40  import javax.faces.convert.Converter;
41  import javax.faces.model.SelectItem;
42  import javax.faces.model.SelectItemGroup;
43  
44  import java.io.IOException;
45  import java.util.List;
46  import java.util.Set;
47  
48  
49  /**
50   * @JSFRenderer
51   *   renderKitId = "HTML_BASIC"
52   *   family = "org.apache.myfaces.Checkbox"
53   *   type = "org.apache.myfaces.Checkbox"
54   * 
55   * @JSFRenderer
56   *   renderKitId = "HTML_BASIC"
57   *   family = "javax.faces.SelectBoolean"
58   *   type = "org.apache.myfaces.Checkbox"
59   *   
60   * @JSFRenderer
61   *   renderKitId = "HTML_BASIC"
62   *   family = "javax.faces.SelectMany"
63   *   type = "org.apache.myfaces.Checkbox"
64   * 
65   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
66   * @version $Revision: 784968 $ $Date: 2009-06-15 15:39:15 -0500 (Mon, 15 Jun 2009) $
67   */
68  public class HtmlCheckboxRenderer
69          extends HtmlCheckboxRendererBase
70  {
71      private static final Log log = LogFactory.getLog(HtmlCheckboxRenderer.class);
72  
73      private static final String PAGE_DIRECTION = "pageDirection";
74  
75      private static final String LINE_DIRECTION = "lineDirection";
76  
77      private static final String LAYOUT_SPREAD = "spread";
78  
79      public void encodeEnd(FacesContext context, UIComponent component) throws IOException
80      {
81          if (context == null) throw new NullPointerException("context");
82          if (component == null) throw new NullPointerException("component");
83  
84          if (component instanceof HtmlCheckbox)
85          {
86              renderSingleCheckbox(context, (HtmlCheckbox)component);
87          }
88          else if (component instanceof DisplayValueOnlyCapable && HtmlRendererUtils.isDisplayValueOnly(component))
89          {
90              HtmlRendererUtils.renderDisplayValueOnlyForSelects(context, component);
91          }
92          else if (component instanceof UISelectMany)
93          {
94              String layout = getLayout((UISelectMany)component);
95              if (layout != null && layout.equals(LAYOUT_SPREAD))
96              {
97                  return; //checkbox inputs are rendered by spread checkbox components
98              }
99              else
100             {
101                 super.encodeEnd(context, component);
102             }
103         }
104         else if(component instanceof UISelectBoolean)
105         {
106             super.encodeEnd(context,component);
107         }
108         else
109         {
110             throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
111         }
112     }
113 
114     public void renderCheckboxList(FacesContext facesContext,
115             UISelectMany selectMany) throws IOException
116     {
117         final String layout = getLayout(selectMany);
118         if (layout != null)
119         {
120             Converter converter = getConverter(facesContext, selectMany);
121             if (layout.equals(PAGE_DIRECTION))
122             {
123                 renderCheckboxListVertically(facesContext, selectMany,
124                         converter);
125             }
126             else if (layout.equals(LINE_DIRECTION))
127             {
128                 renderCheckboxListHorizontally(facesContext, selectMany,
129                         converter);
130             }
131             else
132             {
133                 log.error("Wrong layout attribute for component "
134                         + selectMany.getClientId(facesContext) + ": " + layout);
135             }
136         }
137     }
138 
139     protected void renderCheckboxListHorizontally(FacesContext facesContext,
140             UISelectMany selectMany, Converter converter) throws IOException
141     {
142         Set lookupSet = RendererUtils.getSubmittedValuesAsSet(facesContext,
143                 selectMany, converter, selectMany);
144         boolean useSubmittedValues = lookupSet != null;
145         if (!useSubmittedValues)
146         {
147             lookupSet = RendererUtils.getSelectedValuesAsSet(facesContext,
148                     selectMany, converter, selectMany);
149         }
150 
151         ResponseWriter writer = facesContext.getResponseWriter();
152         writer.startElement(HTML.TABLE_ELEM, selectMany);
153         HtmlRendererUtils.renderHTMLAttributes(writer, selectMany,
154                 HTML.SELECT_TABLE_PASSTHROUGH_ATTRIBUTES);
155         HtmlRendererUtils.writeIdIfNecessary(writer, selectMany, facesContext);
156 
157         final int numRows = getLayoutWidth(selectMany);
158         for (int i = 0; i < numRows; i++)
159         {
160             renderRowForHorizontal(facesContext, selectMany, converter,
161                     lookupSet, writer, numRows, i);
162         }
163 
164         writer.endElement(HTML.TABLE_ELEM);
165     }
166 
167     protected void renderRowForHorizontal(FacesContext facesContext,
168             UISelectMany selectMany, Converter converter, Set lookupSet,
169             ResponseWriter writer, int totalRows, int rowNum)
170             throws IOException
171     {
172 
173         writer.startElement(HTML.TR_ELEM, selectMany);
174         int colNum = 0;
175         List items = RendererUtils.getSelectItemList(selectMany);
176         for (int count = rowNum; count < items.size(); count++)
177         {
178             int mod = count % totalRows;
179             if (mod == rowNum)
180             {
181                 colNum++;
182                 SelectItem selectItem = (SelectItem) items.get(count);
183                 writer.startElement(HTML.TD_ELEM, selectMany);
184                 renderGroupOrItemCheckbox(facesContext, selectMany, selectItem,
185                         lookupSet != null, lookupSet, converter, false);
186                 writer.endElement(HTML.TD_ELEM);
187             }
188         }
189         int totalItems = items.size();
190         int totalCols = (totalItems / totalRows);
191         if (totalItems % totalRows != 0)
192         {
193             totalCols++;
194         }
195         if (colNum < totalCols)
196         {
197             writer.startElement(HTML.TD_ELEM, selectMany);
198             writer.endElement(HTML.TD_ELEM);
199         }
200         writer.endElement(HTML.TR_ELEM);
201     }
202 
203     protected void renderCheckboxListVertically(FacesContext facesContext,
204             UISelectMany selectMany, Converter converter) throws IOException
205     {
206 
207         Set lookupSet = RendererUtils.getSubmittedValuesAsSet(facesContext,
208                 selectMany, converter, selectMany);
209         boolean useSubmittedValues = lookupSet != null;
210         if (!useSubmittedValues)
211         {
212             lookupSet = RendererUtils.getSelectedValuesAsSet(facesContext,
213                     selectMany, converter, selectMany);
214         }
215 
216         ResponseWriter writer = facesContext.getResponseWriter();
217         writer.startElement(HTML.TABLE_ELEM, selectMany);
218         HtmlRendererUtils.renderHTMLAttributes(writer, selectMany,
219                 HTML.SELECT_TABLE_PASSTHROUGH_ATTRIBUTES);
220         HtmlRendererUtils.writeIdIfNecessary(writer, selectMany, facesContext);
221 
222         List items = RendererUtils.getSelectItemList(selectMany);
223         int totalItems = items.size();
224         for (int count = 0; count < totalItems; count++)
225         {
226             writer.startElement(HTML.TR_ELEM, selectMany);
227             final int numCols = getLayoutWidth(selectMany);
228             for (int i = 0; i < numCols; i++)
229             {
230                 writer.startElement(HTML.TD_ELEM, selectMany);
231                 if (count < totalItems)
232                 {
233                     SelectItem selectItem = (SelectItem) items.get(count);
234                     renderGroupOrItemCheckbox(facesContext, selectMany,
235                             selectItem, lookupSet != null, lookupSet,
236                             converter, true);
237                 }
238                 writer.endElement(HTML.TD_ELEM);
239                 if (i < numCols - 1)
240                 {
241                     count += 1;
242                 }
243             }
244             writer.endElement(HTML.TR_ELEM);
245         }
246         writer.endElement(HTML.TABLE_ELEM);
247     }
248 
249     protected void renderGroupOrItemCheckbox(FacesContext facesContext,
250             UIComponent uiComponent, SelectItem selectItem,
251             boolean useSubmittedValues, Set lookupSet,
252             Converter converter, boolean pageDirectionLayout) throws IOException {
253         ResponseWriter writer = facesContext.getResponseWriter();
254         
255         boolean isSelectItemGroup = (selectItem instanceof SelectItemGroup);
256 
257         if (isSelectItemGroup)
258         {
259             SelectItemGroup selectItemGroup = (SelectItemGroup) selectItem;
260             renderCheckboxGroup(facesContext, uiComponent, selectItemGroup,
261                     useSubmittedValues, lookupSet, converter,
262                     pageDirectionLayout);
263         }
264         else
265         {
266             UISelectMany selectMany = (UISelectMany) uiComponent;
267             Object itemValue = selectItem.getValue(); // TODO : Check here for getSubmittedValue. Look at RendererUtils.getValue
268             String itemStrValue = getItemStringValue(facesContext, selectMany,
269                     converter, itemValue);
270 
271             boolean checked = (useSubmittedValues && lookupSet
272                     .contains(itemStrValue))
273                     || (!useSubmittedValues && lookupSet.contains(itemValue));
274 
275             boolean disabled = selectItem.isDisabled();
276 
277             writer.startElement(HTML.LABEL_ELEM, selectMany);
278             renderLabelClassIfNecessary(facesContext, selectMany, disabled);
279             renderCheckbox(facesContext, selectMany, itemStrValue, selectItem
280                     .getLabel(), disabled, checked, false);
281             writer.endElement(HTML.LABEL_ELEM);
282         }
283     }
284 
285     protected void renderLabelClassIfNecessary(FacesContext facesContext,
286             UISelectMany selectMany, boolean disabled) throws IOException
287     {
288         String labelClass = null;
289         boolean componentDisabled = isDisabled(facesContext, selectMany);
290         if (componentDisabled || disabled)
291         {
292             labelClass = (String) selectMany.getAttributes().get(
293                     JSFAttr.DISABLED_CLASS_ATTR);
294         }
295         else
296         {
297             labelClass = (String) selectMany.getAttributes().get(
298                     JSFAttr.ENABLED_CLASS_ATTR);
299         }
300         if (labelClass != null)
301         {
302             ResponseWriter writer = facesContext.getResponseWriter();
303             writer.writeAttribute("class", labelClass, "labelClass");
304         }
305     }
306 
307     protected void renderCheckboxGroup(FacesContext facesContext,
308             UIComponent uiComponent, SelectItemGroup selectItemGroup,
309             boolean useSubmittedValues, Set lookupSet,
310             Converter converter, boolean pageDirectionLayout) throws IOException {
311         ResponseWriter writer = facesContext.getResponseWriter();
312         UISelectMany selectMany = (UISelectMany)uiComponent;
313         writer.startElement(HTML.TABLE_ELEM, selectMany);
314         if (pageDirectionLayout)
315             writer.startElement(HTML.TR_ELEM, selectMany);
316         writer.startElement(HTML.TD_ELEM, selectMany);
317         writer.write(selectItemGroup.getLabel());
318         writer.endElement(HTML.TD_ELEM);
319         
320         if (pageDirectionLayout) {
321             writer.endElement(HTML.TR_ELEM);
322             writer.startElement(HTML.TR_ELEM, selectMany);
323         }
324         writer.startElement(HTML.TD_ELEM, selectMany);
325         writer.startElement(HTML.TABLE_ELEM, selectMany);
326         writer.writeAttribute(HTML.BORDER_ATTR, "0", null);
327 
328         SelectItem[] selectItems = selectItemGroup.getSelectItems();
329         for (int i=0; i<selectItems.length; i++) {
330             renderGroupOrItemCheckbox(facesContext, selectMany, selectItems[i], useSubmittedValues, lookupSet, converter, pageDirectionLayout);
331         }
332         
333         writer.endElement(HTML.TABLE_ELEM);
334         writer.endElement(HTML.TD_ELEM);
335         if (pageDirectionLayout)
336             writer.endElement(HTML.TR_ELEM);
337         writer.endElement(HTML.TABLE_ELEM);
338     }
339 
340     /**
341      * Determines the layout setting.  Defaults to
342      * <code>lineDirection</code> if not specified.
343      * @param selectMany the component
344      * @return the layout
345      */
346     protected String getLayout(UISelectMany selectMany) {
347         String layout = super.getLayout(selectMany);
348         if (layout == null) {
349             layout = LINE_DIRECTION;
350         }
351         return layout;
352     }
353     /**
354      * Gets the layout width.
355      * Returns the default layout width of 1 if the layout width
356      * is not set or is less than 1.  
357      * @param selectMany the component
358      * @return the layout width
359      */
360     protected int getLayoutWidth(UISelectMany selectMany) {
361         String layoutWidthString = null;
362         if (selectMany instanceof HtmlSelectManyCheckbox) {
363             layoutWidthString = ((HtmlSelectManyCheckbox) selectMany).getLayoutWidth();
364         } else {
365             layoutWidthString = (String) selectMany.getAttributes().get(JSFAttr.LAYOUT_WIDTH_ATTR);
366         }
367         final int defaultLayoutWidth = 1;
368         int layoutWidth = defaultLayoutWidth;
369         try {
370             if (layoutWidthString != null && layoutWidthString.trim().length() > 0) {
371                 layoutWidth = Integer.parseInt(layoutWidthString);
372             }
373             if (layoutWidth < 1) {
374                 layoutWidth = defaultLayoutWidth;
375             }
376         } catch (Exception e) {
377             layoutWidth = defaultLayoutWidth;
378         }
379         return layoutWidth;
380     }
381 
382     protected void renderSingleCheckbox(FacesContext facesContext, HtmlCheckbox checkbox) throws IOException
383     {
384         String forAttr = checkbox.getFor();
385         if (forAttr == null)
386         {
387             throw new IllegalStateException("mandatory attribute 'for'");
388         }
389         int index = checkbox.getIndex();
390         if (index < 0)
391         {
392             throw new IllegalStateException("positive index must be given");
393         }
394 
395         UIComponent uiComponent = checkbox.findComponent(forAttr);
396         if (uiComponent == null)
397         {
398             throw new IllegalStateException("Could not find component '" + forAttr + "' (calling findComponent on component '" + checkbox.getClientId(facesContext) + "')");
399         }
400         if (!(uiComponent instanceof UISelectMany))
401         {
402             throw new IllegalStateException("UISelectMany expected");
403         }
404 
405         UISelectMany uiSelectMany = (UISelectMany)uiComponent;
406         Converter converter = getConverter(facesContext, uiSelectMany);
407         List selectItemList = RendererUtils.getSelectItemList(uiSelectMany);
408         if (index >= selectItemList.size())
409         {
410             throw new IndexOutOfBoundsException("index " + index + " >= " + selectItemList.size());
411         }
412 
413         SelectItem selectItem = (SelectItem)selectItemList.get(index);
414         Object itemValue = selectItem.getValue();
415         String itemStrValue = getItemStringValue(facesContext, uiSelectMany, converter, itemValue);
416 
417         //TODO: we must cache this Set!
418         Set lookupSet = RendererUtils.getSubmittedValuesAsSet(facesContext, uiComponent, converter, uiSelectMany);
419         
420         boolean useSubmittedValues = (lookupSet != null);
421         if (!useSubmittedValues)
422         {
423             lookupSet = RendererUtils.getSelectedValuesAsSet(facesContext, uiComponent, converter, uiSelectMany);
424         }
425 
426         ResponseWriter writer = facesContext.getResponseWriter();
427         
428         writer.startElement(HTML.LABEL_ELEM, uiSelectMany);
429         
430         renderCheckbox(facesContext,
431                        uiSelectMany,
432                        itemStrValue,
433                        selectItem.getLabel(),
434                        isDisabled(facesContext,uiSelectMany),
435                        lookupSet.contains(itemStrValue), false,
436                        index);
437         
438         writer.endElement(HTML.LABEL_ELEM);
439     }
440 
441     /**
442      * Renders the input item
443      * @return the 'id' value of the rendered element
444      */
445     protected String renderCheckbox(FacesContext facesContext,
446             UIComponent uiComponent, String value, String label,
447             boolean disabled, boolean checked, boolean renderId, int itemNum) throws IOException {
448         String clientId = uiComponent.getClientId(facesContext);
449 
450         String itemId = clientId + NamingContainer.SEPARATOR_CHAR + itemNum;
451 
452         ResponseWriter writer = facesContext.getResponseWriter();
453 
454         writer.startElement(HTML.INPUT_ELEM, uiComponent);
455 
456         if (itemId != null)
457         {
458             writer.writeAttribute(HTML.ID_ATTR, itemId, null);
459         }
460         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_CHECKBOX, null);
461         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
462         
463         if (renderId) {
464             HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
465         }
466 
467         if (checked) {
468             writer.writeAttribute(HTML.CHECKED_ATTR, org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.CHECKED_ATTR, null);
469         }
470         
471         if (disabled) {
472             writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR, null);
473         }
474 
475         if ((value != null) && (value.length() > 0)) {
476             writer.writeAttribute(HTML.VALUE_ATTR, value, null);
477         }
478 
479         if (uiComponent instanceof UISelectBoolean)
480         {
481             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
482                 HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
483         }
484         else
485         {
486             //HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent,
487             //    HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_STYLE);
488             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.INPUT_ATTRIBUTES);
489             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES_WITHOUT_STYLE);
490             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.COMMON_FIELD_ATTRIBUTES_WITHOUT_DISABLED);
491             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.COMMON_FIELD_EVENT_ATTRIBUTES);
492         }
493         if (isDisabled(facesContext, uiComponent)) {
494             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
495         }
496         
497         writer.endElement(HTML.INPUT_ELEM);
498 
499         if ((label != null) && (label.length() > 0)) {
500             writer.write(HTML.NBSP_ENTITY);
501             writer.writeText(label, null);
502         }
503 
504         return itemId;
505     }
506 
507     protected boolean isDisabled(FacesContext facesContext, UIComponent uiComponent)
508     {
509         if (!UserRoleUtils.isEnabledOnUserRole(uiComponent))
510         {
511             return true;
512         }
513         else
514         {
515             return super.isDisabled(facesContext, uiComponent);
516         }
517     }
518 
519     public void decode(FacesContext facesContext, UIComponent uiComponent)
520     {
521         if (uiComponent instanceof HtmlCheckbox)
522         {
523             //nothing to decode
524         }
525         else
526         {
527             super.decode(facesContext, uiComponent);
528         }
529     }
530 
531     protected String getItemStringValue(FacesContext facesContext, UISelectMany selectMany, 
532             Converter converter, Object itemValue) {
533         String itemStrValue;
534         if (converter == null)
535         {
536             itemStrValue = itemValue.toString();
537         }
538         else
539         {
540             itemStrValue = converter.getAsString(facesContext, selectMany, itemValue);
541         }
542         return itemStrValue;
543     }
544 
545     protected Converter getConverter(FacesContext facesContext,
546             UISelectMany selectMany)
547     {
548         Converter converter;
549         try
550         {
551             converter = RendererUtils.findUISelectManyConverter(facesContext,
552                     selectMany);
553         }
554         catch (FacesException e)
555         {
556             log.error("Error finding Converter for component with id "
557                     + selectMany.getClientId(facesContext));
558             converter = null;
559         }
560         return converter;
561     }
562 
563 }