/*
* (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.edge;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.Inspectors;
import org.openflexo.foundation.bindings.Bindable;
import org.openflexo.foundation.validation.DeletionFixProposal;
import org.openflexo.foundation.validation.FixProposal;
import org.openflexo.foundation.validation.ValidationError;
import org.openflexo.foundation.validation.ValidationIssue;
import org.openflexo.foundation.validation.ValidationRule;
import org.openflexo.foundation.validation.ValidationWarning;
import org.openflexo.foundation.wkf.FlexoLevel;
import org.openflexo.foundation.wkf.FlexoProcess;
import org.openflexo.foundation.wkf.node.ActionNode;
import org.openflexo.foundation.wkf.node.FlexoNode;
import org.openflexo.foundation.wkf.node.Node;
import org.openflexo.foundation.wkf.node.OperationNode;
import org.openflexo.foundation.wkf.node.PetriGraphNode;
import org.openflexo.foundation.xml.FlexoProcessBuilder;
/**
* Edge linking 2 FlexoNode in the context of petri graphs (carry tokens) <U>Main attributes</U><BR>
* <B>Token increment</B>: the number of tokens that are sent to the next precondition when the node is executed.<BR>
* <B>Delay</B>: the time taken by the tokens to go to the next precondition (usually: no delay).<BR>
*
* @author sguerin
*
*/
public final class TokenEdge extends FlexoPostCondition<PetriGraphNode, Node> implements Bindable {
static final Logger logger = Logger.getLogger(TokenEdge.class.getPackage().getName());
private int _tokenIncrem;
/**
* Constructor used during deserialization
*/
public TokenEdge(FlexoProcessBuilder builder) {
this(builder.process);
initializeDeserialization(builder);
}
/**
* Default constructor
*/
public TokenEdge(FlexoProcess process) {
super(process);
_tokenIncrem = 1;
}
/**
* Constructor with start node, next precondition and process
*/
public TokenEdge(PetriGraphNode startNode, Node endNode, FlexoProcess process) throws InvalidEdgeException {
this(process);
if (endNode.getProcess() == process) {
setEndNode(endNode);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Inconsistent data while building TokenEdge !");
}
throw new InvalidEdgeException(this);
}
if (startNode.getProcess() == process) {
setStartNode(startNode);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Inconsistent data while building TokenEdge !");
}
throw new InvalidEdgeException(this);
}
if (!isEdgeValid()) {
throw new InvalidEdgeException(this);
}
// Special stuff to make operation node synchronized if required
if (startNode instanceof ActionNode && endNode.getNode() instanceof ActionNode && ((ActionNode) endNode.getNode()).isEndNode()) {
ActionNode s = (ActionNode) startNode;
ActionNode e = (ActionNode) endNode.getNode();
if (s.getOperationNode() == e.getOperationNode() && s.getOperationNode() != null) {
s.getOperationNode().setIsSynchronized(true);
}
}
}
/**
* Constructor with start node, next precondition
*/
public TokenEdge(PetriGraphNode startNode, Node nextPre) throws InvalidEdgeException {
this(startNode, nextPre, startNode.getProcess());
}
@Override
public int getTokenIncrem() {
return _tokenIncrem;
}
public void setTokenIncrem(int increm) {
_tokenIncrem = increm;
}
// ==========================================================================
// ============================= InspectableObject
// ==========================
// ==========================================================================
@Override
public String getInspectorName() {
return Inspectors.WKF.TOKEN_EDGE_INSPECTOR;
}
// ==========================================================================
// ============================= Accessors
// ==================================
// ==========================================================================
@Override
public FlexoLevel getLevel() {
if (getStartNode() != null) {
if (getStartNode() instanceof FlexoNode && ((FlexoNode) getStartNode()).isEndNode()) {
if (getStartNode() instanceof OperationNode) {
return FlexoLevel.ACTIVITY;
}
if (getStartNode() instanceof ActionNode) {
return FlexoLevel.OPERATION;
}
}
return getStartNode().getLevel();
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("No attached starting node for this post-condition !");
}
return null;
}
}
// ==========================================================================
// ============================= Validation
// =================================
// ==========================================================================
@Override
public boolean isEdgeValid() {
// Such edges are valid if and only if:
// - both nodes are in the same process
// While execution engine is not well defined, we dont't perform more
// validation on it
if (getStartNode() == null || getNextNode() == null || getStartNode() == getNextNode()) {
return false;
}
PetriGraphNode startNode = getStartNode();
PetriGraphNode nextNode = getEndNode().getNode();
if (startNodeIsOperatorNode()) {
// OperatorOutEdge rule
if (getStartNode().getParentPetriGraph() != nextNode.getParentPetriGraph()) {
return false;
}
} else if (endNodeIsOperatorNode()) {
// OperatorInEdge rule
if (!getStartNode().isEmbeddedInPetriGraph(nextNode.getParentPetriGraph())) {
return false;
}
}
return startNode.getProcess() == nextNode.getProcess();
}
public static class TokenEdgeMustBeValid extends ValidationRule<TokenEdgeMustBeValid, TokenEdge> {
public TokenEdgeMustBeValid() {
super(TokenEdge.class, "token_edge_must_be_valid");
}
@Override
public ValidationIssue<TokenEdgeMustBeValid, TokenEdge> applyValidation(TokenEdge edge) {
if (!edge.isEdgeValid()) {
ValidationError<TokenEdgeMustBeValid, TokenEdge> error = new ValidationError<TokenEdgeMustBeValid, TokenEdge>(this, edge,
"token_edge_is_not_valid");
error.addToFixProposals(new DeletionFixProposal<TokenEdgeMustBeValid, TokenEdge>("delete_this_edge"));
return error;
}
return null;
}
}
public static class TokenEdgeShouldHaveNonNullTokenIncrement extends
ValidationRule<TokenEdgeShouldHaveNonNullTokenIncrement, TokenEdge> {
public TokenEdgeShouldHaveNonNullTokenIncrement() {
super(TokenEdge.class, "token_edge_should_have_non_null_token_increment");
}
@Override
public ValidationIssue<TokenEdgeShouldHaveNonNullTokenIncrement, TokenEdge> applyValidation(TokenEdge edge) {
if (edge.getTokenIncrem() == 0) {
ValidationWarning<TokenEdgeShouldHaveNonNullTokenIncrement, TokenEdge> warning = new ValidationWarning<TokenEdgeShouldHaveNonNullTokenIncrement, TokenEdge>(
this, edge, "token_edge_has_a_null_token_increment");
warning.addToFixProposals(new SetsTokenIncrementToDefaultValue("sets_token_increment_to_default_value"));
warning.addToFixProposals(new DeletionFixProposal<TokenEdgeShouldHaveNonNullTokenIncrement, TokenEdge>("delete_this_edge"));
return warning;
}
return null;
}
}
public static class SetsTokenIncrementToDefaultValue extends FixProposal<TokenEdgeShouldHaveNonNullTokenIncrement, TokenEdge> {
public SetsTokenIncrementToDefaultValue(String aMessage) {
super(aMessage);
}
@Override
protected void fixAction() {
getObject().setTokenIncrem(1);
}
}
/**
* Overrides getClassNameKey
*
* @see org.openflexo.foundation.FlexoModelObject#getClassNameKey()
*/
@Override
public String getClassNameKey() {
return "token_edge";
}
@Override
public String toString() {
return super.toString() + " id=" + getSerializationIdentifier();
}
@Override
public Class<PetriGraphNode> getStartNodeClass() {
return PetriGraphNode.class;
}
@Override
public Class<Node> getEndNodeClass() {
return Node.class;
}
}