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

org.bidib.wizard.mvc.main.model.Macro Maven / Gradle / Ivy

There is a newer version: 2.0.0-M1
Show newest version
package org.bidib.wizard.mvc.main.model;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.bidib.wizard.comm.BidibStatus;
import org.bidib.wizard.locale.Resources;
import org.bidib.wizard.mvc.main.model.function.EmptyFunction;
import org.bidib.wizard.mvc.main.model.function.Function;
import org.bidib.wizard.mvc.main.model.function.InputFunction;
import org.bidib.wizard.mvc.main.model.function.PortAction;
import org.bidib.wizard.mvc.main.model.listener.MacroListener;
import org.bidib.wizard.utils.MacroListUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jgoodies.binding.beans.Model;

public class Macro extends Model implements LabelAware, TooltipAware {
    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LoggerFactory.getLogger(Macro.class);

    static {
        try {
            // Q: why is this needed? A: export of beans with XMLDecoder
            PropertyDescriptor[] descriptor = Introspector.getBeanInfo(Macro.class).getPropertyDescriptors();

            for (int i = 0; i < descriptor.length; i++) {
                PropertyDescriptor propertyDescriptor = descriptor[i];
                if (propertyDescriptor.getName().equals("listeners")) {
                    propertyDescriptor.setValue("transient", Boolean.TRUE);
                }
                else if (propertyDescriptor.getName().equals("containsError")) {
                    propertyDescriptor.setValue("transient", Boolean.TRUE);
                }
                else if (propertyDescriptor.getName().equals("macroSaveState")) {
                    propertyDescriptor.setValue("transient", Boolean.TRUE);
                }
            }
        }
        catch (IntrospectionException e) {
            throw new RuntimeException(e);
        }
    }

    public static final String PROPERTY_PENDING_CHANGES = "pendingChanges";

    public static final int INFINITE_CYCLES = 0;

    public static final int MIN_CYCLES = 1;

    public static final int MAX_CYCLES = 250;

    private static final int DEFAULT_CYCLES = 1;

    private static final int DEFAULT_SPEED = 1;

    private final Collection listeners = new LinkedList();

    private int cycles = DEFAULT_CYCLES;

    private Vector> functions = new Vector>();

    /**
     * the maximum number of functions that can be stored in this macro
     */
    private int functionSize;

    private int id;

    private String label;

    private int speed = DEFAULT_SPEED;

    private Set startConditions = new HashSet();

    private boolean containsError;

    private MacroSaveState macroSaveState = MacroSaveState.PENDING_CHANGES;

    private boolean flatPortModel;

    /**
     * This should be only called by xml decoder ...
     */
    public Macro() {
    }

    /**
     * Create a new instance of Macro.
     * 
     * @param functionSize
     *            the maximum number of functions that can be stored in this macro.
     */
    public Macro(int functionSize) {
        this.functionSize = functionSize;
    }

    public void addMacroListener(MacroListener l) {
        listeners.add(l);
    }

    public void removeMacroListener(MacroListener l) {
        listeners.remove(l);
    }

    /**
     * Initialize the macro with default values.
     */
    public void initialize() {
        setCycles(DEFAULT_CYCLES);
        setSpeed(DEFAULT_SPEED);
        startConditions.clear();
        clearFunctions();
        setContainsError(false);
    }

