Hello,
On a table with the payslips of a contract, I have a menu with this action:
<bean parent="removeAnyCollectionFromMasterFrontAction">
<property name="actionabilityGates">
<list>
<bean class="org.popsuite.hr.model.gate.OnlyLastPayslipsGate" />
</list>
</property>
</bean>
The gate OnlyLastPayslipsGate, see code below, allows to remove only the last payslips of the contract.
There is one case where it does'nt behave as expected.
After removing all the payslips but the first one, the gate should be open immediatly. It's not the case if there is more than 2 payslips before removing the last ones.
Whatever the selected payslisps, when the action is called, the gate setModel method is called twice.
The first time, the model is null so the gate returns false.
The second time:
- the model contains the first payslip displayed in the table;
- and contract.getPayslips(period) returns ALL the payslips but the last one.
I would have expected the contract has been already fully updated here, ie without any of the payslips whose the suppression was required.
It seems the second call of the gate is run in the middle of the removing process.
Is it the expected behavior?
Pierre
================================================================
package org.popsuite.hr.model.gate;
import static org.popsuite.common.model.service.CalendarDateService.UNKNOWN_CALENDAR_DATE;
import static org.popsuite.hr.model.helper.PayslipHelper.getFirstPayslip;
import java.util.ArrayList;
import java.util.List;
import org.popsuite.common.model.DateInterval;
import org.popsuite.framework.binding.model.ModelValidatorGate;
import org.popsuite.hr.model.Contract;
import org.popsuite.hr.model.Payslip;
/**
* Check if any selected payslip is not followed by a non selected one.
*/
public class OnlyLastPayslipsGate
extends ModelValidatorGate<Payslip>
{
public OnlyLastPayslipsGate() {
super();
setCollectionBased(true);
}
@Override
protected boolean computeOpenState(Object model) {
if (model == null) return false;
List<Payslip> selectedPayslips = new ArrayList<Payslip>(prepareCollection(model));
if (selectedPayslips.isEmpty()) return false;
Payslip firstPayslip = getFirstPayslip(selectedPayslips);
DateInterval period = firstPayslip.getRefPeriod().getPeriod().clone();
period.setEnd(UNKNOWN_CALENDAR_DATE);
// All the payslips are supposed to have as owner the same contract.
Contract contract = selectedPayslips.get(0).getContract();
List<Payslip> payslipList = contract.getPayslips(period);
return selectedPayslips.size() == payslipList.size();
}
}
package org.popsuite.framework.binding.model;
import static java.util.Collections.singleton;
import static org.popsuite.framework.binding.model.IValidator.NULL_VALIDATOR;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import org.jspresso.framework.security.ISecurable;
import org.jspresso.framework.util.gate.AbstractModelGate;
/**
* Gate whom opening rules are based on validators. Each validator checks the model, or
* a collection of models. All the validator, for all the models, must be valid to open
* the gate.
*
* This class plays the same role than AbstractPropertyModelGate, but without been
* limited to a single property.
*
* @see org.jspresso.framework.binding.model.AbstractPropertyModelGate
*
* @param <E>
* the actual model type.
*/
public class ModelValidatorGate<E>
extends AbstractModelGate
implements PropertyChangeListener, ISecurable
{
private IValidator<E> validator;
private boolean open;
private boolean openOnTrue;
private Collection<String> grantedRoles;
@SuppressWarnings("unchecked")
public ModelValidatorGate() {
super();
openOnTrue = true;
open = ! openOnTrue;
validator = (IValidator<E>) NULL_VALIDATOR;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public ModelValidatorGate<E> clone() {
ModelValidatorGate<E> clonedGate =
(ModelValidatorGate<E>) super.clone();
clonedGate.open = ! openOnTrue;
return clonedGate;
}
@SuppressWarnings("unchecked")
public void setValidator(IValidator<E> validator) {
this.validator = validator != null ? validator : (IValidator<E>) NULL_VALIDATOR;
}
/**
* Configures the roles for which the gate is installed. It supports
* "<b>!</b>" prefix to negate the role(s).
*
* @param grantedRoles
* the grantedRoles to set.
*/
public void setGrantedRoles(Collection<String> grantedRoles) {
this.grantedRoles = grantedRoles;
}
/**
* Gets the grantedRoles.
*
* @return the grantedRoles.
*/
public Collection<String> getGrantedRoles() {
return grantedRoles;
}
@Override
public boolean isOpen() {
return open;
}
/**
* This property allows to revert the standard behaviour of the gate, i.e.
* close when it should normally have opened and the other way around.
*
* @param openOnTrue
* the openOnTrue to set.
*/
public void setOpenOnTrue(boolean openOnTrue) {
if (this.openOnTrue == openOnTrue) return;
this.openOnTrue = openOnTrue;
this.open = ! this.open;
}
private boolean revertOpen(boolean value) {
// return ! (openOnTrue ^ value);
if (openOnTrue) return value;
return ! value;
}
private void fireChangeFor(Object model) {
boolean oldOpen = isOpen();
this.open = revertOpen(computeOpenState(model));
firePropertyChange(OPEN_PROPERTY, oldOpen, isOpen());
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
Object model = getModel();
if (! (model instanceof Collection<?>)) {
model = evt.getNewValue();
}
fireChangeFor(model);
}
@Override
public void setModel(Object model) {
Object oldModel = getModel();
super.setModel(model);
if (oldModel != model) {
removeListenerFrom(oldModel);
addListenerTo(model);
}
if (validator.isUsingListener()) {
removeValidatorListenerFrom(oldModel);
addValidatorListenerTo(model);
}
fireChangeFor(model);
}
@SuppressWarnings("unchecked")
protected Collection<E> prepareCollection(Object model) {
if (model instanceof Collection<?>) return (Collection<E>) model;
return (Collection<E>) singleton(model);
}
protected void removeListenerFrom(Object model) {};
protected void addListenerTo(Object model) {};
private void removeValidatorListenerFrom(Object model) {
if (model == null) return;
for (E item : prepareCollection(model)) {
validator.removeListener(item, this);
}
}
private void addValidatorListenerTo(Object model) {
if (model == null) return;
for (E item : prepareCollection(model)) {
validator.addListener(item, this);
}
}
protected boolean computeOpenState(Object model) {
if (model == null) return false;
for (E item : prepareCollection(model)) {
if (! validator.run(item, model)) return false;
}
return true;
}
}

