All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.jaxx.runtime.swing.wizard.ext.WizardExtModel Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * JAXX :: Runtime
 * %%
 * Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nuiton.jaxx.runtime.swing.wizard.ext;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.nuiton.jaxx.runtime.swing.wizard.WizardModel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Un modèle de wizard avec des opérations.
 *
 * @param  le type des étapes.
 * @author Tony Chemit - [email protected]
 * @since 1.3
 */
public abstract class WizardExtModel extends WizardModel {

    /** Logger */
    private static final Logger log = LogManager.getLogger(WizardExtModel.class);

    public static final String OPERATIONS_PROPERTY_NAME = "operations";

    public static final String STEP_STATE_PROPERTY_NAME = "stepState";

    public static final String MODEL_STATE_PROPERTY_NAME = "modelState";

    public static final String WAS_STARTED_PROPERTY_NAME = "wasStarted";

    /** La liste des opérations à effectuer */
    protected final Set operations;

    /** le dictionnaire des modèles d'opération */
    protected final Map> models;

    /** Pour conserver les états des opérations */
    protected final Map stepStates;

    /** L'état générale du modèle */
    protected WizardState modelState;

    /** un drapeau pour savoir siune opération a été lancée */
    protected boolean wasStarted;

    @SuppressWarnings("unchecked")
    public WizardExtModel(Class stepClass, E... steps) {
        super(stepClass, steps);
        stepStates = new EnumMap(stepClass);
        operations = (Set) EnumSet.noneOf((Class) stepClass);
        models = (Map>) new EnumMap(stepClass);
    }

    public Set getOperations() {
        return operations;
    }

    public WizardState getModelState() {
        return modelState;
    }

    public boolean isWasStarted() {
        return wasStarted;
    }

    public boolean containsOperation(E step) {
        return getOperations().contains(step);
    }

    @SuppressWarnings("unchecked")
    public E getOperation() {
        return getStep() != null && getStep().isOperation() ? getStep() : null;
    }

    public WizardState getStepState() {
        E operation = getOperation();
        return getStepState(operation);
    }

    public WizardState getStepState(E step) {
        return stepStates.get(step);
    }

    public void setStepState(WizardState newState) {
        E operation = getOperation();
        setStepState(operation, newState);
    }

    public void setStepState(E step, WizardState newState) {
        WizardState oldValue = getStepState(step);
        stepStates.put(step, newState);
        if (valueAdjusting) {
            return;
        }
        fireIndexedPropertyChange(STEP_STATE_PROPERTY_NAME, getSteps().indexOf(step), oldValue, newState);
        updateModelState(step, newState);
        validate();
    }

