You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ja...@apache.org on 2010/01/20 23:08:12 UTC

svn commit: r901400 - in /myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets: AbstractFaceletContext.java impl/DefaultFaceletContext.java tag/jsf/ComponentTagHandlerDelegate.java tag/jsf/ValidatorTagHandlerDelegate.java

Author: jakobk
Date: Wed Jan 20 22:08:12 2010
New Revision: 901400

URL: http://svn.apache.org/viewvc?rev=901400&view=rev
Log:
MYFACES-2410 f:validateBean does not work as container for EditableValueHolders

Modified:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/AbstractFaceletContext.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentTagHandlerDelegate.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ValidatorTagHandlerDelegate.java

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/AbstractFaceletContext.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/AbstractFaceletContext.java?rev=901400&r1=901399&r2=901400&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/AbstractFaceletContext.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/AbstractFaceletContext.java Wed Jan 20 22:08:12 2010
@@ -168,4 +168,44 @@
      */
     public abstract void pushAjaxHandlerToStack(AjaxHandler parent);
     
+    /**
+     * Gets all validation groups on the stack.
+     * @return
+     * @since 2.0
+     */
+    public abstract Iterator<String> getValidationGroups();
+    
+    /**
+     * Removes top of stack.
+     * @since 2.0
+     */
+    public abstract void popValidationGroupsToStack();
+    
+    /**
+     * Pushes validationGroups to the stack.
+     * @param validationGroups
+     * @since 2.0
+     */
+    public abstract void pushValidationGroupsToStack(String validationGroups);
+    
+    /**
+     * Gets all validationIds on the stack.
+     * @return
+     * @since 2.0
+     */
+    public abstract Iterator<String> getExcludedValidatorIds();
+    
+    /**
+     * Removes top of stack.
+     * @since 2.0
+     */
+    public abstract void popExcludedValidatorIdToStack();
+    
+    /**
+     * Pushes validatorId to the stack of excluded validatorIds.
+     * @param validatorId
+     * @since 2.0
+     */
+    public abstract void pushExcludedValidatorIdToStack(String validatorId);
+    
 }

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java?rev=901400&r1=901399&r2=901400&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/impl/DefaultFaceletContext.java Wed Jan 20 22:08:12 2010
@@ -66,6 +66,10 @@
     public final static String UNIQUEID_VENDOR_STACK = "org.apache.myfaces.view.facelets.UNIQUEID_VENDOR_STACK";
 
     public final static String AJAX_HANDLER_STACK = "org.apache.myfaces.view.facelets.AJAX_HANDLER_STACK";
+    
+    public final static String VALIDATION_GROUPS_STACK = "org.apache.myfaces.view.facelets.VALIDATION_GROUPS_STACK";
+    
+    public final static String EXCLUDED_VALIDATOR_IDS_STACK = "org.apache.myfaces.view.facelets.EXCLUDED_VALIDATOR_IDS_STACK";
 
     private final FacesContext _faces;
 
@@ -612,4 +616,124 @@
 
         componentStack.addFirst(parent);
     }
