
org.bidib.wizard.mvc.main.model.Macro Maven / Gradle / Ivy
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 extends BidibStatus> 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 extends BidibStatus> 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 extends BidibStatus> 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 extends BidibStatus>[] 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 extends BidibStatus>[] 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 extends BidibStatus>[] 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 extends BidibStatus> currentFunction = functions[index];
Function extends BidibStatus> 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 extends BidibStatus> 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 extends BidibStatus> 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 extends BidibStatus> 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 extends BidibStatus>[] 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 extends BidibStatus> 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