Concepts for new features

4th release

new annotations

Details about @Expression

in addition to @SkipValidation every annotation of the property-validation module should provide a "skipIf" attribute of type @Expression.

@Required(skipIf = @Expression("#{customer.role eq 'admin'}"))

it should be implemented via a generic approach - that means concrete validation strategies should not check it manually.

moreover, @Expression should provide an attribute which points to the expression evaluator. so it's possible to create custom expressions by providing a custom evaluator. See also the ReferenceResolver idea, maybe expression evaluator could build further on that basis.

furthermore, it should also work for component initialization.

Details about @ConstraintSource

It should be possible to expose constraints through DTO's.

Example entities:

@Entity
public class User extends AbstractDomainObject
{
  @NotNull @Column @Size(min = 2, max = 64)
  private String firstName;

  @NotNull @Column @Size(min = 2, max = 64)
  private String lastName;

  @ManyToMany(mappedBy = "users")
  private List<Group> groups = new ArrayList<Group>();
  //...
}

@Entity
public class Group extends AbstractDomainObject
{
    @NotNull @Column @Size(min = 2, max = 14)
    private String name;

    @ManyToMany(cascade = CascadeType.PERSIST)
    private List<User> users = new ArrayList<User>();
  //...
}

Use-Case 1

@ConstraintSource(User.class)
public class UserDTO
{
  private String firstName; //mapped automatically to User#firstName
  private String lastName;  //mapped automatically to User#lastName

  //...
}

Use-Case 2

@ConstraintSource(User.class)
public class UserDTO
{
  @IgnoreConstraintSource
  @Size(min = 2, max = 32)
  private String firstName; //no mapping - constraints of the current property are used

  @TargetProperty("lastName")
  private String surName;  //mapped to User#lastName

  //...
}

Use-Case 3

@ConstraintSource(User.class)
public class UserDTO
{
  //...

  @ConstraintSource(Group.class)
  private String name;  //mapped to Group#name
}

Use-Case 4

@ConstraintSource(User.class)
public class UserDTO
{
  //...

  @ConstraintSource(Group.class)
  @TargetProperty("name")
  private String defaultGroupName;  //mapped to Group#name
}

Use-Case 5

@Entity
public class User extends AbstractDomainObject
{
  @NotNull @Column @Size(min = 2, max = 64)
  private String firstName;

  @LastName @NotNull @Column @Size(min = 2, max = 64)
  private String lastName;
}

@Target({METHOD, FIELD})
@Retention(RUNTIME)
@interface LastName {
}

@ConstraintSource(User.class)
public class UserDTO
{
  //...

  @ConstraintSource(User.class)
  @TargetPropertyId(LastName.class)
  private String surName;  //mapped to User#lastName
}


typesafe constraint parameters

final version at: http://wiki.apache.org/myfaces/Extensions/Validator/ConceptOverview/Constraint_Aspects

original draft:

it isn't a replacement for normal attributes like:
@Length(minimum = 3, maximum = 10)
it's a generic approach for shared mechanisms like a processing mode (warn vs. error)

required

public interface ValidationParameter {}

@Target({FIELD})
@Retention(RUNTIME)
public @interface ParameterKey {}

@Target({FIELD})
@Retention(RUNTIME)
public @interface ParameterValue {}

example

a warn vs. error mode example:

usage

@MyConstraint(/*...*/, parameters = ErrorLevel.class)

and

@MyConstraint(/*...*/, parameters = WarnLevel.class)

instead of the unsafe version:
@MyConstraint(/*...*/, param = @Param(key = "level", value = "warnMessage"))
and
@MyConstraint(/*...*/, param = @Param(key = "level", value = "errorMessage"))

implementation

public @interface MyConstraint
{
    //...

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

public interface WarnLevel extends ValidationParameter
{
    @ParameterKey //allows any type
    ValidationLevel type = ValidationLevel.WARN;

    @ParameterValue //allows any type
    MessagePostfix postfix = MessagePostfix.WARN;
}

public interface ErrorLevel extends ValidationParameter
{
    @ParameterKey //allows any type
    ValidationLevel type = ValidationLevel.ERROR;

    @ParameterValue //allows any type
    MessagePostfix postfix = MessagePostfix.ERROR;
}

or:
public interface ErrorLevel extends ValidationParameter {

}

optional types for this example

public enum ValidationLevel
{
    WARN, ERROR
}

public enum MessagePostfix
{
    WARN("warnMessage"), ERROR("errorMessage");

    private String postfix;

    MessagePostfix(String value)
    {
        this.postfix = value;
    }

    public String getPostfix()
    {
        return this.postfix;
    }
}

the string values are the final values for the parameter.
in the warn vs. error example the *Level interfaces tell e.g. a custom ParameterProcessor how to handle the provided information.
the postfix is e.g. a postfix for the message-key

further possibilities

additionally it's possible to register a ParameterProcessor which knows how to handle the param.
it's linked via the type of the key (which is annotated with @ParameterKey) TODO: methods for ParameterProcessor


brainstorming - ideas in progress

reference resolver

Add the possibility of a Custom reference resolver for cross validation.

With the cross validation annotations, the developer can specify an expression to identify the other value(s) used for validation. At this moment EL expressions and a limited version of Java variable notation is allowed.

With the addition of a ReferenceResolver interface, (some default implementations for EL and (full) Java variable notations could be foreseen) it allows the developer to implement their own resolving mechanism or notations.

The annotations used in Cross field validation will have an additional property resolverClass by default as follows

Class<? extends ReferenceResolver> resolverClass() default DefaultExtvalReferenceResolver.class; 

The referenceResolver interface has a single method resolveReference that could be implemented to allow custom resolving machanisms (like a MyJNDIResolver)

public interface ReferenceResolver {

        ReferencedValue resolveReference(final String targetValue, final MetaDataEntry metadataEntry);

There is another extension point for the developers, the DefaultExtvalReferenceResolver. It has a list of ReferenceGrammar 's (contains by default the EL and Java variable notations). The developers can add their own 'grammars' so custom notations can be developed to allow something like bean->property%indexList%.

The grammars have the InvocationOrder attribute so custom, developers made, grammars could be placed before or after the default ones. The order is important since the first grammar that is found to understand the reference, is used the evaluate it.

For backward compatibility the DefaultExtvalReferenceResolver.class is taken as reference resolver when the property resolverClass isn't found on the cross validation annotation.

Storage improvements

Allow the MetaDataExtractionInterceptor's to use the MetaDataStorage.

5th release

6th release

Extensions/Validator/DevDoc/Drafts (last edited 2010-03-24 10:01:37 by GerhardPetracek)