|
From: Andy D. <an...@ma...> - 2007-08-08 17:27:24
|
Comments inlined below...
Jan Hoskens wrote:
> Hi all,
>
> I'm currently looking at a way to provide class based formModels. Well
> actually it boils down to a different implementation of
> MutablePropertyAccessStrategy called ClassPropertyAccessStrategy (CPAS).
> It all kinda starts to work, but there are a few things that bother me:
>
>
How hard would it be to adapt ClassPropertyAccessStrategy to non-class
based backing objects, such as Data Objects, Maps, or DOMs? We actually
use some of these already with Spring Rich (by using our own custom
MutablePropertyAccessStrategy) - it would be nice to reuse some of
ClassPropertyAccessStrategy if possible.
> - the current implementation (BeanPropertyAccessStrategy - BPAS) is
> using the BeanWrapperImpl behind the scenes. This implies that if
> there's a property method with the incorrect visibility, it will be
> 'fixed' to provide access. Do we need the same functionality here? I
> personally don't think so.
>
I agree (for now).
> - the BPAS is based on beans and thus can inspect collections/maps etc.
> The CPAS cannot look into these and currently doesn't support them. Any
> comments on this? Should we provide this and how? Geoffrey already gave
> a remark about this: we could use annotations to provide information
> about the element type and then go on. WDYT?
>
As Geoffrey has already mentioned, generic types are available at the
Class level, just not the Collection instance level. This means one can
inspect the generic type signature of the Class to determine the
Collection element type. Even so, this should still be configurable,
not just with annotations, but also at runtime (maybe some sort of
binding context key to override the collection type). This is useful
when a broad collection is defined (List<MyBaseObject>), but at runtime
we know it will contain only MySpecificBaseObject.
> - I provided both options BPAS and CPAS in order not to break anything.
> This leads to changes in
> FormModelHelper/AbstractFormModel/DefaultFormModel. Should we continue
> with both of them or evaluate both and remove one from the mentioned
> classes? The 'removed' can always be used when with the specific
> constructor on DefaultFormModel which takes a
> MutablePropertyAccessStrategy.
>
As it is, if CPAS directly uses Java reflection, then we will be forced
to use our own MutablePropertyAccessStrategy, since some of our forms
use non-JavaBean backing objects. However, if CPAS is designed to use
an abstracted interface for introspection, one that could easily be
swapped out for some other type of backing object (such as a
DataObject), then CPAS could do the job all by itself (we could reuse
everything CPAS has to offer and only have to implement the
introspection interface for our custom backing objects).
> - I want to provide some feature to allow write access when
> possible/needed. I'll explain with an example:
>
> interface Person{
> String getName();
> }
>
> class Admin implements Person {
> String getName()...
> }
>
> class BadPerson implements Person{
> String getName()...
> void setName(String)...
> }
>
> Now creating a formModel based on Person will only provide you with read
> access. Which can be ok for most cases. But when I want write access for
> BadPerson, I don't want to create a separate formModel. So I want some
> flexibility to state that, when possible, the property name may be
> writeable. It would be nice to be able to define a number of properties
> that may be writable if the current backing bean allows it. So the class
> provided to create the formModel will define the properties and their
> getters as a minimum together with the setters of those that are always
> writable. If specified, a bean that provides a setter for one of these
> properties might enable write access.
>
> I think I can do this by providing some backingBean specific logic in
> the CPAS. But I would like to hear your comments on this first.
>
We just did something very similar to this. In our case, we bound to a
property, something like this:
"agency.address.name"
However, the "agency" happens to be null when the form is first
displayed and isn't filled in until the user selects an agency from a
combo box. The requirement was that all components bound to the agency
will be disabled until the agency becomes non-null. Since we are using
our own custom MutablePropertyAccessStrategy (MPAS), we decided the
easiest path would be if a ValueModel could indicate whether it is
"enabled" or not. If a ValueModel is from a property path containing
"null", then it is considered disabled, otherwise it is considered
enabled. So, we created a GuardedValueModel interface:
public interface GuardedValueModel extends ValueModel, Guarded,
PropertyChangePublisher
{
String ENABLED_PROPERTY = "enabled";
}
And implement this interface in the custom ValueModels returned from our
custom MPAS. The ValueModel detects when the parent ValueModel becomes
non-null, then changes its enabled state (as defined in the Guarded
interface from Spring Rich), which fires a property change event. We
then link this property change event to the Form's FieldMetadata by
overriding the postProcessNewValueModel method in DefaultFormModel:
@Override
protected void postProcessNewValueModel(final String formProperty,
final ValueModel valueModel)
{
super.postProcessNewValueModel(formProperty, valueModel);
final GuardedValueModel guarded;
if(valueModel instanceof GuardedValueModel) {
guarded = (GuardedValueModel)valueModel;
} else if(valueModel instanceof ValueModelWrapper) {
final ValueModel inner =
((ValueModelWrapper)valueModel).getInnerMostWrappedValueModel();
if(inner instanceof GuardedValueModel) {
guarded = (GuardedValueModel)inner;
} else {
guarded = null;
}
} else {
guarded = null;
}
if(guarded != null) {
final FieldMetadata fieldMetadata = getFieldMetadata(formProperty);
guarded.addPropertyChangeListener(GuardedValueModel.ENABLED_PROPERTY,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
fieldMetadata.setEnabled(guarded.isEnabled());
}
});
}
}
This isn't perfect, but works for our needs.
We decided GuardedValueModel was easier than making, say, the "readable"
and "writeable" properties of PropertyMetaAspectAccessor mutable (and
observable) once we got into the guts of DefaultFormModel (and
AbstractFormModel).
- Andy
|