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.component.html.ext;
20  
21  import java.util.Iterator;
22  
23  import javax.faces.FacesException;
24  import javax.faces.application.FacesMessage;
25  import javax.faces.component.EditableValueHolder;
26  import javax.faces.component.UIComponent;
27  import javax.faces.context.FacesContext;
28  import javax.faces.el.EvaluationException;
29  import javax.faces.el.MethodBinding;
30  import javax.faces.validator.Validator;
31  import javax.faces.validator.ValidatorException;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.myfaces.component.AltProperty;
36  import org.apache.myfaces.component.DataProperties;
37  import org.apache.myfaces.component.DisplayValueOnlyAware;
38  import org.apache.myfaces.component.EscapeAware;
39  import org.apache.myfaces.component.ForceIdAware;
40  import org.apache.myfaces.component.UserRoleAware;
41  import org.apache.myfaces.component.UserRoleUtils;
42  import org.apache.myfaces.component.html.util.HtmlComponentUtils;
43  import org.apache.myfaces.shared_tomahawk.component.DisplayValueOnlyCapable;
44  import org.apache.myfaces.shared_tomahawk.component.EscapeCapable;
45  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
46  import org.apache.myfaces.shared_tomahawk.util.MessageUtils;
47  
48  /**
49   * Implements the standard html selectOneRadio tag, with additional features. 
50   * 
51   * Supports user roles. Supports the "spread" layout value, which 
52   * gives developer control over radio button positioning. 
53   * See the "layout" attribute and the "radio" tag for further 
54   * information. 
55   * 
56   * Unless otherwise specified, all attributes accept static values or EL expressions.
57   * 
58   * @JSFComponent
59   *   name = "t:selectOneRadio"
60   *   class = "org.apache.myfaces.component.html.ext.HtmlSelectOneRadio"
61   *   tagClass = "org.apache.myfaces.generated.taglib.html.ext.HtmlSelectOneRadioTag"
62   * @since 1.1.7
63   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
64   * @version $Revision: 691856 $ $Date: 2008-09-03 21:40:30 -0500 (Wed, 03 Sep 2008) $
65   */
66  public abstract class AbstractHtmlSelectOneRadio
67          extends javax.faces.component.html.HtmlSelectOneRadio
68          implements UserRoleAware, DisplayValueOnlyCapable,
69          EscapeCapable, EscapeAware, DisplayValueOnlyAware, 
70          ForceIdAware, DataProperties, AltProperty
71  {
72      public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlSelectOneRadio";
73      public static final String DEFAULT_RENDERER_TYPE ="org.apache.myfaces.Radio";
74  
75      private static Log log = LogFactory.getLog(AbstractHtmlSelectOneRadio.class);
76  
77      public String getClientId(FacesContext context)
78      {
79          String clientId = HtmlComponentUtils.getClientId(this, getRenderer(context), context);
80          if (clientId == null)
81          {
82              clientId = super.getClientId(context);
83          }
84  
85          return clientId;
86      }
87  
88      /**
89       * Overridden method, as with extended seletOne, value doesn't necessarily
90       * have to be contained within select list, for example, when forceId="true" and
91       * forceIdIndex="false" then component may be used in datatable.
92       */
93      protected void validateValue(FacesContext context, Object value)
94      {
95          //Is this radio button used within a datatable (forceId=true and forceIdIndex=false)
96          boolean forceId = getBooleanValue(JSFAttr.FORCE_ID_ATTR,
97                  this.getAttributes().get(JSFAttr.FORCE_ID_ATTR), false);
98  
99  //      see if the originally supplied id should be used
100         boolean forceIdIndex = getBooleanValue(JSFAttr.FORCE_ID_INDEX_ATTR,
101                 this.getAttributes().get(JSFAttr.FORCE_ID_INDEX_ATTR), true);
102 
103         boolean dataTable = forceId && !forceIdIndex;
104 
105         if (!dataTable)
106         {
107             super.validateValue(context, value);
108         }
109         else
110         {
111             //Specific behavior for data tables, or other scenarios where forceId is
112             //true and forceIdIndex is false
113 
114             //Check if empty
115             boolean empty = value == null
116                     || (value instanceof String && ((String) value).length() == 0);
117 
118             //Check required and empty
119             if (isRequired() && empty)
120             {
121               //Only add this message once, not for every radio button in set
122                 String clientId = this.getClientId(context);
123                 Iterator messages = context.getMessages(clientId);
124                 boolean messageExists = messages.hasNext();
125 
126                 if(!messageExists)
127                 {
128                     //Add message
129                     FacesMessage message = MessageUtils.getMessage(REQUIRED_MESSAGE_ID, new Object[]{clientId});
130                     message.setSeverity(FacesMessage.SEVERITY_WARN);
131                     context.addMessage(clientId, message);
132 
133                     setValid(false);
134                 }
135                 return;
136             }
137 
138             //Call validators
139             if (!empty)
140             {
141                 callValidators(context, this, value);
142             }
143         }
144     }
145 
146 
147     private static boolean getBooleanValue(String attribute, Object value, boolean defaultValue)
148     {
149         if(value instanceof Boolean)
150         {
151             return ((Boolean) value).booleanValue();
152         }
153         else if(value instanceof String)
154         {
155             return Boolean.valueOf((String) value).booleanValue();
156         }
157         else if(value != null)
158         {
159             log.error("value for attribute "+attribute+
160                     " must be instanceof 'Boolean' or 'String', is of type : "+value.getClass());
161 
162             return defaultValue;
163         }
164 
165         return defaultValue;
166     }
167 
168     private static void callValidators(FacesContext context, UIComponent input, Object convertedValue)
169     {
170         if(!(input instanceof EditableValueHolder))
171             throw new FacesException("param input not of type EditableValueHolder, but of : "+input.getClass().getName());
172 
173         EditableValueHolder holder = (EditableValueHolder) input;
174 
175         Validator[] validators = holder.getValidators();
176         for (int i = 0; i < validators.length; i++)
177         {
178             Validator validator = validators[i];
179             try
180             {
181                 validator.validate(context, input, convertedValue);
182             }
183             catch (ValidatorException e)
184             {
185                 holder.setValid(false);
186                 FacesMessage facesMessage = e.getFacesMessage();
187                 if (facesMessage != null)
188                 {
189                     facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
190                     context.addMessage(input.getClientId(context), facesMessage);
191                 }
192             }
193         }
194 
195         MethodBinding validatorBinding = holder.getValidator();
196         if (validatorBinding != null)
197         {
198             try
199             {
200                 validatorBinding.invoke(context, new Object[] {context, input, convertedValue});
201             }
202             catch (EvaluationException e)
203             {
204                 holder.setValid(false);
205                 Throwable cause = e.getCause();
206                 if (cause instanceof ValidatorException)
207                 {
208                     FacesMessage facesMessage = ((ValidatorException) cause).getFacesMessage();
209                     if (facesMessage != null)
210                     {
211                         facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
212                         context.addMessage(input.getClientId(context), facesMessage);
213                     }
214                 }
215                 else
216                 {
217                     throw e;
218                 }
219             }
220         }
221     }
222 
223     public boolean isSetDisplayValueOnly(){
224         return getDisplayValueOnly() != null ? true : false;  
225     }
226     
227     public boolean isDisplayValueOnly(){
228         return getDisplayValueOnly() != null ? getDisplayValueOnly().booleanValue() : false;
229     }
230     
231     public void setDisplayValueOnly(boolean displayValueOnly){
232         this.setDisplayValueOnly((Boolean) Boolean.valueOf(displayValueOnly));
233     }
234 
235     public boolean isRendered()
236     {
237         if (!UserRoleUtils.isVisibleOnUserRole(this)) return false;
238         return super.isRendered();
239     }
240     
241 }