Commits

Anonymous committed 3fd6c0e

XW-565
Conditational visitor validation
o added new feature created by Matt Raible
o added sublementary Annotation
submitted by: Ted Husted, Matt Raible

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@1762e221344d-f017-0410-9bd5-d282ab1896d7

  • Participants
  • Parent commits 1c4743c

Comments (0)

Files changed (6)

File src/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilder.java

                     }
 
                 }
+                // Process ConditionalVisitorFieldValidator
+                else if ( a instanceof ConditionalVisitorFieldValidator) {
+                    ConditionalVisitorFieldValidator v = (ConditionalVisitorFieldValidator) a;
+                    ValidatorConfig temp = processConditionalVisitorFieldValidatorAnnotation(v, fieldName, methodName);
+                    if ( temp != null) {
+                        result.add(temp);
+                    }
+
+                }
                 // Process VisitorFieldValidator
                 else if ( a instanceof VisitorFieldValidator) {
                     VisitorFieldValidator v = (VisitorFieldValidator) a;
                 }
             }
         }
+        ConditionalVisitorFieldValidator[] cvfv = validations.conditionalVisitorFields();
+        if ( cvfv != null ) {
+            for (ConditionalVisitorFieldValidator v : cvfv) {
+                ValidatorConfig temp = processConditionalVisitorFieldValidatorAnnotation(v, fieldName, methodName);
+                if (temp != null) {
+                    result.add(temp);
+                }
+            }
+        }
         VisitorFieldValidator[] vfv = validations.visitorFields();
         if ( vfv != null ) {
             for (VisitorFieldValidator v : vfv) {
             .build();
     }
 
+    private ValidatorConfig processConditionalVisitorFieldValidatorAnnotation(ConditionalVisitorFieldValidator v, String fieldName, String methodName) {
+        String validatorType = "conditionalvisitor";
+
+        Map<String, String> params = new HashMap<String, String>();
+
+        if (fieldName != null) {
+            params.put("fieldName", fieldName);
+        } else if (v.fieldName() != null && v.fieldName().length() > 0 ) {
+            params.put("fieldName", v.fieldName());
+        }
+
+        params.put("expression", v.expression());
+        params.put("context", v.context());
+        params.put("appendPrefix", String.valueOf(v.appendPrefix()));
+
+        validatorFactory.lookupRegisteredValidatorType(validatorType);
+        return new ValidatorConfig.Builder(validatorType)
+            .addParams(params)
+            .addParam("methodName", methodName)
+            .shortCircuit(v.shortCircuit())
+            .defaultMessage(v.message())
+            .messageKey(v.key())
+            .build();
+    }
+
+
+
     private ValidatorConfig processVisitorFieldValidatorAnnotation(VisitorFieldValidator v, String fieldName, String methodName) {
 
         String validatorType = "visitor";

File src/java/com/opensymphony/xwork2/validator/annotations/ConditionalVisitorFieldValidator.java

+package com.opensymphony.xwork2.validator.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * The validator allows you to forward validator to object properties of your action
+ * using the objects own validator files. This allows you to use the ModelDriven development
+ * pattern and manage your validations for your models in one place, where they belong, next to
+ * your model classes.
+ *
+ * The ConditionalVisitorFieldValidator can handle either simple Object properties, Collections of Objects, or Arrays.
+ * The error message for the ConditionalVisitorFieldValidator will be appended in front of validator messages added
+ * by the validations for the Object message.
+ * <!-- END SNIPPET: description -->
+ *
+ * <p/> <u>Annotation usage:</u>
+ *
+ * <!-- START SNIPPET: usage -->
+ * <p/>The annotation must be applied at method level.
+ * <!-- END SNIPPET: usage -->
+ *
+ * <p/> <u>Annotation parameters:</u>
+ *
+ * <!-- START SNIPPET: parameters -->
+ * <table class='confluenceTable'>
+ * <tr>
+ * <th class='confluenceTh'> Parameter </th>
+ * <th class='confluenceTh'> Required </th>
+ * <th class='confluenceTh'> Default </th>
+ * <th class='confluenceTh'> Notes </th>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>expression</td>
+ * <td class='confluenceTd'>yes</td>
+ * <td class='confluenceTd'>&nbsp;</td>
+ * <td class='confluenceTd'>Boolean conditional expression</td>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>message</td>
+ * <td class='confluenceTd'>yes</td>
+ * <td class='confluenceTd'>&nbsp;</td>
+ * <td class='confluenceTd'>field error message</td>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>key</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'>&nbsp;</td>
+ * <td class='confluenceTd'>i18n key from language specific properties file.</td>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>fieldName</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'>&nbsp;</td>
+ * <td class='confluenceTd'>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'>shortCircuit</td>
+ * <td class='confluenceTd'>no</td>
+ * <td class='confluenceTd'>false</td>
+ * <td class='confluenceTd'>If this validator should be used as shortCircuit.</td>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'> context </td>
+ * <td class='confluenceTd'> no </td>
+ * <td class='confluenceTd'> action alias </td>
+ * <td class='confluenceTd'> Determines the context to use for validating the Object property. If not defined, the context of the Action validation is propogated to the Object property validation.  In the case of Action validation, this context is the Action alias.  </td>
+ * </tr>
+ * <tr>
+ * <td class='confluenceTd'> appendPrefix </td>
+ * <td class='confluenceTd'> no </td>
+ * <td class='confluenceTd'> true </td>
+ * <td class='confluenceTd'> Determines whether the field name of this field validator should be prepended to the field name of the visited field to determine the full field name when an error occurs.  For example, suppose that the bean being validated has a "name" property.  If <em>appendPrefix</em> is true, then the field error will be stored under the field "bean.name".  If <em>appendPrefix</em> is false, then the field error will be stored under the field "name".  <br clear="all" /> <img class="emoticon" src="/images/icons/emoticons/warning.gif" height="16" width="16" align="absmiddle" alt="" border="0"/> If you are using the VisitorFieldValidator to validate the model from a ModelDriven Action, you should set <em>appendPrefix</em> to false unless you are using "model.name" to reference the properties on your model. </td>
+ * </tr>
+ * </table>
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <p/> <u>Example code:</u>
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &#64;ConditionalVisitorFieldValidator(expression="app.appid > 100",  message = "Default message", key = "i18n.key", shortCircuit = true, context = "action alias", appendPrefix = true)
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ *
+ * @author Matt Raible
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ConditionalVisitorFieldValidator {
+
+    /**
+     * Determines the context to use for validating the Object property.
+     * If not defined, the context of the Action validator is propogated to the Object property validator.
+     * In the case of Action validator, this context is the Action alias.
+     */
+    String context() default "";
+
+    /**
+     * Determines whether the field name of this field validator should be prepended to the field name of
+     * the visited field to determine the full field name when an error occurs. For example, suppose that
+     * the bean being validated has a "name" property.
+     *
+     * If appendPrefix is true, then the field error will be stored under the field "bean.name".
+     * If appendPrefix is false, then the field error will be stored under the field "name".
+     *
+     * If you are using the ConditionalVisitorFieldValidator to validate the model from a ModelDriven Action,
+     * you should set appendPrefix to false unless you are using "model.name" to reference the properties
+     * on your model.
+     */
+    boolean appendPrefix() default true;
+
+    /**
+     * The conditional expression.
+     */
+    String expression();
+
+    /**
+     * The default error message for this validator.
+     */
+    String message();
+
+    /**
+     * The message key to lookup for i18n.
+     */
+    String key() default "";
+
+    /**
+     * The optional fieldName for SIMPLE validator types.
+     */
+    String fieldName() default "";
+
+    /**
+     * If this is activated, the validator will be used as short-circuit.
+     *
+     * Adds the short-circuit="true" attribute value if <tt>true</tt>.
+     *
+     */
+    boolean shortCircuit() default false;
+    
+}

File src/java/com/opensymphony/xwork2/validator/annotations/Validations.java

 
     public UrlValidator[] urls() default {};
     
+    public ConditionalVisitorFieldValidator[] conditionalVisitorFields() default {};
+
     public VisitorFieldValidator[] visitorFields() default {};
 
     public RegexFieldValidator[] regexFields() default {};

File src/java/com/opensymphony/xwork2/validator/validators/ConditionalVisitorFieldValidator.java

+package com.opensymphony.xwork2.validator.validators;
+
+import com.opensymphony.xwork2.validator.ValidationException;
+
+/**
+ * <code>ConditionalVisitorFieldValidator</code>
+ *
+ *
+ * &lt;field name="colleaguePosition"&gt;
+ *   &lt;field-validator type="fieldexpression" short-circuit="true"&gt;
+ *     reason == 'colleague' and colleaguePositionID == '_CHOOSE_'
+ *     &lt;message&gt;You must choose a position where you worked with this person,
+ * or choose "Other..."&lt;/message&gt;
+ *   &lt;/field-validator&gt;
+ *   &lt;field-validator type="conditionalvisitor"&gt;
+ *     reason == 'colleague' and colleaguePositionID == 'OTHER'
+ *     &lt;message/&gt;
+ *   &lt;/field-validator&gt;
+ * &lt;/field&gt;
+ *
+ * @author Matt Raible
+ */
+public class ConditionalVisitorFieldValidator extends VisitorFieldValidator {
+    private String expression;
+
+    public void setExpression(String expression) {
+        this.expression = expression;
+    }
+
+    public String getExpression() {
+        return expression;
+    }
+
+    /**
+     * If expression evaluates to true, invoke visitor validation.
+     *
+     * @param object the object being validated
+     * @throws ValidationException
+     */
+    public void validate(Object object) throws ValidationException {
+        if (validateExpression(object)) {
+            super.validate(object);
+        }
+    }
+
+    /**
+     * Validate the expression contained in the "expression" paramter.
+     *
+     * @param object the object you're validating
+     * @return true if expression evaluates to true (implying a validation
+     *         failure)
+     * @throws ValidationException if anything goes wrong
+     */
+    public boolean validateExpression(Object object) throws ValidationException {
+        Boolean answer = Boolean.FALSE;
+        Object obj = null;
+
+        try {
+            obj = getFieldValue(expression, object);
+        }
+        catch (ValidationException e) {
+            throw e;
+        }
+        catch (Exception e) {
+            // let this pass, but it will be logged right below
+        }
+
+        if ((obj != null) && (obj instanceof Boolean)) {
+            answer = (Boolean) obj;
+        } else {
+            log.warn("Got result of " + obj + " when trying to get Boolean.");
+        }
+
+        return answer;
+    }
+} 

File src/java/com/opensymphony/xwork2/validator/validators/default.xml

     <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
     <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
     <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
+    <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
 </validators>
 <!--  END SNIPPET: validators-default -->

File src/test/validators.xml

     <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
     <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
     <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
+    <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
 </validators>