    public boolean isValid() {
        for (Function function : functions) {
            if (function instanceof EmptyFunction) {
                LOGGER.warn("Found EmptyFunction in functions list! This is not allowed!");
                return false;
            }
            else if (function instanceof PortAction) {
                PortAction> portAction = (PortAction>) function;
                if (portAction.getPort() != null && portAction.getPort().getId() < 0) {
                    LOGGER.warn("Found illegal portnumber in PortFunction in functions list! This is not allowed!");
                    return false;
                }
            }
            else if (function instanceof InputFunction) {
                InputFunction portAction = (InputFunction) function;
                if (portAction.getInput() != null && portAction.getInput().getId() < 0) {
                    LOGGER.warn("Found illegal portnumber in InputFunction in functions list! This is not allowed!");
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * returns the number of cycles for this macro. This is the repeat count before the macro has finished. Be aware
     * that a value of '0' means endless execution of the macro.
     * 
     * @return the number of cycles
     */
    public int getCycles() {
        return cycles;
    }

    public void setCycles(int cycles) {
        this.cycles = cycles;

        fireCyclesChanged();
    }

    // equals method is overwritten, so we have to find the correct value
    // manually
    public int getFunctionIndex(Function function) {
        int result = -1;

        for (int index = 0; index < functions.size(); index++) {
            if (functions.get(index) == function) {
                result = index;
                break;
            }
        }
        return result;
    }

    /**
     * Add a function at the specified index if the index is lower than the maximum number of functions that can be
     * stored in this macro.
     * 
     * @param index
     *            the index to set the function at
     * @param function
     *            the new function
     * @return function successfully added
     */
    private boolean addFunction(int index, Function function) {
        boolean result = false;

        if (functions.size() < functionSize) {
            this.functions.add(index, function);
            result = true;
        }

        setMacroSaveState(MacroSaveState.PENDING_CHANGES);

        return result;
    }

    public void addFunctionsAfter(int row, Function[] functions) {
        List> functionsAdded = new LinkedList>();

        if (functions != null) {
            for (int index = 0; index < functions.length; index++) {
                if (addFunction(row + index + 1, functions[index])) {
                    functionsAdded.add(functions[index]);
                }
            }
        }
        else if (addFunction(row + 1, null)) {
            functionsAdded.add(null);
        }
        if (functionsAdded.size() > 0) {
            fireFunctionsAdded(row + 1, functionsAdded.toArray(new Function[0]));
        }
    }

    public void addFunctionsBefore(int row, Function[] functions) {
        List> functionsAdded = new LinkedList>();

        if (functions != null) {
            for (int index = 0; index < functions.length; index++) {
                if (addFunction(row + index, functions[index])) {
                    functionsAdded.add(functions[index]);
                }
            }
        }
        else if (addFunction(row, null)) {
            functionsAdded.add(null);
        }
        if (functionsAdded.size() > 0) {
            fireFunctionsAdded(row, functionsAdded.toArray(new Function[0]));
        }
    }

    public void addFunctionsInvertedAfter(int row, Function[] functions) {
        List> functionsAdded = new LinkedList>();

        if (ArrayUtils.isNotEmpty(functions)) {

            for (int index = 0; index < functions.length; index++) {
                // invert the action and add the function
                Function currentFunction = functions[index];
                Function invertedFunction = MacroListUtils.getInvertedFunction(currentFunction);
                if (invertedFunction == null) {
                    invertedFunction = currentFunction;
                }
                if (addFunction(row + index + 1, invertedFunction)) {
                    functionsAdded.add(invertedFunction);
                }
            }
        }
        else if (addFunction(row + 1, null)) {
            functionsAdded.add(null);
        }
        if (functionsAdded.size() > 0) {
            fireFunctionsAdded(row + 1, functionsAdded.toArray(new Function[0]));
        }
    }

    public void clearFunctions() {
        functions.clear();
        fireFunctionsRemoved();
    }

    public Function getFunction(int index) {
        Function result = null;

        if (index >= 0 && index < functions.size()) {
            result = functions.get(index);
        }
        return result;
    }

    public int getFunctionCount() {
        if (CollectionUtils.isNotEmpty(functions)) {
            return functions.size();
        }
        return 0;
    }

    public List> getFunctions() {
        return Collections.unmodifiableList(functions);
    }

    public void setFunctions(List> functions) {

        fireFunctionsRemoved();

        // TODO this is not correct because the maximum number of functions that can be stored in the macro is defined
        // by the hardware
        if (functions.size() > functionSize) {
            // the maximum number of functions that can be stored is exceeded
            LOGGER
                .warn(
                    "The maximum number of functions ({}) that can be stored is exceeded. Storing sublist of provided functions.",
                    functionSize);
            this.functions = new Vector>(functions.subList(0, functionSize - 1));
        }
        else {
            // set the new functions
            this.functions = new Vector>(functions);
        }
        fireFunctionsAdded(0, this.functions.toArray(new Function[0]));
    }

    public void removeFunction(int index) {
        functions.remove(index);

        setMacroSaveState(MacroSaveState.PENDING_CHANGES);
        fireFunctionRemoved(index);
    }

    public void moveFunction(int fromIndex, int toIndex) {
        LOGGER.info("Move function from index: {} to index: {}", fromIndex, toIndex);

        Function fromFunction = null;
        int targetIndex = toIndex;
        if (targetIndex > (functions.size() - 1)) {
            targetIndex = functions.size() - 1;
            LOGGER.info("Move function to end targetIndex: {}", targetIndex);

            fromFunction = functions.remove(fromIndex);
            LOGGER.info("Removed from fromIndex: {}, fromFunction: {}", fromIndex, fromFunction.getDebugString());

            functions.insertElementAt(fromFunction, targetIndex);
        }
        else if (targetIndex > fromIndex) {
            targetIndex = toIndex - 1;
            LOGGER.info("i. Move function targetIndex: {}, fromIndex: {}", targetIndex, fromIndex);

            fromFunction = functions.remove(fromIndex);
            LOGGER.info("i. Removed from fromIndex: {}, fromFunction: {}", fromIndex, fromFunction.getDebugString());
            functions.insertElementAt(fromFunction, targetIndex);
        }
        else { // targetIndex <= fromIndex
            LOGGER.info("ii. Move function targetIndex: {}, fromIndex: {}", targetIndex, fromIndex);

            fromFunction = functions.remove(fromIndex);
            LOGGER.info("ii. Removed from fromIndex: {}, fromFunction: {}", fromIndex, fromFunction.getDebugString());
            functions.insertElementAt(fromFunction, targetIndex);
        }

        setMacroSaveState(MacroSaveState.PENDING_CHANGES);
        fireFunctionMoved(fromIndex, targetIndex, fromFunction);
    }

    public void replaceFunction(int index, Function function) {
        functions.set(index, function);
    }

    /**
     * @return the maximum number of functions that can be stored in this macro
     */
    public int getFunctionSize() {
        return functionSize;
    }

    /**
     * @param functionSize
     *            the maximum number of functions that can be stored in this macro
     */
    public void setFunctionSize(int functionSize) {
        this.functionSize = functionSize;
        // TODO and what happens if more macro points are in the list than the size allows ?
        if (functions.size() > functionSize) {
            functions.setSize(functionSize);
        }
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
        fireLabelChanged(label);
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;

        fireSpeedChanged();
    }

    /**
     * @return the flatPortModel
     */
    public boolean isFlatPortModel() {
        return flatPortModel;
    }

    /**
     * @param flatPortModel
     *            the flatPortModel to set
     */
    public void setFlatPortModel(boolean flatPortModel) {
        this.flatPortModel = flatPortModel;
    }

    public Collection getStartConditions() {
        return Collections.unmodifiableCollection(startConditions);
    }

    public void clearStartConditions() {
        startConditions.clear();

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    public void addStartCondition(StartCondition startCondition) {
        LOGGER.debug("Add new start condition: {}", startCondition);
        startConditions.add(startCondition);

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    public void removeStartCondition(StartCondition startCondition) {
        startConditions.remove(startCondition);

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    public void setStartConditions(Collection startConditions) {
        this.startConditions = new HashSet(startConditions);

        // fire the start conditions have changed
        fireStartConditionChanged();
    }

    /**
     * @return the containsError
     */
    public boolean isContainsError() {
        return containsError;
    }

    /**
     * @param containsError
     *            the containsError to set
     */
    public void setContainsError(boolean containsError) {
        this.containsError = containsError;
    }

    private void fireFunctionsAdded(int index, Function[] functions) {
        for (MacroListener l : listeners) {
            l.functionsAdded(index, functions);
        }
    }

    private void fireFunctionRemoved(int index) {
        for (MacroListener l : listeners) {
            l.functionRemoved(index);
        }
    }

    private void fireFunctionMoved(int fromIndex, int toIndex, Function fromFunction) {
        for (MacroListener l : listeners) {
            l.functionMoved(fromIndex, toIndex, fromFunction);
        }
    }

    private void fireFunctionsRemoved() {
        for (MacroListener l : listeners) {
            l.functionsRemoved();
        }
    }

    private void fireLabelChanged(String label) {
        for (MacroListener l : listeners) {
            l.labelChanged(label);
        }
    }

    private void fireStartConditionChanged() {
        LOGGER.info("fireStartConditionChanged.");
        for (MacroListener l : listeners) {
            l.startConditionChanged();
        }
    }

    private void fireSpeedChanged() {
        LOGGER.info("fireSpeedChanged.");
        for (MacroListener l : listeners) {
            l.slowdownFactorChanged();
        }
    }

    private void fireCyclesChanged() {
        LOGGER.info("fireCyclesChanged.");
        for (MacroListener l : listeners) {
            l.cyclesChanged();
        }
    }

    /**
     * @return the pendingChanges
     */
    public MacroSaveState getMacroSaveState() {
        return macroSaveState;
    }

    /**
     * @param macroSaveState
     *            the pendingChanges to set
     */
    public void setMacroSaveState(MacroSaveState macroSaveState) {
        MacroSaveState oldValue = this.macroSaveState;
        this.macroSaveState = macroSaveState;

        firePropertyChange(PROPERTY_PENDING_CHANGES, oldValue, this.macroSaveState);
    }

    @Override
    public String getTooltip() {

        String tootip = null;
        switch (macroSaveState) {
            case PENDING_CHANGES:
                tootip = Resources.getString(Macro.class, "pending-changes");
                break;
            case SAVED_ON_NODE:
                tootip = Resources.getString(Macro.class, "saved-on-node");
                break;
            case PERMANENTLY_STORED_ON_NODE:
                tootip = Resources.getString(Macro.class, "permanently-stored-on-node");
                break;
            default:
                LOGGER.error("Unknown macro save state detected: {}", macroSaveState);
                break;
        }
        return tootip;
    }

    public String getDebugString() {
        return getClass().getSimpleName() + "[cycles=" + cycles + ",functions=" + functions + ",functionSize="
            + functionSize + ",id=" + id + ",label=" + label + ",speed=" + speed + ",startConditions="
            + startConditions + ",containsError=" + containsError + ",flatPortModel=" + flatPortModel + "]";
    }

    public String toString() {
        String result = "";

        if (label != null && label.length() > 0) {
            result = label;
        }
        else {
            result = Resources.getString(getClass(), "label") + "_" + id;
        }
        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy