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/profile/ComponentProfileCopyMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileCopyMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileCopyMenuAction.java
new file mode 100644
index 0000000..1052960
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileCopyMenuAction.java
@@ -0,0 +1,58 @@
+/*
+* 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.profile;
+
+import static 
io.github.taverna_extras.component.ui.menu.profile.ComponentProfileMenuSection.COMPONENT_PROFILE_SECTION;
+
+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 org.apache.taverna.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteAction.java
new file mode 100644
index 0000000..00f809d
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteAction.java
@@ -0,0 +1,113 @@
+/*
+* 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.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 io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.profile.Profile;
+import io.github.taverna_extras.component.ui.panel.ProfileChooserPanel;
+import io.github.taverna_extras.component.ui.panel.RegistryChooserPanel;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java
new file mode 100644
index 0000000..be6e6d6
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileDeleteMenuAction.java
@@ -0,0 +1,58 @@
+/*
+* 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.profile;
+
+import static 
io.github.taverna_extras.component.ui.menu.profile.ComponentProfileMenuSection.COMPONENT_PROFILE_SECTION;
+
+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 org.apache.taverna.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportAction.java
new file mode 100644
index 0000000..d63133d
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportAction.java
@@ -0,0 +1,181 @@
+/*
+* 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.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 org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.ComponentFactory;
+import io.github.taverna_extras.component.api.License;
+import io.github.taverna_extras.component.api.Registry;
+import io.github.taverna_extras.component.api.SharingPolicy;
+import io.github.taverna_extras.component.api.profile.Profile;
+import io.github.taverna_extras.component.ui.panel.LicenseChooserPanel;
+import io.github.taverna_extras.component.ui.panel.RegistryChooserPanel;
+import io.github.taverna_extras.component.ui.panel.SharingPolicyChooserPanel;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import org.apache.taverna.lang.ui.DeselectingButton;
+
+/**
+ * @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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportMenuAction.java
new file mode 100644
index 0000000..7c060b8
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileImportMenuAction.java
@@ -0,0 +1,64 @@
+/*
+* 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.profile;
+
+import static 
io.github.taverna_extras.component.ui.menu.profile.ComponentProfileMenuSection.COMPONENT_PROFILE_SECTION;
+
+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.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileMenuSection.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileMenuSection.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileMenuSection.java
new file mode 100644
index 0000000..b7e9ff6
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/profile/ComponentProfileMenuSection.java
@@ -0,0 +1,40 @@
+/*
+* 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.profile;
+
+import static 
io.github.taverna_extras.component.ui.menu.ComponentMenu.COMPONENT;
+
+import java.net.URI;
+import org.apache.taverna.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageAction.java
new file mode 100644
index 0000000..a0cc5ab
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageAction.java
@@ -0,0 +1,52 @@
+/*
+* 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.registry;
+
+import static 
io.github.taverna_extras.component.ui.preference.ComponentPreferenceUIFactory.DISPLAY_NAME;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import 
org.apache.taverna.workbench.configuration.workbench.ui.T2ConfigurationFrame;
+
+//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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageMenuAction.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageMenuAction.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageMenuAction.java
new file mode 100644
index 0000000..28a8926
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryManageMenuAction.java
@@ -0,0 +1,63 @@
+/*
+* 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.registry;
+
+import static 
io.github.taverna_extras.component.ui.menu.registry.ComponentRegistryMenuSection.COMPONENT_REGISTRY_SECTION;
+
+import java.net.URI;
+
+import javax.swing.Action;
+
+import 
io.github.taverna_extras.component.ui.serviceprovider.ComponentServiceIcon;
+import org.apache.taverna.ui.menu.AbstractMenuAction;
+import 
org.apache.taverna.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryMenuSection.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryMenuSection.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryMenuSection.java
new file mode 100644
index 0000000..c651770
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/menu/registry/ComponentRegistryMenuSection.java
@@ -0,0 +1,37 @@
+/*
+* 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.registry;
+
+import static 
io.github.taverna_extras.component.ui.menu.ComponentMenu.COMPONENT;
+
+import java.net.URI;
+import org.apache.taverna.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChoiceMessage.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChoiceMessage.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChoiceMessage.java
new file mode 100644
index 0000000..50a2027
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChoiceMessage.java
@@ -0,0 +1,50 @@
+/*
+* 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.panel;
+
+import io.github.taverna_extras.component.api.Component;
+import io.github.taverna_extras.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChooserPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChooserPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChooserPanel.java
new file mode 100644
index 0000000..a8ceaf0
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentChooserPanel.java
@@ -0,0 +1,233 @@
+/*
+* 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.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 io.github.taverna_extras.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 org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.Component;
+import io.github.taverna_extras.component.api.Family;
+import io.github.taverna_extras.component.api.Registry;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+
+/**
+ * @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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentListCellRenderer.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentListCellRenderer.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentListCellRenderer.java
new file mode 100644
index 0000000..24d15d4
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentListCellRenderer.java
@@ -0,0 +1,62 @@
+/*
+* 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.panel;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+
+import io.github.taverna_extras.component.api.Component;
+import io.github.taverna_extras.component.api.Family;
+import io.github.taverna_extras.component.api.Registry;
+import io.github.taverna_extras.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentVersionChooserPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentVersionChooserPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentVersionChooserPanel.java
new file mode 100644
index 0000000..956af9c
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ComponentVersionChooserPanel.java
@@ -0,0 +1,186 @@
+/*
+* 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.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 io.github.taverna_extras.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 org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.Component;
+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.preference.ComponentPreference;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+
+/**
+ * @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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChoiceMessage.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChoiceMessage.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChoiceMessage.java
new file mode 100644
index 0000000..109b125
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChoiceMessage.java
@@ -0,0 +1,40 @@
+/*
+* 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.panel;
+
+import io.github.taverna_extras.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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChooserPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChooserPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChooserPanel.java
new file mode 100644
index 0000000..499d1a4
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/FamilyChooserPanel.java
@@ -0,0 +1,235 @@
+/*
+* 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.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 io.github.taverna_extras.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 org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.Family;
+import io.github.taverna_extras.component.api.Registry;
+import io.github.taverna_extras.component.api.profile.Profile;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+
+/**
+ * @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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/LicenseChooserPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/LicenseChooserPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/LicenseChooserPanel.java
new file mode 100644
index 0000000..faacc35
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/LicenseChooserPanel.java
@@ -0,0 +1,187 @@
+/*
+* 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.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 io.github.taverna_extras.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 org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.License;
+import io.github.taverna_extras.component.api.Registry;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+
+/**
+ * @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/b7b61e71/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/PrefixPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/PrefixPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/PrefixPanel.java
new file mode 100644
index 0000000..53d89c4
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/PrefixPanel.java
@@ -0,0 +1,96 @@
+/*
+* 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.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 io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.profile.Profile;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.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();
+       }
+}

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/panel/ProfileChoiceMessage.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ProfileChoiceMessage.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ProfileChoiceMessage.java
new file mode 100644
index 0000000..432cf44
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ProfileChoiceMessage.java
@@ -0,0 +1,40 @@
+/*
+* 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.panel;
+
+import io.github.taverna_extras.component.api.profile.Profile;
+
+/**
+ * @author alanrw
+ */
+public class ProfileChoiceMessage {
+       private final Profile chosenProfile;
+
+       public ProfileChoiceMessage(Profile chosenProfile) {
+               this.chosenProfile = chosenProfile;
+       }
+
+       /**
+        * @return the chosenProfile
+        */
+       public Profile getChosenProfile() {
+               return chosenProfile;
+       }
+}

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/panel/ProfileChooserPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ProfileChooserPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ProfileChooserPanel.java
new file mode 100644
index 0000000..f8b0148
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/ProfileChooserPanel.java
@@ -0,0 +1,219 @@
+/*
+* 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.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 io.github.taverna_extras.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 org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.ComponentException;
+import io.github.taverna_extras.component.api.Registry;
+import io.github.taverna_extras.component.api.profile.Profile;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+
+/**
+ * @author alanrw
+ */
+public class ProfileChooserPanel extends JPanel implements
+               Observer<RegistryChoiceMessage>, 
Observable<ProfileChoiceMessage> {
+       private static final String READING_MSG = "Reading profiles";
+       private static final String PROFILE_LABEL = "Profile:";
+       private static final long serialVersionUID = 2175274929391537032L;
+       private static Logger logger = getLogger(ProfileChooserPanel.class);
+
+       private final List<Observer<ProfileChoiceMessage>> observers = new 
ArrayList<>();
+       private final JComboBox<String> profileBox = new JComboBox<>();
+       private final SortedMap<String, Profile> profileMap = new TreeMap<>();
+
+       private Registry registry;
+
+       public ProfileChooserPanel(RegistryChooserPanel registryPanel) {
+               this();
+               registryPanel.addObserver(new Observer<RegistryChoiceMessage>() 
{
+                       @Override
+                       public void notify(Observable<RegistryChoiceMessage> 
sender,
+                                       RegistryChoiceMessage message) throws 
Exception {
+                               try {
+                                       registry = message.getChosenRegistry();
+                                       updateProfileModel();
+                               } catch (Exception e) {
+                                       logger.error("failure when notifying 
about chosen registry", e);
+                               }
+                       }
+               });
+       }
+
+       public ProfileChooserPanel() {
+               super();
+               profileBox.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(PROFILE_LABEL), gbc);
+               gbc.gridx = 1;
+               gbc.weightx = 1;
+               gbc.fill = BOTH;
+               this.add(profileBox, gbc);
+               profileBox.addItemListener(new ItemListener() {
+                       @Override
+                       public void itemStateChanged(ItemEvent event) {
+                               if (event.getStateChange() == SELECTED)
+                                       
setProfile(profileMap.get(profileBox.getSelectedItem()));
+                       }
+               });
+
+               profileBox.setEditable(false);
+       }
+
+       @Override
+       public void notify(Observable<RegistryChoiceMessage> sender,
+                       RegistryChoiceMessage message) throws Exception {
+               try {
+                       registry = message.getChosenRegistry();
+                       updateProfileModel();
+               } catch (Exception e) {
+                       logger.error("failure when notifying about chosen 
registry", e);
+               }
+       }
+
+       private void updateProfileModel() {
+               profileMap.clear();
+               profileBox.removeAllItems();
+               profileBox.setToolTipText(null);
+               profileBox.addItem(READING_MSG);
+               profileBox.setEnabled(false);
+               new ProfileUpdater().execute();
+       }
+
+       private void setProfile(Profile componentProfile) {
+               if (componentProfile != null)
+                       
profileBox.setToolTipText(componentProfile.getDescription());
+               else
+                       profileBox.setToolTipText(null);
+
+               Profile chosenProfile = getChosenProfile();
+               ProfileChoiceMessage message = new 
ProfileChoiceMessage(chosenProfile);
+               for (Observer<ProfileChoiceMessage> o : getObservers())
+                       try {
+                               o.notify(this, message);
+                       } catch (Exception e) {
+                               logger.error("failure when notifying about 
profile choice", e);
+                       }
+       }
+
+       public Profile getChosenProfile() {
+               if (profileBox.getSelectedIndex() < 0)
+                       return null;
+
+               return profileMap.get(profileBox.getSelectedItem());
+       }
+
+       private class ProfileUpdater extends SwingWorker<String, Object> {
+               @Override
+               protected String doInBackground() throws Exception {
+                       if (registry == null)
+                               return null;
+                       List<Profile> componentProfiles;
+                       try {
+                               componentProfiles = 
registry.getComponentProfiles();
+                       } catch (ComponentException e) {
+                               logger.error("failed to get profiles", e);
+                               throw e;
+                       } catch (NullPointerException e) {
+                               logger.error("failed to get profiles", e);
+                               throw e;
+                       }
+                       for (Profile profile : componentProfiles)
+                               try {
+                                       profileMap.put(profile.getName(), 
profile);
+                               } catch (NullPointerException e) {
+                                       logger.error("failure getting profile 
name", e);
+                               }
+
+                       return null;
+               }
+
+               @Override
+               protected void done() {
+                       profileBox.removeAllItems();
+                       try {
+                               get();
+                               for (String name : profileMap.keySet())
+                                       profileBox.addItem(name);
+                               if (!profileMap.isEmpty()) {
+                                       String firstKey = profileMap.firstKey();
+                                       profileBox.setSelectedItem(firstKey);
+                                       setProfile(profileMap.get(firstKey));
+                               } else
+                                       profileBox.addItem("No profiles 
available");
+                       } catch (InterruptedException | ExecutionException e) {
+                               logger.error(e);
+                               profileBox.addItem("Unable to read profiles");
+                       }
+                       profileBox.setEnabled(!profileMap.isEmpty());
+               }
+       }
+
+       @Override
+       public void addObserver(Observer<ProfileChoiceMessage> observer) {
+               observers.add(observer);
+               ProfileChoiceMessage message = new 
ProfileChoiceMessage(getChosenProfile());
+               try {
+                       observer.notify(this, message);
+               } catch (Exception e) {
+                       logger.error("failure when notifying about profile 
choice", e);
+               }
+       }
+
+       @Override
+       public void removeObserver(Observer<ProfileChoiceMessage> observer) {
+               observers.remove(observer);
+       }
+
+       @Override
+       public List<Observer<ProfileChoiceMessage>> getObservers() {
+               return observers;
+       }
+}

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/panel/RegistryAndFamilyChooserComponentEntryPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/RegistryAndFamilyChooserComponentEntryPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/RegistryAndFamilyChooserComponentEntryPanel.java
new file mode 100644
index 0000000..1d393c5
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/RegistryAndFamilyChooserComponentEntryPanel.java
@@ -0,0 +1,98 @@
+/*
+* 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.panel;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+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.preference.ComponentPreference;
+
+/**
+ * @author alanrw
+ */
+public class RegistryAndFamilyChooserComponentEntryPanel extends JPanel {
+       private static final long serialVersionUID = -6675545311458594678L;
+       private static final String T2FLOW = ".t2flow";
+       private static final String WFBUNDLE = ".wfbundle";
+
+       private JTextField componentNameField = new JTextField(20);
+       private RegistryAndFamilyChooserPanel registryAndFamilyChooserPanel;
+
+       public RegistryAndFamilyChooserComponentEntryPanel(ComponentPreference 
prefs) {
+               super(new GridBagLayout());
+               registryAndFamilyChooserPanel = new 
RegistryAndFamilyChooserPanel(prefs);
+
+               GridBagConstraints gbc = new GridBagConstraints();
+               gbc.gridx = 0;
+               gbc.gridy = 0;
+               gbc.anchor = GridBagConstraints.WEST;
+               gbc.fill = GridBagConstraints.HORIZONTAL;
+               gbc.gridwidth = 2;
+               gbc.weightx = 1;
+               this.add(registryAndFamilyChooserPanel, gbc);
+               gbc.gridy = 1;
+
+               gbc.gridwidth = 1;
+               gbc.gridx = 0;
+               gbc.weightx = 0;
+               gbc.fill = GridBagConstraints.NONE;
+               this.add(new JLabel("Component name:"), gbc);
+               gbc.gridx = 1;
+               gbc.weightx = 1;
+               gbc.fill = GridBagConstraints.HORIZONTAL;
+               this.add(componentNameField, gbc);
+       }
+
+       public String getComponentName() {
+               return componentNameField.getText();
+       }
+
+       public void setComponentName(String name) {
+               componentNameField.setText(name);
+       }
+
+       public Version.ID getComponentVersionIdentification() {
+               String componentName = getComponentName();
+               Family familyChoice = 
registryAndFamilyChooserPanel.getChosenFamily();
+               Registry registry = 
registryAndFamilyChooserPanel.getChosenRegistry();
+
+               if (familyChoice == null || registry == null || componentName 
== null
+                               || componentName.isEmpty())
+                       return null;
+
+               return new Version.Identifier(registry.getRegistryBase(),
+                               familyChoice.getName(), trim(componentName), 
-1);
+       }
+
+       private static String trim(String name) {
+               if (name.endsWith(WFBUNDLE))
+                       return name.substring(0, name.length() - 
WFBUNDLE.length());
+               else if (name.endsWith(T2FLOW))
+                       return name.substring(0, name.length() - 
T2FLOW.length());
+               return name;
+       }
+}

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/panel/RegistryAndFamilyChooserPanel.java
----------------------------------------------------------------------
diff --git 
a/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/RegistryAndFamilyChooserPanel.java
 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/RegistryAndFamilyChooserPanel.java
new file mode 100644
index 0000000..bc24dca
--- /dev/null
+++ 
b/taverna-component-activity-ui/src/main/java/io/github/taverna_extras/component/ui/panel/RegistryAndFamilyChooserPanel.java
@@ -0,0 +1,102 @@
+/*
+* 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.panel;
+
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.WEST;
+import static org.apache.log4j.Logger.getLogger;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.util.List;
+
+import javax.swing.JPanel;
+
+import org.apache.log4j.Logger;
+import io.github.taverna_extras.component.api.Family;
+import io.github.taverna_extras.component.api.Registry;
+import io.github.taverna_extras.component.ui.preference.ComponentPreference;
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+
+/**
+ * @author alanrw
+ */
+public class RegistryAndFamilyChooserPanel extends JPanel implements
+               Observer<ProfileChoiceMessage>, Observable<FamilyChoiceMessage> 
{
+       private static Logger logger = 
getLogger(RegistryAndFamilyChooserPanel.class);
+       private static final long serialVersionUID = -535518473593617735L;
+       final RegistryChooserPanel registryPanel;
+       final FamilyChooserPanel familyPanel;
+
+       public RegistryAndFamilyChooserPanel(ComponentPreference prefs) {
+               super(new GridBagLayout());
+               registryPanel = new RegistryChooserPanel(prefs);
+               familyPanel = new FamilyChooserPanel(registryPanel);
+
+               GridBagConstraints gbc = new GridBagConstraints();
+               gbc.gridx = 0;
+               gbc.gridy = 0;
+               gbc.anchor = WEST;
+               gbc.fill = BOTH;
+               gbc.gridwidth = 2;
+               gbc.weightx = 1;
+               add(registryPanel, gbc);
+
+               gbc.gridx = 0;
+               gbc.gridy = 1;
+               gbc.weighty = 1;
+               add(familyPanel, gbc);
+       }
+
+       public Registry getChosenRegistry() {
+               return registryPanel.getChosenRegistry();
+       }
+
+       public Family getChosenFamily() {
+               return familyPanel.getChosenFamily();
+       }
+
+       @Override
+       public void addObserver(Observer<FamilyChoiceMessage> observer) {
+               familyPanel.addObserver(observer);
+       }
+
+       @Override
+       public List<Observer<FamilyChoiceMessage>> getObservers() {
+               return familyPanel.getObservers();
+       }
+
+       @Override
+       public void removeObserver(Observer<FamilyChoiceMessage> observer) {
+               familyPanel.removeObserver(observer);
+       }
+
+       @Override
+       public void notify(Observable<ProfileChoiceMessage> sender,
+                       ProfileChoiceMessage message) {
+               try {
+                       familyPanel.notify(sender, message);
+               } catch (Exception e) {
+                       logger.error("problem handling notification about 
profile choice",
+                                       e);
+               }
+       }
+}


Reply via email to