[bug] strange model in a collection gate

5 posts / 0 new
Last post
atao
Offline
Joined: 10/15/2008
[bug] strange model in a collection gate

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
     * &quot;<b>!</b>&quot; 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;
    }
}

 

 

atao
Offline
Joined: 10/15/2008
[bug] strange model in a collection gate

Vincent,

Sorry to bother you with this thread. I'm still stuck with this issue Cry

It's not the same issue that the semantic one of the other thread (remove/delete).

Regards

Pierre

vvandens
Offline
Joined: 05/29/2008
[bug] strange model in a collection gate

Pierre,

I've had a look to it but this one is very difficult for me to investigate. I fear that I need a minimal test-case to be able to debug what's happening since the part of the framework code responsible for assigning the gates model is quite complex. Can you reproduce this problem using framework built-in boolean property model gates ? Could you assemble a test case that highlights the problem ?

 

Thanks,

Vincent

atao
Offline
Joined: 10/15/2008
[bug] strange model in a collection gate

Vincent,

I just created with jspresso 3.5-SNAPSHOT a new project with name "popsuite-test". As I don't know how to attach a file with this post, I'll create a new thread to send it.

I haven't changed anything else in the project.

The user case is:

- launch the project with the Swing UI and test data;

- login with "demo"

- click on "Payroll"

- click on Payroll > Companies

- click on the "Query..." button

- select the "Design2See" entry

- click on the "Opens the item..." button (between the Query... and the Add... buttons)

- click on the tab "Workers"

- select the worker "Durand" (or any other contract with many payslips)

  * as only the first payslip (ie february 2010) is selected the button "Delete selected items..." is inactive (gray minus)

- select all the payslips but the first one (ie february 2010)

  * now the button is active (red minus)

- click on the button

  * there is now only one paylsip (ie february 2010)

  * this paylsip is selected but the button stays grey

- click on any other worker and come back on the Durand's contract

  * now the button "Delete selected items..." is a red minus as expected.

Regards

Pierre

 

 

vvandens
Offline
Joined: 05/29/2008
[bug] strange model in a collection gate

Hi Pierre,

I've fixed it. We were missing a notification event for changing t gate model. I will deploy the new snapshot tomorrow morning.

 

Thanks for the report and the test case !

 

Best,

Vincent