    public boolean[] getAccessibleSteps() {
        if (log.isDebugEnabled()) {
            log.debug("compute with steps " + getSteps());
        }
        boolean[] result = new boolean[getSteps().size()];
        int index = getSteps().indexOf(getStep());
        if (index != -1) {

            for (int i = 0, j = steps.size(); i < j; i++) {
                if (i <= index) {
                    // tous les onglets inferieur ou egal au courant sont accessibles
                    result[i] = true;
                    continue;
                }
                // les onglets au dela de l'onglet sélectionné sont accessibles
                // uniquement si l'onglet precedent est accessible, valide et son etat est a SUCCESSED
                E previousStep = steps.get(i - 1);
                result[i] = modelState == WizardState.SUCCESSED ||
                        result[i - 1] &&
                                validate(previousStep) &&
                                (!previousStep.isOperation() || getStepState(previousStep) == WizardState.SUCCESSED);
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("accessibles steps -------- " + Arrays.toString(result));
        }
        return result;
    }

    @Override
    public void start() {
//        super.start();

        if (steps.isEmpty()) {
            throw new IllegalStateException("can not start, no step found");
        }

        // update universe of steps and actions
        updateUniverse();

        // set first step
        step = null;
        E startStep = steps.get(0);
        setStep(startStep);

        // model is ready
        setModelState(WizardState.PENDING);

        validate();
    }

    public void cancel() {

        for (E op : operations) {
            if (getStepState(op) == WizardState.PENDING) {
                // on annule l'opération à venir
                setStepState(op, WizardState.CANCELED);
            }
        }
        setModelState(WizardState.CANCELED);
    }

    public WizardExtModel addOperation(E operation) {
        if (operations.contains(operation)) {

            // skip add
            return this;
        }

        log.info("Add operation " + operation);
        operations.add(operation);
        // mis a jour de l'univers des etapes et operations
        updateUniverse();
        // validation
        validate();
        return this;
    }

    public void removeOperation(E operation) {
        if (!operations.contains(operation)) {

            // skip remove
            return;
        }
        operations.remove(operation);

        // mis a jour de l'univers des etapes et operations
        updateUniverse();
        // validation
        validate();
    }

    @Override
    public void setSteps(E... steps) {
        super.setSteps(steps);
        if (valueAdjusting) {
            return;
        }
        // on force la propagation de la nouvelle liste
        firePropertyChange(OPERATIONS_PROPERTY_NAME, null, operations);
    }

    public WizardExtStepModel getStepModel(E operation) {
        if (operation == null) {
            return null;
        }
        if (!operation.isOperation()) {
            throw new IllegalStateException("step [" + operation + "] is not an operation.");
        }
        return models.get(operation);
    }

    public void updateStepStates(List steps) {
        int index = 0;
        for (E e : steps) {
            fireIndexedPropertyChange(STEP_STATE_PROPERTY_NAME, index++, null, getStepState(e));
        }
        firePropertyChange(MODEL_STATE_PROPERTY_NAME, null, modelState);
    }

    public void setErrorOnStepModel(Exception error) {
        getStepModel(getOperation()).setError(error);
    }

    protected void setModelState(WizardState modelState) {
        WizardState oldValue = this.modelState;
        this.modelState = modelState;
        firePropertyChange(MODEL_STATE_PROPERTY_NAME, oldValue, modelState);
        if (!wasStarted) {
            if ((oldValue == null || oldValue == WizardState.PENDING) && modelState == WizardState.RUNNING) {
                wasStarted = true;
                firePropertyChange(WAS_STARTED_PROPERTY_NAME, false, true);
            }
        }
    }

    protected void updateModelState(E step, WizardState newState) {

        switch (newState) {
            case RUNNING:
                //le modele est occupé
                setModelState(WizardState.RUNNING);
                break;
            case FAILED:
                //le modele est en erreur
                setModelState(WizardState.FAILED);
                break;
            case CANCELED:
                //le modele devient annulé
                setModelState(WizardState.CANCELED);
                return;
            case PENDING:
                //le modele est en attente
                setModelState(WizardState.PENDING);
                break;
            case NEED_FIX:
                //le modele est en attente
                setModelState(WizardState.PENDING);
                break;
            case SUCCESSED:
                // on regarde si on peut passer le model a l'état success
                boolean valid = true;
                for (E o : operations) {
                    if (getStepState(o) != WizardState.SUCCESSED) {
                        valid = false;
                        break;
                    }
                }
                if (valid) {
                    setModelState(WizardState.SUCCESSED);
                } else {
                    setModelState(WizardState.PENDING);
                }
                break;
        }
        updateStepStates(steps);
    }

    @Override
    public void updateUniverse() {
//        setValueAdjusting(true);

        List oldSteps = new ArrayList<>(getSteps());
        log.info("Start updateUniverse (oldSteps = " + oldSteps + ")");
        E[] newSteps = updateStepUniverse();
        log.info("newSteps = " + Arrays.toString(newSteps));

        // do nothing if steps has not changed
        boolean skip = true;
        for (E newStep : newSteps) {
            if (!oldSteps.contains(newStep)) {
                skip = false;
            }
        }

        if (skip && oldSteps.size() == newSteps.length) {

            // same steps, so nothing to do
            log.info("Steps are same, do not modify anything");
            return;
        }

        for (WizardExtStepModel model : models.values()) {
            log.info("Destroy previous model : " + model);
            model.destroy();
        }
        models.clear();

        List toAdd = new ArrayList<>(Arrays.asList(newSteps));

        log.info("Will add models for " + toAdd);

        Iterator itr = toAdd.iterator();
        while (itr.hasNext()) {

            E step = itr.next();

            if (step.getModelClass() == null) {

                // no model attach to the step
                itr.remove();
                continue;
            }

            if (step.isOperation()) {

                // step is operation, model must be instanciate
                WizardExtStepModel model = (WizardExtStepModel) step.newModel();
                if (log.isInfoEnabled()) {
                    log.info("[" + step + "] Add primary model " + model);
                }
                models.put(step, model);

                itr.remove();
            }

            // step has a model
        }

        if (!toAdd.isEmpty()) {

            // there is some steps with model to attach
            itr = toAdd.iterator();
            while (itr.hasNext()) {

                E step = itr.next();
                Class> modelClass = step.getModelClass();
                WizardExtStepModel selectedModel = null;
                // find out in models a
                for (WizardExtStepModel model : models.values()) {
                    if (modelClass.isAssignableFrom(model.getClass())) {
                        // find one
                        selectedModel = model;
                        break;
                    }
                }

                if (selectedModel == null) {
                    throw new IllegalStateException("Could not find a primary model " + modelClass + " for step [" + step + "]");
                }

                log.info("[" + step + "] Attach model " + selectedModel);
                models.put(step, selectedModel);
                itr.remove();
            }

            if (!toAdd.isEmpty()) {
                throw new IllegalStateException("There is some step no model : " + toAdd);
            }
        }

        setSteps(newSteps);

        // on met a jour les états des étapes
        stepStates.clear();
        for (E step : newSteps) {
            if (!stepStates.containsKey(step)) {
                setStepState(step, WizardState.PENDING);
            }
        }

        // finally set the steps (this will refire a lot)

        log.info("Ending updateUniverse");

//        setValueAdjusting(false);
    }

    protected abstract E[] updateStepUniverse();

    @Override
    public void destroy() {
        super.destroy();

        for (WizardExtStepModel model : models.values()) {
            if (log.isDebugEnabled()) {
                log.debug("destroy model " + model);
            }
            model.destroy();
        }
    }

    protected int getOperationIndex(E operation) {
        int index = 0;
        for (E o : operations) {
            if (o.equals(operation)) {
                return index;
            }
            index++;
        }
        return -1;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy