/*
* This file is part of Alida, a Java library for
* Advanced Library for Integrated Development of Data Analysis Applications.
*
* Copyright (C) 2010 - @YEAR@
*
* This program 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.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Fore more information on Alida, visit
*
* http://www.informatik.uni-halle.de/alida/
*
*/
package de.unihalle.informatik.Alida.grappa;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.BlockingDeque;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mxgraph.io.mxCodec;
import com.mxgraph.io.mxCodecRegistry;
import com.mxgraph.io.mxObjectCodec;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxICell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;
import com.mxgraph.util.mxXmlUtils;
import com.mxgraph.view.mxGraph;
import de.unihalle.informatik.Alida.annotations.Parameter;
import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerSwing;
import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerSwing.ProviderInteractionLevel;
import de.unihalle.informatik.Alida.dataio.provider.swing.components.ALDOperatorParameterPanel;
import de.unihalle.informatik.Alida.exceptions.ALDDataIOException;
import de.unihalle.informatik.Alida.exceptions.ALDException;
import de.unihalle.informatik.Alida.exceptions.ALDOperatorException;
import de.unihalle.informatik.Alida.exceptions.ALDWorkflowException;
import de.unihalle.informatik.Alida.gui.ALDOperatorResultFrame;
import de.unihalle.informatik.Alida.gui.ALDOperatorConfigurationFrame;
import de.unihalle.informatik.Alida.operator.ALDOpParameterDescriptor;
import de.unihalle.informatik.Alida.operator.ALDOperator;
import de.unihalle.informatik.Alida.operator.ALDOperatorLocation;
import de.unihalle.informatik.Alida.operator.events.ALDOpParameterUpdateEvent;
import de.unihalle.informatik.Alida.operator.events.ALDOpParameterUpdateEventListener;
import de.unihalle.informatik.Alida.operator.events.ALDOpParameterUpdateEvent.EventType;
import de.unihalle.informatik.Alida.workflows.ALDWorkflow;
import de.unihalle.informatik.Alida.workflows.ALDWorkflow.ALDWorkflowContextType;
import de.unihalle.informatik.Alida.workflows.ALDWorkflowEdge;
import de.unihalle.informatik.Alida.workflows.ALDWorkflowID;
import de.unihalle.informatik.Alida.workflows.ALDWorkflowEdgeID;
import de.unihalle.informatik.Alida.workflows.ALDWorkflowNode;
import de.unihalle.informatik.Alida.workflows.ALDWorkflowNode.ALDWorkflowNodeState;
import de.unihalle.informatik.Alida.workflows.ALDWorkflowNodeID;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEvent;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowStorageInfo;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEvent.*;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEventListener;
/**
* Grappa workflow graph component.
*
* @author Birgit Moeller
*/
public class ALDGrappaWorkbenchTab extends mxGraphComponent
implements ALDWorkflowEventListener {
/* ******************
* Internal members.
* ******************/
/**
* Slf4j logger.
*/
private static Logger logger =
LoggerFactory.getLogger(ALDOperatorParameterPanel.class);
/**
* Reference to the surrounding work bench object.
*/
protected ALDGrappaWorkbench workBench;
/**
* Reference to the underlying Alida workflow object.
*/
protected ALDWorkflow alidaWorkflow;
/**
* Map of node cells and their corresponding Alida workflow IDs.
*/
protected HashMap<mxCell, ALDWorkflowNodeID> graphNodeIDs;
/**
* Map of Alida workflow IDs and their corresponding node cells.
*/
protected HashMap<ALDWorkflowNodeID, mxCell> graphNodes;
/**
* Map of edge cells and their corresponding Alida workflow IDs.
*/
protected HashMap<mxCell, ALDWorkflowEdgeID> graphEdgeIDs;
/**
* Map of Alida workflow IDs and their corresponding edge cells.
*/
protected HashMap<ALDWorkflowEdgeID, mxCell> graphEdges;
/**
* Map of node cells and their corresponding configuration windows.
*/
protected HashMap<mxCell, ALDOperatorConfigurationFrame> configWindows;
/**
* Map of window configurations for parameter display.
* <p>
* This map stores for each node of the workflow the setting of the
* boolean option for displaying all or only the non-expert parameters.
* If the value of an entry is true, all parameters are to be shown,
* if it is false, only standard parameters are visible.
* <p>
* The values stored in this hashmap are mainly used during the
* creation of menus of type {@link ContextMenuNodeEdge}.
*/
protected HashMap<mxCell, Boolean> nodeConfigShowAllParameters;
/**
* Map of flags for immediately showing node results.
* <p>
* The values stored in this hashmap are updated by
* menus of type {@link ContextMenuNodeEdge}.
*/
protected HashMap<mxCell, Boolean> nodeConfigShowResultsAtOnce;
/**
* Title of this workflow as shown on the tab.
*/
protected String workflowTitle;
/**
* Flag to enable/disable automatic pop-up of results at terminal nodes.
*/
protected boolean popupFinalResults = true;
/**
* Map to count nodes of an operator type and allow for unique labels.
*/
private HashMap<String, Integer> nodeNameCountMap = new HashMap<>();
/**
* List of actions performed on the Alida workflow object.
*/
protected HashMap<ALDWorkflowID, WorkflowModifyAction> actionsOnWorkflow;
/**
* Message window shown while workflow execution is aborted.
*/
protected JFrame interruptWin;
/**
* for debug message to stdout
*/
protected boolean debug = false;
/**
* Default constructor.
* @param bench Workbench to which this tab is to be attached.
* @param _graph Workbench graph associated with this workflow.
*/
public ALDGrappaWorkbenchTab(ALDGrappaWorkbench bench,
ALDGrappaWorkbenchGraph _graph) {
super(_graph);
this.actionsOnWorkflow = new HashMap<ALDWorkflowID,
ALDGrappaWorkbenchTab.WorkflowModifyAction>();
this.graphNodeIDs = new HashMap<mxCell, ALDWorkflowNodeID>();
this.graphNodes = new HashMap<ALDWorkflowNodeID, mxCell>();
this.graphEdgeIDs = new HashMap<mxCell, ALDWorkflowEdgeID>();
this.graphEdges = new HashMap<ALDWorkflowEdgeID, mxCell>();
this.configWindows =
new HashMap<mxCell, ALDOperatorConfigurationFrame>();
this.nodeConfigShowAllParameters = new HashMap<mxCell, Boolean>();
this.nodeConfigShowResultsAtOnce = new HashMap<mxCell, Boolean>();
this.workBench = bench;
try {
this.alidaWorkflow = new ALDWorkflow(ALDWorkflowContextType.GRAPPA);
this.alidaWorkflow.addALDWorkflowEventListener(this);
} catch (ALDOperatorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.workflowTitle = this.alidaWorkflow.getName();
this.graph.setMinimumGraphSize(new mxRectangle(0, 0, 500, 500));
this.graph.setAllowDanglingEdges(false);
this.graph.setCellsEditable(false);
// add reference to workbench tab for handling tooltips
((ALDGrappaWorkbenchGraph)this.graph).setWorkbenchTab(this);
// some additional initializations
this.initInterruptWin();
// add event listeners
this.addKeyListener(new GraphKeyListener());
this.getGraphControl().addMouseListener(new GraphMouseAdapter());
GraphEventListener gEListener = new GraphEventListener();
this.graph.addListener(mxEvent.CONNECT_CELL, gEListener);
this.getConnectionHandler().addListener(mxEvent.CONNECT, gEListener);
this.getConnectionHandler().addListener(mxEvent.CONNECT_CELL, gEListener);
}
/**
* Constructor to setup tab from given (reloaded) workflow.
* @param bench Workbench to which this tab is to be attached.
* @param _graph Workbench graph associated with this workflow.
* @param _flow Alida workflow associated with this tab.
*/
public ALDGrappaWorkbenchTab(ALDGrappaWorkbench bench,
mxGraph _graph, ALDWorkflow _flow) {
super(_graph);
this.actionsOnWorkflow = new HashMap<ALDWorkflowID,
ALDGrappaWorkbenchTab.WorkflowModifyAction>();
this.graphNodeIDs = new HashMap<mxCell, ALDWorkflowNodeID>();
this.graphNodes = new HashMap<ALDWorkflowNodeID, mxCell>();
this.graphEdgeIDs = new HashMap<mxCell, ALDWorkflowEdgeID>();
this.graphEdges = new HashMap<ALDWorkflowEdgeID, mxCell>();
this.configWindows =
new HashMap<mxCell, ALDOperatorConfigurationFrame>();
this.nodeConfigShowAllParameters = new HashMap<mxCell, Boolean>();
this.nodeConfigShowResultsAtOnce = new HashMap<mxCell, Boolean>();
this.workBench = bench;
this.workflowTitle = _flow.getName();
this.alidaWorkflow = _flow;
this.alidaWorkflow.addALDWorkflowEventListener(this);
// make sure that node ids are properly restored
this.restoreIDs();
// generate configuration windows
this.restoreConfigWins();
// make sure that all nodes and all wins show correct configurations
this.updateWorkflowNodeStates();
// properly initialize node name count map
this.initNodeNameCountMap();
this.graph.setMinimumGraphSize(new mxRectangle(0, 0, 500, 500));
this.graph.setAllowDanglingEdges(false);
this.graph.setCellsEditable(false);
// some additional initializations
this.initInterruptWin();
// add event listeners
this.addKeyListener(new GraphKeyListener());
this.getGraphControl().addMouseListener(new GraphMouseAdapter());
GraphEventListener gEListener = new GraphEventListener();
this.graph.addListener(mxEvent.CONNECT_CELL, gEListener);
this.getConnectionHandler().addListener(mxEvent.CONNECT, gEListener);
this.getConnectionHandler().addListener(mxEvent.CONNECT_CELL, gEListener);
this.graph.refresh();
}
/**
* Setup of the window for renaming workflows.
*/
protected void initInterruptWin() {
this.interruptWin = new JFrame("Workflow Message");
JPanel mainPanel = new JPanel();
mainPanel.add(new JLabel("Please wait, aborting execution..."));
this.interruptWin.add(mainPanel);
this.interruptWin.setSize(300,115);
this.interruptWin.setResizable(false);
}
/**
* Restore maps of Alida workflow IDs and corresponding graph cells.
*/
protected void restoreIDs() {
// get all the nodes of the graph
Object [] allCells =
this.graph.getChildVertices(this.graph.getDefaultParent());
LinkedList<mxCell> failedCells = new LinkedList<mxCell>();
for (Object o : allCells) {
if (((mxCell)o).isVertex()) {
mxCell node = (mxCell)o;
ALDGrappaNodeInfo nodeInfo = (ALDGrappaNodeInfo)node.getValue();
if (logger.isDebugEnabled()) {
logger.debug(" Found node: {}", nodeInfo.getNodeName());
}
ALDWorkflowNodeID nodeID =
this.alidaWorkflow.getNodeIdDuringLoading(
nodeInfo.getRefID());
if (logger.isDebugEnabled()) {
logger.debug(" -> ID is = {}", nodeID.id);
}
if ( nodeID != null ) {
// fill data structure
this.graphNodes.put(nodeID, node);
this.graphNodeIDs.put(node, nodeID);
// edges are accessible via children
if (logger.isDebugEnabled()) {
logger.debug(" \t number of childs = {}",
new Integer(node.getChildCount()));
}
for (int c = 0; c<node.getChildCount(); ++c) {
mxCell child = (mxCell)node.getChildAt(c);
if (logger.isDebugEnabled()) {
logger.debug(" \t -> child {} has {} edges",
new Integer(c), new Integer(child.getEdgeCount()));
}
for (int e = 0; e<child.getEdgeCount(); ++e) {
mxCell edge = (mxCell)child.getEdgeAt(e);
ALDGrappaNodeInfo edgeInfo =
(ALDGrappaNodeInfo)edge.getValue();
ALDWorkflowEdgeID edgeID =
this.alidaWorkflow.getEdgeIdDuringLoading(
edgeInfo.getRefID());
if ( edgeID != null
&& !(this.graphEdges.containsKey(edgeID))) {
// fill data structure
this.graphEdges.put(edgeID, edge);
this.graphEdgeIDs.put(edge, edgeID);
}
}
}
}
// node ID is null, i.e. node has not been found in ALDWorkflow
// (happens, e.g., if class is not present anymore)
else {
// store node for later problem solving
failedCells.push(node);
}
}
} // end of for-loop over all cells present in JGraphX graph
// delete cells which could not be associated with nodes in ALDWorkflow
Object[] obsoleteCells = new Object[failedCells.size()];
Object[] edgesToRemove;
int i=0;
for (mxCell node: failedCells) {
obsoleteCells[i] = node;
++i;
// check for childs (ports) and connected edges to remove them
for (int c = 0; c<node.getChildCount(); ++c) {
mxCell child = (mxCell)node.getChildAt(c);
// array to store edges to remove
edgesToRemove = new Object[child.getEdgeCount()];
for (int e = 0; e<child.getEdgeCount(); ++e) {
mxCell edge = (mxCell)child.getEdgeAt(e);
ALDGrappaNodeInfo edgeInfo =
(ALDGrappaNodeInfo)edge.getValue();
ALDWorkflowEdgeID edgeID =
this.alidaWorkflow.getEdgeIdDuringLoading(
edgeInfo.getRefID());
// just to make sure that everything is fine...
if (edgeID != null) {
System.err.println("[ALDGrappaWorkbenchTab::restoreIDs()] " +
"found edge connected to non-existing node...!?");
try {
System.in.read();
} catch (IOException e1) {
e1.printStackTrace();
}
}
else {
edgesToRemove[e] = edge;
}
}
// delete all edges of this node
this.graph.removeCells(edgesToRemove);
}
}
// delete all the cells
this.graph.removeCells(obsoleteCells);
}
/**
* Instantiates for each node in the current workflow a configuration window.
*/
protected void restoreConfigWins() {
Set<ALDWorkflowNodeID> keys = this.graphNodes.keySet();
ALDOperator op = null;
for (ALDWorkflowNodeID nodeID : keys) {
try {
op = this.alidaWorkflow.getOperator(nodeID);
ParameterUpdateListener pL = new ParameterUpdateListener(nodeID);
ALDOperatorConfigurationFrame confWin = this.getNewConfigWin(op, pL);
// check which input params (required/optional/supplemental) are linked
Collection<String> inParams = op.getInInoutNames(new Boolean(false));
inParams.addAll(op.getInInoutNames(new Boolean(true)));
ALDWorkflowNode wNode = this.alidaWorkflow.getNode(nodeID);
for (String ip: inParams) {
// get input edges, should never be more than one!
Collection<ALDWorkflowEdge> iEdges = wNode.getInEdgesForParameter(ip);
if (iEdges.isEmpty()) {
// if there are not incoming edges, skip parameter
continue;
}
// get the edge and its metadata
ALDWorkflowEdge edge = iEdges.iterator().next();
ALDWorkflowNode sourceNode = edge.getSourceNode();
String sourceParam = edge.getSourceParameterName();
// mark the parameter as linked in configuration window
confWin.setParameterLinked(
ip, sourceNode.getOperator().getName(), sourceParam);
}
// confWin.addALDOpParameterUpdateEventListener(
// new ParameterUpdateListener(nodeID));
mxCell node = this.graphNodes.get(nodeID);
this.configWindows.put(node, confWin);
// initially configuration windows always show only standard
// parameters
this.nodeConfigShowAllParameters.put(node,new Boolean(false));
this.nodeConfigShowResultsAtOnce.put(node,new Boolean(false));
// fire a parameter change event, this is the first time when the
// listener can react on that
confWin.fireALDOpParameterUpdateEvent(new ALDOpParameterUpdateEvent(
this, ALDOpParameterUpdateEvent.EventType.CHANGED));
} catch (ALDException e) {
String name = (op == null) ? "Unknown Op" : op.getName();
System.err.println("[ALDGrappaWorkbenchTab::restoreConfigWins] "
+ "cannot instantiating config win for \"" + name + "\"...");
}
}
}
/**
* Checks the states of all nodes and updates their color.
*/
private void updateWorkflowNodeStates() {
Set<ALDWorkflowNodeID> keys = this.graphNodes.keySet();
// LinkedList<ALDWorkflowNodeID> tmpList =
// new LinkedList<ALDWorkflowNodeID>();
// for (ALDWorkflowNodeID nodeID : keys) {
// tmpList.add(nodeID);
// }
HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState> statusMap =
new HashMap<>();
for (ALDWorkflowNodeID nodeID : keys) {
ALDWorkflowNodeState state;
try {
state = this.alidaWorkflow.getNode(nodeID).getState();
statusMap.put(nodeID, state);
} catch (ALDWorkflowException e) {
// just print stack trace and continue...
e.printStackTrace();
}
}
// make sure that all node state are correct
this.handleNodeStateChangeEvent(statusMap);
// ATTENTION: never call this method here, at this point node
// parameters are always up-to-date!
// this.handleNodeParameterChangeEvent(tmpList);
}
/**
* Initialize node name count map on reload of workflow.
*/
private void initNodeNameCountMap() {
// regular expression how new node names with count ID look like
String nameRegExp = ".*\\[(0|1|2|3|4|5|6|7|8|9)+\\]$";
this.nodeNameCountMap = new HashMap<>();
Set<mxCell> cells = this.graphNodeIDs.keySet();
ALDGrappaNodeInfo nInfo;
String nName;
int nCount;
for (mxCell c : cells) {
nInfo = (ALDGrappaNodeInfo)c.getValue();
nName = nInfo.getNodeName();
// check if the node name follows new conventions with count ID,
// i.e., if it looks like <operatorname[count]>
if (nName.matches(nameRegExp)) {
int bracketStart = nName.lastIndexOf("[");
int bracketEnd = nName.lastIndexOf("]");
nCount = Integer.valueOf(
nName.substring(bracketStart+1, bracketEnd)).intValue();
// extract pure name without ID number
nName = nName.substring(0,bracketStart);
if (this.nodeNameCountMap.containsKey(nName)) {
if (nCount > this.nodeNameCountMap.get(nName).intValue())
this.nodeNameCountMap.put(nName, new Integer(nCount));
}
else {
this.nodeNameCountMap.put(nName, new Integer(nCount));
}
}
// if the name is old-fashioned, just count operator nodes so that
// at least new nodes will get unique names with count ID
else {
if (this.nodeNameCountMap.containsKey(nName)) {
this.nodeNameCountMap.put(nName, new Integer(
this.nodeNameCountMap.get(nName).intValue()+1));
}
else {
this.nodeNameCountMap.put(nName, new Integer(1));
}
}
}
}
/**
* Switches the mode for displaying parameters of a node.
* @param cell Graph cell/node for which mode is to be set.
* @param mode Mode to set.
*/
protected synchronized void setWorkflowNodeViewMode(mxCell cell,
Parameter.ExpertMode mode) {
boolean showAll = mode.equals(Parameter.ExpertMode.ADVANCED);
this.nodeConfigShowAllParameters.put(cell, new Boolean(showAll));
ALDWorkflowNodeID nodeID =
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(cell);
ALDOperator op;
try {
op = ALDGrappaWorkbenchTab.this.alidaWorkflow.getOperator(nodeID);
for (int i = 0; i < cell.getChildCount(); i++) {
mxCell port = (mxCell)cell.getChildAt(i);
ALDOpParameterDescriptor descr =
op.getParameterDescriptor(
((ALDGrappaNodePortInfo)port.getValue()).getPortName());
// ignore outputs
if ( descr.getDirection().equals(Parameter.Direction.IN)
|| descr.getDirection().equals(Parameter.Direction.INOUT)) {
if (showAll) {
port.setVisible(true);
}
else {
if (descr.getHandlingMode().equals(Parameter.ExpertMode.ADVANCED)){
// check if port is linked, if so, should remain visible
if (port.getEdgeCount() == 0)
port.setVisible(false);
}
else {
port.setVisible(true);
}
}
}
}
this.graph.refresh();
} catch (ALDException e) {
JOptionPane.showMessageDialog(null, "Changing view mode faild,\n" +
"something is wrong with the operator!",
"Workflow Error", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Changes the workflow title.
* @param newTitle New title of the workflow.
*/
public void setWorkflowTitle(String newTitle) {
// set title for graph
// this.workflowTitle = newTitle;
// set title for underlying Alida workflow
this.alidaWorkflow.setName(newTitle);
this.processWorkflowEventQueue();
}
/**
* Returns the workflow title.
* @return Current title of the workflow.
*/
public String getWorkflowTitle() {
return this.workflowTitle;
}
/**
* Executes the complete workflow, i.e. all nodes currently present.
*/
public void runWorkflow() {
try {
// send status message
this.workBench.stateChanged(new ALDGrappaWorkflowTabChangeEvent(
this, "running workflow..."));
this.alidaWorkflow.runWorkflow();
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Executes the workflow up to the specified node.
* @param nodeID ID of node where to stop execution.
*/
public void runWorkflowNode(ALDWorkflowNodeID nodeID) {
try {
// send status message
this.workBench.stateChanged(new ALDGrappaWorkflowTabChangeEvent(
this, "running workflow node <"
+ this.alidaWorkflow.getNode(nodeID).getOperator().getName()
+ ">..."));
this.alidaWorkflow.runNode(nodeID);
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Executes the workflow from the specified node to the end.
* @param nodeID ID of node where to start execution.
*/
public void runWorkflowFromNode(ALDWorkflowNodeID nodeID) {
try {
// send status message
this.workBench.stateChanged(new ALDGrappaWorkflowTabChangeEvent(
this, "running workflow nodes starting with <"
+ this.alidaWorkflow.getNode(nodeID).getOperator().getName()
+ ">..."));
this.alidaWorkflow.runFromNode(nodeID);
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Aborts running execution of the workflow.
*/
public void interruptExecution() {
Dimension parentDim = this.workBench.mainFrame.getSize();
this.interruptWin.setLocation((int)(parentDim.getWidth()/2), 300);
this.interruptWin.setVisible(true);
this.alidaWorkflow.interruptExecution();
// post-process workflow events
this.processWorkflowEventQueue();
}
/**
* Saves the workflow to the given file.
* @param file File where to save the workflow.
*/
public synchronized void saveWorkflow(File file) {
try {
this.alidaWorkflow.save(file, true);
// post-process workflow events
this.processWorkflowEventQueue();
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Function to return tooltip text for a cell.
* @param cell Cell for which tooltip is requested.
* @return Tooltip text.
*/
public String getTooltipText(Object cell) {
mxCell port = (mxCell) cell;
if (port.isVertex() && port.isConnectable()) {
ALDGrappaNodePortInfo portInfo = (ALDGrappaNodePortInfo)port.getValue();
String toolTip = "<html>" + "Name: " + portInfo.getPortLabel()
+ "<br>" + "Class: " + portInfo.getPortClassName() + "<br>"
+ portInfo.getPortExplanation() + "</html>";
return toolTip;
}
return "";
}
/**
* Check if the workflow is empty.
* @return If true, the workflow contains at least one node.
*/
public boolean workflowHasNodes() {
return !this.alidaWorkflow.getNodes().isEmpty();
}
/**
* Does clean-up on termination, i.e. closes all open windows.
*/
public void quit() {
// close all configuration windows
Set<mxCell> keys = this.configWindows.keySet();
for (mxCell key : keys) {
this.configWindows.get(key).setVisible(false);
}
}
/**
* Creates a new workflow node.
* @param loc Location data of the underlying operator.
* @param posX x position where to place the new node.
* @param posY y position where to place the new node.
*/
protected synchronized void createNewWorkflowNode(ALDOperatorLocation loc,
int posX, int posY) {
try {
ALDWorkflowID actionID = this.alidaWorkflow.createNode(loc);
// remember action on workflow for later reference
WorkflowModifyAction graphMA = new WorkflowModifyAction();
graphMA.setActionPositionX(posX);
graphMA.setActionPositionY(posY);
this.actionsOnWorkflow.put(actionID, graphMA);
} catch (ALDWorkflowException ex) {
JOptionPane.showMessageDialog(null, "Adding node \""
+ loc.getName() + "\" failed!\n",
"Error", JOptionPane.ERROR_MESSAGE);
}
// process workflow events
this.processWorkflowEventQueue();
}
/**
* Copies a workflow node.
* @param opNamePath Full path of the underlying operator.
* @param posX x position where to place the new node.
* @param posY y position where to place the new node.
*/
// protected synchronized void copyWorkflowNode(ALDOperator op,
// int posX, int posY) {
// try {
// ALDWorkflowID actionID = this.alidaWorkflow.createNode(op);
// // remember action on workflow for later reference
// WorkflowModifyAction graphMA = new WorkflowModifyAction();
// graphMA.setActionPositionX(posX);
// graphMA.setActionPositionY(posY);
// this.actionsOnWorkflow.put(actionID, graphMA);
// } catch (ALDWorkflowException ex) {
// JOptionPane.showMessageDialog(null, "Adding node \""
// + op.getName() + "\" failed!\n",
// "Error", JOptionPane.ERROR_MESSAGE);
// }
// // process workflow events
// this.processWorkflowEventQueue();
// }
/**
* Copies only the links of a workflow node to a new node of same type.
* @param srcOp Source node operator which is to be copied.
* @param srcNode Source node to be copied.
* @param newOp New operator instantiated from source operator.
* @param posX x position where to place the new node.
* @param posY y position where to place the new node.
*/
protected synchronized void copyWorkflowNodeLinksOnly(
@SuppressWarnings("unused") ALDOperator srcOp, mxCell srcNode,
ALDOperator newOp, int posX, int posY) {
try {
// first create the new operator node in the workflow
ALDWorkflowNodeID newNodeID = this.alidaWorkflow.createNode(newOp);
// remember action on workflow for later reference
WorkflowModifyAction graphMA = new WorkflowModifyAction();
graphMA.setActionPositionX(posX);
graphMA.setActionPositionY(posY);
this.actionsOnWorkflow.put(newNodeID, graphMA);
// add all input edges of source operator node also to new one
for (int i= 0; i<srcNode.getChildCount(); ++i) {
mxCell child = (mxCell)srcNode.getChildAt(i);
ALDGrappaNodePortInfo childInfo =
(ALDGrappaNodePortInfo)child.getValue();
if (childInfo.getPortDirection().equals("IN")) {
// iterate over all edges of the source node port
int childEdgeNum = child.getEdgeCount();
for (int j=0; j<childEdgeNum; ++j) {
mxCell edge = (mxCell)child.getEdgeAt(j);
// check if edge is incoming
if (edge.getTarget().equals(child)) {
// get node ID of source node and output port parameter name
ALDWorkflowNodeID sourceNodeID =
this.graphNodeIDs.get(edge.getSource().getParent());
String sourceParameter =
((ALDGrappaNodePortInfo)edge.getSource().getValue()).
getPortName();
String targetParameter =
((ALDGrappaNodePortInfo)edge.getTarget().getValue()).
getPortName();
// add edge to workflow
ALDWorkflowEdgeID edgeID =
ALDGrappaWorkbenchTab.this.alidaWorkflow.createEdge(
sourceNodeID, sourceParameter, newNodeID, targetParameter);
ALDGrappaWorkbenchTab.this.actionsOnWorkflow.put(edgeID,
new WorkflowModifyAction());
}
}
}
}
} catch (ALDWorkflowException ex) {
JOptionPane.showMessageDialog(null, "Copying node \""
+ newOp.getName() + "\" failed!\n",
"Error", JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
}
// process workflow events
this.processWorkflowEventQueue();
}
/**
* Adds a new edge object to the graph.
* <p>
* Note that according to the event handling of the GUI the edge object
* has already been inserted into the graph data structure when this
* function is called. Accordingly this function mainly checks if the
* insertion was correct, and if not, removes the formerly inserted edge.
*
* @param edge Edge object recently inserted.
* @param sourceNodeID ID of source node.
* @param sourcePort Name of source port.
* @param targetNodeID ID of target node.
* @param targetPort Name of target port.
*/
protected synchronized void createNewWorkflowEdge(mxCell edge,
ALDWorkflowNodeID sourceNodeID, ALDGrappaNodePortInfo sourcePort,
ALDWorkflowNodeID targetNodeID, ALDGrappaNodePortInfo targetPort) {
String sourceParameter = sourcePort.getPortName();
String targetParameter = targetPort.getPortName();
try {
ALDWorkflowEdgeID edgeID =
ALDGrappaWorkbenchTab.this.alidaWorkflow.createEdge(
sourceNodeID, sourceParameter, targetNodeID, targetParameter);
ALDGrappaWorkbenchTab.this.actionsOnWorkflow.put(edgeID,
new WorkflowModifyAction());
ALDGrappaWorkbenchTab.this.graphEdgeIDs.put(edge, edgeID);
ALDGrappaWorkbenchTab.this.graphEdges.put(edgeID, edge);
// add user data
edge.setValue(new ALDGrappaNodeInfo());
} catch (ALDWorkflowException ex) {
/* Check for type of execption, i.e., check if implicit data type
* conversion is possible; if so, ask the user for permission. */
String message;
switch(ex.getType())
{
case INCOMPATIBLE_TYPES_BUT_CONVERTIBLE:
message = ex.getIdentStringWithoutType()+ex.getCommentString()
+ "\nDo you want to allow the data type conversion?";
int retVal = JOptionPane.showConfirmDialog(null, message,
"Edge Connect Warning", JOptionPane.YES_NO_OPTION);
// user says "no", delete edge
if (retVal == JOptionPane.NO_OPTION) {
ALDGrappaWorkbenchTab.this.getGraph().getModel().remove(edge);
ALDGrappaWorkbenchTab.this.getGraph().refresh();
}
// user says "yes", tell workflow that inserting edge is ok
else {
ALDWorkflowEdgeID edgeID;
try {
edgeID = ALDGrappaWorkbenchTab.this.alidaWorkflow.createEdge(
sourceNodeID, sourceParameter, targetNodeID, targetParameter,
new Boolean(true));
ALDGrappaWorkbenchTab.this.actionsOnWorkflow.put(edgeID,
new WorkflowModifyAction());
ALDGrappaWorkbenchTab.this.graphEdgeIDs.put(edge, edgeID);
ALDGrappaWorkbenchTab.this.graphEdges.put(edgeID, edge);
// add user data
edge.setValue(new ALDGrappaNodeInfo());
} catch (ALDWorkflowException e) {
if ( this.debug ) {
System.out.println("[ALDGrappaWorkbenchTab] workflow error, " +
"offers conversion, but does not do it...");
}
e.printStackTrace();
}
}
break;
case INCOMPATIBLE_TYPES:
default:
// something went wrong, remove edge
ALDGrappaWorkbenchTab.this.getGraph().getModel().remove(edge);
ALDGrappaWorkbenchTab.this.getGraph().refresh();
// format proper error message
message = ex.getIdentStringWithoutType()+ex.getCommentString();
JOptionPane.showMessageDialog(null, message,
"Edge Connect Error", JOptionPane.ERROR_MESSAGE);
break;
}
}
// process workflow events
ALDGrappaWorkbenchTab.this.processWorkflowEventQueue();
}
/**
* Removes the specified cell from the graph.
* @param node Node cell to remove.
*/
protected void removeWorkflowNode(mxCell node) {
ALDWorkflowNodeID nodeID =
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(node);
try {
ALDGrappaWorkbenchTab.this.actionsOnWorkflow.put(nodeID,
new WorkflowModifyAction());
ALDGrappaWorkbenchTab.this.alidaWorkflow.removeNode(nodeID);
} catch (ALDWorkflowException e1) {
JOptionPane.showMessageDialog(null, "Removing node failed!",
"Node Error", JOptionPane.ERROR_MESSAGE);
}
// process workflow events
ALDGrappaWorkbenchTab.this.processWorkflowEventQueue();
}
/**
* Removes the specified edge from the workflow.
* @param edge Edge cell to remove.
*/
protected void removeWorkflowEdge(mxCell edge) {
ALDWorkflowEdgeID edgeID =
ALDGrappaWorkbenchTab.this.graphEdgeIDs.get(edge);
try {
ALDGrappaWorkbenchTab.this.actionsOnWorkflow.put(edgeID,
new WorkflowModifyAction());
ALDGrappaWorkbenchTab.this.alidaWorkflow.removeEdge(edgeID);
} catch (ALDWorkflowException e1) {
JOptionPane.showMessageDialog(null, "Removing edge failed!",
"Edge Error", JOptionPane.ERROR_MESSAGE);
}
// process workflow events
ALDGrappaWorkbenchTab.this.processWorkflowEventQueue();
}
/**
* Redirects the workflow edge.
* <p>
* Note that due to the event handling of the GUI the edge redirect
* was already done when this function is called. Thus, this function
* mainly verifies the redirect and restores the old edge in case of
* failure.
*
* @param edge Edge cell that was redirected.
* @param id Workflow ID of edge.
* @param sourceNodeID Current ID of edge source node.
* @param targetNodeID Current ID of edge target node.
* @param sourcePort Current source parameter name.
* @param targetPort Current target parameter name.
*/
protected synchronized void redirectWorkflowEdge(
mxCell edge, ALDWorkflowEdgeID id,
ALDWorkflowNodeID sourceNodeID, ALDWorkflowNodeID targetNodeID,
ALDGrappaNodePortInfo sourcePort, ALDGrappaNodePortInfo targetPort) {
String sourceParameter = sourcePort.getPortName();
String targetParameter = targetPort.getPortName();
edge.setValue(new ALDGrappaNodeInfo());
ALDWorkflowNodeID flowSourceNodeID = null;
ALDWorkflowNodeID flowTargetNodeID = null;
String flowSourceNodeParamter = null;
String flowTargetNodeParamter = null;
try {
// get old source and target nodes as represented in workflow
flowSourceNodeID =
ALDGrappaWorkbenchTab.this.alidaWorkflow.getSourceNodeId(id);
flowSourceNodeParamter =
ALDGrappaWorkbenchTab.this.alidaWorkflow.getSourceParameterName(id);
flowTargetNodeID =
ALDGrappaWorkbenchTab.this.alidaWorkflow.getTargetNodeId(id);
flowTargetNodeParamter =
ALDGrappaWorkbenchTab.this.alidaWorkflow.getTargetParameterName(id);
// check if source or target changed
if ( (!(sourceNodeID.equals(flowSourceNodeID)))
|| (!(sourceParameter.equals(flowSourceNodeParamter)))) {
// System.out.println("Source node = " + sourceNode.getValue());
// System.out.println("--- port = " + sourcePort.getValue());
// System.out.println("Target node = " + targetNode.getValue());
// System.out.println("--- port = " + targetPort.getValue());
// source of edge was redirected
ALDGrappaWorkbenchTab.this.alidaWorkflow.redirectSource(
id, sourceNodeID, sourceParameter);
// remember action
ALDGrappaWorkbenchTab.this.actionsOnWorkflow.put(id,
new WorkflowModifyAction());
}
else if ( (!(targetNodeID.equals(flowTargetNodeID)))
|| (!(targetParameter.equals(flowTargetNodeParamter)))) {
// System.out.println("Source node = " + sourceNode.getValue());
// System.out.println("--- port = " + sourcePort.getValue());
// System.out.println("Target node = " + targetNode.getValue());
// System.out.println("--- port = " + targetPort.getValue());
// target of edge was redirected
String oldTargetParam = ALDGrappaWorkbenchTab.
this.alidaWorkflow.getTargetParameterName(id);
ALDGrappaWorkbenchTab.this.alidaWorkflow.redirectTarget(
id, targetNodeID, targetParameter);
WorkflowModifyAction wma = new WorkflowModifyAction();
wma.oldEdgeTarget = flowTargetNodeID;
wma.oldEdgeTargetParamName = oldTargetParam;
ALDGrappaWorkbenchTab.this.actionsOnWorkflow.put(id, wma);
}
else {
// just for debugging, we should never end-up here...
System.err.println("--- Edge redirect: nothing changed?!");
}
} catch (ALDWorkflowException e) {
// format proper error message
String message = e.getIdentStringWithoutType()+e.getCommentString();
JOptionPane.showMessageDialog(null, message,
"Edge Redirect Error", JOptionPane.ERROR_MESSAGE);
// reset edge
mxCell oldSource =
ALDGrappaWorkbenchTab.this.graphNodes.get(flowSourceNodeID);
mxCell oldTarget =
ALDGrappaWorkbenchTab.this.graphNodes.get(flowTargetNodeID);
int childs = oldSource.getChildCount();
for (int i= 0; i<childs; ++i) {
mxCell child = (mxCell)oldSource.getChildAt(i);
try {
ALDGrappaNodePortInfo childPortInfo =
(ALDGrappaNodePortInfo)child.getValue();
if (childPortInfo.getPortName().equals(ALDGrappaWorkbenchTab.this.
alidaWorkflow.getSourceParameterName(id))) {
edge.setSource(child);
break;
}
} catch (ALDWorkflowException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
childs = oldTarget.getChildCount();
for (int i= 0; i<childs; ++i) {
mxCell child = (mxCell)oldTarget.getChildAt(i);
try {
ALDGrappaNodePortInfo childPortInfo =
(ALDGrappaNodePortInfo)child.getValue();
if (childPortInfo.getPortName().equals(ALDGrappaWorkbenchTab.this.
alidaWorkflow.getTargetParameterName(id))) {
edge.setTarget(child);
break;
}
} catch (ALDWorkflowException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
// process workflow events
ALDGrappaWorkbenchTab.this.processWorkflowEventQueue();
}
/**
* Processes all events that were recently added to the queue.
* <p>
* Note that this function needs to be called after all actions on the
* Alida workflow except calls to 'run' methods.
*/
protected synchronized void processWorkflowEventQueue() {
BlockingDeque<ALDWorkflowEvent> queue =
this.alidaWorkflow.getEventQueue(this);
ALDWorkflowEvent event = null;
while (!queue.isEmpty()) {
event = queue.pop();
this.handleALDWorkflowEvent(event);
}
ALDGrappaWorkbenchTab.this.getGraph().refresh();
}
/* (non-Javadoc)
* @see de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEventListener#handleALDWorkflowEvent(de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEvent)
*/
@SuppressWarnings("unchecked")
@Override
public synchronized void handleALDWorkflowEvent(ALDWorkflowEvent event) {
// extract event data
ALDWorkflowEventType type = event.getEventType();
Object eventInfo = event.getId();
// get action file related to this action if available
WorkflowModifyAction actionReference=this.actionsOnWorkflow.get(eventInfo);
// handle the event
switch(type)
{
case ADD_NODE:
if ( eventInfo instanceof ALDWorkflowNodeID
&& actionReference != null) {
ALDOperator op;
try {
op = this.alidaWorkflow.getOperator(
(ALDWorkflowNodeID)eventInfo);
// request initial GUI values for operator
ALDOperator initialOp = (ALDOperator)ALDDataIOManagerSwing.
getInstance().getInitialGUIValue(null, op.getClass(), op,
null);
// copy parameter settings
Collection<String> params = op.getParameterNames();
// copy the collection, because iterating over a collection
// which could be meanwhile modified is a very bad idea!
String[] paramArray = new String[params.size()];
int i=0;
for (String s: params) {
paramArray[i] = s;
++i;
}
for (i=0; i<paramArray.length; ++i) {
String pname = paramArray[i];
// if the parameter does not exist anymore (e.g., due to
// callback changes), skip it
if (!op.hasParameter(pname))
continue;
op.setParameter(pname, initialOp.getParameter(pname));
}
this.handleAddNodeEvent(op, (ALDWorkflowNodeID)eventInfo,
actionReference.getActionPositionX(),
actionReference.getActionPositionY());
// remove the action file as event was processed
this.actionsOnWorkflow.remove(eventInfo);
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ALDDataIOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ALDOperatorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
case ADD_EDGE:
if (!(eventInfo instanceof ALDWorkflowEdgeID)) {
JOptionPane.showMessageDialog(null, "Adding edge failed!",
"Workflow Error", JOptionPane.ERROR_MESSAGE);
return;
}
// do some checks and then update configuration window
try {
ALDWorkflowEdgeID id = (ALDWorkflowEdgeID)eventInfo;
if ( this.debug ) {
System.out.println("- ID = " + eventInfo);
}
ALDWorkflowNodeID source = this.alidaWorkflow.getSourceNodeId(id);
ALDWorkflowNodeID target = this.alidaWorkflow.getTargetNodeId(id);
String sourceParam = this.alidaWorkflow.getSourceParameterName(id);
String targetParam = this.alidaWorkflow.getTargetParameterName(id);
// check if edge exists, if not insert edge into graph
mxCell sourceNode = this.graphNodes.get(source);
mxCell targetNode = this.graphNodes.get(target);
boolean edgeExists = false;
for (int i=0;i<sourceNode.getChildCount();++i) {
mxCell child = (mxCell)sourceNode.getChildAt(i);
if (this.graph.getOutgoingEdges(child).length > 0) {
// process outgoing edges
Object[] outEdges = this.graph.getOutgoingEdges(child);
for (Object edge: outEdges) {
mxCell outEdge = (mxCell)edge;
ALDGrappaNodePortInfo targetInfo =
(ALDGrappaNodePortInfo)
( (mxCell)outEdge.getTarget()).getValue();
if ( ((mxCell)outEdge.getTarget().getParent()).equals(targetNode)
&& targetInfo.getPortName().equals(targetParam)) {
edgeExists= true;
break;
}
}
}
}
// if edge does not exist, insert it
if (!edgeExists) {
// figure out source port
mxCell sourcePort= null, targetPort= null;
for (int i=0;i<sourceNode.getChildCount();++i) {
mxCell child = (mxCell)sourceNode.getChildAt(i);
ALDGrappaNodePortInfo sourceInfo =
(ALDGrappaNodePortInfo)child.getValue();
if (sourceInfo.getPortName().equals(sourceParam)) {
sourcePort = child;
break;
}
}
for (int i=0;i<targetNode.getChildCount();++i) {
mxCell child = (mxCell)targetNode.getChildAt(i);
ALDGrappaNodePortInfo targetInfo =
(ALDGrappaNodePortInfo)child.getValue();
if (targetInfo.getPortName().equals(targetParam)) {
targetPort = child;
break;
}
}
if (sourcePort==null || targetPort==null) {
System.err.println("Something went wrong on adding edge...");
}
else {
Object edge = ALDGrappaWorkbenchTab.this.getGraph().insertEdge(
this.graph.getDefaultParent(), null, null,
sourcePort, targetPort);
// store references of new edge
ALDGrappaWorkbenchTab.this.graphEdgeIDs.put((mxCell)edge, id);
ALDGrappaWorkbenchTab.this.graphEdges.put(id, (mxCell)edge);
// add user data
((mxCell)edge).setValue(new ALDGrappaNodeInfo());
// refresh graph
ALDGrappaWorkbenchTab.this.getGraph().refresh();
}
}
// notify config window of link
this.configWindows.get(this.graphNodes.get(target)).setParameterLinked(
targetParam, this.alidaWorkflow.getOperator(source).getName(),
sourceParam);
// finally remove corresponding ID
this.actionsOnWorkflow.remove(eventInfo);
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case DELETE_NODE:
if (!(eventInfo instanceof ALDWorkflowNodeID)) {
JOptionPane.showMessageDialog(null, "Removing node failed!",
"Node Error", JOptionPane.ERROR_MESSAGE);
}
else {
mxCell cell = this.graphNodes.get(eventInfo);
// remove entries in hashmaps
this.graphNodeIDs.remove(cell);
this.graphNodes.remove(eventInfo);
this.configWindows.remove(cell);
this.nodeConfigShowAllParameters.remove(cell);
this.nodeConfigShowResultsAtOnce.remove(cell);
// remove node from JGraphX model
this.graph.getModel().remove(cell);
// remove event reference
this.actionsOnWorkflow.remove(eventInfo);
}
break;
case DELETE_EDGE:
if (!(eventInfo instanceof ALDWorkflowEdgeID)) {
JOptionPane.showMessageDialog(null, "Removing edge failed!",
"Node Error", JOptionPane.ERROR_MESSAGE);
}
else {
mxCell edge = this.graphEdges.get(eventInfo);
if ( this.debug ) {
System.out.println("Edge " + edge);
System.out.println("- ID = " + eventInfo);
}
// update the configuration window
ALDGrappaNodePortInfo targetPortInfo =
(ALDGrappaNodePortInfo)(((mxCell)edge.getTarget()).getValue());
String targetParam = targetPortInfo.getPortName();
mxCell target = (mxCell)(((mxCell)edge.getTarget()).getParent());
// notify config window of link
this.configWindows.get(target).setParameterNotLinked(targetParam);
// finally remove the edge
this.graphEdges.remove(eventInfo);
this.graphEdgeIDs.remove(edge);
this.graph.getModel().remove(edge);
this.actionsOnWorkflow.remove(eventInfo);
}
break;
case REDIRECT_EDGE_SOURCE:
// remove the corresponding ID, if available
this.actionsOnWorkflow.remove(eventInfo);
break;
case REDIRECT_EDGE_TARGET:
if (!(eventInfo instanceof ALDWorkflowEdgeID)) {
JOptionPane.showMessageDialog(null, "Redirecting edge failed!",
"Workflow Edge Error", JOptionPane.ERROR_MESSAGE);
return;
}
// update configuration windows
try {
// first, add link to new target node's window
if ( this.debug ) {
System.out.println("Updating new target window...");
}
ALDWorkflowEdgeID id = (ALDWorkflowEdgeID)eventInfo;
ALDWorkflowNodeID source = this.alidaWorkflow.getSourceNodeId(id);
ALDWorkflowNodeID target = this.alidaWorkflow.getTargetNodeId(id);
String sourceParam = this.alidaWorkflow.getSourceParameterName(id);
String targetParam = this.alidaWorkflow.getTargetParameterName(id);
// notify config window of link
this.configWindows.get(this.graphNodes.get(target)).setParameterLinked(
targetParam, this.alidaWorkflow.getOperator(source).getName(),
sourceParam);
// second, remove link from old target node's window
WorkflowModifyAction wma = this.actionsOnWorkflow.get(eventInfo);
if (wma != null) {
if ( this.debug ) {
System.out.println("Updating old target window...");
}
// my own action, so all data is in workflow action
ALDWorkflowNodeID formerTargetNode = wma.oldEdgeTarget;
String formerTargetParam = wma.oldEdgeTargetParamName;
this.configWindows.get(this.graphNodes.get(formerTargetNode)).
setParameterNotLinked(formerTargetParam);
// finally remove corresponding ID
this.actionsOnWorkflow.remove(eventInfo);
}
else {
// action of someone else, i.e. edge has not yet been redirected
if ( this.debug ) {
System.out.println("No information on action...");
}
// mxCell edge = this.graphEdges.get(eventID);
// // update the configuration window
// String targetParam = (String)(((mxCell)edge.getTarget()).getValue());
// mxCell target = (mxCell)(((mxCell)edge.getTarget()).getParent());
// // notify config window of link
// this.configWindows.get(target).setParameterNotLinked(targetParam);
}
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// remove the corresponding ID, if available
this.actionsOnWorkflow.remove(eventInfo);
break;
case NODE_PARAMETER_CHANGE:
this.handleNodeParameterChangeEvent(
(Collection<ALDWorkflowNodeID>)eventInfo);
// this.handleNodeStateChangeEvent(
// (Collection<ALDWorkflowNodeID>)eventInfo);
break;
case NODE_STATE_CHANGE:
this.handleNodeStateChangeEvent(
(HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState>)eventInfo);
break;
// case LOAD_WORKFLOW:
// if (!(eventID instanceof ALDWorkflowStorageInfo)) {
// JOptionPane.showMessageDialog(null, "Loading workflow failed!",
// "Workflow Load Error", JOptionPane.ERROR_MESSAGE);
// }
// else {
// ALDWorkflowStorageInfo info = (ALDWorkflowStorageInfo)eventID;
// this.workBench.handleLoadWorkflowEvent(info.getFilename(),info.getWorkflow());
// }
// break;
case NODE_EXECUTION_PROGRESS:
// only proceed if progress event messages are to be displayed
if (!this.workBench.showProgressEvents())
break;
// new status messages about execution progress received
try {
HashSet<ALDWorkflowNodeID> idHash =
(HashSet<ALDWorkflowNodeID>) eventInfo;
Iterator<ALDWorkflowNodeID> nodeID = idHash.iterator();
ALDWorkflowNode node;
String msg;
while (nodeID.hasNext()) {
node = this.alidaWorkflow.getNode(nodeID.next());
msg = "\"" + node.getOperator().getName() + "\""
+ " - " + node.getOperatorExecutionProgressDescr();
// update status message in control window
this.workBench.stateChanged(
new ALDGrappaWorkflowTabChangeEvent(this, msg));
}
} catch (ALDWorkflowException e1) {
System.err.println("[ALDOperatorGUIExecutionProxy] "
+ "could not handle/update operator status message... ignoring!");
}
break;
case RENAME:
String newTitle = (String)eventInfo;
this.workflowTitle = newTitle;
this.workBench.handleWorkflowRenameEvent(newTitle);
break;
case SAVE_WORKFLOW:
if (!(eventInfo instanceof ALDWorkflowStorageInfo)) {
JOptionPane.showMessageDialog(null, "Saving workflow failed!",
"Workflow Save Error", JOptionPane.ERROR_MESSAGE);
return;
}
ALDWorkflowStorageInfo info = (ALDWorkflowStorageInfo)eventInfo;
this.handleSaveWorkflowEvent(info.getFilename());
break;
case RUN_FAILURE:
// eventInfo object is of type ALDWorkflowNodeID...
JOptionPane.showMessageDialog(null,
"Something went wrong during workflow execution...\n " +
event.getEventMessage(),
"Workflow Execution Error", JOptionPane.ERROR_MESSAGE);
break;
case USER_INTERRUPT:
this.interruptWin.setVisible(false);
JOptionPane.showMessageDialog(null, "Execution was aborted!",
"Workflow Execution Message", JOptionPane.ERROR_MESSAGE);
break;
case SHOW_RESULTS:
// System.out.println("Received show_results event...");
// if (!(eventInfo instanceof ALDWorkflowNodeID)) {
// JOptionPane.showMessageDialog(null, "Cannot display results!",
// "Workflow Execution Error", JOptionPane.ERROR_MESSAGE);
// return;
// }
// this.handleShowResultsEvent((ALDWorkflowNodeID)eventInfo);
break;
default:
if ( this.debug ) {
System.out.println("Got event of type: " + type.toString());
System.out.println(" => type not yet handled by Grappa!");
}
}
this.graph.refresh();
}
/**
* Adds a node to the workflow graph.
*
* @param op Operator associated with the node.
* @param id Node ID.
* @param posX X-Position of the new node.
* @param posY Y-Position of the new node.
*/
protected synchronized void handleAddNodeEvent(
ALDOperator op, ALDWorkflowNodeID id, int posX, int posY) {
// init the control frame
ALDOperatorConfigurationFrame controlFrame = null;
try {
ParameterUpdateListener pListen = new ParameterUpdateListener(id);
controlFrame = this.getNewConfigWin(op, pListen);
} catch (ALDOperatorException e) {
if ( this.debug ) {
System.out.println("[ALDGrappaWorkbenchTab::addNode] "
+ "cannot instantiate control frame for \"" +op.getName()+ "\"...");
}
return;
}
// set position where to insert the node, if no position given, top left
int x = (posX == -1 ? 50 : posX);
int y = (posY == -1 ? 50 : posY);
// get parameters of the operator
int portCountLeft =
op.getInNames(null).size() + op.getInOutNames(null).size();
int heightLeft = portCountLeft * 20;
int portCountRight =
op.getOutNames().size() + op.getInOutNames(null).size();
int heightRight = portCountRight * 20;
int height =
(heightLeft > heightRight ? heightLeft : heightRight);
// int portCount =
// (portCountLeft > portCountRight ? portCountLeft : portCountRight);
// add the vertex
// mxCell v = (mxCell)this.graph.insertVertex(
// this.graph.getDefaultParent(), null, op.getName(), x, y, 160, height);
// register name
int nodeCountID = 1;
String nodeName = op.getName();
if (this.nodeNameCountMap.containsKey(nodeName)) {
this.nodeNameCountMap.put(nodeName, new Integer(
this.nodeNameCountMap.get(nodeName).intValue() + 1));
}
else {
this.nodeNameCountMap.put(nodeName, new Integer(1));
}
nodeCountID = this.nodeNameCountMap.get(nodeName).intValue();
ALDGrappaNodeInfo nodeInf =
new ALDGrappaNodeInfo(nodeName + "[" + nodeCountID + "]");
mxCell v = (mxCell)this.graph.insertVertex(
this.graph.getDefaultParent(), null, nodeInf, x, y, 160, height);
v.setConnectable(false);
v.setStyle(getNodeStyleString("red"));
// add ports to the node
try {
this.addPortsToNode(op, v);
} catch (ALDOperatorException e) {
System.err.println("[ALDGrappaWorkbenchTab::addNode] "
+ "adding ports failed... undefined node state!!!");
}
// update the display
this.graph.refresh();
// store the control frame and the node id
this.configWindows.put(v, controlFrame);
this.nodeConfigShowAllParameters.put(v, new Boolean(false));
this.nodeConfigShowResultsAtOnce.put(v, new Boolean(false));
this.graphNodeIDs.put(v, id);
this.graphNodes.put(id, v);
// providers might have changed parameters, synchronize operator and GUI
// controlFrame.synchronizeOperatorWithGUI();
LinkedList<ALDWorkflowNodeID> nodeList =
new LinkedList<ALDWorkflowNodeID>();
nodeList.add(id);
this.handleNodeParameterChangeEvent(nodeList);
// tell the workflow that the operator configuration changed
controlFrame.fireALDOpParameterUpdateEvent(
new ALDOpParameterUpdateEvent(this, EventType.CHANGED));
// // process event queue
// ALDGrappaWorkbenchTab.this.processWorkflowEventQueue();
}
/**
* Update node's configuration window according to parameter status.
* @param idList List of nodes that are to be updated.
*/
protected synchronized void handleNodeParameterChangeEvent(
Collection<ALDWorkflowNodeID> idList) {
// init map for later status update call
HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState> statusMap =
new HashMap<>();
for (ALDWorkflowNodeID nodeID: idList) {
try {
mxCell node = this.graphNodes.get(nodeID);
// store node status
ALDWorkflowNodeState state =
this.alidaWorkflow.getNode(nodeID).getState();
statusMap.put(nodeID, state);
// update the configuration window
ALDOperator op = this.alidaWorkflow.getOperator(nodeID);
this.configWindows.get(node).updateOperator(op);
this.configWindows.get(node).updateParamConfigurationStatus(
this.alidaWorkflow.getMissingRequiredInputs(nodeID));
// update the ports of the node
mxCell v = ALDGrappaWorkbenchTab.this.graphNodes.get(nodeID);
// check if there is a difference in the ports
Vector<String> portNames = new Vector<String>();
for (int j=0; j<v.getChildCount();++j) {
mxICell child = v.getChildAt(j);
ALDGrappaNodePortInfo pi =
(ALDGrappaNodePortInfo)child.getValue();
portNames.add(pi.getPortName());
}
Collection<String> parameters = op.getParameterNames();
boolean nodePortsChanged = false;
if (portNames.size() != parameters.size())
// ports and parameters diverge, i.e. someting changed...
nodePortsChanged = true;
if (!nodePortsChanged) {
// check if for all parameters a port exists
for (String s : parameters) {
if (!portNames.contains(s)) {
nodePortsChanged = true;
break;
}
}
}
if (!nodePortsChanged) {
// check if for all ports a corresponding parameter exists
for (String s: portNames) {
if (!parameters.contains(s)) {
nodePortsChanged = true;
break;
}
}
}
// there were changes in the ports, update node
if (nodePortsChanged) {
// remove all former ports
int portNum = v.getChildCount();
for (int i=portNum-1;i>=0;--i) {
mxICell child = v.getChildAt(i);
// if (child.getEdgeCount() != 0) {
// int edges = child.getEdgeCount();
// for (int j=edges-1;j>=0;--j) {
// mxCell edge = (mxCell)child.getEdgeAt(j);
// // update the configuration window
// ALDGrappaNodePortInfo targetPortInfo =
// (ALDGrappaNodePortInfo)(
// ((mxCell)edge.getTarget()).getValue());
// String targetParam = targetPortInfo.getPortName();
// mxCell target =
// (mxCell)(((mxCell)edge.getTarget()).getParent());
// // notify config window of link
// this.configWindows.get(target).setParameterNotLinked(
// targetParam);
// }
// }
ALDGrappaNodePortInfo portInfo =
(ALDGrappaNodePortInfo)(child.getValue());
if (!(parameters.contains(portInfo.getPortName()))) {
// remove the port itself
v.remove(child);
}
}
// add ports of parameters to the node
this.addPortsToNode(op, v);
// update the display
ALDGrappaWorkbenchTab.this.graph.refresh();
}
}
catch (ALDException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// update nodes themselves, i.e. their color
this.handleNodeStateChangeEvent(statusMap);
}
/**
* Update node color according to node's state.
* @param idList List of nodes that are to be updated.
*/
protected synchronized void handleNodeStateChangeEvent(
HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState> idList) {
Set<ALDWorkflowNodeID> nodeIDs = idList.keySet();
for (ALDWorkflowNodeID nodeID: nodeIDs) {
try {
ALDWorkflowNode wNode = this.alidaWorkflow.getNode(nodeID);
// ALDWorkflowNodeState nodeState = wNode.getState();
ALDWorkflowNodeState nodeState = idList.get(nodeID);
mxCell node = this.graphNodes.get(nodeID);
Object [] nodeToModify = new Object[]{node};
switch(nodeState)
{
case UNCONFIGURED:
this.graph.setCellStyle(getNodeStyleString("red"),nodeToModify);
break;
case CONFIGURED:
this.graph.setCellStyle(getNodeStyleString("orange"),nodeToModify);
break;
case RUNNABLE:
this.graph.setCellStyle(getNodeStyleString("yellow"),nodeToModify);
break;
case RUNNING:
this.graph.setCellStyle(getNodeStyleString("blue"),nodeToModify);
break;
case READY:
// change node color
this.graph.setCellStyle(getNodeStyleString("green"),
nodeToModify);
// check if node is a terminal node and if to display results
if (wNode.getChildren().isEmpty()) {
if ( this.popupFinalResults
|| this.nodeConfigShowResultsAtOnce.get(node).booleanValue()) {
handleShowResultsEvent(nodeID);
}
}
// check for other nodes if results are to be displayed
else {
if (this.nodeConfigShowResultsAtOnce.get(node).booleanValue()){
handleShowResultsEvent(nodeID);
}
}
break;
}
}
catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.graph.refresh();
}
/**
* Stores the workflow to the given file.
* @param file File where to save the workflow.
*/
protected synchronized void handleSaveWorkflowEvent(String file) {
ALDGrappaNodeInfo info;
// register user data object class
mxCodec enc = new mxCodec();
mxCodecRegistry.addPackage(
"de.unihalle.informatik.Alida.grappa.ALDGrappaNodeInfo");
mxCodecRegistry.register(new mxObjectCodec(new ALDGrappaNodeInfo()));
mxCodecRegistry.addPackage(
"de.unihalle.informatik.Alida.grappa.ALDGrappaNodePortInfo");
mxCodecRegistry.register(new mxObjectCodec(new ALDGrappaNodePortInfo()));
// set reference IDs of nodes for later restore
HashMap<ALDWorkflowNodeID, Integer> nodeIDMap =
this.alidaWorkflow.getMappingNodeIdToInteger();
Set<ALDWorkflowNodeID> nodeKeys = nodeIDMap.keySet();
for (ALDWorkflowNodeID nodeID: nodeKeys) {
info = (ALDGrappaNodeInfo)this.graphNodes.get(nodeID).getValue();
info.setRefID(nodeIDMap.get(nodeID));
}
// set reference IDs of edges for later restore
HashMap<ALDWorkflowEdgeID, Integer> edgeIDMap =
this.alidaWorkflow.getMappingEdgeIdToInteger();
Set<ALDWorkflowEdgeID> edgeKeys = edgeIDMap.keySet();
for (ALDWorkflowEdgeID edgeID: edgeKeys) {
info = (ALDGrappaNodeInfo)this.graphEdges.get(edgeID).getValue();
info.setRefID(edgeIDMap.get(edgeID));
}
try {
// for (Object child : this.graph.getChildCells(this.graph.getDefaultParent())) {
// mxCell cell = (mxCell) child;
// String name = cell.getValue().toString();
// Integer id = ((ALDGrappaNodeInfo)(cell.getValue())).getRefID();
// if (cell.isVertex()) {
// System.out.println("- Saving node " + name + " with id = " + id);
// cell.toString();
// }
// else {
// System.out.println("- Saving edge " + name + " with id = " + id);
// }
// }
mxUtils.writeFile(mxXmlUtils.getXml(enc.encode(this.graph.getModel())),
file + ".gui");
} catch (IOException e1) {
JOptionPane.showMessageDialog(null, "Saving workflow graphics failed!",
"Workflow Save Error", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Displays result frame for given node.
* @param nodeID Node for which results are to be displayed.
*/
protected void handleShowResultsEvent(ALDWorkflowNodeID nodeID) {
// there are results to display
ALDOperator op;
try {
op = this.alidaWorkflow.getOperator(nodeID);
ALDGrappaNodeInfo nInfo =
(ALDGrappaNodeInfo)this.graphNodes.get(nodeID).getValue();
ALDOperatorResultFrame resultFrame =
new ALDOperatorResultFrame(nInfo.getNodeName(), op,
ProviderInteractionLevel.ALL_ALLOWED);
resultFrame.setVisible(true);
} catch (ALDWorkflowException e) {
JOptionPane.showMessageDialog(null, "Cannot display results!",
"Workflow Execution Error", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Generate a new configuration window.
* <p>
* Is to be overwritten by subclasses.
*
* @param op Operator for which window is requested.
* @param pListen Parameter update listener to attach to window.
* @return New configuration window.
* @throws ALDOperatorException Thrown in case of failure.
*/
protected ALDOperatorConfigurationFrame getNewConfigWin(ALDOperator op,
ParameterUpdateListener pListen)
throws ALDOperatorException {
return new ALDOperatorConfigurationFrame(op, pListen);
}
/* *************************************************
* Static class functions, mainly internal helpers.
* *************************************************/
/**
* Method to properly format node style string.
* @param color Color of node boundary.
* @return Format string.
*/
protected static String getNodeStyleString(String color) {
return new String(
"verticalLabelPosition=top;"
+ "verticalAlign=bottom;"
+ "fontStyle=1;"
+ "strokeWidth=3.0;"
+ "strokeColor=" + color);
}
/**
* Adds the ports of the operator's parameters to the given node.
* @param op Operator linked to the node.
* @param v Node to which the ports are to be added.
* @throws ALDOperatorException Thrown in case of failure.
*/
protected void addPortsToNode(ALDOperator op, mxCell v)
throws ALDOperatorException {
// get parameters of the operator
int portCountLeft =
op.getInNames(null).size() + op.getInOutNames(null).size();
int portCountRight =
op.getOutNames().size() + op.getInOutNames(null).size();
// calculate spacing of ports on left and right side
double countIn = 1 / ( (double) (portCountLeft) + 1);
int posIn = 1;
double countOut = 1 / ((double) (portCountRight) + 1);
int posOut = 1;
// list existing ports
HashMap<String, mxICell> exPorts = new HashMap<String, mxICell>();
for (int j=0; j<v.getChildCount();++j) {
mxICell port = v.getChildAt(j);
ALDGrappaNodePortInfo pi = (ALDGrappaNodePortInfo)port.getValue();
exPorts.put(pi.getPortName(),port);
}
// build a port for each input parameter, sort first
Collection<String> sortedParams =
ALDGrappaWorkbenchTab.sortParamsByIOOrder(
op, op.getInNames(null));
for (String s : sortedParams) {
// get parameter descriptor and configure port layout
try {
ALDOpParameterDescriptor descr = op.getParameterDescriptor(s);
double portPos = countIn * posIn;
mxGeometry geo = new mxGeometry(0, portPos, 10, 10);
geo.setOffset(new mxPoint(-5, -5));
geo.setRelative(true);
// port still exists, simply shift to new position
if (exPorts.containsKey(s)) {
mxICell port = exPorts.get(s);
port.setGeometry(geo);
}
// no port there, create a new one
else {
mxCell port = new mxCell(null, geo, null);
ALDGrappaNodePortInfo portInfo =
new ALDGrappaNodePortInfo(v,descr);
port.setValue(portInfo);
// opDescr.addParameter(s, op.getParameterDescriptor(s), port);
// required parameters are blue and not required are yellow
if (descr.isRequired()) {
port.setStyle("labelPosition=right;align=left;shape=ellipse;"
+ "perimter=ellipsePerimeter");
} else if (!descr.getSupplemental().booleanValue()) {
port.setStyle("labelPosition=right;align=left;fillColor=yellow;"
+ "shape=ellipse;perimter=ellipsePerimeter");
} else {
port.setStyle("labelPosition=right;align=left;fillColor=red;"
+ "shape=ellipse;perimter=ellipsePerimeter");
}
// configure vertex and add to graph
port.setVertex(true);
// check if view mode requires port to be visible,
// by default only standard parameters are visible
if (descr.getHandlingMode() != Parameter.ExpertMode.STANDARD)
port.setVisible(false);
this.graph.addCell(port, v);
}
} catch (ALDOperatorException ex) {
System.out.println("[ALDGrappaWorkbenchTab::addNode] "
+ "problems configuring input port \"" + s + "\"...");
}
posIn++;
}
// build a port for each output parameter, sort first
sortedParams = ALDGrappaWorkbenchTab.sortParamsByIOOrder(
op, op.getOutNames());
for (String s : sortedParams) {
try {
ALDOpParameterDescriptor descr = op.getParameterDescriptor(s);
double portPos = countOut * posOut;
mxGeometry geo = new mxGeometry(1, portPos, 10, 10);
geo.setOffset(new mxPoint(-5, -5));
geo.setRelative(true);
mxCell port = new mxCell(null, geo,
"labelPosition=left;align=right;shape=ellipse;"
+ "perimter=ellipsePerimeter");
ALDGrappaNodePortInfo portInfo =
new ALDGrappaNodePortInfo(v,descr);
port.setValue(portInfo);
port.setVertex(true);
this.graph.addCell(port, v);
} catch (ALDOperatorException ex) {
System.out.println("[ALDGrappaWorkbenchTab::addNode] "
+ "problems configuring output port \"" + s + "\"...");
}
posOut++;
}
// build a port for each inout parameter, sorted first
sortedParams = ALDGrappaWorkbenchTab.sortParamsByIOOrder(
op, op.getInOutNames(null));
for (String s : sortedParams) {
try {
ALDOpParameterDescriptor descr = op.getParameterDescriptor(s);
double portPos = countIn * posIn;
mxGeometry geoLeft = new mxGeometry(1, portPos, 10, 10);
geoLeft.setOffset(new mxPoint(-5, -5));
geoLeft.setRelative(true);
mxCell portLeft =
new mxCell(null, geoLeft, "labelPosition=left;align=right");
ALDGrappaNodePortInfo portInfoLeft =
new ALDGrappaNodePortInfo(v, descr);
portLeft.setValue(portInfoLeft);
mxGeometry geoRight = new mxGeometry(0, portPos, 10, 10);
geoRight.setOffset(new mxPoint(-5, -5));
geoRight.setRelative(true);
mxCell portRight =
new mxCell(null, geoRight, "labelPosition=right;align=left");
ALDGrappaNodePortInfo portInfoRight =
new ALDGrappaNodePortInfo(v, descr);
portRight.setValue(portInfoRight);
portLeft.setVertex(true);
this.graph.addCell(portLeft, v);
portRight.setVertex(true);
this.graph.addCell(portRight, v);
} catch (ALDOperatorException ex) {
System.out.println("[ALDGrappaWorkbenchTab::addNode] "
+ "problems configuring inout port \"" + s + "\"...");
}
// increment position counter
posIn++;
}
}
/**
* Sorts a set of parameters according to category and I/O order.
* <p>
* The parameters are first split-up into required, optional and
* supplemental parameters. Subsequently each subset is sorted
* according to the parameters' data I/O order. Finally all sorted
* subsets are concatenated and returned in a single collection.
*
* @param op Operator to which the parameters belong.
* @param params Set of parameters to sort.
* @return Sorted set of parameter names.
* @throws ALDOperatorException Thrown in case of failure.
*/
private static Collection<String> sortParamsByIOOrder(ALDOperator op,
Collection<String> params) throws ALDOperatorException {
// get list of descriptors
Vector<ALDOpParameterDescriptor> descriptors =
new Vector<ALDOpParameterDescriptor>();
for (String s: params)
descriptors.add(op.getParameterDescriptor(s));
// sort descriptors according to GUI order into hash tables
HashMap<Integer, Vector<ALDOpParameterDescriptor>>
guiOrderHashRequired =
new HashMap<Integer, Vector<ALDOpParameterDescriptor>>();
HashMap<Integer, Vector<ALDOpParameterDescriptor>>
guiOrderHashOptional =
new HashMap<Integer, Vector<ALDOpParameterDescriptor>>();
HashMap<Integer, Vector<ALDOpParameterDescriptor>>
guiOrderHashSupplemental =
new HashMap<Integer, Vector<ALDOpParameterDescriptor>>();
HashMap<Integer, Vector<ALDOpParameterDescriptor>> activeHash;
for (ALDOpParameterDescriptor descr : descriptors) {
// select the right hash according to parameter category
if (descr.isRequired())
activeHash = guiOrderHashRequired;
else if (descr.getSupplemental().booleanValue())
activeHash = guiOrderHashSupplemental;
else
activeHash = guiOrderHashOptional;
// sort into bins by data IO order
Integer order = new Integer(descr.getDataIOOrder());
if (activeHash.containsKey(order)) {
activeHash.get(order).add(descr);
}
else {
Vector<ALDOpParameterDescriptor> paramVec =
new Vector<ALDOpParameterDescriptor>();
paramVec.add(descr);
activeHash.put(order, paramVec);
}
}
// sort the hashes
Collection<String> resultList = sortHash(guiOrderHashRequired);
resultList.addAll(sortHash(guiOrderHashOptional));
resultList.addAll(sortHash(guiOrderHashSupplemental));
return resultList;
}
/**
* Sorts the items for each key alpha-numerically.
* <p>
* As result the sorted lists for all hash keys are concatenated
* according to the ascending order of their key values.
*
* @param hash Input hashmap to sort.
* @return List of sorted hashmap items.
*/
private static Collection<String> sortHash(
HashMap<Integer, Vector<ALDOpParameterDescriptor>> hash) {
// sort the keys of the hashmap in ascending order
Set<Integer> keys = hash.keySet();
LinkedList<Integer> keyList = new LinkedList<Integer>();
for (Integer key : keys) {
keyList.add(key);
}
Collections.sort(keyList);
// create final result list
LinkedList<String> sortedList = new LinkedList<String>();
LinkedList<String> itemsOfCurrentOrder = new LinkedList<String>();
// sort lists of items for each key value and concatenate all lists
for (Integer n: keyList) {
itemsOfCurrentOrder.clear();
for (ALDOpParameterDescriptor d: hash.get(n)) {
itemsOfCurrentOrder.add(d.getName());
}
// sort items in alpha-numerical ordering
Collections.sort(itemsOfCurrentOrder);
// add sorted items to final result list
sortedList.addAll(itemsOfCurrentOrder);
}
return sortedList;
}
/**
* MouseListener for the workbench.
*/
protected class GraphMouseAdapter extends MouseAdapter {
// ActionListener listener;
//
// GraphMouseAdapter(ActionListener list) {
// this.listener = list;
// }
@Override
public synchronized void mousePressed(MouseEvent e) {
// left-click on background initializes node for formerly selected op
ALDOperatorLocation recentSelection =
ALDGrappaWorkbenchTab.this.workBench.popRecentlySelectedOperatorPath();
if (e.getButton() == MouseEvent.BUTTON1 && recentSelection != null) {
ALDGrappaWorkbenchTab.this.createNewWorkflowNode(recentSelection,
e.getX(), e.getY());
ALDGrappaWorkbenchTab.this.workBench.clearTreeSelection();
// try {
// buildNode(tempOpNamePath, tempOpName, e.getX(), e.getY());
// tempOpNamePath = null;
// tempOpName = null;
// // opTreePane.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
// graphComponent.getGraphHandler().DEFAULT_CURSOR = new Cursor(
// Cursor.DEFAULT_CURSOR);
// } catch (ALDOperatorException e1) {
// JOptionPane.showMessageDialog(null, "Building node \""
// + opTree.getLastSelectedPathComponent() + "\" failed!" + " Reason: \n"
// + e1.getCommentString(), "Warning",
// JOptionPane.OK_CANCEL_OPTION);
// }
// }
}
// double-click on node opens control frame
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount()==2) {
mxCell cell =
(mxCell) ALDGrappaWorkbenchTab.this.getCellAt(e.getX(),e.getY());
// double-clicks on background are ignored
if (cell != null) {
if (cell.isVertex() && !cell.isConnectable()) {
ALDOperatorConfigurationFrame frame =
ALDGrappaWorkbenchTab.this.configWindows.get(cell);
frame.setVisible(true);
}
}
}
else if (e.getButton() == MouseEvent.BUTTON1) {
// System.out.println("Got left click, edge draw...");
// mxCell cell = (mxCell) ALDGrappaWorkbenchTab.this.getCellAt(
// e.getX(),e.getY());
//// System.out.println(graphComponent.getGraphHandler().isMoveEnabled());
// if (cell == null)
// return;
// if (cell.isEdge()) {
// System.out.println("... related to an edge");
//// String linkSource = cell.getSource().getParent().getValue()
//// .toString()
//// + "@"
//// + ((ALDPort) cell.getSource().getValue()).getParName();
//// String linkTarget = cell.getTarget().getParent().getValue()
//// .toString()
//// + "@"
//// + ((ALDPort) cell.getTarget().getValue()).getParName();
//// mxCell targetNode = (mxCell)cell.getTarget().getParent();
//// System.out.println(linkSource);
//// System.out.println(linkTarget);
// }
}
// on right-click open context menu
else if (e.getButton() == MouseEvent.BUTTON3) {
mxCell cell =
(mxCell) ALDGrappaWorkbenchTab.this.getCellAt(e.getX(), e.getY());
if (cell != null) {
if (cell.isVertex() && !cell.isConnectable() || cell.isEdge()) {
ContextMenuNodeEdge menu = new ContextMenuNodeEdge(e.getX(), e.getY());
menu.show(e.getComponent(), e.getX(), e.getY());
} else {
// formerly the following code was for switching ports to text mode
// ALDOperatorDescriptor opDescr = opDescrMap.get(cell
// .getParent());
// ALDOpParameterDescriptor descr = opDescr
// .getParameterDescriptor(((ALDPort) cell
// .getValue()).getParName());
// String link = cell.getParent().getValue().toString()
// + "@"
// + ((ALDPort) cell.getValue()).getParName();
// if (descr.getDirection() == Direction.IN
// || descr.getDirection() == Direction.INOUT) {
// if (descr.isLink())
// descr.setLink(false);
// else
// descr.setLink(true);
// opDescr.getOperatorControlFrame().changePanel(link,
// null);
// opDescr.getOperatorControlFrame().setVisible(true);
// } else {
// JOptionPane.showMessageDialog(null,
// "This parameter is an output!", "Warning",
// JOptionPane.OK_CANCEL_OPTION);
// }
}
}
// right-click on empty space of workbench, open graph context menu
else {
ContextMenuGraph contextMenu= new ContextMenuGraph();
contextMenu.show(ALDGrappaWorkbenchTab.this, e.getX(), e.getY());
}
}
}
}
protected class GraphKeyListener implements KeyListener {
@SuppressWarnings("synthetic-access")
@Override
public void keyPressed(KeyEvent e) {
// ignore all types if control key is not down
if (!e.isControlDown())
return;
// process the key press
switch(e.getKeyCode())
{
// case KeyEvent.VK_A:
// // execute the whole workflow
// ALDGrappaWorkbenchTab.this.runWorkflow();
// break;
case KeyEvent.VK_C:
System.out.println("Copying node...");
// if (selectedCells.length == 1) {
// mxCell selectedCell = (mxCell)selectedCells[0];
// }
break;
case KeyEvent.VK_K:
System.out.println("Stopping execution...");
// if (selectedCells.length == 1) {
// mxCell selectedCell = (mxCell)selectedCells[0];
// }
break;
// case KeyEvent.VK_L:
// // load workflow from file
// ALDGrappaWorkbenchTab.this.workBench.loadWorkflow();
// break;
// case KeyEvent.VK_N:
// // add new workflow
// ALDGrappaWorkbenchTab.this.workBench.addNewWorkflow();
// break;
case KeyEvent.VK_P:
// configure selected nodes
for (Object cell :
ALDGrappaWorkbenchTab.this.graph.getChildVertices(
ALDGrappaWorkbenchTab.this.graph.getDefaultParent())) {
if (ALDGrappaWorkbenchTab.this.graph.isCellSelected(cell)) {
ALDOperatorConfigurationFrame frame =
ALDGrappaWorkbenchTab.this.configWindows.get(cell);
frame.setVisible(true);
}
}
break;
case KeyEvent.VK_R:
// execute the selected node
Object selectedCells [] =
ALDGrappaWorkbenchTab.this.graph.getSelectionCells();
if (selectedCells.length == 1) {
mxCell selectedCell = (mxCell)selectedCells[0];
if (selectedCell.isVertex())
ALDGrappaWorkbenchTab.this.runWorkflowNode(
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(selectedCell));
else
JOptionPane.showMessageDialog(ALDGrappaWorkbenchTab.this,
"You did not select a valid node,\n " +
"please check your selection...",
"Node selection problem", JOptionPane.WARNING_MESSAGE);
}
else {
JOptionPane.showMessageDialog(ALDGrappaWorkbenchTab.this,
"You selected more or less than one node,\n " +
"don't know which one to run...?!",
"Node selection problem", JOptionPane.WARNING_MESSAGE);
}
break;
// case KeyEvent.VK_S:
// ALDGrappaWorkbenchTab.this.workBench.saveWorkflow();
// break;
// case KeyEvent.VK_U:
// ALDGrappaWorkbenchTab.this.workBench.renameWorkflow();
// break;
case KeyEvent.VK_V:
System.out.println("Pasting node...");
break;
// case KeyEvent.VK_W:
// // close workflow
// ALDGrappaWorkbenchTab.this.workBench.removeWorkflow();
// break;
case KeyEvent.VK_X:
// remove selected nodes
for (Object cell :
ALDGrappaWorkbenchTab.this.graph.getChildVertices(
ALDGrappaWorkbenchTab.this.graph.getDefaultParent())) {
if (ALDGrappaWorkbenchTab.this.graph.isCellSelected(cell))
ALDGrappaWorkbenchTab.this.removeWorkflowNode((mxCell)cell);
}
for (Object cell :
ALDGrappaWorkbenchTab.this.graph.getChildEdges(
ALDGrappaWorkbenchTab.this.graph.getDefaultParent())) {
if (ALDGrappaWorkbenchTab.this.graph.isCellSelected(cell))
ALDGrappaWorkbenchTab.this.removeWorkflowEdge((mxCell)cell);
}
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
// nothing to do here for the moment...
}
@Override
public void keyTyped(KeyEvent e) {
// nothing to do here for the moment...
}
}
/**
* Event handler for mxGraph events.
*/
protected class GraphEventListener implements mxIEventListener {
// private HashMap<mxCell, mxICell> edgeSources= new HashMap<mxCell, mxICell>();
//
// private HashMap<mxCell, mxICell> edgeTargets= new HashMap<mxCell, mxICell>();
//
@Override
public synchronized void invoke(Object sender, mxEventObject event) {
// init some local helper variables
String eventName = event.getName();
if ( ALDGrappaWorkbenchTab.this.debug ) {
System.out.println("Got event = " + eventName);
}
// connect events indicate addition/modification of edges
mxCell edge = null;
if (eventName.equals("connect")) {
edge = (mxCell) event.getProperty("cell");
}
else if (eventName.equals("connectCell")) {
edge = (mxCell) event.getProperty("edge");
}
if (edge == null || !edge.isEdge()) {
// nothing to do, if event is not related to an edge...
return;
}
// get source and target nodes as defined in mxGraph model
mxICell sourcePort = edge.getSource();
mxICell sourceNode = edge.getSource().getParent();
mxICell targetPort = edge.getTarget();
mxICell targetNode = edge.getTarget().getParent();
ALDWorkflowNodeID sourceNodeID =
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(sourceNode);
ALDWorkflowNodeID targetNodeID =
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(targetNode);
// check if edge is known already
ALDWorkflowEdgeID id =
ALDGrappaWorkbenchTab.this.graphEdgeIDs.get(edge);
// redirect event, edge is known already
if (id != null) {
ALDGrappaWorkbenchTab.this.redirectWorkflowEdge(edge, id,
sourceNodeID, targetNodeID,
(ALDGrappaNodePortInfo)sourcePort.getValue(),
(ALDGrappaNodePortInfo)targetPort.getValue());
}
// add new edge
else {
ALDGrappaWorkbenchTab.this.createNewWorkflowEdge(edge,
// sourceNodeID, (String)sourcePort.getValue(),
// targetNodeID, (String)targetPort.getValue());
sourceNodeID, (ALDGrappaNodePortInfo)sourcePort.getValue(),
targetNodeID, (ALDGrappaNodePortInfo)targetPort.getValue());
// ALDOperatorDescriptor opDescrTarget = opDescrMap.get(cell
// .getTarget().getParent());
// ALDOpParameterDescriptor descr = opDescrTarget
// .getParameterDescriptor(((ALDPort) cell.getTarget()
// .getValue()).getParName());
//
// // if port is already linked to another input, abort operation
// if (descr.isLink()) {
//// System.out.println("Port is already linked...");
// mxICell target = this.edgeTargets.get(cell);
// if (target != null) {
// ALDOperatorDescriptor tdescr = opDescrMap.get(target.getParent());
// ALDOpParameterDescriptor tpdescr = tdescr
// .getParameterDescriptor(((ALDPort) target.getValue()).getParName());
// tpdescr.setLink(false);
// // TODO HACK!!!! Eigentlich sollte dieses explizite Linkumschalten
// // unnötig sein... irgendwo werden Referenzen ausgetauscht?!
// tdescr.getOperatorControlFrame().setDescriptorLinked(target.getParent().getValue().toString() + "@" + ((ALDPort)target.getValue()).getParName(),false);
// tdescr.getOperatorControlFrame().changePanel(target.getParent().getValue().toString() + "@" + ((ALDPort)target.getValue()).getParName(),null);
// }
// opGraph.getModel().remove(cell);
// this.edgeSources.remove(cell);
// this.edgeTargets.remove(cell);
// return;
// }
//
// // if workflow graph is cyclic, abort operation
// if (ALDEditorFrame.this.opGraph.getAllNodesTopologicallyOrdered()==null){
//// System.out.println("Cyclic graph created...");
//// System.out.println("cell: " + cell.toString() + " , source = " + cell.getSource().toString() + " , target = " + cell.getTarget());
// opGraph.getModel().remove(cell);
// this.edgeSources.remove(cell);
// this.edgeTargets.remove(cell);
// return;
// }
//
// String linkSource = cell.getSource().getParent().getValue()
// .toString()
// + "@"
// + ((ALDPort) cell.getSource().getValue()).getParName();
// String linkTarget = cell.getTarget().getParent().getValue()
// .toString()
// + "@"
// + ((ALDPort) cell.getTarget().getValue()).getParName();
// mxCell targetNode = (mxCell)cell.getTarget().getParent();
// if (validateLink(linkSource, linkTarget)) {
//// System.out.println("Validate successful...");
// descr.setLink(true);
// opDescrTarget.getOperatorControlFrame().setDescriptorLinked(linkTarget,true);
// opDescrTarget.getOperatorControlFrame().changePanel(linkTarget,
// linkSource);
// opDescrMap.get(cell.getSource().getParent()).addChild(
// cell.getTarget().getParent().getValue().toString());
// // modify old target operator port
// mxICell target = null;
// if ((target = this.edgeTargets.get(cell)) != null) {
// ALDOperatorDescriptor tdescr = opDescrMap.get(target.getParent());
// ALDOpParameterDescriptor tpdescr = tdescr
// .getParameterDescriptor(((ALDPort) target.getValue()).getParName());
// tpdescr.setLink(false);
// // TODO HACK!!!! Eigentlich sollte dieses explizite Linkumschalten
// // unnötig sein... irgendwo werden Referenzen ausgetauscht?!
// tdescr.getOperatorControlFrame().setDescriptorLinked(target.getParent().getValue().toString() + "@" + ((ALDPort)target.getValue()).getParName(),false);
// tdescr.getOperatorControlFrame().changePanel(target.getParent().getValue().toString() + "@" + ((ALDPort)target.getValue()).getParName(),null);
// }
// // memorize edge
// this.edgeSources.put(cell, cell.getSource());
// this.edgeTargets.put(cell, cell.getTarget());
//// System.out.println("cell: " + cell.toString() + " , source = " + cell.getSource().toString() + " , target = " + cell.getTarget());
// }
// else {
//// System.out.println("Validate failed...");
// if (this.edgeTargets.get(cell) == null) {
//// System.out.println("cell: " + cell.toString() + " , source = " + cell.getSource().toString() + " , target = " + cell.getTarget());
//// System.out.println("Edge was not linked before...");
// opGraph.getModel().remove(cell);
// this.edgeSources.remove(cell);
// this.edgeTargets.remove(cell);
// }
// else {
// mxICell target = this.edgeTargets.get(cell);
// ALDOperatorDescriptor tdescr = opDescrMap.get(target.getParent());
// ALDOpParameterDescriptor tpdescr = tdescr
// .getParameterDescriptor(((ALDPort) target.getValue()).getParName());
// tpdescr.setLink(false);
// // TODO HACK!!!! Eigentlich sollte dieses explizite Linkumschalten
// // unnötig sein... irgendwo werden Referenzen ausgetauscht?!
// tdescr.getOperatorControlFrame().setDescriptorLinked(target.getParent().getValue().toString() + "@" + ((ALDPort)target.getValue()).getParName(),false);
// tdescr.getOperatorControlFrame().changePanel(target.getParent().getValue().toString() + "@" + ((ALDPort)target.getValue()).getParName(),null);
//// System.out.println("Edge was linked before...");
//// System.out.println("cell: " + cell.toString() + " , source = " + cell.getSource().toString() + " , target = " + cell.getTarget());
// opGraph.getModel().remove(cell);
// this.edgeSources.remove(cell);
// this.edgeTargets.remove(cell);
// // cell.setTarget(this.edgeTargets.get(cell));
// // opGraph.insertEdge(null, null, null, this.edgeSources.get(cell), this.edgeTargets.get(cell));
// // System.out.println("cell: " + cell.toString() + " , source = " + cell.getSource().toString() + " , target = " + cell.getTarget());
// }
// }
// // change node color
// if (ALDEditorFrame.this.opDescrMap.get(targetNode).getOperatorControlFrame().validateParameters(false, false)) {
// ALDEditorFrame.this.setToConfigured(targetNode.getValue().toString(),true);
// }
// else {
// ALDEditorFrame.this.setToConfigured(targetNode.getValue().toString(),false);
// }
}
}
}
/**
* Class to handle parameter update events triggered configuration windows.
* @author moeller
*/
protected class ParameterUpdateListener
implements ALDOpParameterUpdateEventListener {
/**
* ID of the corresponding Grappa node.
*/
private ALDWorkflowNodeID id;
/**
* Default constructor.
* @param nodeID ID of node attached to this listener object.
*/
public ParameterUpdateListener(ALDWorkflowNodeID nodeID) {
this.id = nodeID;
}
@Override
public void handleALDParameterUpdateEvent(ALDOpParameterUpdateEvent e) {
// notify workflow of change in node parameters
try {
if ( ALDGrappaWorkbenchTab.this.debug ) {
System.out.println("Parameters of node " + this.id
+ " were updated, i.e., changed or reloaded...");
}
switch(e.getType())
{
case CHANGED:
ALDGrappaWorkbenchTab.this.alidaWorkflow.nodeParameterChanged(
this.id);
ALDGrappaWorkbenchTab.this.processWorkflowEventQueue();
break;
case LOADED:
ALDGrappaWorkbenchTab.this.alidaWorkflow.setOperator( this.id,
ALDGrappaWorkbenchTab.this.configWindows.get(
ALDGrappaWorkbenchTab.this.graphNodes.get(this.id)).
getOperator());
break;
}
// process event queue
ALDGrappaWorkbenchTab.this.processWorkflowEventQueue();
} catch (ALDWorkflowException ex) {
System.err.println("[ParameterChangeListener] Warning! "
+ "could not propagate parameter change event, node not found!");
}
}
}
protected class WorkflowModifyAction {
public ALDWorkflowNodeID oldEdgeTarget;
public String oldEdgeTargetParamName;
/**
* X-coordinate of the working area where action happened.
*/
protected int actionPosition_x;
/**
* Y-coordinate of the working area where action happened.
*/
protected int actionPosition_y;
/**
* Default constructor.
*/
public WorkflowModifyAction() {
// nothing to do here
}
/**
* Set x-coordinate of action's position.
* @param x X-coordinate where action happened.
*/
public void setActionPositionX(int x) {
this.actionPosition_x = x;
}
/**
* Set y-coordinate of action's position.
* @param y Y-coordinate where action happened.
*/
public void setActionPositionY(int y) {
this.actionPosition_y = y;
}
/**
* Returns the x-coordinate of the action's position.
* @return x-coordinate of position.
*/
public int getActionPositionX() {
return this.actionPosition_x;
}
/**
* Returns the y-coordinate of the action's position.
* @return y-coordinate of position.
*/
public int getActionPositionY() {
return this.actionPosition_y;
}
}
/**
* Context menu for nodes and edges within a workflow.
*/
protected class ContextMenuNodeEdge extends JPopupMenu
implements ActionListener {
/**
* Graph object to which the menu is to be attached.
* <p>
* The object can either be a graph node or an edge. Depending on
* its type the menu shows different contents.
*/
private mxCell cell;
/**
* Checkbox for selecting which set of parameters and ports, to show.
* <p>
* Either all parameters of an operator can be shown, which is
* defined to be the expert mode, or only a subset of parameters
* dedicated to non-expert usage can be shown. If the checkbox is
* selected, all parameters are shown.
*/
private JCheckBoxMenuItem parameterShowModeItem;
/**
* Checkbox for enabling/disabling immediate display of results.
*/
private JCheckBoxMenuItem resultsShowImmediateItem;
/**
* Default constructor for context menu.
* @param x X-coordinate of mouse-click position.
* @param y Y-coordinate of mouse-click position.
*/
protected ContextMenuNodeEdge(int x, int y) {
this.cell = (mxCell)ALDGrappaWorkbenchTab.this.getCellAt(x, y);
// context menu for nodes
if (this.cell.isVertex()) {
JMenuItem confItem = new JMenuItem("Configure");
confItem.setActionCommand("configure");
confItem.addActionListener(this);
confItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_P, ActionEvent.CTRL_MASK));
JMenu runMenu = new JMenu("Run...");
JMenuItem runFlowItem = new JMenuItem("Workflow");
runFlowItem.setActionCommand("runFlow");
runFlowItem.addActionListener(this);
runFlowItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_A, ActionEvent.CTRL_MASK));
JMenuItem runUptoItem = new JMenuItem("Node");
runUptoItem.setActionCommand("runNode");
runUptoItem.addActionListener(this);
runUptoItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_R, ActionEvent.CTRL_MASK));
JMenuItem runFromItem = new JMenuItem("Nodes from here");
runFromItem.setActionCommand("runFromHere");
runFromItem.addActionListener(this);
// JMenuItem runItem2 = new JMenuItem("without backtracking");
// runItem2.setActionCommand("run2");
// runItem2.addActionListener(this);
// runFromMenu.add(runItem1);
// runFromMenu.add(runItem2);
// JMenu runNodeMenu = new JMenu("node");
// JMenuItem runNodeItem1 = new JMenuItem("with backtracking");
// runNodeItem1.setActionCommand("runNode1");
// runNodeItem1.addActionListener(this);
// JMenuItem runNodeItem2 = new JMenuItem("without backtracking");
// runNodeItem2.setActionCommand("runNode2");
// runNodeItem2.addActionListener(this);
// runNodeMenu.add(runNodeItem1);
// runNodeMenu.add(runNodeItem2);
runMenu.add(runFlowItem);
runMenu.add(runFromItem);
runMenu.add(runUptoItem);
JMenuItem stopItem = new JMenuItem("Stop");
stopItem.setActionCommand("stop");
stopItem.addActionListener(this);
stopItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_K, ActionEvent.CTRL_MASK));
JMenuItem showItem = new JMenuItem("Show Results");
showItem.setActionCommand("show");
showItem.addActionListener(this);
if (!this.cell.getStyle().contains("strokeColor=green")) {
showItem.setEnabled(false);
}
JMenuItem removeItem = new JMenuItem("Delete Node");
removeItem.setActionCommand("removeNode");
removeItem.addActionListener(this);
removeItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_X, ActionEvent.CTRL_MASK));
JMenu copyNodeMenu = new JMenu("Copy node...");
JMenuItem copyComplete = new JMenuItem("config + links");
copyComplete.setActionCommand("copyNodeComplete");
copyComplete.addActionListener(this);
copyComplete.setEnabled(false);
copyComplete.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_C, ActionEvent.CTRL_MASK));
JMenuItem copyConfigOnly = new JMenuItem("only config");
copyConfigOnly.setActionCommand("copyNodeOnlyConfig");
copyConfigOnly.addActionListener(this);
copyConfigOnly.setEnabled(false);
JMenuItem copyLinksOnly = new JMenuItem("... links only");
copyLinksOnly.setActionCommand("copyNodeOnlyLinks");
copyLinksOnly.addActionListener(this);
copyNodeMenu.add(copyComplete);
copyNodeMenu.add(copyConfigOnly);
copyNodeMenu.add(copyLinksOnly);
JMenu viewMenu = new JMenu("Options");
// request the view mode of the node
Boolean selected = ALDGrappaWorkbenchTab.this.
nodeConfigShowAllParameters.get(this.cell);
this.parameterShowModeItem =
new JCheckBoxMenuItem("Show All Parameters",
selected.booleanValue());
this.parameterShowModeItem.setActionCommand(
"viewModeChanged");
this.parameterShowModeItem.addActionListener(this);
viewMenu.add(this.parameterShowModeItem);
selected = ALDGrappaWorkbenchTab.this.
nodeConfigShowResultsAtOnce.get(this.cell);
this.resultsShowImmediateItem =
new JCheckBoxMenuItem("Show Results At Once",
selected.booleanValue());
this.resultsShowImmediateItem.setActionCommand(
"resultShowModeChanged");
this.resultsShowImmediateItem.addActionListener(this);
viewMenu.add(this.resultsShowImmediateItem);
this.add(confItem);
this.add(runMenu);
this.add(showItem);
this.addSeparator();
this.add(viewMenu);
// this.add(copyNodeMenu);
this.addSeparator();
this.add(removeItem);
this.add(copyNodeMenu);
}
// context menu for edges
else {
JMenuItem removeItem = new JMenuItem("Remove edge");
removeItem.setActionCommand("removeEdge");
removeItem.addActionListener(this);
removeItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_X, ActionEvent.CTRL_MASK));
this.add(removeItem);
}
}
/* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public synchronized void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("configure")) {
ALDOperatorConfigurationFrame frame =
ALDGrappaWorkbenchTab.this.configWindows.get(this.cell);
frame.setVisible(true);
}
else if (command.equals("runFlow")) {
ALDGrappaWorkbenchTab.this.runWorkflow();
}
else if (command.equals("runNode")) {
ALDGrappaWorkbenchTab.this.runWorkflowNode(
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(this.cell));
}
else if (command.equals("runFromHere")) {
ALDGrappaWorkbenchTab.this.runWorkflowFromNode(
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(this.cell));
}
else if (command.equals("stop")) {
ALDGrappaWorkbenchTab.this.interruptExecution();
}
else if (command.equals("show")) {
ALDWorkflowNodeID nodeID =
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(this.cell);
ALDGrappaWorkbenchTab.this.handleShowResultsEvent(nodeID);
}
else if (command.equals("viewModeChanged")) {
if (this.parameterShowModeItem.isSelected()) {
// display all parameters
ALDGrappaWorkbenchTab.this.setWorkflowNodeViewMode(
this.cell, Parameter.ExpertMode.ADVANCED);
} else {
// display only default (non-expert) parameters
ALDGrappaWorkbenchTab.this.setWorkflowNodeViewMode(
this.cell, Parameter.ExpertMode.STANDARD);
}
}
else if (command.equals("resultShowModeChanged")) {
if (this.resultsShowImmediateItem.isSelected())
ALDGrappaWorkbenchTab.this.
nodeConfigShowResultsAtOnce.put(this.cell,
new Boolean(true));
else
ALDGrappaWorkbenchTab.this.
nodeConfigShowResultsAtOnce.put(this.cell,
new Boolean(false));
}
// copy node including links and configuration
else if (command.equals("copyNodeComplete")) {
}
// copy node, but ignore links
else if (command.equals("copyNodeOnlyConfig")) {
// try {
// mxCell copy = buildNode(cell.getId().split("@")[0], cell
// .getValue().toString().split("_")[0], cell
// .getGeometry().getX() + 20, cell.getGeometry()
// .getY() + 20);
// for (int i = 0; i < cell.getChildCount(); i++) {
// Object[] inEdges = opGraph.getIncomingEdges(cell
// .getChildAt(i));
// if (inEdges.length != 0) {
// ALDOperatorDescriptor opDescrTarget = opDescrMap
// .get(copy);
// ALDOpParameterDescriptor descr = opDescrTarget
// .getParameterDescriptor(((ALDPort) copy
// .getChildAt(i).getValue())
// .getParName());
// String linkSource = ((mxCell) inEdges[0])
// .getSource().getParent().getValue()
// .toString()
// + "@"
// + ((ALDPort) ((mxCell) inEdges[0])
// .getSource().getValue())
// .getParName();
// String linkTarget = copy.getValue().toString()
// + "@"
// + ((ALDPort) cell.getChildAt(i).getValue())
// .getParName();
// descr.setLink(true);
// opDescrTarget.getOperatorControlFrame()
// .changePanel(linkTarget, linkSource);
// opDescrTarget.getOperatorControlFrame().setDescriptorLinked(linkTarget,true);
// drawEdge(linkSource, linkTarget);
// }
// }
// opGraph.refresh();
// } catch (ALDOperatorException e1) {
// JOptionPane.showMessageDialog(null, "Copying node \""
// + cell.getValue().toString() + "\" failed!"
// + " Reason: \n" + e1.getCommentString(), "Warning",
// JOptionPane.OK_CANCEL_OPTION);
// }
}
// copy node, but omit configuration and consider only links
else if (command.equals("copyNodeOnlyLinks")) {
try {
ALDOperator srcOp;
srcOp = ALDGrappaWorkbenchTab.this.alidaWorkflow.getOperator(
ALDGrappaWorkbenchTab.this.graphNodeIDs.get(this.cell));
ALDOperator newOp = srcOp.getClass().newInstance();
ALDGrappaWorkbenchTab.this.copyWorkflowNodeLinksOnly(
srcOp, this.cell, newOp, -1, -1);
} catch (ALDWorkflowException e1) {
e1.printStackTrace();
}
// try {
// buildNode(cell.getId().split("@")[0], cell.getValue()
// .toString().split("_")[0], cell.getGeometry()
// .getX() + 20, cell.getGeometry().getY() + 20);
// } catch (ALDOperatorException e1) {
// JOptionPane.showMessageDialog(null, "Copying node \""
// + cell.getValue().toString() + "\" failed!"
// + " Reason: \n" + e1.getCommentString(), "Warning",
// JOptionPane.OK_CANCEL_OPTION);
// }
catch (InstantiationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// try {
// mxCell copy = buildNode(cell.getId().split("@")[0], cell
// .getValue().toString().split("_")[0], cell
// .getGeometry().getX() + 20, cell.getGeometry()
// .getY() + 20);
//
// ALDOperatorDescriptor opDescr1 = opDescrMap.get(cell);
// ALDOperatorDescriptor opDescr2 = opDescrMap.get(copy);
//
// opDescr1.getOperatorControlFrame().copyConfiguration(
// opDescr2.getOperatorControlFrame());
//
// for (int i = 0; i < cell.getChildCount(); i++) {
// Object[] inEdges = opGraph.getIncomingEdges(cell
// .getChildAt(i));
// if (inEdges.length != 0) {
// ALDOperatorDescriptor opDescrTarget = opDescrMap
// .get(copy);
// ALDOpParameterDescriptor descr = opDescrTarget
// .getParameterDescriptor(((ALDPort) copy
// .getChildAt(i).getValue())
// .getParName());
// String linkSource = ((mxCell) inEdges[0])
// .getSource().getParent().getValue()
// .toString()
// + "@"
// + ((ALDPort) ((mxCell) inEdges[0])
// .getSource().getValue())
// .getParName();
// String linkTarget = copy.getValue().toString()
// + "@"
// + ((ALDPort) cell.getChildAt(i).getValue())
// .getParName();
// descr.setLink(true);
// opDescrTarget.getOperatorControlFrame().setDescriptorLinked(linkTarget,true);
// opDescrTarget.getOperatorControlFrame()
// .changePanel(linkTarget, linkSource);
// drawEdge(linkSource, linkTarget);
// }
// }
// opGraph.refresh();
// } catch (ALDOperatorException e1) {
// JOptionPane.showMessageDialog(null, "Copying node \""
// + cell.getValue().toString() + "\" failed!"
// + " Reason: \n" + e1.getCommentString(), "Warning",
// JOptionPane.OK_CANCEL_OPTION);
// } catch (ALDDataIOException e2) {
// // TODO Auto-generated catch block
// e2.printStackTrace();
// }
}
else if (command.equals("removeNode")) {
ALDGrappaWorkbenchTab.this.removeWorkflowNode(this.cell);
}
else if (command.equals("removeEdge")) {
ALDGrappaWorkbenchTab.this.removeWorkflowEdge(this.cell);
}
}
}
/**
* Context menu for whole workbench.
* <p>
* This menu is activated on right-click on background.
*/
protected class ContextMenuGraph extends JPopupMenu
implements ActionListener {
/**
* Default constructor.
*/
protected ContextMenuGraph() {
JMenuItem newItem = new JMenuItem("New");
newItem.setActionCommand("new");
newItem.addActionListener(this);
// newItem.setAccelerator(KeyStroke.getKeyStroke(
// KeyEvent.VK_N, ActionEvent.CTRL_MASK));
JMenuItem closeItem = new JMenuItem("Close");
closeItem.setActionCommand("close");
closeItem.addActionListener(this);
// closeItem.setAccelerator(KeyStroke.getKeyStroke(
// KeyEvent.VK_W, ActionEvent.CTRL_MASK));
JMenuItem loadItem = new JMenuItem("Load");
loadItem.setActionCommand("load");
loadItem.addActionListener(this);
// loadItem.setAccelerator(KeyStroke.getKeyStroke(
// KeyEvent.VK_L, ActionEvent.CTRL_MASK));
JMenuItem saveItem = new JMenuItem("Save");
saveItem.setActionCommand("save");
saveItem.addActionListener(this);
// saveItem.setAccelerator(KeyStroke.getKeyStroke(
// KeyEvent.VK_S, ActionEvent.CTRL_MASK));
JMenuItem renameItem = new JMenuItem("Rename");
renameItem.setActionCommand("rename");
renameItem.addActionListener(this);
// renameItem.setAccelerator(KeyStroke.getKeyStroke(
// KeyEvent.VK_U, ActionEvent.CTRL_MASK));
JMenuItem runItem = new JMenuItem("Run");
runItem.setActionCommand("run");
runItem.addActionListener(this);
// runItem.setAccelerator(KeyStroke.getKeyStroke(
// KeyEvent.VK_A, ActionEvent.CTRL_MASK));
JMenuItem stopItem = new JMenuItem("Stop");
stopItem.setActionCommand("stop");
stopItem.addActionListener(this);
// stopItem.setAccelerator(KeyStroke.getKeyStroke(
// KeyEvent.VK_K, ActionEvent.CTRL_MASK));
JMenu optionsMenu = new JMenu("Options");
// flag to enable automatic display of results at the end
boolean flag = ALDGrappaWorkbenchTab.this.popupFinalResults;
JMenuItem displayFinalResultsItem =
new JCheckBoxMenuItem("Pop-up final results", flag);
displayFinalResultsItem.setActionCommand("popupFinalResults");
displayFinalResultsItem.addActionListener(this);
optionsMenu.add(displayFinalResultsItem);
this.add(newItem);
this.add(renameItem);
this.add(closeItem);
this.addSeparator();
this.add(loadItem);
this.add(saveItem);
this.addSeparator();
this.add(runItem);
this.add(stopItem);
this.addSeparator();
this.add(optionsMenu);
}
/* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("new")) {
ALDGrappaWorkbenchTab.this.workBench.addNewWorkflow();
}
else if (command.equals("rename")) {
ALDGrappaWorkbenchTab.this.workBench.renameWorkflow();
}
else if (command.equals("close")) {
ALDGrappaWorkbenchTab.this.workBench.removeWorkflow();
}
else if (command.equals("load")) {
ALDGrappaWorkbenchTab.this.workBench.loadWorkflow();
}
else if (command.equals("save")) {
ALDGrappaWorkbenchTab.this.workBench.saveWorkflow();
}
else if (command.equals("run")) {
ALDGrappaWorkbenchTab.this.runWorkflow();
}
else if (command.equals("stop")) {
ALDGrappaWorkbenchTab.this.interruptExecution();
}
else if (command.equals("popupFinalResults")) {
if (((JCheckBoxMenuItem)e.getSource()).isSelected())
ALDGrappaWorkbenchTab.this.popupFinalResults = true;
else
ALDGrappaWorkbenchTab.this.popupFinalResults = false;
}
}
}
}