blob: 0fb87a45a4e78efb1c0bbcd9d0906f20cf50c3a6 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.t2.workbench.models.graph;
import static javax.swing.JOptionPane.PLAIN_MESSAGE;
import static javax.swing.JOptionPane.showInputDialog;
import static javax.swing.JOptionPane.showMessageDialog;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.sf.taverna.t2.lang.observer.Observable;
import net.sf.taverna.t2.lang.observer.Observer;
import net.sf.taverna.t2.ui.menu.MenuManager;
import net.sf.taverna.t2.workbench.configuration.colour.ColourManager;
import net.sf.taverna.t2.workbench.edits.CompoundEdit;
import net.sf.taverna.t2.workbench.edits.Edit;
import net.sf.taverna.t2.workbench.edits.EditException;
import net.sf.taverna.t2.workbench.edits.EditManager;
import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment;
import net.sf.taverna.t2.workbench.models.graph.GraphEdge.ArrowStyle;
import net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle;
import net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape;
import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel;
import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage;
import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit;
import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit;
import org.apache.log4j.Logger;
import uk.org.taverna.scufl2.api.activity.Activity;
import uk.org.taverna.scufl2.api.common.NamedSet;
import uk.org.taverna.scufl2.api.common.Scufl2Tools;
import uk.org.taverna.scufl2.api.common.WorkflowBean;
import uk.org.taverna.scufl2.api.core.BlockingControlLink;
import uk.org.taverna.scufl2.api.core.ControlLink;
import uk.org.taverna.scufl2.api.core.DataLink;
import uk.org.taverna.scufl2.api.core.Processor;
import uk.org.taverna.scufl2.api.core.Workflow;
import uk.org.taverna.scufl2.api.port.InputActivityPort;
import uk.org.taverna.scufl2.api.port.InputPort;
import uk.org.taverna.scufl2.api.port.InputProcessorPort;
import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
import uk.org.taverna.scufl2.api.port.OutputActivityPort;
import uk.org.taverna.scufl2.api.port.OutputPort;
import uk.org.taverna.scufl2.api.port.OutputProcessorPort;
import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
import uk.org.taverna.scufl2.api.port.Port;
import uk.org.taverna.scufl2.api.port.ProcessorPort;
import uk.org.taverna.scufl2.api.port.ReceiverPort;
import uk.org.taverna.scufl2.api.port.SenderPort;
import uk.org.taverna.scufl2.api.port.WorkflowPort;
import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
import uk.org.taverna.scufl2.api.profiles.Profile;
/**
* @author David Withers
*/
public abstract class GraphController implements
Observer<DataflowSelectionMessage> {
public enum PortStyle {
ALL {
@Override
Shape inputShape() {
return Shape.INVHOUSE;
}
@Override
Shape outputShape() {
return Shape.HOUSE;
}
@Override
Shape processorShape() {
return Shape.RECORD;
}
},
BOUND {
@Override
Shape inputShape() {
return Shape.INVHOUSE;
}
@Override
Shape outputShape() {
return Shape.HOUSE;
}
@Override
Shape processorShape() {
return Shape.RECORD;
}
},
NONE {
@Override
Shape inputShape() {
return Shape.BOX;
}
@Override
Shape outputShape() {
return Shape.BOX;
}
@Override
Shape processorShape() {
return Shape.BOX;
}
},
BLOB {
@Override
Shape inputShape() {
return Shape.CIRCLE;
}
@Override
Shape outputShape() {
return Shape.CIRCLE;
}
@Override
Shape processorShape() {
return Shape.CIRCLE;
}
};
abstract Shape inputShape();
abstract Shape outputShape();
abstract Shape processorShape();
Shape mergeShape() {
return Shape.CIRCLE;
}
}
private static Logger logger = Logger.getLogger(GraphController.class);
private Map<String, GraphElement> idToElement = new HashMap<>();
private Map<WorkflowBean, GraphElement> workflowToGraph = new HashMap<>();
private Map<Port, GraphNode> ports = new HashMap<>();
private Map<Graph, GraphNode> inputControls = new HashMap<>();
private Map<Graph, GraphNode> outputControls = new HashMap<>();
private Map<Port, Port> nestedWorkflowPorts = new HashMap<>();
private Map<WorkflowPort, ProcessorPort> workflowPortToProcessorPort = new HashMap<>();
private Map<Port, Processor> portToProcessor = new HashMap<>();
private EditManager editManager;
private final Workflow workflow;
private final Profile profile;
private DataflowSelectionModel dataflowSelectionModel;
private GraphEventManager graphEventManager;
private Component componentForPopups;
// graph settings
private PortStyle portStyle = PortStyle.NONE;
private Map<Processor, PortStyle> processorPortStyle = new HashMap<>();
private Alignment alignment = Alignment.VERTICAL;
private boolean expandNestedDataflows = true;
private Map<Activity, Boolean> dataflowExpansion = new HashMap<>();
protected Map<String, GraphElement> graphElementMap = new HashMap<>();
protected GraphElement edgeCreationSource, edgeCreationSink;
protected GraphEdge edgeMoveElement;
protected boolean edgeCreationFromSource = false;
protected boolean edgeCreationFromSink = false;
private Graph graph;
private boolean interactive;
private final ColourManager colourManager;
private Scufl2Tools scufl2Tools = new Scufl2Tools();
public GraphController(Workflow workflow, Profile profile,
boolean interactive, Component componentForPopups,
EditManager editManager, MenuManager menuManager,
ColourManager colourManager) {
this(workflow, profile, interactive, componentForPopups,
Alignment.VERTICAL, PortStyle.NONE, editManager, menuManager,
colourManager);
}
public GraphController(Workflow workflow, Profile profile,
boolean interactive, Component componentForPopups,
Alignment alignment, PortStyle portStyle, EditManager editManager,
MenuManager menuManager, ColourManager colourManager) {
this.workflow = workflow;
this.profile = profile;
this.interactive = interactive;
this.componentForPopups = componentForPopups;
this.alignment = alignment;
this.portStyle = portStyle;
this.editManager = editManager;
this.colourManager = colourManager;
this.graphEventManager = new DefaultGraphEventManager(this,
componentForPopups, menuManager);
graph = generateGraph();
}
public abstract Graph createGraph();
public abstract GraphNode createGraphNode();
public abstract GraphEdge createGraphEdge();
public void mapElement(String id, GraphElement element) {
idToElement.put(id, element);
}
public GraphElement getElement(String id) {
return idToElement.get(id);
}
public Graph getGraph() {
return graph;
}
public abstract void redraw();
/**
* Generates a graph model of a dataflow.
*
* @return
*/
public Graph generateGraph() {
workflowToGraph.clear();
ports.clear();
inputControls.clear();
outputControls.clear();
nestedWorkflowPorts.clear();
workflowPortToProcessorPort.clear();
graphElementMap.clear();
portToProcessor.clear();
return generateGraph(workflow, "", workflow.getName(), 0);
}
private Graph generateGraph(Workflow dataflow, String prefix, String name,
int depth) {
Graph graph = createGraph();
graph.setId(prefix + name);
graph.setAlignment(getAlignment());
if (getPortStyle().equals(PortStyle.BLOB) || depth == 0)
graph.setLabel("");
else
graph.setLabel(name);
graph.setFillColor(GraphColorManager.getSubGraphFillColor(depth));
if (depth == 0)
graph.setLineStyle(LineStyle.NONE);
else
graph.setLineStyle(LineStyle.SOLID);
graph.setColor(Color.BLACK);
graph.setShape(Shape.BOX);
if (depth == 0)
graph.setWorkflowBean(dataflow);
if (interactive)
graph.setWorkflowBean(dataflow);
// processors
for (Processor processor : dataflow.getProcessors())
graph.addNode(generateProcessorNode(processor, graph.getId(), depth));
// dataflow outputs
NamedSet<OutputWorkflowPort> outputPorts = dataflow.getOutputPorts();
if (outputPorts.size() > 0 || depth > 0)
graph.addSubgraph(generateOutputsGraph(outputPorts, graph.getId(),
graph, depth));
// dataflow inputs
NamedSet<InputWorkflowPort> inputPorts = dataflow.getInputPorts();
if (inputPorts.size() > 0 || depth > 0)
graph.addSubgraph(generateInputsGraph(inputPorts, graph.getId(),
graph, depth));
// datalinks
for (DataLink datalink : dataflow.getDataLinks()) {
GraphEdge edge = generateDataLinkEdge(datalink, depth);
if (edge != null)
graph.addEdge(edge);
}
// controlLinks
for (ControlLink controlLink : dataflow.getControlLinks())
if (controlLink instanceof BlockingControlLink) {
GraphEdge edge = generateControlLinkEdge(
(BlockingControlLink) controlLink, depth);
if (edge != null)
graph.addEdge(edge);
}
graphElementMap.put(graph.getId(), graph);
return graph;
}
public void transformGraph(Graph oldGraph, Graph newGraph) {
oldGraph.setAlignment(newGraph.getAlignment());
transformGraphElement(oldGraph, newGraph);
List<GraphEdge> oldEdges = new ArrayList<>(oldGraph.getEdges());
List<GraphEdge> newEdges = new ArrayList<>(newGraph.getEdges());
for (GraphEdge oldEdge : oldEdges) {
int index = newEdges.indexOf(oldEdge);
if (index >= 0) {
GraphEdge newEdge = newEdges.remove(index);
oldEdge.setPath(newEdge.getPath());
workflowToGraph.put(oldEdge.getWorkflowBean(), oldEdge);
} else
oldGraph.removeEdge(oldEdge);
}
List<GraphNode> newNodes = new ArrayList<>(newGraph.getNodes());
List<GraphNode> oldNodes = new ArrayList<>(oldGraph.getNodes());
for (GraphNode oldNode : oldNodes) {
int index = newNodes.indexOf(oldNode);
if (index >= 0) {
GraphNode newNode = newNodes.remove(index);
oldNode.setExpanded(newNode.isExpanded());
List<GraphNode> newSourceNodes = new ArrayList<>(
newNode.getSourceNodes());
List<GraphNode> oldSourceNodes = new ArrayList<>(
oldNode.getSourceNodes());
for (GraphNode oldSourceNode : oldSourceNodes) {
int sourceNodeIndex = newSourceNodes.indexOf(oldSourceNode);
if (sourceNodeIndex >= 0) {
GraphNode newSourceNode = newSourceNodes
.remove(sourceNodeIndex);
transformGraphElement(oldSourceNode, newSourceNode);
} else
oldNode.removeSourceNode(oldSourceNode);
}
for (GraphNode sourceNode : newSourceNodes)
oldNode.addSourceNode(sourceNode);
List<GraphNode> newSinkNodes = new ArrayList<>(
newNode.getSinkNodes());
List<GraphNode> oldSinkNodes = new ArrayList<>(
oldNode.getSinkNodes());
for (GraphNode oldSinkNode : oldSinkNodes) {
int sinkNodeIndex = newSinkNodes.indexOf(oldSinkNode);
if (sinkNodeIndex >= 0) {
GraphNode newSinkNode = newSinkNodes
.remove(sinkNodeIndex);
transformGraphElement(oldSinkNode, newSinkNode);
} else
oldNode.removeSinkNode(oldSinkNode);
}
for (GraphNode sinkNode : newSinkNodes)
oldNode.addSinkNode(sinkNode);
Graph oldSubGraph = oldNode.getGraph();
Graph newSubGraph = newNode.getGraph();
if (oldSubGraph != null && newSubGraph != null)
transformGraph(oldSubGraph, newSubGraph);
transformGraphElement(oldNode, newNode);
} else
oldGraph.removeNode(oldNode);
}
List<Graph> newSubGraphs = new ArrayList<>(newGraph.getSubgraphs());
List<Graph> oldSubGraphs = new ArrayList<>(oldGraph.getSubgraphs());
for (Graph oldSubGraph : oldSubGraphs) {
int index = newSubGraphs.indexOf(oldSubGraph);
if (index >= 0) {
Graph newSubGraph = newSubGraphs.remove(index);
transformGraph(oldSubGraph, newSubGraph);
} else
oldGraph.removeSubgraph(oldSubGraph);
}
for (GraphNode node : newNodes)
oldGraph.addNode(node);
for (Graph graph : newSubGraphs)
oldGraph.addSubgraph(graph);
for (GraphEdge newEdge : newEdges)
oldGraph.addEdge(newEdge);
}
public void transformGraphElement(GraphShapeElement oldGraphElement,
GraphShapeElement newGraphElement) {
oldGraphElement.setWorkflowBean(newGraphElement.getWorkflowBean());
oldGraphElement.setShape(newGraphElement.getShape());
oldGraphElement.setSize(newGraphElement.getSize());
oldGraphElement.setPosition(newGraphElement.getPosition());
oldGraphElement.setLabel(newGraphElement.getLabel());
oldGraphElement.setLabelPosition(newGraphElement.getLabelPosition());
oldGraphElement.setLineStyle(newGraphElement.getLineStyle());
oldGraphElement.setOpacity(newGraphElement.getOpacity());
oldGraphElement.setVisible(newGraphElement.isVisible());
oldGraphElement.setColor(newGraphElement.getColor());
oldGraphElement.setFillColor(newGraphElement.getFillColor());
workflowToGraph.put(oldGraphElement.getWorkflowBean(), oldGraphElement);
}
public void filterGraph(Set<?> dataflowEntities) {
Set<GraphElement> graphElements = new HashSet<>();
for (Entry<WorkflowBean, GraphElement> entry : workflowToGraph
.entrySet())
if (!dataflowEntities.contains(entry.getKey()))
graphElements.add(entry.getValue());
filterGraph(getGraph(), graphElements);
}
private void filterGraph(Graph graph, Set<GraphElement> graphElements) {
for (GraphNode node : graph.getNodes()) {
node.setFiltered(graphElements.contains(node));
Graph subgraph = node.getGraph();
if (subgraph != null)
if (graphElements.contains(subgraph)) {
removeFilter(subgraph);
subgraph.setFiltered(true);
} else {
subgraph.setFiltered(false);
filterGraph(subgraph, graphElements);
}
}
for (GraphEdge edge : graph.getEdges())
edge.setFiltered(graphElements.contains(edge));
for (Graph subgraph : graph.getSubgraphs())
if (graphElements.contains(subgraph)) {
removeFilter(subgraph);
subgraph.setFiltered(true);
} else {
subgraph.setFiltered(false);
filterGraph(subgraph, graphElements);
}
}
public void removeFilter() {
for (Entry<WorkflowBean, GraphElement> entry : workflowToGraph
.entrySet())
entry.getValue().setFiltered(false);
}
private void removeFilter(Graph graph) {
for (GraphNode node : graph.getNodes()) {
node.setOpacity(1f);
Graph subgraph = node.getGraph();
if (subgraph != null) {
subgraph.setFiltered(false);
removeFilter(subgraph);
}
}
for (GraphEdge edge : graph.getEdges())
edge.setFiltered(false);
for (Graph subgraph : graph.getSubgraphs()) {
subgraph.setFiltered(false);
removeFilter(subgraph);
}
}
private GraphEdge generateControlLinkEdge(BlockingControlLink condition,
int depth) {
GraphEdge edge = null;
GraphElement source = workflowToGraph.get(condition.getUntilFinished());
GraphElement sink = workflowToGraph.get(condition.getBlock());
if (source != null && sink != null) {
edge = createGraphEdge();
if (source instanceof Graph)
edge.setSource(outputControls.get(source));
else if (source instanceof GraphNode)
edge.setSource((GraphNode) source);
if (sink instanceof Graph)
edge.setSink(inputControls.get(sink));
else if (sink instanceof GraphNode)
edge.setSink((GraphNode) sink);
String sourceId = edge.getSource().getId();
String sinkId = edge.getSink().getId();
edge.setId(sourceId + "->" + sinkId);
edge.setLineStyle(LineStyle.SOLID);
edge.setColor(Color.decode("#505050"));
edge.setFillColor(null);
edge.setArrowHeadStyle(ArrowStyle.DOT);
if (depth == 0)
edge.setWorkflowBean(condition);
if (interactive)
edge.setWorkflowBean(condition);
workflowToGraph.put(condition, edge);
graphElementMap.put(edge.getId(), edge);
}
return edge;
}
private GraphEdge generateDataLinkEdge(DataLink datalink, int depth) {
GraphEdge edge = null;
Port sourcePort = datalink.getReceivesFrom();
Port sinkPort = datalink.getSendsTo();
if (nestedWorkflowPorts.containsKey(sourcePort))
sourcePort = nestedWorkflowPorts.get(sourcePort);
if (nestedWorkflowPorts.containsKey(sinkPort))
sinkPort = nestedWorkflowPorts.get(sinkPort);
GraphNode sourceNode = ports.get(sourcePort);
GraphNode sinkNode = ports.get(sinkPort);
if (sourceNode != null && sinkNode != null) {
edge = createGraphEdge();
edge.setSource(sourceNode);
edge.setSink(sinkNode);
StringBuilder id = new StringBuilder();
if (sourceNode.getParent() instanceof GraphNode) {
id.append(sourceNode.getParent().getId());
id.append(":");
id.append(sourceNode.getId());
} else
id.append(sourceNode.getId());
id.append("->");
if (sinkNode.getParent() instanceof GraphNode) {
id.append(sinkNode.getParent().getId());
id.append(":");
id.append(sinkNode.getId());
} else
id.append(sinkNode.getId());
edge.setId(id.toString());
edge.setLineStyle(LineStyle.SOLID);
edge.setColor(Color.BLACK);
edge.setFillColor(Color.BLACK);
if (depth == 0)
edge.setWorkflowBean(datalink);
if (interactive)
edge.setWorkflowBean(datalink);
workflowToGraph.put(datalink, edge);
graphElementMap.put(edge.getId(), edge);
}
return edge;
}
private Graph generateInputsGraph(NamedSet<InputWorkflowPort> inputPorts,
String prefix, Graph graph, int depth) {
Graph inputs = createGraph();
inputs.setId(prefix + "sources");
inputs.setColor(Color.BLACK);
inputs.setFillColor(null);
inputs.setShape(Shape.BOX);
inputs.setLineStyle(LineStyle.DOTTED);
if (getPortStyle().equals(PortStyle.BLOB))
inputs.setLabel("");
else
inputs.setLabel("Workflow input ports");
GraphNode triangle = createGraphNode();
triangle.setId(prefix + "WORKFLOWINTERNALSOURCECONTROL");
triangle.setLabel("");
triangle.setShape(Shape.TRIANGLE);
triangle.setSize(new Dimension((int) (0.2f * 72), (int) ((Math.sin(Math
.toRadians(60)) * 0.2) * 72)));
triangle.setFillColor(Color.decode("#ff4040"));
triangle.setColor(Color.BLACK);
triangle.setLineStyle(LineStyle.SOLID);
inputs.addNode(triangle);
inputControls.put(graph, triangle);
for (InputWorkflowPort inputWorkflowPort : inputPorts) {
GraphNode inputNode = createGraphNode();
inputNode.setId(prefix + "WORKFLOWINTERNALSOURCE_"
+ inputWorkflowPort.getName());
if (getPortStyle().equals(PortStyle.BLOB)) {
inputNode.setLabel("");
inputNode.setSize(new Dimension((int) (0.3f * 72),
(int) (0.3f * 72)));
} else
inputNode.setLabel(inputWorkflowPort.getName());
inputNode.setShape(getPortStyle().inputShape());
inputNode.setColor(Color.BLACK);
inputNode.setLineStyle(LineStyle.SOLID);
inputNode.setFillColor(Color.decode("#8ed6f0"));
if (depth == 0)
inputNode.setInteractive(true);
if (interactive)
inputNode.setInteractive(true);
if (depth < 2) {
inputNode.setWorkflowBean(inputWorkflowPort);
if (workflowPortToProcessorPort.containsKey(inputWorkflowPort)) {
ProcessorPort port = workflowPortToProcessorPort
.get(inputWorkflowPort);
inputNode.setWorkflowBean(port);
workflowToGraph.put(port, inputNode);
} else {
inputNode.setWorkflowBean(inputWorkflowPort);
workflowToGraph.put(inputWorkflowPort, inputNode);
}
}
ports.put(inputWorkflowPort, inputNode);
inputs.addNode(inputNode);
graphElementMap.put(inputNode.getId(), inputNode);
}
return inputs;
}
private Graph generateOutputsGraph(
NamedSet<OutputWorkflowPort> outputPorts, String prefix,
Graph graph, int depth) {
Graph outputs = createGraph();
outputs.setId(prefix + "sinks");
outputs.setColor(Color.BLACK);
outputs.setFillColor(null);
outputs.setShape(Shape.BOX);
outputs.setLineStyle(LineStyle.DOTTED);
if (getPortStyle().equals(PortStyle.BLOB))
outputs.setLabel("");
else
outputs.setLabel("Workflow output ports");
GraphNode triangle = createGraphNode();
triangle.setId(prefix + "WORKFLOWINTERNALSINKCONTROL");
triangle.setLabel("");
triangle.setShape(Shape.INVTRIANGLE);
triangle.setSize(new Dimension((int) (0.2f * 72), (int) ((Math.sin(Math
.toRadians(60)) * 0.2) * 72)));
triangle.setFillColor(Color.decode("#66cd00"));
triangle.setColor(Color.BLACK);
triangle.setLineStyle(LineStyle.SOLID);
outputs.addNode(triangle);
outputControls.put(graph, triangle);
for (OutputWorkflowPort outputWorkflowPort : outputPorts) {
GraphNode outputNode = createGraphNode();
outputNode.setId(prefix + "WORKFLOWINTERNALSINK_"
+ outputWorkflowPort.getName());
if (getPortStyle().equals(PortStyle.BLOB)) {
outputNode.setLabel("");
outputNode.setSize(new Dimension((int) (0.3f * 72),
(int) (0.3f * 72)));
} else
outputNode.setLabel(outputWorkflowPort.getName());
outputNode.setShape(getPortStyle().outputShape());
outputNode.setColor(Color.BLACK);
outputNode.setLineStyle(LineStyle.SOLID);
outputNode.setFillColor(Color.decode("#8ed6f0"));
if (depth == 0)
outputNode.setInteractive(true);
if (interactive)
outputNode.setInteractive(true);
if (depth < 2) {
if (workflowPortToProcessorPort.containsKey(outputWorkflowPort)) {
ProcessorPort port = workflowPortToProcessorPort
.get(outputWorkflowPort);
outputNode.setWorkflowBean(port);
workflowToGraph.put(port, outputNode);
} else {
outputNode.setWorkflowBean(outputWorkflowPort);
workflowToGraph.put(outputWorkflowPort, outputNode);
}
}
ports.put(outputWorkflowPort, outputNode);
outputs.addNode(outputNode);
graphElementMap.put(outputNode.getId(), outputNode);
}
return outputs;
}
private GraphNode generateProcessorNode(Processor processor, String prefix,
int depth) {
// Blatantly ignoring any other activities for now
ProcessorBinding processorBinding = scufl2Tools
.processorBindingForProcessor(processor, profile);
Activity activity = processorBinding.getBoundActivity();
@SuppressWarnings("unused")
URI activityType = activity.getType();
GraphNode node = createGraphNode();
node.setId(prefix + processor.getName());
if (getPortStyle().equals(PortStyle.BLOB)) {
node.setLabel("");
node.setSize(new Dimension((int) (0.3f * 72), (int) (0.3f * 72)));
} else
node.setLabel(processor.getName());
node.setShape(getPortStyle(processor).processorShape());
node.setColor(Color.BLACK);
node.setLineStyle(LineStyle.SOLID);
// if (activityType.equals(URI.create(NonExecutableActivity.URI))) {
// if (activityType.equals(URI.create(DisabledActivity.URI))) {
// node.setFillColor(GraphColorManager
// .getFillColor(((DisabledActivity) activity)
// .getActivity(), colourManager));
// } else {
// node.setFillColor(GraphColorManager
// .getFillColor(activityType, colourManager));
// }
// node.setOpacity(0.3f);
// } else
node.setFillColor(GraphColorManager.getFillColor(activity,
colourManager));
// check whether the nested workflow processors should be clickable or
// not, if top level workflow then should be clickable regardless
if (depth == 0) {
node.setInteractive(true);
node.setWorkflowBean(processor);
}
if (interactive) {
node.setInteractive(true);
node.setWorkflowBean(processor);
}
if (scufl2Tools.containsNestedWorkflow(processor, profile)
&& expandNestedDataflow(activity)) {
Workflow subDataflow = scufl2Tools.nestedWorkflowForProcessor(
processor, profile);
NamedSet<InputWorkflowPort> inputWorkflowPorts = subDataflow
.getInputPorts();
for (InputActivityPort inputActivityPort : activity.getInputPorts()) {
InputWorkflowPort inputWorkflowPort = inputWorkflowPorts
.getByName(inputActivityPort.getName());
InputProcessorPort inputProcessorPort = scufl2Tools
.processorPortBindingForPort(inputActivityPort, profile)
.getBoundProcessorPort();
nestedWorkflowPorts.put(inputProcessorPort, inputWorkflowPort);
workflowPortToProcessorPort.put(inputWorkflowPort,
inputProcessorPort);
processorBinding.getInputPortBindings();
}
NamedSet<OutputWorkflowPort> outputWorkflowPorts = subDataflow
.getOutputPorts();
for (OutputActivityPort outputActivityPort : activity
.getOutputPorts()) {
OutputWorkflowPort outputWorkflowPort = outputWorkflowPorts
.getByName(outputActivityPort.getName());
OutputProcessorPort outputProcessorPort = scufl2Tools
.processorPortBindingForPort(outputActivityPort,
profile).getBoundProcessorPort();
nestedWorkflowPorts
.put(outputProcessorPort, outputWorkflowPort);
workflowPortToProcessorPort.put(outputWorkflowPort,
outputProcessorPort);
}
Graph subGraph = generateGraph(subDataflow, prefix,
processor.getName(), depth + 1);
// TODO why does this depth matter?
if (depth == 0)
subGraph.setWorkflowBean(processor);
if (interactive)
subGraph.setWorkflowBean(processor);
node.setGraph(subGraph);
node.setExpanded(true);
workflowToGraph.put(processor, subGraph);
} else {
graphElementMap.put(node.getId(), node);
workflowToGraph.put(processor, node);
}
NamedSet<InputProcessorPort> inputPorts = processor.getInputPorts();
if (inputPorts.size() == 0) {
GraphNode portNode = createGraphNode();
portNode.setShape(Shape.BOX);
portNode.setColor(Color.BLACK);
portNode.setFillColor(node.getFillColor());
portNode.setLineStyle(LineStyle.SOLID);
node.addSinkNode(portNode);
} else
for (InputPort inputPort : inputPorts) {
GraphNode portNode = createGraphNode();
portNode.setId("i" + inputPort.getName().replaceAll("\\.", ""));
portNode.setLabel(inputPort.getName());
portNode.setShape(Shape.BOX);
portNode.setColor(Color.BLACK);
portNode.setFillColor(node.getFillColor());
portNode.setLineStyle(LineStyle.SOLID);
if (depth == 0)
portNode.setWorkflowBean(inputPort);
if (interactive)
portNode.setWorkflowBean(inputPort);
if (!node.isExpanded())
workflowToGraph.put(inputPort, portNode);
ports.put(inputPort, portNode);
node.addSinkNode(portNode);
graphElementMap.put(portNode.getId(), portNode);
// portToActivity.put(inputPort, activity);
portToProcessor.put(inputPort, processor);
}
NamedSet<OutputProcessorPort> outputPorts = processor.getOutputPorts();
if (outputPorts.size() == 0) {
GraphNode portNode = createGraphNode();
portNode.setShape(Shape.BOX);
portNode.setColor(Color.BLACK);
portNode.setFillColor(node.getFillColor());
portNode.setLineStyle(LineStyle.SOLID);
node.addSourceNode(portNode);
} else
for (OutputPort outputPort : outputPorts) {
GraphNode portNode = createGraphNode();
portNode.setId("o" + outputPort.getName().replaceAll("\\.", ""));
portNode.setLabel(outputPort.getName());
portNode.setShape(Shape.BOX);
portNode.setColor(Color.BLACK);
portNode.setFillColor(node.getFillColor());
portNode.setLineStyle(LineStyle.SOLID);
if (depth == 0)
portNode.setWorkflowBean(outputPort);
if (interactive)
portNode.setWorkflowBean(outputPort);
if (!node.isExpanded())
workflowToGraph.put(outputPort, portNode);
ports.put(outputPort, portNode);
node.addSourceNode(portNode);
graphElementMap.put(portNode.getId(), portNode);
// portToActivity.put(outputPort, activity);
portToProcessor.put(outputPort, processor);
}
return node;
}
/**
* Returns the dataflow.
*
* @return the dataflow
*/
public Workflow getWorkflow() {
return workflow;
}
public Profile getProfile() {
return profile;
}
/**
* Returns the dataflowSelectionModel.
*
* @return the dataflowSelectionModel
*/
public DataflowSelectionModel getDataflowSelectionModel() {
return dataflowSelectionModel;
}
/**
* Sets the dataflowSelectionModel.
*
* @param dataflowSelectionModel
* the new dataflowSelectionModel
*/
public void setDataflowSelectionModel(
DataflowSelectionModel dataflowSelectionModel) {
if (this.dataflowSelectionModel != null)
this.dataflowSelectionModel.removeObserver(this);
this.dataflowSelectionModel = dataflowSelectionModel;
this.dataflowSelectionModel.addObserver(this);
}
/**
* Sets the proportion of the node's jobs that have been completed.
*
* @param nodeId
* the id of the node
* @param complete
* the proportion of the nodes's jobs that have been completed, a
* value between 0.0 and 1.0
*/
public void setNodeCompleted(String nodeId, float complete) {
if (graphElementMap.containsKey(nodeId)) {
GraphElement graphElement = graphElementMap.get(nodeId);
graphElement.setCompleted(complete);
}
}
public void setEdgeActive(String edgeId, boolean active) {
}
/**
* Returns the alignment.
*
* @return the alignment
*/
public Alignment getAlignment() {
return alignment;
}
/**
* Returns the portStyle.
*
* @return the portStyle
*/
public PortStyle getPortStyle() {
return portStyle;
}
/**
* Returns the portStyle for a processor.
*
* @return the portStyle for a processor
*/
public PortStyle getPortStyle(Processor processor) {
if (processorPortStyle.containsKey(processor))
return processorPortStyle.get(processor);
return portStyle;
}
/**
* Sets the alignment.
*
* @param alignment
* the new alignment
*/
public void setAlignment(Alignment alignment) {
this.alignment = alignment;
}
/**
* Sets the portStyle.
*
* @param style
* the new portStyle
*/
public void setPortStyle(PortStyle portStyle) {
this.portStyle = portStyle;
processorPortStyle.clear();
}
/**
* Sets the portStyle for a processor.
*
* @param style
* the new portStyle for the processor
*/
public void setPortStyle(Processor processor, PortStyle portStyle) {
processorPortStyle.put(processor, portStyle);
}
/**
* Shut down any processing and update threads related to this controller.
*
*/
public void shutdown() {
}
/**
* Returns true if the default is to expand nested workflows.
*
* @return true if the default is to expand nested workflows
*/
public boolean expandNestedDataflows() {
return expandNestedDataflows;
}
/**
* Returns true if the nested dataflow should be expanded.
*
* @param dataflow
* @return true if the nested dataflow should be expanded
*/
public boolean expandNestedDataflow(Activity dataflow) {
if (dataflowExpansion.containsKey(dataflow))
return dataflowExpansion.get(dataflow);
return expandNestedDataflows;
}
/**
* Sets the default for expanding nested workflows.
*
* @param expand
* the default for expanding nested workflows
*/
public void setExpandNestedDataflows(boolean expand) {
dataflowExpansion.clear();
this.expandNestedDataflows = expand;
}
/**
* Sets whether the nested dataflow should be expanded.
*
* @param expand
* whether the nested dataflow should be expanded
* @param dataflow
* the nested dataflow
*/
public void setExpandNestedDataflow(Activity dataflow, boolean expand) {
dataflowExpansion.put(dataflow, expand);
}
private boolean isSingleOutputProcessor(Object dataflowObject) {
boolean result = false;
if (dataflowObject instanceof Processor) {
Processor processor = (Processor) dataflowObject;
result = processor.getOutputPorts().size() == 1;
}
return result;
}
public boolean startEdgeCreation(GraphElement graphElement, Point point) {
if (!edgeCreationFromSource && !edgeCreationFromSink) {
Object dataflowObject = graphElement.getWorkflowBean();
if (dataflowObject instanceof ReceiverPort) {
edgeCreationSink = graphElement;
edgeCreationFromSink = true;
} else if (dataflowObject instanceof SenderPort
|| isSingleOutputProcessor(dataflowObject)) {
edgeCreationSource = graphElement;
edgeCreationFromSource = true;
} else if (graphElement instanceof GraphEdge) {
GraphEdge edge = (GraphEdge) graphElement;
edgeCreationSource = edge.getSource();
edgeCreationFromSource = true;
edgeMoveElement = edge;
}
}
return edgeCreationFromSource || edgeCreationFromSink;
}
public boolean moveEdgeCreationTarget(GraphElement graphElement, Point point) {
boolean edgeValid = false;
Object dataflowObject = graphElement.getWorkflowBean();
if (edgeCreationFromSink) {
if (graphElement instanceof GraphNode) {
Object sinkObject = edgeCreationSink.getWorkflowBean();
if (dataflowObject instanceof OutputPort) {
Processor sourceProcessor = portToProcessor
.get(dataflowObject);
if (sourceProcessor != null) {
Processor sinkProcessor = null;
if (sinkObject instanceof Processor)
sinkProcessor = (Processor) sinkObject;
else if (portToProcessor.containsKey(sinkObject))
sinkProcessor = portToProcessor.get(sinkObject);
if (sinkProcessor != null) {
Set<Processor> possibleSinkProcessors = scufl2Tools
.possibleDownStreamProcessors(workflow,
sourceProcessor);
if (possibleSinkProcessors.contains(sinkProcessor)) {
edgeCreationSource = graphElement;
edgeValid = true;
}
}
if (sinkObject instanceof OutputWorkflowPort) {
edgeCreationSource = graphElement;
edgeValid = true;
}
}
} else if (dataflowObject instanceof InputWorkflowPort) {
edgeCreationSource = graphElement;
edgeValid = true;
} else if (dataflowObject instanceof Processor) {
Processor sourceProcessor = (Processor) dataflowObject;
Processor sinkProcessor = null;
if (sinkObject instanceof Processor)
sinkProcessor = (Processor) sinkObject;
else if (portToProcessor.containsKey(sinkObject))
sinkProcessor = portToProcessor.get(sinkObject);
if (sinkProcessor != null) {
Set<Processor> possibleSinkProcessors = scufl2Tools
.possibleDownStreamProcessors(workflow,
sourceProcessor);
if (possibleSinkProcessors.contains(sinkProcessor)) {
edgeCreationSource = graphElement;
edgeValid = true;
}
}
if (sinkObject instanceof OutputWorkflowPort) {
edgeCreationSource = graphElement;
edgeValid = true;
}
}
}
if (!edgeValid)
edgeCreationSource = null;
} else if (edgeCreationFromSource) {
if (graphElement instanceof GraphNode) {
Object sourceObject = edgeCreationSource.getWorkflowBean();
if (dataflowObject instanceof InputPort) {
Processor sinkProcessor = portToProcessor
.get(dataflowObject);
if (sinkProcessor != null) {
Processor sourceProcessor = null;
if (sourceObject instanceof Processor)
sourceProcessor = (Processor) sourceObject;
else if (portToProcessor.containsKey(sourceObject))
sourceProcessor = portToProcessor.get(sourceObject);
if (sourceProcessor != null) {
Set<Processor> possibleSourceProcessors = scufl2Tools
.possibleUpStreamProcessors(workflow,
sinkProcessor);
if (possibleSourceProcessors
.contains(sourceProcessor)) {
edgeCreationSink = graphElement;
edgeValid = true;
}
}
if (sourceObject instanceof InputWorkflowPort) {
edgeCreationSink = graphElement;
edgeValid = true;
}
}
} else if (dataflowObject instanceof OutputWorkflowPort) {
if (sourceObject != null) {
edgeCreationSink = graphElement;
edgeValid = true;
}
} else if (dataflowObject instanceof Processor) {
Processor sinkProcessor = (Processor) dataflowObject;
Processor sourceProcessor = null;
if (sourceObject instanceof Processor)
sourceProcessor = (Processor) sourceObject;
else if (portToProcessor.containsKey(sourceObject))
sourceProcessor = portToProcessor.get(sourceObject);
if (sourceProcessor != null) {
Set<Processor> possibleSourceProcessors = scufl2Tools
.possibleUpStreamProcessors(workflow,
sinkProcessor);
if (possibleSourceProcessors.contains(sourceProcessor)) {
edgeCreationSink = graphElement;
edgeValid = true;
}
}
if (sourceObject instanceof InputWorkflowPort) {
edgeCreationSink = graphElement;
edgeValid = true;
}
}
}
if (!edgeValid)
edgeCreationSink = null;
}
return edgeValid;
}
public boolean stopEdgeCreation(GraphElement graphElement, Point point) {
boolean edgeCreated = false;
if (edgeCreationSource != null && edgeCreationSink != null) {
SenderPort source = null;
ReceiverPort sink = null;
Object sourceDataflowObject = edgeCreationSource.getWorkflowBean();
Object sinkDataflowObject = edgeCreationSink.getWorkflowBean();
if (sourceDataflowObject instanceof SenderPort)
source = (SenderPort) sourceDataflowObject;
else if (sourceDataflowObject instanceof Processor) {
Processor processor = (Processor) sourceDataflowObject;
source = showPortOptions(processor.getOutputPorts(), "output",
componentForPopups, point);
}
if (sinkDataflowObject instanceof ReceiverPort)
sink = (ReceiverPort) sinkDataflowObject;
else if (sinkDataflowObject instanceof Processor) {
Processor processor = (Processor) sinkDataflowObject;
sink = showPortOptions(processor.getInputPorts(), "input",
componentForPopups, point);
}
if (source != null && sink != null) {
Edit<?> edit = null;
if (edgeMoveElement == null) {
DataLink dataLink = new DataLink();
dataLink.setReceivesFrom(source);
dataLink.setSendsTo(sink);
edit = new AddDataLinkEdit(workflow, dataLink);
} else {
Object existingSink = edgeMoveElement.getSink()
.getWorkflowBean();
if (existingSink != sink) {
List<Edit<?>> editList = new ArrayList<Edit<?>>();
DataLink existingDataLink = (DataLink) edgeMoveElement
.getWorkflowBean();
DataLink newDataLink = new DataLink();
newDataLink.setReceivesFrom(existingDataLink
.getReceivesFrom());
newDataLink.setSendsTo(sink);
editList.add(new RemoveDataLinkEdit(workflow,
existingDataLink));
editList.add(new AddDataLinkEdit(workflow, newDataLink));
edit = new CompoundEdit(editList);
}
}
try {
if (edit != null) {
editManager.doDataflowEdit(workflow.getParent(), edit);
edgeCreated = true;
}
} catch (EditException e) {
logger.debug("Failed to create datalink from '"
+ source.getName() + "' to '" + sink.getName()
+ "'");
}
}
}
edgeCreationSource = null;
edgeCreationSink = null;
edgeMoveElement = null;
edgeCreationFromSource = false;
edgeCreationFromSink = false;
return edgeCreated;
}
private <T extends Port> T showPortOptions(NamedSet<T> ports,
String portType, Component component, Point point) {
T result = null;
if (ports.size() == 0) {
showMessageDialog(component, "Service has no " + portType
+ " ports to connect to");
} else if (ports.size() == 1)
result = ports.first();
else {
Object[] portNames = ports.getNames().toArray();
String portName = (String) showInputDialog(component, "Select an "
+ portType + " port", "Port Chooser", PLAIN_MESSAGE, null,
portNames, portNames[0]);
if (portName != null)
result = ports.getByName(portName);
}
return result;
}
public void resetSelection() {
if (dataflowSelectionModel != null)
for (Object dataflowElement : dataflowSelectionModel.getSelection()) {
GraphElement graphElement = workflowToGraph
.get(dataflowElement);
if (graphElement != null)
graphElement.setSelected(true);
}
}
public void setIteration(String nodeId, int iteration) {
if (graphElementMap.containsKey(nodeId)) {
GraphElement graphElement = graphElementMap.get(nodeId);
graphElement.setIteration(iteration);
}
}
public void setErrors(String nodeId, int errors) {
if (graphElementMap.containsKey(nodeId)) {
GraphElement graphElement = graphElementMap.get(nodeId);
graphElement.setErrors(errors);
}
}
@Override
public void notify(Observable<DataflowSelectionMessage> sender,
DataflowSelectionMessage message) throws Exception {
GraphElement graphElement = workflowToGraph.get(message.getElement());
if (graphElement != null)
graphElement.setSelected(message.getType().equals(
DataflowSelectionMessage.Type.ADDED));
}
/**
* Returns the GraphEventManager.
*
* @return the GraphEventManager
*/
public GraphEventManager getGraphEventManager() {
return graphEventManager;
}
/**
* Sets the GraphEventManager.
*
* @param graphEventManager
* the new GraphEventManager
*/
public void setGraphEventManager(GraphEventManager graphEventManager) {
this.graphEventManager = graphEventManager;
}
}