/*
* (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;
/*
* FlexoPreCondition.java
* Project WorkflowEditor
*
* Created by benoit on Mar 3, 2004
*/
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.DeletableObject;
import org.openflexo.foundation.Inspectors;
import org.openflexo.foundation.validation.DeletionFixProposal;
import org.openflexo.foundation.validation.FixProposal;
import org.openflexo.foundation.validation.ParameteredFixProposal;
import org.openflexo.foundation.validation.ValidationError;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.wkf.ActionPetriGraph;
import org.openflexo.foundation.wkf.ActivityPetriGraph;
import org.openflexo.foundation.wkf.ExecutableWorkflowElement;
import org.openflexo.foundation.wkf.FlexoLevel;
import org.openflexo.foundation.wkf.FlexoPetriGraph;
import org.openflexo.foundation.wkf.FlexoProcess;
import org.openflexo.foundation.wkf.LevelledObject;
import org.openflexo.foundation.wkf.OperationPetriGraph;
import org.openflexo.foundation.wkf.WKFObject;
import org.openflexo.foundation.wkf.dm.PreRemoved;
import org.openflexo.foundation.xml.FlexoProcessBuilder;
import org.openflexo.inspector.InspectableObject;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.toolbox.ProgrammingLanguage;
/**
* A FlexoPreCondition is a recepient container for tokens that came along TokenEdge linked to it.<BR>
* <U>Main attributes</u><BR>
* <B>initTokenNbr</B> The number of tokens that are initially found in the container.(Usually 0)<BR>
*
* @author benoit
*/
public final class FlexoPreCondition extends Node implements InspectableObject, DeletableObject, LevelledObject, ExecutableWorkflowElement {
private static final Logger logger = Logger.getLogger(FlexoPreCondition.class.getPackage().getName());
// ==========================================================================
// ============================= Variables
// ==================================
// ==========================================================================
private FlexoNode _attachedNode;
private FlexoNode _attachedBeginNode;
private int _initTokenNbr = 0;
private int _triggerLevel = 1;
// ==========================================================================
// ============================= Constructor
// ================================
// ==========================================================================
/**
* Constructor used during deserialization
*/
public FlexoPreCondition(FlexoProcessBuilder builder) {
this(builder.process);
initializeDeserialization(builder);
}
/**
* Constructor with process
*/
public FlexoPreCondition(FlexoProcess process) {
super(process);
}
/**
* Constructor with attached node and process
*/
public FlexoPreCondition(FlexoNode attachedNode, FlexoProcess process) {
this(process);
if (attachedNode.getProcess() == process) {
setAttachedNode(attachedNode);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Inconsistent data while building FlexoPreCondition !");
}
}
}
@Override
public String getFullyQualifiedName() {
return (getAttachedNode() != null ? getAttachedNode().getFullyQualifiedName() : "UNLINKED") + ".PRE_CONDITION"
+ (getAttachedBeginNode() != null ? "." + getAttachedBeginNode().getFullyQualifiedName() : ".UNLINK");
}
@Override
public String toString() {
return getFullyQualifiedName();
}
/**
* Constructor with no attached node
*/
public FlexoPreCondition(FlexoNode attachedNode) {
this(attachedNode, attachedNode.getProcess());
}
/**
* Constructor with attached node
*/
public FlexoPreCondition(FlexoNode attachedNode, FlexoNode attachedBeginNode) {
this(attachedNode);
setAttachedBeginNode(attachedBeginNode);
}
@Override
public String getName() {
if (_attachedBeginNode != null) {
return _attachedBeginNode.getName();
}
return FlexoLocalization.localizedForKey("unbound_precondition");
}
@Override
public void setName(String aName) {
if (_attachedBeginNode != null) {
_attachedBeginNode.setName(aName);
}
}
@Override
public String getDescription() {
if (_attachedBeginNode != null) {
return _attachedBeginNode.getDescription();
}
return null;
}
@Override
public void setDescription(String description) {
if (_attachedBeginNode != null) {
_attachedBeginNode.setDescription(description);
}
}
public int getInitTokenNbr() {
return _initTokenNbr;
}
public void setInitTokenNbr(int tokenNbr) {
_initTokenNbr = tokenNbr;
}
public int getTriggerLevel() {
return _triggerLevel;
}
public void setTriggerLevel(int triggerLevel) {
_triggerLevel = triggerLevel;
}
@Override
public boolean mayHaveIncomingPostConditions() {
return true;
}
@Override
public boolean mayHaveOutgoingPostConditions() {
return false;
}
// ==========================================================================
// ========================== Embedding implementation =====================
// ==========================================================================
@Override
public boolean isContainedIn(WKFObject obj) {
if (getAttachedNode() == null) {
return false;
}
return getAttachedNode().isContainedIn(obj);
}
@Override
public boolean contains(WKFObject obj) {
return false;
}
// ==========================================================================
// ================================= Delete
// =================================
// ==========================================================================
@Override
public final void delete() {
if (getAttachedNode() != null) {
getAttachedNode().removeFromPreCondition(this);
}
if (getAttachedBeginNode() != null) {
getAttachedBeginNode().setAttachedPreCondition(null);
}
super.delete();
setChanged();
notifyObservers(new PreRemoved(this));
deleteObservers();
}
@Override
public boolean isNodeValid() {
return super.isNodeValid() && getAttachedNode() != null;
}
/**
* Build and return a vector of all the objects that will be deleted during this deletion
*
* @param aVector
* of DeletableObject
*/
@Override
public Vector<WKFObject> getAllEmbeddedDeleted() {
Vector<WKFObject> returned = getAllEmbeddedWKFObjects();
returned.addAll(getIncomingPostConditions());
if (_attachedBeginNode != null) {
returned.add(this);
}
return returned;
}
@Override
public String getInspectorName() {
return Inspectors.WKF.PRE_CONDITION_INSPECTOR;
}
@Override
public FlexoLevel getLevel() {
if (getAttachedNode() != null) {
return getAttachedNode().getLevel();
}
return null;
}
@Override
public FlexoNode getNode() {
return getAttachedNode();
}
public FlexoNode getAttachedNode() {
return _attachedNode;
}
public void setAttachedNode(FlexoNode newAttachedNode) {
_attachedNode = newAttachedNode;
if (newAttachedNode != null) {
newAttachedNode.addToPreConditions(this);
}
}
/**
* Return a Vector of all embedded WKFObjects
*
* @return a Vector of WKFObject instances
*/
@Override
public Vector<WKFObject> getAllEmbeddedWKFObjects() {
Vector<WKFObject> returned = new Vector<WKFObject>();
returned.add(this);
return returned;
}
public FlexoNode getAttachedBeginNode() {
FlexoNode returned = _attachedBeginNode;
if (returned != null) {
if (returned.getParentPetriGraph() == null) {
logger.warning("Inconsistent data: found a Node outside a PetriGraph !");
return null;
} else if (getAttachedNode() instanceof SelfExecutableNode) {
if (returned.getParentPetriGraph() != ((SelfExecutableNode) getAttachedNode()).getExecutionPetriGraph()) {
logger.warning("Inconsistent data: found a Node related to wrong ExecutionPetriGraph !");
return null;
}
} else if (getAttachedNode() instanceof FatherNode
&& returned.getParentPetriGraph() != ((FatherNode) getAttachedNode()).getContainedPetriGraph()) {
logger.warning("Inconsistent data: found a Node related to wrong PetriGraph !");
return null;
}
}
return returned;
}
public void setAttachedBeginNode(FlexoNode beginNode) {
if (_attachedBeginNode != null && _attachedBeginNode != beginNode) {
_attachedBeginNode.setAttachedPreCondition(null);
}
if (beginNode != null) {
if (beginNode.getNodeType() != NodeType.BEGIN) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Attached BEGIN node must be a BEGIN node !");
}
} else {
_attachedBeginNode = beginNode;
beginNode.setAttachedPreCondition(this);
}
} else {
_attachedBeginNode = null;
}
}
public Vector getAvailableBeginNodes() {
if (_attachedNode != null) {
if (_attachedNode instanceof SelfExecutableNode) {
FlexoPetriGraph pg = ((SelfExecutableNode) _attachedNode).getExecutionPetriGraph();
if (pg != null) {
return pg.getAllBeginNodes();
}
} else if (_attachedNode instanceof AbstractActivityNode) {
OperationPetriGraph pg = ((AbstractActivityNode) _attachedNode).getOperationPetriGraph();
if (pg != null) {
return pg.getAllBeginNodes();
}
} else if (_attachedNode instanceof OperationNode) {
ActionPetriGraph pg = ((OperationNode) _attachedNode).getActionPetriGraph();
if (pg != null) {
return pg.getAllBeginNodes();
}
}
}
return null;
}
/**
* Overrides getClassNameKey
*
* @see org.openflexo.foundation.FlexoModelObject#getClassNameKey()
*/
@Override
public String getClassNameKey() {
return "flexo_pre_condition";
}
// =========================================================
// ============= Control graph management ==================
// =========================================================
private static ControlGraphFactory<FlexoPreCondition> _executionComputingFactory;
public static void setExecutionComputingFactory(ControlGraphFactory<FlexoPreCondition> factory) {
_executionComputingFactory = factory;
}
public WorkflowControlGraph<FlexoPreCondition> getExecution() {
if (_executionComputingFactory != null) {
return _executionComputingFactory.getControlGraph(this);
}
return null;
}
@Override
public void setProgrammingLanguageForControlGraphComputation(ProgrammingLanguage language) {
if (getExecution() != null) {
getExecution().setProgrammingLanguage(language);
}
}
@Override
public void setInterproceduralForControlGraphComputation(boolean interprocedural) {
if (getExecution() != null) {
getExecution().setInterprocedural(interprocedural);
}
}
@Override
public String getExecutableElementName() {
return FlexoLocalization.localizedForKeyWithParams("pre_condition_($0)", getName());
}
// =========================================================
// ===================== Validation ========================
// =========================================================
public static class PreConditionMustBeAttachedToANode extends ValidationRule<PreConditionMustBeAttachedToANode, FlexoPreCondition> {
public PreConditionMustBeAttachedToANode() {
super(FlexoPreCondition.class, "pre_condition_must_be_attached_to_a_node");
}
@Override
public ValidationIssue<PreConditionMustBeAttachedToANode, FlexoPreCondition> applyValidation(FlexoPreCondition pre) {
if (pre.getAttachedNode() == null) {
return new ValidationError<PreConditionMustBeAttachedToANode, FlexoPreCondition>(this, pre,
"pre_condition_is_not_attached_to_a_node",
new DeletionFixProposal<PreConditionMustBeAttachedToANode, FlexoPreCondition>("delete_this_pre_condition"));
}
return null;
}
}
public static class PreConditionMustHaveIncomingEdges extends ValidationRule<PreConditionMustHaveIncomingEdges, FlexoPreCondition> {
public PreConditionMustHaveIncomingEdges() {
super(FlexoPreCondition.class, "pre_condition_must_have_incoming_edges");
}
@Override
public ValidationIssue<PreConditionMustHaveIncomingEdges, FlexoPreCondition> applyValidation(FlexoPreCondition pre) {
if (!pre.hasIncomingPostConditions()) {
return new ValidationError<PreConditionMustHaveIncomingEdges, FlexoPreCondition>(this, pre,
"pre_condition_has_no_incoming_edges",
new DeletionFixProposal<PreConditionMustHaveIncomingEdges, FlexoPreCondition>("delete_this_pre_condition"));
}
return null;
}
}
public static class PreConditionMustBeLinkedToABeginNode extends
ValidationRule<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition> {
public PreConditionMustBeLinkedToABeginNode() {
super(FlexoPreCondition.class, "pre_condition_must_be_linked_to_a_begin_node");
}
@Override
public ValidationIssue<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition> applyValidation(FlexoPreCondition pre) {
if (pre.getAttachedNode() != null
&& !pre.getAttachedNode().isEndNode()
&& pre.getAttachedNode() instanceof FatherNode
&& (!(pre.getAttachedNode() instanceof AbstractActivityNode) || ((AbstractActivityNode) pre.getAttachedNode())
.mightHaveOperationPetriGraph())) {
ValidationError<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition> error = null;
if (pre.getAttachedBeginNode() == null) {
error = new ValidationError<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition>(this, pre,
"pre_condition_is_not_linked_to_a_begin_node");
} else if (pre.getAttachedBeginNode().getParentPetriGraph() == null) {
error = new ValidationError<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition>(this, pre,
"pre_condition_is_linked_to_a_begin_node_outside_a_petri_graph");
} else if (pre.getAttachedNode() instanceof SelfExecutableNode) {
if (pre.getAttachedBeginNode().getParentPetriGraph() != ((SelfExecutableNode) pre.getAttachedNode())
.getExecutionPetriGraph()) {
error = new ValidationError<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition>(this, pre,
"pre_condition_is_linked_to_a_begin_node_related_to_wrong_petri_graph");
}
} else if (pre.getAttachedBeginNode().getParentPetriGraph() != ((FatherNode) pre.getAttachedNode())
.getContainedPetriGraph()) {
error = new ValidationError<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition>(this, pre,
"pre_condition_is_linked_to_a_begin_node_related_to_wrong_petri_graph");
}
if (error != null) {
error.addToFixProposals(new CreateAndLinkNewBeginNode((FatherNode) pre.getAttachedNode()));
FatherNode attachedNode = (FatherNode) pre.getAttachedNode();
FlexoPetriGraph pg = attachedNode.getContainedPetriGraph();
if (pg != null) {
Vector<FlexoNode> allAvailableBeginNodes = pg.getAllBeginNodes();
for (Enumeration<FlexoNode> e = allAvailableBeginNodes.elements(); e.hasMoreElements();) {
FlexoNode beginNode = e.nextElement();
if (beginNode.getAttachedPreCondition() == null) {
error.addToFixProposals(new LinkPreToExistingBeginNode(beginNode));
}
}
}
}
return error;
}
return null;
}
public class LinkPreToExistingBeginNode extends FixProposal<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition> {
public FlexoNode beginNode;
public LinkPreToExistingBeginNode(FlexoNode aBeginNode) {
super("link_pre_condition_to_($beginNode.name)");
beginNode = aBeginNode;
}
@Override
protected void fixAction() {
getObject().setAttachedBeginNode(beginNode);
}
}
public class CreateAndLinkNewBeginNode extends ParameteredFixProposal<PreConditionMustBeLinkedToABeginNode, FlexoPreCondition> {
public CreateAndLinkNewBeginNode(FatherNode flexoNode) {
super("create_and_link_new_begin_node", "newBeginNodeName", "enter_a_name_for_the_new_begin_node", flexoNode.getProcess()
.findNextInitialName(FlexoLocalization.localizedForKey("begin_node"), flexoNode));
}
@Override
protected void fixAction() {
String newBeginNodeName = (String) getValueForParameter("newBeginNodeName");
FlexoPreCondition pre = getObject();
FatherNode attachedNode = (FatherNode) pre.getAttachedNode();
FlexoPetriGraph pg = attachedNode instanceof SelfExecutableNode ? ((SelfExecutableNode) pre.getAttachedNode())
.getExecutionPetriGraph() : attachedNode.getContainedPetriGraph();
FlexoNode newBeginNode = null;
if (pg instanceof ActivityPetriGraph) {
newBeginNode = ((ActivityPetriGraph) pg).createNewBeginNode(newBeginNodeName);
} else if (pg instanceof OperationPetriGraph) {
newBeginNode = ((OperationPetriGraph) pg).createNewBeginNode(newBeginNodeName);
} else if (pg instanceof ActionPetriGraph) {
newBeginNode = ((ActionPetriGraph) pg).createNewBeginNode(newBeginNodeName);
}
pre.setAttachedBeginNode(newBeginNode);
}
}
}
@Override
public String getDefaultName() {
return null;
}
}