/* * (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.action; import java.util.Vector; import java.util.logging.Logger; import org.openflexo.foundation.FlexoEditor; import org.openflexo.foundation.FlexoException; import org.openflexo.foundation.FlexoModelObject; import org.openflexo.foundation.action.FlexoActionType; import org.openflexo.foundation.action.FlexoUndoableAction; import org.openflexo.foundation.dm.DMEntity; import org.openflexo.foundation.dm.DMProperty; import org.openflexo.foundation.dm.DMPropertyImplementationType; import org.openflexo.foundation.wkf.FlexoProcess; import org.openflexo.foundation.wkf.edge.BackwardWSEdge; import org.openflexo.foundation.wkf.edge.ContextualEdgeStarting; import org.openflexo.foundation.wkf.edge.ExternalMessageInEdge; import org.openflexo.foundation.wkf.edge.ExternalMessageOutEdge; import org.openflexo.foundation.wkf.edge.FlexoPostCondition; import org.openflexo.foundation.wkf.edge.ForwardWSEdge; import org.openflexo.foundation.wkf.edge.InternalMessageInEdge; import org.openflexo.foundation.wkf.edge.InternalMessageOutEdge; import org.openflexo.foundation.wkf.edge.InvalidEdgeException; import org.openflexo.foundation.wkf.edge.MessageEdge; import org.openflexo.foundation.wkf.edge.TokenEdge; import org.openflexo.foundation.wkf.edge.TransferWSEdge; import org.openflexo.foundation.wkf.node.AbstractActivityNode; import org.openflexo.foundation.wkf.node.AbstractNode; import org.openflexo.foundation.wkf.node.ActionNode; import org.openflexo.foundation.wkf.node.ActionType; import org.openflexo.foundation.wkf.node.ChildNode; import org.openflexo.foundation.wkf.node.FlexoNode; import org.openflexo.foundation.wkf.node.FlexoPreCondition; import org.openflexo.foundation.wkf.node.Node; import org.openflexo.foundation.wkf.node.PetriGraphNode; import org.openflexo.foundation.wkf.ws.AbstractInPort; import org.openflexo.foundation.wkf.ws.FlexoPort; import org.openflexo.foundation.wkf.ws.FlexoPortMap; import org.openflexo.foundation.wkf.ws.InOutPort; import org.openflexo.foundation.wkf.ws.MessageEntry; import org.openflexo.foundation.wkf.ws.MessageEntryBinding; public class CreateEdge extends FlexoUndoableAction<CreateEdge, AbstractNode, AbstractNode> { private static final Logger logger = Logger.getLogger(CreateEdge.class.getPackage().getName()); public static FlexoActionType<CreateEdge, AbstractNode, AbstractNode> actionType = new FlexoActionType<CreateEdge, AbstractNode, AbstractNode>( "create_edge", FlexoActionType.defaultGroup) { /** * Factory method */ @Override public CreateEdge makeNewAction(AbstractNode focusedObject, Vector<AbstractNode> globalSelection, FlexoEditor editor) { return new CreateEdge(focusedObject, globalSelection, editor); } @Override public boolean isVisibleForSelection(AbstractNode object, Vector<AbstractNode> globalSelection) { return false; } @Override public boolean isEnabledForSelection(AbstractNode object, Vector<AbstractNode> globalSelection) { return true; } private String[] persistentProperties = { "startingNode", "endNode", "endNodePreCondition" }; @Override protected String[] getPersistentProperties() { return persistentProperties; } }; CreateEdge(AbstractNode focusedObject, Vector<AbstractNode> globalSelection, FlexoEditor editor) { super(actionType, focusedObject, globalSelection, editor); } static { FlexoModelObject.addActionForClass(actionType, AbstractNode.class); } AbstractNode startingNode; AbstractNode endNode; private FlexoPreCondition endNodePreCondition; private FlexoPostCondition newPostCondition = null; private boolean isGenericOutput = false; private Object outputContext; private String newEdgeName; private CreatePreCondition createPreConditionForInducedEdgeConstruction = null; private CreatePreCondition createPreCondition = null; @Override protected void doAction(Object context) throws InvalidEdgeDefinition, DisplayActionCannotBeBound { // 1. Check validity // Cannot have null start node or end node if (startingNode == null || endNode == null) { throw new InvalidEdgeDefinition(); } // Display action cannot have outgoing edges if (startingNode instanceof ActionNode && ((ActionNode) startingNode).getActionType() == ActionType.DISPLAY_ACTION) { throw new DisplayActionCannotBeBound(); } // End activity node of the top level cannot have outgoing edges if (startingNode instanceof AbstractActivityNode && ((AbstractActivityNode) startingNode).isEndNode() && ((AbstractActivityNode) startingNode).getActivityPetriGraph().getContainer() instanceof FlexoProcess) { throw new InvalidEdgeDefinition(); } // 2. Let's find the end node (mainly ensure that edges that go to a FlexoNode go onto a precondition AbstractNode realEndNode = findEndNode(); // Start node check if (!startingNode.mayHaveOutgoingPostConditions()) { throw new InvalidEdgeDefinition(); } // End node check if (!realEndNode.mayHaveIncomingPostConditions()) { throw new InvalidEdgeDefinition(); } // 3. Create the edge try { if (startingNode instanceof AbstractInPort) { AbstractInPort start = (AbstractInPort) startingNode; if (realEndNode instanceof FlexoPortMap && ((FlexoPortMap) realEndNode).isInputPort()) { newPostCondition = new ForwardWSEdge(start, (FlexoPortMap) realEndNode); } else if (realEndNode instanceof Node) { newPostCondition = new InternalMessageInEdge(start, (Node) realEndNode); } } else if (startingNode instanceof FlexoPortMap && ((FlexoPortMap) startingNode).isOutputPort()) { FlexoPortMap start = (FlexoPortMap) startingNode; if (realEndNode instanceof FlexoPort && ((FlexoPort) realEndNode).isOutPort()) { newPostCondition = new BackwardWSEdge(start, (FlexoPort) realEndNode); } else if (realEndNode instanceof FlexoPortMap && ((FlexoPortMap) realEndNode).isInputPort()) { newPostCondition = new TransferWSEdge(start, (FlexoPortMap) realEndNode); } else if (realEndNode instanceof Node) { newPostCondition = new ExternalMessageOutEdge(start, (Node) realEndNode); } } else if (realEndNode instanceof FlexoPort && ((FlexoPort) realEndNode).isOutPort()) { FlexoPort end = (FlexoPort) realEndNode; if (startingNode instanceof FlexoPortMap && ((FlexoPortMap) startingNode).isOutputPort()) { newPostCondition = new BackwardWSEdge((FlexoPortMap) startingNode, end); } else if (startingNode instanceof PetriGraphNode) { newPostCondition = new InternalMessageOutEdge((PetriGraphNode) startingNode, end); } } else if (realEndNode instanceof FlexoPortMap && ((FlexoPortMap) realEndNode).isInputPort()) { FlexoPortMap end = (FlexoPortMap) realEndNode; if (startingNode instanceof AbstractInPort) { newPostCondition = new ForwardWSEdge((AbstractInPort) startingNode, end); } else if (startingNode instanceof PetriGraphNode) { newPostCondition = new ExternalMessageInEdge((PetriGraphNode) startingNode, end); } } else if (startingNode instanceof PetriGraphNode && realEndNode instanceof Node) { newPostCondition = new TokenEdge((PetriGraphNode) startingNode, (Node) realEndNode); } } catch (InvalidEdgeException e) { e.printStackTrace(); throw new InvalidEdgeDefinition(); } if (newPostCondition instanceof MessageEdge) { addMessageVariableToProcessInstance((MessageEdge) newPostCondition); } if (newPostCondition != null) { // Set name when defined if (getNewEdgeName() != null) { newPostCondition.setName(getNewEdgeName()); } newPostCondition.setLocationConstraintFlag(true); newPostCondition.setIsGenericOutput(getIsGenericOutput()); newPostCondition.updateMetricsValues(); if (getStartingNode() instanceof ContextualEdgeStarting && getOutputContext() != null) { ((ContextualEdgeStarting) getStartingNode()).addToOutgoingPostConditions(newPostCondition, getOutputContext()); } else { getStartingNode().addToOutgoingPostConditions(newPostCondition); } } else { throw new InvalidEdgeDefinition(); } if (createPreCondition != null && createPreCondition.getNewPreCondition() != null) { createPreCondition.getNewPreCondition().resetLocation(); } if (createPreConditionForInducedEdgeConstruction != null && createPreConditionForInducedEdgeConstruction.getNewPreCondition() != null) { createPreConditionForInducedEdgeConstruction.getNewPreCondition().resetLocation(); } objectCreated("NEW_POST_CONDITION", newPostCondition); } @Override protected void undoAction(Object context) throws FlexoException { logger.info("CreateEdge: UNDO"); getNewPostCondition().delete(); if (createPreConditionForInducedEdgeConstruction != null) { createPreConditionForInducedEdgeConstruction.undoAction(); } if (createPreCondition != null) { createPreCondition.undoAction(); } } @Override protected void redoAction(Object context) throws FlexoException { logger.info("CreateEdge: REDO"); if (createPreConditionForInducedEdgeConstruction != null) { createPreConditionForInducedEdgeConstruction.redoAction(); endNodePreCondition = createPreConditionForInducedEdgeConstruction.getNewPreCondition(); } if (createPreCondition != null) { createPreCondition.redoAction(); endNodePreCondition = createPreCondition.getNewPreCondition(); } doAction(context); } private AbstractNode findEndNode() throws InvalidEdgeDefinition { if (endNode instanceof ActionNode) { return endNode; } if (endNode instanceof FlexoNode) { FlexoNode endFlexoNode = (FlexoNode) endNode; if (endFlexoNode.isBeginNode() && !(startingNode instanceof AbstractInPort)) { // This is a induced edge construction if (endFlexoNode.getAttachedPreCondition() != null) { endNodePreCondition = endFlexoNode.getAttachedPreCondition(); } else if (endFlexoNode instanceof ChildNode) { createPreConditionForInducedEdgeConstruction = CreatePreCondition.actionType.makeNewEmbeddedAction( ((ChildNode) endFlexoNode).getFather(), null, this); createPreConditionForInducedEdgeConstruction.setAttachedBeginNode(endFlexoNode); createPreConditionForInducedEdgeConstruction.doAction(); endNodePreCondition = createPreConditionForInducedEdgeConstruction.getNewPreCondition(); } else { throw new InvalidEdgeDefinition(); } } // We have to check that precondition is defined // Otherwise create it (but this is normally handled by initializer) if (endNodePreCondition == null) { createPreCondition = CreatePreCondition.actionType.makeNewEmbeddedAction((FlexoNode) endNode, null, this); createPreCondition.setAllowsToSelectPreconditionOnly(true); createPreCondition.doAction(); endNodePreCondition = createPreCondition.getNewPreCondition(); } return endNodePreCondition; } return endNode; } public AbstractNode getStartingNode() { if (startingNode == null) { return getFocusedObject(); } return startingNode; } public void setStartingNode(AbstractNode startingNode) { this.startingNode = startingNode; } public AbstractNode getEndNode() { return endNode; } public void setEndNode(AbstractNode endNode) { this.endNode = endNode; } public class InvalidEdgeDefinition extends FlexoException { protected InvalidEdgeDefinition() { super("InvalidEdgeDefinition startingNode=" + startingNode + " endNode=" + endNode, "invalid_edge_definition"); } } public class DisplayActionCannotBeBound extends FlexoException { protected DisplayActionCannotBeBound() { super("DisplayActionCannotBeBound", "display_action_can_not_be_bound"); } } public FlexoPreCondition getEndNodePreCondition() { return endNodePreCondition; } public void setEndNodePreCondition(FlexoPreCondition endNodePreCondition) { this.endNodePreCondition = endNodePreCondition; } public FlexoPostCondition getNewPostCondition() { return newPostCondition; } public Object getOutputContext() { return outputContext; } public void setOutputContext(Object outputContext) { this.outputContext = outputContext; } public String getNewEdgeName() { return newEdgeName; } public void setNewEdgeName(String newEdgeName) { this.newEdgeName = newEdgeName; } private void addMessageVariableToProcessInstance(MessageEdge pme) { // here, should insert the code to create automatically the bindings with the messages. DMEntity processInstance = pme.getProcess().getProcessDMEntity(); // TODO : wrong convention used with ports. // edge leaving a API Port if (pme.getStartNode() instanceof InOutPort) { Vector<MessageEntryBinding> inputBindings = pme.getInputMessage().getBindings(); for (MessageEntryBinding b : inputBindings) { // for every entry in the message def // add an property to the process instance MessageEntry m = b.getBindingDefinition(); String propertyName = "Service_" + m.getVariableName() + "_IN"; // String propertyName=pme.getOutputMessage().getName()+"_"+m.getVariableName()+"_IN"; DMProperty p = processInstance.createDMProperty(propertyName, m.getType(), DMPropertyImplementationType.PUBLIC_ACCESSORS_PRIVATE_FIELD); // bind the created property to the message. pme.getProject().getBindingValueConverter().setBindable(pme); b.setBindingValue(pme.getProject().getBindingValueConverter().convertFromString("processInstance." + propertyName)); } } if (pme.getEndNode() instanceof InOutPort) { Vector<MessageEntryBinding> inputBindings = pme.getOutputMessage().getBindings(); for (MessageEntryBinding b : inputBindings) { // for every entry in the message def // add an property to the process instance MessageEntry m = b.getBindingDefinition(); String propertyName = "Service_" + m.getVariableName() + "_OUT"; // String propertyName=pme.getOutputMessage().getName()+"_"+m.getVariableName()+"_IN"; DMProperty p = processInstance.createDMProperty(propertyName, m.getType(), DMPropertyImplementationType.PUBLIC_ACCESSORS_PRIVATE_FIELD); // bind the created property to the message. pme.getProject().getBindingValueConverter().setBindable(pme); b.setBindingValue(pme.getProject().getBindingValueConverter().convertFromString("processInstance." + propertyName)); } } // edge ending in a WS port -> create a message for input if (pme.getEndNode() instanceof FlexoPortMap && pme.getInputMessage() != null) { Vector<MessageEntryBinding> inputBindings = pme.getInputMessage().getBindings(); for (MessageEntryBinding b : inputBindings) { // for every entry in the message def // add an property to the process instance MessageEntry m = b.getBindingDefinition(); String propertyName = ((FlexoPortMap) pme.getEndNode()).getSubProcessNode().getName() + "_" + m.getVariableName() + "_IN"; // String propertyName=pme.getOutputMessage().getName()+"_"+m.getVariableName()+"_IN"; DMProperty p = processInstance.createDMProperty(propertyName, m.getType(), DMPropertyImplementationType.PUBLIC_ACCESSORS_PRIVATE_FIELD); // bind the created property to the message. pme.getProject().getBindingValueConverter().setBindable(pme); b.setBindingValue(pme.getProject().getBindingValueConverter().convertFromString("processInstance." + propertyName)); } } // edge starting is a WSPort -> create a message for output if (pme.getStartNode() instanceof FlexoPortMap && pme.getOutputMessage() != null) { Vector<MessageEntryBinding> outputBindings = pme.getOutputMessage().getBindings(); for (MessageEntryBinding b : outputBindings) { // for every entry in the message def // add an property to the process instance MessageEntry m = b.getBindingDefinition(); String propertyName = ((FlexoPortMap) pme.getStartNode()).getSubProcessNode().getName() + "_" + m.getVariableName() + "_OUT"; DMProperty p = processInstance.createDMProperty(propertyName, m.getType(), DMPropertyImplementationType.PUBLIC_ACCESSORS_PRIVATE_FIELD); // bind the created property to the message. pme.getProject().getBindingValueConverter().setBindable(pme); b.setBindingValue(pme.getProject().getBindingValueConverter().convertFromString("processInstance." + propertyName)); } } } public boolean getIsGenericOutput() { return isGenericOutput; } public void setIsGenericOutput(boolean isGenericOutput) { this.isGenericOutput = isGenericOutput; } }