/* * (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; /* * FlexoPostCondition.java * Project WorkflowEditor * * Created by benoit on Mar 3, 2004 */ 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.action.FlexoActionizer; import org.openflexo.foundation.bindings.AbstractBinding; import org.openflexo.foundation.bindings.BindingDefinition.BindingDefinitionType; import org.openflexo.foundation.bindings.BindingModel; import org.openflexo.foundation.bindings.WKFBindingDefinition; 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.ExecutableWorkflowElement; import org.openflexo.foundation.wkf.FlexoProcess; import org.openflexo.foundation.wkf.LevelledObject; import org.openflexo.foundation.wkf.MetricsValue; import org.openflexo.foundation.wkf.MetricsValue.MetricsValueOwner; import org.openflexo.foundation.wkf.MetricsValueAdded; import org.openflexo.foundation.wkf.MetricsValueRemoved; import org.openflexo.foundation.wkf.WKFObject; import org.openflexo.foundation.wkf.action.AddEdgeMetricsValue; import org.openflexo.foundation.wkf.action.DeleteMetricsValue; import org.openflexo.foundation.wkf.dm.PostRemoved; import org.openflexo.foundation.wkf.dm.WKFAttributeDataModification; import org.openflexo.foundation.wkf.node.AbstractActivityNode; import org.openflexo.foundation.wkf.node.AbstractNode; import org.openflexo.foundation.wkf.node.ActivityNode; import org.openflexo.foundation.wkf.node.EventNode; import org.openflexo.foundation.wkf.node.FlexoNode; import org.openflexo.foundation.wkf.node.FlexoPreCondition; import org.openflexo.foundation.wkf.node.IFOperator; import org.openflexo.foundation.wkf.node.OROperator; import org.openflexo.foundation.wkf.node.OperationNode; import org.openflexo.foundation.wkf.node.OperatorNode; import org.openflexo.foundation.wkf.node.SubProcessNode; import org.openflexo.foundation.wkf.ws.FlexoPortMap; import org.openflexo.localization.FlexoLocalization; import org.openflexo.toolbox.Duration; import org.openflexo.toolbox.ProgrammingLanguage; /** * A FlexoPostCondition is a link (edge) between two AbstractNode, eventually through a FlexoPreCondition, in the context of Petri graphs * * @author benoit, sylvain */ public abstract class FlexoPostCondition<S extends AbstractNode, E extends AbstractNode> extends WKFEdge<S, E> implements DeletableObject, LevelledObject, ExecutableWorkflowElement, MetricsValueOwner { private static final Logger logger = Logger.getLogger(FlexoPostCondition.class.getPackage().getName()); public static final String EDGE_REPRESENTATION = "edgeRepresentation"; public static final String CONDITION_PRIMITIVE = "conditionPrimitive"; // ========================================================================== // ============================= Variables ================================== // ========================================================================== private Duration _delay; private Vector<MetricsValue> metricsValues; private boolean isGenericOutput = false; private AbstractBinding _conditionPrimitive; private boolean isConditional = false; private boolean isDefaultFlow = false; private String conditionDescription; public static FlexoActionizer<AddEdgeMetricsValue, FlexoPostCondition<?, ?>, WKFObject> addMetricsActionizer; public static FlexoActionizer<DeleteMetricsValue, MetricsValue, MetricsValue> deleteMetricsActionizer; // ========================================================================== // ============================= Constructor ================================ // ========================================================================== /** * Default constructor */ public FlexoPostCondition(FlexoProcess process) { super(process); metricsValues = new Vector<MetricsValue>(); } public void setDefaultName() { setName(getDefaultName()); } public String getDefaultName() { return FlexoLocalization.localizedForKey("no_name"); } public boolean isStartingFromAnExclusiveGateway() { return getStartNode() instanceof OperatorNode && ((OperatorNode) getStartNode()).isExclusiveGateway(); } @Override public Vector<MetricsValue> getMetricsValues() { return metricsValues; } public void setMetricsValues(Vector<MetricsValue> metricsValues) { this.metricsValues = metricsValues; setChanged(); } @Override public void addToMetricsValues(MetricsValue value) { if (value.getMetricsDefinition() != null) { metricsValues.add(value); value.setOwner(this); setChanged(); notifyObservers(new MetricsValueAdded(value, "metricsValues")); } } @Override public void removeFromMetricsValues(MetricsValue value) { metricsValues.remove(value); value.setOwner(null); setChanged(); notifyObservers(new MetricsValueRemoved(value, "metricsValues")); } @Override public void updateMetricsValues() { getWorkflow().updateMetricsForEdge(this); } public void addMetrics() { if (addMetricsActionizer != null) { addMetricsActionizer.run(this, null); } } public void deleteMetrics(MetricsValue value) { if (deleteMetricsActionizer != null) { deleteMetricsActionizer.run(value, null); } } /** * Default token increment is 1 Parametered value is overriden in TokenEdge * * @return token increment as int */ public int getTokenIncrem() { return 1; } /** * Remove itself from the datastructure. Mark itself has deleted. Set status to changed. */ @Override public final void delete() { logger.info("delete() in FlexoPostCondition for " + this); FlexoPreCondition pre = null; if (getEndNode() instanceof FlexoPreCondition) { pre = (FlexoPreCondition) getEndNode(); } super.delete(); if (pre != null && pre.getIncomingPostConditions().size() == 0) { pre.delete(); } setChanged(); notifyObservers(new PostRemoved(this)); deleteObservers(); } @Override public void addOutgoingEdgeToStartNode(S startNode) { // ContextualEdgeStarting must take care of maintaining the start node of their outgoing edges if (!(startNode instanceof ContextualEdgeStarting)) { startNode.addToOutgoingPostConditions(this); } } @Override public void addIncomingEdgeToEndNode(E endNode) { endNode.addToIncomingPostConditions(this); } @Override public void removeOutgoingEdgeFromStartNode(S startNode) { startNode.removeFromOutgoingPostConditions(this); } @Override public void removeIncomingEdgeFromEndNode(E endNode) { endNode.removeFromIncomingPostConditions(this); } public AbstractNode getNextNode() { if (getEndNode() != null) { return getEndNode().getNode(); } return null; } // ========================================================================== // ============================= InspectableObject ========================== // ========================================================================== @Override public String getInspectorName() { return Inspectors.WKF.POST_CONDITION_INSPECTOR; } public Duration getDelay() { return _delay; } public void setDelay(Duration delay) { if (_delay == null || !_delay.equals(delay)) { Duration oldDelay = _delay; _delay = delay; setChanged(); notifyAttributeModification("delay", oldDelay, delay); } } public boolean hasDelay() { return getDelay() != null && getDelay().isValid(); } public boolean hideWhenInduced() { return _booleanGraphicalPropertyForKey("hideWhenInduced", false); } public void setHideWhenInduced(boolean hide) { _setGraphicalPropertyForKey(hide, "hideWhenInduced"); setChanged(); notifyAttributeModification("hideWhenInduced", !hide, hide); } // ========================================================= // ============= Control graph management ================== // ========================================================= private static ControlGraphFactory<FlexoPostCondition<?, ?>> _executionComputingFactory; public static void setExecutionComputingFactory(ControlGraphFactory<FlexoPostCondition<?, ?>> factory) { _executionComputingFactory = factory; } public WorkflowControlGraph<FlexoPostCondition<?, ?>> 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("edge_($0)", getName()); } public Object getEdgeRepresentation() { return _graphicalPropertyForKey(EDGE_REPRESENTATION + "_" + DEFAULT); } public void setEdgeRepresentation(Object edgeRepresentation) { if (requireChange(getEdgeRepresentation(), edgeRepresentation)) { Object oldEdgeRepresentation = getEdgeRepresentation(); _setGraphicalPropertyForKey(edgeRepresentation, EDGE_REPRESENTATION + "_" + DEFAULT); setChanged(); notifyObservers(new WKFAttributeDataModification(EDGE_REPRESENTATION, oldEdgeRepresentation, edgeRepresentation)); } } public boolean startOperatorIsIfNode() { return getStartNode() instanceof IFOperator; } public boolean isPositiveEvaluation() { if (getStartNode() instanceof OperatorNode) { if (getStartNode() instanceof IFOperator) { return ((IFOperator) getStartNode()).isPositiveEvaluationPostcondition(this); } if (logger.isLoggable(Level.WARNING)) { logger.warning("isPositiveEvaluation called on an edge that does not come out of an IF-operator"); } } return false; } public void setIsPositiveEvaluation(boolean positive) { if (getStartNode() instanceof OperatorNode) { if (getStartNode() instanceof IFOperator) { IFOperator operator = (IFOperator) getStartNode(); if (isPositiveEvaluation() && !positive) { operator.removeFromPositiveEvaluationPostConditions(this); operator.addToNegativeEvaluationPostConditions(this); } else if (!isPositiveEvaluation() && positive) { operator.removeFromNegativeEvaluationPostConditions(this); operator.addToPositiveEvaluationPostConditions(this); } } else if (logger.isLoggable(Level.WARNING)) { logger.warning("isPositiveEvaluation called on an edge that does not come out of an IF-operator"); } } } public String getDerivedNameFromStartingObject() { if (getName() != null) { return getName(); } else if (getStartNode() != null) { return getStartNode().getName(); } return null; } public String getDerivedNameFromEndingObject() { if (getName() != null) { return getName(); } else if (getNextNode() != null) { return getNextNode().getName(); } return null; } public boolean startNodeIsFlexoNode() { return getStartNode() instanceof FlexoNode; } public boolean endNodeIsFlexoNode() { return getEndNode() instanceof FlexoNode; } public boolean endNodeIsPreCondition() { return getEndNode() instanceof FlexoPreCondition; } public boolean startNodeIsOperatorNode() { return getStartNode() instanceof OperatorNode; } public boolean endNodeIsOperatorNode() { return getEndNode() instanceof OperatorNode; } public boolean startNodeIsEventNode() { return getStartNode() instanceof EventNode; } public boolean endNodeIsEventNode() { return getEndNode() instanceof EventNode; } // ========================================================= // ===================== Validation ======================== // ========================================================= public abstract boolean isEdgeValid(); public static class PostConditionMustHaveAStartingObject extends ValidationRule<PostConditionMustHaveAStartingObject, FlexoPostCondition<AbstractNode, AbstractNode>> { public PostConditionMustHaveAStartingObject() { super(FlexoPostCondition.class, "post_condition_must_have_a_starting_oject"); } @Override public ValidationIssue<PostConditionMustHaveAStartingObject, FlexoPostCondition<AbstractNode, AbstractNode>> applyValidation( FlexoPostCondition<AbstractNode, AbstractNode> post) { if (post.getStartNode() == null || !post.getStartNode().isNodeValid()) { return new ValidationError<PostConditionMustHaveAStartingObject, FlexoPostCondition<AbstractNode, AbstractNode>>(this, post, "post_condition_has_no_starting_object", new DeletionFixProposal<PostConditionMustHaveAStartingObject, FlexoPostCondition<AbstractNode, AbstractNode>>( "delete_this_post_condition")); } return null; } } public static class PostConditionMustHaveAnEndingObject extends ValidationRule<PostConditionMustHaveAnEndingObject, FlexoPostCondition<?, ?>> { public PostConditionMustHaveAnEndingObject() { super(FlexoPostCondition.class, "post_condition_must_have_an_ending_oject"); } @Override public ValidationIssue<PostConditionMustHaveAnEndingObject, FlexoPostCondition<?, ?>> applyValidation(FlexoPostCondition<?, ?> post) { if (post.getEndNode() == null || post.getEndNode().getNode() == null || !post.getEndNode().getNode().isNodeValid()) { return new ValidationError<PostConditionMustHaveAnEndingObject, FlexoPostCondition<?, ?>>( this, post, "post_condition_has_no_starting_object", new DeletionFixProposal<PostConditionMustHaveAnEndingObject, FlexoPostCondition<?, ?>>("delete_this_post_condition")); } return null; } } public static class PostConditionStartingPointShouldBeExplicitelyDefined extends ValidationRule<PostConditionStartingPointShouldBeExplicitelyDefined, FlexoPostCondition<?, ?>> { public PostConditionStartingPointShouldBeExplicitelyDefined() { super(FlexoPostCondition.class, "post_condition_starting_point_should_be_explicitely_defined"); } @Override public ValidationIssue<PostConditionStartingPointShouldBeExplicitelyDefined, FlexoPostCondition<?, ?>> applyValidation( FlexoPostCondition<?, ?> post) { if (post.getStartNode() instanceof AbstractActivityNode && ((AbstractActivityNode) post.getStartNode()).isNormalNode() && post.getNextNode() instanceof AbstractActivityNode && !post.getIsGenericOutput() && ((AbstractActivityNode) post.getStartNode()).mightHaveOperationPetriGraph()) { ValidationWarning<PostConditionStartingPointShouldBeExplicitelyDefined, FlexoPostCondition<?, ?>> warning = new ValidationWarning<PostConditionStartingPointShouldBeExplicitelyDefined, FlexoPostCondition<?, ?>>( this, post, "post_condition_starting_point_should_be_explicitely_defined"); warning.addToFixProposals(new MarkAsGenericOutput(post)); warning.addToFixProposals(new DeletionFixProposal<PostConditionStartingPointShouldBeExplicitelyDefined, FlexoPostCondition<?, ?>>( "delete_this_post_condition")); return warning; } return null; } } public static class EdgesStartingFromEventBasedGatewayCannotBeConditionnal extends ValidationRule<EdgesStartingFromEventBasedGatewayCannotBeConditionnal, FlexoPostCondition<?, ?>> { public EdgesStartingFromEventBasedGatewayCannotBeConditionnal() { super(FlexoPostCondition.class, "EdgesStartingFromEventBasedGatewayCannotBeConditionnal"); } @Override public ValidationIssue<EdgesStartingFromEventBasedGatewayCannotBeConditionnal, FlexoPostCondition<?, ?>> applyValidation( FlexoPostCondition<?, ?> post) { if (post.getIsConditional() && post.isStartingFromAnExclusiveGateway()) { ValidationWarning<EdgesStartingFromEventBasedGatewayCannotBeConditionnal, FlexoPostCondition<?, ?>> warning = new ValidationWarning<EdgesStartingFromEventBasedGatewayCannotBeConditionnal, FlexoPostCondition<?, ?>>( this, post, "EdgesStartingFromEventBasedGatewayCannotBeConditionnal", new UnsetConditional()); return warning; } return null; } public static class UnsetConditional extends FixProposal<EdgesStartingFromEventBasedGatewayCannotBeConditionnal, FlexoPostCondition<?, ?>> { public UnsetConditional() { super("unset_conditional"); } @Override protected void fixAction() { getObject().setIsConditional(false); } } } public static class EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow extends ValidationRule<EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow, FlexoPostCondition<?, ?>> { public EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow() { super(FlexoPostCondition.class, "EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow"); } @Override public ValidationIssue<EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow, FlexoPostCondition<?, ?>> applyValidation( FlexoPostCondition<?, ?> post) { if (post.getIsDefaultFlow() && post.isStartingFromAnExclusiveGateway()) { ValidationWarning<EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow, FlexoPostCondition<?, ?>> warning = new ValidationWarning<EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow, FlexoPostCondition<?, ?>>( this, post, "EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow", new UnsetDefaultFlow()); return warning; } return null; } public static class UnsetDefaultFlow extends FixProposal<EdgesStartingFromEventBasedGatewayCannotBeDefaultFlow, FlexoPostCondition<?, ?>> { public UnsetDefaultFlow() { super("unset_default_flow"); } @Override protected void fixAction() { getObject().setIsDefaultFlow(false); } } } public static class SinglePostConditionCannotBeConditionnal extends ValidationRule<SinglePostConditionCannotBeConditionnal, FlexoPostCondition<?, ?>> { public SinglePostConditionCannotBeConditionnal() { super(FlexoPostCondition.class, "SinglePostConditionCannotBeConditionnal"); } @Override public ValidationIssue<SinglePostConditionCannotBeConditionnal, FlexoPostCondition<?, ?>> applyValidation( FlexoPostCondition<?, ?> post) { if (post.getIsConditional() && post.getStartNode() instanceof FlexoNode && ((FlexoNode) post.getStartNode()).isEndNode() && post.getEndNodesAndOutgoingPortCountForParentNodeOfStartNode() == 1) { FlexoNode startNode = (FlexoNode) post.getStartNode(); if (startNode.getOutgoingPostConditions().size() == 1) { ValidationWarning<SinglePostConditionCannotBeConditionnal, FlexoPostCondition<?, ?>> warning = new ValidationWarning<SinglePostConditionCannotBeConditionnal, FlexoPostCondition<?, ?>>( this, post, "SinglePostConditionCannotBeConditionnal"); return warning; } return null; } if (post.getIsConditional()) { if (post.getStartNode().getOutgoingPostConditions().size() == 1 && post.getEndNodesAndOutgoingPortCountForParentNodeOfStartNode() == 1) { ValidationWarning<SinglePostConditionCannotBeConditionnal, FlexoPostCondition<?, ?>> warning = new ValidationWarning<SinglePostConditionCannotBeConditionnal, FlexoPostCondition<?, ?>>( this, post, "SinglePostConditionCannotBeConditionnal"); return warning; } } return null; } } public static class MarkAsGenericOutput extends FixProposal<PostConditionStartingPointShouldBeExplicitelyDefined, FlexoPostCondition<?, ?>> { public MarkAsGenericOutput(FlexoPostCondition<?, ?> post) { super("define_as_generic_output"); } @Override protected void fixAction() { getObject().setIsGenericOutput(true); } } /** * Return a flag indicating if this edge was explicitely created as generic output: no semantic except that validation forget to mention * that this edge should be defined as outgoing an END node * * @return */ public boolean getIsGenericOutput() { return isGenericOutput; } public void setIsGenericOutput(boolean isGenericOutput) { if (requireChange(getIsGenericOutput(), isGenericOutput)) { this.isGenericOutput = isGenericOutput; setChanged(); notifyObservers(new WKFAttributeDataModification("isGenericOutput", !isGenericOutput, isGenericOutput)); } } public WKFBindingDefinition getConditionPrimitiveBindingDefinition() { if (getProject() != null) { WKFBindingDefinition returned = WKFBindingDefinition.get(this, CONDITION_PRIMITIVE, Boolean.class, BindingDefinitionType.GET, false); if (logger.isLoggable(Level.FINE)) { logger.fine("Returned WKFBindingDefinition : " + returned); } return returned; } return null; } public AbstractBinding getConditionPrimitive() { if (isBeingCloned()) { return null; } return _conditionPrimitive; } public void setConditionPrimitive(AbstractBinding conditionPrimitive) { AbstractBinding oldBindingValue = _conditionPrimitive; _conditionPrimitive = conditionPrimitive; if (_conditionPrimitive != null) { _conditionPrimitive.setOwner(this); _conditionPrimitive.setBindingDefinition(getConditionPrimitiveBindingDefinition()); isConditional = true; } setChanged(); notifyObservers(new WKFAttributeDataModification(CONDITION_PRIMITIVE, oldBindingValue, conditionPrimitive)); } public BindingModel getBindingModel() { if (getProcess() != null) { return getProcess().getBindingModel(); } return null; } public boolean getIsConditional() { /* * if (getStartNode() instanceof OperatorNode && ((OperatorNode) getStartNode()).isExclusiveGateway()) { return !getIsDefaultFlow(); * } */ if (isStartingFromOr()) { return !getIsDefaultFlow(); } if (canBeConditional()) { if (mustBeConditional()) { isConditional = true; } } return isConditional; } public boolean mustBeConditional() { if (canBeConditional()) { if (getStartNode() instanceof ActivityNode || getStartNode() instanceof OperationNode) { FlexoNode start = (FlexoNode) getStartNode(); if (start.isEndNode() && getEndNodesAndOutgoingPortCountForParentNodeOfStartNode() > 1) { return true; } } else if (getStartNode() instanceof FlexoPortMap && ((FlexoPortMap) getStartNode()).isOutputPort()) { if (getEndNodesAndOutgoingPortCountForParentNodeOfStartNode() > 1) { return true; } } } return false; } public int getEndNodesAndOutgoingPortCountForParentNodeOfStartNode() { int count = 0; if (getStartNode() instanceof FlexoNode) { FlexoNode start = (FlexoNode) getStartNode(); if (start.getParentPetriGraph() != null) { count += start.getParentPetriGraph().getAllEndNodes().size(); if (start.getParentPetriGraph().getContainer() instanceof SubProcessNode && ((SubProcessNode) start.getParentPetriGraph().getContainer()).getPortMapRegistery() != null) { count += ((SubProcessNode) start.getParentPetriGraph().getContainer()).getPortMapRegistery().getAllOutPortmaps().size(); } } } else if (getStartNode() instanceof FlexoPortMap && ((FlexoPortMap) getStartNode()).isOutputPort()) { SubProcessNode node = ((FlexoPortMap) getStartNode()).getSubProcessNode(); if (node != null && node.getPortMapRegistery() != null) { count = node.getPortMapRegistery().getAllOutPortmaps().size() + (node.getOperationPetriGraph() != null ? node.getOperationPetriGraph().getAllEndNodes().size() : 0); } } return count; } public boolean canBeConditional() { // Incident 1007031 if (getStartNode() instanceof EventNode) { return false; } boolean b = !getIsDefaultFlow() /* && getStartNode().getOutgoingPostConditions().size()>1 */; if (b && getStartNode() instanceof OperatorNode) { OperatorNode start = (OperatorNode) getStartNode(); b &= start.isInclusiveGateway(); } return b; } public boolean canBeDefaultFlow() { if (getStartNode() instanceof EventNode) { return false; } if (getStartNode() instanceof OperatorNode) { OperatorNode start = (OperatorNode) getStartNode(); return start.isInclusiveGateway() && !getIsConditional() || start instanceof OROperator; } return !getIsConditional(); } public boolean isStartingFromOr() { return getStartNode() instanceof OROperator; } /** * used by inspector to display or not the hasConditionnal checkBox * * @return */ public boolean isFreelyConditionnal() { return !(getStartNode() instanceof OperatorNode && ((OperatorNode) getStartNode()).isExclusiveGateway()); } public void setIsConditional(boolean isConditional) { if (isDefaultFlow && isConditional) { setIsDefaultFlow(false); } this.isConditional = isConditional; setChanged(); notifyAttributeModification("isConditional", !isConditional, isConditional); } public boolean getIsDefaultFlow() { return isDefaultFlow; } public void setIsDefaultFlow(boolean isDefaultFlow) { // a token edge cannot be conditional and the default flow at the same time. if (isDefaultFlow && isConditional) { setIsConditional(false); } this.isDefaultFlow = isDefaultFlow; setChanged(); notifyAttributeModification("isDefaultFlow", !isDefaultFlow, isDefaultFlow); } public String getConditionDescription() { return conditionDescription; } public void setConditionDescription(String conditionDescription) { this.conditionDescription = conditionDescription; setChanged(); notifyAttributeModification("conditionDescription", null, conditionDescription); } /** fake property to have a ReadOnly checkbox in inspector */ public boolean isImplicitConditional() { return getIsConditional(); } public void setIsImplicitConditional(boolean b) { } }