Using cross-validation

It's quite easy to use it.

Examples

Example 1:

public class Person
{
    @NotEquals("lastName")
    private String firstName;

    private String lastName;
...
}

In this case we refer a local property.

Example 2:

public class RegistrationPage
{
    private Person person = new Person();

    @Equals("person.password")
    private String oldPassword;
...
}

In this case we refer a property of a local reference.

Example 3:

public class RegistrationPage
{
    @Equals("#{person.password}")
    private String oldPassword;
...
}

In this case we refer a property of a bean.

Details of the examples

Within the page you just have to bind a property which has a cross-validation constraint.
If both, the source property as well as the referenced target, are bound to input components of the same form, the converted objects are used for validation. In case of a validation error, the validation error message(s) is/are displayed.

Model aware cross-validation (since 1.x.2)

If the target of the cross-validation isn't bound to an input component of the same form, the model value of the target is used for validation. The property which triggers the cross-validation always "uses" the converted object (not the model value).

That means:
Normal cross-validation: converted object (constraint source) + converted object (constraint target) Model aware cross-validation: converted object (constraint source) + model object (constraint target)

Reverse validation error messages

In case of model aware cross-validation the following issues are possible:

Solution: The validation strategy optionally provides a meaningful validation error message. It's called a reverse validation error message.

Custom cross-validation strategies

Example 1:

@SkipValidationSupport
public class EqualsStrategy extends AbstractCompareStrategy
{
    public boolean useTargetComponentToDisplayErrorMsg(CrossValidationStorageEntry crossValidationStorageEntry)
    {
        return true;
    }

    protected String getValidationErrorMsgKey(Annotation annotation, boolean isTargetComponent)
    {
        return ((Equals) annotation).validationErrorMsgKey();
    }

    public boolean isViolation(Object object1, Object object2, Annotation annotation)
    {
        return object1 != null && !object1.equals(object2);
    }

    public String[] getValidationTargets(Annotation annotation)
    {
        return ((Equals) annotation).value();
    }
}

In case of model aware cross-validation the validation error message is displayed at the source component instead of the target component.
Since the message is still meaningful, there's no need to provide a special reverse message.

Example 2:

...
    @Override
    protected String getReverseErrorMessageSummary(Annotation annotation)
    {
        return "meaningful validation error message summary";
    }
...
    @Override
    protected String getReverseErrorMessageDetail(Annotation annotation)
    {
        return "meaningful validation error message details";
    }
...

... override these methods to display a meaningful reverse validation message at the source component.

Conditional validation via cross-validation

Conditional validation is possible e.g. via @SkipValidation - that works if the condition is already available before the current request.
It might be interesting to validate constraints based on a condition which is available with the same request.
The group validation concept available via BV as well as via a possible add-on for ExtVal-Constraints (a simple example is available at demo of group validation light) might already solve your requirements.

If you prefer a style which directly uses JSF mechanisms, you can also use cross-validation for it. The validation target can be used as condition.

It's possible since the first version of ExtVal. Due to new features introduced in the 3rd release of ExtVal you will see additional things in the example you might not have seen so far. However, they aren't required since the approach just uses the mechanism of cross-validation.:

@Target({METHOD, FIELD})
@Retention(RUNTIME)
@Documented
@UsageInformation(UsageCategory.API)
public @interface ValidateLengthIf
{
    String[] validateIf();

    int minimum() default 0;

    int maximum() default Integer.MAX_VALUE;

    Class<? extends ValidationParameter>[] parameters() default ViolationSeverity.Error.class;
}

@SkipValidationSupport
public class ValidateLengthIfValidationStrategy extends AbstractCompareStrategy<ValidateLengthIf>
{
    private UIComponent component;
    private FacesMessage facesMessage;

    @Override
    protected void initCrossValidation(CrossValidationStorageEntry crossValidationStorageEntry)
    {
        this.component = crossValidationStorageEntry.getComponent();
    }

    public boolean isViolation(Object source, Object target, ValidateLengthIf annotation)
    {
        if (Boolean.TRUE.equals(target))
        {
            try
            {
                LengthValidator lengthValidator = resolveLengthValidator();
                lengthValidator.setMinimum(annotation.minimum());
                lengthValidator.setMaximum(annotation.maximum());
                lengthValidator.validate(FacesContext.getCurrentInstance(), this.component, source);
            }
            catch (ValidatorException e)
            {
                this.facesMessage = e.getFacesMessage();
                return true;
            }
        }
        return false;
    }

    private LengthValidator resolveLengthValidator()
    {
        return (LengthValidator)FacesContext.getCurrentInstance()
                .getApplication().createValidator("javax.faces.Length");
    }

    public String[] getValidationTargets(ValidateLengthIf annotation)
    {
        return annotation.validateIf();
    }

    @Override
    public boolean useTargetComponentToDisplayErrorMsg(CrossValidationStorageEntry crossValidationStorageEntry)
    {
        return false;
    }

    @Override
    protected String getErrorMessageSummary(ValidateLengthIf annotation, boolean isTargetComponent)
    {
        return this.facesMessage.getSummary();
    }

    @Override
    protected String getErrorMessageDetail(ValidateLengthIf annotation, boolean isTargetComponent)
    {
        return this.facesMessage.getDetail();
    }

    protected String getValidationErrorMsgKey(ValidateLengthIf annotation, boolean isTargetComponent)
    {
        //for using the message of the std. validator instead of a cross-validation-key for extval message resolving
        return null;
    }
}

Extensions/Validator/Getting_Started/Cross_Validation (last edited 2009-11-22 19:08:29 by 91-115-190-220)