+    
+    /**
+     * Gets all validation groups on the stack.
+     * @return
+     * @since 2.0
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public Iterator<String> getValidationGroups()
+    {
+        Map<Object, Object> attributes = getFacesContext().getAttributes();
+        
+        LinkedList<String> validationGroupsStack 
+                = (LinkedList<String>) attributes.get(VALIDATION_GROUPS_STACK);
+        if (validationGroupsStack != null && !validationGroupsStack.isEmpty())
+        {
+            return validationGroupsStack.iterator();
+        }
+        return null;
+    }
+    
+    /**
+     * Removes top of stack.
+     * @since 2.0
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public void popValidationGroupsToStack()
+    {
+        Map<Object, Object> contextAttributes = getFacesContext().getAttributes();
+        
+        LinkedList<String> validationGroupsStack 
+                = (LinkedList<String>) contextAttributes.get(VALIDATION_GROUPS_STACK);
+        if (validationGroupsStack != null && !validationGroupsStack.isEmpty())
+        {
+            validationGroupsStack.removeFirst();
+        }
+    }
+    
+    /**
+     * Pushes validationGroups to the stack.
+     * @param validationGroups
+     * @since 2.0
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public void pushValidationGroupsToStack(String validationGroups)
+    {
+        Map<Object, Object> attributes = getFacesContext().getAttributes();
+
+        LinkedList<String> validationGroupsStack 
+                = (LinkedList<String>) attributes.get(VALIDATION_GROUPS_STACK);
+        if (validationGroupsStack == null)
+        {
+            validationGroupsStack = new LinkedList<String>();
+            attributes.put(VALIDATION_GROUPS_STACK, validationGroupsStack);
+        }
+
+        validationGroupsStack.addFirst(validationGroups);
+    }
+    
+    /**
+     * Gets all validationIds on the stack.
+     * @return
+     * @since 2.0
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public Iterator<String> getExcludedValidatorIds()
+    {
+        Map<Object, Object> attributes = getFacesContext().getAttributes();
+        
+        LinkedList<String> excludedValidatorIdsStack 
+                = (LinkedList<String>) attributes.get(EXCLUDED_VALIDATOR_IDS_STACK);
+        if (excludedValidatorIdsStack != null && !excludedValidatorIdsStack.isEmpty())
+        {
+            return excludedValidatorIdsStack.iterator();
+        }
+        return null;
+    }
+    
+    /**
+     * Removes top of stack.
+     * @since 2.0
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public void popExcludedValidatorIdToStack()
+    {
+        Map<Object, Object> contextAttributes = getFacesContext().getAttributes();
+        
+        LinkedList<String> excludedValidatorIdsStack 
+                = (LinkedList<String>) contextAttributes.get(EXCLUDED_VALIDATOR_IDS_STACK);
+        if (excludedValidatorIdsStack != null && !excludedValidatorIdsStack.isEmpty())
+        {
+            excludedValidatorIdsStack.removeFirst();
+        }
+    }
+    
+    /**
+     * Pushes validatorId to the stack of excluded validatorIds.
+     * @param validatorId
+     * @since 2.0
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public void pushExcludedValidatorIdToStack(String validatorId)
+    {
+        Map<Object, Object> attributes = getFacesContext().getAttributes();
+
+        LinkedList<String> excludedValidatorIdsStack 
+                = (LinkedList<String>) attributes.get(EXCLUDED_VALIDATOR_IDS_STACK);
+        if (excludedValidatorIdsStack == null)
+        {
+            excludedValidatorIdsStack = new LinkedList<String>();
+            attributes.put(EXCLUDED_VALIDATOR_IDS_STACK, excludedValidatorIdsStack);
+        }
+
+        excludedValidatorIdsStack.addFirst(validatorId);
+    }
 }

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentTagHandlerDelegate.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentTagHandlerDelegate.java?rev=901400&r1=901399&r2=901400&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentTagHandlerDelegate.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ComponentTagHandlerDelegate.java Wed Jan 20 22:08:12 2010
@@ -19,6 +19,7 @@
 package org.apache.myfaces.view.facelets.tag.jsf;
 
 import java.io.IOException;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -75,7 +76,7 @@
      * This constant is duplicate in javax.faces.webapp.UIComponentClassicTagBase
      */
     public final static String FACET_CREATED_UIPANEL_MARKER = "org.apache.myfaces.facet.createdUIPanel";
-
+    
     private final ComponentHandler _delegate;
 
     private final String _componentType;
@@ -246,7 +247,7 @@
         {
             // add default validators here, because this feature 
             // is only available in facelets (see MYFACES-2362 for details)
-            addDefaultValidators(facesContext, (EditableValueHolder) c);
+            addDefaultValidators(facesContext, actx, (EditableValueHolder) c);
         }
         
         _delegate.onComponentPopulated(ctx, c, parent);
@@ -447,9 +448,11 @@
      * Add the default Validators to the component.
      *
      * @param context The FacesContext.
+     * @param actx the AbstractFaceletContext
      * @param component The EditableValueHolder to which the validators should be added
      */
