| Summary: | [DataBinding] model restrictions | ||
|---|---|---|---|
| Product: | [Eclipse Project] Platform | Reporter: | Mario Van Steenberghe <mario.vansteenberghe> |
| Component: | UI | Assignee: | Matthew Hall <qualidafial> |
| Status: | RESOLVED WONTFIX | QA Contact: | |
| Severity: | enhancement | ||
| Priority: | P3 | CC: | bokowski, gmendel, qualidafial |
| Version: | 3.2 | ||
| Target Milestone: | --- | ||
| Hardware: | PC | ||
| OS: | All | ||
| Whiteboard: | |||
| Bug Depends on: | 120582 | ||
| Bug Blocks: | |||
Have you looked at the IBinding that is returned by the IDataBindingContext and how you can hook up a binding event listener to that? In our application, that is how we hook in business model validation. Also, I think the IDomainValidator code that landed last night might help you here. Mario, most of what you suggest can be accomplished by using update strategies and custom validators. The only thing we would not support is a restriction registry. The problem with centralized registries is that you have one place where the behavior of the entire system can be altered, and this has the potential to introduce bugs that are hard to track down. However if this pattern works in your case there is nothing stopping you from implementing an IValidator that delegates validation to the restriction registry, and using that validator in your bindings. WONTFIX the suggestion for a restriction registry. The other features can be implemented using IValidators so consider those as WORKSFORME. :) |
Hi, I have been working an a similar databinding framework for about half a year now, and would like to switch yours in the future. I have been browsing through your code and the good thing is that there are a lot of similarities with what I have, but some very important features are still missing. Could you please give me your feedback about the following: I would like to see a way to add restrictions to the model that are automatically validated by the framework. For example, let's consider the following simplified model object: Person - firstName - lastName - age - birthDate On this model object, we could apply the following restrictions: Person - firstName (maxLen="20", mandatory="true") - lastName (maxLen="40", mandatory="true") - age (minVal="0", mandatory="false") - birthDate (mask="dd/mm/yy") When a restriction fails, the concerned fields should be marked, by a red background, a red border, ... I've been playing a bit with the existing framework and I came up with the following solution: public class RestrictionRegistry { private static RestrictionRegistry instance; private final Map restrictions = new HashMap(); public static RestrictionRegistry getDefault() { if (instance == null) instance = new RestrictionRegistry(); return instance; } public void associate(final Class modelType, final String property, final IRestriction restriction) { getRestrictionSet(modelType, property).add(restriction); } public void associate(final Class modelType, final String property, final IRestriction[] prestrictions) { final Set restrictionSet = getRestrictionSet(modelType, property); for (int i=0; i < prestrictions.length; i++) { restrictionSet.add(prestrictions[i]); } } public IRestriction[] getRestrictions(final Class modelType, final String property) { final Set restrictionSet = getRestrictionSet(modelType, property); return (IRestriction[]) restrictionSet.toArray(new IRestriction[restrictionSet.size()]); } private Set getRestrictionSet(final Class modelType, final String property) { Map restrictionsByProperty = (Map) this.restrictions.get(modelType); if (restrictionsByProperty == null) { restrictionsByProperty = new HashMap(); this.restrictions.put(modelType, restrictionsByProperty); } Set restrictionSet = (Set) restrictionsByProperty.get(property); if (restrictionSet == null) { restrictionSet = new HashSet(); restrictionsByProperty.put(property, restrictionSet); } return restrictionSet; } } public interface IRestriction { String validate(final Object value); String getId(); } public class DoubleMaxValueRestriction extends Restriction { public static final String ID = DoubleMaxValueRestriction.class.getName(); private final double maxValue; public DoubleMaxValueRestriction(final double pmaxValue) { super(ID); this.maxValue = pmaxValue; } public double getMaxValue() { return this.maxValue; } public String validate(final Object value) { String msg = null; final Double dval = (Double) value; if (dval != null && dval.doubleValue() > this.maxValue) msg = "max allowed value for this field is: " + this.maxValue; return msg; } } Restrictions can be defined programatically as follows: RestrictionRegistry.getDefault().associate(Employee.class, "salary", new DoubleMaxValueRestriction(10000)); Or through an XML file: <class name="org.eclipse.databinding.sample.model.Employee"> <property name="salary"> <maxValue>10000</maxValue> </property> </class> The thing I'm not so sure about is where to properly integrate this restriction mechanism into the framework. Perhaps it should be achieved by the validator, but then this validator doesn't know anything about the attached model objects, except for its value. So, for now I've created a custom 'RestrictableBeanUpdatableFactory', which returns RestrictableJavaBeanUpdatableValue objects. The RestrictableJavaBeanUpdatableValue is very much like the JavaBeanUpdatableValue, only that it will validate restrictions before the model is updated: public void setValue(final Object value) { this.updating = true; try { final String errorMsg = validateRestrictions(value); if (errorMsg != null) throw new RestrictionException("restriction failure: " + errorMsg); final Object oldValue = getValue(); final Method writeMethod = this.propertyDescriptor.getWriteMethod(); if (!writeMethod.isAccessible()) { writeMethod.setAccessible(true); } writeMethod.invoke(this.object, new Object[] { value }); fireChangeEvent(ChangeEvent.CHANGE, oldValue, getValue()); } catch (Exception e) { throw new RuntimeException(e); } finally { this.updating = false; } } private String validateRestrictions(final Object value) { String msg = null; final IRestriction[] restrictions = RestrictionRegistry.getDefault().getRestrictions(getModelType(), this.propertyDescriptor.getName()); for (int i=0; msg == null && restrictions != null && i < restrictions.length; i++) { msg = restrictions[i].validate(value); } return msg; } Now, the exception is propagated to the Binding object, where it should be communicated to a central error manager. Custom error handlers can then be implemented to listen to events from the error manager to color fields red for example.