You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by "Shannon, Andrew" <an...@pqa.com> on 2008/06/06 14:59:12 UTC

s2 : custom expression validator - evaluates null properties

After doing some research on how the struts2 ognl stack implementation
works when attempting to find a null
value on the value stack (essentially it returns nothing when evaluating
an expression), I had to figure out a work 
around for a special case to handle some Long type properties.  I saw a
few posts from a while back where others 
seem to have wrestled with the same issue.  I believe the stack
implementation is just fine, so I came up with this 
validator to solve my problem.  Maybe this can help someone else out
too.
 
------------------------------------------------------------------------
-------------------------------
 
package x.y.z.web.validator;
 
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.ValidatorSupport;
 
/**
 * CustomExpressionValidator
 * <p/>
 * The purpose for this validator is to allow us to evaluate null
properties
 * on the value stack.  The reason for this is that expressions are
interpreted
 * to populate the properties with values in the expression string, and
if a property
 * is null on the value stack, then no value is placed into the string
and the
 * resulting expression is essentially useless and cannot evaluate.
 * <p/>
 * The tactic employed in this validator is to leverage some control
over how the
 * expression is populated by introducing a ParsedValueEvaluator that
will return a
 * string value of "null" into the expression when the property value is
null.
 * <p/>
 * The resulting string is then executed against the value stack as any
other expression.
 * <p/>
 * This validator will return a boolean value of true or false.
 * <p/>
 * This example was developed because there was a conditional pair of
query string parameters
 * that needed to be validated.  If it was just one param then the more
appropriate validation
 * is to use the @RequiredFieldValidator.
 * <p/>
 * private Long requiredIdOne;
 * private Long requiredIdTwo;
 * <p/>
 * public Long getRequiredIdOne() {
 *      return requiredIdOne;
 * }
 *<p/>
 * public void setRequiredIdOne(final Long requiredIdOne) {
 *      this.requiredIdOne = requiredIdOne;
 * }
 *<p/>
 * public Long getRequiredIdTwo() {
 *      return requiredIdTwo;
 * }
 * <p/>
 * public void setRequiredIdTwo(final Long requiredIdTwo) {
 *      this.requiredIdTwo = requiredIdTwo;
 * }
 * <p/>
 *   @Validations(
 *        customValidators = {
 *        @CustomValidator(
 *                type = "customExpressionValidator",
 *                message = "",
 *                key = "validation.idQueryStringParameter.required",
 *                parameters = {@ValidationParameter(
 *                        name = "expression",
 *                        value = "((${requiredIdOne} != null) ||
(${requiredIdTwo} != null))"
 *                                + "&& !((${requiredIdOne} != null) &&
(${requiredIdTwo} != null))")})
 *                }
 *        )
 *   public String actionMethod() {
 *        return Action.SUCCESS;
 *   }
 * <p/>
 * <p/>
 * You must delimit the property to be found on the stack using ${...}
notation.
 * <p/>
 * This notation is used by the text parser methods in TextParseUtil.
 * <p/>
 * Don't forget to put an entry for your validator in validators.xml.
 * A JMock test was used to prove this validator works, as well as
actual implementation.
 * JMock MockObjectTestCase's are very effective for testing/developing
validtors since all
 * the Struts2 ingredients are easily created or mocked.
 * <p/>
 */
public final class CustomExpressionValidator extends ValidatorSupport {
 
    private String expression;
 
    public void setExpression(final String expression) {
        this.expression = expression;
    }
 
    public String getExpression() {
        return expression;
    }
 
    public void validate(final Object object) throws ValidationException
{
        ValueStack stack = ActionContext.getContext().getValueStack();
 
        // Create the expression string by populating it with property
        // values off the stack.
        String parsedObject = TextParseUtil.translateVariables(
                expression, stack, new
TextParseUtil.ParsedValueEvaluator() {
            public Object evaluate(final Object parsedValue) {
                if (parsedValue != null) {
                    return parsedValue.toString();
                }
                // This string value of null will be inserted into the
expression when
                // the saught after property defined as ${property} is
null on the stack.  It
                // will indeed be evaluated as null when the expression
is executed against
                // the stack.
                return "null";
            }
        });
 
        // Evaluate the expression string against the stack.
        Object obj = stack.findValue(parsedObject);
 
        Boolean answer = Boolean.FALSE;
        if (obj != null && Boolean.valueOf(obj.toString()) instanceof
Boolean) {
            answer = Boolean.valueOf(obj.toString());
        } else {
            log.error("Got result of " + obj + " when trying to get
Boolean from CustomExpressionValidator");
        }
 
        if (!answer.booleanValue()) {
            addActionError(object);
        }
    }
}

 
Andrew Shannon
Senior Associate
Perrin Quarles Associates
andrewshannon@pqa.com