-    private void addDefaultValidators(FacesContext context, EditableValueHolder component)
+    private void addDefaultValidators(FacesContext context, AbstractFaceletContext actx,
+                                      EditableValueHolder component)
     {
         Application application = context.getApplication();
         Map<String, String> defaultValidators = application.getDefaultValidatorInfo();
@@ -460,9 +463,77 @@
             {
                 String validatorId = entry.getKey();
                 String validatorClassName = entry.getValue();
-                if (shouldAddDefaultValidator(validatorId, validatorClassName, context, component))
+                
+                if (shouldAddDefaultValidator(validatorId, validatorClassName, context, actx, component))
                 {
-                    component.addValidator(application.createValidator(validatorId));
+                    Validator validator = null;
+                    boolean created = false;
+                    // check if the validator is already registered for the given component
+                    for (Validator v : component.getValidators())
+                    {
+                        if (v.getClass().getName().equals(validatorClassName))
+                        {
+                            // found
+                            validator = v;
+                            break;
+                        }
+                    }
+                    if (validator == null)
+                    {
+                        // create it
+                        validator = application.createValidator(validatorId);
+                        created = true;
+                    }
+                    
+                    // special things to do for a BeanValidator
+                    if (validator instanceof BeanValidator)
+                    {
+                        BeanValidator beanValidator = (BeanValidator) validator;
+                        
+                        // add validation groups from stack
+                        Iterator<String> itValidationGroups = actx.getValidationGroups();
+                        if (itValidationGroups != null && itValidationGroups.hasNext())
+                        {
+                            // we use a Set to eliminate duplicates
+                            Set<String> groupsSet = new HashSet<String>();
+                            
+                            // add any existing validationGroups to Set first
+                            String validationGroups =  beanValidator.getValidationGroups();
+                            if (validationGroups != null)
+                            {
+                                addValidationGroups(validationGroups, groupsSet);
+                            }
+                            while (itValidationGroups.hasNext())
+                            {
+                                // note that the validationGroups from the stack are non-null
+                                validationGroups = itValidationGroups.next();
+                                addValidationGroups(validationGroups, groupsSet);
+                            }
+                            
+                            // join validationGroups and add them to beanValidator
+                            StringBuilder sb = new StringBuilder();
+                            boolean first = true;
+                            for (String group : groupsSet)
+                            {
+                                if (first)
+                                {
+                                    first = false;
+                                }
+                                else
+                                {
+                                    sb.append(BeanValidator.VALIDATION_GROUPS_DELIMITER);
+                                }
+                                sb.append(group);
+                            }
+                            beanValidator.setValidationGroups(sb.toString());
+                        }
+                    }
+                    
+                    if (created)
+                    {
+                        // add the validator to the component
+                        component.addValidator(validator);
+                    }
                 }
             }
         }
@@ -474,18 +545,25 @@
      * @param validatorId The validatorId.
      * @param validatorClassName The class name of the validator.
      * @param context The FacesContext.
