/* * 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.gui; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; import java.util.concurrent.BlockingDeque; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import de.unihalle.informatik.Alida.batch.ALDBatchRunResultInfo; import de.unihalle.informatik.Alida.batch.ALDBatchOutputManagerSwing.ProviderInteractionLevel; import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerSwing; import de.unihalle.informatik.Alida.exceptions.ALDBatchIOException; import de.unihalle.informatik.Alida.exceptions.ALDDataIOException; 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.operator.ALDOperator; import de.unihalle.informatik.Alida.operator.ALDOperatorControllable; import de.unihalle.informatik.Alida.operator.ALDOperatorLocation; import de.unihalle.informatik.Alida.operator.events.*; import de.unihalle.informatik.Alida.operator.events.ALDControlEvent.ALDControlEventType; 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.ALDWorkflowNodeID; import de.unihalle.informatik.Alida.workflows.ALDWorkflowNode.ALDWorkflowNodeState; import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEvent; import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowRunFailureInfo; import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEvent.*; import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEventListener; /** * Manager for executing single operator and workflow objects via GUI. * * @author Birgit Moeller */ public class ALDOperatorGUIExecutionProxy implements ALDWorkflowEventListener, ActionListener { /* *********************************** * Some local data type declarations. * ***********************************/ /** * Status of the associated thread to execute operator. */ protected static enum WorkflowThreadStatus { /** * Thread is ready for execution. */ THREAD_READY, /** * Thread is running. */ THREAD_RUNNING, /** * Thread is paused. */ THREAD_PAUSED, /** * Thread was interrupted. */ THREAD_INTERRUPTED, /** * Thread is not running. */ THREAD_STOPPED } /** * Labels to be used on buttons of Yes/No message boxes. */ protected final Object[] yesnoOption = { "YES", "NO" }; /* ****************** * Internal members. * ******************/ /** * Reference to the underlying Alida workflow object. */ protected ALDWorkflow alidaWorkflow; /** * Current status of the operator execution thread. */ protected WorkflowThreadStatus workflowStatus = WorkflowThreadStatus.THREAD_READY; /** * Corresponding configuration and control window. */ protected ALDOperatorControlFrame controlWin; /** * Corresponding configuration and control window. */ protected JFrame failureMessageWin; /** * Listener object attached to the control window. */ protected ParameterUpdateListener paramUpdateListener; /** * Reference ID of the operator node in Alida workflow; */ protected ALDWorkflowNodeID operatorNodeID; /** * Frame showing operator execution results. */ protected ALDOperatorResultFrame resultFrame; /** * Flag to indicate if the is an abortion currently going on. */ // protected boolean processEvents = true; /* *********************** * Batch mode ingredients. * ***********************/ /** * Flag to indicate if batch mode is active. */ protected boolean batchModeActive; /** * Name of batch mode input parameter. */ protected String batchInputParameter; /** * Iterator for batch mode input parameter. */ protected Iterator<Object> batchInputIterator; /** * List of batch mode output parameters. */ protected LinkedList<String> batchOutputParameter; /** * Batch mode result objects. * <p> * The key of the hashmap is corresponding to the names of the output * parameters, the values are the result data objects. */ protected HashMap<String, ALDBatchRunResultInfo> batchOutputResultMap; /** * Default constructor. * @param opLocation Info from where to instantiate the operator object. */ public ALDOperatorGUIExecutionProxy(ALDOperatorLocation opLocation) { // init workflow try { this.alidaWorkflow = new ALDWorkflow(" ", ALDWorkflowContextType.OP_RUNNER); this.alidaWorkflow.addALDWorkflowEventListener(this); } catch (ALDOperatorException e) { JOptionPane.showMessageDialog(null, "[ALDOperatorGUIExecutionProxy] " + "Instantiation of workflow failed!\n", "Error", JOptionPane.ERROR_MESSAGE); } // init the operator, i.e. add a single node to the flow try { this.operatorNodeID = this.alidaWorkflow.createNode(opLocation); } catch (ALDWorkflowException ex) { JOptionPane.showMessageDialog(null, "Instantiation of operator node \"" + opLocation.getName() + "\" failed!\n", "Error", JOptionPane.ERROR_MESSAGE); } // initialize the parameter update listener this.paramUpdateListener = new ParameterUpdateListener(null); // process workflow events this.processWorkflowEventQueue(); } /** * Displays the configuration and control window. */ public void showGUI() { this.controlWin.setVisible(true); } public void configureWorkflow(ALDConfigurationEvent confEvent) { this.alidaWorkflow.handleALDConfigurationEvent(confEvent); // post-process workflow events this.processWorkflowEventQueue(); } /** * Returns current status of workflow thread. * @return Status of workflow thread. */ public WorkflowThreadStatus getWorkflowThreadStatus() { return this.workflowStatus; } /** * Executes the workflow. */ public void runWorkflow() { try { // this.processEvents = true; ALDWorkflowNodeState state = this.alidaWorkflow.getState(this.operatorNodeID); if ( !this.workflowStatus.equals(WorkflowThreadStatus.THREAD_STOPPED) && state.equals(ALDWorkflowNodeState.READY)) { if ( this.alidaWorkflow.getOperator(this.operatorNodeID) instanceof ALDOperatorControllable) { if (!this.workflowStatus.equals( WorkflowThreadStatus.THREAD_INTERRUPTED)) { JOptionPane.showMessageDialog(null, "Operator was already executed with current configuration,\n" + "please change parameters before executing it again.", "Alida Message", JOptionPane.INFORMATION_MESSAGE); return; } this.resultFrame = null; this.alidaWorkflow.handleALDControlEvent( new ALDControlEvent(this, ALDControlEventType.RUN_EVENT)); this.alidaWorkflow.nodeParameterChanged(this.operatorNodeID); this.workflowStatus = WorkflowThreadStatus.THREAD_RUNNING; this.alidaWorkflow.runWorkflow(); } else { // non-controllables operators should not be run again with same config JOptionPane.showMessageDialog(null, "Operator was already executed with current configuration,\n" + "please change parameters before executing it again.", "Alida Message", JOptionPane.INFORMATION_MESSAGE); return; } } else { // if node is runnable, run it if (state.equals(ALDWorkflowNodeState.RUNNABLE)) { this.resultFrame = null; this.alidaWorkflow.handleALDControlEvent( new ALDControlEvent(this, ALDControlEventType.RUN_EVENT)); this.workflowStatus = WorkflowThreadStatus.THREAD_RUNNING; this.alidaWorkflow.runWorkflow(); } else { JOptionPane.showMessageDialog(null, "Executing operator failed -\n" + "operator is not fully configured!\nCheck your parameter settings!", "Error", JOptionPane.ERROR_MESSAGE); } } } catch (ALDWorkflowException e) { JOptionPane.showMessageDialog(null, "Executing operator failed!\n" + e.getCommentString(), "Error", JOptionPane.ERROR_MESSAGE); } // post-process workflow events this.processWorkflowEventQueue(); } public void runWorkflowInBatchMode() throws ALDBatchIOException, ALDOperatorException { LinkedList<String> batchParams = this.controlWin.getBatchInputParameters(); if ( batchParams.size() > 0) { this.batchInputParameter = batchParams.get(0); this.batchInputIterator = this.controlWin.getInputParamIterator(this.batchInputParameter); this.batchOutputParameter = this.controlWin.getBatchOutputParameters(); this.batchOutputResultMap = new HashMap<String, ALDBatchRunResultInfo>(); for (String output : this.batchOutputParameter) { this.batchOutputResultMap.put(output, new ALDBatchRunResultInfo(output)); } this.batchModeActive = true; this.workflowStatus = WorkflowThreadStatus.THREAD_RUNNING; this.doNextBatchModeStep(); } } protected void doNextBatchModeStep() { if (this.batchInputIterator.hasNext()) { Object nextItem = this.batchInputIterator.next(); for (String output : this.batchOutputParameter) { this.batchOutputResultMap.get(output). getParameterValueVec().add(nextItem); } try { this.alidaWorkflow.getOperator(this.operatorNodeID).setParameter( this.batchInputParameter, nextItem); this.alidaWorkflow.nodeParameterChanged(this.operatorNodeID); this.processWorkflowEventQueue(); // this.alidaWorkflow.getOperator(this.operatorNodeID).print(); this.alidaWorkflow.runWorkflow(); } catch (ALDWorkflowException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ALDOperatorException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { ALDBatchOperatorResultFrame batchResultFrame; try { batchResultFrame = new ALDBatchOperatorResultFrame( this.alidaWorkflow.getOperator(this.operatorNodeID), this.batchOutputResultMap, ProviderInteractionLevel.WARNINGS_ONLY); batchResultFrame.setVisible(true); this.batchModeActive = false; } catch (ALDWorkflowException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void pauseWorkflow() { this.workflowStatus = WorkflowThreadStatus.THREAD_PAUSED; this.alidaWorkflow.handleALDControlEvent( new ALDControlEvent(this, ALDControlEventType.PAUSE_EVENT)); // post-process workflow events this.processWorkflowEventQueue(); } public void resumeWorkflow() { this.workflowStatus = WorkflowThreadStatus.THREAD_RUNNING; this.alidaWorkflow.handleALDControlEvent( new ALDControlEvent(this, ALDControlEventType.RESUME_EVENT)); // post-process workflow events this.processWorkflowEventQueue(); } public void doNextStepInWorkflow() { this.workflowStatus = WorkflowThreadStatus.THREAD_RUNNING; this.alidaWorkflow.handleALDControlEvent( new ALDControlEvent(this, ALDControlEventType.STEP_EVENT)); // post-process workflow events this.processWorkflowEventQueue(); } public void stopWorkflow() { this.workflowStatus = WorkflowThreadStatus.THREAD_STOPPED; this.alidaWorkflow.handleALDControlEvent( new ALDControlEvent(this, ALDControlEventType.STOP_EVENT)); // post-process workflow events this.processWorkflowEventQueue(); } // TODO Kill event vs. interrupt Execution! public void killWorkflowThread() { // this.alidaWorkflow.handleALDControlEvent( // new ALDControlEvent(this, ALDControlEventType.KILL_EVENT)); // this.processEvents = false; // this.interruptWin.setVisible(true); this.alidaWorkflow.interruptExecution(); // post-process workflow events this.processWorkflowEventQueue(); } /** * Aborts running execution of the workflow. */ public void interruptExecution() { // this.processEvents = false; // this.interruptWin.setVisible(true); this.alidaWorkflow.interruptExecution(); // post-process workflow events this.processWorkflowEventQueue(); } /** * (Re-)display the result frame again, if results available. */ public void showResultFrame() { if (this.resultFrame != null) this.resultFrame.setVisible(true); } /** * Does clean-up on termination, i.e. closes all open windows. */ public boolean quit() { // this.processEvents = false; // check if thread terminated this.processWorkflowEventQueue(); if ( this.workflowStatus == WorkflowThreadStatus.THREAD_RUNNING || this.workflowStatus == WorkflowThreadStatus.THREAD_PAUSED) { int selection = JOptionPane .showOptionDialog( null, "The operator is running or paused!\n" + "Do you really want to quit?", "Warning", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, this.yesnoOption, this.yesnoOption[0]); switch (selection) { case JOptionPane.YES_OPTION: // this.execProxy.killWorkflowThread(); this.workflowStatus = WorkflowThreadStatus.THREAD_STOPPED; // ((ALDOperatorControllable) this.op) // .handleALDControlEvent(new ALDControlEvent(this, // ALDControlEventType.STOP_EVENT)); break; case JOptionPane.NO_OPTION: case JOptionPane.CANCEL_OPTION: return false; } } else { this.workflowStatus = WorkflowThreadStatus.THREAD_STOPPED; } // try to close the configuration window if (this.resultFrame != null) this.resultFrame.dispose(); return this.controlWin.quit(); } /** * 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); } } /* (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(); // handle the event switch(type) { case ADD_NODE: if (eventInfo instanceof ALDWorkflowNodeID) { 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 ( initialOp == null || !op.hasParameter(pname) || !initialOp.hasParameter(pname)) continue; op.setParameter(pname, initialOp.getParameter(pname)); } this.handleAddNodeEvent(op, (ALDWorkflowNodeID)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(); } this.workflowStatus = WorkflowThreadStatus.THREAD_READY; } break; case NODE_PARAMETER_CHANGE: // don't do updates in the GUI if batch mode is active // (in particular the image I/O provider would open images used as // batch input parameters - which is of course, not intended) if (!this.batchModeActive) this.handleNodeParameterChangeEvent( (Collection<ALDWorkflowNodeID>)eventInfo); // update node states as well HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState> statusMap = new HashMap<>(); for (ALDWorkflowNodeID nodeID: (Collection<ALDWorkflowNodeID>)eventInfo) { ALDWorkflowNodeState state; try { state = this.alidaWorkflow.getNode(nodeID).getState(); statusMap.put(nodeID, state); } catch (ALDWorkflowException e) { // if something goes wrong, just skip... e.printStackTrace(); } } this.handleNodeStateChangeEvent(statusMap); break; case NODE_STATE_CHANGE: this.handleNodeStateChangeEvent( (HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState>)eventInfo); break; case NODE_EXECUTION_PROGRESS: // only proceed if progress event messages are to be displayed if (!this.controlWin.showProgressEvents()) break; // new status message about execution progress received try { HashSet<ALDWorkflowNodeID> idHash = (HashSet<ALDWorkflowNodeID>) eventInfo; // we only consider the first node here... Iterator<ALDWorkflowNodeID> nodeID = idHash.iterator(); String msg = this.alidaWorkflow.getNode(nodeID.next()). getOperatorExecutionProgressDescr(); // update status message in control window this.controlWin.postStatusMessage(msg); } catch (ALDWorkflowException e1) { System.err.println("[ALDOperatorGUIExecutionProxy] " + "could not handle/update operator status message... ignoring!"); } break; case RUN_FAILURE: this.workflowStatus = WorkflowThreadStatus.THREAD_READY; this.displayFailureMessageWindow(event); break; case USER_INTERRUPT: // JOptionPane.showMessageDialog(null, "Execution was aborted!", // "Workflow Execution Message", JOptionPane.ERROR_MESSAGE); this.workflowStatus = WorkflowThreadStatus.THREAD_INTERRUPTED; this.controlWin.updateNodeStatus(ALDWorkflowNodeState.RUNNABLE); this.controlWin.postStatusMessage("[GUI-Proxy] Operator stopped."); break; case EXECUTION_FINISHED: if (this.workflowStatus.equals(WorkflowThreadStatus.THREAD_STOPPED)) this.workflowStatus = WorkflowThreadStatus.THREAD_STOPPED; // inform the control window of the termination of the thread this.controlWin.updateNodeStatus(ALDWorkflowNodeState.READY); break; case SHOW_RESULTS: if (!(eventInfo instanceof ALDWorkflowNodeID)) { JOptionPane.showMessageDialog(null, "Cannot display results!", "Workflow Execution Error", JOptionPane.ERROR_MESSAGE); return; } if (this.batchModeActive) { ALDOperator op; try { op = this.alidaWorkflow.getOperator((ALDWorkflowNodeID)eventInfo); for (String output : this.batchOutputParameter) { Object resultValue = op.getParameter(output); this.batchOutputResultMap.get(output). getResultDataVec().add(resultValue); } } catch (ALDWorkflowException e) { JOptionPane.showMessageDialog(null, "Cannot extract results!", "Workflow Execution Error", JOptionPane.ERROR_MESSAGE); } catch (ALDOperatorException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.doNextBatchModeStep(); } else { // there are results to display ALDOperator op; try { op = this.alidaWorkflow.getOperator((ALDWorkflowNodeID)eventInfo); this.resultFrame = new ALDOperatorResultFrame(op, ALDDataIOManagerSwing.ProviderInteractionLevel.ALL_ALLOWED); this.resultFrame.setVisible(true); } catch (ALDWorkflowException e) { JOptionPane.showMessageDialog(null, "Cannot display results!", "Workflow Execution Error", JOptionPane.ERROR_MESSAGE); } this.workflowStatus = WorkflowThreadStatus.THREAD_READY; } break; case RENAME: // event is specific for Grappa editor, ignored here break; default: System.out.println("Event type \'" + type + "\' not yet handled..."); } } /* (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("closeFailWin")) { this.failureMessageWin.setVisible(false); } } /** * Displays window with detailed error information on execution failures. * @param event Workflow event got on failure. */ protected void displayFailureMessageWindow(ALDWorkflowEvent event) { // extract event data Object eventInfo = event.getId(); // eventInfo object is of type ALDWorkflowRunFailureInfo... if (eventInfo instanceof ALDWorkflowRunFailureInfo) { ALDWorkflowRunFailureInfo failInfo = (ALDWorkflowRunFailureInfo)eventInfo; StringBuffer msg = new StringBuffer(); // reset control window to runnable status this.controlWin.updateNodeStatus(ALDWorkflowNodeState.RUNNABLE); // put some general information to the header msg.append("Something went wrong during operator execution!\n"); msg.append("\nException message:\n"); msg.append(event.getEventMessage(120) + "\n"); msg.append("Exception class type: \n"); msg.append(failInfo.getException().getClass() + "\n"); msg.append("\nException stack trace:\n"); StackTraceElement[] trace = failInfo.getException().getStackTrace(); for (StackTraceElement elem : trace) { msg.append(elem.toString()); msg.append("\n"); } this.failureMessageWin = new JFrame("Operator Execution Failure!"); this.failureMessageWin.setLayout(new BorderLayout()); this.failureMessageWin.setSize(500, 300); JTextArea textArea = new JTextArea(msg.toString()); textArea.setEditable(false); JScrollPane scrollPane = new JScrollPane(textArea); this.failureMessageWin.add(scrollPane,BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); JButton okButton = new JButton("Ok"); okButton.setActionCommand("closeFailWin"); okButton.addActionListener(this); buttonPanel.add(okButton); this.failureMessageWin.add(buttonPanel,BorderLayout.SOUTH); this.failureMessageWin.setVisible(true); } } /** * Adds a node to the workflow graph. * @param op Operator associated with the node. * @param id Workflow node ID of the corresponding Alida workflow node. */ protected synchronized void handleAddNodeEvent(ALDOperator op, ALDWorkflowNodeID id) { // if the window has already been initialized, ignore event... if (this.controlWin != null) return; // init the control frame ALDOperatorControlFrame controlFrame = null; try { this.paramUpdateListener.updateNodeID(id); controlFrame = this.getNewConfigWin(op); } catch (ALDOperatorException e) { System.err.println("[ALDOperatorGUIExecutionProxy::addNode] " + "cannot instantiate control frame for \"" +op.getName()+ "\"..."); return; } // store the control frame this.controlWin = controlFrame; // make sure that operator is in sync with window // this.controlWin.synchronizeOperatorWithGUI(); LinkedList<ALDWorkflowNodeID> nodeList = new LinkedList<ALDWorkflowNodeID>(); nodeList.add(id); this.handleNodeParameterChangeEvent(nodeList); // tell the workflow that the operator configuration changed this.paramUpdateListener.handleALDParameterUpdateEvent( new ALDOpParameterUpdateEvent(this, EventType.CHANGED)); } /** * Update parameter labels according to configuration states. * @param idList List of nodes that are to be updated. */ protected synchronized void handleNodeParameterChangeEvent( Collection<ALDWorkflowNodeID> idList) { // in this case list contains only a single node for (ALDWorkflowNodeID nodeID: idList) { try { // update parameter values in control window this.controlWin.updateOperator( this.alidaWorkflow.getOperator(nodeID)); this.controlWin.updateParamConfigurationStatus( this.alidaWorkflow.getOperator(nodeID).unconfiguredItems()); } catch (ALDWorkflowException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * Update control window color according to node's state. * @param idList List of nodes that are to be updated. */ protected synchronized void handleNodeStateChangeEvent( HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState> idList) { // in this case list contains only a single node Set<ALDWorkflowNodeID> nodeIDs = idList.keySet(); for (ALDWorkflowNodeID nodeID: nodeIDs) { ALDWorkflowNodeState nodeState = idList.get(nodeID); // update the configuration window this.controlWin.updateNodeStatus(nodeState); } } /** * Generate a new configuration window. * <p> * Is to be overwritten by subclasses. * * @param op Operator for which window is requested. * @return New configuration window. * @throws ALDOperatorException Thrown if initialization of window fails. */ protected ALDOperatorControlFrame getNewConfigWin(ALDOperator op) throws ALDOperatorException { return new ALDOperatorControlFrame(op, this, this.paramUpdateListener); } /** * Listener class to react on parameter value updates in config window. * @author moeller */ protected class ParameterUpdateListener implements ALDOpParameterUpdateEventListener { /** * Corresponding node ID. */ private ALDWorkflowNodeID id; /** * Default constructor. * @param nodeID Alida workflow node ID of associated operator node. */ public ParameterUpdateListener(ALDWorkflowNodeID nodeID) { this.id = nodeID; } /** * Updates the ID of the workflow node associated with this listener. * @param nodeID New node ID. */ public void updateNodeID(ALDWorkflowNodeID nodeID) { this.id = nodeID; } /* (non-Javadoc) * @see de.unihalle.informatik.Alida.operator.events.ALDOpParameterUpdateEventListener#handleALDParameterUpdateEvent(de.unihalle.informatik.Alida.operator.events.ALDOpParameterUpdateEvent) */ @Override public void handleALDParameterUpdateEvent(ALDOpParameterUpdateEvent e) { // make sure that parameter updater is properly initialized, // if not, just ignore the event if (this.id == null) return; try { switch(e.getType()) { case CHANGED: // notify workflow of change in node parameters ALDOperatorGUIExecutionProxy.this.alidaWorkflow.nodeParameterChanged( this.id); break; case LOADED: ALDOperatorGUIExecutionProxy.this.alidaWorkflow.setOperator(this.id, ALDOperatorGUIExecutionProxy.this.controlWin.getOperator()); break; } // process event queue ALDOperatorGUIExecutionProxy.this.processWorkflowEventQueue(); } catch (ALDWorkflowException ex) { System.err.println("[ParameterUpdateListener] Warning! " + "could not propagate parameter update event, node not found!"); } } } }