http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreationDialog.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreationDialog.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreationDialog.java
new file mode 100644
index 0000000..3a467fd
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreationDialog.java
@@ -0,0 +1,666 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.NORTH;
+import static java.awt.BorderLayout.SOUTH;
+import static java.util.Collections.sort;
+import static javax.swing.JOptionPane.WARNING_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static org.apache.log4j.Logger.getLogger;
+import static io.github.taverna_extras.component.ui.util.Utils.uniqueName;
+import static org.apache.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.AbstractAction;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.ListCellRenderer;
+
+//import net.sf.taverna.t2.lang.ui.DeselectingButton;
+//import net.sf.taverna.t2.workbench.edits.CompoundEdit;
+//import net.sf.taverna.t2.workbench.edits.Edit;
+//import net.sf.taverna.t2.workbench.edits.EditException;
+//import net.sf.taverna.t2.workbench.edits.EditManager;
+//import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog;
+//import net.sf.taverna.t2.workbench.models.graph.GraphController;
+//import net.sf.taverna.t2.workbench.views.graph.GraphViewComponent;
+//import net.sf.taverna.t2.workflow.edits.AddActivityEdit;
+//import net.sf.taverna.t2.workflow.edits.AddActivityInputPortMappingEdit;
+//import net.sf.taverna.t2.workflow.edits.AddActivityOutputPortMappingEdit;
+//import net.sf.taverna.t2.workflow.edits.AddChildEdit;
+//import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit;
+//import net.sf.taverna.t2.workflow.edits.AddProcessorInputPortEdit;
+//import net.sf.taverna.t2.workflow.edits.AddProcessorOutputPortEdit;
+//import net.sf.taverna.t2.workflow.edits.AddWorkflowInputPortEdit;
+//import net.sf.taverna.t2.workflow.edits.AddWorkflowOutputPortEdit;
+//import net.sf.taverna.t2.workflow.edits.RemoveChildEdit;
+//import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit;
+//import net.sf.taverna.t2.workflow.edits.SetIterationStrategyStackEdit;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.annotation.annotationbeans.DescriptiveTitle;
+import org.apache.taverna.lang.ui.DeselectingButton;
+
+import org.apache.taverna.scufl2.api.activity.Activity;
+import org.apache.taverna.scufl2.api.annotation.Annotation;
+import org.apache.taverna.scufl2.api.common.Named;
+import org.apache.taverna.scufl2.api.common.NamedSet;
+import org.apache.taverna.scufl2.api.common.Scufl2Tools;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.BlockingControlLink;
+import org.apache.taverna.scufl2.api.core.ControlLink;
+import org.apache.taverna.scufl2.api.core.DataLink;
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.port.InputActivityPort;
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputActivityPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.ProcessorPort;
+import org.apache.taverna.scufl2.api.port.ReceiverPort;
+import org.apache.taverna.scufl2.api.port.SenderPort;
+import org.apache.taverna.scufl2.api.profiles.Profile;
+import org.apache.taverna.workbench.edits.CompoundEdit;
+import org.apache.taverna.workbench.edits.Edit;
+import org.apache.taverna.workbench.edits.EditException;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.helper.HelpEnabledDialog;
+import org.apache.taverna.workbench.models.graph.GraphController;
+import org.apache.taverna.workbench.views.graph.GraphViewComponent;
+import org.apache.taverna.workflow.edits.AddActivityEdit;
+import org.apache.taverna.workflow.edits.AddActivityInputPortMappingEdit;
+import org.apache.taverna.workflow.edits.AddActivityOutputPortMappingEdit;
+import org.apache.taverna.workflow.edits.AddChildEdit;
+import org.apache.taverna.workflow.edits.AddDataLinkEdit;
+import org.apache.taverna.workflow.edits.AddProcessorInputPortEdit;
+import org.apache.taverna.workflow.edits.AddProcessorOutputPortEdit;
+import org.apache.taverna.workflow.edits.AddWorkflowInputPortEdit;
+import org.apache.taverna.workflow.edits.AddWorkflowOutputPortEdit;
+import org.apache.taverna.workflow.edits.RemoveChildEdit;
+import org.apache.taverna.workflow.edits.RemoveDataLinkEdit;
+import org.apache.taverna.workflow.edits.SetIterationStrategyStackEdit;
+import org.apache.taverna.workflowmodel.utils.AnnotationTools;
+
+/**
+ * @author alanrw
+ */
+public class NestedWorkflowCreationDialog extends HelpEnabledDialog {
+       private static final long serialVersionUID = 727059218457420449L;
+       private static final Logger logger = 
getLogger(NestedWorkflowCreationDialog.class);
+       private static final Comparator<Processor> processorComparator = new 
Comparator<Processor>() {
+               @Override
+               public int compare(Processor o1, Processor o2) {
+                       return o1.getName().compareTo(o2.getName());
+               }
+       };
+       private static final ListCellRenderer<Object> defaultRenderer = new 
DefaultListCellRenderer();
+       private static final ListCellRenderer<Processor> processorRenderer = 
new ListCellRenderer<Processor>() {
+               @Override
+               public Component getListCellRendererComponent(
+                               JList<? extends Processor> list,
+                               Processor value, int index, boolean isSelected,
+                               boolean cellHasFocus) {
+                       return 
defaultRenderer.getListCellRendererComponent(list,
+                                       value.getName(), index, isSelected, 
cellHasFocus);
+               }
+       };
+
+       private final EditManager em;
+       private final GraphViewComponent graphView;
+       private final List<Processor> includedProcessors = new ArrayList<>();
+       private List<Processor> allProcessors;
+       private final List<Processor> includableProcessors = new ArrayList<>();
+
+       private JList<Processor> includableList = new JList<>();
+       private JList<Processor> includedList = new JList<>();
+       private final Workflow currentDataflow;
+       private JButton excludeButton;
+       private JButton includeButton;
+       private JButton okButton;
+       private JButton resetButton;
+       private JTextField nameField = new JTextField(30);
+
+       public NestedWorkflowCreationDialog(Frame owner, Object o,
+                       Workflow dataflow, EditManager em, GraphViewComponent 
graphView) {
+               super(owner, "Nested workflow creation", true, null);
+               this.em = em;
+               this.graphView = graphView;
+
+               if (o instanceof Processor)
+                       includedProcessors.add((Processor) o);
+               this.currentDataflow = dataflow;
+
+               allProcessors = new ArrayList<>(dataflow.getProcessors());
+
+               this.setLayout(new BorderLayout());
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout());
+
+               okButton = new DeselectingButton(new OKAction(this));
+               buttonPanel.add(okButton);
+
+               resetButton = new DeselectingButton(new ResetAction(this));
+               buttonPanel.add(resetButton);
+
+               JButton cancelButton = new DeselectingButton(new 
CancelAction(this));
+               buttonPanel.add(cancelButton);
+
+               JPanel innerPanel = new JPanel(new BorderLayout());
+               JPanel processorChoice = createProcessorChoicePanel(dataflow);
+               innerPanel.add(processorChoice, CENTER);
+
+               JPanel namePanel = new JPanel(new FlowLayout());
+               namePanel.add(new JLabel("Workflow name: "));
+               nameField.setText("nested");
+               namePanel.add(nameField);
+               innerPanel.add(namePanel, SOUTH);
+
+               this.add(innerPanel, CENTER);
+
+               this.add(buttonPanel, SOUTH);
+               this.pack();
+               this.setSize(new Dimension(500, 800));
+       }
+
+       private JPanel createProcessorChoicePanel(Workflow dataflow) {
+               JPanel result = new JPanel();
+               result.setLayout(new GridLayout(0, 2));
+
+               JPanel includedProcessorsPanel = 
createIncludedProcessorsPanel();
+               JPanel includableProcessorsPanel = 
createIncludableProcessorsPanel();
+               result.add(includableProcessorsPanel);
+               result.add(includedProcessorsPanel);
+               updateLists();
+               return result;
+       }
+
+       private JPanel createIncludableProcessorsPanel() {
+               JPanel result = new JPanel();
+               result.setLayout(new BorderLayout());
+               result.add(new JLabel("Possible services"), NORTH);
+               includableList.setModel(new DefaultComboBoxModel<>(new Vector<>(
+                               includableProcessors)));
+               includableList.setCellRenderer(processorRenderer);
+               result.add(new JScrollPane(includableList), CENTER);
+
+               includeButton = new DeselectingButton("Include", new 
ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               includedProcessors.addAll(includableList
+                                               .getSelectedValuesList());
+                               calculateIncludableProcessors();
+                               updateLists();
+                       }
+               });
+
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout());
+               buttonPanel.add(includeButton);
+               result.add(buttonPanel, SOUTH);
+               return result;
+       }
+
+       private void resetLists() {
+               includedProcessors.clear();
+               updateLists();
+       }
+
+       private JPanel createIncludedProcessorsPanel() {
+               JPanel result = new JPanel();
+               result.setLayout(new BorderLayout());
+               result.add(new JLabel("Included services"), NORTH);
+               includedList.setModel(new DefaultComboBoxModel<>(new Vector<>(
+                               includedProcessors)));
+               includedList.setCellRenderer(processorRenderer);
+               result.add(new JScrollPane(includedList), CENTER);
+
+               excludeButton = new DeselectingButton("Exclude", new 
ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               includedProcessors.removeAll(includedList
+                                               .getSelectedValuesList());
+                               calculateIncludableProcessors();
+                               updateLists();
+                       }
+               });
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout());
+               buttonPanel.add(excludeButton);
+
+               result.add(buttonPanel, SOUTH);
+               return result;
+       }
+
+       private void updateLists() {
+               calculateIncludableProcessors();
+               sort(includedProcessors, processorComparator);
+               sort(includableProcessors, processorComparator);
+               includedList.setModel(new DefaultComboBoxModel<>(new Vector<>(
+                               includedProcessors)));
+               includableList.setModel(new DefaultComboBoxModel<>(new Vector<>(
+                               includableProcessors)));
+               boolean someIncludedProcessors = includedProcessors.size() > 0;
+               excludeButton.setEnabled(someIncludedProcessors);
+               okButton.setEnabled(someIncludedProcessors);
+               resetButton.setEnabled(someIncludedProcessors);
+               boolean someIncludableProcessors = includableProcessors.size() 
> 0;
+               includeButton.setEnabled(someIncludableProcessors);
+       }
+
+       public void calculateIncludableProcessors() {
+               includableProcessors.clear();
+               if (includedProcessors.isEmpty())
+                       includableProcessors.addAll(allProcessors);
+               else
+                       for (Processor p : includedProcessors) {
+                               considerNearestUpstream(p);
+                               considerNearestDownstream(p);
+                       }
+               sort(includableProcessors, processorComparator);
+       }
+
+       private void considerNearestDownstream(Processor investigate) {
+               for (BlockingControlLink condition : 
investigate.controlLinksWaitingFor())
+                       considerInclusion(condition.getBlock());
+
+               for (OutputProcessorPort outputPort : 
investigate.getOutputPorts())
+                       for (DataLink datalink : outputPort.getDatalinksFrom()) 
{
+                               ReceiverPort sink = datalink.getSendsTo();
+                               if (sink instanceof InputProcessorPort)
+                                       considerInclusion(((InputProcessorPort) 
sink).getParent());
+                       }
+       }
+
+       private void considerNearestUpstream(Processor investigate) {
+               for (BlockingControlLink condition : 
investigate.controlLinksBlocking())
+                       considerInclusion(condition.getUntilFinished());
+               for (InputProcessorPort inputPort : investigate.getInputPorts())
+                       for (DataLink incomingLink : 
inputPort.getDatalinksTo()) {
+                               if (incomingLink == null)
+                                       continue;
+                               SenderPort source = 
incomingLink.getReceivesFrom();
+                               if (source instanceof OutputProcessorPort)
+                                       
considerInclusion(((OutputProcessorPort) source).getParent());
+               }
+       }
+
+       private void considerInclusion(Processor p) {
+               if (!includedProcessors.contains(p)
+                               && !includableProcessors.contains(p))
+                       includableProcessors.add(p);
+       }
+
+       private void createNestedWorkflow() {
+               final List<Edit<?>> currentWorkflowEditList = new ArrayList<>();
+               Map<Object, Object> oldNewMapping = new HashMap<>();
+               Map<DataLink, String> linkProcessorPortMapping = new 
HashMap<>();
+               Map<SenderPort, OutputWorkflowPort> outputPortMap = new 
HashMap<>();
+               Map<ReceiverPort, InputWorkflowPort> inputPortMap = new 
HashMap<>();
+
+               Profile profile;//FIXME
+               Processor nestingProcessor = 
createNestingProcessor(currentWorkflowEditList);
+               Workflow nestedDataflow = createNestedDataflow();
+
+               transferProcessors(currentWorkflowEditList, oldNewMapping,
+                               nestedDataflow);
+               transferDatalinks(oldNewMapping, linkProcessorPortMapping,
+                               outputPortMap, inputPortMap, nestedDataflow);
+               transferConditions(currentWorkflowEditList, oldNewMapping,
+                               nestingProcessor);
+               addDataflowToNestingProcessor(nestingProcessor, nestedDataflow, 
profile);
+               currentWorkflowEditList.add(new AddChildEdit<>(currentDataflow,
+                               nestingProcessor));
+               createDatalinkEdits(currentWorkflowEditList, oldNewMapping,
+                               linkProcessorPortMapping, nestingProcessor);
+
+               try {
+                       GraphController gc = 
graphView.getGraphController(currentDataflow);
+                       
gc.setExpandNestedDataflow(nestingProcessor.getActivity(profile), true);
+                       em.doDataflowEdit(currentDataflow.getParent(), new 
CompoundEdit(
+                                       currentWorkflowEditList));
+                       gc.redraw();
+               } catch (EditException e1) {
+                       logger.error("failed to manufacture nested workflow", 
e1);
+               }
+       }
+
+       private void addDataflowToNestingProcessor(Processor nestingProcessor,
+                       Workflow nestedDataflow, Profile profile) {
+               Activity da = new Activity();
+               da.setParent(profile);
+               da.createConfiguration(NESTED_WORKFLOW).getJsonAsObjectNode()
+                               .put("nestedWorkflow", 
nestedDataflow.getName());
+               try {
+                       new AddActivityEdit(nestingProcessor, da).doEdit();
+                       new SetIterationStrategyStackEdit(nestingProcessor, 
null/*FIXME*/).doEdit();
+                       for (InputActivityPort aip : da.getInputPorts()) {
+                               InputProcessorPort pip = new 
InputProcessorPort();
+                               pip.setName(aip.getName());
+                               pip.setDepth(aip.getDepth());
+                               new AddProcessorInputPortEdit(nestingProcessor, 
pip).doEdit();
+                               new AddActivityInputPortMappingEdit(da, pip, 
aip).doEdit();
+                       }
+                       for (OutputActivityPort aop : da.getOutputPorts()) {
+                               OutputProcessorPort pop = new 
OutputProcessorPort();
+                               pop.setName(aop.getName());
+                               pop.setDepth(aop.getDepth());
+                               pop.setGranularDepth(aop.getGranularDepth());
+                               new 
AddProcessorOutputPortEdit(nestingProcessor, pop).doEdit();
+                               new AddActivityOutputPortMappingEdit(da, pop, 
aop).doEdit();
+                       }
+               } catch (EditException e1) {
+                       logger.error("failed to add ports to processor", e1);
+               }
+       }
+
+       private void createDatalinkEdits(List<Edit<?>> editList,
+                       Map<Object, Object> oldNewMapping,
+                       Map<DataLink, String> linkProcessorPortMapping,
+                       Processor nestingProcessor) {
+               for (DataLink dl : currentDataflow.getDataLinks())
+                       if (oldNewMapping.containsKey(dl.getReceivesFrom())
+                                       && 
oldNewMapping.containsKey(dl.getSendsTo()))
+                               // Internal to nested workflow
+                               editList.add(new 
RemoveDataLinkEdit(dl.getParent(), dl));
+                       else if 
(oldNewMapping.containsKey(dl.getReceivesFrom())) {
+                               // Coming out of nested workflow
+                               OutputProcessorPort nestedPort = 
nestingProcessor
+                                               .getOutputPorts().getByName(
+                                                               
linkProcessorPortMapping.get(dl));
+                               if (nestedPort != null) {
+                                       DataLink replacementDatalink = new 
DataLink(nestedPort
+                                                       
.getParent().getParent(), nestedPort,
+                                                       dl.getSendsTo());
+                                       editList.add(new 
RemoveDataLinkEdit(dl.getParent(), dl));
+                                       editList.add(new 
AddDataLinkEdit(nestedPort.getParent()
+                                                       .getParent(), 
replacementDatalink));
+                               }
+                       } else if (oldNewMapping.containsKey(dl.getSendsTo())) {
+                               // Coming into nested workflow
+                               InputProcessorPort nestedPort = nestingProcessor
+                                               .getInputPorts().getByName(
+                                                               
linkProcessorPortMapping.get(dl));
+                               if (nestedPort != null) {
+                                       DataLink replacementDatalink = new 
DataLink(nestedPort
+                                                       
.getParent().getParent(), dl.getReceivesFrom(),
+                                                       nestedPort);
+                                       editList.add(new 
RemoveDataLinkEdit(dl.getParent(), dl));
+                                       editList.add(new 
AddDataLinkEdit(nestedPort.getParent()
+                                                       .getParent(), 
replacementDatalink));
+                               }
+                       }
+       }
+
+       private void transferConditions(List<Edit<?>> editList,
+                       Map<Object, Object> oldNewMapping, Processor 
nestingProcessor) {
+               for (Processor p : currentDataflow.getProcessors()) {
+                       boolean isTargetMoved = oldNewMapping.containsKey(p);
+                       for (BlockingControlLink c : 
p.controlLinksWaitingFor()) {
+                               Processor pre = c.getUntilFinished();
+                               boolean isControlMoved = 
oldNewMapping.containsKey(pre);
+                               if (isTargetMoved && isControlMoved) {
+                                       // Add in new condition
+                                       new BlockingControlLink(
+                                                       (Processor) 
oldNewMapping.get(pre),
+                                                       (Processor) 
oldNewMapping.get(p));
+                               } else if (isTargetMoved) {
+                                       editList.add(new 
RemoveChildEdit<>(c.getParent(),c));
+                                       editList.add(new 
AddChildEdit<>(c.getParent(),
+                                                       new 
BlockingControlLink(pre, nestingProcessor)));
+                               } else if (isControlMoved) {
+                                       editList.add(new 
RemoveChildEdit<>(c.getParent(), c));
+                                       editList.add(new 
AddChildEdit<>(c.getParent(),
+                                                       new 
BlockingControlLink(nestingProcessor, p)));
+                               }
+                       }
+               }
+       }
+
+       private void transferDatalinks(Map<Object, Object> oldNewMapping,
+                       Map<DataLink, String> linkProcessorPortMapping,
+                       Map<SenderPort, OutputWorkflowPort> outputPortMap,
+                       Map<ReceiverPort, InputWorkflowPort> inputPortMap,
+                       Workflow nestedDataflow) {
+               NamedSet<InputWorkflowPort> inputPorts = new NamedSet<>();
+               NamedSet<OutputWorkflowPort> outputPorts = new NamedSet<>();
+
+               for (DataLink dl : currentDataflow.getDataLinks()) {
+                       final SenderPort datalinkSource = dl.getReceivesFrom();
+                       final ReceiverPort datalinkSink = dl.getSendsTo();
+                       if (oldNewMapping.containsKey(datalinkSource)
+                                       && 
oldNewMapping.containsKey(datalinkSink)) {
+                               // Internal to nested workflow
+                               DataLink newDatalink = new DataLink(null,
+                                               (SenderPort) 
oldNewMapping.get(datalinkSource),
+                                               (ReceiverPort) 
oldNewMapping.get(datalinkSink));
+                               try {
+                                       new AddDataLinkEdit(nestedDataflow, 
newDatalink).doEdit();
+                               } catch (EditException e1) {
+                                       logger.error("failed to connect 
datalink", e1);
+                               }
+                       } else if (oldNewMapping.containsKey(datalinkSource)) {
+                               OutputWorkflowPort dop = null;
+                               if (!outputPortMap.containsKey(datalinkSource)) 
{
+                                       dop = new 
OutputWorkflowPort(nestedDataflow, uniqueName(
+                                                       
datalinkSource.getName(), outputPorts));
+                                       outputPorts.add(dop);
+                                       outputPortMap.put(datalinkSource, dop);
+                               } else
+                                       dop = outputPortMap.get(datalinkSource);
+                               String portName = dop.getName();
+                               // Coming out of nested workflow
+                               linkProcessorPortMapping.put(dl, portName);
+                               try {
+                                       new 
AddWorkflowOutputPortEdit(nestedDataflow, dop).doEdit();
+                                       DataLink newDatalink = new DataLink(
+                                                       (SenderPort) 
oldNewMapping.get(datalinkSource),
+                                                       
dop.getInternalInputPort());
+                                       new AddDataLinkEdit(nestedDataflow, 
newDatalink).doEdit();
+                               } catch (EditException e1) {
+                                       logger.error("failed to add dataflow 
output", e1);
+                               }
+                       } else if (oldNewMapping.containsKey(datalinkSink)) {
+                               InputWorkflowPort dip = null;
+                               if (!inputPortMap.containsKey(datalinkSink)) {
+                                       dip = new 
InputWorkflowPort(nestedDataflow, uniqueName(
+                                                       datalinkSink.getName(), 
inputPorts));
+                                       inputPorts.add(dip);
+                                       dip.setDepth(dl.getResolvedDepth());
+                                       inputPortMap.put(datalinkSink, dip);
+                               } else
+                                       dip = inputPortMap.get(datalinkSink);
+                               String portName = dip.getName();
+                               // Coming into nested workflow
+                               linkProcessorPortMapping.put(dl, portName);
+                               try {
+                                       new 
AddWorkflowInputPortEdit(nestedDataflow, dip).doEdit();
+                                       DataLink newDatalink = new DataLink(
+                                                       
dip.getInternalOutputPort(),
+                                                       (ReceiverPort) 
oldNewMapping.get(datalinkSink));
+                                       new AddDataLinkEdit(nestedDataflow, 
newDatalink).doEdit();
+                               } catch (EditException e1) {
+                                       logger.error("failed to add dataflow 
input", e1);
+                               }
+                       }
+               }
+       }
+
+       private void transferProcessors(List<Edit<?>> editList,
+                       Map<Object, Object> oldNewMapping, Workflow 
nestedDataflow) {
+               for (Processor entity : includedProcessors)
+                       try {
+                               if (entity instanceof Processor)
+                                       transferProcessor(editList, 
oldNewMapping, nestedDataflow,
+                                                       (Processor) entity);
+                               /*else if (entity instanceof Merge)
+                                       //FIXME what to do here? Anything?
+                                       transferMerge(editList, oldNewMapping, 
nestedDataflow,
+                                                       (Merge) entity);*/
+                       } catch (Exception e1) {
+                               logger.error("failed to transfer processor", 
e1);
+                       }
+       }
+
+       /*private void transferMerge(List<Edit<?>> editList,
+                       Map<Object, Object> oldNewMapping, Workflow 
nestedDataflow,
+                       Merge merge) throws EditException {
+               editList.add(edits.getRemoveMergeEdit(currentDataflow, merge));
+               Merge newMerge = edits.createMerge(nestedDataflow);
+               edits.getAddMergeEdit(nestedDataflow, newMerge).doEdit();
+               oldNewMapping.put(merge, newMerge);
+               for (MergeInputPort mip : merge.getInputPorts()) {
+                       MergeInputPort newMip = 
edits.createMergeInputPort(newMerge,
+                                       mip.getName(), mip.getDepth());
+                       edits.getAddMergeInputPortEdit(newMerge, 
newMip).doEdit();
+                       oldNewMapping.put(mip, newMip);
+               }
+               oldNewMapping.put(merge.getOutputPort(), 
newMerge.getOutputPort());
+       }*/
+
+       private void transferProcessor(List<Edit<?>> editList,
+                       Map<Object, Object> oldNewMapping, Workflow 
nestedDataflow,
+                       Processor p) throws Exception {
+               editList.add(new RemoveChildEdit<>(currentDataflow, p));
+               Processor newProcessor = (Processor) p.clone();
+               newProcessor.setParent(nestedDataflow);
+               oldNewMapping.put(p, newProcessor);
+               for (InputProcessorPort pip : p.getInputPorts())
+                       for (InputProcessorPort newPip : 
newProcessor.getInputPorts())
+                               if (pip.getName().equals(newPip.getName())) {
+                                       oldNewMapping.put(pip, newPip);
+                                       break;
+                               }
+               for (OutputProcessorPort pop : p.getOutputPorts())
+                       for (OutputProcessorPort newPop : 
newProcessor.getOutputPorts())
+                               if (pop.getName().equals(newPop.getName())) {
+                                       oldNewMapping.put(pop, newPop);
+                                       break;
+                               }
+       }
+
+       private Processor createNestingProcessor(List<Edit<?>> editList) {
+               //TODO check what workflow the new processor is going into
+               Processor nestingProcessor = new Processor(currentDataflow, 
uniqueName(
+                               nameField.getText(), 
currentDataflow.getProcessors()));
+               if (includedProcessors.size() != 1)
+                       return nestingProcessor;
+               Processor includedProcessor = includedProcessors.get(0);
+               for (Annotation a: includedProcessor.getAnnotations()) {
+                       Annotation newAnn = (Annotation) a.clone();
+                       newAnn.setTarget(nestingProcessor);
+                       editList.add(new AddChildEdit<>(a.getParent(), newAnn));
+               }
+               return nestingProcessor;
+       }
+
+       private Workflow createNestedDataflow() {
+               Workflow nestedDataflow = new 
Workflow(uniqueName(nameField.getText(),
+                               currentDataflow.getParent().getWorkflows()));
+               // Set the title of the nested workflow to the name suggested 
by the user
+               try {
+                       new 
AnnotationTools().setAnnotationString(nestedDataflow,
+                                       DescriptiveTitle.class, 
nameField.getText()).doEdit();
+               } catch (EditException ex) {
+                       logger.error("failed to put annotation on nested 
dataflow", ex);
+               }
+               return nestedDataflow;
+       }
+
+       private final class OKAction extends AbstractAction {
+               private static final long serialVersionUID = 
6516891432445682857L;
+               private final JDialog dialog;
+
+               private OKAction(JDialog dialog) {
+                       super("OK");
+                       this.dialog = dialog;
+               }
+
+               @Override
+               public void actionPerformed(ActionEvent e) {
+                       if (includedProcessors.isEmpty()) {
+                               showMessageDialog(
+                                               null,
+                                               "At least one service must be 
included in the nested workflow",
+                                               "Nested workflow creation", 
WARNING_MESSAGE);
+                               return;
+                       }
+
+                       createNestedWorkflow();
+                       dialog.setVisible(false);
+               }
+       }
+
+       private final class ResetAction extends AbstractAction {
+               private static final long serialVersionUID = 
7296742769289881218L;
+
+               private ResetAction(JDialog dialog) {
+                       super("Reset");
+               }
+
+               @Override
+               public void actionPerformed(ActionEvent e) {
+                       resetLists();
+               }
+       }
+
+       private final class CancelAction extends AbstractAction {
+               private static final long serialVersionUID = 
-7842176979437027091L;
+               private final JDialog dialog;
+
+               private CancelAction(JDialog dialog) {
+                       super("Cancel");
+                       this.dialog = dialog;
+               }
+
+               @Override
+               public void actionPerformed(ActionEvent e) {
+                       dialog.setVisible(false);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreatorMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreatorMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreatorMenuAction.java
new file mode 100644
index 0000000..0cdb2ba
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/NestedWorkflowCreatorMenuAction.java
@@ -0,0 +1,94 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu;
+
+import java.awt.Dialog;
+import java.awt.event.ActionEvent;
+import java.net.URI;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import 
org.apache.taverna.activities.dataflow.servicedescriptions.DataflowActivityIcon;
+
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.ui.menu.AbstractContextualMenuAction;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.selection.SelectionManager;
+import org.apache.taverna.workbench.views.graph.GraphViewComponent;
+
+/**
+ * @author alanrw
+ */
+public class NestedWorkflowCreatorMenuAction extends
+               AbstractContextualMenuAction {
+       private static final URI configureSection = URI
+                       
.create("http://taverna.sf.net/2009/contextMenu/configure";);
+
+       private SelectionManager sm;
+       private EditManager em;
+       private GraphViewComponent gv;
+
+       public NestedWorkflowCreatorMenuAction() {
+               super(configureSection, 70);
+       }
+
+       public void setEditManager(EditManager editManager) {
+               em = editManager;
+       }
+       public void setGraphView(GraphViewComponent graphView) {
+               gv = graphView;
+       }
+       public void setSelectionManager(SelectionManager selectionManager) {
+               sm = selectionManager;
+       }
+
+       @Override
+       public boolean isEnabled() {
+               Object selection = getContextualSelection().getSelection();
+               if (!super.isEnabled() || selection == null)
+                       return false;
+               if (selection instanceof Processor)
+                       return true;
+               if (!(selection instanceof Workflow))
+                       return false;
+               return !((Workflow) selection).getProcessors().isEmpty();
+       }
+
+       @Override
+       protected Action createAction() {
+               return new AbstractAction("Create nested workflow...",
+                               DataflowActivityIcon.getDataflowIcon()) {
+                       private static final long serialVersionUID = 
-3121307982540205215L;
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               createNestedWorkflow();
+                       }
+               };
+       }
+
+       private void createNestedWorkflow() {
+               Dialog dialog = new NestedWorkflowCreationDialog(null,
+                               getContextualSelection().getSelection(),
+                               sm.getSelectedWorkflow(), em, gv);
+               dialog.setVisible(true);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityAction.java
new file mode 100644
index 0000000..2bce63a
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityAction.java
@@ -0,0 +1,81 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu;
+
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.event.ActionEvent;
+import java.net.MalformedURLException;
+
+import org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.ComponentFactory;
+import io.github.taverna_extras.component.api.Version;
+import io.github.taverna_extras.component.ui.ComponentAction;
+import 
io.github.taverna_extras.component.ui.ComponentActivityConfigurationBean;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+
+import org.apache.taverna.scufl2.api.activity.Activity;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.file.exceptions.OpenException;
+import org.apache.taverna.workbench.views.graph.GraphViewComponent;
+
+/**
+ * @author alanrw
+ */
+@SuppressWarnings("serial")
+public class OpenComponentFromComponentActivityAction extends ComponentAction {
+       private static Logger logger = 
getLogger(OpenComponentFromComponentActivityAction.class);
+
+       private final FileManager fileManager;
+       private final ComponentFactory factory;
+       private final FileType fileType;
+
+       public OpenComponentFromComponentActivityAction(FileManager fileManager,
+                       ComponentFactory factory, FileType ft,
+                       GraphViewComponent graphView, ComponentServiceIcon 
icon) {
+               super("Open component...", graphView);
+               this.fileManager = fileManager;
+               this.factory = factory;
+               this.fileType = ft;
+               setIcon(icon);
+       }
+
+       private Activity selection;
+
+       @Override
+       public void actionPerformed(ActionEvent ev) {
+               try {
+                       Version.ID ident = new 
ComponentActivityConfigurationBean(
+                                       selection.getConfiguration(), factory);
+                       WorkflowBundle d = fileManager.openDataflow(fileType, 
ident);
+                       markGraphAsBelongingToComponent(d);
+               } catch (OpenException e) {
+                       logger.error("failed to open component", e);
+               } catch (MalformedURLException e) {
+                       logger.error("bad URL in component description", e);
+               }
+       }
+
+       public void setSelection(Activity selection) {
+               this.selection = selection;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityMenuAction.java
new file mode 100644
index 0000000..6f28816
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/OpenComponentFromComponentActivityMenuAction.java
@@ -0,0 +1,104 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import io.github.taverna_extras.component.api.ComponentFactory;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+
+import org.apache.taverna.scufl2.api.activity.Activity;
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.ui.menu.AbstractContextualMenuAction;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.selection.SelectionManager;
+import org.apache.taverna.workbench.views.graph.GraphViewComponent;
+
+/**
+ * @author alanrw
+ */
+public class OpenComponentFromComponentActivityMenuAction extends
+               AbstractContextualMenuAction {
+       private static final URI configureSection = URI
+                       
.create("http://taverna.sf.net/2009/contextMenu/configure";);
+
+       private SelectionManager sm;
+       private FileManager fileManager;
+       private ComponentFactory factory;
+       private FileType fileType;
+       private GraphViewComponent graphView;
+       private ComponentServiceIcon icon;
+
+       public OpenComponentFromComponentActivityMenuAction() {
+               super(configureSection, 75);
+       }
+
+       public void setSelectionManager(SelectionManager sm) {
+               this.sm = sm;
+       }
+
+       public void setFileManager(FileManager fileManager) {
+               this.fileManager = fileManager;
+       }
+
+       public void setComponentFactory(ComponentFactory factory) {
+               this.factory = factory;
+       }
+
+       public void setFileType(FileType fileType) {
+               this.fileType = fileType;
+       }
+
+       public void setGraphView(GraphViewComponent graphView) {
+               this.graphView = graphView;
+       }
+
+       public void setIcon(ComponentServiceIcon icon) {
+               this.icon = icon;
+       }
+
+       @Override
+       public boolean isEnabled() {
+               return getSelectedActivity() != null;
+       }
+
+       @Override
+       protected Action createAction() {
+               OpenComponentFromComponentActivityAction action = new 
OpenComponentFromComponentActivityAction(
+                               fileManager, factory, fileType, graphView, 
icon);
+               action.setSelection(getSelectedActivity());
+               return action;
+       }
+
+       private Activity getSelectedActivity() {
+               Object selection = getContextualSelection().getSelection();
+               if (!super.isEnabled() || !(selection instanceof Processor))
+                       return null;
+
+               try {
+                       return ((Processor) 
selection).getActivity(sm.getSelectedProfile());
+               } catch (RuntimeException e) {
+                       return null;
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentAction.java
new file mode 100644
index 0000000..ebb51a8
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentAction.java
@@ -0,0 +1,275 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu;
+
+import static java.awt.BorderLayout.CENTER;
+import static java.awt.BorderLayout.SOUTH;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.OK_CANCEL_OPTION;
+import static javax.swing.JOptionPane.OK_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static 
io.github.taverna_extras.component.api.config.ComponentPropertyNames.COMPONENT_NAME;
+import static 
io.github.taverna_extras.component.ui.ComponentActivityConfigurationBean.ignorableNames;
+import static io.github.taverna_extras.component.ui.util.Utils.uniqueName;
+import static org.apache.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+
+import io.github.taverna_extras.component.api.Component;
+import io.github.taverna_extras.component.api.ComponentFactory;
+import io.github.taverna_extras.component.api.Family;
+import io.github.taverna_extras.component.api.Registry;
+import io.github.taverna_extras.component.api.Version;
+import 
io.github.taverna_extras.component.ui.ComponentActivityConfigurationBean;
+import io.github.taverna_extras.component.ui.panel.ComponentChooserPanel;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import io.github.taverna_extras.component.ui.util.Utils;
+
+import org.apache.taverna.scufl2.api.activity.Activity;
+import org.apache.taverna.scufl2.api.common.Scufl2Tools;
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.port.InputActivityPort;
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputActivityPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+import org.apache.taverna.workbench.edits.CompoundEdit;
+import org.apache.taverna.workbench.edits.Edit;
+import org.apache.taverna.workbench.edits.EditException;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.selection.SelectionManager;
+import org.apache.taverna.workflow.edits.AddActivityEdit;
+import org.apache.taverna.workflow.edits.AddActivityInputPortMappingEdit;
+import org.apache.taverna.workflow.edits.AddActivityOutputPortMappingEdit;
+import org.apache.taverna.workflow.edits.RemoveActivityEdit;
+import org.apache.taverna.workflow.edits.RenameEdit;
+
+/**
+ * @author alanrw
+ */
+public class ReplaceByComponentAction extends AbstractAction {
+       private static final long serialVersionUID = 7364648399658711574L;
+
+       private final EditManager em;
+       private final ComponentPreference prefs;
+       private final SelectionManager sm;
+       private final ComponentFactory factory;
+       private final Scufl2Tools tools = new Scufl2Tools();
+
+       private Processor selection;
+
+       public ReplaceByComponentAction(ComponentPreference prefs,
+                       ComponentFactory factory, EditManager em, 
SelectionManager sm,
+                       ComponentServiceIcon icon) {
+               super("Replace by component...", icon.getIcon());
+               this.prefs = prefs;
+               this.em = em;
+               this.sm = sm;
+               this.factory = factory;
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               JPanel overallPanel = new JPanel(new BorderLayout());
+               ComponentChooserPanel panel = new ComponentChooserPanel(prefs);
+               overallPanel.add(panel, CENTER);
+               JPanel checkBoxPanel = new JPanel(new FlowLayout());
+               JCheckBox replaceAllCheckBox = new JCheckBox(
+                               "Replace all matching services");
+               checkBoxPanel.add(replaceAllCheckBox);
+               checkBoxPanel.add(new JSeparator());
+               JCheckBox renameServicesCheckBox = new JCheckBox("Rename 
service(s)");
+               checkBoxPanel.add(renameServicesCheckBox);
+               renameServicesCheckBox.setSelected(true);
+               overallPanel.add(checkBoxPanel, SOUTH);
+               int answer = showConfirmDialog(null, overallPanel, "Component 
choice",
+                               OK_CANCEL_OPTION);
+               if (answer == OK_OPTION)
+                       doReplace(panel.getChosenRegistry(), 
panel.getChosenFamily(),
+                                       replaceAllCheckBox.isSelected(),
+                                       renameServicesCheckBox.isSelected(),
+                                       panel.getChosenComponent());
+       }
+
+       private void doReplace(Registry chosenRegistry, Family chosenFamily,
+                       boolean replaceAll, boolean rename, Component 
chosenComponent) {
+               Version chosenVersion = 
chosenComponent.getComponentVersionMap().get(
+                               
chosenComponent.getComponentVersionMap().lastKey());
+               Version.ID ident = new Version.Identifier(
+                               chosenRegistry.getRegistryBase(), 
chosenFamily.getName(),
+                               chosenComponent.getName(), 
chosenVersion.getVersionNumber());
+
+               ComponentActivityConfigurationBean cacb = new 
ComponentActivityConfigurationBean(
+                               ident, factory);
+
+               try {
+                       if (replaceAll) {
+                               Activity baseActivity = selection.getActivity(sm
+                                               .getSelectedProfile());
+                               URI activityType = baseActivity.getType();
+                               String configString = 
getConfigString(baseActivity);
+
+                               replaceAllMatchingActivities(activityType, 
cacb, configString,
+                                               rename, 
sm.getSelectedWorkflow());
+                       } else
+                               replaceActivity(cacb, selection, rename,
+                                               sm.getSelectedWorkflow());
+               } catch (Exception e) {
+                       showMessageDialog(
+                                       null,
+                                       "Failed to replace nested workflow with 
component: "
+                                                       + e.getMessage(), 
"Component Problem",
+                                       ERROR_MESSAGE);
+               }
+       }
+
+       private String getConfigString(Activity baseActivity) {
+               return baseActivity.getConfiguration().getJsonAsString();
+       }
+
+       private void replaceAllMatchingActivities(URI activityType,
+                       ComponentActivityConfigurationBean cacb, String 
configString,
+                       boolean rename, Workflow d) throws 
IntermediateException {
+               for (Processor p : d.getProcessors()) {
+                       Activity a = p.getActivity(sm.getSelectedProfile());
+                       if (a.getType().equals(activityType)
+                                       && 
getConfigString(a).equals(configString))
+                               replaceActivity(cacb, p, rename, d);
+                       else if (a.getType().equals(NESTED_WORKFLOW))
+                               replaceAllMatchingActivities(activityType, 
cacb, configString,
+                                               rename,
+                                               
tools.nestedWorkflowForProcessor(p, a.getParent()));
+               }
+       }
+
+       private void replaceActivity(ComponentActivityConfigurationBean cacb,
+                       Processor p, boolean rename, Workflow d) throws 
IntermediateException {
+               final Activity originalActivity = 
p.getActivity(sm.getSelectedProfile());
+               final List<Edit<?>> currentWorkflowEditList = new ArrayList<>();
+                               
+               Activity replacementActivity = new Activity();
+               try {
+                       URI configType;
+                       replacementActivity.createConfiguration(configType);
+                       
+                       replacementActivity.configure(cacb);
+                       //FIXME
+               } catch (Exception e) {
+                       throw new IntermediateException(
+                                       "Unable to configure component", e);
+               }
+               if (originalActivity.getInputPorts().size() != 
replacementActivity
+                               .getInputPorts().size())
+                       throw new IntermediateException(
+                                       "Component does not have matching 
ports", null);
+
+               int replacementOutputSize = 
replacementActivity.getOutputPorts().size();
+               int originalOutputSize = 
originalActivity.getOutputPorts().size();
+               for (String name : ignorableNames) {
+                       if (originalActivity.getOutputPorts().getByName(name) 
!= null)
+                               originalOutputSize--;
+                       if 
(replacementActivity.getOutputPorts().getByName(name) != null)
+                               replacementOutputSize--;
+               }
+
+               int sizeDifference = replacementOutputSize - originalOutputSize;
+               if (sizeDifference != 0)
+                       throw new IntermediateException(
+                                       "Component does not have matching 
ports", null);
+
+               for (InputActivityPort aip : originalActivity.getInputPorts()) {
+                       String aipName = aip.getName();
+                       int aipDepth = aip.getDepth();
+                       InputActivityPort caip = 
replacementActivity.getInputPorts().getByName(aipName);
+                       if ((caip == null) || (caip.getDepth() != aipDepth))
+                               throw new RuntimeException("Original input port 
"
+                                               + aipName + " is not matched");
+               }
+               for (OutputActivityPort aop : 
originalActivity.getOutputPorts()) {
+                       String aopName = aop.getName();
+                       int aopDepth = aop.getDepth();
+                       OutputActivityPort caop = 
replacementActivity.getOutputPorts().getByName(aopName);
+                       if ((caop == null || aopDepth != caop.getDepth())
+                                       && !ignorableNames.contains(aopName))
+                               throw new IntermediateException(
+                                               "Original output port " + 
aopName + " is not matched", null);
+               }
+
+               for (InputProcessorPort pip : p.getInputPorts()) {
+                       InputActivityPort iap = 
replacementActivity.getInputPorts()
+                                       .getByName(pip.getName());
+                       if (iap == null)
+                               iap = new 
InputActivityPort(replacementActivity, pip.getName());
+                       currentWorkflowEditList.add(new 
AddActivityInputPortMappingEdit(
+                                       replacementActivity, pip, iap));
+               }
+
+               for (OutputProcessorPort pop : p.getOutputPorts()) {
+                       OutputActivityPort oap = 
replacementActivity.getOutputPorts()
+                                       .getByName(pop.getName());
+                       if (oap == null)
+                               oap = new 
OutputActivityPort(replacementActivity, pop.getName());
+                       currentWorkflowEditList.add(new 
AddActivityOutputPortMappingEdit(
+                                       replacementActivity, pop, oap));
+               }
+
+               currentWorkflowEditList
+                               .add(new AddActivityEdit(p, 
replacementActivity));
+               currentWorkflowEditList
+                               .add(new RemoveActivityEdit(p, 
originalActivity));
+               
+               if (rename) {
+                       String possibleName = 
replacementActivity.getConfiguration()
+                                       
.getJsonAsObjectNode().get(COMPONENT_NAME).textValue();
+                       currentWorkflowEditList.add(new RenameEdit<>(p, 
uniqueName(
+                                       possibleName, d.getProcessors())));
+               }
+               try {
+                       em.doDataflowEdit(d.getParent(), new CompoundEdit(
+                                       currentWorkflowEditList));
+               } catch (EditException e) {
+                       throw new IntermediateException(
+                                       "Unable to replace with component", e);
+               }
+       }
+
+       public void setSelection(Processor selection) {
+               this.selection = selection;
+       }
+
+       @SuppressWarnings("serial")
+       private static class IntermediateException extends Exception {
+               IntermediateException(String msg, Throwable cause) {
+                       super(msg, cause);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentMenuAction.java
new file mode 100644
index 0000000..470ad40
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/ReplaceByComponentMenuAction.java
@@ -0,0 +1,87 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import io.github.taverna_extras.component.api.ComponentFactory;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.ui.menu.AbstractContextualMenuAction;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.selection.SelectionManager;
+
+/**
+ * @author alanrw
+ */
+public class ReplaceByComponentMenuAction extends AbstractContextualMenuAction 
{
+       private static final URI configureSection = URI
+                       
.create("http://taverna.sf.net/2009/contextMenu/configure";);
+
+       private ComponentPreference preferences;
+       private EditManager editManager;
+       private SelectionManager selectionManager;
+       private ComponentFactory factory;
+       private ComponentServiceIcon icon;
+
+       public ReplaceByComponentMenuAction() {
+               super(configureSection, 75);
+       }
+
+       public void setPreferences(ComponentPreference preferences) {
+               this.preferences = preferences;
+       }
+
+       public void setEditManager(EditManager editManager) {
+               this.editManager = editManager;
+       }
+
+       public void setSelectionManager(SelectionManager selectionManager) {
+               this.selectionManager = selectionManager;
+       }
+
+       public void setComponentFactory(ComponentFactory factory) {
+               this.factory = factory;
+       }
+
+       public void setIcon(ComponentServiceIcon icon) {
+               this.icon = icon;
+       }
+
+       @Override
+       public boolean isEnabled() {
+               Object selection = getContextualSelection().getSelection();
+               if (!super.isEnabled())
+                       return false;
+               return (selection instanceof Processor);
+       }
+
+       @Override
+       protected Action createAction() {
+               ReplaceByComponentAction action = new ReplaceByComponentAction(
+                               preferences, factory, editManager, 
selectionManager, icon);
+               action.setSelection((Processor) 
getContextualSelection().getSelection());
+               return action;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/AbstractComponentMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/AbstractComponentMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/AbstractComponentMenuAction.java
new file mode 100644
index 0000000..671e190
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/AbstractComponentMenuAction.java
@@ -0,0 +1,31 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu.component;
+
+import static 
io.github.taverna_extras.component.ui.menu.component.ComponentMenuSection.COMPONENT_SECTION;
+
+import java.net.URI;
+import org.apache.taverna.ui.menu.AbstractMenuAction;
+
+abstract class AbstractComponentMenuAction extends AbstractMenuAction {
+       public AbstractComponentMenuAction(int positionHint, URI id) {
+               super(COMPONENT_SECTION, positionHint, id);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseAction.java
new file mode 100644
index 0000000..da22a60
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseAction.java
@@ -0,0 +1,73 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu.component;
+
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+
+import org.apache.log4j.Logger;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import io.github.taverna_extras.component.ui.util.Utils;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.events.FileManagerEvent;
+
+/**
+ * @author alanrw
+ */
+public class ComponentCloseAction extends AbstractAction implements
+               Observer<FileManagerEvent> {
+       private static final long serialVersionUID = -153986599735293879L;
+       private static final String CLOSE_COMPONENT = "Close component";
+       @SuppressWarnings("unused")
+       private static Logger logger = getLogger(ComponentCloseAction.class);
+
+       private Action closeAction;
+       private final Utils utils;
+
+       public ComponentCloseAction(Action closeWorkflowAction, FileManager fm,
+                       ComponentServiceIcon icon, Utils utils) {
+               super(CLOSE_COMPONENT, icon.getIcon());
+               closeAction = closeWorkflowAction;
+               this.utils = utils;
+               fm.addObserver(this);
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent arg0) {
+               closeAction.actionPerformed(arg0);
+       }
+
+       @Override
+       public boolean isEnabled() {
+               return utils.currentDataflowIsComponent();
+       }
+
+       @Override
+       public void notify(Observable<FileManagerEvent> sender,
+                       FileManagerEvent message) throws Exception {
+               setEnabled(utils.currentDataflowIsComponent());
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseMenuAction.java
new file mode 100644
index 0000000..6c40a5f
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCloseMenuAction.java
@@ -0,0 +1,66 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu.component;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import io.github.taverna_extras.component.ui.util.Utils;
+import org.apache.taverna.workbench.file.FileManager;
+
+/**
+ * @author alanrw
+ */
+public class ComponentCloseMenuAction extends AbstractComponentMenuAction {
+       private static final URI CLOSE_COMPONENT_URI = URI
+                       
.create("http://taverna.sf.net/2008/t2workbench/menu#componentClose";);
+
+       private Action action;
+       private FileManager fm;
+       private ComponentServiceIcon icon;
+       private Utils utils;
+
+       public ComponentCloseMenuAction() {
+               super(1000, CLOSE_COMPONENT_URI);
+       }
+       
+       public void setCloseWorkflowAction(Action action) {
+               this.action = action;
+       }
+
+       public void setFileManager(FileManager fm) {
+               this.fm = fm;
+       }
+
+       public void setIcon(ComponentServiceIcon icon) {
+               this.icon = icon;
+       }
+
+       public void setUtils(Utils utils) {
+               this.utils = utils;
+       }
+
+       @Override
+       protected Action createAction() {
+               return new ComponentCloseAction(action, fm, icon, utils);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyAction.java
new file mode 100644
index 0000000..676a6c9
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyAction.java
@@ -0,0 +1,164 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu.component;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.WEST;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.OK_CANCEL_OPTION;
+import static javax.swing.JOptionPane.OK_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JPanel;
+import javax.swing.border.TitledBorder;
+
+import org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.Component;
+import io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.Family;
+import io.github.taverna_extras.component.api.Version;
+import io.github.taverna_extras.component.api.profile.Profile;
+import io.github.taverna_extras.component.ui.panel.ComponentChoiceMessage;
+import io.github.taverna_extras.component.ui.panel.ComponentChooserPanel;
+import io.github.taverna_extras.component.ui.panel.ProfileChoiceMessage;
+import 
io.github.taverna_extras.component.ui.panel.RegistryAndFamilyChooserPanel;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceProviderConfig;
+import io.github.taverna_extras.component.ui.util.Utils;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+
+/**
+ * @author alanrw
+ */
+public class ComponentCopyAction extends AbstractAction {
+       private static final long serialVersionUID = -4440978712410081685L;
+       private static final Logger logger = 
getLogger(ComponentCopyAction.class);
+       private static final String COPY_COMPONENT = "Copy component...";
+
+       private final ComponentPreference prefs;
+       private final Utils utils;
+
+       public ComponentCopyAction(ComponentPreference pref, 
ComponentServiceIcon icon, Utils utils) {
+               super(COPY_COMPONENT, icon.getIcon());
+               this.prefs = pref;
+               this.utils = utils;
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent arg0) {
+               JPanel overallPanel = new JPanel();
+               overallPanel.setLayout(new GridBagLayout());
+
+               GridBagConstraints gbc = new GridBagConstraints();
+
+               ComponentChooserPanel source = new ComponentChooserPanel(prefs);
+               source.setBorder(new TitledBorder("Source component"));
+
+               gbc.insets = new Insets(0, 5, 0, 5);
+               gbc.gridx = 0;
+               gbc.gridy = 0;
+               gbc.anchor = WEST;
+               gbc.fill = BOTH;
+               gbc.gridwidth = 2;
+               gbc.weightx = 1;
+               overallPanel.add(source, gbc);
+
+               final RegistryAndFamilyChooserPanel target = new 
RegistryAndFamilyChooserPanel(prefs);
+               target.setBorder(new TitledBorder("Target family"));
+               gbc.gridy++;
+               overallPanel.add(target, gbc);
+
+               source.addObserver(new Observer<ComponentChoiceMessage>() {
+                       @Override
+                       public void notify(Observable<ComponentChoiceMessage> 
sender,
+                                       ComponentChoiceMessage message) throws 
Exception {
+                               Profile componentProfile = null;
+                               Family componentFamily = 
message.getComponentFamily();
+                               if (componentFamily != null)
+                                       componentProfile = 
componentFamily.getComponentProfile();
+                               ProfileChoiceMessage profileMessage = new 
ProfileChoiceMessage(
+                                               componentProfile);
+                               target.notify(null, profileMessage);
+                       }
+               });
+
+               int answer = showConfirmDialog(null, overallPanel, "Copy 
Component",
+                               OK_CANCEL_OPTION);
+               if (answer == OK_OPTION)
+                       doCopy(source.getChosenComponent(), 
target.getChosenFamily());
+       }
+
+       private void doCopy(Component sourceComponent, Family targetFamily) {
+               if (sourceComponent == null) {
+                       showMessageDialog(null, "Unable to determine source 
component",
+                                       "Component Copy Problem", 
ERROR_MESSAGE);
+                       return;
+               } else if (targetFamily == null) {
+                       showMessageDialog(null, "Unable to determine target 
family",
+                                       "Component Copy Problem", 
ERROR_MESSAGE);
+                       return;
+               }
+
+               try {
+                       String componentName = sourceComponent.getName();
+                       boolean alreadyUsed = 
targetFamily.getComponent(componentName) != null;
+                       if (alreadyUsed)
+                               showMessageDialog(null, componentName + " is 
already used",
+                                               "Duplicate component name", 
ERROR_MESSAGE);
+                       else {
+                               Version targetVersion = doCopy(sourceComponent, 
targetFamily,
+                                               componentName);
+                               try {
+                                       
utils.refreshComponentServiceProvider(new ComponentServiceProviderConfig(
+                                                       
targetVersion.getID()).getConfiguration());
+                               } catch (Exception e) {
+                                       logger.error(e);
+                               }
+                       }
+               } catch (ComponentException e) {
+                       logger.error("failed to copy component", e);
+                       showMessageDialog(null,
+                                       "Unable to create component: " + 
e.getMessage(),
+                                       "Component Copy Problem", 
ERROR_MESSAGE);
+               }
+       }
+
+       private Version doCopy(Component sourceComponent, Family targetFamily,
+                       String componentName) throws ComponentException {
+               return targetFamily
+                               .createComponentBasedOn(
+                                               componentName,
+                                               
sourceComponent.getDescription(),
+                                               sourceComponent
+                                                               
.getComponentVersionMap()
+                                                               
.get(sourceComponent.getComponentVersionMap()
+                                                                               
.lastKey()).getImplementation());
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyMenuAction.java
new file mode 100644
index 0000000..3c6db63
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCopyMenuAction.java
@@ -0,0 +1,61 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu.component;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import io.github.taverna_extras.component.ui.util.Utils;
+
+/**
+ * @author alanrw
+ */
+public class ComponentCopyMenuAction extends AbstractComponentMenuAction {
+       private static final URI COPY_COMPONENT_URI = URI
+                       
.create("http://taverna.sf.net/2008/t2workbench/menu#componentCopy";);
+
+       private ComponentPreference prefs;
+       private ComponentServiceIcon icon;
+       private Utils utils;
+
+       public ComponentCopyMenuAction() {
+               super(800, COPY_COMPONENT_URI);
+       }
+       
+       public void setIcon(ComponentServiceIcon icon) {
+               this.icon = icon;
+       }
+
+       public void setPreferences(ComponentPreference prefs) {//FIXME 
beaninject
+               this.prefs = prefs;
+       }
+
+       public void setUtils(Utils utils) {
+               this.utils = utils;
+       }
+
+       @Override
+       protected Action createAction() {
+               return new ComponentCopyAction(prefs, icon, utils);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCreatorSupport.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCreatorSupport.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCreatorSupport.java
new file mode 100644
index 0000000..ffe26d8
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentCreatorSupport.java
@@ -0,0 +1,257 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu.component;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.OK_CANCEL_OPTION;
+import static javax.swing.JOptionPane.OK_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static org.apache.log4j.Logger.getLogger;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.Component;
+import io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.ComponentFactory;
+import io.github.taverna_extras.component.api.Version;
+import 
io.github.taverna_extras.component.ui.ComponentActivityConfigurationBean;
+import 
io.github.taverna_extras.component.ui.panel.RegistryAndFamilyChooserComponentEntryPanel;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceProviderConfig;
+import io.github.taverna_extras.component.ui.util.Utils;
+
+import org.apache.taverna.scufl2.api.activity.Activity;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.DataLink;
+import org.apache.taverna.scufl2.api.core.Processor;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.port.InputProcessorPort;
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputProcessorPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+import org.apache.taverna.workbench.edits.CompoundEdit;
+import org.apache.taverna.workbench.edits.Edit;
+import org.apache.taverna.workbench.edits.EditException;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.file.FileManager;
+import org.apache.taverna.workbench.file.FileType;
+import org.apache.taverna.workbench.file.exceptions.SaveException;
+import org.apache.taverna.workbench.selection.SelectionManager;
+import org.apache.taverna.workflow.edits.AddActivityEdit;
+import org.apache.taverna.workflow.edits.AddActivityInputPortMappingEdit;
+import org.apache.taverna.workflow.edits.AddActivityOutputPortMappingEdit;
+import org.apache.taverna.workflow.edits.AddDataLinkEdit;
+import org.apache.taverna.workflow.edits.AddProcessorEdit;
+import org.apache.taverna.workflow.edits.AddWorkflowInputPortEdit;
+import org.apache.taverna.workflow.edits.AddWorkflowOutputPortEdit;
+import org.apache.taverna.workflow.edits.RemoveActivityEdit;
+import org.apache.taverna.workflow.edits.RenameEdit;
+import org.apache.taverna.workflowmodel.processor.activity.NestedDataflow;
+import org.apache.taverna.workflowmodel.utils.Tools;
+
+public class ComponentCreatorSupport {
+       private static final Logger logger = 
getLogger(ComponentCreatorSupport.class);
+
+       private ComponentFactory factory;
+       private FileManager fm;
+       private EditManager em;
+       private ComponentPreference prefs;
+       private FileType ft;
+       private SelectionManager sm;
+
+       public void setComponentFactory(ComponentFactory factory) {
+               this.factory = factory;
+       }
+
+       public void setPreferences(ComponentPreference pref) {
+               this.prefs = pref;
+       }
+
+       public void setFileManager(FileManager fm) {
+               this.fm = fm;
+       }
+
+       public void setEditManager(EditManager em) {
+               this.em = em;
+       }
+
+       public void setFileType(FileType ft) {
+               this.ft = ft;
+       }
+
+       public void setSelectionManager(SelectionManager sm) {
+               this.sm = sm;
+       }
+
+       public class CopiedProcessor {
+               Processor processor;
+               Map<String,Workflow> requiredSubworkflows;
+       }
+
+       void moveComponentActivityIntoPlace(Activity toReplace, Processor 
contextProcessor,
+                       Activity replacingActivity) throws EditException {
+               List<Edit<?>> editsToDo = new ArrayList<>();
+               for (InputProcessorPort pip : contextProcessor.getInputPorts())
+                       editsToDo.add(new 
AddActivityInputPortMappingEdit(toReplace, pip, null/*FIXME*/));
+               for (OutputProcessorPort pop : 
contextProcessor.getOutputPorts())
+                       editsToDo.add(new 
AddActivityOutputPortMappingEdit(toReplace, pop, null/*FIXME*/));
+               editsToDo.add(new RemoveActivityEdit(contextProcessor, 
toReplace));
+               editsToDo.add(new AddActivityEdit(contextProcessor, 
replacingActivity));
+               em.doDataflowEdit(contextProcessor.getParent().getParent(),
+                               new CompoundEdit(editsToDo));
+       }
+
+       void connectNewProcessor(Workflow d, Processor newProcessor)
+                       throws EditException {
+               List<Edit<?>> editsToDo = new ArrayList<>();
+
+               for (InputProcessorPort pip : newProcessor.getInputPorts()) {
+                       InputWorkflowPort dip = new InputWorkflowPort(d, 
pip.getName());
+                       // FIXME How to set depth?
+                       editsToDo.add(new AddWorkflowInputPortEdit(d, dip));
+                       editsToDo.add(new AddDataLinkEdit(d, new DataLink(d, 
dip, pip)));
+               }
+
+               for (OutputProcessorPort pop : newProcessor.getOutputPorts()) {
+                       OutputWorkflowPort dop = new OutputWorkflowPort(d, 
pop.getName());
+                       // TODO How to indicate depth?
+                       editsToDo.add(new AddWorkflowOutputPortEdit(d, dop));
+                       editsToDo.add(new AddDataLinkEdit(d, new DataLink(d, 
pop, dop)));
+               }
+               em.doDataflowEdit(d.getParent(), new CompoundEdit(editsToDo));
+       }
+
+       public ComponentActivityConfigurationBean saveWorkflowAsComponent(
+                       WorkflowBundle d, Version.ID ident) throws 
SaveException, IOException,
+                       ComponentException {
+               if (ident == null)
+                       return null;
+
+               createInitialComponent(d, ident);
+
+               Utils.refreshComponentServiceProvider(new 
ComponentServiceProviderConfig(
+                               ident));
+               return new ComponentActivityConfigurationBean(ident, factory);
+       }
+
+       public ComponentActivityConfigurationBean saveWorkflowAsComponent(
+                       Workflow d, Version.ID ident) throws SaveException, 
IOException,
+                       ComponentException {
+               WorkflowBundle b = new WorkflowBundle();
+               ((Workflow)d.clone()).setParent(b);
+               //FIXME also must copy profile parts!
+               return saveWorkflowAsComponent(b, ident);
+       }
+
+       Version.ID getNewComponentIdentification(String defaultName) {
+               RegistryAndFamilyChooserComponentEntryPanel panel = new 
RegistryAndFamilyChooserComponentEntryPanel(prefs);
+               panel.setComponentName(defaultName);
+               int result = showConfirmDialog(null, panel, "Component 
location",
+                               OK_CANCEL_OPTION);
+               if (result != OK_OPTION)
+                       return null;
+
+               Version.ID ident = panel.getComponentVersionIdentification();
+               if (ident == null) {
+                       showMessageDialog(null,
+                                       "Not enough information to create 
component",
+                                       "Component creation problem", 
ERROR_MESSAGE);
+                       return null;
+               }
+
+               try {
+                       Component existingComponent = 
factory.getComponent(ident);
+                       if (existingComponent != null) {
+                               showMessageDialog(null,
+                                               "Component with this name 
already exists",
+                                               "Component creation problem", 
ERROR_MESSAGE);
+                               return null;
+                       }
+               } catch (ComponentException e) {
+                       logger.error("failed to search registry", e);
+                       showMessageDialog(null,
+                                       "Problem searching registry: " + 
e.getMessage(),
+                                       "Component creation problem", 
ERROR_MESSAGE);
+                       return null;
+               }
+               return ident;
+       }
+
+       CopiedProcessor copyProcessor(Processor p) throws IOException {
+               CopiedProcessor copy = new CopiedProcessor();
+               copy.processor = 
ProcessorXMLSerializer.getInstance().processorToXML(p);
+               copy.requiredSubworkflows = new HashMap<>();
+               rememberSubworkflows(p, copy);
+               return copy;
+       }
+
+       void rememberSubworkflows(Processor p, CopiedProcessor copy) {
+               for (Activity a : p.getActivity(sm.getSelectedProfile()))
+                       if (a instanceof NestedDataflow) {
+                               NestedDataflow da = (NestedDataflow) a;
+                               Workflow df = da.getNestedDataflow();
+                               if 
(!copy.requiredSubworkflows.containsKey(df.getIdentifier())) {
+                                       
copy.requiredSubworkflows.put(df.getIdentifier(),
+                                                       
DataflowXMLSerializer.getInstance()
+                                                                       
.serializeDataflow(df));
+                                       for (Processor sp : df.getProcessors())
+                                               rememberSubworkflows(sp, copy);
+                               }
+                       }
+       }
+
+       public Processor pasteProcessor(CopiedProcessor copy, Workflow d)
+                       throws 
+                       ClassNotFoundException, InstantiationException,
+                       IllegalAccessException, EditException {
+               Processor result = ProcessorXMLDeserializer.getInstance()
+                               .deserializeProcessor(copy.processor, 
copy.requiredSubworkflows);
+               if (result == null)
+                       return null;
+
+               String newName = Tools.uniqueProcessorName(result.getName(), d);
+
+               List<Edit<?>> editList = new ArrayList<>();
+               if (!newName.equals(result.getName()))
+                       editList.add(new RenameEdit<>(result, newName));
+               editList.add(new AddProcessorEdit(d, result));
+               em.doDataflowEdit(d.getParent(), new CompoundEdit(editList));
+
+               return result;
+       }
+
+       public Version.ID createInitialComponent(WorkflowBundle d, Version.ID 
ident)
+                       throws ComponentException {
+               try {
+                       fm.saveDataflow(d, ft, ident, false);
+
+                       em.doDataflowEdit(d, new RenameEdit<>(d, d.getName()));
+               } catch (SaveException | IllegalStateException | EditException 
e) {
+                       throw new ComponentException(e);
+               }
+               return ident;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentDeleteAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentDeleteAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentDeleteAction.java
new file mode 100644
index 0000000..72141c9
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/component/ComponentDeleteAction.java
@@ -0,0 +1,143 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package io.github.taverna_extras.component.ui.menu.component;
+
+import static java.lang.String.format;
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.OK_CANCEL_OPTION;
+import static javax.swing.JOptionPane.OK_OPTION;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.YES_OPTION;
+import static javax.swing.JOptionPane.showConfirmDialog;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.event.ActionEvent;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.AbstractAction;
+import javax.swing.SwingWorker;
+
+import org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.Component;
+import io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.Version;
+import io.github.taverna_extras.component.ui.panel.ComponentChooserPanel;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceProviderConfig;
+import io.github.taverna_extras.component.ui.util.Utils;
+
+import org.apache.taverna.scufl2.api.configurations.Configuration;
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.workbench.file.FileManager;
+
+/**
+ * @author alanrw
+ */
+public class ComponentDeleteAction extends AbstractAction {
+       private static final String COMPONENT_PROBLEM_TITLE = "Component 
Problem";
+       private static final String CONFIRM_MSG = "Are you sure you want to 
delete %s?";
+       private static final String CONFIRM_TITLE = "Delete Component 
Confirmation";
+       private static final String DELETE_COMPONENT_LABEL = "Delete 
component...";
+       private static final String DELETE_FAILED_TITLE = "Component Deletion 
Error";
+       private static final String FAILED_MSG = "Unable to delete %s: %s";
+       private static final String OPEN_COMPONENT_MSG = "The component is 
open";
+       private static final String TITLE = "Component choice";
+       private static final String WHAT_COMPONENT_MSG = "Unable to determine 
component";
+       private static final long serialVersionUID = -2992743162132614936L;
+       private static final Logger logger = 
getLogger(ComponentDeleteAction.class);
+
+       private final FileManager fm;
+       private final ComponentPreference prefs;
+       private final Utils utils;
+
+       public ComponentDeleteAction(FileManager fm, ComponentPreference prefs,
+                       ComponentServiceIcon icon, Utils utils) {
+               super(DELETE_COMPONENT_LABEL, icon.getIcon());
+               this.fm = fm;
+               this.prefs = prefs;
+               this.utils = utils;
+       }
+
+       @Override
+       public void actionPerformed(ActionEvent evt) {
+               ComponentChooserPanel panel = new ComponentChooserPanel(prefs);
+               int answer = showConfirmDialog(null, panel, TITLE, 
OK_CANCEL_OPTION);
+               if (answer == OK_OPTION)
+                       doDelete(panel.getChosenComponent());
+       }
+
+       private void doDelete(final Component chosenComponent) {
+               if (chosenComponent == null) {
+                       showMessageDialog(null, WHAT_COMPONENT_MSG,
+                                       COMPONENT_PROBLEM_TITLE, ERROR_MESSAGE);
+               } else if (componentIsInUse(chosenComponent)) {
+                       showMessageDialog(null, OPEN_COMPONENT_MSG,
+                                       COMPONENT_PROBLEM_TITLE, ERROR_MESSAGE);
+               } else if (showConfirmDialog(null,
+                               format(CONFIRM_MSG, chosenComponent.getName()), 
CONFIRM_TITLE,
+                               YES_NO_OPTION) == YES_OPTION)
+                       new SwingWorker<Configuration, Object>() {
+                               @Override
+                               protected Configuration doInBackground() throws 
Exception {
+                                       return deleteComponent(chosenComponent);
+                               }
+
+                               @Override
+                               protected void done() {
+                                       refresh(chosenComponent, this);
+                               }
+                       }.execute();
+       }
+
+       private Configuration deleteComponent(Component component)
+                       throws ComponentException {
+               ComponentServiceProviderConfig config = new 
ComponentServiceProviderConfig(
+                               component.getFamily());
+               component.delete();
+               return config.getConfiguration();
+       }
+
+       protected void refresh(Component component,
+                       SwingWorker<Configuration, Object> worker) {
+               try {
+                       utils.refreshComponentServiceProvider(worker.get());
+               } catch (ExecutionException e) {
+                       logger.error("failed to delete component", 
e.getCause());
+                       showMessageDialog(
+                                       null,
+                                       format(FAILED_MSG, component.getName(), 
e.getCause()
+                                                       .getMessage()), 
DELETE_FAILED_TITLE, ERROR_MESSAGE);
+               } catch (InterruptedException e) {
+                       logger.warn("interrupted during component deletion", e);
+               }
+       }
+
+       private boolean componentIsInUse(Component component) {
+               for (WorkflowBundle d : fm.getOpenDataflows()) {
+                       Object dataflowSource = fm.getDataflowSource(d);
+                       if (dataflowSource instanceof Version.ID
+                                       && ((Version.ID) 
dataflowSource).mostlyEqualTo(component))
+                               return true;
+               }
+               return false;
+       }
+}


Reply via email to