We have a bit of a stalemate currently in how we'll support validators
that are or contribute behaviors.
First, why would we want such behaviors? It is to enable validators to
influence what is rendered so that they can e.g. set a maxlength
attribute, add client side javascript validation, or even add ajax
validation (or a combination of these).
For example:
private static class MaxLengthValidator extends
StringValidator.MaximumLengthValidator
implements
IBehaviorProvider
{
public MaxLengthValidator(int maximum)
{
super(maximum);
}
public IBehavior newValidationBehavior(Component component)
{
if (component instanceof AbstractTextComponent)
{
return new SimpleAttributeModifier("maxlength",
String.valueOf(getMaximum()));
}
return null;
}
}
The current implementation is based on interface IBehaviorProvider:
/**
* Validator that provides a behavior, e.g. for rendering client-side or ajax
* validation. This interface can be implemented by either
* [EMAIL PROTECTED] IValidator validators} or [EMAIL PROTECTED] IFormValidator
form validators}.
*/
public interface IBehaviorProvider extends IClusterable
{
/**
* Gets behavior for validation. This method is called right after the
* validator is added to a form or form component. The resulting
behavior
* will be added to the component. Note that after the behavior is
added, it
* will just lead its own life; this method will not be invoked anymore.
*
* @param component
* component currently using the validator
* @return The behavior, which can be used for rendering e.g.
javascript or
* ajax validation. If this returns null, it will be ignored.
*/
IBehavior newValidationBehavior(Component component);
}
which is an optional interface your validators can implement if they
want to contribute such behaviors.
Now Igor in particular has a problem with this solution as he likes
the alternative (which I actually implemented at first, but didn't
commit) where you can simply code classes that are both a validator
and a behavior (implement IValidator or IFormValidator and IBehavior).
The technical issue with that is that we have an add method for both,
while they have a different hierarchy, so unless you do a hard cast,
it's not going to work. And even if you do a hard cast, behaviors and
validators are currently stored in their own structures, so either the
add methods would have to be aware that a behavior can also be a
validator (detail: validators currently are only known by form
components, so add(IBehavior) would have to do some instanceof
checking on itself to get this working) and that a validator can be a
behavior in the add(I(Form)Validator) method.
A way to fix this is to create a common interface (in my
implementation I called this IComponentFacet) for IBehavior and
IValidator and have one add method. In the mechanism I implemented,
facets were stored in the same structure (so the validators field in
FormComponent was removed) and it worked with a lookup method like
getFacets(Class).
The problem with this though is that once we start thinking in this
direction, it opens up a whole new range of questions. For instance,
what happens when people add validators to non-form components? Is
'detachable' or 'onBefore/AfterRender' really only something for
behaviors? What kind of mixing and matching can we expect people to
try in the future? I guess my largest problem is that I don't have a
great feeling about this mixing conceptually. I think in a future
version of Wicket, we might improve components by decoupling parts of
it so that you can assemble the parts you need. If and only if that
would really be worth the increased complexity of the API that comes
with such a change. However, I'm afraid that by supporting a kind-of
mixin like a combined validator and behavior is a step in that
direction, but it's too soon, half baked (like Jonathan stated on IRC,
we probably first need a full fledged event system in components if we
want to support components by composition).
The elegance of the current solution is that imho it doesn't have any
conceptual blur. Validators are still validators, but by implementing
IBehaviorProvider they can additionally *contribute* a behavior, which
is very different from *being* (IS-A) a behavior at the same time. It
is easy to implement (it's in svn right now, and I'm not worried for
one second this addition is limiting the way we can grow our API as
long as we keep this simple). Nice about the current solution is also
that it is easy to let your validator return different behaviors, e.g.
depending on the (type of) component it is coupled to. You can achieve
the same by using delegation in the validator/behavior you'd implement
otherwise, but imo, that's much less elegant and basically forces you
to either implement this pattern over and over again, or extend a base
class which limits your options. Not to mention that in the IS-A
scenario you'd have to be aware that if something can be an attribute
modifier or/ and an ajax behavior, you'd have to implement interface
IBehaviorListener or be ok with implementing an abstract ajax behavior
even if you would just need the attribute modifier for one occassion.
I guess my point is made. I'll back off for a bit and see what other
people have to say about this. Anyone's opinions are welcome, and core
developers, please contribute so that we can make this decision in
consensus.
Cheers,
Eelco