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