You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/02/23 11:35:57 UTC

[42/58] [abbrv] incubator-taverna-plugin-component git commit: org.apache.taverna.component.ui

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/component/OpenWorkflowFromComponentAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/component/OpenWorkflowFromComponentAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/component/OpenWorkflowFromComponentAction.java
new file mode 100644
index 0000000..94e7fdb
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/component/OpenWorkflowFromComponentAction.java
@@ -0,0 +1,122 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.component;
+
+import static javax.swing.JOptionPane.CANCEL_OPTION;
+import static javax.swing.JOptionPane.OK_OPTION;
+import static javax.swing.JOptionPane.QUESTION_MESSAGE;
+import static javax.swing.JOptionPane.YES_NO_OPTION;
+import static javax.swing.JOptionPane.showOptionDialog;
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JOptionPane;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+import net.sf.taverna.t2.workbench.file.FileManager;
+import net.sf.taverna.t2.workbench.file.FileType;
+import net.sf.taverna.t2.workbench.file.exceptions.OpenException;
+import net.sf.taverna.t2.workbench.views.graph.GraphViewComponent;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.Component;
+import org.apache.taverna.component.api.Family;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.Version;
+import org.apache.taverna.component.ui.ComponentAction;
+import org.apache.taverna.component.ui.panel.ComponentChoiceMessage;
+import org.apache.taverna.component.ui.panel.ComponentVersionChooserPanel;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * @author alanrw
+ */
+public class OpenWorkflowFromComponentAction extends ComponentAction {
+	private static final long serialVersionUID = 7382677337746318211L;
+	private static final Logger logger = getLogger(OpenWorkflowFromComponentAction.class);
+	private static final String ACTION_NAME = "Open component...";
+	private static final String ACTION_DESCRIPTION = "Open the workflow that implements a component";
+
+	private final FileManager fm;
+	private final FileType ft;
+	private final ComponentPreference prefs;
+
+	public OpenWorkflowFromComponentAction(FileManager fm, FileType ft,
+			ComponentPreference prefs, GraphViewComponent graphView,
+			ComponentServiceIcon icon) {
+		super(ACTION_NAME, graphView);
+		this.fm = fm;
+		this.ft = ft;
+		this.prefs = prefs;
+		setIcon(icon);
+		putValue(SHORT_DESCRIPTION, ACTION_DESCRIPTION);
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent arg) {
+		final ComponentVersionChooserPanel panel = new ComponentVersionChooserPanel(prefs);	
+		
+		final JButton okay = new JButton("OK");
+		okay.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				getOptionPane((JComponent) e.getSource()).setValue(OK_OPTION);
+				doOpen(panel.getChosenRegistry(), panel.getChosenFamily(),
+						panel.getChosenComponent(),
+						panel.getChosenComponentVersion());
+			}
+		});
+		okay.setEnabled(false);
+		// Only enable the OK button of a component is not null
+		panel.getComponentChooserPanel().addObserver(
+				new Observer<ComponentChoiceMessage>() {
+					@Override
+					public void notify(
+							Observable<ComponentChoiceMessage> sender,
+							ComponentChoiceMessage message) throws Exception {
+						okay.setEnabled(message.getChosenComponent() != null);
+					}
+				});
+
+		final JButton cancel = new JButton("Cancel");
+		cancel.addActionListener(new ActionListener() {
+		    @Override
+		    public void actionPerformed(ActionEvent e) {
+                getOptionPane((JComponent)e.getSource()).setValue(CANCEL_OPTION);
+		    }
+		});
+
+		showOptionDialog(graphView, panel, "Component version choice",
+				YES_NO_OPTION, QUESTION_MESSAGE, null, new Object[] { okay,
+						cancel }, okay);
+	}
+	
+    protected JOptionPane getOptionPane(JComponent parent) {
+		if (parent instanceof JOptionPane)
+			return (JOptionPane) parent;
+		return getOptionPane((JComponent) parent.getParent());
+    }
+
+	private void doOpen(Registry registry, Family family, Component component,
+			Version version) {
+		Version.ID ident = new Version.Identifier(
+				registry.getRegistryBase(), family.getName(),
+				component.getName(), version.getVersionNumber());
+
+		try {
+			WorkflowBundle d = fm.openDataflow(ft, ident);
+			markGraphAsBelongingToComponent(d);
+		} catch (OpenException e) {
+			logger.error("Failed to open component definition", e);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateAction.java
new file mode 100644
index 0000000..33dbbd3
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateAction.java
@@ -0,0 +1,160 @@
+/**
+ *
+ */
+package org.apache.taverna.component.ui.menu.family;
+
+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.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.border.TitledBorder;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.License;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.SharingPolicy;
+import org.apache.taverna.component.api.profile.Profile;
+import org.apache.taverna.component.ui.panel.LicenseChooserPanel;
+import org.apache.taverna.component.ui.panel.ProfileChooserPanel;
+import org.apache.taverna.component.ui.panel.RegistryChooserPanel;
+import org.apache.taverna.component.ui.panel.SharingPolicyChooserPanel;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+/**
+ * @author alanrw
+ */
+public class ComponentFamilyCreateAction extends AbstractAction {
+	private static final long serialVersionUID = -7780471499146286881L;
+	private static final Logger logger = getLogger(ComponentFamilyCreateAction.class);
+	private static final String CREATE_FAMILY = "Create family...";
+
+	private ComponentPreference prefs;
+	private JPanel overallPanel;
+	private GridBagConstraints gbc;
+
+	public ComponentFamilyCreateAction(ComponentPreference prefs,
+			ComponentServiceIcon iconProvider) {
+		super(CREATE_FAMILY, iconProvider.getIcon());
+		this.prefs = prefs;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent arg0) {
+		overallPanel = new JPanel(new GridBagLayout());
+		gbc = new GridBagConstraints();
+
+		RegistryChooserPanel registryPanel = new RegistryChooserPanel(prefs);
+
+		gbc.insets.left = 5;
+		gbc.insets.right = 5;
+		gbc.gridx = 0;
+		gbc.anchor = WEST;
+		gbc.fill = BOTH;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1;
+		gbc.gridy++;
+		overallPanel.add(registryPanel, gbc);
+
+		ProfileChooserPanel profilePanel = new ProfileChooserPanel(
+				registryPanel);
+		gbc.gridx = 0;
+		gbc.weighty = 1;
+		gbc.gridy++;
+		overallPanel.add(profilePanel, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridwidth = 1;
+		gbc.weightx = 0;
+		gbc.weighty = 0;
+		gbc.gridy++;
+		overallPanel.add(new JLabel("Component family name:"), gbc);
+
+		gbc.gridx = 1;
+		gbc.weightx = 1;
+		JTextField familyNameField = new JTextField(60);
+		overallPanel.add(familyNameField, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridwidth = 2;
+		gbc.weightx = 0;
+		gbc.weighty = 0;
+		gbc.gridy++;
+		JTextArea familyDescription = new JTextArea(10, 60);
+		JScrollPane familyDescriptionPane = new JScrollPane(familyDescription);
+		familyDescriptionPane.setBorder(new TitledBorder("Family description"));
+		overallPanel.add(familyDescriptionPane, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1;
+		gbc.weighty = 1;
+		gbc.gridy++;
+		SharingPolicyChooserPanel permissionPanel = new SharingPolicyChooserPanel(
+				registryPanel);
+		overallPanel.add(permissionPanel, gbc);
+
+		gbc.gridy++;
+		LicenseChooserPanel licensePanel = new LicenseChooserPanel();
+		registryPanel.addObserver(licensePanel);
+		overallPanel.add(licensePanel, gbc);
+
+		int answer = showConfirmDialog(null, overallPanel,
+				"Create Component Family", OK_CANCEL_OPTION);
+		if (answer == OK_OPTION)
+			doCreate(registryPanel.getChosenRegistry(),
+					profilePanel.getChosenProfile(), familyNameField.getText(),
+					familyDescription.getText(),
+					permissionPanel.getChosenPermission(),
+					licensePanel.getChosenLicense());
+	}
+
+	private void doCreate(Registry chosenRegistry, Profile chosenProfile,
+			String newName, String familyDescription, SharingPolicy permission,
+			License license) {
+		if (chosenRegistry == null) {
+			showMessageDialog(null, "Unable to determine registry",
+					"Component Registry Problem", ERROR_MESSAGE);
+			return;
+		} else if (chosenProfile == null) {
+			showMessageDialog(null, "Unable to determine profile",
+					"Component Profile Problem", ERROR_MESSAGE);
+			return;
+		} else if ((newName == null) || newName.isEmpty()) {
+			showMessageDialog(null, "Name must be specified",
+					"Missing component family name", ERROR_MESSAGE);
+			return;
+		}
+
+		try {
+			if (chosenRegistry.getComponentFamily(newName) != null) {
+				showMessageDialog(null, newName + " is already used",
+						"Duplicate component family name", ERROR_MESSAGE);
+				return;
+			}
+			chosenRegistry.createComponentFamily(newName, chosenProfile,
+					familyDescription, license, permission);
+		} catch (ComponentException e) {
+			logger.error("failed to create family", e);
+			showMessageDialog(null,
+					"Unable to create family: " + e.getMessage(),
+					"Family creation problem", ERROR_MESSAGE);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateMenuAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateMenuAction.java
new file mode 100644
index 0000000..b0cb699
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyCreateMenuAction.java
@@ -0,0 +1,43 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.family;
+
+import static org.apache.taverna.component.ui.menu.family.ComponentFamilyMenuSection.COMPONENT_FAMILY_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * @author alanrw
+ */
+public class ComponentFamilyCreateMenuAction extends AbstractMenuAction {
+	private static final URI COMPONENT_FAMILY_CREATE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentFamilyCreate");
+
+	private ComponentPreference prefs;
+	private ComponentServiceIcon iconProvider;
+
+	public ComponentFamilyCreateMenuAction() {
+		super(COMPONENT_FAMILY_SECTION, 400, COMPONENT_FAMILY_CREATE_URI);
+	}
+
+	public void setPreferences(ComponentPreference prefs) {
+		this.prefs = prefs;
+	}
+
+	public void setIcon(ComponentServiceIcon iconProvider) {
+		this.iconProvider = iconProvider;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ComponentFamilyCreateAction(prefs, iconProvider);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteAction.java
new file mode 100644
index 0000000..9bd39b8
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteAction.java
@@ -0,0 +1,186 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.family;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.WEST;
+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.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.AbstractAction;
+import javax.swing.JPanel;
+import javax.swing.SwingWorker;
+
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.Family;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.Version;
+import org.apache.taverna.component.ui.panel.FamilyChooserPanel;
+import org.apache.taverna.component.ui.panel.RegistryChooserPanel;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceProviderConfig;
+import org.apache.taverna.component.ui.util.Utils;
+
+import uk.org.taverna.scufl2.api.configurations.Configuration;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+
+/**
+ * @author alanrw
+ */
+public class ComponentFamilyDeleteAction extends AbstractAction {
+	private static final String CONFIRM_MSG = "Are you sure you want to delete %s";
+	private static final String CONFIRM_TITLE = "Delete Component Family Confirmation";
+	private static final String DELETE_FAMILY_LABEL = "Delete family...";
+	private static final String ERROR_TITLE = "Component Family Deletion Error";
+	private static final String FAILED_MSG = "Unable to delete %s: %s";
+	private static final String FAMILY_FAIL_TITLE = "Component Family Problem";
+	private static final String OPEN_MSG = "Components in the family are open";
+	private static final String PICK_FAMILY_TITLE = "Delete Component Family";
+	private static final String REGISTRY_FAIL_TITLE = "Component Registry Problem";
+	private static final String WHAT_FAMILY_MSG = "Unable to determine family";
+	private static final String WHAT_REGISTRY_MSG = "Unable to determine registry";
+	private static final Logger logger = getLogger(ComponentFamilyDeleteAction.class);
+	private static final long serialVersionUID = -4976161883778371344L;
+
+	private final FileManager fm;
+	private final ComponentPreference prefs;
+	private final Utils utils;
+
+	public ComponentFamilyDeleteAction(FileManager fm,
+			ComponentPreference prefs, ComponentServiceIcon icon, Utils utils) {
+		super(DELETE_FAMILY_LABEL, icon.getIcon());
+		this.fm = fm;
+		this.prefs = prefs;
+		this.utils = utils;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent ev) {
+		JPanel overallPanel = new JPanel(new GridBagLayout());
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		RegistryChooserPanel registryPanel = new RegistryChooserPanel(prefs);
+
+		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(registryPanel, gbc);
+
+		FamilyChooserPanel familyPanel = new FamilyChooserPanel(registryPanel);
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.weighty = 1;
+		overallPanel.add(familyPanel, gbc);
+
+		int answer = showConfirmDialog(null, overallPanel, PICK_FAMILY_TITLE,
+				OK_CANCEL_OPTION);
+		if (answer == OK_OPTION)
+			deletionActionFlow(registryPanel.getChosenRegistry(),
+					familyPanel.getChosenFamily());
+	}
+
+	/**
+	 * Check if the preconditions for the deletion action are satisfied.
+	 * 
+	 * @param chosenRegistry
+	 *            What registry contains the family.
+	 * @param chosenFamily
+	 */
+	private void deletionActionFlow(Registry chosenRegistry,
+			final Family chosenFamily) {
+		if (chosenRegistry == null) {
+			showMessageDialog(null, WHAT_REGISTRY_MSG, REGISTRY_FAIL_TITLE,
+					ERROR_MESSAGE);
+			return;
+		} else if (chosenFamily == null) {
+			showMessageDialog(null, WHAT_FAMILY_MSG, FAMILY_FAIL_TITLE,
+					ERROR_MESSAGE);
+			return;
+		} else if (familyIsInUse(chosenRegistry, chosenFamily)) {
+			showMessageDialog(null, OPEN_MSG, FAMILY_FAIL_TITLE, ERROR_MESSAGE);
+			return;
+		} else if (showConfirmDialog(null,
+				format(CONFIRM_MSG, chosenFamily.getName()), CONFIRM_TITLE,
+				YES_NO_OPTION) == YES_OPTION)
+			new SwingWorker<ComponentServiceProviderConfig, Object>() {
+				@Override
+				protected ComponentServiceProviderConfig doInBackground()
+						throws Exception {
+					return deleteFamily(chosenFamily);
+				}
+
+				@Override
+				protected void done() {
+					deletionDone(chosenFamily, this);
+				}
+			}.execute();
+	}
+
+	private ComponentServiceProviderConfig deleteFamily(Family family)
+			throws ComponentException {
+		ComponentServiceProviderConfig config = new ComponentServiceProviderConfig(
+				family);
+		family.delete();
+		return config;
+	}
+
+	private void deletionDone(Family family,
+			SwingWorker<ComponentServiceProviderConfig, Object> worker) {
+		Configuration config;
+		try {
+		config = worker.get().getConfiguration();
+		} catch (InterruptedException e) {
+			logger.warn("interrupted during removal of component family", e);
+			return;
+		} catch (ExecutionException e) {
+			logger.error("failed to delete family", e.getCause());
+			showMessageDialog(
+					null,
+					format(FAILED_MSG, family.getName(), e.getCause()
+							.getMessage()), ERROR_TITLE, ERROR_MESSAGE);
+			return;
+		}
+		try {
+			utils.removeComponentServiceProvider(config);
+		} catch (Exception e) {
+			logger.error("failed to update service provider panel "
+					+ "after deleting family", e);
+		}
+	}
+
+	private boolean familyIsInUse(Registry chosenRegistry, Family chosenFamily) {
+		for (WorkflowBundle d : fm.getOpenDataflows()) {
+			Object dataflowSource = fm.getDataflowSource(d);
+			if (dataflowSource instanceof Version.ID) {
+				Version.ID ident = (Version.ID) dataflowSource;
+				if (ident.getRegistryBase().toString()
+						.equals(chosenRegistry.getRegistryBase().toString())
+						&& ident.getFamilyName().equals(chosenFamily.getName()))
+					return true;
+			}
+		}
+		return false;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteMenuAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteMenuAction.java
new file mode 100644
index 0000000..3e63cac
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyDeleteMenuAction.java
@@ -0,0 +1,54 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.family;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+import org.apache.taverna.component.ui.util.Utils;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.file.FileManager;
+
+/**
+ * @author alanrw
+ */
+public class ComponentFamilyDeleteMenuAction extends AbstractMenuAction {
+	private static final URI COMPONENT_FAMILY_DELETE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentFamilyDelete");
+
+	private FileManager fm;
+	private ComponentPreference prefs;
+	private ComponentServiceIcon icon;
+	private Utils utils;
+
+	public ComponentFamilyDeleteMenuAction() {
+		super(ComponentFamilyMenuSection.COMPONENT_FAMILY_SECTION, 500,
+				COMPONENT_FAMILY_DELETE_URI);
+	}
+
+	public void setFileManager(FileManager fm) {
+		this.fm = fm;
+	}
+
+	public void setIcon(ComponentServiceIcon icon) {
+		this.icon = icon;
+	}
+	
+	public void setPreferences(ComponentPreference prefs) {
+		this.prefs = prefs;
+	}
+
+	public void setUtils(Utils utils) {
+		this.utils = utils;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ComponentFamilyDeleteAction(fm, prefs, icon, utils);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyMenuSection.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyMenuSection.java
new file mode 100644
index 0000000..21b954b
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/family/ComponentFamilyMenuSection.java
@@ -0,0 +1,22 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.family;
+
+import java.net.URI;
+
+import org.apache.taverna.component.ui.menu.ComponentMenu;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author alanrw
+ */
+public class ComponentFamilyMenuSection extends AbstractMenuSection {
+	public static final URI COMPONENT_FAMILY_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentFamilySection");
+
+	public ComponentFamilyMenuSection() {
+		super(ComponentMenu.COMPONENT, 300, COMPONENT_FAMILY_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyAction.java
new file mode 100644
index 0000000..97ab164
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyAction.java
@@ -0,0 +1,161 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.profile;
+
+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 org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.License;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.SharingPolicy;
+import org.apache.taverna.component.api.profile.Profile;
+import org.apache.taverna.component.ui.panel.LicenseChooserPanel;
+import org.apache.taverna.component.ui.panel.ProfileChooserPanel;
+import org.apache.taverna.component.ui.panel.RegistryChooserPanel;
+import org.apache.taverna.component.ui.panel.SharingPolicyChooserPanel;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+/**
+ * @author alanrw
+ */
+public class ComponentProfileCopyAction extends AbstractAction {
+	private static final long serialVersionUID = 6332253931049645259L;
+	private static final Logger log = getLogger(ComponentProfileCopyAction.class);
+	private static final String COPY_PROFILE = "Copy profile...";
+
+	private final ComponentPreference prefs;
+
+	public ComponentProfileCopyAction(ComponentPreference prefs,
+			ComponentServiceIcon iconProvider) {
+		super(COPY_PROFILE, iconProvider.getIcon());
+		this.prefs = prefs;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent ev) {
+		JPanel overallPanel = new JPanel();
+		overallPanel.setLayout(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		RegistryChooserPanel sourceRegistryPanel = new RegistryChooserPanel(prefs);
+		sourceRegistryPanel.setBorder(new TitledBorder("Source registry"));
+
+		gbc.insets = new Insets(0, 5, 0, 5);
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.anchor = GridBagConstraints.WEST;
+		gbc.fill = GridBagConstraints.BOTH;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1;
+		overallPanel.add(sourceRegistryPanel, gbc);
+
+		ProfileChooserPanel profilePanel = new ProfileChooserPanel(sourceRegistryPanel);
+		profilePanel.setBorder(new TitledBorder("Source profile"));
+
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.weighty = 1;
+		overallPanel.add(profilePanel, gbc);
+
+		RegistryChooserPanel targetRegistryPanel = new RegistryChooserPanel(prefs);
+		targetRegistryPanel.setBorder(new TitledBorder("Target registry"));
+		gbc.gridy = 2;
+		overallPanel.add(targetRegistryPanel, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1;
+		gbc.weighty = 1;
+		gbc.gridy++;
+		SharingPolicyChooserPanel permissionPanel = new SharingPolicyChooserPanel(targetRegistryPanel);
+		overallPanel.add(permissionPanel, gbc);
+
+		gbc.gridy++;
+		LicenseChooserPanel licensePanel = new LicenseChooserPanel();
+		targetRegistryPanel.addObserver(licensePanel);
+		overallPanel.add(licensePanel, gbc);
+
+		int answer = showConfirmDialog(null, overallPanel,
+				"Copy Component Profile", OK_CANCEL_OPTION);
+		try {
+			if (answer == OK_OPTION) 
+				doCopy(sourceRegistryPanel.getChosenRegistry(),
+						profilePanel.getChosenProfile(),
+						targetRegistryPanel.getChosenRegistry(),
+						permissionPanel.getChosenPermission(),
+						licensePanel.getChosenLicense());
+		} catch (ComponentException e) {
+			log.error("failed to copy profile", e);
+			showMessageDialog(null, "Unable to save profile: " + e.getMessage(),
+					"Registry Exception", ERROR_MESSAGE);
+		}
+	}
+
+	private void doCopy(Registry sourceRegistry, Profile sourceProfile,
+			Registry targetRegistry, SharingPolicy permission, License license)
+			throws ComponentException {
+		if (sourceRegistry == null) {
+			showMessageDialog(null, "Unable to determine source registry",
+					"Component Registry Problem", ERROR_MESSAGE);
+			return;
+		}
+		if (targetRegistry == null) {
+			showMessageDialog(null, "Unable to determine target registry",
+					"Component Registry Problem", ERROR_MESSAGE);
+			return;
+		}
+		if (sourceRegistry.equals(targetRegistry)) {
+			showMessageDialog(null, "Cannot copy to the same registry",
+					"Copy Problem", ERROR_MESSAGE);
+			return;
+		}
+		if (sourceProfile == null) {
+			showMessageDialog(null, "Unable to determine source profile",
+					"Component Profile Problem", ERROR_MESSAGE);
+			return;
+		}
+		for (Profile p : targetRegistry.getComponentProfiles()) {
+			if (p.getName().equals(sourceProfile.getName())) {
+				showMessageDialog(null,
+						"Target registry already contains a profile named "
+								+ sourceProfile.getName(), "Copy Problem",
+						ERROR_MESSAGE);
+				return;
+			}
+			String sourceId = sourceProfile.getId();
+			if (sourceId == null) {
+				showMessageDialog(null,
+						"Source profile \"" + sourceProfile.getName()
+								+ "\" has no id ", "Copy Problem",
+						ERROR_MESSAGE);
+				return;
+			}
+			String id = p.getId();
+			if (sourceId.equals(id)) {
+				showMessageDialog(null,
+						"Target registry already contains a profile with id "
+								+ sourceId, "Copy Problem", ERROR_MESSAGE);
+				return;
+			}
+		}
+		targetRegistry.addComponentProfile(sourceProfile, license, permission);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyMenuAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyMenuAction.java
new file mode 100644
index 0000000..00d1ceb
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileCopyMenuAction.java
@@ -0,0 +1,43 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.profile;
+
+import static org.apache.taverna.component.ui.menu.profile.ComponentProfileMenuSection.COMPONENT_PROFILE_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * @author alanrw
+ */
+public class ComponentProfileCopyMenuAction extends AbstractMenuAction {
+	private static final URI COMPONENT_PROFILE_COPY_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentProfileCopy");
+
+	private ComponentPreference prefs;
+	private ComponentServiceIcon icon;
+
+	public ComponentProfileCopyMenuAction() {
+		super(COMPONENT_PROFILE_SECTION, 250, COMPONENT_PROFILE_COPY_URI);
+	}
+
+	public void setPreferences(ComponentPreference prefs) {
+		this.prefs = prefs;
+	}
+
+	public void setIcon(ComponentServiceIcon icon) {
+		this.icon = icon;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ComponentProfileCopyAction(prefs, icon);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteAction.java
new file mode 100644
index 0000000..f7f5a71
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteAction.java
@@ -0,0 +1,97 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.profile;
+
+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 org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.profile.Profile;
+import org.apache.taverna.component.ui.panel.ProfileChooserPanel;
+import org.apache.taverna.component.ui.panel.RegistryChooserPanel;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+/**
+ * @author alanrw
+ */
+public class ComponentProfileDeleteAction extends AbstractAction {
+	private static final long serialVersionUID = -5697971204434020559L;
+	private static final Logger log = getLogger(ComponentProfileDeleteAction.class);
+	private static final String DELETE_PROFILE = "Delete profile...";
+
+	private final ComponentPreference prefs;
+
+	public ComponentProfileDeleteAction(ComponentPreference prefs,
+			ComponentServiceIcon icon) {
+		super(DELETE_PROFILE, icon.getIcon());
+		// FIXME Should we switch this on?
+		setEnabled(false);
+		this.prefs = prefs;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent ev) {
+		JPanel overallPanel = new JPanel(new GridBagLayout());
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		RegistryChooserPanel registryPanel = new RegistryChooserPanel(prefs);
+		registryPanel.setBorder(new TitledBorder("Registry"));
+
+		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(registryPanel, gbc);
+
+		ProfileChooserPanel profilePanel = new ProfileChooserPanel(
+				registryPanel);
+		profilePanel.setBorder(new TitledBorder("Profile"));
+
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.weighty = 1;
+		overallPanel.add(profilePanel, gbc);
+
+		int answer = showConfirmDialog(null, overallPanel,
+				"Delete Component Profile", OK_CANCEL_OPTION);
+		try {
+			if (answer == OK_OPTION)
+				doDelete(profilePanel.getChosenProfile());
+		} catch (ComponentException e) {
+			log.error("failed to delete profile", e);
+			showMessageDialog(null,
+					"Unable to delete profile: " + e.getMessage(),
+					"Registry Exception", ERROR_MESSAGE);
+		}
+	}
+
+	private void doDelete(Profile profile) throws ComponentException {
+		if (profile == null) {
+			showMessageDialog(null, "Unable to determine profile",
+					"Component Profile Problem", ERROR_MESSAGE);
+			return;
+		}
+		profile.delete();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java
new file mode 100644
index 0000000..3544eec
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java
@@ -0,0 +1,43 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.profile;
+
+import static org.apache.taverna.component.ui.menu.profile.ComponentProfileMenuSection.COMPONENT_PROFILE_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * @author alanrw
+ */
+public class ComponentProfileDeleteMenuAction extends AbstractMenuAction {
+	private static final URI COMPONENT_PROFILE_DELETE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentProfileDelete");
+
+	private ComponentPreference prefs;
+	private ComponentServiceIcon icon;
+
+	public ComponentProfileDeleteMenuAction() {
+		super(COMPONENT_PROFILE_SECTION, 300, COMPONENT_PROFILE_DELETE_URI);
+	}
+
+	public void setIcon(ComponentServiceIcon icon) {
+		this.icon = icon;
+	}
+
+	public void setPreferences(ComponentPreference prefs) {
+		this.prefs = prefs;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ComponentProfileDeleteAction(prefs, icon);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportAction.java
new file mode 100644
index 0000000..0ab10fc
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportAction.java
@@ -0,0 +1,166 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.profile;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.WEST;
+import static javax.swing.JFileChooser.APPROVE_OPTION;
+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 java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.filechooser.FileNameExtensionFilter;
+
+import net.sf.taverna.t2.lang.ui.DeselectingButton;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.ComponentFactory;
+import org.apache.taverna.component.api.License;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.SharingPolicy;
+import org.apache.taverna.component.api.profile.Profile;
+import org.apache.taverna.component.ui.panel.LicenseChooserPanel;
+import org.apache.taverna.component.ui.panel.RegistryChooserPanel;
+import org.apache.taverna.component.ui.panel.SharingPolicyChooserPanel;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+/**
+ * @author alanrw
+ */
+public class ComponentProfileImportAction extends AbstractAction {
+	private static final long serialVersionUID = -3796754761286943970L;
+	private static final Logger log = getLogger(ComponentProfileImportAction.class);
+	private static final String IMPORT_PROFILE = "Import profile...";
+	private static final JFileChooser chooser = new JFileChooser();
+
+	private final ComponentFactory factory;
+	private final ComponentPreference prefs;
+
+	public ComponentProfileImportAction(ComponentFactory factory,
+			ComponentPreference prefs, ComponentServiceIcon icon) {
+		super(IMPORT_PROFILE, icon.getIcon());
+		this.factory = factory;
+		this.prefs = prefs;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent arg0) {
+		JPanel overallPanel = new JPanel();
+		overallPanel.setLayout(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		RegistryChooserPanel registryPanel = new RegistryChooserPanel(prefs);
+
+		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(registryPanel, gbc);
+		gbc.gridx = 0;
+		gbc.gridy = 1;
+		gbc.gridwidth = 1;
+		gbc.weightx = 0;
+		overallPanel.add(new JLabel("Profile URL or local file path:"), gbc);
+		gbc.gridx = 1;
+		gbc.weightx = 1;
+		final JTextField profileLocation = new JTextField(30);
+		overallPanel.add(profileLocation, gbc);
+		gbc.gridx = 0;
+		gbc.weightx = 0;
+		gbc.gridy++;
+		JButton browseButton = new DeselectingButton(new AbstractAction(
+				"Browse") {
+			private static final long serialVersionUID = 1574330610799117697L;
+
+			@Override
+			public void actionPerformed(ActionEvent arg0) {
+				FileNameExtensionFilter filter = new FileNameExtensionFilter(
+						"XML files", "xml");
+				chooser.setFileFilter(filter);
+				int returnVal = chooser.showOpenDialog(null);
+				if (returnVal == APPROVE_OPTION)
+					profileLocation.setText(chooser.getSelectedFile().toURI()
+							.toString());
+			}
+		});
+		overallPanel.add(browseButton, gbc);
+
+		gbc.gridx = 0;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1;
+		gbc.weighty = 1;
+		gbc.gridy++;
+		SharingPolicyChooserPanel permissionPanel = new SharingPolicyChooserPanel(registryPanel);
+		overallPanel.add(permissionPanel, gbc);
+
+		gbc.gridy++;
+		LicenseChooserPanel licensePanel = new LicenseChooserPanel();
+		registryPanel.addObserver(licensePanel);
+		overallPanel.add(licensePanel, gbc);
+
+		int answer = showConfirmDialog(null, overallPanel,
+				"Import Component Profile", OK_CANCEL_OPTION);
+		if (answer == OK_OPTION)
+			doImport(registryPanel.getChosenRegistry(),
+					profileLocation.getText(),
+					permissionPanel.getChosenPermission(),
+					licensePanel.getChosenLicense());
+	}
+
+	private void doImport(Registry chosenRegistry, String profileLocation,
+			SharingPolicy permission, License license) {
+		if (profileLocation == null || profileLocation.isEmpty()) {
+			showMessageDialog(null, "Profile location cannot be blank",
+					"Invalid URL", ERROR_MESSAGE);
+			return;
+		}
+		if (chosenRegistry == null) {
+			showMessageDialog(null, "Unable to determine registry",
+					"Component Registry Problem", ERROR_MESSAGE);
+			return;
+		}
+		try {
+			Profile newProfile = factory.getProfile(new URL(profileLocation));
+			String newName = newProfile.getName();
+			for (Profile p : chosenRegistry.getComponentProfiles())
+				if (p.getName().equals(newName)) {
+					showMessageDialog(null, newName + " is already used",
+							"Duplicate profile name", ERROR_MESSAGE);
+					return;
+				}
+			chosenRegistry.addComponentProfile(newProfile, license,
+					permission);
+		} catch (MalformedURLException e) {
+			showMessageDialog(null, profileLocation + " is not a valid URL",
+					"Invalid URL", ERROR_MESSAGE);
+		} catch (ComponentException e) {
+			log.error("import profile failed", e);
+			showMessageDialog(null,
+					"Unable to save profile: " + e.getMessage(),
+					"Registry Exception", ERROR_MESSAGE);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportMenuAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportMenuAction.java
new file mode 100644
index 0000000..2aca696
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileImportMenuAction.java
@@ -0,0 +1,49 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.profile;
+
+import static org.apache.taverna.component.ui.menu.profile.ComponentProfileMenuSection.COMPONENT_PROFILE_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.component.api.ComponentFactory;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+
+/**
+ * @author alanrw
+ */
+public class ComponentProfileImportMenuAction extends AbstractMenuAction {
+	private static final URI COMPONENT_PROFILE_IMPORT_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentProfileImport");
+
+	private ComponentFactory factory;
+	private ComponentPreference prefs;
+	private ComponentServiceIcon icon;
+
+	public ComponentProfileImportMenuAction() {
+		super(COMPONENT_PROFILE_SECTION, 200, COMPONENT_PROFILE_IMPORT_URI);
+	}
+
+	public void setComponentFactory(ComponentFactory factory) {
+		this.factory = factory;
+	}
+	
+	public void setIcon(ComponentServiceIcon icon) {
+		this.icon = icon;
+	}
+
+	public void setPreferences(ComponentPreference prefs) {
+		this.prefs = prefs;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ComponentProfileImportAction(factory, prefs, icon);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileMenuSection.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileMenuSection.java
new file mode 100644
index 0000000..f6c137c
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/profile/ComponentProfileMenuSection.java
@@ -0,0 +1,25 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.profile;
+
+import static org.apache.taverna.component.ui.menu.ComponentMenu.COMPONENT;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class ComponentProfileMenuSection extends AbstractMenuSection {
+
+	public static final URI COMPONENT_PROFILE_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentProfileSection");
+
+	public ComponentProfileMenuSection() {
+		super(COMPONENT, 200, COMPONENT_PROFILE_SECTION);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageAction.java
new file mode 100644
index 0000000..efb7ed6
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageAction.java
@@ -0,0 +1,35 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.registry;
+
+import static org.apache.taverna.component.ui.preference.ComponentPreferenceUIFactory.DISPLAY_NAME;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+import net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame;
+
+/**
+ * @author alanrw
+ */
+public class ComponentRegistryManageAction extends AbstractAction {
+	private static final long serialVersionUID = 8993945811345164194L;
+	private static final String MANAGE_REGISTRY = "Manage registries...";
+
+	private final T2ConfigurationFrame configFrame;
+
+	public ComponentRegistryManageAction(T2ConfigurationFrame configFrame,
+			ComponentServiceIcon icon) {
+		super(MANAGE_REGISTRY, icon.getIcon());
+		this.configFrame = configFrame;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent evt) {
+		configFrame.showConfiguration(DISPLAY_NAME);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageMenuAction.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageMenuAction.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageMenuAction.java
new file mode 100644
index 0000000..d8151d9
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryManageMenuAction.java
@@ -0,0 +1,48 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.registry;
+
+import static org.apache.taverna.component.ui.menu.registry.ComponentRegistryMenuSection.COMPONENT_REGISTRY_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import org.apache.taverna.component.ui.serviceprovider.ComponentServiceIcon;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuAction;
+import net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame;
+
+/**
+ * @author alanrw
+ */
+public class ComponentRegistryManageMenuAction extends AbstractMenuAction {
+	private static final URI COMPONENT_REGISTRY_MANAGE_URI = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentRegistryManage");
+
+	private T2ConfigurationFrame configFrame;
+	private ComponentServiceIcon icon;
+
+	public ComponentRegistryManageMenuAction() {
+		super(COMPONENT_REGISTRY_SECTION, 100, COMPONENT_REGISTRY_MANAGE_URI);
+	}
+
+	public void setConfigurationFrame(T2ConfigurationFrame configFrame) {
+		this.configFrame = configFrame;
+	}
+
+	public void setIcon(ComponentServiceIcon icon) {
+		this.icon = icon;
+	}
+
+	@Override
+	public boolean isEnabled() {
+		return true;
+	}
+
+	@Override
+	protected Action createAction() {
+		return new ComponentRegistryManageAction(configFrame, icon);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryMenuSection.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryMenuSection.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryMenuSection.java
new file mode 100644
index 0000000..096dcfd
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/menu/registry/ComponentRegistryMenuSection.java
@@ -0,0 +1,22 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.menu.registry;
+
+import static org.apache.taverna.component.ui.menu.ComponentMenu.COMPONENT;
+
+import java.net.URI;
+
+import net.sf.taverna.t2.ui.menu.AbstractMenuSection;
+
+/**
+ * @author alanrw
+ */
+public class ComponentRegistryMenuSection extends AbstractMenuSection {
+	public static final URI COMPONENT_REGISTRY_SECTION = URI
+			.create("http://taverna.sf.net/2008/t2workbench/menu#componentRegistrySection");
+
+	public ComponentRegistryMenuSection() {
+		super(COMPONENT, 100, COMPONENT_REGISTRY_SECTION);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChoiceMessage.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChoiceMessage.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChoiceMessage.java
new file mode 100644
index 0000000..ee15b5d
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChoiceMessage.java
@@ -0,0 +1,34 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.panel;
+
+import org.apache.taverna.component.api.Component;
+import org.apache.taverna.component.api.Family;
+
+/**
+ * @author alanrw
+ */
+public class ComponentChoiceMessage {
+	private final Component chosenComponent;
+	private final Family componentFamily;
+
+	public ComponentChoiceMessage(Family componentFamily, Component chosenComponent) {
+		this.componentFamily = componentFamily;
+		this.chosenComponent = chosenComponent;
+	}
+
+	/**
+	 * @return the chosenComponent
+	 */
+	public Component getChosenComponent() {
+		return chosenComponent;
+	}
+
+	/**
+	 * @return the componentFamily
+	 */
+	public Family getComponentFamily() {
+		return componentFamily;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChooserPanel.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChooserPanel.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChooserPanel.java
new file mode 100644
index 0000000..a71c906
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentChooserPanel.java
@@ -0,0 +1,218 @@
+/**
+ *
+ */
+package org.apache.taverna.component.ui.panel;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.WEST;
+import static java.awt.event.ItemEvent.SELECTED;
+import static org.apache.log4j.Logger.getLogger;
+import static org.apache.taverna.component.ui.util.Utils.LONG_STRING;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingWorker;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.Component;
+import org.apache.taverna.component.api.Family;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+
+/**
+ * @author alanrw
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class ComponentChooserPanel extends JPanel implements
+		Observable<ComponentChoiceMessage>, Observer {
+	private static final String NAME_LABEL = "Component name:";
+	private static final long serialVersionUID = -4459660016225074302L;
+	private static Logger logger = getLogger(ComponentChooserPanel.class);
+
+	private final List<Observer<ComponentChoiceMessage>> observers = new ArrayList<>();
+	private final JComboBox<String> componentChoice = new JComboBox<>();
+	private final SortedMap<String, Component> componentMap = new TreeMap<>();
+	private final RegistryAndFamilyChooserPanel registryAndFamilyChooserPanel;
+
+	public ComponentChooserPanel(ComponentPreference prefs, FamilyChooserPanel familyPanel) {
+		this(prefs);
+		familyPanel.addObserver(new Observer<FamilyChoiceMessage>() {
+			@Override
+			public void notify(Observable<FamilyChoiceMessage> sender,
+					FamilyChoiceMessage message) throws Exception {
+				try {
+					updateComponentModel();
+				} catch (Exception e) {
+					logger.error("problem when component/family was selected",
+							e);
+				}
+			}
+		});
+	}
+
+	public ComponentChooserPanel(ComponentPreference prefs) {
+		super(new GridBagLayout());
+		registryAndFamilyChooserPanel = new RegistryAndFamilyChooserPanel(prefs);
+
+		componentChoice.setPrototypeDisplayValue(LONG_STRING);
+
+		updateComponentModel();
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.gridx = 0;
+		gbc.anchor = WEST;
+		gbc.fill = HORIZONTAL;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1;
+		add(registryAndFamilyChooserPanel, gbc);
+
+		gbc.gridy = 1;
+		gbc.gridwidth = 1;
+		gbc.weightx = 0;
+		add(new JLabel(NAME_LABEL), gbc);
+		gbc.gridx = 1;
+		gbc.weightx = 1;
+		add(componentChoice, gbc);
+		registryAndFamilyChooserPanel.addObserver(this);
+
+		componentChoice.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent event) {
+				if (event.getStateChange() == SELECTED) {
+					updateToolTipText();
+					notifyObservers();
+				}
+			}
+		});
+	}
+
+	protected void updateToolTipText() {
+		Component chosenComponent = componentMap.get(componentChoice
+				.getSelectedItem());
+		if (chosenComponent == null)
+			componentChoice.setToolTipText(null);
+		else
+			componentChoice.setToolTipText(chosenComponent.getDescription());
+	}
+
+	private void notifyObservers() {
+		ComponentChoiceMessage message = new ComponentChoiceMessage(
+				registryAndFamilyChooserPanel.getChosenFamily(),
+				getChosenComponent());
+		for (Observer<ComponentChoiceMessage> o : getObservers())
+			try {
+				o.notify(ComponentChooserPanel.this, message);
+			} catch (Exception e) {
+				logger.error(
+						"observer had problem with component selection message",
+						e);
+			}
+	}
+
+	private void updateComponentModel() {
+		componentMap.clear();
+		componentChoice.removeAllItems();
+		componentChoice.setToolTipText(null);
+		notifyObservers();
+		componentChoice.addItem("Reading components");
+		componentChoice.setEnabled(false);
+		new ComponentUpdater().execute();
+	}
+
+	public Component getChosenComponent() {
+		if (componentMap.isEmpty())
+			return null;
+		return componentMap.get(componentChoice.getSelectedItem());
+	}
+
+	@Override
+	public void notify(Observable sender, Object message) {
+		try {
+			if (message instanceof FamilyChoiceMessage)
+				updateComponentModel();
+			else if (message instanceof ProfileChoiceMessage)
+				registryAndFamilyChooserPanel.notify(null,
+						(ProfileChoiceMessage) message);
+		} catch (Exception e) {
+			logger.error("problem when component/family was selected", e);
+		}
+	}
+
+	@Override
+	public void addObserver(Observer<ComponentChoiceMessage> observer) {
+		observers.add(observer);
+		Component chosenComponent = getChosenComponent();
+		ComponentChoiceMessage message = new ComponentChoiceMessage(
+				registryAndFamilyChooserPanel.getChosenFamily(),
+				chosenComponent);
+		try {
+			observer.notify(this, message);
+		} catch (Exception e) {
+			logger.error("failed to notify about addition of observer", e);
+		}
+	}
+
+	@Override
+	public List<Observer<ComponentChoiceMessage>> getObservers() {
+		return observers;
+	}
+
+	@Override
+	public void removeObserver(Observer<ComponentChoiceMessage> observer) {
+		observers.remove(observer);
+	}
+
+	public Registry getChosenRegistry() {
+		return registryAndFamilyChooserPanel.getChosenRegistry();
+	}
+
+	public Family getChosenFamily() {
+		return registryAndFamilyChooserPanel.getChosenFamily();
+	}
+
+	private class ComponentUpdater extends SwingWorker<String, Object> {
+		@Override
+		protected String doInBackground() throws Exception {
+			Family chosenFamily = registryAndFamilyChooserPanel
+					.getChosenFamily();
+			if (chosenFamily != null)
+				for (Component component : chosenFamily.getComponents())
+					componentMap.put(component.getName(), component);
+
+			return null;
+		}
+
+		@Override
+		protected void done() {
+			componentChoice.removeAllItems();
+			try {
+				get();
+				for (String componentName : componentMap.keySet())
+					componentChoice.addItem(componentName);
+				if (!componentMap.isEmpty()) {
+					componentChoice.setSelectedItem(componentMap.firstKey());
+					updateToolTipText();
+				} else
+					componentChoice.addItem("No components available");
+			} catch (InterruptedException | ExecutionException e) {
+				logger.error(e);
+				componentChoice.addItem("Unable to read components");
+			}
+			notifyObservers();
+			componentChoice.setEnabled(!componentMap.isEmpty());
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentListCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentListCellRenderer.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentListCellRenderer.java
new file mode 100644
index 0000000..9dd7d26
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentListCellRenderer.java
@@ -0,0 +1,46 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.panel;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+
+import org.apache.taverna.component.api.Component;
+import org.apache.taverna.component.api.Family;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.Version;
+
+/**
+ * @author alanrw
+ */
+public class ComponentListCellRenderer<T> implements ListCellRenderer<T> {
+	private static DefaultListCellRenderer defaultRenderer = new DefaultListCellRenderer();
+
+	@Override
+	public java.awt.Component getListCellRendererComponent(
+			JList<? extends T> list, T value, int index, boolean isSelected,
+			boolean cellHasFocus) {
+		return defaultRenderer.getListCellRendererComponent(list,
+				convertValueToString(value), index, isSelected, cellHasFocus);
+	}
+
+	private static String convertValueToString(Object value) {
+		if (value instanceof Registry)
+			return ((Registry) value).getRegistryBase().toString();
+		if (value instanceof Family)
+			return ((Family) value).getName();
+		if (value instanceof Component)
+			return ((Component) value).getName();
+		if (value instanceof Version)
+			return ((Version) value).getVersionNumber().toString();
+		if (value instanceof Integer)
+			return ((Integer) value).toString();
+		if (value instanceof String)
+			return (String) value;
+		if (value == null)
+			return "null";
+		return "Spaceholder for " + value.getClass().getName();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentVersionChooserPanel.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentVersionChooserPanel.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentVersionChooserPanel.java
new file mode 100644
index 0000000..a2c6b42
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/ComponentVersionChooserPanel.java
@@ -0,0 +1,171 @@
+/**
+ *
+ */
+package org.apache.taverna.component.ui.panel;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+import static java.awt.event.ItemEvent.SELECTED;
+import static org.apache.log4j.Logger.getLogger;
+import static org.apache.taverna.component.ui.util.Utils.SHORT_STRING;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingWorker;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.Component;
+import org.apache.taverna.component.api.Family;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.Version;
+import org.apache.taverna.component.ui.preference.ComponentPreference;
+
+/**
+ * @author alanrw
+ */
+public class ComponentVersionChooserPanel extends JPanel implements
+		Observer<ComponentChoiceMessage> {
+	private static final long serialVersionUID = 5125907010496468219L;
+	private static Logger logger = getLogger(ComponentVersionChooserPanel.class);
+
+	private final JComboBox<String> componentVersionChoice;
+	private final SortedMap<Integer, Version> componentVersionMap;
+	private final ComponentChooserPanel componentChooserPanel;
+
+	public ComponentVersionChooserPanel(ComponentPreference prefs) {
+		super(new GridBagLayout());
+		componentVersionMap = new TreeMap<>();
+		componentChooserPanel = new ComponentChooserPanel(prefs);
+		componentVersionChoice = new JComboBox<>();
+		componentVersionChoice.setPrototypeDisplayValue(SHORT_STRING);
+
+		GridBagConstraints gbc = new GridBagConstraints();
+		gbc.gridx = 0;
+		gbc.anchor = WEST;
+		gbc.fill = HORIZONTAL;
+		gbc.gridwidth = 2;
+		gbc.weightx = 1;
+		add(componentChooserPanel, gbc);
+		componentChooserPanel.addObserver(this);
+
+		gbc.gridy = 1;
+		gbc.gridwidth = 1;
+		gbc.weightx = 0;
+		gbc.fill = NONE;
+		add(new JLabel("Component version:"), gbc);
+		gbc.gridx = 1;
+		gbc.weightx = 1;
+		add(componentVersionChoice, gbc);
+		componentVersionChoice.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent event) {
+				if (event.getStateChange() == SELECTED)
+					updateToolTipText();
+			}
+		});
+	}
+
+	protected void updateToolTipText() {
+		Version chosenComponentVersion = getChosenComponentVersion();
+		componentVersionChoice
+				.setToolTipText(chosenComponentVersion == null ? null
+						: chosenComponentVersion.getDescription());
+	}
+
+	private void updateComponentVersionModel() {
+		componentVersionMap.clear();
+		componentVersionChoice.removeAllItems();
+		componentVersionChoice.setToolTipText(null);
+		componentVersionChoice.addItem("Reading component versions");
+		componentVersionChoice.setEnabled(false);
+		new ComponentVersionUpdater().execute();
+	}
+
+	public Version getChosenComponentVersion() {
+		if (componentVersionMap.isEmpty())
+			return null;
+		try {
+			return componentVersionMap.get(new Integer(componentVersionChoice
+					.getSelectedItem().toString()));
+		} catch (NumberFormatException nfe) {
+			// Not a number, no version chosen
+			return null;
+		}
+	}
+
+	@Override
+	public void notify(Observable<ComponentChoiceMessage> sender,
+			ComponentChoiceMessage message) {
+		try {
+			updateComponentVersionModel();
+		} catch (RuntimeException e) {
+			logger.error("problem updating view from component version", e);
+		}
+	}
+
+	public Registry getChosenRegistry() {
+		return componentChooserPanel.getChosenRegistry();
+	}
+
+	public Family getChosenFamily() {
+		return componentChooserPanel.getChosenFamily();
+	}
+
+	public Component getChosenComponent() {
+		return componentChooserPanel.getChosenComponent();
+	}
+
+	private class ComponentVersionUpdater extends SwingWorker<String, Object> {
+		@Override
+		protected String doInBackground() throws Exception {
+			Component chosenComponent = componentChooserPanel
+					.getChosenComponent();
+			if (chosenComponent != null)
+				for (Version version : chosenComponent.getComponentVersionMap()
+						.values()) {
+					Integer versionNumber = version.getVersionNumber();
+					componentVersionMap.put(versionNumber, version);
+				}
+			return null;
+		}
+
+		@Override
+		protected void done() {
+			componentVersionChoice.removeAllItems();
+			try {
+				get();
+				for (Integer versionNumber : componentVersionMap.keySet())
+					componentVersionChoice.addItem(versionNumber.toString());
+
+				if (!componentVersionMap.isEmpty()) {
+					componentVersionChoice.setSelectedItem(componentVersionMap
+							.lastKey());
+					updateToolTipText();
+				} else
+					componentVersionChoice.addItem("No versions available");
+			} catch (InterruptedException | ExecutionException e) {
+				componentVersionChoice.addItem("Unable to read versions");
+				logger.error(e);
+			}
+
+			componentVersionChoice.setEnabled(!componentVersionMap.isEmpty());
+		}
+	}
+
+	public ComponentChooserPanel getComponentChooserPanel() {
+		return componentChooserPanel;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChoiceMessage.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChoiceMessage.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChoiceMessage.java
new file mode 100644
index 0000000..5443000
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChoiceMessage.java
@@ -0,0 +1,24 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.panel;
+
+import org.apache.taverna.component.api.Family;
+
+/**
+ * @author alanrw
+ */
+public class FamilyChoiceMessage {
+	private final Family chosenFamily;
+
+	public FamilyChoiceMessage(Family chosenFamily) {
+		this.chosenFamily = chosenFamily;
+	}
+
+	/**
+	 * @return the chosenFamily
+	 */
+	public Family getChosenFamily() {
+		return chosenFamily;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChooserPanel.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChooserPanel.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChooserPanel.java
new file mode 100644
index 0000000..945cff2
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/FamilyChooserPanel.java
@@ -0,0 +1,220 @@
+/**
+ *
+ */
+package org.apache.taverna.component.ui.panel;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+import static java.awt.event.ItemEvent.SELECTED;
+import static org.apache.log4j.Logger.getLogger;
+import static org.apache.taverna.component.ui.util.Utils.LONG_STRING;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingWorker;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.Family;
+import org.apache.taverna.component.api.Registry;
+import org.apache.taverna.component.api.profile.Profile;
+
+/**
+ * @author alanrw
+ */
+public class FamilyChooserPanel extends JPanel implements
+		Observer<ProfileChoiceMessage>, Observable<FamilyChoiceMessage> {
+	private static final String FAMILY_LABEL = "Component family:";
+	private static final String READING_MSG = "Reading families";
+	private static final long serialVersionUID = -2608831126562927778L;
+	private static Logger logger = getLogger(FamilyChooserPanel.class);
+
+	private final List<Observer<FamilyChoiceMessage>> observers = new ArrayList<>();
+	private final JComboBox<String> familyBox = new JComboBox<>();
+	// private JTextArea familyDescription = new JTextArea(10,60);
+	private final SortedMap<String, Family> familyMap = new TreeMap<>();
+	private Registry chosenRegistry = null;
+	private Profile profileFilter = null;
+
+	public FamilyChooserPanel(RegistryChooserPanel registryPanel) {
+		this();
+		registryPanel.addObserver(new Observer<RegistryChoiceMessage>() {
+			@Override
+			public void notify(Observable<RegistryChoiceMessage> sender,
+					RegistryChoiceMessage message) throws Exception {
+				try {
+					chosenRegistry = message.getChosenRegistry();
+					updateList();
+				} catch (RuntimeException e) {
+					logger.error("failed to update list after registry choice",
+							e);
+				}
+			}
+		});
+	}
+
+	public FamilyChooserPanel() {
+		super(new GridBagLayout());
+		familyBox.setPrototypeDisplayValue(LONG_STRING);
+
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.anchor = WEST;
+		gbc.fill = NONE;
+		this.add(new JLabel(FAMILY_LABEL), gbc);
+		gbc.gridx = 1;
+		gbc.weightx = 1;
+		gbc.fill = BOTH;
+		this.add(familyBox, gbc);
+		familyBox.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent event) {
+				if (event.getStateChange() == SELECTED) {
+					updateDescription();
+					notifyObservers();
+				}
+			}
+		});
+
+		familyBox.setEditable(false);
+	}
+
+	protected void updateDescription() {
+		Family chosenFamily = getChosenFamily();
+		if (chosenFamily != null)
+			familyBox.setToolTipText(chosenFamily.getDescription());
+		else
+			familyBox.setToolTipText(null);
+	}
+
+	private void updateList() {
+		familyMap.clear();
+		familyBox.removeAllItems();
+		familyBox.setToolTipText(null);
+		notifyObservers();
+		familyBox.addItem(READING_MSG);
+		familyBox.setEnabled(false);
+		new FamilyUpdater().execute();
+	}
+
+	private void notifyObservers() {
+		Family chosenFamily = getChosenFamily();
+		FamilyChoiceMessage message = new FamilyChoiceMessage(chosenFamily);
+		for (Observer<FamilyChoiceMessage> o : getObservers())
+			try {
+				o.notify(this, message);
+			} catch (Exception e) {
+				logger.error("failed to notify about change of state of panel",
+						e);
+			}
+	}
+
+	public Family getChosenFamily() {
+		if (familyBox.getSelectedIndex() < 0)
+			return null;
+		return familyMap.get(familyBox.getSelectedItem());
+	}
+
+	@Override
+	public void addObserver(Observer<FamilyChoiceMessage> observer) {
+		observers.add(observer);
+		Family chosenFamily = getChosenFamily();
+		FamilyChoiceMessage message = new FamilyChoiceMessage(chosenFamily);
+		try {
+			observer.notify(this, message);
+		} catch (Exception e) {
+			logger.error("failed to notify about family choice", e);
+		}
+	}
+
+	@Override
+	public List<Observer<FamilyChoiceMessage>> getObservers() {
+		return observers;
+	}
+
+	@Override
+	public void removeObserver(Observer<FamilyChoiceMessage> observer) {
+		observers.remove(observer);
+	}
+
+	@Override
+	public void notify(Observable<ProfileChoiceMessage> sender,
+			ProfileChoiceMessage message) throws Exception {
+		try {
+			profileFilter = message.getChosenProfile();
+			updateList();
+		} catch (RuntimeException e) {
+			logger.error("failed to update list after profile choice", e);
+		}
+	}
+
+	private void updateFamiliesFromRegistry() throws ComponentException {
+		for (Family f : chosenRegistry.getComponentFamilies()) {
+			if (profileFilter == null) {
+				familyMap.put(f.getName(), f);
+				continue;
+			}
+			Profile componentProfile;
+			try {
+				componentProfile = f.getComponentProfile();
+			} catch (ComponentException | RuntimeException e) {
+				logger.error("failed to get profile of component", e);
+				componentProfile = null;
+			}
+			if (componentProfile != null) {
+				String id = componentProfile.getId();
+				if ((profileFilter == null) || id.equals(profileFilter.getId()))
+					familyMap.put(f.getName(), f);
+			} else
+				logger.info("Ignoring " + f.getName());
+		}
+	}
+
+	private class FamilyUpdater extends SwingWorker<String, Object> {
+		@Override
+		protected String doInBackground() throws Exception {
+			if (chosenRegistry != null)
+				updateFamiliesFromRegistry();
+			return null;
+		}
+
+		@Override
+		protected void done() {
+			familyBox.removeAllItems();
+			try {
+				get();
+				for (String name : familyMap.keySet())
+					familyBox.addItem(name);
+				if (!familyMap.isEmpty()) {
+					String firstKey = familyMap.firstKey();
+					familyBox.setSelectedItem(firstKey);
+					updateDescription();
+				} else
+					familyBox.addItem("No families available");
+			} catch (InterruptedException | ExecutionException e) {
+				familyBox.addItem("Unable to read families");
+				logger.error(e);
+			}
+
+			notifyObservers();
+			familyBox.setEnabled(!familyMap.isEmpty());
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/LicenseChooserPanel.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/LicenseChooserPanel.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/LicenseChooserPanel.java
new file mode 100644
index 0000000..941f887
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/LicenseChooserPanel.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (C) 2013 The University of Manchester
+ *
+ *  Modifications to the initial code base are copyright of their
+ *  respective authors, or their employers as appropriate.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1 of
+ *  the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ ******************************************************************************/
+package org.apache.taverna.component.ui.panel;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.WEST;
+import static java.awt.event.ItemEvent.SELECTED;
+import static org.apache.log4j.Logger.getLogger;
+import static org.apache.taverna.component.ui.util.Utils.LONG_STRING;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingWorker;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+
+import org.apache.log4j.Logger;
+import org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.License;
+import org.apache.taverna.component.api.Registry;
+
+/**
+ * @author alanrw
+ */
+public class LicenseChooserPanel extends JPanel implements
+		Observer<RegistryChoiceMessage> {
+	private static final long serialVersionUID = 2175274929391537032L;
+	private static final Logger logger = getLogger(LicenseChooserPanel.class);
+
+	private JComboBox<String> licenseBox = new JComboBox<>();
+	private SortedMap<String, License> licenseMap = new TreeMap<>();
+	private Registry registry;
+
+	public LicenseChooserPanel() {
+		super();
+		licenseBox.setPrototypeDisplayValue(LONG_STRING);
+		this.setLayout(new GridBagLayout());
+
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		gbc.gridx = 0;
+		gbc.gridy = 0;
+		gbc.anchor = WEST;
+		gbc.fill = NONE;
+		this.add(new JLabel("License:"), gbc);
+		gbc.gridx = 1;
+		gbc.weightx = 1;
+		gbc.fill = BOTH;
+		this.add(licenseBox, gbc);
+
+		licenseBox.setEditable(false);
+		licenseBox.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent event) {
+				if (event.getStateChange() == SELECTED)
+					setLicense(licenseMap.get(licenseBox.getSelectedItem()));
+			}
+		});
+	}
+
+	protected void setLicense(License license) {
+		if (license != null)
+			licenseBox.setToolTipText("<html>" + license.getDescription()
+					+ "</html>");
+		else
+			licenseBox.setToolTipText(null);
+	}
+
+	@Override
+	public void notify(Observable<RegistryChoiceMessage> sender,
+			RegistryChoiceMessage message) throws Exception {
+		try {
+			registry = message.getChosenRegistry();
+			updateLicenseModel();
+		} catch (Exception e) {
+			logger.error("failure when handling license choice", e);
+		}
+	}
+
+	private void updateLicenseModel() {
+		licenseMap.clear();
+		licenseBox.removeAllItems();
+		licenseBox.setToolTipText(null);
+		licenseBox.addItem("Reading licenses");
+		licenseBox.setEnabled(false);
+		new LicenseUpdater().execute();
+	}
+
+	public License getChosenLicense() {
+		if (licenseBox.getSelectedIndex() < 0)
+			return null;
+		Object selectedItem = licenseBox.getSelectedItem();
+		return licenseMap.get(selectedItem);
+	}
+
+	private class LicenseUpdater extends SwingWorker<String, Object> {
+		@Override
+		protected String doInBackground() throws Exception {
+			List<License> licenses;
+			if (registry == null)
+				return null;
+			try {
+				licenses = registry.getLicenses();
+				if (licenses == null)
+					return null;
+			} catch (ComponentException e) {
+				logger.error("failure when reading licenses from registry", e);
+				throw e;
+			} catch (NullPointerException e) {
+				logger.error("unexpected exception when reading licenses", e);
+				throw e;
+			}
+			for (License license : licenses)
+				try {
+					String name = license.getName();
+					licenseMap.put(name, license);
+				} catch (NullPointerException e) {
+					logger.error("could not get name of license", e);
+				}
+			return null;
+		}
+
+		@Override
+		protected void done() {
+			licenseBox.removeAllItems();
+			try {
+				get();
+			} catch (InterruptedException | ExecutionException e1) {
+				logger.error(e1);
+				licenseBox.addItem("Unable to read licenses");
+				licenseBox.setEnabled(false);
+				return;
+			}
+			for (String name : licenseMap.keySet())
+				licenseBox.addItem(name);
+			if (licenseMap.isEmpty()) {
+				licenseBox.addItem("No licenses available");
+				licenseBox.setEnabled(false);
+				return;
+			}
+
+			String firstKey = licenseMap.firstKey();
+			License preferredLicense = null;
+			try {
+				preferredLicense = registry.getPreferredLicense();
+			} catch (ComponentException e) {
+				logger.error("failed to get preferred license", e);
+			}
+			if (preferredLicense != null) {
+				licenseBox.setSelectedItem(preferredLicense.getName());
+				setLicense(preferredLicense);
+			} else {
+				licenseBox.setSelectedItem(firstKey);
+				setLicense(licenseMap.get(firstKey));
+			}
+			licenseBox.setEnabled(true);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-plugin-component/blob/43334c1d/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/PrefixPanel.java
----------------------------------------------------------------------
diff --git a/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/PrefixPanel.java b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/PrefixPanel.java
new file mode 100644
index 0000000..e8d8945
--- /dev/null
+++ b/taverna-component-activity-ui/src/main/java/org/apache/taverna/component/ui/panel/PrefixPanel.java
@@ -0,0 +1,81 @@
+/**
+ * 
+ */
+package org.apache.taverna.component.ui.panel;
+
+import static javax.swing.BorderFactory.createEtchedBorder;
+import static javax.swing.BorderFactory.createTitledBorder;
+import static javax.swing.border.TitledBorder.CENTER;
+import static javax.swing.border.TitledBorder.TOP;
+
+import java.awt.BorderLayout;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+import org.apache.taverna.component.api.ComponentException;
+import org.apache.taverna.component.api.profile.Profile;
+
+import net.sf.taverna.t2.lang.observer.Observable;
+import net.sf.taverna.t2.lang.observer.Observer;
+
+/**
+ * @author alanrw
+ */
+@SuppressWarnings("serial")
+public class PrefixPanel extends JPanel {
+	private DefaultTableModel prefixModel = new DefaultTableModel(10, 2) {
+		@Override
+		public boolean isCellEditable(int row, int column) {
+			// all cells false
+			return false;
+		};
+	};
+
+	private JTable prefixTable = new JTable(prefixModel);
+
+	public PrefixPanel(ProfileChooserPanel profilePanel) {
+		this();
+		profilePanel.addObserver(new Observer<ProfileChoiceMessage>() {
+			@Override
+			public void notify(Observable<ProfileChoiceMessage> sender,
+					ProfileChoiceMessage message) throws Exception {
+				profileChanged(message.getChosenProfile());
+			}
+		});
+	}
+
+	public PrefixPanel() {
+		super(new BorderLayout());
+		prefixModel.setColumnIdentifiers(new String[] { "Prefix", "URL" });
+		add(new JScrollPane(prefixTable), BorderLayout.CENTER);
+		setBorder(createTitledBorder(createEtchedBorder(), "Prefixes", CENTER,
+				TOP));
+	}
+
+	public TreeMap<String, String> getPrefixMap() {
+		TreeMap<String, String> result = new TreeMap<>();
+		for (int i = 0; i < prefixModel.getRowCount(); i++)
+			result.put((String) prefixModel.getValueAt(i, 0),
+					(String) prefixModel.getValueAt(i, 1));
+		return result;
+	}
+
+	private void profileChanged(Profile newProfile) throws ComponentException {
+		prefixModel.setRowCount(0);
+		if (newProfile != null)
+			for (Entry<String, String> entry : newProfile.getPrefixMap()
+					.entrySet()) {
+				String key = entry.getKey();
+				String value = entry.getValue();
+				if (!value.endsWith("#"))
+					value += "#";
+				prefixModel.addRow(new String[] { key, value });
+			}
+		validate();
+	}
+}