+     * @param actx the AbstractFaceletContext
      * @param component The EditableValueHolder to which the validator should be added.
      * @return true if the Validator should be added, false otherwise.
      */
     private boolean shouldAddDefaultValidator(String validatorId, String validatorClassName,
-                                              FacesContext context, EditableValueHolder component)
+                                              FacesContext context, AbstractFaceletContext actx,
+                                              EditableValueHolder component)
     {
-        // check if the validator is already registered for the given component
-        for (Validator v : component.getValidators())
-        {
-            if (v.getClass().getName().equals(validatorClassName))
+        // check if the validatorId is on the exclusion list
+        Iterator<String> it = actx.getExcludedValidatorIds();
+        if (it != null)
+        {            
+            while (it.hasNext())
             {
-                return false;
+                String excludedId = it.next();
+                if (excludedId.equals(validatorId))
+                {
+                    return false;
+                }
             }
         }
         
@@ -509,6 +587,21 @@
     }
     
     /**
+     * Splits the validationGroups String with BeanValidator.VALIDATION_GROUPS_DELIMITER
+     * and then trims and adds every String in the resulting array to the Set. 
+     * @param validationGroups
+     * @param set
+     */
+    private void addValidationGroups(String validationGroups, Set<String> set)
+    {
+        String[] sa = validationGroups.split(BeanValidator.VALIDATION_GROUPS_DELIMITER);
+        for (String group : sa)
+        {
+            set.add(group.trim());
+        }
+    }
+    
+    /**
      * Create a new UIPanel for the use as a dynamically 
      * created container for multiple children in a facet.
      * Duplicate in javax.faces.webapp.UIComponentClassicTagBase.

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ValidatorTagHandlerDelegate.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ValidatorTagHandlerDelegate.java?rev=901400&r1=901399&r2=901400&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ValidatorTagHandlerDelegate.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/ValidatorTagHandlerDelegate.java Wed Jan 20 22:08:12 2010
@@ -34,6 +34,8 @@
 import javax.faces.view.facelets.TagHandlerDelegate;
 import javax.faces.view.facelets.ValidatorHandler;
 
+import org.apache.myfaces.view.facelets.AbstractFaceletContext;
+import org.apache.myfaces.view.facelets.compiler.FaceletsCompilerUtils;
 import org.apache.myfaces.view.facelets.tag.MetaRulesetImpl;
 import org.apache.myfaces.view.facelets.tag.composite.CompositeComponentResourceTagHandler;
 
@@ -52,46 +54,108 @@
 {
     private ValidatorHandler _delegate;
     
+    /**
+     * true - this tag has children
+     * false - this tag is a leave
+     */
+    private final boolean _wrapMode;
+    
     public ValidatorTagHandlerDelegate(ValidatorHandler delegate)
     {
         _delegate = delegate;
+
+        // According to jsf 2.0 spec section 10.4.1.4
+        // this tag can be used as a leave within an EditableValueHolder
+        // or as a container to provide validator information for all 
+        // EditableValueHolder-children (and grandchildren and ...)
+        // (this behavior is analog to <f:ajax>)
+        // --> Determine if we have children:
+        _wrapMode = FaceletsCompilerUtils.hasChildren(_delegate.getValidatorConfig());
     }
 
     @Override
     public void apply(FaceletContext ctx, UIComponent parent) throws IOException
     {
+        // Apply only if we are creating a new component
         if (!ComponentHandler.isNew(parent))
         {
             return;
         }
-        if (parent instanceof EditableValueHolder)
+        if (_wrapMode)
         {
-            applyAttachedObject(ctx.getFacesContext(), parent);
-        }
-        else if (UIComponent.isCompositeComponent(parent))
-        {
-            CompositeComponentResourceTagHandler.addAttachedObjectHandler(parent, _delegate);
+            // the tag has children --> provide validator information for all children
+            
+            // FIXME the spec says we should save the validation groups in an attribute
+            // on the parent UIComponent, but this will be a problem in the following scenario:
+            // <h:form>
+            //     <f:validateBean>
+            //         <h:inputText />
+            //     </f:validateBean>
+            //     <h:inputText />
+            // </h:form>
+            // because the validator would also be applied to the second h:inputText,
+            // which it should not, on my opinion. In addition, mojarra also does not
+            // attach the validator to the second h:inputText in this scenario (blackbox test).
+            // So I use the same way as f:ajax for this problem. -=Jakob Korherr=-
+            
+            // we need methods from AbstractFaceletContext
+            AbstractFaceletContext abstractCtx = (AbstractFaceletContext) ctx;
+             
+            boolean disabled = _delegate.isDisabled(ctx);
+            if (disabled)
+            {
+                // the validator is disabled --> add its id to the exclusion stack
+                String validatorId = _delegate.getValidatorConfig().getValidatorId();
+                if (validatorId != null && !"".equals(validatorId))
+                {
+                    abstractCtx.pushExcludedValidatorIdToStack(validatorId);
+                    _delegate.getValidatorConfig().getNextHandler().apply(ctx, parent);
+                    abstractCtx.popExcludedValidatorIdToStack();
+                }
+            }
+            else
+            {
+                // the validator is enabled --> add the validation groups to the stack
+                String groups = getValidationGroups(ctx);
+                // spec: don't save the validation groups string if it is null or empty string
+                if (groups != null && !"".equals(groups))
+                {
+                    abstractCtx.pushValidationGroupsToStack(groups);
+                    _delegate.getValidatorConfig().getNextHandler().apply(ctx, parent);
+                    abstractCtx.popValidationGroupsToStack();
+                }
+            }
         }
         else
         {
-            throw new TagException(_delegate.getTag(), "Parent not composite component or an instance of EditableValueHolder: " + parent);
+            // the tag is a leave --> attach validator to parent
+            if (parent instanceof EditableValueHolder)
+            {
+                applyAttachedObject(ctx.getFacesContext(), parent);
+            }
+            else if (UIComponent.isCompositeComponent(parent))
+            {
+                CompositeComponentResourceTagHandler.addAttachedObjectHandler(parent, _delegate);
+            }
+            else
+            {
+                throw new TagException(_delegate.getTag(), "Parent not composite component or an instance of EditableValueHolder: " + parent);
+            }
         }
     }
 
     /**
      * Template method for creating a Validator instance
      * 
-     * @param ctx
-     *            FaceletContext to use
+     * @param ctx FaceletContext to use
      * @return a new Validator instance
      */
     protected Validator createValidator(FaceletContext ctx)
     {
         if (_delegate.getValidatorId(ctx) == null)
         {
-            throw new TagException(
-                                   _delegate.getTag(),
-                                   "Default behavior invoked of requiring a validator-id passed in the constructor, must override ValidateHandler(ValidatorConfig)");
+            throw new TagException(_delegate.getTag(), "Default behavior invoked of requiring " +
+                    "a validator-id passed in the constructor, must override ValidateHandler(ValidatorConfig)");
         }
         return ctx.getFacesContext().getApplication().createValidator(_delegate.getValidatorId(ctx));
     }
@@ -130,7 +194,7 @@
             throw new TagException(_delegate.getTag(), "No Validator was created");
         }
         _delegate.setAttributes(faceletContext, v);
-        evh.addValidator(v);
+        evh.addValidator(v); 
     }
 
     public String getFor()
@@ -146,5 +210,19 @@
             return forAttribute.getValue();
         }
     }
+    
+    public String getValidationGroups(FaceletContext ctx)
+    {
+        TagAttribute attribute = _delegate.getTagAttribute("validationGroups");
+        
+        if (attribute == null)
+        {
+            return null;
+        }
+        else
+        {
+            return attribute.getValue(ctx);
+        }
+    }
 
 }