/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.wkf.node;
/*
* FlexoNode.java
* Project WorkflowEditor
*
* Created by benoit on Mar 3, 2004
*/
import java.util.Iterator;
import java.util.TreeMap;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.AttributeDataModification;
import org.openflexo.foundation.bindings.Bindable;
import org.openflexo.foundation.bindings.BindingAssignment;
import org.openflexo.foundation.bindings.BindingDefinition.BindingDefinitionType;
import org.openflexo.foundation.bindings.BindingModel;
import org.openflexo.foundation.bindings.BindingValue;
import org.openflexo.foundation.bindings.WKFBindingDefinition;
import org.openflexo.foundation.dm.DMType;
import org.openflexo.foundation.utils.FlexoIndexManager;
import org.openflexo.foundation.utils.Sortable;
import org.openflexo.foundation.validation.DeletionFixProposal;
import org.openflexo.foundation.validation.ParameteredFixProposal;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.validation.ValidationWarning;
import org.openflexo.foundation.wkf.ActivityPetriGraph;
import org.openflexo.foundation.wkf.FlexoLevel;
import org.openflexo.foundation.wkf.FlexoPetriGraph;
import org.openflexo.foundation.wkf.FlexoProcess;
import org.openflexo.foundation.wkf.Role;
import org.openflexo.foundation.wkf.Status;
import org.openflexo.foundation.wkf.WKFGroup;
import org.openflexo.foundation.wkf.WKFObject;
import org.openflexo.foundation.wkf.dm.ChildrenOrderChanged;
import org.openflexo.foundation.wkf.dm.StatusSetOnNode;
import org.openflexo.foundation.wkf.dm.WKFAttributeDataModification;
import org.openflexo.localization.FlexoLocalization;
/**
* A FlexoNode is the base element representing a node in a PetriGraph. Three levels of FlexoNode exist and correspond to layers: Activity,
* Operation and Action. A FlexoNode is abstract and must be subsequently subclassed with ActivityFlexoNode, OperationFlexoNode and
* ActionFlexoNode
*
* @author bmangez, sguerin
*/
public abstract class PetriGraphNode extends Node implements Bindable, Sortable {
private static final Logger logger = Logger.getLogger(PetriGraphNode.class.getPackage().getName());
private Status _newStatus;
private FlexoPetriGraph parentPetriGraph;
/**
* Default constructor
*/
public PetriGraphNode(FlexoProcess process) {
super(process);
activationAssignments = new Vector<BindingAssignment>();
desactivationAssignments = new Vector<BindingAssignment>();
}
@Override
public void delete() {
if (getParentPetriGraph() != null) {
getParentPetriGraph().removeFromNodes(this);
}
super.delete();
}
@Override
public BindingModel getBindingModel() {
if (getProcess() != null) {
return getProcess().getBindingModel();
}
return null;
}
@Override
public PetriGraphNode getNode() {
return this;
}
@Override
public String getFullyQualifiedName() {
if (getParentPetriGraph() != null && getParentPetriGraph().getContainer() != null) {
return getParentPetriGraph().getContainer().getFullyQualifiedName() + "." + formattedString(getNodeName());
}
return "???";
}
public FlexoPetriGraph getParentPetriGraph() {
return parentPetriGraph;
}
public final void setParentPetriGraph(FlexoPetriGraph pg) {
parentPetriGraph = pg;
}
public int getDepth() {
if (getParentPetriGraph() != null && getParentPetriGraph().getContainer() instanceof PetriGraphNode) {
return ((PetriGraphNode) getParentPetriGraph().getContainer()).getDepth() + 1;
} else {
return 1;
}
}
public boolean isInRootPetriGraph() {
return getParentPetriGraph() != null && getParentPetriGraph().getContainer() == getProcess();
}
public boolean getDontGenerateRecursive() {
if (getDontGenerate()) {
return true;
}
if (getParentPetriGraph() != null && getParentPetriGraph().getContainer() != null) {
if (getParentPetriGraph().getContainer() instanceof PetriGraphNode) {
return ((PetriGraphNode) getParentPetriGraph().getContainer()).getDontGenerateRecursive();
} else {
return getParentPetriGraph().getContainer().getDontGenerate();
}
}
return false;
}
public final AbstractActivityNode getAbstractActivityNode() {
if (this instanceof AbstractActivityNode) {
return (AbstractActivityNode) this;
}
FlexoPetriGraph currentPetriGraph = getParentPetriGraph();
while (currentPetriGraph != null) {
if (currentPetriGraph.getContainer() instanceof AbstractActivityNode) {
return (AbstractActivityNode) currentPetriGraph.getContainer();
}
if (currentPetriGraph.getContainer() == null) {
return null;
}
if (currentPetriGraph.getContainer() instanceof PetriGraphNode) {
currentPetriGraph = ((PetriGraphNode) currentPetriGraph.getContainer()).getParentPetriGraph();
} else {
return null;
}
}
return null;
}
public PetriGraphNode getProcessLevelNode() {
PetriGraphNode node = this;
while (node != null && node.getParentPetriGraph() != null) {
if (node.isProcessLevel()) {
return node;
}
if (node.getParentPetriGraph().getContainer() instanceof PetriGraphNode) {
node = (PetriGraphNode) node.getParentPetriGraph().getContainer();
} else {
return null;
}
}
return null;
}
public boolean isProcessLevel() {
return getParentPetriGraph() != null && getParentPetriGraph().getContainer() instanceof FlexoProcess;
}
public final ActivityPetriGraph getActivityPetriGraph() {
FlexoPetriGraph currentPetriGraph = getParentPetriGraph();
while (currentPetriGraph != null) {
if (currentPetriGraph instanceof ActivityPetriGraph) {
return (ActivityPetriGraph) currentPetriGraph;
}
if (currentPetriGraph.getContainer() == null) {
return null;
}
if (currentPetriGraph.getContainer() instanceof PetriGraphNode) {
currentPetriGraph = ((PetriGraphNode) currentPetriGraph.getContainer()).getParentPetriGraph();
} else {
return null;
}
}
return null;
}
public final OperationNode getOperationNode() {
FlexoPetriGraph currentPetriGraph = getParentPetriGraph();
while (currentPetriGraph != null) {
if (currentPetriGraph.getContainer() instanceof OperationNode) {
return (OperationNode) currentPetriGraph.getContainer();
}
if (currentPetriGraph.getContainer() == null) {
return null;
}
if (currentPetriGraph.getContainer() instanceof PetriGraphNode) {
currentPetriGraph = ((PetriGraphNode) currentPetriGraph.getContainer()).getParentPetriGraph();
} else {
return null;
}
}
return null;
}
private String newStatusAsString;
public Status getNewStatus() {
if (_newStatus == null && newStatusAsString != null) {
if (getProject() != null) {
_newStatus = getProject().getGlobalStatus().get(newStatusAsString);
if (_newStatus == null && !isDeserializing()) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Status with name " + newStatusAsString + " could not be found.");
}
newStatusAsString = null;
}
} else if (!isDeserializing()) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No project for node " + getName());
}
}
}
return _newStatus;
}
public void setNewStatus(Status newStatus) {
Status old = _newStatus;
_newStatus = newStatus;
setChanged();
notifyObservers(new StatusSetOnNode(this, old, newStatus));
}
public String getNewStatusAsString() {
if (getNewStatus() != null) {
return getNewStatus().getFullyQualifiedName();
} else {
return null;
}
}
public void setNewStatusAsString(String statusName) {
this.newStatusAsString = statusName;
}
// ==========================================================================
// ============================= Accessors
// ==================================
// ==========================================================================
/**
* This attribute isn't supposed to be changed after node creation. Activity nodes are level ACTIVITY. Operations are level OPERATION.
* Actions are level ACTION. Other specific node can exist at level 3 or more.
*
* @return the node level.
*/
@Override
public abstract FlexoLevel getLevel();
@Override
public boolean mayHaveIncomingPostConditions() {
return true;
}
@Override
public boolean mayHaveOutgoingPostConditions() {
return true;
}
// ==========================================================================
// ================== Activation/Desactivation primitives ===================
// ==========================================================================
public static final String ACTIVATION_PRIMITIVE = "activationPrimitive";
private BindingValue _activationPrimitive;
public WKFBindingDefinition getActivationPrimitiveBindingDefinition() {
return WKFBindingDefinition.get(this, ACTIVATION_PRIMITIVE, (DMType) null, BindingDefinitionType.EXECUTE, false);
}
public BindingValue getActivationPrimitive() {
if (isBeingCloned()) {
return null;
}
return _activationPrimitive;
}
public void setActivationPrimitive(BindingValue activationPrimitive) {
BindingValue oldBindingValue = _activationPrimitive;
_activationPrimitive = activationPrimitive;
if (_activationPrimitive != null) {
_activationPrimitive.setOwner(this);
_activationPrimitive.setBindingDefinition(getActivationPrimitiveBindingDefinition());
}
setChanged();
notifyObservers(new WKFAttributeDataModification(ACTIVATION_PRIMITIVE, oldBindingValue, activationPrimitive));
}
public static final String DESACTIVATION_PRIMITIVE = "desactivationPrimitive";
private BindingValue _desactivationPrimitive;
public WKFBindingDefinition getDesactivationPrimitiveBindingDefinition() {
return WKFBindingDefinition.get(this, DESACTIVATION_PRIMITIVE, (DMType) null, BindingDefinitionType.EXECUTE, false);
}
public BindingValue getDesactivationPrimitive() {
if (isBeingCloned()) {
return null;
}
return _desactivationPrimitive;
}
public void setDesactivationPrimitive(BindingValue desactivationPrimitive) {
BindingValue oldBindingValue = _desactivationPrimitive;
_desactivationPrimitive = desactivationPrimitive;
if (_desactivationPrimitive != null) {
_desactivationPrimitive.setOwner(this);
_desactivationPrimitive.setBindingDefinition(getDesactivationPrimitiveBindingDefinition());
}
setChanged();
notifyObservers(new WKFAttributeDataModification(DESACTIVATION_PRIMITIVE, oldBindingValue, desactivationPrimitive));
}
private Vector<BindingAssignment> activationAssignments;
private Vector<BindingAssignment> desactivationAssignments;
public Vector<BindingAssignment> getActivationAssignments() {
return activationAssignments;
}
public void setActivationAssignments(Vector<BindingAssignment> someAssignments) {
this.activationAssignments = someAssignments;
setChanged();
notifyObservers(new WKFAttributeDataModification("activationAssignments", null, null)); // TODO notify better
}
public void addToActivationAssignments(BindingAssignment assignment) {
assignment.setOwner(this);
activationAssignments.add(assignment);
setChanged();
notifyObservers(new WKFAttributeDataModification("activationAssignments", null, null)); // TODO notify better
}
public void removeFromActivationAssignments(BindingAssignment assignment) {
assignment.setOwner(null);
activationAssignments.remove(assignment);
setChanged();
notifyObservers(new WKFAttributeDataModification("activationAssignments", null, null)); // TODO notify better
}
public BindingAssignment createActivationAssignement() {
BindingAssignment returned = new BindingAssignment(this);
addToActivationAssignments(returned);
return returned;
}
public void deleteActivationAssignement(BindingAssignment assignment) {
removeFromActivationAssignments(assignment);
}
public boolean isActivationAssignementDeletable(BindingAssignment assignment) {
return true;
}
public Vector<BindingAssignment> getDesactivationAssignments() {
return desactivationAssignments;
}
public void setDesactivationAssignments(Vector<BindingAssignment> someAssignments) {
this.desactivationAssignments = someAssignments;
setChanged();
notifyObservers(new WKFAttributeDataModification("desactivationAssignments", null, null)); // TODO notify better
}
public void addToDesactivationAssignments(BindingAssignment assignment) {
assignment.setOwner(this);
desactivationAssignments.add(assignment);
setChanged();
notifyObservers(new WKFAttributeDataModification("desactivationAssignments", null, null)); // TODO notify better
}
public void removeFromDesactivationAssignments(BindingAssignment assignment) {
assignment.setOwner(null);
desactivationAssignments.remove(assignment);
setChanged();
notifyObservers(new WKFAttributeDataModification("desactivationAssignments", null, null)); // TODO notify better
}
public BindingAssignment createDesactivationAssignement() {
BindingAssignment returned = new BindingAssignment(this);
addToDesactivationAssignments(returned);
return returned;
}
public void deleteDesactivationAssignement(BindingAssignment assignment) {
removeFromDesactivationAssignments(assignment);
}
public boolean isDesactivationAssignementDeletable(BindingAssignment assignment) {
return true;
}
private int index = -1;
@Override
public int getIndex() {
if (isBeingCloned()) {
return -1;
}
if (index == -1 && getCollection() != null) {
index = getCollection().length;
FlexoIndexManager.reIndexObjectOfArray(getCollection());
}
return index;
}
@Override
public void setIndex(int index) {
if (isDeserializing() || isCreatedByCloning()) {
setIndexValue(index);
return;
}
FlexoIndexManager.switchIndexForKey(this.index, index, this);
if (getIndex() != index) {
setChanged();
AttributeDataModification dm = new AttributeDataModification("index", null, getIndex());
dm.setReentrant(true);
notifyObservers(dm);
}
}
@Override
public int getIndexValue() {
return getIndex();
}
@Override
public void setIndexValue(int index) {
if (this.index == index) {
return;
}
int old = this.index;
this.index = index;
setChanged();
notifyAttributeModification("index", old, index);
if (!isDeserializing() && !isCreatedByCloning() && getParentPetriGraph() != null) {
getParentPetriGraph().setChanged();
getParentPetriGraph().notifyObservers(new ChildrenOrderChanged());
}
}
/**
* Overrides getCollection
*
* @see org.openflexo.foundation.utils.Sortable#getCollection()
*/
@Override
public PetriGraphNode[] getCollection() {
if (getParentPetriGraph() == null) {
return null;
}
return getParentPetriGraph().getNodes().toArray(new PetriGraphNode[0]);
}
public boolean isGrouped() {
return getContainerGroup() != null;
}
public boolean isEmbeddedInSelfExecutableNode() {
return isEmbeddedInObjectType(SelfExecutableNode.class);
}
public WKFGroup getContainerGroup() {
FlexoPetriGraph pg = getParentPetriGraph();
if (pg == null) {
return null;
}
for (WKFGroup group : pg.getGroups()) {
if (group.contains(this)) {
return group;
}
}
return null;
}
@Override
public boolean isNodeValid() {
if (this instanceof OperatorNode) {
if (getProcess() == null) {
return false;
}
return isEmbeddedInPetriGraph(getProcess().getActivityPetriGraph());
}
return getProcess() != null && getParentPetriGraph() != null;
}
@Override
public boolean isContainedIn(WKFObject obj) {
if (obj instanceof SelfExecutableNode) {
if (((SelfExecutableNode) obj).hasExecutionPetriGraph()) {
return isEmbeddedInPetriGraph(((SelfExecutableNode) obj).getExecutionPetriGraph());
}
return false;
} else if (obj instanceof LOOPOperator) {
if (((LOOPOperator) obj).hasExecutionPetriGraph()) {
return isEmbeddedInPetriGraph(((LOOPOperator) obj).getExecutionPetriGraph());
}
return false;
} else if (obj instanceof FatherNode) {
if (((FatherNode) obj).hasContainedPetriGraph()) {
return isEmbeddedInPetriGraph(((FatherNode) obj).getContainedPetriGraph());
}
return false;
} else if (obj instanceof FlexoPetriGraph) {
return isEmbeddedInPetriGraph((FlexoPetriGraph) obj);
} else if (obj instanceof WKFGroup) {
return getContainerGroup() == obj;
}
return super.isContainedIn(obj);
}
/**
* Recursive method to determine if the current node is embedded in the Petri graph <code>petriGraph</code>
*/
public boolean isEmbeddedInPetriGraph(FlexoPetriGraph petriGraph) {
if (getParentPetriGraph() == petriGraph) {
return true;
} else if (getParentPetriGraph() != null && getParentPetriGraph().getContainer() instanceof PetriGraphNode) {
return ((PetriGraphNode) getParentPetriGraph().getContainer()).isEmbeddedInPetriGraph(petriGraph);
}
return false;
}
/**
* Recursive method to determine if the current node is embedded in an object of type <code>klass</code>
*/
public boolean isEmbeddedInObjectType(Class<?> klass) {
if (getParentPetriGraph() == null) {
return false;
}
WKFObject parent = getParentPetriGraph();
while (parent instanceof PetriGraphNode || parent instanceof FlexoPetriGraph) {
if (klass.isAssignableFrom(parent.getClass())) {
return true;
}
if (parent instanceof AbstractNode) {
parent = ((PetriGraphNode) parent).getParentPetriGraph();
} else if (parent instanceof FlexoPetriGraph) {
parent = ((FlexoPetriGraph) parent).getContainer();
}
}
return false;
}
public String getRoleNameForInspector(Role role) {
if (role != null) {
if (role.isCache() || role.getProject() != getProject()) {
return role.getName() + " [" + role.getWorkflow().getName() + "]";
} else {
return role.getName();
}
} else {
return "";
}
}
public Role getBestRole() {
TreeMap<Integer, Vector<Role>> map = new TreeMap<Integer, Vector<Role>>();
getBestRole(new Vector<Node>(), map, 0);
Iterator<Integer> i = map.keySet().iterator();
while (i.hasNext()) {
Vector<Role> v = map.get(i.next());
if (v.size() > 0) {
return v.firstElement();
}
}
return null;
}
public boolean isAccessible() {
return hasIncomingPostConditions();
}
// ==========================================================================
// ============================= Validation =================================
// ==========================================================================
public static class PetriGraphNodeNameCannotBeEmpty extends ValidationRule<PetriGraphNodeNameCannotBeEmpty, PetriGraphNode> {
public PetriGraphNodeNameCannotBeEmpty() {
super(PetriGraphNode.class, "flexo_node_name_cannot_be_empty");
}
@Override
public ValidationIssue<PetriGraphNodeNameCannotBeEmpty, PetriGraphNode> applyValidation(PetriGraphNode object) {
PetriGraphNode node = object;
if (node.getName() == null || node.getName().trim().length() == 0) {
String proposal = node.findNextNonAmbigousName();
return new ValidationWarning<PetriGraphNodeNameCannotBeEmpty, PetriGraphNode>(this, node,
FlexoLocalization.localizedForKey("name_is_empty"), new RenameThisNode(node, proposal));
}
return null;
}
}
public static class RenameThisNode extends ParameteredFixProposal<PetriGraphNodeNameCannotBeEmpty, PetriGraphNode> {
public RenameThisNode(AbstractNode node, String proposal) {
super("rename_this_node", "newName", "enter_a_non_ambigous_name", proposal);
}
@Override
protected void fixAction() {
String newName = (String) getValueForParameter("newName");
getObject().setName(newName);
}
}
public static class PetriGraphNodeShouldBeAccessible extends ValidationRule<PetriGraphNodeShouldBeAccessible, PetriGraphNode> {
public PetriGraphNodeShouldBeAccessible() {
super(PetriGraphNode.class, "node_should_be_accessible");
}
@Override
public ValidationIssue<PetriGraphNodeShouldBeAccessible, PetriGraphNode> applyValidation(PetriGraphNode node) {
if (node instanceof ActionNode && ((ActionNode) node).getActionType() == ActionType.DISPLAY_ACTION) {
return null;
}
if (!node.isAccessible()) {
ValidationWarning<PetriGraphNodeShouldBeAccessible, PetriGraphNode> warning = new ValidationWarning<PetriGraphNodeShouldBeAccessible, PetriGraphNode>(
this, node, "node_($object.name)_is_not_accessible");
warning.addToFixProposals(new DeletionFixProposal<PetriGraphNodeShouldBeAccessible, PetriGraphNode>("delete_this_node"));
return warning;
}
return null;
}
}
}