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.view.facelets.tag.jsf;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import javax.el.ValueExpression;
26  import javax.faces.FacesWrapper;
27  import javax.faces.component.EditableValueHolder;
28  import javax.faces.component.UIComponent;
29  import javax.faces.context.FacesContext;
30  import javax.faces.validator.Validator;
31  import javax.faces.view.EditableValueHolderAttachedObjectHandler;
32  import javax.faces.view.facelets.ComponentHandler;
33  import javax.faces.view.facelets.FaceletContext;
34  import javax.faces.view.facelets.MetaRuleset;
35  import javax.faces.view.facelets.TagAttribute;
36  import javax.faces.view.facelets.TagException;
37  import javax.faces.view.facelets.TagHandlerDelegate;
38  import javax.faces.view.facelets.ValidatorHandler;
39  
40  import org.apache.myfaces.shared.renderkit.JSFAttr;
41  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
42  import org.apache.myfaces.view.facelets.compiler.FaceletsCompilerUtils;
43  import org.apache.myfaces.view.facelets.tag.MetaRulesetImpl;
44  
45  /**
46   * Handles setting a Validator instance on a EditableValueHolder. Will wire all attributes set to the Validator instance
47   * created/fetched. Uses the "binding" attribute for grabbing instances to apply attributes to. <p/> Will only
48   * set/create Validator is the passed UIComponent's parent is null, signifying that it wasn't restored from an existing
49   * tree.
50   * 
51   * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
52   * @version $Revision: 1294325 $ $Date: 2012-02-27 16:01:44 -0500 (Mon, 27 Feb 2012) $
53   *
54   * @since 2.0
55   */
56  public class ValidatorTagHandlerDelegate extends TagHandlerDelegate 
57      implements EditableValueHolderAttachedObjectHandler, FacesWrapper<ValidatorHandler>
58  {
59      
60      /**
61       * if <f:validateBean> has no children and its disabled attribute is true,
62       * its validatorId will be added to the exclusion list stored under
63       * this key on the parent UIComponent.
64       */
65      public final static String VALIDATOR_ID_EXCLUSION_LIST_KEY
66              = "org.apache.myfaces.validator.VALIDATOR_ID_EXCLUSION_LIST";
67      
68      private ValidatorHandler _delegate;
69      
70      /**
71       * true - this tag has children
72       * false - this tag is a leave
73       */
74      private final boolean _wrapMode;
75      
76      public ValidatorTagHandlerDelegate(ValidatorHandler delegate)
77      {
78          _delegate = delegate;
79  
80          // According to jsf 2.0 spec section 10.4.1.4
81          // this tag can be used as a leave within an EditableValueHolder
82          // or as a container to provide validator information for all 
83          // EditableValueHolder-children (and grandchildren and ...)
84          // (this behavior is analog to <f:ajax>)
85          // --> Determine if we have children:
86          _wrapMode = FaceletsCompilerUtils.hasChildren(_delegate.getValidatorConfig());
87      }
88  
89      @Override
90      public void apply(FaceletContext ctx, UIComponent parent) throws IOException
91      {
92          // we need methods from AbstractFaceletContext
93          FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(ctx);
94  
95          if (_wrapMode)
96          {
97              // the tag has children --> provide validator information for all children
98              
99              // FIXME the spec says we should save the validation groups in an attribute
100             // on the parent UIComponent, but this will be a problem in the following scenario:
101             // <h:form>
102             //     <f:validateBean>
103             //         <h:inputText />
104             //     </f:validateBean>
105             //     <h:inputText />
106             // </h:form>
107             // because the validator would also be applied to the second h:inputText,
108             // which it should not, on my opinion. In addition, mojarra also does not
109             // attach the validator to the second h:inputText in this scenario (blackbox test).
110             // So I use the same way as f:ajax for this problem. -=Jakob Korherr=-
111             
112             String validatorId = _delegate.getValidatorConfig().getValidatorId();
113             /*
114             boolean disabled = _delegate.isDisabled(ctx);
115             if (disabled)
116             {
117                 // the validator is disabled --> add its id to the exclusion stack
118                 boolean validatorIdAvailable = validatorId != null && !"".equals(validatorId);
119                 try
120                 {
121                     if (validatorIdAvailable)
122                     {
123                         mctx.pushExcludedValidatorIdToStack(validatorId);
124                     }
125                     _delegate.getValidatorConfig().getNextHandler().apply(ctx, parent);
126                 }
127                 finally
128                 {
129                     if (validatorIdAvailable)
130                     {
131                         mctx.popExcludedValidatorIdToStack();
132                     }
133                 }
134             }
135             else
136             {*/
137                 // the validator is enabled 
138                 // --> add the validation groups and the validatorId to the stack
139                 //String groups = getValidationGroups(ctx);
140                 // spec: don't save the validation groups string if it is null or empty string
141                 //boolean groupsAvailable = groups != null 
142                         //&& !groups.matches(BeanValidator.EMPTY_VALIDATION_GROUPS_PATTERN);
143                 //try
144                 //{
145                     //if (groupsAvailable)
146                     //{
147                     //    mctx.pushValidationGroupsToStack(groups);
148                     //}
149                     try
150                     {
151                         mctx.pushEnclosingValidatorIdToStack(validatorId, this);
152                         _delegate.applyNextHandler(ctx, parent);
153                     }
154                     finally
155                     {
156                         mctx.popEnclosingValidatorIdToStack();
157                     }
158                 //}
159                 //finally
160                 //{
161                     //if (groupsAvailable)
162                     //{
163                         //mctx.popValidationGroupsToStack();
164                     //}
165                 //}
166             /*}*/
167         }
168         else
169         {
170             // Apply only if we are creating a new component
171             if (!ComponentHandler.isNew(parent))
172             {
173                 return;
174             }
175 
176             // the tag is a leave --> attach validator to parent
177             if (parent instanceof EditableValueHolder)
178             {
179                 applyAttachedObject(ctx.getFacesContext(), parent);
180             }
181             else if (UIComponent.isCompositeComponent(parent))
182             {
183                 if (getFor() == null)
184                 {
185                     throw new TagException(_delegate.getTag(), "is nested inside a composite component"
186                             + " but does not have a for attribute.");
187                 }
188                 mctx.addAttachedObjectHandler(parent, _delegate);
189             }
190             else
191             {
192                 throw new TagException(_delegate.getTag(),
193                         "Parent not composite component or an instance of EditableValueHolder: " + parent);
194             }
195         }
196     }
197 
198     /**
199      * Template method for creating a Validator instance
200      * 
201      * @param ctx FaceletContext to use
202      * @return a new Validator instance
203      */
204     protected Validator createValidator(FaceletContext ctx)
205     {
206         if (_delegate.getValidatorId(ctx) == null)
207         {
208             throw new TagException(_delegate.getTag(), "Default behavior invoked of requiring " +
209                     "a validator-id passed in the constructor, must override ValidateHandler(ValidatorConfig)");
210         }
211         return ctx.getFacesContext().getApplication().createValidator(_delegate.getValidatorId(ctx));
212     }
213 
214     @Override
215     public MetaRuleset createMetaRuleset(Class type)
216     {
217         MetaRuleset metaRuleset = new MetaRulesetImpl(_delegate.getTag(), type);
218         
219         // ignore binding and disabled, because they are handled by DelegatingMetaTagHandler
220         metaRuleset.ignore(JSFAttr.BINDING_ATTR).ignore(JSFAttr.DISABLED_ATTR);
221         // ignore for, because it is handled by FaceletsAttachedObjectHandler
222         metaRuleset.ignore(JSFAttr.FOR_ATTR);
223         
224         return metaRuleset;
225     }
226 
227     @SuppressWarnings("unchecked")
228     public void applyAttachedObject(FacesContext context, UIComponent parent)
229     {
230         // Retrieve the current FaceletContext from FacesContext object
231         FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(
232                 FaceletContext.FACELET_CONTEXT_KEY);
233         
234         // spec: if the disabled attribute is true, the validator should not be added.
235         // in addition, the validatorId, if present, should be added to an exclusion
236         // list on the parent component to prevent a default validator with the same
237         // id from beeing registered on the component.
238         if (_delegate.isDisabled(faceletContext))
239         {
240             // tag is disabled --> add its validatorId to the parent's exclusion list
241             String validatorId = _delegate.getValidatorConfig().getValidatorId();
242             if (validatorId != null && !"".equals(validatorId))
243             {
244                 List<String> exclusionList = (List<String>) parent.getAttributes()
245                         .get(VALIDATOR_ID_EXCLUSION_LIST_KEY);
246                 if (exclusionList == null)
247                 {
248                     exclusionList = new ArrayList<String>();
249                     parent.getAttributes().put(VALIDATOR_ID_EXCLUSION_LIST_KEY, exclusionList);
250                 }
251                 exclusionList.add(validatorId);
252             }
253         }
254         else
255         {
256             // tag is enabled --> create the validator and attach it
257             
258             // cast to a ValueHolder
259             EditableValueHolder evh = (EditableValueHolder) parent;
260             ValueExpression ve = null;
261             Validator v = null;
262             if (_delegate.getBinding() != null)
263             {
264                 ve = _delegate.getBinding().getValueExpression(faceletContext, Validator.class);
265                 v = (Validator) ve.getValue(faceletContext);
266             }
267             if (v == null)
268             {
269                 v = this.createValidator(faceletContext);
270                 if (ve != null)
271                 {
272                     ve.setValue(faceletContext, v);
273                 }
274             }
275             if (v == null)
276             {
277                 throw new TagException(_delegate.getTag(), "No Validator was created");
278             }
279             _delegate.setAttributes(faceletContext, v);
280             evh.addValidator(v); 
281         }
282     }
283 
284     public String getFor()
285     {
286         TagAttribute forAttribute = _delegate.getTagAttribute("for");
287         
288         if (forAttribute == null)
289         {
290             return null;
291         }
292         else
293         {
294             return forAttribute.getValue();
295         }
296     }
297     
298     public String getValidationGroups(FaceletContext ctx)
299     {
300         TagAttribute attribute = _delegate.getTagAttribute("validationGroups");
301         
302         if (attribute == null)
303         {
304             return null;
305         }
306         else
307         {
308             return attribute.getValue(ctx);
309         }
310     }
311 
312     public ValidatorHandler getWrapped()
313     {
314         return _delegate;
315     }
316 
317 }