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 java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  
25  import javax.faces.FacesException;
26  import javax.faces.component.UIComponent;
27  import javax.faces.component.UINamingContainer;
28  import javax.faces.component.UISelectOne;
29  import javax.faces.component.behavior.ClientBehavior;
30  import javax.faces.component.behavior.ClientBehaviorHolder;
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.ResponseWriter;
33  import javax.faces.convert.Converter;
34  import javax.faces.model.SelectItem;
35  
36  import org.apache.myfaces.component.UserRoleUtils;
37  import org.apache.myfaces.custom.radio.HtmlRadio;
38  import org.apache.myfaces.shared_tomahawk.renderkit.ClientBehaviorEvents;
39  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
40  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
41  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
42  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRadioRendererBase;
43  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
44  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
45  
46  
47  /**
48   * 
49   * @JSFRenderer
50   *   renderKitId = "HTML_BASIC"
51   *   family = "org.apache.myfaces.Radio"
52   *   type = "org.apache.myfaces.Radio"
53   *    
54   * @JSFRenderer
55   *   renderKitId = "HTML_BASIC"
56   *   family = "javax.faces.SelectOne"
57   *   type = "org.apache.myfaces.Radio"
58   * 
59   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
60   * @author Thomas Spiegl
61   * @version $Revision: 685654 $ $Date: 2008-08-13 14:57:50 -0500 (miƩ, 13 ago 2008) $
62   */
63  public class HtmlRadioRenderer
64          extends HtmlRadioRendererBase
65  {
66      //private static final Log log = LogFactory.getLog(HtmlRadioRenderer.class);
67  
68      private static final String LAYOUT_SPREAD = "spread";
69      
70      private static final String[] LABEL_STYLES = { "labelStyle", "labelStyleClass" };
71  
72      public void encodeEnd(FacesContext context, UIComponent component) throws IOException
73      {
74          if (context == null) throw new NullPointerException("context");
75          if (component == null) throw new NullPointerException("component");
76  
77          if (component instanceof HtmlRadio)
78          {
79              renderRadio(context, (HtmlRadio)component);
80          }
81          else if (HtmlRendererUtils.isDisplayValueOnly(component))
82          {
83              HtmlRendererUtils.renderDisplayValueOnlyForSelects(context, component);
84          }
85          else if (component instanceof UISelectOne)
86          {
87              String layout = getLayout(component);
88              if (layout != null && layout.equals(LAYOUT_SPREAD))
89              {
90                  return; //radio inputs are rendered by spread radio components
91              }
92              else
93              {
94                  super.encodeEnd(context, component);
95              }
96          }
97          else
98          {
99              throw new IllegalArgumentException("Unsupported component class " + component.getClass().getName());
100         }
101     }
102 
103     protected void renderRadio(FacesContext facesContext, HtmlRadio radio) throws IOException
104     {
105         String forAttr = radio.getFor();
106         if (forAttr == null)
107         {
108             throw new IllegalStateException("mandatory attribute 'for'");
109         }
110         int index = radio.getIndex();
111         if (index < 0)
112         {
113             throw new IllegalStateException("positive index must be given");
114         }
115 
116         UIComponent uiComponent = radio.findComponent(forAttr);
117         if (uiComponent == null)
118         {
119             throw new IllegalStateException("Could not find component '" + forAttr + "' (calling findComponent on component '" + radio.getClientId(facesContext) + "')");
120         }
121         if (!(uiComponent instanceof UISelectOne))
122         {
123             throw new IllegalStateException("UISelectOne expected");
124         }
125 
126         UISelectOne uiSelectOne = (UISelectOne)uiComponent;
127         Converter converter;
128         List selectItemList = RendererUtils.getSelectItemList(uiSelectOne);
129         if (index >= selectItemList.size())
130         {
131             throw new IndexOutOfBoundsException("index " + index + " >= " + selectItemList.size());
132         }
133 
134         try
135         {
136             converter = RendererUtils.findUIOutputConverter(facesContext, uiSelectOne);
137         }
138         catch (FacesException e)
139         {
140             converter = null;
141         }
142 
143         Object currentValue = RendererUtils.getObjectValue(uiSelectOne);
144         currentValue
145             = RendererUtils.getConvertedStringValue(facesContext, uiSelectOne,
146                                                     converter, currentValue);
147         SelectItem selectItem = (SelectItem)selectItemList.get(index);
148         String itemStrValue
149             = RendererUtils.getConvertedStringValue(facesContext, uiSelectOne,
150                                                     converter,
151                                                     selectItem.getValue());
152 
153         ResponseWriter writer = facesContext.getResponseWriter();
154 
155         //writer.startElement(HTML.LABEL_ELEM, uiSelectOne);
156         
157         //renderRadio(facesContext,
158         //            uiSelectOne,
159         //            itemStrValue,
160         //            selectItem.getLabel(),
161         //            selectItem.isDisabled(),
162         //            itemStrValue.equals(currentValue), false);
163         //writer.endElement(HTML.LABEL_ELEM);
164 
165         //Render the radio component
166         String itemId = renderRadio(
167                 facesContext,
168                 uiSelectOne,
169                 radio,
170                 itemStrValue,
171                 selectItem.isDisabled(),
172                 itemStrValue.equals(currentValue),
173                 false,
174                 index);
175         
176         //Render the
177         // label element after the input
178         boolean componentDisabled = isDisabled(facesContext, uiSelectOne);
179         boolean itemDisabled = selectItem.isDisabled();
180         boolean disabled = (componentDisabled || itemDisabled);
181 
182         renderLabel(writer, radio, uiSelectOne, itemId, selectItem, disabled);
183     }
184     
185     protected String renderRadio(
186             FacesContext facesContext,
187             UISelectOne uiComponent, HtmlRadio radio, 
188             String value, boolean disabled,
189             boolean checked, boolean renderId, Integer itemNum)
190             throws IOException
191     {
192         String clientId = uiComponent.getClientId(facesContext);
193 
194         String itemId = radio.isRenderLogicalId() ? 
195                 clientId + UINamingContainer.getSeparatorChar(facesContext) + itemNum :
196                 radio.getClientId(facesContext);
197 
198         ResponseWriter writer = facesContext.getResponseWriter();
199 
200         writer.startElement(HTML.INPUT_ELEM, uiComponent);
201         
202         // Force id rendering because it is necessary for the label
203         // and for @this work correctly
204         writer.writeAttribute(HTML.ID_ATTR, itemId, null);
205         
206         writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_RADIO, null);
207         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
208         
209         
210         if (disabled)
211         {
212             writer.writeAttribute(HTML.DISABLED_ATTR, HTML.DISABLED_ATTR, null);
213         }
214 
215         if (checked)
216         {
217             writer.writeAttribute(HTML.CHECKED_ATTR, HTML.CHECKED_ATTR, null);
218         }
219 
220         if (value != null)
221         {
222             writer.writeAttribute(HTML.VALUE_ATTR, value, null);
223         }
224         
225         Map<String, List<ClientBehavior>> behaviors = null;
226         if (uiComponent instanceof ClientBehaviorHolder
227                 && JavascriptUtils.isJavascriptAllowed(facesContext
228                         .getExternalContext()))
229         {
230             behaviors = ((ClientBehaviorHolder) uiComponent)
231                     .getClientBehaviors();
232 
233             renderBehaviorizedOnchangeEventHandler(facesContext, writer, radio, uiComponent, itemId, behaviors);
234             renderBehaviorizedEventHandlers(facesContext,writer, radio, uiComponent, itemId, behaviors);
235             renderBehaviorizedFieldEventHandlersWithoutOnchange(facesContext, writer, radio, uiComponent, itemId, behaviors);
236             renderHTMLAttributes(writer, radio, uiComponent, HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
237         }
238         else
239         {
240             renderHTMLAttributes(writer, radio, uiComponent, HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
241         }
242         
243         if (isDisabled(facesContext, uiComponent))
244         {
245             writer.writeAttribute(
246                     org.apache.myfaces.shared_tomahawk.renderkit.html.HTML.DISABLED_ATTR,
247                     Boolean.TRUE, null);
248         }
249 
250         writer.endElement(HTML.INPUT_ELEM);
251 
252         return itemId;
253     }
254     
255     public static void renderLabel(ResponseWriter writer, UIComponent radio,
256             UIComponent component, String forClientId, SelectItem item,
257             boolean disabled) throws IOException
258     {
259         writer.startElement(HTML.LABEL_ELEM, component);
260         writer.writeAttribute(HTML.FOR_ATTR, forClientId, null);
261 
262         String labelClass = null;
263 
264         if (disabled)
265         {
266             labelClass = (String) radio.getAttributes().get(
267                     JSFAttr.DISABLED_CLASS_ATTR);
268             if (labelClass == null)
269             {
270                 labelClass = (String) component.getAttributes().get(
271                         JSFAttr.DISABLED_CLASS_ATTR);
272             }
273         }
274         else
275         {
276             labelClass = (String) radio.getAttributes().get(
277                     JSFAttr.ENABLED_CLASS_ATTR);
278             if (labelClass == null)
279             {
280                 labelClass = (String) component.getAttributes().get(
281                         JSFAttr.ENABLED_CLASS_ATTR);
282             }
283         }
284         if (labelClass != null)
285         {
286             writer.writeAttribute("class", labelClass, "labelClass");
287         }
288 
289         if ((item.getLabel() != null) && (item.getLabel().length() > 0))
290         {
291             // writer.write(HTML.NBSP_ENTITY);
292             writer.write(" ");
293             if (item.isEscape())
294             {
295                 //writer.write(item.getLabel());
296                 writer.writeText(item.getLabel(), null);
297             }
298             else
299             {
300                 //writer.write(HTMLEncoder.encode (item.getLabel()));
301                 writer.write(item.getLabel());
302             }
303         }
304 
305         writer.endElement(HTML.LABEL_ELEM);
306     }
307 
308     private static boolean renderHTMLAttributes(ResponseWriter writer,
309             UIComponent radio, UIComponent selectOne, String[] attributes) throws IOException
310     {
311         boolean somethingDone = false;
312         for (int i = 0, len = attributes.length; i < len; i++)
313         {
314             String attrName = attributes[i];
315             Object value = radio.getAttributes().get(attrName);
316             if (value == null)
317             {
318                 value = selectOne.getAttributes().get(attrName);
319             }
320             if (HtmlRendererUtils.renderHTMLAttribute(writer, attrName, attrName, value ))
321             {
322                 somethingDone = true;
323             }
324         }
325         return somethingDone;
326     }
327     
328     private static boolean renderBehaviorizedOnchangeEventHandler(
329             FacesContext facesContext, ResponseWriter writer, UIComponent radio, UIComponent uiComponent, String targetClientId,
330             Map<String, List<ClientBehavior>> clientBehaviors) throws IOException {
331         boolean hasChange = HtmlRendererUtils.hasClientBehavior(ClientBehaviorEvents.CHANGE, clientBehaviors, facesContext);
332         boolean hasValueChange = HtmlRendererUtils.hasClientBehavior(ClientBehaviorEvents.VALUECHANGE, clientBehaviors, facesContext);
333 
334         String value = (String) radio.getAttributes().get(HTML.ONCHANGE_ATTR);
335         if (value == null)
336         {
337             value = (String) uiComponent.getAttributes().get(HTML.ONCHANGE_ATTR);
338         }
339         if (hasChange && hasValueChange) {
340             String chain = HtmlRendererUtils.buildBehaviorChain(facesContext,
341                     uiComponent, targetClientId, ClientBehaviorEvents.CHANGE, null, ClientBehaviorEvents.VALUECHANGE, null, clientBehaviors,
342                     value, null);
343             
344             return HtmlRendererUtils.renderHTMLAttribute(writer, HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR, chain);
345         } else if (hasChange) {
346             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, HTML.ONCHANGE_ATTR, uiComponent, targetClientId,
347                     ClientBehaviorEvents.CHANGE, null, clientBehaviors, HTML.ONCHANGE_ATTR, value);
348         } else if (hasValueChange) {
349             return HtmlRendererUtils.renderBehaviorizedAttribute(facesContext, writer, HTML.ONCHANGE_ATTR, uiComponent, targetClientId,
350                     ClientBehaviorEvents.VALUECHANGE, null, clientBehaviors, HTML.ONCHANGE_ATTR, value);
351         } else {
352             return HtmlRendererUtils.renderHTMLAttribute(writer, HTML.ONCHANGE_ATTR, HTML.ONCHANGE_ATTR, value);
353         }
354     }
355     
356     private static void renderBehaviorizedEventHandlers(
357             FacesContext facesContext, ResponseWriter writer, UIComponent radio, UIComponent uiComponent, String targetClientId,
358             Map<String, List<ClientBehavior>> clientBehaviors) throws IOException {
359         renderBehaviorizedAttribute(facesContext, writer, HTML.ONCLICK_ATTR, radio, uiComponent, targetClientId,
360                 ClientBehaviorEvents.CLICK, clientBehaviors, HTML.ONCLICK_ATTR);
361         renderBehaviorizedAttribute(facesContext, writer, HTML.ONDBLCLICK_ATTR, radio, uiComponent, targetClientId,
362                 ClientBehaviorEvents.DBLCLICK, clientBehaviors, HTML.ONDBLCLICK_ATTR);
363         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEDOWN_ATTR, radio, uiComponent, targetClientId,
364                 ClientBehaviorEvents.MOUSEDOWN, clientBehaviors, HTML.ONMOUSEDOWN_ATTR);
365         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEUP_ATTR, radio, uiComponent, targetClientId,
366                 ClientBehaviorEvents.MOUSEUP, clientBehaviors, HTML.ONMOUSEUP_ATTR);
367         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEOVER_ATTR, radio, uiComponent, targetClientId,
368                 ClientBehaviorEvents.MOUSEOVER, clientBehaviors, HTML.ONMOUSEOVER_ATTR);
369         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEMOVE_ATTR, radio, uiComponent, targetClientId,
370                 ClientBehaviorEvents.MOUSEMOVE, clientBehaviors, HTML.ONMOUSEMOVE_ATTR);
371         renderBehaviorizedAttribute(facesContext, writer, HTML.ONMOUSEOUT_ATTR, radio, uiComponent, targetClientId,
372                 ClientBehaviorEvents.MOUSEOUT, clientBehaviors, HTML.ONMOUSEOUT_ATTR);
373         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYPRESS_ATTR, radio, uiComponent, targetClientId,
374                 ClientBehaviorEvents.KEYPRESS, clientBehaviors, HTML.ONKEYPRESS_ATTR);
375         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYDOWN_ATTR, radio, uiComponent, targetClientId,
376                 ClientBehaviorEvents.KEYDOWN, clientBehaviors, HTML.ONKEYDOWN_ATTR);
377         renderBehaviorizedAttribute(facesContext, writer, HTML.ONKEYUP_ATTR, radio, uiComponent, targetClientId,
378                 ClientBehaviorEvents.KEYUP, clientBehaviors, HTML.ONKEYUP_ATTR);
379     }
380     
381     private static void renderBehaviorizedFieldEventHandlersWithoutOnchange(
382             FacesContext facesContext, ResponseWriter writer, UIComponent radio, UIComponent uiComponent, String targetClientId,
383             Map<String, List<ClientBehavior>> clientBehaviors) throws IOException {
384         renderBehaviorizedAttribute(facesContext, writer, HTML.ONFOCUS_ATTR, radio, uiComponent, targetClientId,
385                 ClientBehaviorEvents.FOCUS, clientBehaviors, HTML.ONFOCUS_ATTR);
386         renderBehaviorizedAttribute(facesContext, writer, HTML.ONBLUR_ATTR, radio, uiComponent, targetClientId,
387                 ClientBehaviorEvents.BLUR, clientBehaviors, HTML.ONBLUR_ATTR);
388         renderBehaviorizedAttribute(facesContext, writer, HTML.ONSELECT_ATTR, radio, uiComponent, targetClientId,
389                 ClientBehaviorEvents.SELECT, clientBehaviors, HTML.ONSELECT_ATTR);
390     }
391     
392     private static boolean renderBehaviorizedAttribute(
393             FacesContext facesContext, ResponseWriter writer,
394             String componentProperty, UIComponent radio, UIComponent component, String targetClientId,
395             String eventName, Map<String, List<ClientBehavior>> clientBehaviors,
396             String htmlAttrName) throws IOException
397     {
398         String attributeValue = (String) radio.getAttributes().get(componentProperty);
399         if (attributeValue == null)
400         {
401             attributeValue = (String) component.getAttributes().get(componentProperty);
402         }
403         return HtmlRendererUtils.renderBehaviorizedAttribute(
404                 facesContext, writer,
405                 componentProperty, component, targetClientId,
406                 eventName, null, clientBehaviors,
407                 htmlAttrName, attributeValue);
408     }
409 
410     protected boolean isDisabled(FacesContext facesContext, UIComponent uiComponent)
411     {
412         if (!UserRoleUtils.isEnabledOnUserRole(uiComponent))
413         {
414             return true;
415         }
416         else
417         {
418             return super.isDisabled(facesContext, uiComponent);
419         }
420     }
421 
422 
423     public void decode(FacesContext facesContext, UIComponent uiComponent)
424     {
425         if (uiComponent instanceof HtmlRadio)
426         {
427             HtmlRadio radio = (HtmlRadio) uiComponent;
428             String forAttr = radio.getFor();
429             if (forAttr == null)
430             {
431                 throw new IllegalStateException("mandatory attribute 'for'");
432             }
433             int index = radio.getIndex();
434             if (index < 0)
435             {
436                 throw new IllegalStateException("positive index must be given");
437             }
438 
439             UIComponent uiSelectOne = radio.findComponent(forAttr);
440             if (uiSelectOne == null)
441             {
442                 throw new IllegalStateException("Could not find component '" + forAttr + "' (calling findComponent on component '" + radio.getClientId(facesContext) + "')");
443             }
444             if (!(uiSelectOne instanceof UISelectOne))
445             {
446                 throw new IllegalStateException("UISelectOne expected");
447             }
448 
449             if (uiSelectOne instanceof ClientBehaviorHolder) {
450                 ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) uiSelectOne;
451 
452                 Map<String, List<ClientBehavior>> clientBehaviors =
453                         clientBehaviorHolder.getClientBehaviors();
454 
455                 if (clientBehaviors != null && !clientBehaviors.isEmpty()) {
456                     Map<String, String> paramMap = facesContext.getExternalContext().
457                             getRequestParameterMap();
458 
459                     String behaviorEventName = paramMap.get("javax.faces.behavior.event");
460 
461                     if (behaviorEventName != null) {
462                         List<ClientBehavior> clientBehaviorList = clientBehaviors.get(behaviorEventName);
463 
464                         if (clientBehaviorList != null && !clientBehaviorList.isEmpty()) {
465                             String clientId = paramMap.get("javax.faces.source");
466 
467                             if (radio.getClientId().equals(clientId)) {
468                                 for (ClientBehavior clientBehavior : clientBehaviorList) {
469                                     clientBehavior.decode(facesContext, radio);
470                                 }
471                             }
472                         }
473                     }
474                 }
475             }
476         }
477         else
478         {
479             super.decode(facesContext, uiComponent);
480         }
481     }
482 
483 }