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