data class OTitleDetail (
val titleElements : MutableList<OTitleElement> = mutableListOf()
)
Is it possible with a BeanValidationBinder<OTitleDetail> to bind properties of the OTitleElement objects?
Or to ask it in another way: If we have to create a single BeanValidationBinder<OTitleElement> for each of the child elements, what would be the proper way to chain validation of all the binders we would get as a result.
Not sure if you’re asking for a generic way of handling a group of binders or something else. Anyway, I needed to handle a group of binders because I have multiple entities and they are not always of the same type. Here’s what I did:
/**
* The BinderGroup is used to register and manage multiple binders.
*
* @author martinisraelsen
*
*/
@Tag("BinderGroup")
@SpringComponent
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BinderGroup extends Component implements HasLogger {
@SuppressWarnings("rawtypes")
Map<Binder, Object> bindings = new HashMap<Binder, Object>();
List<FormDataChangedListener> changedListeners = new ArrayList<FormDataChangedListener>();
List<RecalculateEventListener> recalculateListeners = new ArrayList<RecalculateEventListener>();
/**
* Manual change override for fields that are not bound by the binder
*/
private boolean changed;
/**
* Is true if all binders are valid
*/
private boolean valid;
/**
* If set to true, onChange will not be marking a change
*/
private boolean inhibit;
public void addRecalcListener(RecalculateEventListener listener) {
recalculateListeners.add(listener);
}
public void addFormDataChangedListener(FormDataChangedListener listener) {
changedListeners.add(listener);
}
/**
* Registers a binder
*
* @param binder
* @param o
*/
public void register(Binder<?> binder, Object o) {
boolean addListener = !bindings.containsKey(binder);
bindings.put(binder, o);
if (addListener) {
binder.addValueChangeListener(e -> {
if (!e.isFromClient())
return;
boolean newChanged = (!Util.equals(e.getOldValue(), e.getValue()));
boolean newIsValid = this.isValid();
if (changed != newChanged || valid != newIsValid) {
this.valid = newIsValid;
setChanged(newChanged);
}
});
}
}
/**
* Unregisters a binder.
*
* @param b
*/
public void unregister(Binder<?> b) {
bindings.remove(b);
}
public void clear() {
bindings.clear();
}
/**
* Returns true if all registered beans are valid
*
* @return
*/
public boolean isValid() {
return bindings.keySet().stream().allMatch(e -> e.isValid());
}
/**
* Returns true if any of the registered beans have changes.
*
* @return
*/
public boolean hasChanges() {
return changed || bindings.keySet().stream().anyMatch(e -> e.hasChanges());
}
/**
* Indicates a change not handled by binders
*
* @param b
*/
public void setChanged(boolean b) {
if (inhibit)
return;
this.changed = b;
this.valid = isValid();
// Manually fire event
for (FormDataChangedListener f : changedListeners) {
f.dataChanged(new FormDataChangedEvent(this, this.changed, this.valid));
}
}
/**
* Turns off "listining" for changes. Useful when reloading data.
*
* @param b
*/
public void setInhibit(boolean b) {
inhibit = b;
}
/**
* Writes all registered beans. Throws a validation exception if unable to
* write.
*
* @throws ValidationException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void writeBeans() throws ValidationException {
for (Binder b : bindings.keySet()) {
if (bindings.get(b) != null) {
b.writeBean(bindings.get(b));
}
}
}
/**
* Fires a recalc request, unless this has been disabled with the inhibit
* flag
*/
public void fireRecalcRequest() {
if (inhibit)
return;
for (RecalculateEventListener e : recalculateListeners) {
e.recalculate(null);
}
}
}