Revert "temporarily empty repository" This reverts commit dc466d6d0d588fefbec8f009ac57cfa520679050.
diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a23d348 --- /dev/null +++ b/pom.xml
@@ -0,0 +1,91 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.taverna</groupId> + <artifactId>taverna-parent</artifactId> + <version>1-incubating-SNAPSHOT</version> + </parent> + <groupId>org.apache.taverna.workbench</groupId> + <artifactId>taverna-workbench</artifactId> + <version>3.1.0-incubating-SNAPSHOT</version> + <packaging>pom</packaging> + <name>Apache Taverna Workbench</name> + <description>Graphical workbench for editing and running Apache Taverna workflows</description> + <properties> + <taverna.language.version>0.15.0-incubating-SNAPSHOT</taverna.language.version> + <taverna.osgi.version>0.2.0-incubating-SNAPSHOT</taverna.osgi.version> + <taverna.engine.version>3.1.0-incubating-SNAPSHOT</taverna.engine.version> + <taverna.commonactivities.version>2.1.0-incubating-SNAPSHOT</taverna.commonactivities.version> + </properties> + <modules> + <module>taverna-dataflow-activity-ui</module> + <module>taverna-disabled-activity-ui</module> + <module>taverna-stringconstant-activity-ui</module> + <module>taverna-unrecognized-activity-ui</module> + <module>taverna-workbench-activity-icons-api</module> + <module>taverna-workbench-activity-palette-api</module> + <module>taverna-workbench-activity-palette-impl</module> + <module>taverna-workbench-activity-palette-ui</module> + <module>taverna-workbench-activity-tools</module> + <module>taverna-workbench-configuration-api</module> + <module>taverna-workbench-configuration-impl</module> + <module>taverna-workbench-contextual-views</module> + <module>taverna-workbench-contextual-views-api</module> + <module>taverna-workbench-contextual-views-impl</module> + <module>taverna-workbench-credential-manager-ui</module> + <module>taverna-workbench-data-management-config-ui</module> + <module>taverna-workbench-design-ui</module> + <module>taverna-workbench-edits-api</module> + <module>taverna-workbench-edits-impl</module> + <module>taverna-workbench-file-api</module> + <module>taverna-workbench-file-impl</module> + <module>taverna-workbench-graph-model</module> + <module>taverna-workbench-graph-view</module> + <module>taverna-workbench-helper</module> + <module>taverna-workbench-helper-api</module> + <module>taverna-workbench-httpproxy-config</module> + <module>taverna-workbench-iteration-strategy-ui</module> + <module>taverna-workbench-loop-ui</module> + <module>taverna-workbench-menu-api</module> + <module>taverna-workbench-menu-impl</module> + <module>taverna-workbench-menu-items</module> + <module>taverna-workbench-monitor-view</module> + <module>taverna-workbench-parallelize-ui</module> + <module>taverna-workbench-perspective-biocatalogue</module> + <module>taverna-workbench-perspective-design</module> + <module>taverna-workbench-perspective-myexperiment</module> + <module>taverna-workbench-perspective-results</module> + <module>taverna-workbench-plugin-manager</module> + <module>taverna-workbench-plugins-gui</module> + <module>taverna-workbench-reference-ui</module> + <module>taverna-workbench-renderers-api</module> + <module>taverna-workbench-renderers-exts</module> + <module>taverna-workbench-renderers-impl</module> + <module>taverna-workbench-report-api</module> + <module>taverna-workbench-report-explainer</module> + <module>taverna-workbench-report-impl</module> + <module>taverna-workbench-report-view</module> + <module>taverna-workbench-results-view</module> + <module>taverna-workbench-retry-ui</module> + <module>taverna-workbench-run-ui</module> + <module>taverna-workbench-selection-api</module> + <module>taverna-workbench-selection-impl</module> + <module>taverna-workbench-update-manager</module> + <module>taverna-workbench-workbench-api</module> + <module>taverna-workbench-workbench-impl</module> + <module>taverna-workbench-workflow-explorer</module> + <module>taverna-workbench-workflow-view</module> + </modules> + <repositories> + <repository> + <id>taverna-incubating</id> + <name>Apache Taverna incubating Repository</name> + <url>http://repository.mygrid.org.uk/artifactory/incubator-snapshot-local/</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + </repository> + </repositories> +</project>
diff --git a/taverna-dataflow-activity-ui/pom.xml b/taverna-dataflow-activity-ui/pom.xml new file mode 100644 index 0000000..d93cf07 --- /dev/null +++ b/taverna-dataflow-activity-ui/pom.xml
@@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna</groupId> + <artifactId>taverna-parent</artifactId> + <version>3.0.1-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>dataflow-activity-ui</artifactId> + <version>2.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>Taverna 2 Dataflow Activity UI</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-icons-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-palette-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>workflow-view</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>graph-view</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + <version>${scufl2.version}</version> + </dependency> + + <dependency> + <groupId>javax.help</groupId> + <artifactId>javahelp</artifactId> + <version>${javahelp.version}</version> + </dependency> + + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-tools</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + + <!-- testing dependencies --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + <version> ${junit.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>file-impl</artifactId> + <version>${t2.ui.impl.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>edits-impl</artifactId> + <version>${t2.ui.impl.version}</version> + <scope>test</scope> + </dependency> + <!-- <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-t2flow</artifactId> + <version>${scufl2.version}</version> + <scope>test</scope> + </dependency> --> + </dependencies> + <repositories> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository + </url> + </repository> + <repository> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + <id>mygrid-snapshot-repository</id> + <name>myGrid Snapshot Repository</name> + <url>http://www.mygrid.org.uk/maven/snapshot-repository</url> + </repository> + </repositories> +</project> +
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/actions/EditNestedDataflowAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/actions/EditNestedDataflowAction.java new file mode 100644 index 0000000..679209c --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/actions/EditNestedDataflowAction.java
@@ -0,0 +1,49 @@ +/** + * + */ +package net.sf.taverna.t2.activities.dataflow.actions; + +import java.awt.event.ActionEvent; +import java.util.List; + +import javax.swing.AbstractAction; + +import com.fasterxml.jackson.databind.JsonNode; + +import net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowTemplateService; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; + +@SuppressWarnings("serial") +public class EditNestedDataflowAction extends AbstractAction { + + private final Activity activity; + private final SelectionManager selectionManager; + + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public EditNestedDataflowAction(Activity activity, SelectionManager selectionManager) { + super("Edit nested workflow"); + this.activity = activity; + this.selectionManager = selectionManager; + } + + public void actionPerformed(ActionEvent e) { + if (activity.getType().equals(DataflowTemplateService.ACTIVITY_TYPE)) { + for (Configuration configuration : scufl2Tools.configurationsFor(activity, selectionManager.getSelectedProfile())) { + JsonNode nested = configuration.getJson().get("nestedWorkflow"); + Workflow nestedWorkflow = selectionManager.getSelectedWorkflowBundle().getWorkflows().getByName(nested.asText()); + if (nestedWorkflow != null) { + selectionManager.setSelectedWorkflow(nestedWorkflow); + break; + } + } + } + } + +} \ No newline at end of file
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/menu/EditNestedDataflowMenuAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/menu/EditNestedDataflowMenuAction.java new file mode 100644 index 0000000..8bdf0e1 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/menu/EditNestedDataflowMenuAction.java
@@ -0,0 +1,28 @@ +package net.sf.taverna.t2.activities.dataflow.menu; + +import javax.swing.Action; + +import net.sf.taverna.t2.activities.dataflow.actions.EditNestedDataflowAction; +import net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowTemplateService; +import net.sf.taverna.t2.workbench.activitytools.AbstractConfigureActivityMenuAction; +import net.sf.taverna.t2.workbench.selection.SelectionManager; + +public class EditNestedDataflowMenuAction extends AbstractConfigureActivityMenuAction { + + private SelectionManager selectionManager; + + public EditNestedDataflowMenuAction() { + super(DataflowTemplateService.ACTIVITY_TYPE); + } + + @Override + protected Action createAction() { + EditNestedDataflowAction configAction = new EditNestedDataflowAction(findActivity(), selectionManager); + return configAction; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/servicedescriptions/DataflowActivityIcon.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/servicedescriptions/DataflowActivityIcon.java new file mode 100644 index 0000000..6d7e766 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/servicedescriptions/DataflowActivityIcon.java
@@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.dataflow.servicedescriptions; + +import java.net.URI; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +import net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI; + +/** + * + * @author Alex Nenadic + * @author alanrw + * + */ +public class DataflowActivityIcon implements ActivityIconSPI{ + + private static Icon icon; + + public int canProvideIconScore(URI activityType) { + if (DataflowTemplateService.ACTIVITY_TYPE.equals(activityType)) + return DEFAULT_ICON + 1; + else + return NO_ICON; + } + + public Icon getIcon(URI activityType) { + return getDataflowIcon(); + } + + public static Icon getDataflowIcon() { + if (icon == null) { + icon = new ImageIcon(DataflowActivityIcon.class.getResource("/dataflow.png")); + } + return icon; + } +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/servicedescriptions/DataflowTemplateService.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/servicedescriptions/DataflowTemplateService.java new file mode 100644 index 0000000..f5cd8f2 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/servicedescriptions/DataflowTemplateService.java
@@ -0,0 +1,54 @@ +package net.sf.taverna.t2.activities.dataflow.servicedescriptions; + +import java.net.URI; + +import javax.swing.Icon; + +import net.sf.taverna.t2.servicedescriptions.AbstractTemplateService; +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import uk.org.taverna.scufl2.api.configurations.Configuration; + +public class DataflowTemplateService extends AbstractTemplateService { + + public static final URI ACTIVITY_TYPE = URI.create("http://ns.taverna.org.uk/2010/activity/nested-workflow"); + + private static final String A_CONFIGURABLE_NESTED_WORKFLOW = "A service that allows you to have one workflow nested within another"; + private static final String DATAFLOW = "Nested workflow"; + + private static final URI providerId = URI.create("http://taverna.sf.net/2010/service-provider/dataflow"); + + @Override + public URI getActivityType() { + return ACTIVITY_TYPE; + } + + @Override + public Configuration getActivityConfiguration() { + Configuration configuration = new Configuration(); + configuration.setType(ACTIVITY_TYPE.resolve("#Config")); + return configuration; + } + + @Override + public Icon getIcon() { + return DataflowActivityIcon.getDataflowIcon(); + } + + public String getName() { + return DATAFLOW; + } + + public String getDescription() { + return A_CONFIGURABLE_NESTED_WORKFLOW; + } + + public static ServiceDescription getServiceDescription() { + DataflowTemplateService dts = new DataflowTemplateService(); + return dts.templateService; + } + + public String getId() { + return providerId.toString(); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/views/DataflowActivityContextualView.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/views/DataflowActivityContextualView.java new file mode 100644 index 0000000..7bc44cb --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/views/DataflowActivityContextualView.java
@@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.dataflow.views; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Frame; + +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; + +import net.sf.taverna.t2.activities.dataflow.actions.EditNestedDataflowAction; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.actions.ReplaceNestedWorkflowAction; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.actions.activity.HTMLBasedActivityContextualView; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.activity.Activity; + +@SuppressWarnings("serial") +public class DataflowActivityContextualView extends HTMLBasedActivityContextualView { + + static Logger logger = Logger.getLogger(DataflowActivityContextualView.class); + + private final EditManager editManager; + private final FileManager fileManager; + private final MenuManager menuManager; + private final ActivityIconManager activityIconManager; + private final ColourManager colourManager; + private final WorkbenchConfiguration workbenchConfiguration; + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + private final SelectionManager selectionManager; + + public DataflowActivityContextualView(Activity activity, EditManager editManager, + FileManager fileManager, MenuManager menuManager, + ActivityIconManager activityIconManager, ColourManager colourManager, + ServiceDescriptionRegistry serviceDescriptionRegistry, + WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) { + super(activity, colourManager); + this.editManager = editManager; + this.fileManager = fileManager; + this.menuManager = menuManager; + this.activityIconManager = activityIconManager; + this.colourManager = colourManager; + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + this.workbenchConfiguration = workbenchConfiguration; + this.selectionManager = selectionManager; + addEditButtons(); + } + + @Override + public Activity getActivity() { + return super.getActivity(); + } + + public void addEditButtons() { + JComponent mainFrame = getMainFrame(); + JButton viewWorkflowButton = new JButton("Edit workflow"); + viewWorkflowButton.addActionListener(new EditNestedDataflowAction(getActivity(), + selectionManager)); + JButton configureButton = new JButton(new ReplaceNestedWorkflowAction(getActivity(), + editManager, fileManager, menuManager, activityIconManager, colourManager, + serviceDescriptionRegistry, workbenchConfiguration, selectionManager)); + configureButton.setIcon(null); + JPanel flowPanel = new JPanel(new FlowLayout()); + flowPanel.add(viewWorkflowButton); + flowPanel.add(configureButton); + mainFrame.add(flowPanel, BorderLayout.SOUTH); + mainFrame.revalidate(); + } + +// @Override +// public JComponent getMainFrame() { +// JComponent mainFrame = super.getMainFrame(); +// JButton viewWorkflowButton = new JButton("Edit workflow"); +// viewWorkflowButton.addActionListener(new EditNestedDataflowAction(getActivity(), +// selectionManager)); +// JButton configureButton = new JButton(new ReplaceNestedWorkflowAction(getActivity(), +// editManager, fileManager, menuManager, activityIconManager, colourManager, +// serviceDescriptionRegistry, workbenchConfiguration, selectionManager)); +// configureButton.setIcon(null); +// JPanel flowPanel = new JPanel(new FlowLayout()); +// flowPanel.add(viewWorkflowButton); +// flowPanel.add(configureButton); +// mainFrame.add(flowPanel, BorderLayout.SOUTH); +// return mainFrame; +// } + + @Override + protected String getRawTableRowsHtml() { + return ("<tr><td colspan=2>" + getActivity().getName() + "</td></tr>"); + } + + @Override + public String getViewTitle() { + return "Nested workflow"; + } + + @Override + public Action getConfigureAction(Frame owner) { + return null; + // return new OpenNestedDataflowFromFileAction( + // (DataflowActivity) getActivity(), owner); + } + + @Override + public int getPreferredPosition() { + return 100; + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/views/DataflowActivityViewFactory.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/views/DataflowActivityViewFactory.java new file mode 100644 index 0000000..e5d8f33 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/activities/dataflow/views/DataflowActivityViewFactory.java
@@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.dataflow.views; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowTemplateService; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.activity.Activity; + +public class DataflowActivityViewFactory implements ContextualViewFactory<Activity> { + + private EditManager editManager; + private FileManager fileManager; + private MenuManager menuManager; + private ColourManager colourManager; + private ActivityIconManager activityIconManager; + private WorkbenchConfiguration workbenchConfiguration; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + private SelectionManager selectionManager; + + public boolean canHandle(Object object) { + return object instanceof Activity + && ((Activity) object).getType().equals(DataflowTemplateService.ACTIVITY_TYPE); + } + + public List<ContextualView> getViews(Activity activity) { + return Arrays.asList(new ContextualView[] { new DataflowActivityContextualView(activity, + editManager, fileManager, menuManager, activityIconManager, colourManager, + serviceDescriptionRegistry, workbenchConfiguration, selectionManager) }); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setServiceDescriptionRegistry(ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/DataflowMerger.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/DataflowMerger.java new file mode 100644 index 0000000..327e5a7 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/DataflowMerger.java
@@ -0,0 +1,124 @@ +package net.sf.taverna.t2.workbench.file.importworkflow; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workflow.edits.AddChildEdit; +import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit; +import net.sf.taverna.t2.workflow.edits.AddProcessorEdit; +import net.sf.taverna.t2.workflow.edits.AddWorkflowInputPortEdit; +import net.sf.taverna.t2.workflow.edits.AddWorkflowOutputPortEdit; +import uk.org.taverna.scufl2.api.common.AbstractCloneable; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.ControlLink; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * A tool that allows merging of two workflow. + * <p> + * The merge is performed as a series of edit, inserting a copy of the source + * workflow into the destination workflow. + * + * @author Stian Soiland-Reyes + * @author David Withers + */ +public class DataflowMerger { + + /** + * Make a copy of a workflow. + * + * @param source + * workflow to copy + * @return A copy of the workflow. + */ + public static Workflow copyWorkflow(Workflow source) { + WorkflowBundle workflowBundle = AbstractCloneable.cloneWorkflowBean(source.getParent()); + return workflowBundle.getWorkflows().getByName(source.getName()); + } + + private final Workflow destinationWorkflow; + + /** + * Construct a {@link DataflowMerger} for the given destination workflow. + * + * @param destinationWorkflow + * Workflow to be merged into + */ + public DataflowMerger(Workflow destinationWorkflow) { + this.destinationWorkflow = destinationWorkflow; + } + + /** + * Make an {@link Edit} that when performed merges the given source dataflow + * into the destination dataflow. + * <p> + * Internally a copy is made of the source dataflow, to avoid modifying the + * links and processors. + * + * @param sourceDataflow + * Dataflow to merge from + * @return An edit that can perform and undo the insertion of the components + * from the source dataflow. + * @throws MergeException + * If the merge cannot be performed. + */ + public CompoundEdit getMergeEdit(Workflow sourceDataflow) + throws MergeException { + return getMergeEdit(sourceDataflow, ""); + } + + /** + * Make an {@link Edit} that when performed merges the given source dataflow + * into the destination dataflow. + * <p> + * Internally a copy is made of the source dataflow, to avoid modifying the + * links and processors. + * + * @param sourceWorkflow + * Dataflow to merge from + * @param prefix + * A prefix which will be inserted in front of the names for the + * merged workflow components. + * @return An edit that can perform and undo the insertion of the components + * from the source dataflow. + * @throws MergeException + * If the merge cannot be performed. + */ + public CompoundEdit getMergeEdit(Workflow sourceWorkflow, String prefix) + throws MergeException { + List<Edit<?>> compoundEdit = new ArrayList<>(); + + Workflow workflow = copyWorkflow(sourceWorkflow); + + for (InputWorkflowPort input : workflow.getInputPorts()) { + destinationWorkflow.getInputPorts().addWithUniqueName(input); + destinationWorkflow.getInputPorts().remove(input); + compoundEdit.add(new AddWorkflowInputPortEdit(destinationWorkflow, input)); + } + for (OutputWorkflowPort output : workflow.getOutputPorts()) { + destinationWorkflow.getOutputPorts().addWithUniqueName(output); + destinationWorkflow.getOutputPorts().remove(output); + compoundEdit.add(new AddWorkflowOutputPortEdit(destinationWorkflow, output)); + } + for (Processor processor : workflow.getProcessors()) { + processor.setName(prefix + processor.getName()); + compoundEdit.add(new AddProcessorEdit(destinationWorkflow, processor)); + } + for (DataLink dataLink : workflow.getDataLinks()) { + compoundEdit.add(new AddDataLinkEdit(destinationWorkflow, dataLink)); + } + for (ControlLink controlLink : workflow.getControlLinks()) { + compoundEdit.add(new AddChildEdit<Workflow>(destinationWorkflow, controlLink)); + } + + return new CompoundEdit(compoundEdit); + + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/MergeException.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/MergeException.java new file mode 100644 index 0000000..3645f91 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/MergeException.java
@@ -0,0 +1,22 @@ +package net.sf.taverna.t2.workbench.file.importworkflow; + +public class MergeException extends Exception { + private static final long serialVersionUID = 6018700359518335402L; + + public MergeException() { + super(); + } + + public MergeException(String message, Throwable cause) { + super(message, cause); + } + + public MergeException(String message) { + super(message); + } + + public MergeException(Throwable cause) { + super(cause); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/AddNestedWorkflowAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/AddNestedWorkflowAction.java new file mode 100644 index 0000000..d6f04dd --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/AddNestedWorkflowAction.java
@@ -0,0 +1,59 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.actions; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowActivityIcon; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.gui.ImportWorkflowWizard; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Utils; + +/** + * An action for adding a nested workflow. + * + * @author Stian Soiland-Reyes + * + */ +public class AddNestedWorkflowAction extends AbstractAction { + private static final long serialVersionUID = -2242979457902699028L; + private final EditManager editManager; + private final FileManager fileManager; + private final MenuManager menuManager; + private final ColourManager colourManager; + private final WorkbenchConfiguration workbenchConfiguration; + private final SelectionManager selectionManager; + + public AddNestedWorkflowAction(EditManager editManager, FileManager fileManager, + MenuManager menuManager, ColourManager colourManager, + WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) { + super("Add nested workflow", DataflowActivityIcon.getDataflowIcon()); + this.editManager = editManager; + this.fileManager = fileManager; + this.menuManager = menuManager; + this.colourManager = colourManager; + this.workbenchConfiguration = workbenchConfiguration; + this.selectionManager = selectionManager; + } + + public void actionPerformed(ActionEvent e) { + final Component parentComponent; + if (e.getSource() instanceof Component) { + parentComponent = (Component) e.getSource(); + } else { + parentComponent = null; + } + ImportWorkflowWizard wizard = new ImportWorkflowWizard( + Utils.getParentFrame(parentComponent), editManager, fileManager, menuManager, + colourManager, workbenchConfiguration, selectionManager); + wizard.setMergeEnabled(false); + wizard.setVisible(true); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/ImportWorkflowAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/ImportWorkflowAction.java new file mode 100644 index 0000000..6d9fffb --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/ImportWorkflowAction.java
@@ -0,0 +1,59 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.actions; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowActivityIcon; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.gui.ImportWorkflowWizard; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Utils; + +/** + * A general version of {@link AddNestedWorkflowAction} and {@link MergeWorkflowAction} that allows + * the user to choose which action to perform. + * + * @author Stian Soiland-Reyes + * + */ +public class ImportWorkflowAction extends AbstractAction { + private static final long serialVersionUID = -2242979457902699028L; + private final EditManager editManager; + private final FileManager fileManager; + private final MenuManager menuManager; + private final ColourManager colourManager; + private final WorkbenchConfiguration workbenchConfiguration; + private final SelectionManager selectionManager; + + public ImportWorkflowAction(EditManager editManager, FileManager fileManager, + MenuManager menuManager, ColourManager colourManager, + WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) { + super("Import workflow", DataflowActivityIcon.getDataflowIcon()); + this.editManager = editManager; + this.fileManager = fileManager; + this.menuManager = menuManager; + this.colourManager = colourManager; + this.workbenchConfiguration = workbenchConfiguration; + this.selectionManager = selectionManager; + } + + public void actionPerformed(ActionEvent e) { + final Component parentComponent; + if (e.getSource() instanceof Component) { + parentComponent = (Component) e.getSource(); + } else { + parentComponent = null; + } + ImportWorkflowWizard wizard = new ImportWorkflowWizard( + Utils.getParentFrame(parentComponent), editManager, fileManager, menuManager, + colourManager, workbenchConfiguration, selectionManager); + wizard.setVisible(true); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/MergeWorkflowAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/MergeWorkflowAction.java new file mode 100644 index 0000000..d86f97c --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/MergeWorkflowAction.java
@@ -0,0 +1,58 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.actions; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.gui.ImportWorkflowWizard; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Utils; + +/** + * An action for merging two workflows + * + * @author Stian Soiland-Reyes + * + */ +public class MergeWorkflowAction extends AbstractAction { + private static final long serialVersionUID = -2242979457902699028L; + private final EditManager editManager; + private final FileManager fileManager; + private final MenuManager menuManager; + private final ColourManager colourManager; + private final WorkbenchConfiguration workbenchConfiguration; + private final SelectionManager selectionManager; + + public MergeWorkflowAction(EditManager editManager, FileManager fileManager, + MenuManager menuManager, ColourManager colourManager, + WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) { + super("Merge workflow"); + this.editManager = editManager; + this.fileManager = fileManager; + this.menuManager = menuManager; + this.colourManager = colourManager; + this.workbenchConfiguration = workbenchConfiguration; + this.selectionManager = selectionManager; + } + + public void actionPerformed(ActionEvent e) { + final Component parentComponent; + if (e.getSource() instanceof Component) { + parentComponent = (Component) e.getSource(); + } else { + parentComponent = null; + } + ImportWorkflowWizard wizard = new ImportWorkflowWizard( + Utils.getParentFrame(parentComponent), editManager, fileManager, menuManager, + colourManager, workbenchConfiguration, selectionManager); + wizard.setNestedEnabled(false); + wizard.setVisible(true); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/OpenSourceWorkflowAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/OpenSourceWorkflowAction.java new file mode 100644 index 0000000..f392405 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/OpenSourceWorkflowAction.java
@@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.importworkflow.actions; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.prefs.Preferences; + +import javax.swing.AbstractAction; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; + +import org.apache.log4j.Logger; + +/** + * @author David Withers + */ +@SuppressWarnings("serial") +public abstract class OpenSourceWorkflowAction extends AbstractAction { + + private static Logger logger = Logger.getLogger(OpenSourceWorkflowAction.class); + + private static final String OPEN_WORKFLOW = "Open workflow..."; + + protected FileManager fileManager; + + public OpenSourceWorkflowAction(FileManager fileManager) { + super(OPEN_WORKFLOW, WorkbenchIcons.openIcon); + this.fileManager = fileManager; + } + + public void actionPerformed(ActionEvent e) { + final Component parentComponent; + if (e.getSource() instanceof Component) { + parentComponent = (Component) e.getSource(); + } else { + parentComponent = null; + } + openWorkflows(parentComponent); + } + + public abstract void openWorkflows(Component parentComponent, File[] files); + + /** + * Pop up an Open-dialogue to select one or more workflow files to open. + * + * @param parentComponent + * The UI parent component to use for pop up dialogues + * @param openCallback + * An {@link OpenCallback} to be called during the file opening. + * The callback will be invoked for each file that has been + * opened, as file opening happens in a separate thread that + * might execute after the return of this method. + * @return <code>false</code> if no files were selected or the dialogue was + * cancelled, or <code>true</code> if the process of opening one or + * more files has been started. + */ + public boolean openWorkflows(final Component parentComponent) { + JFileChooser fileChooser = new JFileChooser(); + Preferences prefs = Preferences.userNodeForPackage(getClass()); + String curDir = prefs.get("currentDir", System.getProperty("user.home")); + fileChooser.setDialogTitle(OPEN_WORKFLOW); + + fileChooser.resetChoosableFileFilters(); + fileChooser.setAcceptAllFileFilterUsed(false); + List<FileFilter> fileFilters = fileManager.getOpenFileFilters(); + if (fileFilters.isEmpty()) { + logger.warn("No file types found for opening workflow"); + JOptionPane + .showMessageDialog(parentComponent, + "No file types found for opening workflow.", "Error", + JOptionPane.ERROR_MESSAGE); + return false; + } + for (FileFilter fileFilter : fileFilters) { + fileChooser.addChoosableFileFilter(fileFilter); + } + + fileChooser.setFileFilter(fileFilters.get(0)); + + fileChooser.setCurrentDirectory(new File(curDir)); + fileChooser.setMultiSelectionEnabled(true); + + int returnVal = fileChooser.showOpenDialog(parentComponent); + if (returnVal == JFileChooser.APPROVE_OPTION) { + prefs.put("currentDir", fileChooser.getCurrentDirectory().toString()); + final File[] selectedFiles = fileChooser.getSelectedFiles(); + if (selectedFiles.length == 0) { + logger.warn("No files selected"); + return false; + } + new FileOpenerThread(parentComponent, selectedFiles).start(); + return true; + } + return false; + } + + private final class FileOpenerThread extends Thread { + private final File[] files; + private final Component parentComponent; + + private FileOpenerThread(Component parentComponent, File[] selectedFiles) { + super("Opening workflows(s) " + Arrays.asList(selectedFiles)); + this.parentComponent = parentComponent; + this.files = selectedFiles; + } + + @Override + public void run() { + openWorkflows(parentComponent, files); + } + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/ReplaceNestedWorkflowAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/ReplaceNestedWorkflowAction.java new file mode 100644 index 0000000..9199ab5 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/actions/ReplaceNestedWorkflowAction.java
@@ -0,0 +1,84 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.actions; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.gui.ImportWorkflowWizard; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Utils; +import net.sf.taverna.t2.workbench.ui.actions.activity.ActivityConfigurationAction; +import net.sf.taverna.t2.workflow.edits.ConfigureEdit; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class ReplaceNestedWorkflowAction extends ActivityConfigurationAction { + private static final long serialVersionUID = 1L; + + private final EditManager editManager; + private final FileManager fileManager; + private final MenuManager menuManager; + + private final ColourManager colourManager; + + private final WorkbenchConfiguration workbenchConfiguration; + + private final SelectionManager selectionManager; + + public ReplaceNestedWorkflowAction(Activity activity, EditManager editManager, + FileManager fileManager, MenuManager menuManager, + ActivityIconManager activityIconManager, ColourManager colourManager, + ServiceDescriptionRegistry serviceDescriptionRegistry, + WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) { + super(activity, activityIconManager, serviceDescriptionRegistry); + this.editManager = editManager; + this.fileManager = fileManager; + this.menuManager = menuManager; + this.colourManager = colourManager; + this.workbenchConfiguration = workbenchConfiguration; + this.selectionManager = selectionManager; + putValue(NAME, "Replace nested workflow"); + } + + public void actionPerformed(ActionEvent e) { + final Component parentComponent; + if (e.getSource() instanceof Component) { + parentComponent = (Component) e.getSource(); + } else { + parentComponent = null; + } + ImportWorkflowWizard wizard = new ImportWorkflowWizard( + Utils.getParentFrame(parentComponent), editManager, fileManager, menuManager, + colourManager, workbenchConfiguration, selectionManager) { + private static final long serialVersionUID = 1L; + +// @Override +// protected Edit<?> makeInsertNestedWorkflowEdit(Workflow nestedFlow, String name) { +// Configuration configuration = new Configuration(); +// configuration.setType(null); +// // TODO use service registry +// return new ConfigureEdit<Activity>(getActivity(), null, configuration); +// } + +// @Override +// protected Activity getInsertedActivity() { +// return getActivity(); +// } + }; + + wizard.setMergeEnabled(false); +// wizard.setCustomDestinationDataflow(fileManager.getCurrentDataflow(), +// "Existing nested workflow"); +// wizard.setDestinationEnabled(false); + wizard.setVisible(true); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/gui/ImportWorkflowWizard.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/gui/ImportWorkflowWizard.java new file mode 100644 index 0000000..b8ddf1a --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/gui/ImportWorkflowWizard.java
@@ -0,0 +1,1272 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.ProgressMonitor; +import javax.swing.SwingUtilities; + +import net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowTemplateService; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.MainWindow; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.file.importworkflow.DataflowMerger; +import net.sf.taverna.t2.workbench.file.importworkflow.MergeException; +import net.sf.taverna.t2.workbench.file.importworkflow.actions.OpenSourceWorkflowAction; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphController; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.AddChildEdit; +import net.sf.taverna.t2.workflow.edits.AddProcessorEdit; + +import org.apache.batik.swing.JSVGCanvas; +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.iterationstrategy.CrossProduct; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +@SuppressWarnings("serial") +public class ImportWorkflowWizard extends HelpEnabledDialog { + + private static Logger logger = Logger.getLogger(ImportWorkflowWizard.class); + + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + protected BrowseFileOnClick browseFileOnClick = new BrowseFileOnClick(); + protected JButton buttonBrowse; + protected JComboBox chooseDataflow; + protected DataflowOpenerThread dataflowOpenerThread; + + private WorkflowBundle destinationWorkflowBundle; + private Workflow destinationWorkflow; + private Profile destinationProfile; + private Workflow sourceWorkflow; + + protected JTextField fieldFile; + + protected JTextField fieldUrl; + protected boolean mergeEnabled = true; + protected boolean nestedEnabled = true; + protected JSVGCanvas previewSource = new JSVGCanvas(null, false, false); + protected JSVGCanvas previewDestination = new JSVGCanvas(null, false, false); + protected JTextField prefixField; + protected JRadioButton radioFile; + protected JRadioButton radioNew; + protected JRadioButton radioOpened; + protected JRadioButton radioUrl; + protected ButtonGroup sourceSelection; + protected ActionListener updateChosenListener = new UpdateChosenListener(); + protected Thread updatePreviewsThread; + protected Component sourceSelectionPanel; + protected JLabel prefixLabel; + protected JLabel prefixHelp; +// protected JPanel destinationSelectionPanel; +// protected ButtonGroup destinationSelection; +// protected JRadioButton radioNewDestination; +// protected JRadioButton radioOpenDestination; +// protected JComboBox destinationAlreadyOpen; + protected JPanel introductionPanel; + protected ButtonGroup actionSelection; + protected JRadioButton actionNested; + protected JRadioButton actionMerge; + protected JRadioButton radioCustomSource; + protected JRadioButton radioCustomDestination; + + private final EditManager editManager; + private final FileManager fileManager; + private final MenuManager menuManager; + private final ColourManager colourManager; + private final WorkbenchConfiguration workbenchConfiguration; + private final SelectionManager selectionManager; + + private WorkflowBundle customSourceDataFlow = null; +// private Workflow customDestinationDataflow = null; + private String customSourceName = ""; +// private String customDestinationName = ""; + + private boolean sourceEnabled = true; +// private boolean destinationEnabled = true; + private Activity insertedActivity; + + public ImportWorkflowWizard(Frame parentFrame, EditManager editManager, + FileManager fileManager, MenuManager menuManager, ColourManager colourManager, + WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) { + super(parentFrame, "Import workflow", true, null); + this.selectionManager = selectionManager; + destinationWorkflow = selectionManager.getSelectedWorkflow(); + destinationProfile = selectionManager.getSelectedProfile(); + destinationWorkflowBundle = selectionManager.getSelectedWorkflowBundle(); + + this.editManager = editManager; + this.fileManager = fileManager; + this.menuManager = menuManager; + this.colourManager = colourManager; + this.workbenchConfiguration = workbenchConfiguration; + + setSize(600, 600); + add(makeContentPane(), BorderLayout.CENTER); + // Add some space + add(new JPanel(), BorderLayout.WEST); + add(new JPanel(), BorderLayout.NORTH); + add(new JPanel(), BorderLayout.SOUTH); + add(new JPanel(), BorderLayout.EAST); + findChosenDataflow(this, true); + updateAll(); + } + + public void setMergeEnabled(boolean importEnabled) { + this.mergeEnabled = importEnabled; + updateAll(); + } + + public void setNestedEnabled(boolean nestedEnabled) { + this.nestedEnabled = nestedEnabled; + updateAll(); + } + + /** + * Silly workaround to avoid "Cannot call invokeAndWait from the event dispatcher thread" + * exception. + * + * @param runnable + */ + public static void invokeAndWait(Runnable runnable) { + if (SwingUtilities.isEventDispatchThread()) { + runnable.run(); + return; + } + try { + SwingUtilities.invokeAndWait(runnable); + } catch (InterruptedException ex) { + // logger.warn("Runnable " + runnable + " was interrupted " + runnable, ex); + } catch (InvocationTargetException e) { + logger.warn("Can't invoke " + runnable, e); + } + } + + protected Component makeWorkflowImage() { + JPanel workflowImages = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridy = 0; + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 0.1; + + gbc.weightx = 0.1; + workflowImages.add(new JPanel(), gbc);// filler + + gbc.weightx = 0.0; + previewSource.setBackground(workflowImages.getBackground()); + workflowImages.add(previewSource, gbc); + + JLabel arrow = new JLabel("\u2192"); + arrow.setFont(arrow.getFont().deriveFont(48f)); + workflowImages.add(arrow, gbc); + + previewDestination.setBackground(workflowImages.getBackground()); + workflowImages.add(previewDestination, gbc); + + gbc.weightx = 0.1; + workflowImages.add(new JPanel(), gbc); + gbc.weightx = 0.0; + + return workflowImages; + } + + protected void updateAll() { + updatePreviews(); // will go in separate thread anyway, do it first + updateHeader(); + updateSourceSection(); +// updateDestinationSection(); + updateFooter(); + } + +// protected void updateDestinationSection() { +// +// radioNewDestination.setVisible(false); +// +// radioCustomDestination.setText(customDestinationName); +// radioCustomDestination.setVisible(customDestinationDataflow != null); +// +// // radioNewDestination.setVisible(nestedEnabled); +// // radioNewDestination.setEnabled(actionNested.isSelected()); +// +// destinationSelectionPanel.setVisible(destinationEnabled); +// +// } + + protected synchronized void updatePreviews() { + if (updatePreviewsThread != null && updatePreviewsThread.isAlive()) { + updatePreviewsThread.interrupt(); + } + updatePreviewsThread = new UpdatePreviewsThread(); + updatePreviewsThread.start(); + } + + protected void updateDestinationPreview() { + updateWorkflowGraphic(previewDestination, destinationWorkflow, destinationProfile); + } + + protected void updateSourcePreview() { + Profile sourceProfile = null; + if (sourceWorkflow != null) { + sourceProfile = sourceWorkflow.getParent().getMainProfile(); + } + updateWorkflowGraphic(previewSource, sourceWorkflow, sourceProfile); + } + + protected void updateFooter() { + prefixField.setVisible(mergeEnabled); + prefixLabel.setVisible(mergeEnabled); + prefixHelp.setVisible(mergeEnabled); + + prefixField.setEnabled(actionMerge.isSelected()); + prefixLabel.setEnabled(actionMerge.isSelected()); + prefixHelp.setEnabled(actionMerge.isSelected()); + if (actionMerge.isSelected()) { + prefixHelp.setForeground(prefixLabel.getForeground()); + } else { + // Work around + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4303706 + // and assume gray is the 'disabled' colour in our Look n Feel + prefixHelp.setForeground(Color.gray); + } + + } + + protected void updateHeader() { + makeIntroductionPanel(); + } + + protected void updateSourceSection() { + radioCustomSource.setText(customSourceName); + radioCustomSource.setVisible(customSourceDataFlow != null); + + radioNew.setVisible(nestedEnabled); + radioNew.setEnabled(actionNested.isSelected()); + + if (actionNested.isSelected() && sourceSelection.getSelection() == null) { + // Preselect the new workflow + radioNew.setSelected(true); + } + + sourceSelectionPanel.setVisible(sourceEnabled); + } + + /** + * Create a PNG image of the workflow and place inside an ImageIcon + * + * @param dataflow + * @return + * @throws InvocationTargetException + * @throws InterruptedException + */ + protected void updateWorkflowGraphic(final JSVGCanvas svgCanvas, final Workflow workflow, final Profile profile) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + // Set it to blank while reloading + svgCanvas.setSVGDocument(null); + if (workflow != null) { + SVGGraphController currentWfGraphController = new SVGGraphController( + workflow, profile, false, svgCanvas, + editManager, menuManager, colourManager, workbenchConfiguration); + } + } + }); + } catch (InterruptedException e) { + // logger.error(e); + } catch (InvocationTargetException e) { + // logger.error(e); + } + } + + /** + * Open the selected source and destination workflows. If background is true, this method will + * return immediately while a {@link DataflowOpenerThread} performs the updates. If a + * DataflowOpenerThread is already running, it will be interrupted and stopped. + * + * @param parentComponent + * The parent component for showing dialogues + * @param background + * If true, will run in separate thread. + * @return <code>false</code> if running in the background, or if a dialogue was shown and the + * operation is aborted by the user, or <code>true</code> if not running in the + * background and the method completed without user interruption. + */ + protected synchronized boolean findChosenDataflow(Component parentComponent, boolean background) { + if (dataflowOpenerThread != null && dataflowOpenerThread.isAlive()) { + if (background) { + // We've changed our mind + dataflowOpenerThread.interrupt(); + } else { + // We'll let it finish, we don't need to do it again + try { + dataflowOpenerThread.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return !dataflowOpenerThread.shownWarning; + } + } + dataflowOpenerThread = new DataflowOpenerThread(parentComponent, background); + + if (background) { + dataflowOpenerThread.start(); + return false; + } else { + dataflowOpenerThread.run(); + return !dataflowOpenerThread.shownWarning; + } + + } + + protected Container makeContentPane() { + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.ipadx = 5; + gbc.ipady = 5; + + gbc.gridx = 0; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.BOTH; + + introductionPanel = makeIntroductionPanel(); + panel.add(introductionPanel, gbc); + + sourceSelectionPanel = makeSourceSelectionPanel(); + panel.add(sourceSelectionPanel, gbc); + +// destinationSelectionPanel = makeDestinationSelectionPanel(); +// panel.add(destinationSelectionPanel, gbc); + + gbc.weighty = 0.1; + panel.add(makeImportStylePanel(), gbc); + + return panel; + } + + protected JPanel makeIntroductionPanel() { + if (introductionPanel == null) { + introductionPanel = new JPanel(new GridBagLayout()); + } else { + introductionPanel.removeAll(); + } + boolean bothEnabled = mergeEnabled && nestedEnabled; + if (bothEnabled) { + introductionPanel.setBorder(BorderFactory.createTitledBorder("Import method")); + } else { + introductionPanel.setBorder(BorderFactory.createEmptyBorder()); + } + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + // gbc.gridy = 0; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.BOTH; + gbc.anchor = GridBagConstraints.FIRST_LINE_START; + + StringBuilder nestedHelp = new StringBuilder(); + nestedHelp.append("<html><small>"); + nestedHelp.append("Add a <strong>nested workflow</strong> "); + nestedHelp.append("into the "); + nestedHelp.append("destination workflow as a single service. "); + nestedHelp.append("The nested workflow "); + nestedHelp.append("can be <em>edited separately</em>, but is shown "); + nestedHelp.append("expanded in the diagram of the parent "); + nestedHelp.append("workflow. In the parent workflow you can "); + nestedHelp.append("connect to the input and output ports of the nested "); + nestedHelp.append("workflow. "); + nestedHelp.append("</small></html>"); + + StringBuilder mergeHelp = new StringBuilder(); + mergeHelp.append("<html><small>"); + mergeHelp.append("<strong>Merge</strong> a workflow "); + mergeHelp.append("by copying all services, ports and links "); + mergeHelp.append("directly into the destination workflow. This can be "); + mergeHelp.append("useful for merging smaller workflow fragments. For "); + mergeHelp.append("inclusion of larger workflows you might find using "); + mergeHelp.append("<em>nested workflows</em> more beneficial."); + mergeHelp.append("</small></html>"); + + actionSelection = new ButtonGroup(); + actionNested = new JRadioButton(nestedHelp.toString()); + ActionListener updateListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateSourceSection(); +// updateDestinationSection(); + updateFooter(); + } + }; + actionNested.addActionListener(updateListener); + actionSelection.add(actionNested); + + actionMerge = new JRadioButton(mergeHelp.toString()); + actionMerge.addActionListener(updateListener); + actionSelection.add(actionMerge); + + if (bothEnabled) { + introductionPanel.add(actionNested, gbc); + introductionPanel.add(actionMerge, gbc); + actionNested.setSelected(true); + } else if (nestedEnabled) { + introductionPanel.add(new JLabel(nestedHelp.toString()), gbc); + actionNested.setSelected(true); + } else if (mergeEnabled) { + introductionPanel.add(new JLabel(mergeHelp.toString()), gbc); + actionMerge.setSelected(true); + } + return introductionPanel; + } + +// protected JPanel makeDestinationSelectionPanel() { +// JPanel j = new JPanel(new GridBagLayout()); +// j.setBorder(BorderFactory.createTitledBorder("Workflow destination")); +// +// GridBagConstraints gbc = new GridBagConstraints(); +// gbc.gridx = 0; +// gbc.gridy = 0; +// gbc.fill = GridBagConstraints.BOTH; +// +// destinationSelection = new ButtonGroup(); +// radioNewDestination = new JRadioButton("New workflow"); +// gbc.gridy = 0; +// j.add(radioNewDestination, gbc); +// destinationSelection.add(radioNewDestination); +// radioNewDestination.addActionListener(updateChosenListener); +// +// radioOpenDestination = new JRadioButton("Already opened workflow"); +// gbc.gridy = 2; +// j.add(radioOpenDestination, gbc); +// destinationSelection.add(radioOpenDestination); +// radioOpenDestination.addActionListener(updateChosenListener); +// gbc.weightx = 0.1; +// gbc.gridx = 1; +// destinationAlreadyOpen = makeSelectOpenWorkflowComboBox(true); +// j.add(destinationAlreadyOpen, gbc); +// +// radioCustomDestination = new JRadioButton(customDestinationName); +// radioCustomDestination.setVisible(customDestinationName != null); +// gbc.gridx = 0; +// gbc.gridy = 3; +// gbc.gridwidth = 2; +// j.add(radioCustomDestination, gbc); +// destinationSelection.add(radioCustomDestination); +// radioCustomDestination.addActionListener(updateChosenListener); +// gbc.gridwidth = 1; +// +// radioOpenDestination.setSelected(true); +// return j; +// } + + protected Component makeImportStylePanel() { + JPanel j = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.BOTH; + + j.setBorder(BorderFactory.createTitledBorder("Import")); + + prefixLabel = new JLabel("Prefix"); + j.add(prefixLabel, gbc); + gbc.weightx = 0.1; + gbc.gridx = 1; + + prefixField = new JTextField(10); + prefixLabel.setLabelFor(prefixField); + j.add(prefixField, gbc); + + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 2; + + prefixHelp = new JLabel( + "<html><small>Optional prefix to be prepended to the name of the " + + "inserted services and workflow ports. Even if no prefix is given, duplicate names will be " + + "resolved by adding numbers, for instance <code>my_service_2</code> if <code>my_service</code> already " + + "existed." + "</small></html>"); + prefixHelp.setLabelFor(prefixField); + j.add(prefixHelp, gbc); + + gbc.gridy = 2; + gbc.weightx = 0.1; + gbc.weighty = 0.1; + + j.add(makeWorkflowImage(), gbc); + + gbc.gridy = 3; + gbc.weighty = 0.0; + j.add(new JPanel(), gbc); + + gbc.gridy = 4; + gbc.fill = GridBagConstraints.NONE; + JButton comp = new JButton(new ImportWorkflowAction()); + j.add(comp, gbc); + return j; + + } + + protected Component makeSelectFile() { + JPanel j = new JPanel(new GridBagLayout()); + j.setBorder(BorderFactory.createEtchedBorder()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.BOTH; + gbc.weightx = 0.1; + + fieldFile = new JTextField(20); + fieldFile.setEditable(false); + fieldFile.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + radioFile.setSelected(true); + } + + @Override + public void focusLost(FocusEvent e) { + findChosenDataflow(e.getComponent(), true); + } + }); + j.add(fieldFile, gbc); + radioFile.addItemListener(new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + browseFileOnClick.checkEmptyFile(); + } + } + }); + + gbc.gridx = 1; + gbc.weightx = 0.0; + gbc.fill = GridBagConstraints.NONE; + buttonBrowse = new JButton(new OpenSourceWorkflowAction(fileManager) { + @Override + public void openWorkflows(Component parentComponent, File[] files) { + if (files.length == 0) { + radioFile.setSelected(false); + fieldFile.setText(""); + radioFile.requestFocus(); + return; + } + fieldFile.setText(files[0].getPath()); + if (!radioFile.isSelected()) { + radioFile.setSelected(true); + } + findChosenDataflow(parentComponent, true); + } + }); + buttonBrowse.setText("Browse"); + j.add(buttonBrowse, gbc); + + // This just duplicates things - we already have actions on + // the radioFile and fieldFile that will handle the events + // radioFile.addActionListener(browseFileOnClick); + // fieldFile.addActionListener(browseFileOnClick); + return j; + } + + protected JComboBox makeSelectOpenWorkflowComboBox(boolean selectCurrent) { + List<DataflowSelection> openDataflows = new ArrayList<DataflowSelection>(); + DataflowSelection current = null; + for (WorkflowBundle df : fileManager.getOpenDataflows()) { + String name = df.getMainWorkflow().getName(); + boolean isCurrent = df.equals(fileManager.getCurrentDataflow()); + if (isCurrent) { + // Wrapping as HTML causes weird drop-down box under MAC, so + // we just use normal text + // name = "<html><body>" + name + // + " <i>(current)</i></body></html>"; + name = name + " (current)"; + } + DataflowSelection selection = new DataflowSelection(df, name); + openDataflows.add(selection); + if (isCurrent) { + current = selection; + } + } + JComboBox chooseDataflow = new JComboBox(openDataflows.toArray()); + if (selectCurrent) { + chooseDataflow.setSelectedItem(current); + } + chooseDataflow.addActionListener(updateChosenListener); + return chooseDataflow; + + } + + protected Component makeSourceSelectionPanel() { + JPanel j = new JPanel(new GridBagLayout()); + j.setBorder(BorderFactory.createTitledBorder("Workflow source")); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.BOTH; + + sourceSelection = new ButtonGroup(); + radioNew = new JRadioButton("New workflow"); + gbc.gridy = 0; + j.add(radioNew, gbc); + sourceSelection.add(radioNew); + + radioNew.addActionListener(updateChosenListener); + + radioFile = new JRadioButton("Import from file"); + gbc.gridy = 1; + j.add(radioFile, gbc); + sourceSelection.add(radioFile); + radioFile.addActionListener(updateChosenListener); + + radioUrl = new JRadioButton("Import from URL"); + gbc.gridy = 2; + j.add(radioUrl, gbc); + sourceSelection.add(radioUrl); + radioUrl.addActionListener(updateChosenListener); + + radioOpened = new JRadioButton("Already opened workflow"); + gbc.gridy = 3; + j.add(radioOpened, gbc); + sourceSelection.add(radioOpened); + radioOpened.addActionListener(updateChosenListener); + + radioCustomSource = new JRadioButton(customSourceName); + radioCustomSource.setVisible(customSourceDataFlow != null); + gbc.gridy = 4; + gbc.gridwidth = 2; + j.add(radioCustomSource, gbc); + sourceSelection.add(radioCustomSource); + radioCustomSource.addActionListener(updateChosenListener); + gbc.gridwidth = 1; + + gbc.gridx = 1; + gbc.gridy = 1; + gbc.weightx = 0.1; + j.add(makeSelectFile(), gbc); + + gbc.gridy = 2; + fieldUrl = new JTextField(20); + j.add(fieldUrl, gbc); + fieldUrl.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + radioUrl.setSelected(true); + } + + @Override + public void focusLost(FocusEvent e) { + findChosenDataflow(e.getComponent(), true); + } + }); + + gbc.gridy = 3; + chooseDataflow = makeSelectOpenWorkflowComboBox(false); + chooseDataflow.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + radioOpened.setSelected(true); + } + }); + j.add(chooseDataflow, gbc); + + return j; + } + + protected Edit<?> makeInsertNestedWorkflowEdit(Workflow nestedFlow) { + Processor processor = new Processor(); + processor.setName("nestedWorkflow"); + + CrossProduct crossProduct = new CrossProduct(); + crossProduct.setParent(processor.getIterationStrategyStack()); + + Activity activity = new Activity(); + activity.setType(DataflowTemplateService.ACTIVITY_TYPE); + Configuration configuration = new Configuration(); + configuration.setType(DataflowTemplateService.ACTIVITY_TYPE.resolve("#Config")); + destinationWorkflowBundle.getWorkflows().addWithUniqueName(nestedFlow); + ((ObjectNode) configuration.getJson()).put("nestedWorkflow", nestedFlow.getName()); + destinationWorkflowBundle.getWorkflows().remove(nestedFlow); + configuration.setConfigures(activity); + + ProcessorBinding processorBinding = new ProcessorBinding(); + processorBinding.setBoundProcessor(processor); + processorBinding.setBoundActivity(activity); + + for (InputWorkflowPort workflowPort : nestedFlow.getInputPorts()) { + InputActivityPort activityPort = new InputActivityPort(activity, workflowPort.getName()); + activityPort.setDepth(workflowPort.getDepth()); + // create processor port + InputProcessorPort processorPort = new InputProcessorPort(processor, activityPort.getName()); + processorPort.setDepth(activityPort.getDepth()); + // add a new port binding + new ProcessorInputPortBinding(processorBinding, processorPort, activityPort); + } + for (OutputWorkflowPort workflowPort : nestedFlow.getOutputPorts()) { + OutputActivityPort activityPort = new OutputActivityPort(activity, workflowPort.getName()); + // TODO calculate output depth + activityPort.setDepth(0); + activityPort.setGranularDepth(0); + // create processor port + OutputProcessorPort processorPort = new OutputProcessorPort(processor, activityPort.getName()); + processorPort.setDepth(activityPort.getDepth()); + processorPort.setGranularDepth(activityPort.getGranularDepth()); + // add a new port binding + new ProcessorOutputPortBinding(processorBinding, activityPort, processorPort); + } + + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + editList.add(new AddChildEdit<Profile>(destinationProfile, activity)); + editList.add(new AddChildEdit<Profile>(destinationProfile, configuration)); + editList.add(new AddChildEdit<Profile>(destinationProfile, processorBinding)); + editList.add(new AddProcessorEdit(destinationWorkflow, processor)); + + editList.add(makeInsertWorkflowEdit(nestedFlow, nestedFlow.getParent().getMainProfile())); + + return new CompoundEdit(editList); + } + + protected Edit<?> makeInsertWorkflowEdit(Workflow nestedFlow, Profile profile) { + return makeInsertWorkflowEdit(nestedFlow, profile, new HashSet<>()); + } + + protected Edit<?> makeInsertWorkflowEdit(Workflow nestedFlow, Profile profile, Set<Object> seen) { + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + // add the nested workflow to the workflow bundle + editList.add(new AddChildEdit<WorkflowBundle>(destinationWorkflowBundle, nestedFlow)); + seen.add(nestedFlow); + for (Processor processor : nestedFlow.getProcessors()) { + // add processor bindings to the profile + List<ProcessorBinding> processorBindings = scufl2Tools.processorBindingsForProcessor(processor, profile); + for (ProcessorBinding processorBinding : processorBindings) { + editList.add(new AddChildEdit<Profile>(destinationProfile, processorBinding)); + // add activity to the profile + Activity activity = processorBinding.getBoundActivity(); + if (!seen.contains(activity)) { + editList.add(new AddChildEdit<Profile>(destinationProfile, activity)); + // add activity configurations to the profile + for (Configuration configuration : scufl2Tools.configurationsFor(activity, profile)) { + editList.add(new AddChildEdit<Profile>(destinationProfile, configuration)); + } + seen.add(activity); + } + } + // add processor configurations to the profile + List<Configuration> configurations = scufl2Tools.configurationsFor(processor, profile); + for (Configuration configuration : configurations) { + editList.add(new AddChildEdit<Profile>(destinationProfile, configuration)); + } + + for (Workflow workflow : scufl2Tools.nestedWorkflowsForProcessor(processor, profile)) { + if (!seen.contains(workflow)) { + // recursively add nested workflows + editList.add(makeInsertWorkflowEdit(workflow, profile, seen)); + } + } + } + return new CompoundEdit(editList); + } + +// protected Activity getInsertedActivity() { +// return insertedActivity; +// } + + protected class ImportWorkflowAction extends AbstractAction implements Runnable { + private static final String VALID_NAME_REGEX = "[\\p{L}\\p{Digit}_.]+"; + private Component parentComponent; + private ProgressMonitor progressMonitor; + + protected ImportWorkflowAction() { + super("Import workflow"); + } + + public void actionPerformed(ActionEvent e) { + /* + * if (e.getSource() instanceof Component) { parentComponent = (Component) + * e.getSource(); } else { parentComponent = null; } + */ + parentComponent = MainWindow.getMainWindow(); + Thread t = new Thread(this, "Import workflow"); + progressMonitor = new ProgressMonitor(parentComponent, "Importing workflow", "", 0, 100); + progressMonitor.setMillisToDecideToPopup(200); + progressMonitor.setProgress(5); + t.start(); + setVisible(false); + } + + protected void nested() { + if (progressMonitor.isCanceled()) { + return; + } + progressMonitor.setProgress(15); + selectionManager.setSelectedWorkflowBundle(destinationWorkflowBundle); + if (progressMonitor.isCanceled()) { + return; + } + + progressMonitor.setNote("Copying source workflow"); + Workflow nestedFlow; + try { + nestedFlow = DataflowMerger.copyWorkflow(sourceWorkflow); + } catch (Exception ex) { + logger.warn("Could not copy nested workflow", ex); + progressMonitor.setProgress(100); + JOptionPane.showMessageDialog(parentComponent, + "An error occured while copying workflow:\n" + ex.getLocalizedMessage(), + "Could not copy nested workflow", JOptionPane.WARNING_MESSAGE); + return; + } + if (progressMonitor.isCanceled()) { + return; + } + + progressMonitor.setNote("Creating nested workflow"); + progressMonitor.setProgress(45); + + Edit<?> edit = makeInsertNestedWorkflowEdit(nestedFlow); + if (progressMonitor.isCanceled()) { + return; + } + + progressMonitor.setNote("Inserting nested workflow"); + progressMonitor.setProgress(65); + + try { + editManager.doDataflowEdit(destinationWorkflowBundle, edit); + } catch (EditException e) { + progressMonitor.setProgress(100); + logger.warn("Could not import nested workflow", e); + JOptionPane.showMessageDialog(parentComponent, + "An error occured while importing workflow:\n" + e.getLocalizedMessage(), + "Could not import workflows", JOptionPane.WARNING_MESSAGE); + return; + } + + if (radioNew.isSelected()) { + progressMonitor.setNote("Opening new nested workflow for editing"); + progressMonitor.setProgress(90); + selectionManager.setSelectedWorkflow(nestedFlow); + } + progressMonitor.setProgress(100); + } + + protected void merge() { + progressMonitor.setProgress(10); + DataflowMerger merger = new DataflowMerger(destinationWorkflow); + progressMonitor.setProgress(25); + progressMonitor.setNote("Planning workflow merging"); + + String prefix = prefixField.getText(); + if (!prefix.equals("")) { + if (!prefix.matches("[_.]$")) { + prefix = prefix + "_"; + } + if (!prefix.matches(VALID_NAME_REGEX)) { + progressMonitor.setProgress(100); + final String wrongPrefix = prefix; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JOptionPane.showMessageDialog(parentComponent, "The merge prefix '" + + wrongPrefix + "' is not valid. Try " + + "using only letters, numbers, " + "underscore and dot.", + "Invalid merge prefix", JOptionPane.ERROR_MESSAGE); + prefixField.requestFocus(); + ImportWorkflowWizard.this.setVisible(true); + } + }); + return; + } + } + + CompoundEdit mergeEdit; + try { + mergeEdit = merger.getMergeEdit(ImportWorkflowWizard.this.sourceWorkflow, prefix); + } catch (MergeException e1) { + progressMonitor.setProgress(100); + logger.warn("Could not merge workflow", e1); + JOptionPane.showMessageDialog(parentComponent, + "An error occured while merging workflows:\n" + e1.getLocalizedMessage(), + "Could not merge workflows", JOptionPane.WARNING_MESSAGE); + return; + } + + progressMonitor.setProgress(55); + selectionManager.setSelectedWorkflowBundle(destinationWorkflowBundle); + + progressMonitor.setNote("Merging workflows"); + progressMonitor.setProgress(75); + + if (progressMonitor.isCanceled()) { + return; + } + + try { + editManager.doDataflowEdit(destinationWorkflowBundle, mergeEdit); + } catch (EditException e1) { + progressMonitor.setProgress(100); + JOptionPane.showMessageDialog(parentComponent, + "An error occured while merging workflows:\n" + e1.getLocalizedMessage(), + "Could not merge workflows", JOptionPane.WARNING_MESSAGE); + return; + } + progressMonitor.setProgress(100); + + } + + public void run() { + boolean completed = findChosenDataflow(parentComponent, false); + if (!completed) { + return; + } + if (actionMerge.isSelected()) { + merge(); + } else if (actionNested.isSelected()) { + nested(); + } + } + } + + protected class UpdatePreviewsThread extends Thread { + protected UpdatePreviewsThread() { + super("Updating destination previews"); + } + + public void run() { + if (Thread.interrupted()) { + return; + } + updateSourcePreview(); + + if (Thread.interrupted()) { + return; + } + updateDestinationPreview(); + } + } + + protected class BrowseFileOnClick implements ActionListener { + public void actionPerformed(ActionEvent e) { + checkEmptyFile(); + } + + public void checkEmptyFile() { + if (radioFile.isSelected() && fieldFile.getText().equals("")) { + // On first label click pop up Browse dialogue. + buttonBrowse.doClick(); + } + } + } + + protected class DataflowOpenerThread extends Thread { + private final boolean background; + private final Component parentComponent; + private boolean shouldStop = false; + private boolean shownWarning = false; + + protected DataflowOpenerThread(Component parentComponent, boolean background) { + super("Inspecting selected workflow"); + this.parentComponent = parentComponent; + this.background = background; + } + + @Override + public void interrupt() { + this.shouldStop = true; + super.interrupt(); + } + + public void run() { + updateSource(); +// updateDestination(); + } + +// public void updateDestination() { +// ButtonModel selection = destinationSelection.getSelection(); +// Workflow chosenDataflow = null; +// if (selection == null) { +// chosenDataflow = null; +// } else if (selection.equals(radioNewDestination.getModel())) { +// chosenDataflow = new Workflow(); +// } else if (selection.equals(radioOpenDestination.getModel())) { +// DataflowSelection chosen = (DataflowSelection) destinationAlreadyOpen +// .getSelectedItem(); +// chosenDataflow = chosen.getDataflow(); +// } else if (selection.equals(radioCustomDestination.getModel())) { +// chosenDataflow = customDestinationDataflow; +// } else { +// logger.error("Unknown selection " + selection); +// } +// +// if (chosenDataflow == null) { +// if (!background && !shownWarning) { +// shownWarning = true; +// SwingUtilities.invokeLater(new Runnable() { +// public void run() { +// JOptionPane.showMessageDialog(parentComponent, +// "You need to choose a destination workflow", +// "No destination workflow chosen", JOptionPane.ERROR_MESSAGE); +// setVisible(true); +// } +// }); +// return; +// } +// } +// if (checkInterrupted()) { +// return; +// } +// if (chosenDataflow != ImportWorkflowWizard.this.destinationDataflow) { +// updateWorkflowGraphic(previewDestination, chosenDataflow); +// if (checkInterrupted()) { +// return; +// } +// ImportWorkflowWizard.this.destinationDataflow = chosenDataflow; +// } +// +// } + + public void updateSource() { + ButtonModel selection = sourceSelection.getSelection(); + Workflow chosenDataflow = null; + if (selection == null) { + chosenDataflow = null; + } else if (selection.equals(radioNew.getModel())) { + WorkflowBundle workflowBundle = new WorkflowBundle(); + workflowBundle.setMainWorkflow(new Workflow()); + workflowBundle.getMainWorkflow().setName(fileManager.getDefaultWorkflowName()); + workflowBundle.setMainProfile(new Profile()); + scufl2Tools.setParents(workflowBundle); + chosenDataflow = workflowBundle.getMainWorkflow(); + } else if (selection.equals(radioFile.getModel())) { + final String filePath = fieldFile.getText(); + try { + DataflowInfo opened = fileManager + .openDataflowSilently(null, new File(filePath)); + if (checkInterrupted()) { + return; + } + chosenDataflow = opened.getDataflow().getMainWorkflow(); + } catch (final OpenException e1) { + if (!background && !shownWarning) { + shownWarning = true; + logger.warn("Could not open workflow for merging: " + filePath, e1); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + radioFile.requestFocus(); + JOptionPane.showMessageDialog(parentComponent, + "An error occured while trying to open " + filePath + "\n" + + e1.getMessage(), "Could not open workflow", + JOptionPane.WARNING_MESSAGE); + setVisible(true); + } + }); + } + } + } else if (selection.equals(radioUrl.getModel())) { + final String url = fieldUrl.getText(); + try { + DataflowInfo opened = fileManager.openDataflowSilently(null, new URL(url)); + if (checkInterrupted()) { + return; + } + chosenDataflow = opened.getDataflow().getMainWorkflow(); + } catch (final OpenException e1) { + if (!background && !shownWarning) { + logger.warn("Could not open source workflow: " + url, e1); + shownWarning = true; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + fieldUrl.requestFocus(); + JOptionPane.showMessageDialog( + parentComponent, + "An error occured while trying to open " + url + "\n" + + e1.getMessage(), "Could not open workflow", + JOptionPane.WARNING_MESSAGE); + setVisible(true); + } + }); + + } + if (checkInterrupted()) { + return; + } + } catch (final MalformedURLException e1) { + if (!background && !shownWarning) { + logger.warn("Invalid workflow URL: " + url, e1); + shownWarning = true; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + fieldUrl.requestFocus(); + JOptionPane.showMessageDialog( + parentComponent, + "The workflow location " + url + " is invalid\n" + + e1.getLocalizedMessage(), "Invalid URL", + JOptionPane.ERROR_MESSAGE); + setVisible(true); + } + }); + } + if (checkInterrupted()) { + return; + } + } + } else if (selection.equals(radioOpened.getModel())) { + DataflowSelection chosen = (DataflowSelection) chooseDataflow.getSelectedItem(); + chosenDataflow = chosen.getDataflow().getMainWorkflow(); + } else if (selection.equals(radioCustomSource.getModel())) { + chosenDataflow = customSourceDataFlow.getMainWorkflow(); + } else { + logger.error("Unknown selection " + selection); + } + if (checkInterrupted()) { + return; + } + if (chosenDataflow != ImportWorkflowWizard.this.sourceWorkflow) { + Profile chosenProfile = null; + if (chosenDataflow != null) { + chosenProfile = chosenDataflow.getParent().getMainProfile(); + } + updateWorkflowGraphic(previewSource, chosenDataflow, chosenProfile); + if (checkInterrupted()) { + return; + } + ImportWorkflowWizard.this.sourceWorkflow = chosenDataflow; + } + if (chosenDataflow == null) { + if (!background && !shownWarning) { + shownWarning = true; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JOptionPane.showMessageDialog(parentComponent, + "You need to choose a workflow for merging", + "No workflow chosen", JOptionPane.ERROR_MESSAGE); + setVisible(true); + } + }); + } + } + } + + private boolean checkInterrupted() { + if (Thread.interrupted() || this.shouldStop) { + // ImportWorkflowWizard.this.chosenDataflow = null; + return true; + } + return false; + } + } + + public static class DataflowSelection { + private final WorkflowBundle dataflow; + private final String name; + + public DataflowSelection(WorkflowBundle dataflow, String name) { + this.dataflow = dataflow; + this.name = name; + } + + public WorkflowBundle getDataflow() { + return dataflow; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + } + + protected class UpdateChosenListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + Component parentComponent; + if (e.getSource() instanceof Component) { + parentComponent = (Component) e.getSource(); + } else { + parentComponent = null; + } + findChosenDataflow(parentComponent, true); + + } + } + + public void setCustomSourceDataflow(WorkflowBundle sourceDataflow, String label) { + this.customSourceDataFlow = sourceDataflow; + this.customSourceName = label; + updateSourceSection(); + radioCustomSource.doClick(); + } + +// public void setCustomDestinationDataflow(Workflow destinationDataflow, String label) { +// this.customDestinationDataflow = destinationDataflow; +// this.customDestinationName = label; +// updateDestinationSection(); +// radioCustomDestination.doClick(); +// } + +// public void setDestinationEnabled(boolean destinationEnabled) { +// this.destinationEnabled = destinationEnabled; +// updateDestinationSection(); +// } + + public void setSourceEnabled(boolean sourceEnabled) { + this.sourceEnabled = sourceEnabled; + updateSourceSection(); + } +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/AddNestedWorkflowMenuAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/AddNestedWorkflowMenuAction.java new file mode 100644 index 0000000..a37e308 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/AddNestedWorkflowMenuAction.java
@@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.importworkflow.menu; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.net.URI; + +import javax.swing.Action; +import javax.swing.KeyStroke; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.actions.AddNestedWorkflowAction; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu; + +/** + * An action to add a nested workflow activity + a wrapping processor to the + * workflow. + * + * @author Alex Nenadic + * @author Stian Soiland-Reyes + * + */ +public class AddNestedWorkflowMenuAction extends AbstractMenuAction { + + private static final String ADD_NESTED_WORKFLOW = "Nested workflow"; + + private static final URI ADD_NESTED_WORKFLOW_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuAddNestedWorkflow"); + + private EditManager editManager; + private FileManager fileManager; + private MenuManager menuManager; + private ColourManager colourManager; + private WorkbenchConfiguration workbenchConfiguration; + private SelectionManager selectionManager; + + public AddNestedWorkflowMenuAction() { + super(InsertMenu.INSERT, 400, ADD_NESTED_WORKFLOW_URI); + } + + @Override + protected Action createAction() { + AddNestedWorkflowAction a = new AddNestedWorkflowAction(editManager, fileManager, + menuManager, colourManager, workbenchConfiguration, selectionManager); + // Override name to avoid "Add " + a.putValue(Action.NAME, ADD_NESTED_WORKFLOW); + a.putValue(Action.SHORT_DESCRIPTION, ADD_NESTED_WORKFLOW); + a.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke( + KeyEvent.VK_N, InputEvent.SHIFT_DOWN_MASK + | InputEvent.ALT_DOWN_MASK)); + return a; + + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/ImportWorkflowMenuAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/ImportWorkflowMenuAction.java new file mode 100644 index 0000000..1c8b40b --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/ImportWorkflowMenuAction.java
@@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.importworkflow.menu; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.actions.ImportWorkflowAction; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * An action to import nested/merged workflows. + * + * @author Alex Nenadic + * @author Stian Soiland-Reyes + * + */ +public class ImportWorkflowMenuAction extends AbstractContextualMenuAction { + + private static final URI insertSection = URI + .create("http://taverna.sf.net/2009/contextMenu/insert"); + + private EditManager editManager; + private FileManager fileManager; + private MenuManager menuManager; + private ColourManager colourManager; + private WorkbenchConfiguration workbenchConfiguration; + private SelectionManager selectionManager; + + public ImportWorkflowMenuAction() { + super(insertSection, 400); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() && getContextualSelection().getSelection() instanceof Workflow; + } + + @Override + protected Action createAction() { + ImportWorkflowAction myAction = new ImportWorkflowAction(editManager, fileManager, + menuManager, colourManager, workbenchConfiguration, selectionManager); + // Just "Workflow" as we go under the "Insert" menu + myAction.putValue(Action.NAME, "Nested workflow"); + return myAction; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/MergeWorkflowMenuAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/MergeWorkflowMenuAction.java new file mode 100644 index 0000000..7ce4891 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/MergeWorkflowMenuAction.java
@@ -0,0 +1,65 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.menu; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.actions.MergeWorkflowAction; +import net.sf.taverna.t2.workbench.selection.SelectionManager; + +public class MergeWorkflowMenuAction extends AbstractMenuAction { + + public static final URI INSERT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#insert"); + + public static final URI IMPORT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#insert"); + + private EditManager editManager; + private FileManager fileManager; + private MenuManager menuManager; + private ColourManager colourManager; + private WorkbenchConfiguration workbenchConfiguration; + private SelectionManager selectionManager; + + public MergeWorkflowMenuAction() { + super(INSERT_URI, 2000, IMPORT_URI); + } + + @Override + protected Action createAction() { + return new MergeWorkflowAction(editManager, fileManager, menuManager, colourManager, + workbenchConfiguration, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/ReplaceNestedWorkflowMenuAction.java b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/ReplaceNestedWorkflowMenuAction.java new file mode 100644 index 0000000..3d424df --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/java/net/sf/taverna/t2/workbench/file/importworkflow/menu/ReplaceNestedWorkflowMenuAction.java
@@ -0,0 +1,76 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.menu; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.activitytools.AbstractConfigureActivityMenuAction; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.importworkflow.actions.ReplaceNestedWorkflowAction; +import net.sf.taverna.t2.workbench.selection.SelectionManager; + +public class ReplaceNestedWorkflowMenuAction extends AbstractConfigureActivityMenuAction { + + private static final URI NESTED_ACTIVITY = URI.create("http://ns.taverna.org.uk/2010/activity/nested-workflow"); + + private EditManager editManager; + private FileManager fileManager; + private MenuManager menuManager; + private ActivityIconManager activityIconManager; + private ColourManager colourManager; + private WorkbenchConfiguration workbenchConfiguration; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + private SelectionManager selectionManager; + + public ReplaceNestedWorkflowMenuAction() { + super(NESTED_ACTIVITY); + } + + @Override + protected Action createAction() { + ReplaceNestedWorkflowAction configAction = new ReplaceNestedWorkflowAction(findActivity(), + editManager, fileManager, menuManager, activityIconManager, colourManager, + serviceDescriptionRegistry, workbenchConfiguration, selectionManager); + addMenuDots(configAction); + return configAction; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setServiceDescriptionRegistry(ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider new file mode 100644 index 0000000..bf42bef --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowTemplateService
diff --git a/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..6e7eec5 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,9 @@ +# Needs to be first AbstractConfigureActivityMenuAction to be +# picked up as the automatic 'configure' action for template services +net.sf.taverna.t2.workbench.file.importworkflow.menu.ReplaceNestedWorkflowMenuAction + +net.sf.taverna.t2.workbench.file.importworkflow.menu.AddNestedWorkflowMenuAction +net.sf.taverna.t2.workbench.file.importworkflow.menu.ImportWorkflowMenuAction +net.sf.taverna.t2.workbench.file.importworkflow.menu.MergeWorkflowMenuAction + +net.sf.taverna.t2.activities.dataflow.menu.EditNestedDataflowMenuAction
diff --git a/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI new file mode 100644 index 0000000..5cb0543 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowActivityIcon \ No newline at end of file
diff --git a/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler new file mode 100644 index 0000000..a334e66 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.dataflow.filemanager.NestedDataflowPersistenceHandler \ No newline at end of file
diff --git a/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..39d7ec2 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.dataflow.views.DataflowActivityViewFactory \ No newline at end of file
diff --git a/taverna-dataflow-activity-ui/src/main/resources/META-INF/spring/dataflow-activity-ui-context-osgi.xml b/taverna-dataflow-activity-ui/src/main/resources/META-INF/spring/dataflow-activity-ui-context-osgi.xml new file mode 100644 index 0000000..e664429 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/META-INF/spring/dataflow-activity-ui-context-osgi.xml
@@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="DataflowActivityIcon" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI" /> + + <service ref="DataflowTemplateService" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider" /> + + <service ref="ReplaceNestedWorkflowMenuAction" auto-export="interfaces" /> + <service ref="AddNestedWorkflowMenuAction" auto-export="interfaces" /> + <service ref="ImportWorkflowMenuAction" auto-export="interfaces" /> + <service ref="MergeWorkflowMenuAction" auto-export="interfaces" /> + <service ref="EditNestedDataflowMenuAction" auto-export="interfaces" /> + + <!-- <service ref="NestedDataflowPersistenceHandler" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" /> --> + + <service ref="DataflowActivityViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + <reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" /> + <reference id="edits" interface="net.sf.taverna.t2.workflowmodel.Edits" /> + <reference id="activityIconManager" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconManager" /> + <reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + <reference id="serviceDescriptionRegistry" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry" /> + <reference id="workbenchConfiguration" interface="net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + +</beans:beans>
diff --git a/taverna-dataflow-activity-ui/src/main/resources/META-INF/spring/dataflow-activity-ui-context.xml b/taverna-dataflow-activity-ui/src/main/resources/META-INF/spring/dataflow-activity-ui-context.xml new file mode 100644 index 0000000..f72abd2 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/META-INF/spring/dataflow-activity-ui-context.xml
@@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="DataflowActivityIcon" class="net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowActivityIcon" /> + + <bean id="DataflowTemplateService" class="net.sf.taverna.t2.activities.dataflow.servicedescriptions.DataflowTemplateService" /> + + <bean id="ReplaceNestedWorkflowMenuAction" class="net.sf.taverna.t2.workbench.file.importworkflow.menu.ReplaceNestedWorkflowMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="AddNestedWorkflowMenuAction" class="net.sf.taverna.t2.workbench.file.importworkflow.menu.AddNestedWorkflowMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="colourManager" ref="colourManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="ImportWorkflowMenuAction" class="net.sf.taverna.t2.workbench.file.importworkflow.menu.ImportWorkflowMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="colourManager" ref="colourManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="MergeWorkflowMenuAction" class="net.sf.taverna.t2.workbench.file.importworkflow.menu.MergeWorkflowMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="colourManager" ref="colourManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="EditNestedDataflowMenuAction" class="net.sf.taverna.t2.activities.dataflow.menu.EditNestedDataflowMenuAction"> + <property name="selectionManager" ref="selectionManager" /> + </bean> + + <!-- <bean id="NestedDataflowPersistenceHandler" class="net.sf.taverna.t2.activities.dataflow.filemanager.NestedDataflowPersistenceHandler"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + </bean> --> + + <bean id="DataflowActivityViewFactory" class="net.sf.taverna.t2.activities.dataflow.views.DataflowActivityViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + +</beans>
diff --git a/taverna-dataflow-activity-ui/src/main/resources/dataflow.png b/taverna-dataflow-activity-ui/src/main/resources/dataflow.png new file mode 100644 index 0000000..71b188c --- /dev/null +++ b/taverna-dataflow-activity-ui/src/main/resources/dataflow.png Binary files differ
diff --git a/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/AbstractTestHelper.java b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/AbstractTestHelper.java new file mode 100644 index 0000000..7a4d2f6 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/AbstractTestHelper.java
@@ -0,0 +1,266 @@ +package net.sf.taverna.t2.workbench.file.importworkflow; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Before; + +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.BlockingControlLink; +import uk.org.taverna.scufl2.api.core.ControlLink; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.io.WorkflowBundleIO; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import uk.org.taverna.scufl2.api.port.ProcessorPort; +import uk.org.taverna.scufl2.api.port.ReceiverPort; +import uk.org.taverna.scufl2.api.port.SenderPort; + +public abstract class AbstractTestHelper { + + private static final String Q_T2FLOW = "/q.t2flow"; + + private static final String ABC_T2FLOW = "/abc.t2flow"; + + private static final String P_T2FLOW = "/p.t2flow"; + + private WorkflowBundleIO workflowBundleIO = new WorkflowBundleIO(); + + protected Scufl2Tools scufl2Tools = new Scufl2Tools(); + + protected Workflow abc; + + protected Workflow p; + + protected Workflow q; + + protected void assertHasConditionals(Workflow dataflow, + String... expectedConditionalDef) { + Set<String> expectedConditionals = new HashSet<String>(); + for (String expected : expectedConditionalDef) { + expectedConditionals.add(expected); + } + + Set<String> foundConditionals = new HashSet<String>(); + + for (ControlLink c : dataflow.getControlLinks()) { + if (c instanceof BlockingControlLink) { + BlockingControlLink bcl = (BlockingControlLink) c; + foundConditionals.add(bcl.getUntilFinished().getName() + ";" + + bcl.getBlock().getName()); + } + } + + Set<String> extras = new HashSet<String>(foundConditionals); + extras.removeAll(expectedConditionals); + assertTrue("Unexpected conditional " + extras, extras.isEmpty()); + + Set<String> missing = new HashSet<String>(expectedConditionals); + missing.removeAll(foundConditionals); + assertTrue("Could not find conditional " + missing, missing.isEmpty()); + } + + protected void assertHasDatalinks(Workflow dataflow, + String... expectedLinkDef) { + Set<String> expectedLinks = new HashSet<String>(); + for (String expected : expectedLinkDef) { + expectedLinks.add(expected); + } + + Set<String> foundLinks = new HashSet<String>(); + + for (DataLink link : dataflow.getDataLinks()) { + StringBuilder linkRef = new StringBuilder(); + SenderPort source = link.getReceivesFrom(); + if (source instanceof ProcessorPort) { + linkRef.append(((ProcessorPort) source).getParent() + .getName()); + linkRef.append('.'); + } + linkRef.append(source.getName()); + + linkRef.append("->"); + + ReceiverPort sink = link.getSendsTo(); + if (sink instanceof ProcessorPort) { + linkRef.append(((ProcessorPort) sink).getParent() + .getName()); + linkRef.append('.'); + } + linkRef.append(sink.getName()); + + String linkStr = linkRef.toString(); + foundLinks.add(linkStr); + } + + Set<String> extras = new HashSet<String>(foundLinks); + extras.removeAll(expectedLinks); + assertTrue("Unexpected links " + extras, extras.isEmpty()); + + Set<String> missing = new HashSet<String>(expectedLinks); + missing.removeAll(foundLinks); + assertTrue("Could not find links " + missing, missing.isEmpty()); + } + + protected void assertHasInputPorts(Workflow dataflow, + String... expectedInputPorts) { + Set<String> expectedNames = new HashSet<String>(); + for (String expected : expectedInputPorts) { + expectedNames.add(expected); + } + Set<String> foundNames = new HashSet<String>(); + for (InputWorkflowPort port : dataflow.getInputPorts()) { + String name = port.getName(); + foundNames.add(name); + } + + Set<String> extras = new HashSet<String>(foundNames); + extras.removeAll(expectedNames); + assertTrue("Unexpected input port " + extras, extras.isEmpty()); + + Set<String> missing = new HashSet<String>(expectedNames); + missing.removeAll(foundNames); + assertTrue("Could not find input port " + missing, missing.isEmpty()); + + } + + protected void assertHasOutputPorts(Workflow dataflow, + String... expectedOutputPorts) { + Set<String> expectedNames = new HashSet<String>(); + for (String expected : expectedOutputPorts) { + expectedNames.add(expected); + } + Set<String> foundNames = new HashSet<String>(); + for (OutputWorkflowPort port : dataflow.getOutputPorts()) { + String name = port.getName(); + foundNames.add(name); + } + + Set<String> extras = new HashSet<String>(foundNames); + extras.removeAll(expectedNames); + assertTrue("Unexpected output port " + extras, extras.isEmpty()); + + Set<String> missing = new HashSet<String>(expectedNames); + missing.removeAll(foundNames); + assertTrue("Could not find output port " + missing, missing.isEmpty()); + } + + protected void assertHasProcessors(Workflow dataflow, + String... expectedProcessors) { + Set<String> expectedNames = new HashSet<String>(); + for (String expected : expectedProcessors) { + expectedNames.add(expected); + } + Set<String> foundNames = new HashSet<String>(); + + for (Processor proc : dataflow.getProcessors()) { + String processorName = proc.getName(); + foundNames.add(processorName); + } + + Set<String> extras = new HashSet<String>(foundNames); + extras.removeAll(expectedNames); + assertTrue("Unexpected processor " + extras, extras.isEmpty()); + + Set<String> missing = new HashSet<String>(expectedNames); + missing.removeAll(foundNames); + assertTrue("Could not find processor " + missing, missing.isEmpty()); + } + + protected void checkAbc() throws Exception { + assertHasProcessors(abc, "A", "B", "C"); + assertHasInputPorts(abc, "in1", "in2"); + assertHasOutputPorts(abc, "a", "b", "c"); + assertHasDatalinks(abc, "in2->B.inputlist", "in1->A.string1", + "in2->A.string2", "Merge0:Merge0_output->C.inputlist", + "A.output->a", "B.outputlist->b", + "B.outputlist->Merge0:outputlistToMerge0_input0", + "A.output->Merge0:outputToMerge0_input0", "C.outputlist->c"); + assertHasConditionals(abc, "A;B"); + } + + protected void checkP() throws Exception { + assertHasProcessors(p, "P"); + assertHasInputPorts(p, "i"); + assertHasOutputPorts(p, "o"); + assertHasDatalinks(p, "i->P.inputlist", "P.outputlist->o"); + assertHasConditionals(p); + + } + + protected void checkQ() throws Exception { + assertHasProcessors(q, "Q"); + assertHasInputPorts(q, "p"); + assertHasOutputPorts(q, "p", "q"); + assertHasDatalinks(q, "p->Q.inputlist", "Q.outputlist->q", "p->p"); + assertHasConditionals(q); + + List<DataLink> datalinksTo = scufl2Tools.datalinksTo(findOutputPort(q, "p")); + assertEquals(1, datalinksTo.size()); + SenderPort source = datalinksTo.get(0).getReceivesFrom(); + assertEquals("out port P not linked to input P", source, findInputPort(q, "p")); + + } + + protected Workflow loadAbc() throws Exception { + return openWorkflow(getClass().getResourceAsStream(ABC_T2FLOW)); + } + + protected Workflow loadP() throws Exception { + return openWorkflow(getClass().getResourceAsStream(P_T2FLOW)); + } + + protected Workflow loadQ() throws Exception { + return openWorkflow(getClass().getResourceAsStream(Q_T2FLOW)); + } + + @Before + public void loadWorkflows() throws Exception { + abc = loadAbc(); + p = loadP(); + q = loadQ(); + } + + protected Workflow openWorkflow(InputStream workflowXMLstream) throws Exception { + assertNotNull(workflowXMLstream); + WorkflowBundle workflowBundle = workflowBundleIO.readBundle(workflowXMLstream, "application/vnd.taverna.t2flow+xml"); + return workflowBundle.getMainWorkflow(); + } + + protected InputWorkflowPort findInputPort(Workflow wf, String name) { + for (InputWorkflowPort inp : wf.getInputPorts()) { + if (inp.getName().equals(name)) { + return inp; + } + } + throw new IllegalArgumentException("Unknown input port: " + name); + } + + protected OutputWorkflowPort findOutputPort(Workflow wf, String name) { + for (OutputWorkflowPort outp : wf.getOutputPorts()) { + if (outp.getName().equals(name)) { + return outp; + } + } + throw new IllegalArgumentException("Unknown output port: " + name); + } + + protected Processor findProcessor(Workflow wf, String name) { + for (Processor proc : wf.getProcessors()) { + if (proc.getName().equals(name)) { + return proc; + } + } + throw new IllegalArgumentException("Unknown processor: " + name); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestPortMerge.java b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestPortMerge.java new file mode 100644 index 0000000..9141693 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestPortMerge.java
@@ -0,0 +1,38 @@ +package net.sf.taverna.t2.workbench.file.importworkflow; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.SenderPort; + +@Ignore +public class TestPortMerge extends AbstractTestHelper { + + @Test + public void mergeQintoP() throws Exception { + DataflowMerger merger = new DataflowMerger(p); + merger.getMergeEdit(q).doEdit(); + Workflow merged = p; + checkQ(); + + assertHasProcessors(merged, "P", "Q"); + assertHasInputPorts(merged, "i", "p"); + assertHasOutputPorts(merged, "o", "p", "q"); + assertHasDatalinks(merged, "i->P.inputlist", "P.outputlist->o", "p->Q.inputlist", + "Q.outputlist->q", "p->p"); + + List<DataLink> datalinksTo = scufl2Tools.datalinksTo(findOutputPort(merged, "p")); + assertEquals(1, datalinksTo.size()); + SenderPort source = datalinksTo.get(0).getReceivesFrom(); + assertSame("out port P not linked to input P", source, findInputPort(merged, "p")); + + } + +}
diff --git a/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestRename.java b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestRename.java new file mode 100644 index 0000000..c235c98 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestRename.java
@@ -0,0 +1,58 @@ +package net.sf.taverna.t2.workbench.file.importworkflow; + +import org.junit.Ignore; +import org.junit.Test; + +import uk.org.taverna.scufl2.api.core.Workflow; + +@Ignore +public class TestRename extends AbstractTestHelper { + + @Test + public void mergePintoP() throws Exception { + DataflowMerger merger = new DataflowMerger(p); + merger.getMergeEdit(p).doEdit(); + Workflow merged = p; + + assertHasProcessors(merged, "P", "P_2"); + assertHasInputPorts(merged, "i", "i_2"); + assertHasOutputPorts(merged, "o", "o_2"); + assertHasDatalinks(merged, "i->P.inputlist", "P.outputlist->o", + "i_2->P_2.inputlist", "P_2.outputlist->o_2"); + } + + @Test + public void mergePintoPintoP() throws Exception { + // Don't put p in constructor, or we would get exponential merging! + Workflow merged = new Workflow(); + DataflowMerger merger = new DataflowMerger(merged); + merger.getMergeEdit(p).doEdit(); + merger.getMergeEdit(p).doEdit(); + merger.getMergeEdit(p).doEdit(); + + assertHasProcessors(merged, "P", "P_2", "P_3"); + assertHasInputPorts(merged, "i", "i_2", "i_3"); + assertHasOutputPorts(merged, "o", "o_2", "o_3"); + assertHasDatalinks(merged, "i->P.inputlist", "P.outputlist->o", + "i_2->P_2.inputlist", "P_2.outputlist->o_2", + "i_3->P_3.inputlist", "P_3.outputlist->o_3"); + } + + @Test + public void mergePintoPWithPrefix() throws Exception { + // Don't put p in constructor, or we would get exponential merging! + Workflow merged = new Workflow(); + DataflowMerger merger = new DataflowMerger(merged); + merger.getMergeEdit(p).doEdit(); + merger.getMergeEdit(p, "fish_").doEdit(); + merger.getMergeEdit(p, "soup_").doEdit(); + + assertHasProcessors(merged, "P", "fish_P", "soup_P"); + assertHasInputPorts(merged, "i", "fish_i", "soup_i"); + assertHasOutputPorts(merged, "o", "fish_o", "soup_o"); + assertHasDatalinks(merged, "i->P.inputlist", "P.outputlist->o", + "fish_i->fish_P.inputlist", "fish_P.outputlist->fish_o", + "soup_i->soup_P.inputlist", "soup_P.outputlist->soup_o"); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestSimpleMerge.java b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestSimpleMerge.java new file mode 100644 index 0000000..811678e --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestSimpleMerge.java
@@ -0,0 +1,98 @@ +package net.sf.taverna.t2.workbench.file.importworkflow; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.SenderPort; + +@Ignore +public class TestSimpleMerge extends AbstractTestHelper { + + private void checkMergedAbcP(Workflow merged) { + // Check that it has everything from both + assertHasProcessors(merged, "A", "B", "C", "P"); + assertHasInputPorts(merged, "in1", "in2", "i"); + assertHasOutputPorts(merged, "a", "b", "c", "o"); + assertHasDatalinks(merged, "in2->B.inputlist", "in1->A.string1", + "in2->A.string2", "Merge0:Merge0_output->C.inputlist", + "A.output->a", "B.outputlist->b", + "B.outputlist->Merge0:outputlistToMerge0_input0", + "A.output->Merge0:outputToMerge0_input0", "C.outputlist->c", + "i->P.inputlist", "P.outputlist->o"); + assertHasConditionals(merged, "A;B"); + } + + private void checkCopiedFromP(Workflow merged) { + Processor newProcP = findProcessor(merged, "P"); + Processor originalProcP = findProcessor(p, "P"); + assertNotSame("Did not copy processor P", newProcP, originalProcP); + + InputProcessorPort inp = newProcP.getInputPorts().first(); + InputWorkflowPort newInI = findInputPort(merged, "i"); + assertEquals(0, newInI.getDepth().intValue()); + + InputWorkflowPort originalInI = findInputPort(p, "i"); + assertNotSame("Did not copy port 'i'", originalInI, newInI); + + List<DataLink> datalinksTo = scufl2Tools.datalinksTo(inp); + assertEquals(1, datalinksTo.size()); + SenderPort source = datalinksTo.get(0).getReceivesFrom(); + + assertSame("Not linked to new port", source, newInI); + assertNotSame("Still linked to old port", source, originalInI); + } + + + @Test + public void mergeAbcAndPIntoNew() throws Exception { + Workflow merged = new Workflow(); + DataflowMerger merger = new DataflowMerger(merged); + merger.getMergeEdit(abc).doEdit(); + + assertNotSame(abc, merged); + merger.getMergeEdit(p).doEdit(); + + + // Assert abc and p were not modified + checkAbc(); + checkP(); + + checkMergedAbcP(merged); + checkCopiedFromP(merged); + } + + @Test + public void mergePintoAbc() throws Exception { + DataflowMerger merger = new DataflowMerger(abc); + Workflow merged = abc; + + merger.getMergeEdit(p).doEdit(); + checkMergedAbcP(merged); + checkCopiedFromP(merged); + // Assert P did not change + checkP(); + } + + @Test + public void mergeAbcintoP() throws Exception { + Workflow merged = p; + DataflowMerger merger = new DataflowMerger(merged); + merger.getMergeEdit(abc).doEdit(); + + checkMergedAbcP(merged); + // Assert ABC did not change + checkAbc(); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestTestHelper.java b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestTestHelper.java new file mode 100644 index 0000000..2165a67 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/TestTestHelper.java
@@ -0,0 +1,24 @@ +package net.sf.taverna.t2.workbench.file.importworkflow; + +import org.junit.Ignore; +import org.junit.Test; + +@Ignore +public class TestTestHelper extends AbstractTestHelper { + + @Test + public void checkAbc() throws Exception { + super.checkAbc(); + } + + @Test + public void checkP() throws Exception { + super.checkP(); + } + + @Test + public void checkQ() throws Exception { + super.checkQ(); + } + +}
diff --git a/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/gui/ImportWizardLauncher.java b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/gui/ImportWizardLauncher.java new file mode 100644 index 0000000..b45a774 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/java/net/sf/taverna/t2/workbench/file/importworkflow/gui/ImportWizardLauncher.java
@@ -0,0 +1,24 @@ +package net.sf.taverna.t2.workbench.file.importworkflow.gui; + +import javax.swing.UIManager; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.FileManagerImpl; + + +public class ImportWizardLauncher { + + public static void main(String[] args) throws Exception { + + UIManager.setLookAndFeel(UIManager + .getSystemLookAndFeelClassName()); + + EditManager editManager = new EditManagerImpl(); + FileManager fileManager = new FileManagerImpl(editManager); + + ImportWorkflowWizard s = new ImportWorkflowWizard(null, editManager, fileManager, null, null, null, null); + s.setVisible(true); + } +}
diff --git a/taverna-dataflow-activity-ui/src/test/resources/abc.t2flow b/taverna-dataflow-activity-ui/src/test/resources/abc.t2flow new file mode 100644 index 0000000..a30cdc6 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/resources/abc.t2flow
@@ -0,0 +1,116 @@ +<workflow xmlns="http://taverna.sf.net/2008/xml/t2flow" version="1" producedBy="taverna-2.1-beta-2"><dataflow id="55a3691f-127a-4fd3-b51c-a7ed27f6ec88" role="top"><name>Workflow2</name><inputPorts><port><name>in1</name><depth>0</depth><granularDepth>0</granularDepth><annotations /></port><port><name>in2</name><depth>1</depth><granularDepth>1</granularDepth><annotations /></port></inputPorts><outputPorts><port><name>a</name></port><port><name>b</name></port><port><name>c</name></port></outputPorts><processors><processor><name>B</name><inputPorts><port><name>inputlist</name><depth>1</depth></port></inputPorts><outputPorts><port><name>outputlist</name><depth>1</depth><granularDepth>1</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2.activities</group><artifact>localworker-activity</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.activities.localworker.LocalworkerActivity</class><inputMap><map from="inputlist" to="inputlist" /></inputMap><outputMap><map from="outputlist" to="outputlist" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean xmlns=""> + <script>outputlist = inputlist;</script> + <dependencies /> + <classLoaderSharing>workflow</classLoaderSharing> + <localDependencies /> + <artifactDependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>[B</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>inputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>1</granularDepth> + <name>outputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> +</net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean></configBean><annotations /></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>1000</initialDelay> + <maxDelay>5000</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="inputlist" depth="1" /></cross></strategy></iteration></iterationStrategyStack></processor><processor><name>A</name><inputPorts><port><name>string1</name><depth>0</depth></port><port><name>string2</name><depth>0</depth></port></inputPorts><outputPorts><port><name>output</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2.activities</group><artifact>localworker-activity</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.activities.localworker.LocalworkerActivity</class><inputMap><map from="string2" to="string2" /><map from="string1" to="string1" /></inputMap><outputMap><map from="output" to="output" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean xmlns=""> + <script>output = string1 + string2;</script> + <dependencies /> + <classLoaderSharing>workflow</classLoaderSharing> + <localDependencies /> + <artifactDependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>java.lang.String</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>string1</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>java.lang.String</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>string2</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>0</granularDepth> + <name>output</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> +</net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean></configBean><annotations /></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>1000</initialDelay> + <maxDelay>5000</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="string1" depth="0" /><port name="string2" depth="0" /></cross></strategy></iteration></iterationStrategyStack></processor><processor><name>C</name><inputPorts><port><name>inputlist</name><depth>1</depth></port></inputPorts><outputPorts><port><name>outputlist</name><depth>1</depth><granularDepth>1</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2.activities</group><artifact>localworker-activity</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.activities.localworker.LocalworkerActivity</class><inputMap><map from="inputlist" to="inputlist" /></inputMap><outputMap><map from="outputlist" to="outputlist" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean xmlns=""> + <script>outputlist = inputlist;</script> + <dependencies /> + <classLoaderSharing>workflow</classLoaderSharing> + <localDependencies /> + <artifactDependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>[B</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>inputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>1</granularDepth> + <name>outputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> +</net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean></configBean><annotations /></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>1000</initialDelay> + <maxDelay>5000</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="inputlist" depth="1" /></cross></strategy></iteration></iterationStrategyStack></processor></processors><conditions><condition control="A" target="B" /></conditions><datalinks><datalink><sink type="processor"><processor>B</processor><port>inputlist</port></sink><source type="dataflow"><port>in2</port></source></datalink><datalink><sink type="processor"><processor>A</processor><port>string1</port></sink><source type="dataflow"><port>in1</port></source></datalink><datalink><sink type="processor"><processor>A</processor><port>string2</port></sink><source type="dataflow"><port>in2</port></source></datalink><datalink><sink type="merge"><processor>C</processor><port>inputlist</port></sink><source type="processor"><processor>B</processor><port>outputlist</port></source></datalink><datalink><sink type="merge"><processor>C</processor><port>inputlist</port></sink><source type="processor"><processor>A</processor><port>output</port></source></datalink><datalink><sink type="dataflow"><port>a</port></sink><source type="processor"><processor>A</processor><port>output</port></source></datalink><datalink><sink type="dataflow"><port>b</port></sink><source type="processor"><processor>B</processor><port>outputlist</port></source></datalink><datalink><sink type="dataflow"><port>c</port></sink><source type="processor"><processor>C</processor><port>outputlist</port></source></datalink></datalinks><annotations /></dataflow></workflow> \ No newline at end of file
diff --git a/taverna-dataflow-activity-ui/src/test/resources/p.t2flow b/taverna-dataflow-activity-ui/src/test/resources/p.t2flow new file mode 100644 index 0000000..d4e191c --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/resources/p.t2flow
@@ -0,0 +1,36 @@ +<workflow xmlns="http://taverna.sf.net/2008/xml/t2flow" version="1" producedBy="taverna-2.1-beta-2"><dataflow id="a158f691-3561-424f-bec1-e6359b6b486f" role="top"><name>Workflow7</name><inputPorts><port><name>i</name><depth>0</depth><granularDepth>0</granularDepth><annotations /></port></inputPorts><outputPorts><port><name>o</name></port></outputPorts><processors><processor><name>P</name><inputPorts><port><name>inputlist</name><depth>1</depth></port></inputPorts><outputPorts><port><name>outputlist</name><depth>1</depth><granularDepth>1</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2.activities</group><artifact>localworker-activity</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.activities.localworker.LocalworkerActivity</class><inputMap><map from="inputlist" to="inputlist" /></inputMap><outputMap><map from="outputlist" to="outputlist" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean xmlns=""> + <script>outputlist = inputlist;</script> + <dependencies /> + <classLoaderSharing>workflow</classLoaderSharing> + <localDependencies /> + <artifactDependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>[B</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>inputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>1</granularDepth> + <name>outputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> +</net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean></configBean><annotations /></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>1000</initialDelay> + <maxDelay>5000</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="inputlist" depth="1" /></cross></strategy></iteration></iterationStrategyStack></processor></processors><conditions /><datalinks><datalink><sink type="processor"><processor>P</processor><port>inputlist</port></sink><source type="dataflow"><port>i</port></source></datalink><datalink><sink type="dataflow"><port>o</port></sink><source type="processor"><processor>P</processor><port>outputlist</port></source></datalink></datalinks><annotations /></dataflow></workflow> \ No newline at end of file
diff --git a/taverna-dataflow-activity-ui/src/test/resources/q.t2flow b/taverna-dataflow-activity-ui/src/test/resources/q.t2flow new file mode 100644 index 0000000..03a3cd2 --- /dev/null +++ b/taverna-dataflow-activity-ui/src/test/resources/q.t2flow
@@ -0,0 +1,36 @@ +<workflow xmlns="http://taverna.sf.net/2008/xml/t2flow" version="1" producedBy="taverna-2.1-beta-2"><dataflow id="0833816b-d18b-41b4-b2f7-dae317023444" role="top"><name>Workflow2</name><inputPorts><port><name>p</name><depth>1</depth><granularDepth>1</granularDepth><annotations /></port></inputPorts><outputPorts><port><name>q</name></port><port><name>p</name></port></outputPorts><processors><processor><name>Q</name><inputPorts><port><name>inputlist</name><depth>1</depth></port></inputPorts><outputPorts><port><name>outputlist</name><depth>1</depth><granularDepth>1</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2.activities</group><artifact>localworker-activity</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.activities.localworker.LocalworkerActivity</class><inputMap><map from="inputlist" to="inputlist" /></inputMap><outputMap><map from="outputlist" to="outputlist" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean xmlns=""> + <script>outputlist = inputlist;</script> + <dependencies /> + <classLoaderSharing>workflow</classLoaderSharing> + <localDependencies /> + <artifactDependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>[B</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>inputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>1</granularDepth> + <name>outputlist</name> + <depth>1</depth> + <mimeTypes> + <string>l('')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> +</net.sf.taverna.t2.activities.localworker.LocalworkerActivityConfigurationBean></configBean><annotations /></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>1000</initialDelay> + <maxDelay>5000</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2.core</group><artifact>workflowmodel-impl</artifact><version>0.8</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="inputlist" depth="1" /></cross></strategy></iteration></iterationStrategyStack></processor></processors><conditions /><datalinks><datalink><sink type="processor"><processor>Q</processor><port>inputlist</port></sink><source type="dataflow"><port>p</port></source></datalink><datalink><sink type="dataflow"><port>q</port></sink><source type="processor"><processor>Q</processor><port>outputlist</port></source></datalink><datalink><sink type="dataflow"><port>p</port></sink><source type="dataflow"><port>p</port></source></datalink></datalinks><annotations /></dataflow></workflow> \ No newline at end of file
diff --git a/taverna-disabled-activity-ui/pom.xml b/taverna-disabled-activity-ui/pom.xml new file mode 100644 index 0000000..ddbaf29 --- /dev/null +++ b/taverna-disabled-activity-ui/pom.xml
@@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna</groupId> + <artifactId>taverna-parent</artifactId> + <version>3.0.1-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>disabled-activity-ui</artifactId> + <version>2.0.1-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>Taverna 2 Disabled Activity UI</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-icons-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>report-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>contextual-views-impl</artifactId> + <version>${t2.ui.impl.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-tools</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>workflow-view</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.activities</groupId> + <artifactId>beanshell-activity</artifactId> + <version>${t2.activities.version}</version> + </dependency> + <dependency> + <groupId>javax.help</groupId> + <artifactId>javahelp</artifactId> + <version>${javahelp.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>uibuilder</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>helper</artifactId> + <version>${t2.ui.impl.version}</version> + </dependency> + </dependencies> + <repositories> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository + </url> + </repository> + <repository> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + <id>mygrid-snapshot-repository</id> + <name>myGrid Snapshot Repository</name> + <url>http://www.mygrid.org.uk/maven/snapshot-repository</url> + </repository> + </repositories> + <scm> + <connection>scm:git:https://github.com/taverna/taverna-disabled-activity-ui.git</connection> + <developerConnection>scm:git:ssh://git@github.com/taverna/taverna-disabled-activity-ui.git</developerConnection> + <url>https://github.com/taverna/taverna-disabled-activity-ui/</url> + <tag>HEAD</tag> + </scm> +</project> +
diff --git a/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/actions/DisabledActivityConfigurationAction.java b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/actions/DisabledActivityConfigurationAction.java new file mode 100644 index 0000000..c71d483 --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/actions/DisabledActivityConfigurationAction.java
@@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.disabled.actions; + +import java.awt.Component; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JOptionPane; + +import uk.org.taverna.scufl2.api.activity.Activity; + +import net.sf.taverna.t2.activities.disabled.views.DisabledConfigView; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.report.ReportManager; +import net.sf.taverna.t2.workbench.ui.actions.activity.ActivityConfigurationAction; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ActivityConfigurationDialog; + +@SuppressWarnings("serial") +public class DisabledActivityConfigurationAction extends ActivityConfigurationAction { + + public static final String FIX_DISABLED = "Edit properties"; + private final EditManager editManager; + private final FileManager fileManager; + private final ReportManager reportManager; + + public DisabledActivityConfigurationAction(Activity activity, Frame owner, + EditManager editManager, FileManager fileManager, ReportManager reportManager, + ActivityIconManager activityIconManager, ServiceDescriptionRegistry serviceDescriptionRegistry) { + super(activity, activityIconManager, serviceDescriptionRegistry); + this.editManager = editManager; + this.fileManager = fileManager; + this.reportManager = reportManager; + putValue(NAME, FIX_DISABLED); + } + + public void actionPerformed(ActionEvent e) { + ActivityConfigurationDialog currentDialog = ActivityConfigurationAction + .getDialog(getActivity()); + if (currentDialog != null) { + currentDialog.toFront(); + return; + } + int answer = JOptionPane.showConfirmDialog((Component) e.getSource(), + "Directly editing properties can be dangerous. Are you sure you want to proceed?", + "Confirm editing", JOptionPane.YES_NO_OPTION); + if (answer != JOptionPane.YES_OPTION) { + return; + } + + final DisabledConfigView disabledConfigView = new DisabledConfigView(getActivity()); + final DisabledActivityConfigurationDialog dialog = new DisabledActivityConfigurationDialog( + getActivity(), disabledConfigView); + + ActivityConfigurationAction.setDialog(getActivity(), dialog, fileManager); + + } + + private class DisabledActivityConfigurationDialog extends ActivityConfigurationDialog { + public DisabledActivityConfigurationDialog(Activity a, DisabledConfigView p) { + super(a, p, editManager); + this.setModal(true); + super.applyButton.setEnabled(false); + super.applyButton.setVisible(false); + } + + public void configureActivity(Dataflow df, Activity a, Object bean) { + Edit<?> configureActivityEdit = editManager.getEdits() + .getConfigureActivityEdit(a, bean); + try { + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + editList.add(configureActivityEdit); + Processor p = findProcessor(df, a); + if (p != null && p.getActivityList().size() == 1) { + editList.add(editManager.getEdits().getMapProcessorPortsForActivityEdit(p)); + } + Edit e = Tools.getEnableDisabledActivityEdit(super.owningProcessor, activity, + editManager.getEdits()); + if (e != null) { + editList.add(e); + editManager.doDataflowEdit(df, new CompoundEdit(editList)); + reportManager.updateObjectReport(super.owningDataflow, super.owningProcessor); + + } + } catch (IllegalStateException e) { + // TODO Auto-generated catch block + logger.error(e); + } catch (EditException e) { + logger.error(e); + } + } + + } + +}
diff --git a/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/menu/ConfigureDisabledMenuAction.java b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/menu/ConfigureDisabledMenuAction.java new file mode 100644 index 0000000..68a906e --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/menu/ConfigureDisabledMenuAction.java
@@ -0,0 +1,52 @@ +package net.sf.taverna.t2.activities.disabled.menu; + +import javax.swing.Action; + +import net.sf.taverna.t2.activities.disabled.actions.DisabledActivityConfigurationAction; +import net.sf.taverna.t2.activities.disabled.views.DisabledActivityViewFactory; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.activitytools.AbstractConfigureActivityMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.report.ReportManager; + +public class ConfigureDisabledMenuAction extends AbstractConfigureActivityMenuAction { + + private EditManager editManager; + private FileManager fileManager; + private ReportManager reportManager; + private ActivityIconManager activityIconManager; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + + public ConfigureDisabledMenuAction() { + super(DisabledActivityViewFactory.ACTIVITY_TYPE); + } + + @Override + protected Action createAction() { + return new DisabledActivityConfigurationAction(findActivity(), getParentFrame(), + editManager, fileManager, reportManager, activityIconManager, serviceDescriptionRegistry); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setReportManager(ReportManager reportManager) { + this.reportManager = reportManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setServiceDescriptionRegistry(ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + +}
diff --git a/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledActivityViewFactory.java b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledActivityViewFactory.java new file mode 100644 index 0000000..a168974 --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledActivityViewFactory.java
@@ -0,0 +1,80 @@ +package net.sf.taverna.t2.activities.disabled.views; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.report.ReportManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.activity.Activity; + +/** + * This class generates a contextual view for a DisabledActivity + * + * @author alanrw + * @author David Withers + */ +public class DisabledActivityViewFactory implements ContextualViewFactory<Activity> { + + public static final URI ACTIVITY_TYPE = URI.create("http://ns.taverna.org.uk/2010/activity/disabled"); + + private EditManager editManager; + private FileManager fileManager; + private ReportManager reportManager; + private ActivityIconManager activityIconManager; + private ColourManager colourManager; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + + /** + * The factory can handle a DisabledActivity + * + * @param object + * @return + */ + public boolean canHandle(Object object) { + return object instanceof Activity && ((Activity) object).getType().equals(ACTIVITY_TYPE); + } + + /** + * Return a contextual view that can display information about a DisabledActivity + * + * @param activity + * @return + */ + public List<ContextualView> getViews(Activity activity) { + return Arrays.asList(new ContextualView[] { new DisabledContextualView(activity, + editManager, fileManager, reportManager, colourManager, activityIconManager, + serviceDescriptionRegistry) }); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setReportManager(ReportManager reportManager) { + this.reportManager = reportManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setServiceDescriptionRegistry(ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + +}
diff --git a/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledConfigView.java b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledConfigView.java new file mode 100644 index 0000000..9c8c9cd --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledConfigView.java
@@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.disabled.views; + +import java.awt.BorderLayout; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import javax.help.CSH; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import uk.org.taverna.scufl2.api.activity.Activity; + +import net.sf.taverna.t2.lang.uibuilder.UIBuilder; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ActivityConfigurationPanel; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.DomDriver; + +@SuppressWarnings("serial") +public class DisabledConfigView extends ActivityConfigurationPanel { + + private ActivityAndBeanWrapper configuration; + private List<String> fieldNames; + + private Object clonedConfig = null; + String origConfigXML = ""; + + public DisabledConfigView(Activity activity) { + super(activity); + setLayout(new BorderLayout()); + fieldNames = null; + initialise(); + } + + private void initialise() { + CSH.setHelpIDString( + this, + "net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.DisabledConfigView"); + configuration = activity.getConfiguration(); + XStream xstream = new XStream(new DomDriver()); + Activity a = configuration.getActivity(); + xstream.setClassLoader(a.getClass().getClassLoader()); + Object origConfig = configuration.getBean(); + if (fieldNames == null) { + fieldNames = getFieldNames(origConfig); + } + origConfigXML = xstream.toXML(origConfig); + clonedConfig = xstream.fromXML(origConfigXML); + JPanel panel = UIBuilder.buildEditor(clonedConfig, (String[]) fieldNames.toArray(new String[0])); + this.add(panel, BorderLayout.CENTER); + this.revalidate(); + } + + @Override + public void refreshConfiguration() { + this.removeAll(); + initialise(); + } + + public boolean checkValues() { + boolean result = false; + result = activity.configurationWouldWork(clonedConfig); + if (!result) { + JOptionPane.showMessageDialog( + this, + "The new properties are invalid or not consistent with the workflow", + "Invalid properties", JOptionPane.WARNING_MESSAGE); + } + return result; + } + + public void noteConfiguration() { + if (isConfigurationChanged()) { + ActivityAndBeanWrapper newConfig = new ActivityAndBeanWrapper(); + newConfig.setActivity(configuration.getActivity()); + newConfig.setBean(clonedConfig); + configuration = newConfig; + + XStream xstream = new XStream(new DomDriver()); + xstream.setClassLoader(configuration.getActivity().getClass().getClassLoader()); + + origConfigXML = xstream.toXML(clonedConfig); + } + } + + @Override + public ActivityAndBeanWrapper getConfiguration() { + return configuration; + } + + public boolean isConfigurationChanged() { + XStream xstream = new XStream(new DomDriver()); + xstream.setClassLoader(configuration.getActivity().getClass().getClassLoader()); + return (!xstream.toXML(clonedConfig).equals(origConfigXML)); + } + + private List<String> getFieldNames(Object config) { + List<String> result = new ArrayList<String>(); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(config.getClass()); + for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { + Method readMethod = pd.getReadMethod(); + if ((readMethod != null) && !(pd.getName().equals("class"))) { + try { + result.add(pd.getName()); + } catch (IllegalArgumentException ex) { + // ignore + } + } + } + } catch (IntrospectionException e) { + // ignore + } + return result; + } +}
diff --git a/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledContextualView.java b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledContextualView.java new file mode 100644 index 0000000..9d60faa --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/java/net/sf/taverna/t2/activities/disabled/views/DisabledContextualView.java
@@ -0,0 +1,128 @@ +/** + * + */ +package net.sf.taverna.t2.activities.disabled.views; + +import java.awt.Frame; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.Action; + +import net.sf.taverna.t2.activities.disabled.actions.DisabledActivityConfigurationAction; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.report.ReportManager; +import net.sf.taverna.t2.workbench.ui.actions.activity.HTMLBasedActivityContextualView; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * A DisabledContextualView displays information about a DisabledActivity + * + * @author alanrw + * @author David Withers + */ +@SuppressWarnings("serial") +public class DisabledContextualView extends HTMLBasedActivityContextualView { + + private List<String> fieldNames; + + private final EditManager editManager; + private final FileManager fileManager; + private final ReportManager reportManager; + private final ActivityIconManager activityIconManager; + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public DisabledContextualView(Activity activity, EditManager editManager, + FileManager fileManager, ReportManager reportManager, ColourManager colourManager, + ActivityIconManager activityIconManager, ServiceDescriptionRegistry serviceDescriptionRegistry) { + super(activity, colourManager); + this.editManager = editManager; + this.fileManager = fileManager; + this.reportManager = reportManager; + this.activityIconManager = activityIconManager; + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + /** + * The table for the DisabledActivity shows its ports and the information within the offline + * Activity's configuration. + * + * @return + */ + @Override + protected String getRawTableRowsHtml() { + StringBuilder html = new StringBuilder(); + html.append("<tr><th>Input Port Name</th><th>Depth</th></tr>"); + for (InputActivityPort inputActivityPort : getActivity().getInputPorts()) { + html.append("<tr><td>" + inputActivityPort.getName() + "</td><td>"); + html.append(inputActivityPort.getDepth() + "</td></tr>"); + } + html.append("<tr><th>Output Port Name</th><th>Depth</th></tr>"); + for (OutputActivityPort outputActivityPort : getActivity().getOutputPorts()) { + html.append("<tr><td>" + outputActivityPort.getName() + "</td><td>"); + html.append(outputActivityPort.getDepth() + "</td></tr>"); + } + + JsonNode config = getConfigBean().getJson(); + try { + html.append("<tr><th>Property Name</th><th>Property Value</th></tr>"); + BeanInfo beanInfo = Introspector.getBeanInfo(config.getClass()); + for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { + Method readMethod = pd.getReadMethod(); + if ((readMethod != null) && !(pd.getName().equals("class"))) { + try { + html.append("<tr><td>"); + html.append(pd.getName()); + html.append("</td><td>"); + html.append(readMethod.invoke(config)); + html.append("</td></tr>"); + if (fieldNames == null) { + fieldNames = new ArrayList<String>(); + } + fieldNames.add(pd.getName()); + } catch (IllegalAccessException ex) { + // ignore + } catch (IllegalArgumentException ex) { + // ignore + } catch (InvocationTargetException ex) { + // ignore + } + } + } + } catch (IntrospectionException e) { + // ignore + } + return html.toString(); + } + + @Override + public String getViewTitle() { + return "Unavailable service"; + } + + @Override + public int getPreferredPosition() { + return 100; + } + + @Override + public Action getConfigureAction(Frame owner) { + return new DisabledActivityConfigurationAction(getActivity(), owner, + editManager, fileManager, reportManager, activityIconManager, serviceDescriptionRegistry); + } + +}
diff --git a/taverna-disabled-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-disabled-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..022189a --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.disabled.menu.ConfigureDisabledMenuAction \ No newline at end of file
diff --git a/taverna-disabled-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-disabled-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..80b0bf3 --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.disabled.views.DisabledActivityViewFactory
diff --git a/taverna-disabled-activity-ui/src/main/resources/META-INF/spring/disabled-activity-ui-context-osgi.xml b/taverna-disabled-activity-ui/src/main/resources/META-INF/spring/disabled-activity-ui-context-osgi.xml new file mode 100644 index 0000000..f938272 --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/resources/META-INF/spring/disabled-activity-ui-context-osgi.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="ConfigureDisabledMenuAction" auto-export="interfaces" /> + + <service ref="DisabledActivityViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + <reference id="reportManager" interface="net.sf.taverna.t2.workbench.report.ReportManager" /> + <reference id="activityIconManager" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconManager" /> + <reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + <reference id="serviceDescriptionRegistry" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry" /> + +</beans:beans>
diff --git a/taverna-disabled-activity-ui/src/main/resources/META-INF/spring/disabled-activity-ui-context.xml b/taverna-disabled-activity-ui/src/main/resources/META-INF/spring/disabled-activity-ui-context.xml new file mode 100644 index 0000000..fbc0aa0 --- /dev/null +++ b/taverna-disabled-activity-ui/src/main/resources/META-INF/spring/disabled-activity-ui-context.xml
@@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="ConfigureDisabledMenuAction" class="net.sf.taverna.t2.activities.disabled.menu.ConfigureDisabledMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="reportManager" ref="reportManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + </bean> + + <bean id="DisabledActivityViewFactory" class="net.sf.taverna.t2.activities.disabled.views.DisabledActivityViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="reportManager" ref="reportManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + </bean> + +</beans>
diff --git a/taverna-stringconstant-activity-ui/pom.xml b/taverna-stringconstant-activity-ui/pom.xml new file mode 100644 index 0000000..761981f --- /dev/null +++ b/taverna-stringconstant-activity-ui/pom.xml
@@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna</groupId> + <artifactId>taverna-parent</artifactId> + <version>3.0.1-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>stringconstant-activity-ui</artifactId> + <version>2.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>Taverna 2 StringConstant Activity UI</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-icons-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-palette-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>workflow-view</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.commons</groupId> + <artifactId>taverna-services-api</artifactId> + <version>0.1.0-SNAPSHOT</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>com.springsource.org.apache.commons.lang</artifactId> + <version>${commons.lang.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <!-- <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>activity-palette-impl</artifactId> + <version>${t2.ui.impl.version}</version> + <scope>test</scope> + </dependency> --> + <!-- <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-tools</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> --> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-tools</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + </dependencies> + <repositories> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository + </url> + </repository> + <repository> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + <id>mygrid-snapshot-repository</id> + <name>myGrid Snapshot Repository</name> + <url>http://www.mygrid.org.uk/maven/snapshot-repository</url> + </repository> + </repositories> +</project> +
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/actions/StringConstantActivityConfigurationAction.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/actions/StringConstantActivityConfigurationAction.java new file mode 100644 index 0000000..fa8bafc --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/actions/StringConstantActivityConfigurationAction.java
@@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.actions; + +import java.awt.Frame; +import java.awt.event.ActionEvent; + +import net.sf.taverna.t2.activities.stringconstant.views.StringConstantConfigView; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.actions.activity.ActivityConfigurationAction; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ActivityConfigurationDialog; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.activity.Activity; + +public class StringConstantActivityConfigurationAction extends + ActivityConfigurationAction { + private static final long serialVersionUID = 2518716617809186972L; + public static final String CONFIGURE_STRINGCONSTANT = "Edit value"; + + private final EditManager editManager; + private final FileManager fileManager; + private final ServiceRegistry serviceRegistry; + + public StringConstantActivityConfigurationAction(Activity activity, + Frame owner, EditManager editManager, FileManager fileManager, + ActivityIconManager activityIconManager, + ServiceDescriptionRegistry serviceDescriptionRegistry, + ServiceRegistry serviceRegistry) { + super(activity, activityIconManager, serviceDescriptionRegistry); + this.editManager = editManager; + this.fileManager = fileManager; + this.serviceRegistry = serviceRegistry; + putValue(NAME, CONFIGURE_STRINGCONSTANT); + } + + @Override + public void actionPerformed(ActionEvent e) { + ActivityConfigurationDialog currentDialog = getDialog(getActivity()); + if (currentDialog != null) { + currentDialog.toFront(); + return; + } + + StringConstantConfigView configView = new StringConstantConfigView( + activity, serviceRegistry); + ActivityConfigurationDialog dialog = new ActivityConfigurationDialog( + getActivity(), configView, editManager); + setDialog(getActivity(), dialog, fileManager); + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/AddStringConstantTemplateAction.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/AddStringConstantTemplateAction.java new file mode 100644 index 0000000..fb9d069 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/AddStringConstantTemplateAction.java
@@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.menu; + +import static net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView.importServiceDescription; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * An action to add a string constant activity + a wrapping processor to the + * workflow. + * + * @author Alex Nenadic + * @author David Withers + */ +@SuppressWarnings("serial") +public class AddStringConstantTemplateAction extends + AbstractContextualMenuAction { + private static final URI ACTIVITY_TYPE = URI + .create("http://ns.taverna.org.uk/2010/activity/constant"); + private static final URI insertSection = URI + .create("http://taverna.sf.net/2009/contextMenu/insert"); + + private EditManager editManager; + private MenuManager menuManager; + private SelectionManager selectionManager; + private ActivityIconManager activityIconManager; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + private ServiceRegistry serviceRegistry; + + public AddStringConstantTemplateAction() { + super(insertSection, 800); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Workflow; + } + + @Override + protected Action createAction() { + AbstractAction action = new AbstractAction("Text constant", + activityIconManager.iconForActivity(ACTIVITY_TYPE)) { + @Override + public void actionPerformed(ActionEvent e) { + importServiceDescription( + serviceDescriptionRegistry + .getServiceDescription(ACTIVITY_TYPE), + false, editManager, menuManager, selectionManager, + serviceRegistry); + } + }; + return action; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setServiceDescriptionRegistry( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/AddStringConstantTemplateMenuAction.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/AddStringConstantTemplateMenuAction.java new file mode 100644 index 0000000..cb1682d --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/AddStringConstantTemplateMenuAction.java
@@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.menu; + +import static java.awt.event.InputEvent.ALT_DOWN_MASK; +import static java.awt.event.InputEvent.SHIFT_DOWN_MASK; +import static java.awt.event.KeyEvent.VK_S; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView.importServiceDescription; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.commons.services.ServiceRegistry; + +/** + * An action to add a string constant activity + a wrapping processor to the + * workflow. + * + * @author Alex Nenadic + * @author Alan R Williams + * @author David Withers + */ +@SuppressWarnings("serial") +public class AddStringConstantTemplateMenuAction extends AbstractMenuAction { + private static final URI ACTIVITY_TYPE = URI + .create("http://ns.taverna.org.uk/2010/activity/constant"); + private static final URI INSERT = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#insert"); + private static final String ADD_STRING_CONSTANT = "Text constant"; + private static final URI ADD_STRING_CONSTANT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuAddStringConstant"); + + private EditManager editManager; + private MenuManager menuManager; + private SelectionManager selectionManager; + private ActivityIconManager activityIconManager; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + private ServiceRegistry serviceRegistry; + + public AddStringConstantTemplateMenuAction() { + super(INSERT, 800, ADD_STRING_CONSTANT_URI); + } + + @Override + protected Action createAction() { + return new AddStringConstantMenuAction(); + } + + protected class AddStringConstantMenuAction extends AbstractAction + implements DesignOnlyAction { + AddStringConstantMenuAction() { + super(); + putValue(SMALL_ICON, + activityIconManager.iconForActivity(ACTIVITY_TYPE)); + putValue(NAME, ADD_STRING_CONSTANT); + putValue(SHORT_DESCRIPTION, ADD_STRING_CONSTANT); + putValue(ACCELERATOR_KEY, + getKeyStroke(VK_S, SHIFT_DOWN_MASK | ALT_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + importServiceDescription( + serviceDescriptionRegistry + .getServiceDescription(ACTIVITY_TYPE), + false, editManager, menuManager, selectionManager, + serviceRegistry); + } + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setServiceDescriptionRegistry( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/ConfigureStringConstantMenuAction.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/ConfigureStringConstantMenuAction.java new file mode 100644 index 0000000..46bdde9 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/menu/ConfigureStringConstantMenuAction.java
@@ -0,0 +1,64 @@ +package net.sf.taverna.t2.activities.stringconstant.menu; + +import static javax.swing.Action.NAME; +import static net.sf.taverna.t2.activities.stringconstant.actions.StringConstantActivityConfigurationAction.CONFIGURE_STRINGCONSTANT; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.activities.stringconstant.actions.StringConstantActivityConfigurationAction; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.activitytools.AbstractConfigureActivityMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import uk.org.taverna.commons.services.ServiceRegistry; + +public class ConfigureStringConstantMenuAction extends + AbstractConfigureActivityMenuAction { + private static final URI ACTIVITY_TYPE = URI + .create("http://ns.taverna.org.uk/2010/activity/constant"); + + private EditManager editManager; + private FileManager fileManager; + private ActivityIconManager activityIconManager; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + private ServiceRegistry serviceRegistry; + + public ConfigureStringConstantMenuAction() { + super(ACTIVITY_TYPE); + } + + @Override + protected Action createAction() { + StringConstantActivityConfigurationAction configAction = new StringConstantActivityConfigurationAction( + findActivity(), getParentFrame(), editManager, fileManager, + activityIconManager, serviceDescriptionRegistry, + serviceRegistry); + configAction.putValue(NAME, CONFIGURE_STRINGCONSTANT); + addMenuDots(configAction); + return configAction; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setServiceDescriptionRegistry( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/servicedescriptions/StringConstantActivityIcon.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/servicedescriptions/StringConstantActivityIcon.java new file mode 100644 index 0000000..409c0f5 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/servicedescriptions/StringConstantActivityIcon.java
@@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.servicedescriptions; + +import java.net.URI; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +import net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI; + +/** + * @author Alex Nenadic + */ +public class StringConstantActivityIcon implements ActivityIconSPI { + private static final URI ACTIVITY_TYPE = URI + .create("http://ns.taverna.org.uk/2010/activity/constant"); + private static Icon icon = null; + + @Override + public int canProvideIconScore(URI activityType) { + if (activityType.equals(ACTIVITY_TYPE)) + return DEFAULT_ICON + 1; + else + return NO_ICON; + } + + @Override + public Icon getIcon(URI activityType) { + return getStringConstantIcon(); + } + + public static Icon getStringConstantIcon() { + if (icon == null) + icon = new ImageIcon( + StringConstantActivityIcon.class + .getResource("/stringconstant.png")); + return icon; + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/servicedescriptions/StringConstantTemplateService.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/servicedescriptions/StringConstantTemplateService.java new file mode 100644 index 0000000..157f3b6 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/servicedescriptions/StringConstantTemplateService.java
@@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.servicedescriptions; + +import java.net.URI; + +import javax.swing.Icon; + +import net.sf.taverna.t2.servicedescriptions.AbstractTemplateService; +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import uk.org.taverna.scufl2.api.configurations.Configuration; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class StringConstantTemplateService extends AbstractTemplateService { + private static final URI ACTIVITY_TYPE = URI + .create("http://ns.taverna.org.uk/2010/activity/constant"); + private static final URI providerId = URI + .create("http://taverna.sf.net/2010/service-provider/stringconstant"); + public static final String DEFAULT_VALUE = "Add your own value here"; + private static final String STRINGCONSTANT = "Text constant"; + + @Override + public URI getActivityType() { + return ACTIVITY_TYPE; + } + + @Override + public Configuration getActivityConfiguration() { + Configuration configuration = new Configuration(); + configuration.setType(ACTIVITY_TYPE.resolve("#Config")); + ((ObjectNode) configuration.getJson()).put("string", DEFAULT_VALUE); + return configuration; + } + + @Override + public Icon getIcon() { + return StringConstantActivityIcon.getStringConstantIcon(); + } + + @Override + public String getName() { + return STRINGCONSTANT; + } + + @Override + public String getDescription() { + return "A string value that you can set"; + } + + public static ServiceDescription getServiceDescription() { + StringConstantTemplateService scts = new StringConstantTemplateService(); + return scts.templateService; + } + + @Override + public String getId() { + return providerId.toString(); + } + + @Override + public ServiceDescriptionProvider newInstance() { + return new StringConstantTemplateService(); + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantActivityContextualView.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantActivityContextualView.java new file mode 100644 index 0000000..803692d --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantActivityContextualView.java
@@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.views; + +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; +import static org.apache.commons.lang.StringUtils.abbreviate; + +import java.awt.Frame; + +import javax.swing.Action; + +import net.sf.taverna.t2.activities.stringconstant.actions.StringConstantActivityConfigurationAction; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.actions.activity.HTMLBasedActivityContextualView; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.activity.Activity; + +import com.fasterxml.jackson.databind.JsonNode; + +public class StringConstantActivityContextualView extends + HTMLBasedActivityContextualView { + private static final long serialVersionUID = -553974544001808511L; + private static final int MAX_LENGTH = 100; + + private final EditManager editManager; + private final FileManager fileManager; + private final ActivityIconManager activityIconManager; + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + private final ServiceRegistry serviceRegistry; + + public StringConstantActivityContextualView(Activity activity, + EditManager editManager, FileManager fileManager, + ActivityIconManager activityIconManager, + ColourManager colourManager, + ServiceDescriptionRegistry serviceDescriptionRegistry, + ServiceRegistry serviceRegistry) { + super(activity, colourManager); + this.editManager = editManager; + this.fileManager = fileManager; + this.activityIconManager = activityIconManager; + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + this.serviceRegistry = serviceRegistry; + } + + @Override + public String getViewTitle() { + return "Text constant"; + } + + @Override + protected String getRawTableRowsHtml() { + JsonNode json = getConfigBean().getJson(); + String value = json.get("string").textValue(); + value = abbreviate(value, MAX_LENGTH); + value = escapeHtml(value); + String html = "<tr><td>Value</td><td>" + value + "</td></tr>"; + return html; + } + + @Override + public Action getConfigureAction(Frame owner) { + return new StringConstantActivityConfigurationAction(getActivity(), + owner, editManager, fileManager, activityIconManager, + serviceDescriptionRegistry, serviceRegistry); + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantActivityViewFactory.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantActivityViewFactory.java new file mode 100644 index 0000000..3f1e480 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantActivityViewFactory.java
@@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.views; + +import static java.util.Arrays.asList; + +import java.net.URI; +import java.util.List; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.activity.Activity; + +public class StringConstantActivityViewFactory implements + ContextualViewFactory<Activity> { + private static final URI ACTIVITY_TYPE = URI + .create("http://ns.taverna.org.uk/2010/activity/constant"); + + private EditManager editManager; + private FileManager fileManager; + private ActivityIconManager activityIconManager; + private ColourManager colourManager; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + private ServiceRegistry serviceRegistry; + + @Override + public boolean canHandle(Object object) { + return object instanceof Activity + && ((Activity) object).getType().equals(ACTIVITY_TYPE); + } + + @Override + public List<ContextualView> getViews(Activity activity) { + return asList(new ContextualView[] { new StringConstantActivityContextualView( + activity, editManager, fileManager, activityIconManager, + colourManager, serviceDescriptionRegistry, serviceRegistry) }); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setServiceDescriptionRegistry( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantConfigView.java b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantConfigView.java new file mode 100644 index 0000000..b371adb --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/java/net/sf/taverna/t2/activities/stringconstant/views/StringConstantConfigView.java
@@ -0,0 +1,243 @@ +/** + * + */ +package net.sf.taverna.t2.activities.stringconstant.views; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Color.WHITE; +import static java.awt.Font.PLAIN; +import static java.awt.GridBagConstraints.BOTH; +import static java.awt.GridBagConstraints.FIRST_LINE_START; +import static java.lang.String.format; +import static javax.swing.BorderFactory.createTitledBorder; +import static javax.swing.JOptionPane.YES_NO_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION; +import static javax.swing.border.TitledBorder.DEFAULT_POSITION; +import static net.sf.taverna.t2.activities.stringconstant.servicedescriptions.StringConstantTemplateService.DEFAULT_VALUE; +import static net.sf.taverna.t2.lang.ui.FileTools.readStringFromFile; +import static net.sf.taverna.t2.lang.ui.FileTools.saveStringToFile; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.JTextPane; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; + +import net.sf.taverna.t2.lang.ui.LineEnabledTextPanel; +import net.sf.taverna.t2.lang.ui.LinePainter; +import net.sf.taverna.t2.lang.ui.NoWrapEditorKit; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ActivityConfigurationPanel; + +import org.apache.log4j.Logger; + +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; + +/** + * @author alanrw + * @author David Withers + */ +@SuppressWarnings("serial") +public class StringConstantConfigView extends ActivityConfigurationPanel { + private static final String CONTENT_PROPERTY = "string"; + private static final String TEXT_FILE_EXTENSION = ".txt"; + public static Logger logger = Logger.getLogger(StringConstantConfigView.class); + private static final Color LINE_COLOR = WHITE; + @SuppressWarnings("unused") + private static final String HELP_TOKEN = "net.sf.taverna.t2.activities.stringconstant.views.StringConstantConfigView"; + + /** The text */ + private JEditorPane scriptTextArea; + private final ServiceRegistry serviceRegistry; + + public StringConstantConfigView(Activity activity, + Configuration configuration, ServiceRegistry serviceRegistry) { + super(activity, configuration); + this.serviceRegistry = serviceRegistry; + setLayout(new GridBagLayout()); + initialise(); + addAncestorListener(new AncestorListener() { + @Override + public void ancestorAdded(AncestorEvent event) { + whenOpened(); + } + + @Override + public void ancestorMoved(AncestorEvent event) { + } + + @Override + public void ancestorRemoved(AncestorEvent event) { + } + }); + } + + public StringConstantConfigView(Activity activity, + ServiceRegistry serviceRegistry) { + super(activity); + this.serviceRegistry = serviceRegistry; + setLayout(new GridBagLayout()); + initialise(); + addAncestorListener(new AncestorListener() { + @Override + public void ancestorAdded(AncestorEvent event) { + whenOpened(); + } + + @Override + public void ancestorMoved(AncestorEvent event) { + } + + @Override + public void ancestorRemoved(AncestorEvent event) { + } + }); + } + + @Override + public void whenOpened() { + scriptTextArea.requestFocus(); + if (scriptTextArea.getText().equals(DEFAULT_VALUE)) + scriptTextArea.selectAll(); + } + + /** The name of the thing we are working with. */ + protected String entityName() { + return "text"; + } + + @Override + protected void initialise() { + super.initialise(); + // CSH.setHelpIDString(this, HELP_TOKEN); + + setBorder(createTitledBorder(null, null, DEFAULT_JUSTIFICATION, + DEFAULT_POSITION, new Font("Lucida Grande", 1, 12))); + + JPanel scriptEditPanel = new JPanel(new BorderLayout()); + + scriptTextArea = new JTextPane(); + new LinePainter(scriptTextArea, LINE_COLOR); + + // NOTE: Due to T2-1145 - always set editor kit BEFORE setDocument + scriptTextArea.setEditorKit(new NoWrapEditorKit()); + scriptTextArea.setFont(new Font("Monospaced", PLAIN, 14)); + scriptTextArea.setText(getProperty(CONTENT_PROPERTY)); + scriptTextArea.setCaretPosition(0); + scriptTextArea.setPreferredSize(new Dimension(200, 100)); + + scriptEditPanel.add(new LineEnabledTextPanel(scriptTextArea), CENTER); + + GridBagConstraints outerConstraint = new GridBagConstraints(); + outerConstraint.anchor = FIRST_LINE_START; + outerConstraint.gridx = 0; + outerConstraint.gridy = 0; + + outerConstraint.fill = BOTH; + outerConstraint.weighty = 0.1; + outerConstraint.weightx = 0.1; + add(scriptEditPanel, outerConstraint); + + JButton loadScriptButton = new JButton("Load " + entityName()); + loadScriptButton.setToolTipText(format("Load %s from a file", + entityName())); + loadScriptButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + loadText(); + } + }); + + JButton saveRScriptButton = new JButton("Save " + entityName()); + saveRScriptButton.setToolTipText(format("Save the %s to a file", + entityName())); + saveRScriptButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveText(); + } + }); + + JButton clearScriptButton = new JButton("Clear " + entityName()); + clearScriptButton.setToolTipText(format( + "Clear current %s from the edit area", entityName())); + clearScriptButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clearText(); + } + }); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout()); + buttonPanel.add(loadScriptButton); + buttonPanel.add(saveRScriptButton); + buttonPanel.add(clearScriptButton); + + scriptEditPanel.add(buttonPanel, SOUTH); + setPreferredSize(new Dimension(600, 500)); + this.validate(); + } + + /** + * Method for loading the value + */ + private void loadText() { + String newScript = readStringFromFile(this, "Load " + entityName(), + TEXT_FILE_EXTENSION); + if (newScript != null) { + scriptTextArea.setText(newScript); + scriptTextArea.setCaretPosition(0); + } + } + + /** + * Method for saving the value + */ + private void saveText() { + saveStringToFile(this, "Save " + entityName(), TEXT_FILE_EXTENSION, + scriptTextArea.getText()); + } + + /** + * Method for clearing the value + */ + private void clearText() { + if (showConfirmDialog(this, + format("Do you really want to clear the %s?", entityName()), + "Clearing the " + entityName(), YES_NO_OPTION) == YES_OPTION) + scriptTextArea.setText(""); + } + + @Override + public boolean checkValues() { + return true; + } + + @Override + public boolean isConfigurationChanged() { + return !scriptTextArea.getText().equals(getProperty(CONTENT_PROPERTY)); + } + + @Override + public void noteConfiguration() { + setProperty(CONTENT_PROPERTY, scriptTextArea.getText()); + configureInputPorts(serviceRegistry); + configureOutputPorts(serviceRegistry); + } +}
diff --git a/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider new file mode 100644 index 0000000..7a14b56 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.stringconstant.servicedescriptions.StringConstantTemplateService
diff --git a/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..22938a2 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,3 @@ +net.sf.taverna.t2.activities.stringconstant.menu.AddStringConstantTemplateAction +net.sf.taverna.t2.activities.stringconstant.menu.AddStringConstantTemplateMenuAction +net.sf.taverna.t2.activities.stringconstant.menu.ConfigureStringConstantMenuAction \ No newline at end of file
diff --git a/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI new file mode 100644 index 0000000..58228ef --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.stringconstant.servicedescriptions.StringConstantActivityIcon \ No newline at end of file
diff --git a/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..73ca2a1 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.stringconstant.views.StringConstantActivityViewFactory \ No newline at end of file
diff --git a/taverna-stringconstant-activity-ui/src/main/resources/META-INF/spring/stringconstant-activity-ui-context-osgi.xml b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/spring/stringconstant-activity-ui-context-osgi.xml new file mode 100644 index 0000000..359a72d --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/spring/stringconstant-activity-ui-context-osgi.xml
@@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="StringConstantActivityIcon" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI" /> + <service ref="StringConstantTemplateService" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider" /> + <service ref="StringConstantActivityViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <service ref="AddStringConstantTemplateAction" auto-export="interfaces" /> + <service ref="AddStringConstantTemplateMenuAction" auto-export="interfaces" /> + <service ref="ConfigureStringConstantMenuAction" auto-export="interfaces" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + <reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + <reference id="activityIconManager" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconManager" /> + <reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + <reference id="serviceDescriptionRegistry" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry" /> + <reference id="serviceRegistry" interface="uk.org.taverna.commons.services.ServiceRegistry" /> +</beans:beans>
diff --git a/taverna-stringconstant-activity-ui/src/main/resources/META-INF/spring/stringconstant-activity-ui-context.xml b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/spring/stringconstant-activity-ui-context.xml new file mode 100644 index 0000000..19bb6fd --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/resources/META-INF/spring/stringconstant-activity-ui-context.xml
@@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="StringConstantActivityIcon" + class="net.sf.taverna.t2.activities.stringconstant.servicedescriptions.StringConstantActivityIcon" /> + + <bean id="StringConstantTemplateService" + class="net.sf.taverna.t2.activities.stringconstant.servicedescriptions.StringConstantTemplateService" /> + + <bean id="AddStringConstantTemplateAction" + class="net.sf.taverna.t2.activities.stringconstant.menu.AddStringConstantTemplateAction"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> + <bean id="AddStringConstantTemplateMenuAction" + class="net.sf.taverna.t2.activities.stringconstant.menu.AddStringConstantTemplateMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> + <bean id="ConfigureStringConstantMenuAction" + class="net.sf.taverna.t2.activities.stringconstant.menu.ConfigureStringConstantMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> + + <bean id="StringConstantActivityViewFactory" + class="net.sf.taverna.t2.activities.stringconstant.views.StringConstantActivityViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> +</beans>
diff --git a/taverna-stringconstant-activity-ui/src/main/resources/stringconstant.png b/taverna-stringconstant-activity-ui/src/main/resources/stringconstant.png new file mode 100644 index 0000000..0810c97 --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/main/resources/stringconstant.png Binary files differ
diff --git a/taverna-stringconstant-activity-ui/src/test/java/net/sf/taverna/t2/activities/stringconstant/views/TestStringConstantContextualView.java b/taverna-stringconstant-activity-ui/src/test/java/net/sf/taverna/t2/activities/stringconstant/views/TestStringConstantContextualView.java new file mode 100644 index 0000000..4555d0c --- /dev/null +++ b/taverna-stringconstant-activity-ui/src/test/java/net/sf/taverna/t2/activities/stringconstant/views/TestStringConstantContextualView.java
@@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.activities.stringconstant.views; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import net.sf.taverna.t2.activities.stringconstant.actions.StringConstantActivityConfigurationAction; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import uk.org.taverna.scufl2.api.activity.Activity; + +public class TestStringConstantContextualView { + Activity activity; + + @Before + public void setup() { + activity = new Activity(); + } + + @Test + @Ignore + public void testGetConfigureAction() throws Exception { + ContextualView view = new StringConstantActivityContextualView( + activity, null, null, null, null, null, null); + assertNotNull("The action should not be null", + view.getConfigureAction(null)); + assertTrue( + "Should be a StringConstantActivityConfigurationAction", + view.getConfigureAction(null) instanceof StringConstantActivityConfigurationAction); + } +}
diff --git a/taverna-unrecognized-activity-ui/pom.xml b/taverna-unrecognized-activity-ui/pom.xml new file mode 100644 index 0000000..eb7a7c6 --- /dev/null +++ b/taverna-unrecognized-activity-ui/pom.xml
@@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna</groupId> + <artifactId>taverna-parent</artifactId> + <version>3.0.1-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>unrecognized-activity-ui</artifactId> + <version>2.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>Taverna 2 Unrecognized Activity UI</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>configuration-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>contextual-views-impl</artifactId> + <version>${t2.ui.impl.version}</version> + </dependency> + </dependencies> + <repositories> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository + </url> + </repository> + <repository> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + <id>mygrid-snapshot-repository</id> + <name>myGrid Snapshot Repository</name> + <url>http://www.mygrid.org.uk/maven/snapshot-repository</url> + </repository> + </repositories> +</project> +
diff --git a/taverna-unrecognized-activity-ui/src/main/java/net/sf/taverna/t2/activities/unrecognized/views/UnrecognizedActivityViewFactory.java b/taverna-unrecognized-activity-ui/src/main/java/net/sf/taverna/t2/activities/unrecognized/views/UnrecognizedActivityViewFactory.java new file mode 100644 index 0000000..9388016 --- /dev/null +++ b/taverna-unrecognized-activity-ui/src/main/java/net/sf/taverna/t2/activities/unrecognized/views/UnrecognizedActivityViewFactory.java
@@ -0,0 +1,48 @@ +package net.sf.taverna.t2.activities.unrecognized.views; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.activity.Activity; + +/** + * This class generates a contextual view for a UnrecognizedActivity + * + * @author alanrw + */ +public class UnrecognizedActivityViewFactory implements ContextualViewFactory<Activity> { + + public static final URI ACTIVITY_TYPE = URI.create("http://ns.taverna.org.uk/2010/activity/unrecognized"); + + private ColourManager colourManager; + + /** + * The factory can handle a UnrecognizedActivity + * + * @param object + * @return + */ + public boolean canHandle(Object object) { + return object instanceof Activity && ((Activity) object).getType().equals(ACTIVITY_TYPE); + } + + /** + * Return a contextual view that can display information about a UnrecognizedActivity + * + * @param activity + * @return + */ + public List<ContextualView> getViews(Activity activity) { + return Arrays.asList(new ContextualView[] { new UnrecognizedContextualView(activity, + colourManager) }); + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + +}
diff --git a/taverna-unrecognized-activity-ui/src/main/java/net/sf/taverna/t2/activities/unrecognized/views/UnrecognizedContextualView.java b/taverna-unrecognized-activity-ui/src/main/java/net/sf/taverna/t2/activities/unrecognized/views/UnrecognizedContextualView.java new file mode 100644 index 0000000..783704c --- /dev/null +++ b/taverna-unrecognized-activity-ui/src/main/java/net/sf/taverna/t2/activities/unrecognized/views/UnrecognizedContextualView.java
@@ -0,0 +1,56 @@ +/** + * + */ +package net.sf.taverna.t2.activities.unrecognized.views; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.ui.actions.activity.HTMLBasedActivityContextualView; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; + +/** + * A UnrecognizedContextualView displays information about a UnrecognizedActivity + * + * @author alanrw + * @author David Withers + */ +@SuppressWarnings("serial") +public class UnrecognizedContextualView extends HTMLBasedActivityContextualView { + + public UnrecognizedContextualView(Activity activity, ColourManager colourManager) { + super(activity, colourManager); + } + + /** + * The table for the UnrecognizedActivity shows its ports. + * + * @return + */ + @Override + protected String getRawTableRowsHtml() { + StringBuilder html = new StringBuilder(); + html.append("<tr><th>Input Port Name</th><th>Depth</th></tr>"); + for (InputActivityPort inputActivityPort : getActivity().getInputPorts()) { + html.append("<tr><td>" + inputActivityPort.getName() + "</td><td>"); + html.append(inputActivityPort.getDepth() + "</td></tr>"); + } + html.append("<tr><th>Output Port Name</th><th>Depth</th></tr>"); + for (OutputActivityPort outputActivityPort : getActivity().getOutputPorts()) { + html.append("<tr><td>" + outputActivityPort.getName() + "</td><td>"); + html.append(outputActivityPort.getDepth() + "</td></tr>"); + } + return html.toString(); + } + + @Override + public String getViewTitle() { + return "Unrecognized service"; + } + + @Override + public int getPreferredPosition() { + return 100; + } + +}
diff --git a/taverna-unrecognized-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-unrecognized-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..0ec5cf1 --- /dev/null +++ b/taverna-unrecognized-activity-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.activities.unrecognized.views.UnrecognizedActivityViewFactory
diff --git a/taverna-unrecognized-activity-ui/src/main/resources/META-INF/spring/unrecognized-activity-ui-context-osgi.xml b/taverna-unrecognized-activity-ui/src/main/resources/META-INF/spring/unrecognized-activity-ui-context-osgi.xml new file mode 100644 index 0000000..e5ec9ac --- /dev/null +++ b/taverna-unrecognized-activity-ui/src/main/resources/META-INF/spring/unrecognized-activity-ui-context-osgi.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="UnrecognizedActivityViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + +</beans:beans>
diff --git a/taverna-unrecognized-activity-ui/src/main/resources/META-INF/spring/unrecognized-activity-ui-context.xml b/taverna-unrecognized-activity-ui/src/main/resources/META-INF/spring/unrecognized-activity-ui-context.xml new file mode 100644 index 0000000..940693e --- /dev/null +++ b/taverna-unrecognized-activity-ui/src/main/resources/META-INF/spring/unrecognized-activity-ui-context.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="UnrecognizedActivityViewFactory" class="net.sf.taverna.t2.activities.unrecognized.views.UnrecognizedActivityViewFactory"> + <property name="colourManager" ref="colourManager" /> + </bean> + +</beans>
diff --git a/taverna-workbench-activity-icons-api/pom.xml b/taverna-workbench-activity-icons-api/pom.xml new file mode 100644 index 0000000..0024571 --- /dev/null +++ b/taverna-workbench-activity-icons-api/pom.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-icons-api</artifactId> + <packaging>bundle</packaging> + <name>Activity icon manager API</name> + + <dependencies> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + </dependencies> +</project> \ No newline at end of file
diff --git a/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconManager.java b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconManager.java new file mode 100644 index 0000000..d2c8fff --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconManager.java
@@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (C) 2011 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.activityicons; + +import java.net.URI; + +import javax.swing.Icon; + +import uk.org.taverna.scufl2.api.activity.Activity; + +/** + * Manager for activities' icons. + * + * @author David Withers + */ +public interface ActivityIconManager { + /** Returns an icon for the Activity. */ + Icon iconForActivity(URI activityType); + + Icon iconForActivity(Activity activity); + + void resetIcon(URI activityType); +}
diff --git a/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconSPI.java b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconSPI.java new file mode 100644 index 0000000..7270dfc --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/ActivityIconSPI.java
@@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.activityicons; + +import java.net.URI; + +import javax.swing.Icon; + +/** + * Defines an interface for getting an icon for an Activity. + * + * @author Alex Nenadic + */ +public interface ActivityIconSPI { + /** + * A return value for {@link canProvideIconScore()} indicating an SPI cannot + * provide an icon for a given activity. + */ + int NO_ICON = 0; + + /** + * {@link DefaultActivityIcon} returns this value that will be used when an + * activity that has no other SPI providing an icon for. Any SPI shour + * return value of at least DEFAULT_ICON + 1 if they want to 'override' the + * default icon. + */ + int DEFAULT_ICON = 10; + + /** + * Returns a positive number if the class can provide an icon for the given + * activity or {@link NO_ICON} otherwise. Out of two SPIs capable of + * providing an icon for the same activity, the one returning a higher score + * will be used. + */ + int canProvideIconScore(URI activityType); + + /** Returns an icon for the Activity. */ + Icon getIcon(URI activityType); +}
diff --git a/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/DefaultActivityIcon.java b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/DefaultActivityIcon.java new file mode 100644 index 0000000..c474e69 --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/DefaultActivityIcon.java
@@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.activityicons; + +import java.net.URI; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +public class DefaultActivityIcon implements ActivityIconSPI { + private static final String ICON_RESOURCE = "/default-activity-icon.png"; + + @Override + public int canProvideIconScore(URI activityType) { + // For any activity we can provide a default icon + return DEFAULT_ICON; + } + + @Override + public Icon getIcon(URI activityType) { + return getDefaultIcon(); + } + + public static Icon getDefaultIcon() { + return IconLoader.icon; + } + + private static class IconLoader { + static final Icon icon = loadDefaultIcon(); + + private static Icon loadDefaultIcon() { + return new ImageIcon( + DefaultActivityIcon.class.getResource(ICON_RESOURCE)); + } + } +}
diff --git a/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/impl/ActivityIconManagerImpl.java b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/impl/ActivityIconManagerImpl.java new file mode 100644 index 0000000..f8294fc --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/java/net/sf/taverna/t2/workbench/activityicons/impl/ActivityIconManagerImpl.java
@@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.activityicons.impl; + +import static net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI.NO_ICON; + +import java.net.URI; +import java.util.List; +import java.util.WeakHashMap; + +import javax.swing.Icon; + +import uk.org.taverna.scufl2.api.activity.Activity; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI; + +/** + * Manager for activities' icons. + * + * @author Alex Nenadic + * @author Alan R Williams + */ +public class ActivityIconManagerImpl implements ActivityIconManager { + /** Cache of already obtained icons; maps activities to their icons*/ + private WeakHashMap<URI, Icon> iconsMap = new WeakHashMap<>(); + + private List<ActivityIconSPI> activityIcons; + + /** Returns an icon for the Activity. */ + @Override + public Icon iconForActivity(URI activityType) { + Icon icon = iconsMap.get(activityType); + if (icon != null) + return icon; + int bestScore = NO_ICON; + ActivityIconSPI bestSPI = null; + for (ActivityIconSPI spi : activityIcons) { + int spiScore = spi.canProvideIconScore(activityType); + if (spiScore > bestScore) { + bestSPI = spi; + bestScore = spiScore; + } + } + if (bestSPI == null) + return null; + icon = bestSPI.getIcon(activityType); + iconsMap.put(activityType, icon); + return icon; + } + + @Override + public Icon iconForActivity(Activity activity) { + return iconForActivity(activity.getType()); + } + + @Override + public void resetIcon(URI activityType) { + Icon icon = iconsMap.get(activityType); + if (icon != null) + iconsMap.remove(activityType); + iconForActivity(activityType); + } + + public void setActivityIcons(List<ActivityIconSPI> activityIcons) { + this.activityIcons = activityIcons; + } +}
diff --git a/taverna-workbench-activity-icons-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI b/taverna-workbench-activity-icons-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI new file mode 100644 index 0000000..d268c81 --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.activityicons.DefaultActivityIcon \ No newline at end of file
diff --git a/taverna-workbench-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context-osgi.xml b/taverna-workbench-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context-osgi.xml new file mode 100644 index 0000000..5c67640 --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context-osgi.xml
@@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="DefaultActivityIcon" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI" /> + + <service ref="ActivityIconManager" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconManager" /> + + <list id="activityIcons" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconSPI" cardinality="0..N" /> + +</beans:beans>
diff --git a/taverna-workbench-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context.xml b/taverna-workbench-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context.xml new file mode 100644 index 0000000..93c98c4 --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/resources/META-INF/spring/activity-icons-api-context.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="DefaultActivityIcon" class="net.sf.taverna.t2.workbench.activityicons.DefaultActivityIcon" /> + + <bean id="ActivityIconManager" class="net.sf.taverna.t2.workbench.activityicons.impl.ActivityIconManagerImpl"> + <property name="activityIcons" ref="activityIcons" /> + </bean> + + +</beans>
diff --git a/taverna-workbench-activity-icons-api/src/main/resources/default-activity-icon.png b/taverna-workbench-activity-icons-api/src/main/resources/default-activity-icon.png new file mode 100644 index 0000000..b7ed3e9 --- /dev/null +++ b/taverna-workbench-activity-icons-api/src/main/resources/default-activity-icon.png Binary files differ
diff --git a/taverna-workbench-activity-palette-api/pom.xml b/taverna-workbench-activity-palette-api/pom.xml new file mode 100644 index 0000000..1c9ab8c --- /dev/null +++ b/taverna-workbench-activity-palette-api/pom.xml
@@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-palette-api</artifactId> + <packaging>bundle</packaging> + <name>Activity Palette API</name> + <description>Activity Palette API</description> + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <systemProperties> + <property> + <name>java.awt.headless</name> + <value>false</value> + </property> + </systemProperties> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>beans</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-api</artifactId> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractConfigurableServiceProvider.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractConfigurableServiceProvider.java new file mode 100644 index 0000000..18cb176 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractConfigurableServiceProvider.java
@@ -0,0 +1,53 @@ +package net.sf.taverna.t2.servicedescriptions; + +import uk.org.taverna.scufl2.api.configurations.Configuration; + +public abstract class AbstractConfigurableServiceProvider extends + IdentifiedObject implements ConfigurableServiceProvider { + protected Configuration serviceProviderConfig; + + /** + * Construct configurable service provider. + * + * @param configTemplate + * Template configuration + */ + public AbstractConfigurableServiceProvider(Configuration configTemplate) { + if (configTemplate == null) + throw new NullPointerException("Default config can't be null"); + serviceProviderConfig = configTemplate; + } + + /** + * Package access constructor - only used with {@link #clone()} - otherwise + * use {@link #AbstractConfigurableServiceProvider(Object)} + */ + AbstractConfigurableServiceProvider() { + } + + @Override + public AbstractConfigurableServiceProvider clone() { + AbstractConfigurableServiceProvider provider = (AbstractConfigurableServiceProvider) newInstance(); + Configuration configuration = getConfiguration(); + if (configuration != null) + provider.configure(configuration); + return provider; + } + + @Override + public synchronized void configure(Configuration conf) { + if (conf == null) + throw new IllegalArgumentException("Config can't be null"); + this.serviceProviderConfig = conf; + } + + @Override + public Configuration getConfiguration() { + return serviceProviderConfig; + } + + @Override + public String toString() { + return getName() + " " + getConfiguration(); + } +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractTemplateService.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractTemplateService.java new file mode 100644 index 0000000..d4909b1 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/AbstractTemplateService.java
@@ -0,0 +1,85 @@ +package net.sf.taverna.t2.servicedescriptions; + +import static java.util.Collections.singleton; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import javax.swing.Icon; + +import uk.org.taverna.scufl2.api.configurations.Configuration; + +public abstract class AbstractTemplateService implements + ServiceDescriptionProvider { + protected TemplateServiceDescription templateService = new TemplateServiceDescription(); + + @Override + public void findServiceDescriptionsAsync( + FindServiceDescriptionsCallBack callBack) { + callBack.partialResults(singleton(templateService)); + callBack.finished(); + } + + @Override + public abstract Icon getIcon(); + + public URI getActivityType() { + return null; + } + + public abstract Configuration getActivityConfiguration(); + + public class TemplateServiceDescription extends ServiceDescription { + @Override + public Icon getIcon() { + return AbstractTemplateService.this.getIcon(); + } + + @Override + public String getName() { + return AbstractTemplateService.this.getName(); + } + + @Override + public List<String> getPath() { + return Arrays.asList(SERVICE_TEMPLATES); + } + + @Override + public boolean isTemplateService() { + return true; + } + + @Override + protected List<Object> getIdentifyingData() { + // Do it by object identity + return null; + } + + @Override + public URI getActivityType() { + return AbstractTemplateService.this.getActivityType(); + } + + @Override + public Configuration getActivityConfiguration() { + return AbstractTemplateService.this.getActivityConfiguration(); + } + + @Override + public String getDescription() { + return AbstractTemplateService.this.getDescription(); + } + } + + @Override + public String toString() { + return "Template service " + getName(); + } + + public String getDescription() { + // Default to an empty string + return ""; + } +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ConfigurableServiceProvider.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ConfigurableServiceProvider.java new file mode 100644 index 0000000..0bf01bd --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ConfigurableServiceProvider.java
@@ -0,0 +1,10 @@ +package net.sf.taverna.t2.servicedescriptions; + +import uk.org.taverna.scufl2.api.common.Configurable; +import uk.org.taverna.scufl2.api.configurations.Configuration; + +public interface ConfigurableServiceProvider extends + ServiceDescriptionProvider, Configurable, Cloneable { + void configure(Configuration configuration) throws Exception; + Configuration getConfiguration(); +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/CustomizedConfigurePanelProvider.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/CustomizedConfigurePanelProvider.java new file mode 100644 index 0000000..8bb5331 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/CustomizedConfigurePanelProvider.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.servicedescriptions; + +import uk.org.taverna.scufl2.api.configurations.Configuration; + +public interface CustomizedConfigurePanelProvider extends + ConfigurableServiceProvider { + void createCustomizedConfigurePanel(CustomizedConfigureCallBack callBack); + + interface CustomizedConfigureCallBack { + void newProviderConfiguration(Configuration providerConfig); + + Configuration getTemplateConfig(); + + ServiceDescriptionRegistry getServiceDescriptionRegistry(); + } +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/IdentifiedObject.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/IdentifiedObject.java new file mode 100644 index 0000000..596f502 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/IdentifiedObject.java
@@ -0,0 +1,30 @@ +package net.sf.taverna.t2.servicedescriptions; + +import java.util.List; + +import net.sf.taverna.t2.lang.beans.PropertyAnnotated; + +public abstract class IdentifiedObject extends PropertyAnnotated { + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IdentifiedObject)) + return false; + List<? extends Object> myIdentifyingData = getIdentifyingData(); + if (myIdentifyingData == null) + return super.equals(obj); + if (!getClass().isInstance(obj) && obj.getClass().isInstance(this)) + return false; + IdentifiedObject id = (IdentifiedObject) obj; + return myIdentifyingData.equals(id.getIdentifyingData()); + } + + @Override + public int hashCode() { + List<? extends Object> identifyingData = getIdentifyingData(); + if (identifyingData == null) + return super.hashCode(); + return identifyingData.hashCode(); + } + + protected abstract List<? extends Object> getIdentifyingData(); +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescription.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescription.java new file mode 100644 index 0000000..8551934 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescription.java
@@ -0,0 +1,80 @@ +package net.sf.taverna.t2.servicedescriptions; + +import java.net.URI; +import java.util.List; + +import javax.swing.Icon; + +import net.sf.taverna.t2.lang.beans.PropertyAnnotation; +import net.sf.taverna.t2.workbench.edits.Edit; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; + +public abstract class ServiceDescription extends IdentifiedObject { + public static final String SERVICE_TEMPLATES = "Service templates"; + private static final String NAME = "Name"; + private static final String SERVICE_CONFIGURATION = "Service configuration"; + private static final String SERVICE_IMPLEMENTATION_URI = "Service implementation URI"; + private static final String DESCRIPTION = "Description"; + public static final String LOCAL_SERVICES = "Local services"; + + private String description = ""; + + @PropertyAnnotation(expert = true, displayName = SERVICE_IMPLEMENTATION_URI) + public abstract URI getActivityType(); + + @PropertyAnnotation(expert = true, displayName = SERVICE_CONFIGURATION) + public Configuration getActivityConfiguration() { + Configuration configuration = new Configuration(); + configuration.setType(getActivityType().resolve("#Config")); + return configuration; + } + + @PropertyAnnotation(displayName = DESCRIPTION) + public String getDescription() { + return this.description; + } + + @PropertyAnnotation(expert = true) + public abstract Icon getIcon(); + + @PropertyAnnotation(displayName = NAME) + public abstract String getName(); + + @PropertyAnnotation(expert = true) + public abstract List<? extends Comparable<?>> getPath(); + + @PropertyAnnotation(hidden = true) + public boolean isTemplateService() { + return false; + } + + /** + * @param description + * the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + @Override + public String toString() { + return "Service description " + getName(); + } + + /** + * Any additional edit that needs to be performed upon insertion of an + * instance of the ServiceDescription into the {@link Workflow} within the + * specified {@link Processor} + * + * @param dataflow + * @param p + * @param a + * @return + */ + public Edit<?> getInsertionEdit(Workflow dataflow, Processor p, Activity a) { + return null; + } +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionProvider.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionProvider.java new file mode 100644 index 0000000..8170819 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionProvider.java
@@ -0,0 +1,61 @@ +package net.sf.taverna.t2.servicedescriptions; + +import java.util.Collection; + +import javax.swing.Icon; + +import net.sf.taverna.t2.lang.beans.PropertyAnnotation; + +/** + * A provider of service descriptions + * + * @author Stian Soiland-Reyes + */ +public interface ServiceDescriptionProvider { + /** + * Get all service descriptions. + * + * @param callBack + */ + void findServiceDescriptionsAsync(FindServiceDescriptionsCallBack callBack); + + /** + * @author stain + */ + interface FindServiceDescriptionsCallBack { + void partialResults( + Collection<? extends ServiceDescription> serviceDescriptions); + + void status(String message); + + void warning(String message); + + void finished(); + + void fail(String message, Throwable ex); + } + + /** + * Name of this service description provider, for instance "BioCatalogue" or + * "WSDL". This name is typically used in a "Add service..." menu. + * + * @return Name of provider + */ + String getName(); + + @PropertyAnnotation(expert = true) + abstract Icon getIcon(); + + /** + * @return unique id of this provider. + */ + String getId(); + + /** + * Create a new copy of this service provider. It <i>need not be + * configured</i> at the point where it is returned. + * + * @return The copy. + */ + ServiceDescriptionProvider newInstance(); +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionRegistry.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionRegistry.java new file mode 100644 index 0000000..e9b6c04 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionRegistry.java
@@ -0,0 +1,50 @@ +package net.sf.taverna.t2.servicedescriptions; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.Set; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent; + +public interface ServiceDescriptionRegistry extends + Observable<ServiceDescriptionRegistryEvent> { + void addServiceDescriptionProvider(ServiceDescriptionProvider provider); + + Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProviders(); + + Set<ServiceDescriptionProvider> getServiceDescriptionProviders(); + + Set<ServiceDescriptionProvider> getServiceDescriptionProviders( + ServiceDescription sd); + + Set<ServiceDescription> getServiceDescriptions(); + + ServiceDescription getServiceDescription(URI activityType); + + List<ConfigurableServiceProvider> getUnconfiguredServiceProviders(); + + Set<ServiceDescriptionProvider> getUserAddedServiceProviders(); + + Set<ServiceDescriptionProvider> getUserRemovedServiceProviders(); + + void loadServiceProviders(); + + void loadServiceProviders(File serviceProvidersURL); + + void loadServiceProviders(URL serviceProvidersURL); + + void refresh(); + + void removeServiceDescriptionProvider(ServiceDescriptionProvider provider); + + void saveServiceDescriptions(); + + void saveServiceDescriptions(File serviceDescriptionsFile); + + void exportCurrentServiceDescriptions(File serviceDescriptionsFile); + + boolean isDefaultSystemConfigurableProvidersLoaded(); +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionsConfiguration.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionsConfiguration.java new file mode 100644 index 0000000..7fbcbfc --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/ServiceDescriptionsConfiguration.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.servicedescriptions; + +import uk.org.taverna.configuration.Configurable; + +/** + * @author David Withers + */ +public interface ServiceDescriptionsConfiguration extends Configurable { + public boolean isIncludeDefaults(); + + public void setIncludeDefaults(boolean includeDefaults); + + public boolean isRemovePermanently(); + + public void setRemovePermanently(boolean removePermanently); +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderEvent.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderEvent.java new file mode 100644 index 0000000..1fd224e --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderEvent.java
@@ -0,0 +1,16 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public abstract class AbstractProviderEvent extends + ServiceDescriptionRegistryEvent { + private final ServiceDescriptionProvider provider; + + public AbstractProviderEvent(ServiceDescriptionProvider provider) { + this.provider = provider; + } + + public ServiceDescriptionProvider getProvider() { + return provider; + } +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderNotification.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderNotification.java new file mode 100644 index 0000000..2cabf90 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AbstractProviderNotification.java
@@ -0,0 +1,18 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class AbstractProviderNotification extends AbstractProviderEvent { + + private final String message; + + public AbstractProviderNotification(ServiceDescriptionProvider provider, String message) { + super(provider); + this.message = message; + } + + public String getMessage() { + return message; + } + +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AddedProviderEvent.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AddedProviderEvent.java new file mode 100644 index 0000000..6e003d7 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/AddedProviderEvent.java
@@ -0,0 +1,10 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class AddedProviderEvent extends AbstractProviderEvent { + + public AddedProviderEvent(ServiceDescriptionProvider provider) { + super(provider); + } +} \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/PartialServiceDescriptionsNotification.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/PartialServiceDescriptionsNotification.java new file mode 100644 index 0000000..3bd8c7f --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/PartialServiceDescriptionsNotification.java
@@ -0,0 +1,22 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import java.util.Collection; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class PartialServiceDescriptionsNotification extends + AbstractProviderNotification { + private final Collection<? extends ServiceDescription> serviceDescriptions; + + public PartialServiceDescriptionsNotification( + ServiceDescriptionProvider provider, + Collection<? extends ServiceDescription> serviceDescriptions) { + super(provider, "Found " + serviceDescriptions.size() + " services"); + this.serviceDescriptions = serviceDescriptions; + } + + public Collection<? extends ServiceDescription> getServiceDescriptions() { + return serviceDescriptions; + } +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderErrorNotification.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderErrorNotification.java new file mode 100644 index 0000000..a712124 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderErrorNotification.java
@@ -0,0 +1,19 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class ProviderErrorNotification extends AbstractProviderNotification { + + private final Throwable cause; + + public ProviderErrorNotification(ServiceDescriptionProvider provider, + String message, Throwable cause) { + super(provider, message); + this.cause = cause; + } + + public Throwable getCause() { + return cause; + } + +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderStatusNotification.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderStatusNotification.java new file mode 100644 index 0000000..f094e47 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderStatusNotification.java
@@ -0,0 +1,12 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class ProviderStatusNotification extends AbstractProviderNotification { + + public ProviderStatusNotification(ServiceDescriptionProvider provider, + String message) { + super(provider, message); + } + +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderUpdatingNotification.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderUpdatingNotification.java new file mode 100644 index 0000000..e2947e1 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderUpdatingNotification.java
@@ -0,0 +1,11 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class ProviderUpdatingNotification extends AbstractProviderNotification { + + public ProviderUpdatingNotification(ServiceDescriptionProvider provider) { + super(provider, "Updating"); + } + +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderWarningNotification.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderWarningNotification.java new file mode 100644 index 0000000..d7476aa --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ProviderWarningNotification.java
@@ -0,0 +1,12 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class ProviderWarningNotification extends AbstractProviderNotification { + + public ProviderWarningNotification(ServiceDescriptionProvider provider, + String message) { + super(provider, message); + } + +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/RemovedProviderEvent.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/RemovedProviderEvent.java new file mode 100644 index 0000000..a382bdf --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/RemovedProviderEvent.java
@@ -0,0 +1,10 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class RemovedProviderEvent extends AbstractProviderEvent { + + public RemovedProviderEvent(ServiceDescriptionProvider provider) { + super(provider); + } +} \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionProvidedEvent.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionProvidedEvent.java new file mode 100644 index 0000000..76ef22d --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionProvidedEvent.java
@@ -0,0 +1,20 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +import java.util.Set; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; + +public class ServiceDescriptionProvidedEvent extends AbstractProviderEvent { + private final Set<ServiceDescription> serviceDescriptions; + + public ServiceDescriptionProvidedEvent(ServiceDescriptionProvider provider, + Set<ServiceDescription> serviceDescriptions) { + super(provider); + this.serviceDescriptions = serviceDescriptions; + } + + public Set<ServiceDescription> getServiceDescriptions() { + return serviceDescriptions; + } +}
diff --git a/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionRegistryEvent.java b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionRegistryEvent.java new file mode 100644 index 0000000..0604510 --- /dev/null +++ b/taverna-workbench-activity-palette-api/src/main/java/net/sf/taverna/t2/servicedescriptions/events/ServiceDescriptionRegistryEvent.java
@@ -0,0 +1,4 @@ +package net.sf.taverna.t2.servicedescriptions.events; + +public abstract class ServiceDescriptionRegistryEvent { +}
diff --git a/taverna-workbench-activity-palette-impl/pom.xml b/taverna-workbench-activity-palette-impl/pom.xml new file mode 100644 index 0000000..4926a94 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/pom.xml
@@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>activity-palette-impl</artifactId> + <packaging>bundle</packaging> + <name>Activity Palette Impl</name> + <description>Activity Palette Implementation</description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-palette-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.core</groupId> + <artifactId>workflowmodel-api</artifactId> + <version>${t2.core.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + <dependency> + <groupId>org.jdom</groupId> + <artifactId>com.springsource.org.jdom</artifactId> + </dependency> + + <!-- TODO Remove non-test impl dependency --> + <dependency> + <groupId>net.sf.taverna.t2.core</groupId> + <artifactId>workflowmodel-impl</artifactId> + <version>${t2.core.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-impl</artifactId> + <version>${taverna.configuration.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-impl</artifactId> + <version>${taverna.configuration.version}</version> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionConstants.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionConstants.java new file mode 100644 index 0000000..c5221be --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionConstants.java
@@ -0,0 +1,10 @@ +package net.sf.taverna.t2.servicedescriptions.impl; + +public interface ServiceDescriptionConstants { + String SERVICE_PANEL_CONFIGURATION = "servicePanelConfiguration"; + String PROVIDERS = "providers"; + String IGNORED = "ignored"; + String PROVIDER_ID = "providerID"; + String CONFIGURATION = "configuration"; + String TYPE = "type"; +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java new file mode 100644 index 0000000..0a40bda --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionDeserializer.java
@@ -0,0 +1,167 @@ +package net.sf.taverna.t2.servicedescriptions.impl; + +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.CONFIGURATION; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.IGNORED; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDERS; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDER_ID; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.TYPE; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import uk.org.taverna.scufl2.api.configurations.Configuration; +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +class ServiceDescriptionDeserializer { + private List<ServiceDescriptionProvider> serviceDescriptionProviders; + + ServiceDescriptionDeserializer( + List<ServiceDescriptionProvider> serviceDescriptionProviders) { + this.serviceDescriptionProviders = serviceDescriptionProviders; + } + + public void deserialize(ServiceDescriptionRegistry registry, + File serviceDescriptionsFile) throws DeserializationException { + try (FileInputStream serviceDescriptionFileStream = new FileInputStream( + serviceDescriptionsFile)) { + deserialize(registry, serviceDescriptionFileStream); + } catch (FileNotFoundException ex) { + throw new DeserializationException("Could not locate file " + + serviceDescriptionsFile.getAbsolutePath() + + " containing service descriptions."); + } catch (IOException ex) { + throw new DeserializationException( + "Could not read stream containing service descriptions from " + + serviceDescriptionsFile.getAbsolutePath(), ex); + } + } + + public void deserialize(ServiceDescriptionRegistry registry, + URL serviceDescriptionsURL) throws DeserializationException { + try (InputStream serviceDescriptionInputStream = serviceDescriptionsURL + .openStream()) { + deserialize(registry, serviceDescriptionInputStream); + } catch (FileNotFoundException ex) { + throw new DeserializationException("Could not open URL " + + serviceDescriptionsURL + + " containing service descriptions."); + } catch (IOException ex) { + throw new DeserializationException( + "Could not read stream containing service descriptions from " + + serviceDescriptionsURL, ex); + } + } + + private static final JsonFactory factory = new JsonFactory(); + + private void deserialize(ServiceDescriptionRegistry registry, + InputStream serviceDescriptionsInputStream) throws IOException, + DeserializationException { + ObjectNode node = (ObjectNode) new ObjectMapper(factory) + .readTree(serviceDescriptionsInputStream); + List<ServiceDescriptionProvider> providers = deserializeProviders(node, + true); + for (ServiceDescriptionProvider provider : providers) + registry.addServiceDescriptionProvider(provider); + } + + public Collection<? extends ServiceDescriptionProvider> deserializeDefaults( + ServiceDescriptionRegistry registry, + File defaultConfigurableServiceProvidersFile) + throws DeserializationException { + ObjectNode node; + try (FileInputStream serviceDescriptionStream = new FileInputStream( + defaultConfigurableServiceProvidersFile)) { + node = (ObjectNode) new ObjectMapper(factory) + .readTree(serviceDescriptionStream); + } catch (IOException e) { + throw new DeserializationException("Can't read " + + defaultConfigurableServiceProvidersFile); + } + return deserializeProviders(node, false); + } + + private List<ServiceDescriptionProvider> deserializeProviders( + ObjectNode rootNode, boolean obeyIgnored) + throws DeserializationException { + List<ServiceDescriptionProvider> providers = new ArrayList<>(); + + ArrayNode providersNode = (ArrayNode) rootNode.get(PROVIDERS); + if (providersNode != null) + for (JsonNode provider : providersNode) + providers.add(deserializeProvider((ObjectNode) provider)); + + if (obeyIgnored) { + ArrayNode ignoredNode = (ArrayNode) rootNode.get(IGNORED); + if (ignoredNode != null) + for (JsonNode provider : ignoredNode) + providers + .remove(deserializeProvider((ObjectNode) provider)); + } + + return providers; + } + + private ServiceDescriptionProvider deserializeProvider( + ObjectNode providerNode) throws DeserializationException { + String providerId = providerNode.get(PROVIDER_ID).asText().trim(); + ServiceDescriptionProvider provider = null; + for (ServiceDescriptionProvider serviceProvider : serviceDescriptionProviders) + if (serviceProvider.getId().equals(providerId)) { + provider = serviceProvider; + break; + } + if (provider == null) + throw new DeserializationException( + "Could not find provider with id " + providerId); + + /* + * So we know the service provider now, but we need a separate instance + * of that provider for each providerElem. E.g. we can have 2 or more + * WSDL provider elements and need to return a separate provider + * instance for each as they will have different configurations. + */ + ServiceDescriptionProvider instance = provider.newInstance(); + + if (instance instanceof ConfigurableServiceProvider) + try { + Configuration config = new Configuration(); + config.setType(URI.create(providerNode.get(TYPE).textValue())); + config.setJson(providerNode.get(CONFIGURATION)); + if (config != null) + ((ConfigurableServiceProvider) instance).configure(config); + } catch (Exception e) { + throw new DeserializationException( + "Could not configure provider " + providerId + + " using bean " + providerNode, e); + } + return instance; + } + + @SuppressWarnings("serial") + static class DeserializationException extends Exception { + public DeserializationException(String string) { + super(string); + } + + public DeserializationException(String string, Exception ex) { + super(string, ex); + } + } +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java new file mode 100644 index 0000000..9dc3f00 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionRegistryImpl.java
@@ -0,0 +1,652 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.servicedescriptions.impl; + +import static java.lang.System.currentTimeMillis; +import static java.lang.Thread.MIN_PRIORITY; +import static java.lang.Thread.currentThread; + +import java.io.File; +import java.io.IOException; +import java.lang.Thread.UncaughtExceptionHandler; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.sf.taverna.t2.lang.observer.MultiCaster; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider.FindServiceDescriptionsCallBack; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.servicedescriptions.events.AddedProviderEvent; +import net.sf.taverna.t2.servicedescriptions.events.PartialServiceDescriptionsNotification; +import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification; +import net.sf.taverna.t2.servicedescriptions.events.ProviderStatusNotification; +import net.sf.taverna.t2.servicedescriptions.events.ProviderUpdatingNotification; +import net.sf.taverna.t2.servicedescriptions.events.ProviderWarningNotification; +import net.sf.taverna.t2.servicedescriptions.events.RemovedProviderEvent; +import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent; +import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent; +import net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionDeserializer.DeserializationException; + +import org.apache.commons.beanutils.BeanUtils; +import org.apache.log4j.Logger; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; + +public class ServiceDescriptionRegistryImpl implements ServiceDescriptionRegistry { + /** + * If a writable property of this name on a provider exists (ie. the provider has a method + * setServiceDescriptionRegistry(ServiceDescriptionRegistry registry) - then this property will + * be set to the current registry. + */ + public static final String SERVICE_DESCRIPTION_REGISTRY = "serviceDescriptionRegistry"; + public static Logger logger = Logger.getLogger(ServiceDescriptionRegistryImpl.class); + public static final ThreadGroup threadGroup = new ThreadGroup("Service description providers"); + /** + * Total maximum timeout while waiting for description threads to finish + */ + private static final long DESCRIPTION_THREAD_TIMEOUT_MS = 3000; + protected static final String CONF_DIR = "conf"; + protected static final String SERVICE_PROVIDERS_FILENAME = "service_providers.xml"; + private static final String DEFAULT_CONFIGURABLE_SERVICE_PROVIDERS_FILENAME = "default_service_providers.xml"; + + private ServiceDescriptionsConfiguration serviceDescriptionsConfig; + private ApplicationConfiguration applicationConfiguration; + /** + * <code>false</code> until first call to {@link #loadServiceProviders()} - which is done by + * first call to {@link #getServiceDescriptionProviders()}. + */ + private boolean hasLoadedProviders = false; + /** + * <code>true</code> while {@link #loadServiceProviders(File)}, + * {@link #loadServiceProviders(URL)} or {@link #loadServiceProviders()} is in progress, avoids + * triggering {@link #saveServiceDescriptions()} on + * {@link #addServiceDescriptionProvider(ServiceDescriptionProvider)} calls. + */ + private boolean loading = false; + private MultiCaster<ServiceDescriptionRegistryEvent> observers = new MultiCaster<>(this); + private List<ServiceDescriptionProvider> serviceDescriptionProviders; + private Set<ServiceDescriptionProvider> allServiceProviders; + private Map<ServiceDescriptionProvider, Set<ServiceDescription>> providerDescriptions = new HashMap<>(); + private Map<ServiceDescriptionProvider, Thread> serviceDescriptionThreads = new HashMap<>(); + /** + * Service providers added by the user, should be saved + */ + private Set<ServiceDescriptionProvider> userAddedProviders = new HashSet<>(); + private Set<ServiceDescriptionProvider> userRemovedProviders = new HashSet<>(); + private Set<ServiceDescriptionProvider> defaultServiceDescriptionProviders; + /** + * File containing a list of configured ConfigurableServiceProviders which is used to get the + * default set of service descriptions together with those provided by AbstractTemplateServiceS. + * This file is located in the conf directory of the Taverna startup directory. + */ + private File defaultConfigurableServiceProvidersFile; + private boolean defaultSystemConfigurableProvidersLoaded = false; + + static { + threadGroup.setMaxPriority(MIN_PRIORITY); + } + + public ServiceDescriptionRegistryImpl( + ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + defaultConfigurableServiceProvidersFile = new File( + getTavernaStartupConfigurationDirectory(), + DEFAULT_CONFIGURABLE_SERVICE_PROVIDERS_FILENAME); + } + + /** + * Get the Taverna distribution (startup) configuration directory. + */ + private File getTavernaStartupConfigurationDirectory() { + File distroHome = null; + File configDirectory = null; + distroHome = applicationConfiguration.getStartupDir(); + configDirectory = new File(distroHome, "conf"); + if (!configDirectory.exists()) + configDirectory.mkdir(); + return configDirectory; + } + + private static void joinThreads(Collection<? extends Thread> threads, + long descriptionThreadTimeoutMs) { + long finishJoinBy = currentTimeMillis() + descriptionThreadTimeoutMs; + for (Thread thread : threads) { + // No shorter timeout than 1 ms (thread.join(0) waits forever!) + long timeout = Math.max(1, finishJoinBy - currentTimeMillis()); + try { + thread.join(timeout); + } catch (InterruptedException e) { + currentThread().interrupt(); + return; + } + if (thread.isAlive()) + logger.debug("Thread did not finish " + thread); + } + } + + + @Override + public void addObserver(Observer<ServiceDescriptionRegistryEvent> observer) { + observers.addObserver(observer); + } + + @Override + public void addServiceDescriptionProvider(ServiceDescriptionProvider provider) { + synchronized (this) { + userRemovedProviders.remove(provider); + if (!getDefaultServiceDescriptionProviders().contains(provider)) + userAddedProviders.add(provider); + allServiceProviders.add(provider); + } + + // Spring-like auto-config + try { + // BeanUtils should ignore this if provider does not have that property + BeanUtils.setProperty(provider, SERVICE_DESCRIPTION_REGISTRY, this); + } catch (IllegalAccessException | InvocationTargetException e) { + logger.warn("Could not set serviceDescriptionRegistry on " + + provider, e); + } + + if (!loading) + saveServiceDescriptions(); + observers.notify(new AddedProviderEvent(provider)); + updateServiceDescriptions(false, false); + } + + private File findServiceDescriptionsFile() { + File confDir = new File( + applicationConfiguration.getApplicationHomeDir(), CONF_DIR); + confDir.mkdirs(); + if (!confDir.isDirectory()) + throw new RuntimeException("Invalid directory: " + confDir); + File serviceDescriptionsFile = new File(confDir, + SERVICE_PROVIDERS_FILENAME); + return serviceDescriptionsFile; + } + + @Override + public List<Observer<ServiceDescriptionRegistryEvent>> getObservers() { + return observers.getObservers(); + } + + // Fallback to this method that uses hardcoded default services if you cannot read them from + // the file. +// @SuppressWarnings("unchecked") +// public synchronized Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProvidersFallback() { +// /*if (defaultServiceDescriptionProviders != null) { +// return defaultServiceDescriptionProviders; +// } +// defaultServiceDescriptionProviders = new HashSet<ServiceDescriptionProvider>(); +// */ +// for (ServiceDescriptionProvider provider : serviceDescriptionProviders) { +// +// /* We do not need these - already loaded them from getDefaultServiceDescriptionProviders() +// if (!(provider instanceof ConfigurableServiceProvider)) { +// defaultServiceDescriptionProviders.add(provider); +// continue; +// }*/ +// +// // Just load the hard coded default configurable service providers +// if (provider instanceof ConfigurableServiceProvider){ +// ConfigurableServiceProvider<Object> template = ((ConfigurableServiceProvider<Object>) +// provider); +// // Get configurations +// List<Object> configurables = template.getDefaultConfigurations(); +// for (Object config : configurables) { +// // Make a copy that we can configure +// ConfigurableServiceProvider<Object> configurableProvider = template.clone(); +// try { +// configurableProvider.configure(config); +// } catch (ConfigurationException e) { +// logger.warn("Can't configure provider " +// + configurableProvider + " with " + config); +// continue; +// } +// defaultServiceDescriptionProviders.add(configurableProvider); +// } +// } +// } +// return defaultServiceDescriptionProviders; +// } + + // Get the default services. + @Override + public synchronized Set<ServiceDescriptionProvider> getDefaultServiceDescriptionProviders() { + if (defaultServiceDescriptionProviders != null) + return defaultServiceDescriptionProviders; + defaultServiceDescriptionProviders = new HashSet<>(); + + /* + * Add default configurable service description providers from the + * default_service_providers.xml file + */ + if (defaultConfigurableServiceProvidersFile.exists()) { + try { + ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer( + serviceDescriptionProviders); + defaultServiceDescriptionProviders.addAll(deserializer + .deserializeDefaults(this, + defaultConfigurableServiceProvidersFile)); + /* + * We have successfully loaded the defaults for system + * configurable providers. Note that there are still defaults + * for third party configurable providers, which will be loaded + * below using getDefaultConfigurations(). + */ + defaultSystemConfigurableProvidersLoaded = true; + } catch (Exception e) { + logger.error("Could not load default service providers from " + + defaultConfigurableServiceProvidersFile.getAbsolutePath(), e); + + /* + * Fallback on the old hardcoded method of loading default + * system configurable service providers using + * getDefaultConfigurations(). + */ + defaultSystemConfigurableProvidersLoaded = false; + } + } else { + logger.warn("Could not find the file " + + defaultConfigurableServiceProvidersFile.getAbsolutePath() + + " containing default system service providers. " + + "Using the hardcoded list of default system providers."); + + /* + * Fallback on the old hardcoded method of loading default system + * configurable service providers using getDefaultConfigurations(). + */ + defaultSystemConfigurableProvidersLoaded = false; + } + + /* + * Load other default service description providers - template, local + * workers and third party configurable service providers + */ + for (ServiceDescriptionProvider provider : serviceDescriptionProviders) { + /* + * Template service providers (beanshell, string constant, etc. ) + * and providers of local workers. + */ + if (!(provider instanceof ConfigurableServiceProvider)) { + defaultServiceDescriptionProviders.add(provider); + continue; + } + + /* + * Default system or third party configurable service description + * provider. System ones are read from the + * default_service_providers.xml file so getDefaultConfigurations() + * on them will not have much effect here unless + * defaultSystemConfigurableProvidersLoaded is set to false. + */ + //FIXME needs to be designed to work using Configuration instances + //FIXME needs to get configurations via OSGi discovery + /* + ConfigurableServiceProvider template = (ConfigurableServiceProvider) provider; + // Get configurations + for (ObjectNode config : template.getDefaultConfigurations()) { + // Make a copy that we can configure + ConfigurableServiceProvider configurableProvider = template.clone(); + try { + configurableProvider.configure(config); + } catch (ConfigurationException e) { + logger.warn("Can't configure provider " + + configurableProvider + " with " + config); + continue; + } + defaultServiceDescriptionProviders.add(configurableProvider); + } + */ + } + + return defaultServiceDescriptionProviders; + } + + @Override + public synchronized Set<ServiceDescriptionProvider> getServiceDescriptionProviders() { + if (allServiceProviders != null) + return new HashSet<>(allServiceProviders); + allServiceProviders = new HashSet<>(userAddedProviders); + synchronized (this) { + if (!hasLoadedProviders) + try { + loadServiceProviders(); + } catch (Exception e) { + logger.error("Could not load service providers", e); + } finally { + hasLoadedProviders = true; + } + } + for (ServiceDescriptionProvider provider : getDefaultServiceDescriptionProviders()) { + if (userRemovedProviders.contains(provider)) + continue; + if (provider instanceof ConfigurableServiceProvider + && !serviceDescriptionsConfig.isIncludeDefaults()) + // We'll skip the default configurable service provders + continue; + allServiceProviders.add(provider); + } + return new HashSet<>(allServiceProviders); + } + + @Override + public Set<ServiceDescriptionProvider> getServiceDescriptionProviders( + ServiceDescription sd) { + Set<ServiceDescriptionProvider> result = new HashSet<>(); + for (ServiceDescriptionProvider sdp : providerDescriptions.keySet()) + if (providerDescriptions.get(sdp).contains(sd)) + result.add(sdp); + return result; + } + + @Override + public Set<ServiceDescription> getServiceDescriptions() { + updateServiceDescriptions(false, true); + Set<ServiceDescription> serviceDescriptions = new HashSet<>(); + synchronized (providerDescriptions) { + for (Set<ServiceDescription> providerDesc : providerDescriptions + .values()) + serviceDescriptions.addAll(providerDesc); + } + return serviceDescriptions; + } + + @Override + public ServiceDescription getServiceDescription(URI serviceType) { + for (ServiceDescription serviceDescription : getServiceDescriptions()) + if (serviceDescription.getActivityType().equals(serviceType)) + return serviceDescription; + return null; + } + + @Override + public List<ConfigurableServiceProvider> getUnconfiguredServiceProviders() { + List<ConfigurableServiceProvider> providers = new ArrayList<>(); + for (ServiceDescriptionProvider provider : serviceDescriptionProviders) + if (provider instanceof ConfigurableServiceProvider) + providers.add((ConfigurableServiceProvider) provider); + return providers; + } + + @Override + public Set<ServiceDescriptionProvider> getUserAddedServiceProviders() { + return new HashSet<>(userAddedProviders); + } + + @Override + public Set<ServiceDescriptionProvider> getUserRemovedServiceProviders() { + return new HashSet<>(userRemovedProviders); + } + + @Override + public void loadServiceProviders() { + File serviceProviderFile = findServiceDescriptionsFile(); + if (serviceProviderFile.isFile()) + loadServiceProviders(serviceProviderFile); + hasLoadedProviders = true; + } + + @Override + public void loadServiceProviders(File serviceProvidersFile) { + ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer( + serviceDescriptionProviders); + loading = true; + try { + deserializer.deserialize(this, serviceProvidersFile); + } catch (DeserializationException e) { + logger.error("failed to deserialize configuration", e); + } + loading = false; + } + + @Override + public void loadServiceProviders(URL serviceProvidersURL) { + ServiceDescriptionDeserializer deserializer = new ServiceDescriptionDeserializer( + serviceDescriptionProviders); + loading = true; + try { + deserializer.deserialize(this, serviceProvidersURL); + } catch (DeserializationException e) { + logger.error("failed to deserialize configuration", e); + } + loading = false; + } + + @Override + public void refresh() { + updateServiceDescriptions(true, false); + } + + @Override + public void removeObserver(Observer<ServiceDescriptionRegistryEvent> observer) { + observers.removeObserver(observer); + } + + @Override + public synchronized void removeServiceDescriptionProvider( + ServiceDescriptionProvider provider) { + if (!userAddedProviders.remove(provider)) + // Not previously added - must be a default one.. but should we remove it? + if (loading || serviceDescriptionsConfig.isRemovePermanently() + && serviceDescriptionsConfig.isIncludeDefaults()) + userRemovedProviders.add(provider); + if (allServiceProviders.remove(provider)) { + synchronized (providerDescriptions) { + Thread thread = serviceDescriptionThreads.remove(provider); + if (thread != null) + thread.interrupt(); + providerDescriptions.remove(provider); + } + observers.notify(new RemovedProviderEvent(provider)); + } + if (!loading) + saveServiceDescriptions(); + } + + @Override + public void saveServiceDescriptions() { + File serviceDescriptionsFile = findServiceDescriptionsFile(); + saveServiceDescriptions(serviceDescriptionsFile); + } + + @Override + public void saveServiceDescriptions(File serviceDescriptionsFile) { + ServiceDescriptionSerializer serializer = new ServiceDescriptionSerializer(); + try { + serializer.serializeRegistry(this, serviceDescriptionsFile); + } catch (IOException e) { + throw new RuntimeException("Can't save service descriptions to " + + serviceDescriptionsFile); + } + } + + /** + * Exports all configurable service providers (that give service + * descriptions) currently found in the Service Registry (apart from service + * templates and local services) regardless of who added them (user or + * default system providers). + * <p> + * Unlike {@link #saveServiceDescriptions}, this export does not have the + * "ignored providers" section as this is just a plain export of everything + * in the Service Registry. + * + * @param serviceDescriptionsFile + */ + @Override + public void exportCurrentServiceDescriptions(File serviceDescriptionsFile) { + ServiceDescriptionSerializer serializer = new ServiceDescriptionSerializer(); + try { + serializer.serializeFullRegistry(this, serviceDescriptionsFile); + } catch (IOException e) { + throw new RuntimeException("Could not save service descriptions to " + + serviceDescriptionsFile); + } + } + + public void setServiceDescriptionProvidersList( + List<ServiceDescriptionProvider> serviceDescriptionProviders) { + this.serviceDescriptionProviders = serviceDescriptionProviders; + } + + private void updateServiceDescriptions(boolean refreshAll, boolean waitFor) { + List<Thread> threads = new ArrayList<>(); + for (ServiceDescriptionProvider provider : getServiceDescriptionProviders()) { + synchronized (providerDescriptions) { + if (providerDescriptions.containsKey(provider) && !refreshAll) + // We'll used the cached values + continue; + Thread oldThread = serviceDescriptionThreads.get(provider); + if (oldThread != null && oldThread.isAlive()) { + if (refreshAll) + // New thread will override the old thread + oldThread.interrupt(); + else { + // observers.notify(new ProviderStatusNotification(provider, "Waiting for provider")); + continue; + } + } + // Not run yet - we'll start a new tread + Thread thread = new FindServiceDescriptionsThread(provider); + threads.add(thread); + serviceDescriptionThreads.put(provider, thread); + thread.start(); + } + } + if (waitFor) + joinThreads(threads, DESCRIPTION_THREAD_TIMEOUT_MS); + } + + @Override + public boolean isDefaultSystemConfigurableProvidersLoaded() { + return defaultSystemConfigurableProvidersLoaded; + } + + /** + * Sets the serviceDescriptionsConfig. + * + * @param serviceDescriptionsConfig + * the new value of serviceDescriptionsConfig + */ + public void setServiceDescriptionsConfig( + ServiceDescriptionsConfiguration serviceDescriptionsConfig) { + this.serviceDescriptionsConfig = serviceDescriptionsConfig; + } + + class FindServiceDescriptionsThread extends Thread implements + UncaughtExceptionHandler, FindServiceDescriptionsCallBack { + private final ServiceDescriptionProvider provider; + private boolean aborting = false; + private final Set<ServiceDescription> providerDescs = new HashSet<>(); + + FindServiceDescriptionsThread(ServiceDescriptionProvider provider) { + super(threadGroup, "Find service descriptions from " + provider); + this.provider = provider; + setUncaughtExceptionHandler(this); + setDaemon(true); + } + + @Override + public void fail(String message, Throwable ex) { + logger.warn("Provider " + getProvider() + ": " + message, ex); + if (aborting) + return; + observers.notify(new ProviderErrorNotification(getProvider(), + message, ex)); + } + + @Override + public void finished() { + if (aborting) + return; + synchronized (providerDescriptions) { + providerDescriptions.put(getProvider(), providerDescs); + } + observers.notify(new ServiceDescriptionProvidedEvent(getProvider(), + providerDescs)); + } + + @Override + public void partialResults( + Collection<? extends ServiceDescription> serviceDescriptions) { + if (aborting) + return; + providerDescs.addAll(serviceDescriptions); + synchronized (providerDescriptions) { + providerDescriptions.put(getProvider(), providerDescs); + } + observers.notify(new PartialServiceDescriptionsNotification( + getProvider(), serviceDescriptions)); + } + + @Override + public void status(String message) { + logger.debug("Provider " + getProvider() + ": " + message); + if (aborting) + return; + observers.notify(new ProviderStatusNotification(getProvider(), + message)); + } + + @Override + public void warning(String message) { + logger.warn("Provider " + getProvider() + ": " + message); + if (aborting) + return; + observers.notify(new ProviderWarningNotification(getProvider(), + message)); + } + + public ServiceDescriptionProvider getProvider() { + return provider; + } + + @Override + public void interrupt() { + aborting = true; + super.interrupt(); + } + + @Override + public void run() { + observers.notify(new ProviderUpdatingNotification(provider)); + getProvider().findServiceDescriptionsAsync(this); + } + + @Override + public void uncaughtException(Thread t, Throwable ex) { + logger.error("Uncaught exception in " + t, ex); + fail("Uncaught exception", ex); + } + } +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java new file mode 100644 index 0000000..8a047a3 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionSerializer.java
@@ -0,0 +1,102 @@ +package net.sf.taverna.t2.servicedescriptions.impl; + +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.CONFIGURATION; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.IGNORED; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDERS; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.PROVIDER_ID; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.SERVICE_PANEL_CONFIGURATION; +import static net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionConstants.TYPE; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Set; + +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +import org.apache.log4j.Logger; +import org.jdom.JDOMException; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +class ServiceDescriptionSerializer { + private static Logger logger = Logger + .getLogger(ServiceDescriptionSerializer.class); + + public void serializeRegistry(ServiceDescriptionRegistry registry, File file) + throws IOException { + Set<ServiceDescriptionProvider> ignoreProviders = registry + .getUserRemovedServiceProviders(); + JsonNode registryElement = serializeRegistry(registry, ignoreProviders); + try (BufferedOutputStream bufferedOutStream = new BufferedOutputStream( + new FileOutputStream(file))) { + bufferedOutStream.write(registryElement.toString() + .getBytes("UTF-8")); + } + } + + /** + * Export the whole service registry to an xml file, regardless of who added + * the service provider (user or system default). In this case there will be + * no "ignored providers" in the saved file. + */ + public void serializeFullRegistry(ServiceDescriptionRegistry registry, + File file) throws IOException { + JsonNode registryElement = serializeRegistry(registry, ALL_PROVIDERS); + try (BufferedOutputStream bufferedOutStream = new BufferedOutputStream( + new FileOutputStream(file))) { + bufferedOutStream.write(registryElement.toString() + .getBytes("UTF-8")); + } + } + + private static final JsonNodeFactory factory = JsonNodeFactory.instance; + private static final Set<ServiceDescriptionProvider> ALL_PROVIDERS = null; + + private JsonNode serializeRegistry(ServiceDescriptionRegistry registry, + Set<ServiceDescriptionProvider> ignoreProviders) { + ObjectNode overallConfiguration = factory.objectNode(); + overallConfiguration.put(SERVICE_PANEL_CONFIGURATION, + ignoreProviders != ALL_PROVIDERS ? "full" : "defaults only"); + ArrayNode providers = overallConfiguration.putArray(PROVIDERS); + + for (ServiceDescriptionProvider provider : registry + .getUserAddedServiceProviders()) + try { + providers.add(serializeProvider(provider)); + } catch (JDOMException | IOException e) { + logger.warn("Could not serialize " + provider, e); + } + + if (ignoreProviders != ALL_PROVIDERS) { + ArrayNode ignored = overallConfiguration.putArray(IGNORED); + for (ServiceDescriptionProvider provider : ignoreProviders) + try { + ignored.add(serializeProvider(provider)); + } catch (JDOMException | IOException e) { + logger.warn("Could not serialize " + provider, e); + } + } + + return overallConfiguration; + } + + private JsonNode serializeProvider(ServiceDescriptionProvider provider) + throws JDOMException, IOException { + ObjectNode node = factory.objectNode(); + node.put(PROVIDER_ID, provider.getId()); + + if (provider instanceof ConfigurableServiceProvider) { + ConfigurableServiceProvider configurable = (ConfigurableServiceProvider) provider; + node.put(TYPE, configurable.getConfiguration().getType().toString()); + node.put(CONFIGURATION, configurable.getConfiguration().getJson()); + } + return node; + } +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java new file mode 100644 index 0000000..ee180a7 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionXMLConstants.java
@@ -0,0 +1,15 @@ +package net.sf.taverna.t2.servicedescriptions.impl; + +import org.jdom.Namespace; + +public interface ServiceDescriptionXMLConstants { + + public static final Namespace SERVICE_DESCRIPTION_NS = Namespace + .getNamespace("http://taverna.sf.net/2009/xml/servicedescription"); + public static final String PROVIDER = "provider"; + public static final String PROVIDERS = "providers"; + public static final String SERVICE_DESCRIPTIONS = "serviceDescriptions"; + public static final String IGNORED_PROVIDERS = "ignoredProviders"; + public static final String PROVIDER_IDENTIFIER = "providerId"; + +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java new file mode 100644 index 0000000..ef0295c --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/servicedescriptions/impl/ServiceDescriptionsConfigurationImpl.java
@@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.servicedescriptions.impl; + +import java.util.HashMap; +import java.util.Map; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration; + +import uk.org.taverna.configuration.AbstractConfigurable; +import uk.org.taverna.configuration.ConfigurationManager; + +public class ServiceDescriptionsConfigurationImpl extends AbstractConfigurable + implements ServiceDescriptionsConfiguration { + private static final String INCLUDE_DEFAULTS = "includeDefaults"; + private static final String SERVICE_PALETTE = "Service providers"; + private static final String SERVICE_PALETTE_PREFIX = "ServiceProviders"; + private static final String CATEGORY = "Services"; + private static final String UUID = "f0d1ef24-9337-412f-b2c3-220a01e2efd0"; + private static final String REMOVE_PERMANENTLY = "removePermanently"; + + public ServiceDescriptionsConfigurationImpl( + ConfigurationManager configurationManager) { + super(configurationManager); + } + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Map<String, String> getDefaultPropertyMap() { + Map<String, String> defaults = new HashMap<String, String>(); + defaults.put(INCLUDE_DEFAULTS, "true"); + defaults.put(REMOVE_PERMANENTLY, "true"); + return defaults; + } + + @Override + public String getDisplayName() { + return SERVICE_PALETTE; + } + + @Override + public String getFilePrefix() { + return SERVICE_PALETTE_PREFIX; + } + + @Override + public String getUUID() { + return UUID; + } + + @Override + public boolean isIncludeDefaults() { + return Boolean.parseBoolean(getProperty(INCLUDE_DEFAULTS)); + } + + @Override + public void setIncludeDefaults(boolean includeDefaults) { + setProperty(INCLUDE_DEFAULTS, Boolean.toString(includeDefaults)); + } + + @Override + public boolean isRemovePermanently() { + return Boolean.parseBoolean(getProperty(REMOVE_PERMANENTLY)); + } + + @Override + public void setRemovePermanently(boolean removePermanently) { + setProperty(REMOVE_PERMANENTLY, Boolean.toString(removePermanently)); + } +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java new file mode 100644 index 0000000..46f82c4 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfiguration.java
@@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.activitypalette; + +import java.util.HashMap; +import java.util.Map; + +import uk.org.taverna.configuration.AbstractConfigurable; +import uk.org.taverna.configuration.ConfigurationManager; + +public class ActivityPaletteConfiguration extends AbstractConfigurable { + private Map<String,String> defaultPropertyMap; + + public ActivityPaletteConfiguration(ConfigurationManager configurationManager) { + super(configurationManager); + } + + @Override + public String getCategory() { + return "Services"; + } + + @Override + public Map<String, String> getDefaultPropertyMap() { + if (defaultPropertyMap == null) { + defaultPropertyMap = new HashMap<>(); + + // //wsdl + //defaultPropertyMap.put("taverna.defaultwsdl", "http://www.ebi.ac.uk/xembl/XEMBL.wsdl,"+ + // "http://soap.genome.jp/KEGG.wsdl,"+ + // "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/soap/eutils.wsdl,"+ + // "http://soap.bind.ca/wsdl/bind.wsdl,"+ + // "http://www.ebi.ac.uk/ws/services/urn:Dbfetch?wsdl"); + + // //soaplab + //defaultPropertyMap.put("taverna.defaultsoaplab", "http://www.ebi.ac.uk/soaplab/services/"); + + // //biomart + //defaultPropertyMap.put("taverna.defaultmartregistry","http://www.biomart.org/biomart"); + + //add property names + //defaultPropertyMap.put("name.taverna.defaultwsdl", "WSDL"); + //defaultPropertyMap.put("name.taverna.defaultsoaplab","Soaplab"); + //defaultPropertyMap.put("name.taverna.defaultmartregistry", "Biomart"); + } + return defaultPropertyMap; + } + + @Override + public String getDisplayName() { + return "Activity Palette"; + } + + @Override + public String getFilePrefix() { + return "ActivityPalette"; + } + + @Override + public String getUUID() { + return "ad9f3a60-5967-11dd-ae16-0800200c9a66"; + } +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java new file mode 100644 index 0000000..6166e60 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationPanel.java
@@ -0,0 +1,284 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.activitypalette; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.EAST; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.FlowLayout.LEFT; +import static java.awt.FlowLayout.RIGHT; +import static javax.swing.BoxLayout.Y_AXIS; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showInputDialog; +import static javax.swing.border.BevelBorder.LOWERED; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.BoxLayout; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; + +import org.apache.log4j.Logger; + +@SuppressWarnings("serial") +public class ActivityPaletteConfigurationPanel extends JPanel { + private static Logger logger = Logger + .getLogger(ActivityPaletteConfigurationPanel.class); + + private Map<String,List<String>> values = new HashMap<>(); + private Map<String,String> names = new HashMap<>(); + private DefaultComboBoxModel<String> model; + private DefaultListModel<String> listModel; + private JList<String> propertyListItems; + private String selectedKey; + private JButton deleteTypeButton; + private final ActivityPaletteConfiguration config; + + public ActivityPaletteConfigurationPanel(ActivityPaletteConfiguration config) { + super(new BorderLayout()); + this.config = config; + + model = new DefaultComboBoxModel<>(); + for (String key : config.getInternalPropertyMap().keySet()) { + if (key.startsWith("taverna.") + && config.getPropertyStringList(key) != null) { + model.addElement(key); + values.put(key, + new ArrayList<>(config.getPropertyStringList(key))); + } + if (key.startsWith("name.taverna.")) + names.put(key, config.getProperty(key).toString()); + } + deleteTypeButton = new JButton("Delete"); + + final JButton addTypeButton = new JButton("Add"); + final JComboBox<String> comboBox = new JComboBox<>(model); + comboBox.setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList<?> list, + Object value, int index, boolean isSelected, + boolean cellHasFocus) { + if (value != null && value instanceof String) { + String name = names.get("name." + value); + if (name != null) + value = name; + } + return super.getListCellRendererComponent(list, value, index, + isSelected, cellHasFocus); + } + }); + + deleteTypeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String displayText = names.get("name." + selectedKey); + if (displayText == null) + displayText = selectedKey; + if (confirm("Confirm removal", + "Are you sure you wish to remove the type " + + displayText + "?")) { + names.remove("name." + selectedKey); + values.remove(selectedKey); + model.removeElement(selectedKey); + comboBox.setSelectedIndex(0); + } + } + }); + + addTypeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String key = input("New key", "Provide the new key."); + if (key == null) + return; + String name = input("Name for the key", + "Provide the name for the key: " + key); + if (name == null) + return; + + values.put(key, new ArrayList<String>()); + names.put("name." + key, name); + model.addElement(key); + comboBox.setSelectedItem(key); + } + }); + + comboBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (comboBox.getSelectedItem() != null + && comboBox.getSelectedItem() instanceof String) { + selectedKey = (String) comboBox.getSelectedItem(); + List<String> selectedList = values.get(selectedKey); + populateList(selectedList); + deleteTypeButton.setEnabled(selectedList.size() == 0); + } + } + }); + + JPanel propertySelectionPanel = new JPanel(new FlowLayout(LEFT)); + propertySelectionPanel.add(new JLabel("Activity type:")); + propertySelectionPanel.add(comboBox); + propertySelectionPanel.add(addTypeButton); + propertySelectionPanel.add(deleteTypeButton); + add(propertySelectionPanel, NORTH); + + JPanel listPanel = new JPanel(new BorderLayout()); + listModel = new DefaultListModel<>(); + propertyListItems = new JList<>(listModel); + propertyListItems.setBorder(new BevelBorder(LOWERED)); + + listPanel.add(propertyListItems, CENTER); + listPanel.add(listButtons(), EAST); + + add(listPanel, CENTER); + + add(applyButtonPanel(), SOUTH); + + if (model.getSize() > 0) + comboBox.setSelectedItem(model.getElementAt(0)); + } + + private void populateList(List<String> selectedList) { + listModel.removeAllElements(); + for (String item : selectedList) + listModel.addElement(item); + } + + private JPanel applyButtonPanel() { + JPanel applyPanel = new JPanel(new FlowLayout(RIGHT)); + JButton applyButton = new JButton("Apply"); + + applyButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + config.getInternalPropertyMap().clear(); + for (String key : values.keySet()) { + List<String> properties = values.get(key); + config.setPropertyStringList(key, new ArrayList<>( + properties)); + } + for (String key : names.keySet()) + config.setProperty(key, names.get(key)); + store(); + } + }); + + applyPanel.add(applyButton); + return applyPanel; + } + + private void store() { + try { + //FIXME + //ConfigurationManager.getInstance().store(config); + } catch (Exception e1) { + logger.error("There was an error storing the configuration:" + + config.getFilePrefix() + " (UUID=" + config.getUUID() + + ")", e1); + } + } + + private JPanel listButtons() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, Y_AXIS)); + JButton addButton = new JButton("+"); + addButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String value = input("New property", "Provide new value for: " + + selectedKey); + if (value != null) { + listModel.addElement(value); + values.get(selectedKey).add(value); + deleteTypeButton.setEnabled(false); + } + } + }); + + JButton deleteButton = new JButton("-"); + deleteButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Object value = propertyListItems.getSelectedValue(); + if (confirm("Confirm removal", + "Are you sure you wish to remove " + value + "?")) { + listModel.removeElement(value); + values.get(selectedKey).remove(value); + if (values.get(selectedKey).size() == 0) + deleteTypeButton.setEnabled(true); + } + } + }); + + panel.add(addButton); + panel.add(deleteButton); + + return panel; + } + + private boolean confirm(String title, String message) { + return showConfirmDialog(this, message, title, YES_NO_OPTION, + WARNING_MESSAGE) == YES_OPTION; + } + + private String input(String title, String message) { + return showInputDialog(this, message, title, INFORMATION_MESSAGE); + } + +/* private JButton getAddTypeButton() { + JButton result = new JButton("Add"); + result.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String val = input("New property value","New property value"); + if (val!=null) { + if (values.get(val) == null) { + model.addElement(val); + values.put(val, new ArrayList<String>()); + } else + showMessageDialog(ActivityPaletteConfigurationPanel.this, "This property already exists"); + } + } + }); + return result; + } +*/ +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java new file mode 100644 index 0000000..39c4a5a --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationUIFactory.java
@@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.activitypalette; + +import javax.swing.JPanel; + +import uk.org.taverna.configuration.Configurable; +import uk.org.taverna.configuration.ConfigurationUIFactory; + +public class ActivityPaletteConfigurationUIFactory implements + ConfigurationUIFactory { + private ActivityPaletteConfiguration activityPaletteConfiguration; + + @Override + public boolean canHandle(String uuid) { + return uuid != null && uuid.equals(getConfigurable().getUUID()); + } + + @Override + public Configurable getConfigurable() { + return activityPaletteConfiguration; + } + + @Override + public JPanel getConfigurationPanel() { + return new ActivityPaletteConfigurationPanel( + activityPaletteConfiguration); + } + + public void setActivityPaletteConfiguration( + ActivityPaletteConfiguration activityPaletteConfiguration) { + this.activityPaletteConfiguration = activityPaletteConfiguration; + } +}
diff --git a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory new file mode 100644 index 0000000..6c9fed9 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@ +#net.sf.taverna.t2.workbench.ui.activitypalette.ActivityPaletteConfigurationUIFactory \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml new file mode 100644 index 0000000..34921f5 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context-osgi.xml
@@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="ServiceDescriptionRegistryImpl" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry"/> + <service ref="ServiceDescriptionsConfigurationImpl" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration"/> + + <reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" /> + <reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" /> + + <list id="serviceDescriptionProviders" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider" cardinality="0..N" greedy-proxying="true"/> + +</beans:beans>
diff --git a/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml new file mode 100644 index 0000000..9f7110f --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/main/resources/META-INF/spring/activity-palette-impl-context.xml
@@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean name="ServiceDescriptionRegistryImpl" class="net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionRegistryImpl"> + <constructor-arg name="applicationConfiguration" ref="applicationConfiguration" /> + <property name="serviceDescriptionProvidersList" ref="serviceDescriptionProviders" /> + <property name="serviceDescriptionsConfig"> + <ref local="ServiceDescriptionsConfigurationImpl"/> + </property> + </bean> + + <bean id="ServiceDescriptionsConfigurationImpl" class="net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionsConfigurationImpl"> + <constructor-arg ref="configurationManager"/> + </bean> + + <!-- Don't think ActivityPalette is still used --> + <!-- <bean id="ActivityPaletteConfiguration" class="net.sf.taverna.t2.workbench.ui.activitypalette.ActivityPaletteConfiguration"> + <constructor-arg ref="configurationManager"/> + </bean> --> + +</beans>
diff --git a/taverna-workbench-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java b/taverna-workbench-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java new file mode 100644 index 0000000..081a9af --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/test/java/net/sf/taverna/t2/workbench/ui/activitypalette/ActivityPaletteConfigurationTest.java
@@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.activitypalette; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; + +import uk.org.taverna.configuration.app.impl.ApplicationConfigurationImpl; +import uk.org.taverna.configuration.impl.ConfigurationManagerImpl; + +public class ActivityPaletteConfigurationTest { + + private ActivityPaletteConfiguration conf; + private ConfigurationManagerImpl manager; + + @Before + public void setup() { + File f = new File(System.getProperty("java.io.tmpdir")); + final File d = new File(f,UUID.randomUUID().toString()); + d.mkdir(); + manager = new ConfigurationManagerImpl(new ApplicationConfigurationImpl() { + @Override + public File getApplicationHomeDir() { + return d; + } + }); + conf=new ActivityPaletteConfiguration(manager); + conf.restoreDefaults(); + } + + @Test + public void testEmptyList() throws Exception { + conf.setPropertyStringList("list", new ArrayList<String>()); + assertTrue("Result was not a list but was:"+conf.getProperty("list"),conf.getPropertyStringList("list") instanceof List); + assertTrue("Result was not a list but was:"+conf.getPropertyStringList("list"),conf.getPropertyStringList("list") instanceof List); + List<String> list = conf.getPropertyStringList("list"); + assertEquals("There should be 0 elements",0,list.size()); + } + + @Test + public void testSingleItem() throws Exception { + List<String> list = new ArrayList<>(); + list.add("fred"); + conf.setPropertyStringList("single", list); + + assertTrue("should be an ArrayList",conf.getPropertyStringList("single") instanceof List); + List<String> l = conf.getPropertyStringList("single"); + assertEquals("There should be 1 element",1,l.size()); + assertEquals("Its value should be fred","fred",l.get(0)); + } + + @Test + public void testList() throws Exception { + List<String> list = new ArrayList<>(); + list.add("fred"); + list.add("bloggs"); + conf.setPropertyStringList("list", list); + + assertTrue("should be an ArrayList",conf.getPropertyStringList("list") instanceof List); + List<String> l = conf.getPropertyStringList("list"); + assertEquals("There should be 1 element",2,l.size()); + assertEquals("Its value should be fred","fred",l.get(0)); + assertEquals("Its value should be bloggs","bloggs",l.get(1)); + } + + @Test + public void testNull() throws Exception { + assertNull("Should return null",conf.getProperty("blah blah blah")); + } +}
diff --git a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI new file mode 100644 index 0000000..9f3c02d --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PartitionAlgorithmSetSPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.partition.DummyPartitionAlgorithmSet \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI new file mode 100644 index 0000000..f5e7226 --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.PropertyExtractorSPI
@@ -0,0 +1,3 @@ +net.sf.taverna.t2.partition.DummyExtractor1 +net.sf.taverna.t2.partition.DummyExtractor2 +net.sf.taverna.t2.partition.DummyExtractor3 \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory new file mode 100644 index 0000000..2d26d31a --- /dev/null +++ b/taverna-workbench-activity-palette-impl/src/test/resources/META-INF/services/net.sf.taverna.t2.partition.QueryFactory
@@ -0,0 +1,2 @@ +net.sf.taverna.t2.partition.DummyActivityQueryFactory +net.sf.taverna.t2.partition.DummyQueryFactory \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-ui/pom.xml b/taverna-workbench-activity-palette-ui/pom.xml new file mode 100644 index 0000000..52549d0 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/pom.xml
@@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>activity-palette-ui</artifactId> + <packaging>bundle</packaging> + <name>Activity Palette UI Component</name> + <description>Activity Palette UI</description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-icons-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-palette-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>workflow-view</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>uibuilder</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.commons</groupId> + <artifactId>taverna-services-api</artifactId> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>${commons.beanutils.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/PathElementFilterTreeNode.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/PathElementFilterTreeNode.java new file mode 100644 index 0000000..5c65bc0 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/PathElementFilterTreeNode.java
@@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode; + +/** + * @author alanrw + */ +public class PathElementFilterTreeNode extends FilterTreeNode { + private static final long serialVersionUID = 6491242031931630314L; + + public PathElementFilterTreeNode(String userObject) { + super(userObject); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/RootFilterTreeNode.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/RootFilterTreeNode.java new file mode 100644 index 0000000..86fca05 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/RootFilterTreeNode.java
@@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode; + +/** + * @author alanrw + */ +public class RootFilterTreeNode extends FilterTreeNode { + private static final long serialVersionUID = 1047743498806473971L; + + public RootFilterTreeNode(String userObject) { + super(userObject); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilter.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilter.java new file mode 100644 index 0000000..a83ac2d --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilter.java
@@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import static java.beans.Introspector.getBeanInfo; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.swing.tree.DefaultMutableTreeNode; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.Filter; + +import org.apache.log4j.Logger; + +public class ServiceFilter implements Filter { + private static Logger logger = Logger.getLogger(ServiceFilter.class); + + private String filterString; + private boolean superseded; + private String[] filterLowerCaseSplit; + private final Object rootToIgnore; + + public ServiceFilter(String filterString, Object rootToIgnore) { + this.filterString = filterString; + this.rootToIgnore = rootToIgnore; + this.filterLowerCaseSplit = filterString.toLowerCase().split(" "); + this.superseded = false; + } + + private boolean basicFilter(DefaultMutableTreeNode node) { + if (node == rootToIgnore) + return false; + if (filterString.isEmpty()) + return true; + + if (node.getUserObject() instanceof ServiceDescription) { + ServiceDescription serviceDescription = (ServiceDescription) node + .getUserObject(); + for (String searchTerm : filterLowerCaseSplit) { + if (superseded) + return false; + String[] typeSplit = searchTerm.split(":", 2); + String type; + String keyword; + if (typeSplit.length == 2) { + type = typeSplit[0]; + keyword = typeSplit[1].toLowerCase(); + } else { + type = null; + keyword = searchTerm.toLowerCase(); + } + try { + if (!doesPropertySatisfy(serviceDescription, type, keyword)) + return false; + } catch (IntrospectionException | IllegalArgumentException + | IllegalAccessException | InvocationTargetException e) { + logger.error( + "failed to get properties of service description", + e); + return false; + } + } + return true; + } + for (String searchString : filterLowerCaseSplit) + if (!node.getUserObject().toString().toLowerCase().contains( + searchString)) + return false; + return true; + } + + /** + * Determine whether a service description satisfies a search term. + * + * @param serviceDescription + * The service description bean to look in. + * @param type + * The name of the property to look in, or <tt>null</tt> to + * search in all public non-expert properties. + * @param searchTerm + * The string to search for. + * @return <tt>true</tt> if-and-only-if the description matches. + */ + private boolean doesPropertySatisfy(ServiceDescription serviceDescription, + String type, String searchTerm) throws IllegalAccessException, + IllegalArgumentException, InvocationTargetException, + IntrospectionException { + BeanInfo beanInfo = getBeanInfo(serviceDescription.getClass()); + for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) { + if (superseded) + return false; + if ((type == null && !property.isHidden() && !property.isExpert()) + || property.getName().equalsIgnoreCase(type)) { + Method readMethod = property.getReadMethod(); + if (readMethod == null) + continue; + Object readProperty = readMethod.invoke(serviceDescription, + new Object[0]); + if (readProperty == null) + continue; + if (readProperty.toString().toLowerCase().contains(searchTerm)) + return true; + // Dig deeper? + } + } + return false; + } + + @Override + public boolean pass(DefaultMutableTreeNode node) { + return basicFilter(node); + } + + @Override + public String filterRepresentation(String original) { + return original; + } + + /** + * @return the superseded + */ + @Override + public boolean isSuperseded() { + return superseded; + } + + /** + * @param superseded + * the superseded to set + */ + @Override + public void setSuperseded(boolean superseded) { + this.superseded = superseded; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilterTreeNode.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilterTreeNode.java new file mode 100644 index 0000000..0e559ce --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceFilterTreeNode.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode; + +/** + * @author alanrw + */ +public class ServiceFilterTreeNode extends FilterTreeNode { + private static final long serialVersionUID = 6066698619971305454L; + + public ServiceFilterTreeNode(ServiceDescription userObject) { + super(userObject); + } + + @Override + public ServiceDescription getUserObject() { + return (ServiceDescription) super.getUserObject(); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java new file mode 100644 index 0000000..0b01fe2 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanel.java
@@ -0,0 +1,411 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.SwingUtilities.invokeLater; +import static net.sf.taverna.t2.servicedescriptions.ServiceDescription.LOCAL_SERVICES; +import static net.sf.taverna.t2.servicedescriptions.ServiceDescription.SERVICE_TEMPLATES; + +import java.awt.BorderLayout; +import java.lang.reflect.InvocationTargetException; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.servicedescriptions.events.AbstractProviderEvent; +import net.sf.taverna.t2.servicedescriptions.events.AbstractProviderNotification; +import net.sf.taverna.t2.servicedescriptions.events.PartialServiceDescriptionsNotification; +import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification; +import net.sf.taverna.t2.servicedescriptions.events.RemovedProviderEvent; +import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent; +import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeModel; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; + +import org.apache.log4j.Logger; + +import uk.org.taverna.commons.services.ServiceRegistry; + +/** + * A panel of available services + * + * @author Stian Soiland-Reyes + */ +@SuppressWarnings("serial") +public class ServicePanel extends JPanel implements UIComponentSPI { + private static Logger logger = Logger.getLogger(ServicePanel.class); + private static final int INITIAL_BLANK_OUT_COUNTER = 2; + public static final String AVAILABLE_SERVICES = "Available services"; + public static final String MATCHING_SERVIES = "Matching services"; + public static final String NO_MATCHING_SERVICES = "No matching services"; + public static final String MOBY_OBJECTS = "MOBY Objects"; + /** + * A Comparable constant to be used with buildPathMap + */ + private static final String SERVICES = "4DA84170-7746-4817-8C2E-E29AF8B2984D"; + private static final int STATUS_LINE_MESSAGE_MS = 600; + private static ServicePathElementComparator servicePathElementComparator = new ServicePathElementComparator(); + + public int blankOutCounter = 0; + private TreeUpdaterThread updaterThread; + private RootFilterTreeNode root = new RootFilterTreeNode(AVAILABLE_SERVICES); + private ServiceTreePanel serviceTreePanel; + private JLabel statusLine; + private FilterTreeModel treeModel; + protected Timer statusUpdateTimer; + + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + protected final ServiceDescriptionRegistryObserver serviceDescriptionRegistryObserver = new ServiceDescriptionRegistryObserver(); + protected final Object updateLock = new Object(); + private final EditManager editManager; + private final MenuManager menuManager; + private final SelectionManager selectionManager; + private final ServiceRegistry serviceRegistry; + + public ServicePanel(ServiceDescriptionRegistry serviceDescriptionRegistry, + EditManager editManager, MenuManager menuManager, + SelectionManager selectionManager, ServiceRegistry serviceRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + this.editManager = editManager; + this.menuManager = menuManager; + this.selectionManager = selectionManager; + this.serviceRegistry = serviceRegistry; + serviceDescriptionRegistry.addObserver(serviceDescriptionRegistryObserver); + initialise(); + } + + @Override + public ImageIcon getIcon() { + return null; + } + + @Override + public String getName() { + return "Service panel"; + } + + @Override + public void onDisplay() { + } + + @Override + public void onDispose() { + } + + public void providerStatus(ServiceDescriptionProvider provider, String message) { + logger.info(message + " " + provider); + final String htmlMessage = "<small>" + message + " [" + provider + "]</small>"; + + invokeLater(new Runnable() { + @Override + public void run() { + blankOutCounter = INITIAL_BLANK_OUT_COUNTER; + statusLine.setText("<html>" + htmlMessage + "</html>"); + statusLine.setVisible(true); + } + }); + } + + protected void initialise() { + removeAll(); + setLayout(new BorderLayout()); + treeModel = new FilterTreeModel(root); + serviceTreePanel = new ServiceTreePanel(treeModel, serviceDescriptionRegistry, editManager, menuManager, selectionManager, serviceRegistry); + serviceTreePanel.setAvailableObjectsString(AVAILABLE_SERVICES); + serviceTreePanel.setMatchingObjectsString(MATCHING_SERVIES); + serviceTreePanel.setNoMatchingObjectsString(NO_MATCHING_SERVICES); + add(serviceTreePanel); + statusLine = new JLabel(); + add(statusLine, BorderLayout.SOUTH); + if (statusUpdateTimer != null) + statusUpdateTimer.cancel(); + statusUpdateTimer = new Timer("Clear status line", true); + statusUpdateTimer + .scheduleAtFixedRate(new UpdateStatusLineTask(), 0, STATUS_LINE_MESSAGE_MS); + updateTree(); + } + + protected void updateTree() { + synchronized (updateLock) { + if (updaterThread != null && updaterThread.isAlive()) { + return; + } + updaterThread = new TreeUpdaterThread(); + updaterThread.start(); + } + } + + protected static class ServicePathElementComparator implements Comparator<Object> { + @Override + public int compare(Object o1, Object o2) { + if ((o1 instanceof String) && (o2 instanceof String)) { + if (o1.equals(SERVICE_TEMPLATES)) + return -1; + else if (o2.equals(SERVICE_TEMPLATES)) + return 1; + if (o1.equals(LOCAL_SERVICES)) + return -1; + else if (o2.equals(LOCAL_SERVICES)) + return 1; + if (o1.equals(MOBY_OBJECTS)) + return -1; + else if (o2.equals(MOBY_OBJECTS)) + return 1; + } + return o1.toString().compareToIgnoreCase(o2.toString()); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + //FIXME this class is type-disastrous! Really bad. + public class TreeUpdaterThread extends Thread { + private boolean aborting = false; + + private TreeUpdaterThread() { + super("Updating service panel"); + setDaemon(true); + } + + public void abort() { + aborting = true; + interrupt(); + } + + @Override + public void run() { + Map<Comparable, Map> pathMap = buildPathMap(); + populateChildren(root, pathMap); + invokeLater(new Runnable() { + @Override + public void run() { + try { + serviceTreePanel.runFilter(); + } catch (InterruptedException | InvocationTargetException e) { + logger.error("failed to filter", e); + } + } + }); + } + + protected Map<Comparable, Map> buildPathMap() { + Map<Comparable, Map> paths = new TreeMap<>(); + for (ServiceDescription serviceDescription : serviceDescriptionRegistry + .getServiceDescriptions()) { + if (aborting) + return paths; + Map currentPath = paths; + for (Object pathElem : serviceDescription.getPath()) { + Map pathEntry = (Map) currentPath.get(pathElem); + if (pathEntry == null) { + pathEntry = new TreeMap(); + currentPath.put(pathElem, pathEntry); + } + currentPath = pathEntry; + } + TreeMap<String, Set<ServiceDescription>> services = (TreeMap) currentPath + .get(SERVICES); + if (services == null) { + services = new TreeMap<>(); + currentPath.put(SERVICES, services); + } + String serviceDescriptionName = serviceDescription.getName(); + if (!services.containsKey(serviceDescriptionName)) { + Set<ServiceDescription> serviceSet = new HashSet<>(); + services.put(serviceDescriptionName, serviceSet); + } + services.get(serviceDescriptionName).add(serviceDescription); + } + return paths; + } + + protected void populateChildren(FilterTreeNode node, Map pathMap) { + if (aborting) + return; + if (node == root) { + // Clear top root + invokeLater(new Runnable() { + @Override + public void run() { + if (aborting) + return; + serviceTreePanel.setFilter(null); + root.removeAllChildren(); + } + }); + } + + Set<Comparable> paths = new TreeSet<>(servicePathElementComparator); + Map<String, Set<ServiceDescription>> services = (Map) pathMap + .get(SERVICES); + if (services == null) + services = new TreeMap<>(); + paths.addAll(pathMap.keySet()); + paths.addAll(services.keySet()); + + for (Comparable pathElement : paths) { + if (aborting) + return; + if (pathElement.equals(SERVICES)) + continue; + Set<FilterTreeNode> childNodes = new HashSet<>(); + if (services.containsKey(pathElement)) { + for (ServiceDescription sd : services.get(pathElement)) + childNodes.add(new ServiceFilterTreeNode(sd)); + } else + childNodes.add(new PathElementFilterTreeNode((String) pathElement)); + invokeLater(new AddNodeRunnable(node, childNodes)); + if ((pathMap.containsKey(pathElement)) && !childNodes.isEmpty()) + populateChildren(childNodes.iterator().next(), (Map) pathMap.get(pathElement)); + } + // if (!services.isEmpty()) { + // Collections.sort(services, serviceComparator); + // for (String serviceName : services.keySet()) { + // if (aborting) { + // return; + // } + // if (pathMap.containsKey(serviceName)) { + // continue; + // } + // SwingUtilities.invokeLater(new AddNodeRunnable(node, + // new ServiceFilterTreeNode(services.get(serviceName)))); + // } + // } + } + + public class AddNodeRunnable implements Runnable { + private final Set<FilterTreeNode> nodes; + private final FilterTreeNode root; + + public AddNodeRunnable(FilterTreeNode root, Set<FilterTreeNode> nodes) { + this.root = root; + this.nodes = nodes; + } + + @Override + public void run() { + if (aborting) + return; + for (FilterTreeNode n : nodes) + root.add(n); + } + } + } + + public static class RemoveNodeRunnable implements Runnable { + private final FilterTreeNode root; + + public RemoveNodeRunnable(FilterTreeNode root) { + this.root = root; + } + + @Override + public void run() { + root.removeFromParent(); + } + } + + private final class ServiceDescriptionRegistryObserver implements + Observer<ServiceDescriptionRegistryEvent> { + Set<ServiceDescriptionProvider> alreadyComplainedAbout = new HashSet<>(); + + @Override + public void notify(Observable<ServiceDescriptionRegistryEvent> sender, + ServiceDescriptionRegistryEvent message) throws Exception { + if (message instanceof ProviderErrorNotification) + reportServiceProviderError((ProviderErrorNotification) message); + else if (message instanceof ServiceDescriptionProvidedEvent + || message instanceof RemovedProviderEvent) { + AbstractProviderEvent ape = (AbstractProviderEvent) message; + alreadyComplainedAbout.remove(ape.getProvider()); + } + + if (message instanceof AbstractProviderNotification) { + AbstractProviderNotification abstractProviderNotification = (AbstractProviderNotification) message; + providerStatus(abstractProviderNotification.getProvider(), + abstractProviderNotification.getMessage()); + } + if (message instanceof PartialServiceDescriptionsNotification) + /* + * TODO: Support other events and only update relevant parts of + * tree, or at least select the recently added provider + */ + updateTree(); + else if (message instanceof RemovedProviderEvent) + updateTree(); + } + + private void reportServiceProviderError( + final ProviderErrorNotification pen) { + ServiceDescriptionProvider provider = pen.getProvider(); + if (serviceDescriptionRegistry + .getDefaultServiceDescriptionProviders().contains(provider)) + return; + if (alreadyComplainedAbout.contains(provider)) + return; + + alreadyComplainedAbout.add(provider); + invokeLater(new Runnable() { + @Override + public void run() { + showMessageDialog(ServicePanel.this, pen.getMessage() + + "\n" + pen.getProvider(), "Import service error", + ERROR_MESSAGE); + } + }); + } + } + + private final class UpdateStatusLineTask extends TimerTask { + @Override + public void run() { + if (blankOutCounter < 0 || blankOutCounter-- > 0) + // Only clear it once + return; + invokeLater(new Runnable() { + @Override + public void run() { + if (blankOutCounter < 0) + statusLine.setVisible(false); + } + }); + } + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java new file mode 100644 index 0000000..2eb97ef --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServicePanelComponentFactory.java
@@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (C) 2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import javax.swing.ImageIcon; + +import uk.org.taverna.commons.services.ServiceRegistry; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; + +/** + * Service panel factory + * + * @author Stian Soiland-Reyes + */ +public class ServicePanelComponentFactory implements UIComponentFactorySPI { + private ServiceDescriptionRegistry serviceDescriptionRegistry; + private EditManager editManager; + private MenuManager menuManager; + private SelectionManager selectionManager; + private ServiceRegistry serviceRegistry; + + @Override + public UIComponentSPI getComponent() { + return new ServicePanel(serviceDescriptionRegistry, editManager, + menuManager, selectionManager, serviceRegistry); + } + + @Override + public ImageIcon getIcon() { + return null; + } + + @Override + public String getName() { + return "Service panel"; + } + + public void setServiceDescriptionRegistry( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java new file mode 100644 index 0000000..4b7388d --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
@@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import static net.sf.taverna.t2.workbench.activityicons.DefaultActivityIcon.getDefaultIcon; + +import java.awt.Component; + +import javax.swing.Icon; +import javax.swing.JTree; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeCellRenderer; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode; + +@SuppressWarnings("serial") +public class ServiceTreeCellRenderer extends FilterTreeCellRenderer { + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean sel, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + Component result = super.getTreeCellRendererComponent(tree, value, sel, + expanded, leaf, row, hasFocus); + if (result instanceof ServiceTreeCellRenderer + && value instanceof FilterTreeNode + && ((FilterTreeNode) value).getUserObject() instanceof ServiceDescription) + prettifyServiceTreeCell((ServiceTreeCellRenderer) result, + (ServiceDescription) ((FilterTreeNode) value) + .getUserObject()); + else { + // Commented out - these are ugly, use the default folder icons instead + /* + * if (expanded) { ((ServiceTreeCellRenderer) result) + * .setIcon(WorkbenchIcons.folderOpenIcon); } else { + * ((ServiceTreeCellRenderer) result) + * .setIcon(WorkbenchIcons.folderClosedIcon); } + */ + } + return result; + } + + private void prettifyServiceTreeCell(ServiceTreeCellRenderer renderer, + ServiceDescription item) { + String name = item.getName(); + if (getFilter() != null) + name = getFilter().filterRepresentation(name); + // serviceTreeCellRenderer.setForeground(Color.red); + String displayName = name; + + String textualDescription = item.getDescription(); + if (textualDescription != null && !textualDescription.isEmpty()) + displayName = displayName + " - " + textualDescription; + renderer.setText(displayName); + + Icon activityIcon = item.getIcon(); + renderer.setIcon(activityIcon != null ? activityIcon + : getDefaultIcon()); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java new file mode 100644 index 0000000..a76dcc9 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreeClickListener.java
@@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import static java.awt.Color.RED; +import static javax.swing.SwingUtilities.invokeLater; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.BLUE; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.ORANGE; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.halfShade; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.minusIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.plusIcon; +import static net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView.importServiceDescription; + +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import javax.swing.AbstractAction; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.tree.TreePath; + +import net.sf.taverna.t2.lang.ui.ShadedLabel; +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ExportServiceDescriptionsAction; +import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromFileAction; +import net.sf.taverna.t2.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromURLAction; +import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RemoveDefaultServicesAction; +import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RemoveUserServicesAction; +import net.sf.taverna.t2.workbench.ui.servicepanel.actions.RestoreDefaultServicesAction; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeSelectionModel; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.TreePanel; + +import org.apache.log4j.Logger; + +import uk.org.taverna.commons.services.ServiceRegistry; + +/** + * @author alanrw + */ +public class ServiceTreeClickListener extends MouseAdapter { + private static Logger logger = Logger.getLogger(ServiceTreeClickListener.class); + + private JTree tree; + private TreePanel panel; + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + private final EditManager editManager; + private final MenuManager menuManager; + private final SelectionManager selectionManager; + private final ServiceRegistry serviceRegistry; + + public ServiceTreeClickListener(JTree tree, TreePanel panel, + ServiceDescriptionRegistry serviceDescriptionRegistry, EditManager editManager, + MenuManager menuManager, SelectionManager selectionManager, ServiceRegistry serviceRegistry) { + this.tree = tree; + this.panel = panel; + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + this.editManager = editManager; + this.menuManager = menuManager; + this.selectionManager = selectionManager; + this.serviceRegistry = serviceRegistry; + } + + @SuppressWarnings("serial") + private void handleMouseEvent(MouseEvent evt) { + FilterTreeSelectionModel selectionModel = (FilterTreeSelectionModel) tree + .getSelectionModel(); + // Discover the tree row that was clicked on + int selRow = tree.getRowForLocation(evt.getX(), evt.getY()); + if (selRow == -1) + return; + + // Get the selection path for the row + TreePath selectionPath = tree + .getPathForLocation(evt.getX(), evt.getY()); + if (selectionPath == null) + return; + + // Get the selected node + final FilterTreeNode selectedNode = (FilterTreeNode) selectionPath + .getLastPathComponent(); + + selectionModel.clearSelection(); + selectionModel.mySetSelectionPath(selectionPath); + + if (evt.isPopupTrigger()) { + JPopupMenu menu = new JPopupMenu(); + Object selectedObject = selectedNode.getUserObject(); + logger.info(selectedObject.getClass().getName()); + if (!(selectedObject instanceof ServiceDescription)) { + menu.add(new ShadedLabel("Tree", BLUE)); + menu.add(new JMenuItem(new AbstractAction("Expand all", + plusIcon) { + @Override + public void actionPerformed(ActionEvent evt) { + invokeLater(new Runnable() { + @Override + public void run() { + panel.expandAll(selectedNode, true); + } + }); + } + })); + menu.add(new JMenuItem(new AbstractAction("Collapse all", + minusIcon) { + @Override + public void actionPerformed(ActionEvent evt) { + invokeLater(new Runnable() { + @Override + public void run() { + panel.expandAll(selectedNode, false); + } + }); + } + })); + } + + if (selectedObject instanceof ServiceDescription) { + final ServiceDescription sd = (ServiceDescription) selectedObject; + menu.add(new ShadedLabel(sd.getName(), ORANGE)); + menu.add(new AbstractAction("Add to workflow") { + @Override + public void actionPerformed(ActionEvent e) { + importServiceDescription(sd, false, editManager, + menuManager, selectionManager, serviceRegistry); + } + }); + menu.add(new AbstractAction("Add to workflow with name...") { + @Override + public void actionPerformed(ActionEvent e) { + importServiceDescription(sd, true, editManager, + menuManager, selectionManager, serviceRegistry); + } + }); + } + + Map<String, ServiceDescriptionProvider> nameMap = getServiceDescriptionProviderMap(selectedNode); + + boolean first = true; + for (String name : nameMap.keySet()) { + final ServiceDescriptionProvider sdp = nameMap.get(name); + if (!(sdp instanceof ConfigurableServiceProvider)) + continue; + if (first) { + menu.add(new ShadedLabel( + "Remove individual service provider", GREEN)); + first = false; + } + menu.add(new AbstractAction(name) { + @Override + public void actionPerformed(ActionEvent e) { + serviceDescriptionRegistry + .removeServiceDescriptionProvider(sdp); + } + }); + } + + if (selectedNode.isRoot()) { // Root "Available services" + menu.add(new ShadedLabel("Default and added service providers", + ORANGE)); + menu.add(new RemoveUserServicesAction( + serviceDescriptionRegistry)); + menu.add(new RemoveDefaultServicesAction( + serviceDescriptionRegistry)); + menu.add(new RestoreDefaultServicesAction( + serviceDescriptionRegistry)); + + menu.add(new ShadedLabel("Import/export services", halfShade(RED))); + menu.add(new ImportServiceDescriptionsFromFileAction( + serviceDescriptionRegistry)); + menu.add(new ImportServiceDescriptionsFromURLAction( + serviceDescriptionRegistry)); + menu.add(new ExportServiceDescriptionsAction( + serviceDescriptionRegistry)); + } + + menu.show(evt.getComponent(), evt.getX(), evt.getY()); + } + } + + private Map<String, ServiceDescriptionProvider> getServiceDescriptionProviderMap( + FilterTreeNode selectedNode) { + Set<ServiceDescriptionProvider> providers; + + if (selectedNode.isRoot()) + providers = serviceDescriptionRegistry + .getServiceDescriptionProviders(); + else { + providers = new HashSet<>(); + for (FilterTreeNode leaf : selectedNode.getLeaves()) { + if (!leaf.isLeaf()) + logger.info("Not a leaf"); + if (!(leaf.getUserObject() instanceof ServiceDescription)) { + logger.info(leaf.getUserObject().getClass() + .getCanonicalName()); + logger.info(leaf.getUserObject().toString()); + continue; + } + providers + .addAll(serviceDescriptionRegistry + .getServiceDescriptionProviders((ServiceDescription) leaf + .getUserObject())); + } + } + + TreeMap<String, ServiceDescriptionProvider> nameMap = new TreeMap<>(); + for (ServiceDescriptionProvider sdp : providers) + nameMap.put(sdp.toString(), sdp); + return nameMap; + } + + @Override + public void mousePressed(MouseEvent evt) { + handleMouseEvent(evt); + } + + @Override + public void mouseReleased(MouseEvent evt) { + handleMouseEvent(evt); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java new file mode 100644 index 0000000..ce3e336 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/ServiceTreePanel.java
@@ -0,0 +1,176 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.ui.servicepanel; + +import static java.awt.datatransfer.DataFlavor.javaJVMLocalObjectMimeType; +import static javax.swing.SwingUtilities.invokeLater; + +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.TransferHandler; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeWillExpandListener; +import javax.swing.tree.ExpandVetoException; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.servicepanel.menu.AddServiceProviderMenu; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.Filter; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeModel; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.FilterTreeNode; +import net.sf.taverna.t2.workbench.ui.servicepanel.tree.TreePanel; + +import org.apache.log4j.Logger; + +import uk.org.taverna.commons.services.ServiceRegistry; + +public class ServiceTreePanel extends TreePanel { + private static final long serialVersionUID = 6611462684296693909L; + private static Logger logger = Logger.getLogger(ServiceTreePanel.class); + + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + private final EditManager editManager; + private final MenuManager menuManager; + private final SelectionManager selectionManager; + private final ServiceRegistry serviceRegistry; + + public ServiceTreePanel(FilterTreeModel treeModel, + ServiceDescriptionRegistry serviceDescriptionRegistry, EditManager editManager, + MenuManager menuManager, SelectionManager selectionManager, ServiceRegistry serviceRegistry) { + super(treeModel); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + this.editManager = editManager; + this.menuManager = menuManager; + this.selectionManager = selectionManager; + this.serviceRegistry = serviceRegistry; + initialize(); + } + + @Override + protected void initialize() { + super.initialize(); + tree.setDragEnabled(true); + tree.setTransferHandler(new ServiceTransferHandler()); + tree.addTreeWillExpandListener(new AvoidRootCollapse()); + tree.expandRow(0); + + invokeLater(new Runnable() { + @Override + public void run() { + tree.addMouseListener(new ServiceTreeClickListener(tree, + ServiceTreePanel.this, serviceDescriptionRegistry, + editManager, menuManager, selectionManager, + serviceRegistry)); + } + }); + } + + @Override + protected Component createExtraComponent() { + JComponent buttonPanel = new JPanel(new FlowLayout()); + buttonPanel.add(new AddServiceProviderMenu(serviceDescriptionRegistry)); + // buttonPanel.add(new JButton(new RefreshProviderRegistryAction())); + return buttonPanel; + } + + @Override + public Filter createFilter(String text) { + return new ServiceFilter(text, filterTreeModel.getRoot()); + } + + @Override + protected TreeCellRenderer createCellRenderer() { + return new ServiceTreeCellRenderer(); + } + + public static class AvoidRootCollapse implements TreeWillExpandListener { + @Override + public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { + if (event.getPath().getPathCount() == 1) + throw new ExpandVetoException(event, "Can't collapse root"); + } + + @Override + public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { + } + } + + private final class ServiceTransferHandler extends TransferHandler { + private static final long serialVersionUID = 4347965626386951176L; + + /** + * Triggered when a node ie. an {@link ActivityItem} is dragged out of + * the tree. Figures out what node it is being dragged and then starts a + * drag action with it + */ + @Override + protected Transferable createTransferable(JComponent c) { + TreePath selectionPath = tree.getSelectionPath(); + if (selectionPath == null) + return null; + FilterTreeNode lastPathComponent = (FilterTreeNode) selectionPath + .getLastPathComponent(); + if (!(lastPathComponent.getUserObject() instanceof ServiceDescription)) + return null; + final ServiceDescription serviceDescription = (ServiceDescription) lastPathComponent + .getUserObject(); + + return new Transferable() { + @Override + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + return serviceDescription; + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + DataFlavor[] flavors = new DataFlavor[1]; + try { + flavors[0] = getFlavorForClass(ServiceDescription.class); + } catch (ClassNotFoundException e) { + logger.error("Error casting Dataflavor", e); + flavors[0] = null; + } + return flavors; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + DataFlavor thisFlavor = null; + try { + thisFlavor = getFlavorForClass(ServiceDescription.class); + } catch (ClassNotFoundException e) { + logger.error("Error casting Dataflavor", e); + } + return flavor.equals(thisFlavor); + } + }; + } + + @Override + public int getSourceActions(JComponent c) { + return COPY_OR_MOVE; + } + } + + private DataFlavor getFlavorForClass(Class<?> clazz) + throws ClassNotFoundException { + String name = clazz.getName(); + return new DataFlavor(javaJVMLocalObjectMimeType + ";class=" + clazz, + name.substring(name.lastIndexOf('.') + 1), + clazz.getClassLoader()); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java new file mode 100644 index 0000000..c8f2bfa --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/AddServiceProviderAction.java
@@ -0,0 +1,256 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.BorderLayout.WEST; +import static java.awt.event.KeyEvent.VK_ENTER; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow; +import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors; +import static org.apache.log4j.Logger.getLogger; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.beans.PropertyDescriptor; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.uibuilder.UIBuilder; +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.CustomizedConfigurePanelProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.servicedescriptions.CustomizedConfigurePanelProvider.CustomizedConfigureCallBack; +import net.sf.taverna.t2.servicedescriptions.events.ProviderErrorNotification; +import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionProvidedEvent; +import net.sf.taverna.t2.servicedescriptions.events.ServiceDescriptionRegistryEvent; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.configurations.Configuration; + +/** + * Action for adding a service provider + * + * @author Stian Soiland-Reyes + * @author Alan R Williams + */ +@SuppressWarnings("serial") +public class AddServiceProviderAction extends AbstractAction { + private static Logger logger = getLogger(AddServiceProviderAction.class); + + // protected static Dimension DIALOG_SIZE = new Dimension(400, 300); + + private ServiceDescriptionRegistry serviceDescriptionRegistry; + + private final ConfigurableServiceProvider confProvider; + private final Component owner; + + public AddServiceProviderAction(ConfigurableServiceProvider confProvider, + Component owner) { + super(confProvider.getName() + "...", confProvider.getIcon()); + this.confProvider = confProvider; + this.owner = owner; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (confProvider instanceof CustomizedConfigurePanelProvider) { + final CustomizedConfigurePanelProvider provider = (CustomizedConfigurePanelProvider) confProvider; + provider.createCustomizedConfigurePanel(new CustomizedConfigureCallBack() { + @Override + public Configuration getTemplateConfig() { + return (Configuration) provider.getConfiguration().clone(); + } + + @Override + public ServiceDescriptionRegistry getServiceDescriptionRegistry() { + return AddServiceProviderAction.this.getServiceDescriptionRegistry(); + } + + @Override + public void newProviderConfiguration(Configuration providerConfig) { + addNewProvider(providerConfig); + } + }); + return; + } + + Configuration configuration; + try { + configuration = (Configuration) confProvider.getConfiguration().clone(); + } catch (Exception ex) { + throw new RuntimeException("Can't clone configuration bean", ex); + } + JPanel buildEditor = buildEditor(configuration); + String title = "Add " + confProvider.getName(); + JDialog dialog = new HelpEnabledDialog(getMainWindow(), title, true, null); + JPanel iconPanel = new JPanel(); + iconPanel.add(new JLabel(confProvider.getIcon()), NORTH); + dialog.add(iconPanel, WEST); + dialog.add(buildEditor, CENTER); + JPanel buttonPanel = new JPanel(new BorderLayout()); + final AddProviderAction addProviderAction = new AddProviderAction(configuration, + dialog); + JButton addProviderButton = new JButton(addProviderAction); + buttonPanel.add(addProviderButton, WEST); + + dialog.add(buttonPanel, SOUTH); + // When user presses "Return" key fire the action on the "Add" button + addProviderButton.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent evt) { + if (evt.getKeyCode() == VK_ENTER) + addProviderAction.actionPerformed(null); + } + }); + dialog.getRootPane().setDefaultButton(addProviderButton); + + // dialog.setSize(buttonPanel.getPreferredSize()); + dialog.pack(); + dialog.setLocationRelativeTo(owner); +// dialog.setLocation(owner.getLocationOnScreen().x + owner.getWidth(), +// owner.getLocationOnScreen().y + owner.getHeight()); + dialog.setVisible(true); + } + + protected void addNewProvider(Configuration configurationBean) { + ConfigurableServiceProvider cloned = (ConfigurableServiceProvider) confProvider + .newInstance(); + try { + cloned.configure(configurationBean); + getServiceDescriptionRegistry().addObserver( + new CheckAddedCorrectlyObserver(cloned)); + getServiceDescriptionRegistry().addServiceDescriptionProvider( + cloned); + } catch (Exception ex) { + logger.warn("Can't configure provider " + cloned + " using " + + configurationBean, ex); + showMessageDialog(owner, "Can't configure service provider " + + cloned.getName(), "Can't add service provider", + ERROR_MESSAGE); + } + } + + private PropertyDescriptor[] getProperties(Configuration configuration) { + // FIXME This is *so* wrong! + try { + return getPropertyDescriptors(configuration); + } catch (Exception ex) { + throw new RuntimeException("Can't inspect configuration bean", ex); + } + } + + // TODO This is probably not right + protected JPanel buildEditor(Configuration configuration) { + List<String> uiBuilderConfig = new ArrayList<>(); + int lastPreferred = 0; + for (PropertyDescriptor property : getProperties(configuration)) { + if (property.isHidden() || property.isExpert()) + // TODO: Add support for expert properties + continue; + String propertySpec = property.getName() + ":name=" + + property.getDisplayName(); + if (property.isPreferred()) + // Add it to the front + uiBuilderConfig.add(lastPreferred++, propertySpec); + else + uiBuilderConfig.add(propertySpec); + } + + return UIBuilder.buildEditor(configuration, uiBuilderConfig + .toArray(new String[0])); + } + + public void setServiceDescriptionRegistry( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public ServiceDescriptionRegistry getServiceDescriptionRegistry() { + return serviceDescriptionRegistry; + } + + public class AddProviderAction extends AbstractAction { + private final Configuration configurationBean; + private final JDialog dialog; + + private AddProviderAction(Configuration configurationBean, JDialog dialog) { + super("Add"); + this.configurationBean = configurationBean; + this.dialog = dialog; + } + + @Override + public void actionPerformed(ActionEvent e) { + addNewProvider(configurationBean); + dialog.setVisible(false); + } + } + + public class CheckAddedCorrectlyObserver implements + Observer<ServiceDescriptionRegistryEvent> { + private final ConfigurableServiceProvider provider; + + private CheckAddedCorrectlyObserver(ConfigurableServiceProvider provider) { + this.provider = provider; + } + + @Override + public void notify(Observable<ServiceDescriptionRegistryEvent> sender, + ServiceDescriptionRegistryEvent message) throws Exception { + if (message instanceof ProviderErrorNotification) + notify((ProviderErrorNotification) message); + else if (message instanceof ServiceDescriptionProvidedEvent) + notify((ServiceDescriptionProvidedEvent) message); + } + + private void notify(ServiceDescriptionProvidedEvent providedMsg) { + if (providedMsg.getProvider() == provider) + getServiceDescriptionRegistry().removeObserver(this); + } + + private void notify(ProviderErrorNotification errorMsg) { + if (errorMsg.getProvider() != provider) + return; + getServiceDescriptionRegistry().removeObserver(this); + getServiceDescriptionRegistry().removeServiceDescriptionProvider( + provider); +// showMessageDialog(owner, errorMsg.getMessage(), +// "Can't add provider " + provider, ERROR_MESSAGE); + } + } +} \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java new file mode 100644 index 0000000..990e429 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ExportServiceDescriptionsAction.java
@@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.NO_OPTION; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showMessageDialog; + +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.prefs.Preferences; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +import org.apache.log4j.Logger; + +/** + * Action to export the current service descritpions from the Service + * Registry to an xml file. + * + * @author Alex Nenadic + */ +//FIXME this file assumes we're writing out as XML +@SuppressWarnings("serial") +public class ExportServiceDescriptionsAction extends AbstractAction { + private static final String EXTENSION = ".xml"; + private static final String EXPORT_SERVICES = "Export services to file"; + private static final String SERVICE_EXPORT_DIR_PROPERTY = "serviceExportDir"; + private Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class); + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public ExportServiceDescriptionsAction(ServiceDescriptionRegistry serviceDescriptionRegistry) { + super(EXPORT_SERVICES); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public static final boolean INHIBIT = true; + + @Override + public void actionPerformed(ActionEvent e) { + JComponent parentComponent = null; + if (e.getSource() instanceof JComponent) + parentComponent = (JComponent) e.getSource(); + + if (INHIBIT) { + showMessageDialog(parentComponent, + "Operation not currently working correctly", + "Not Implemented", ERROR_MESSAGE); + return; + } + + JFileChooser fileChooser = new JFileChooser(); + Preferences prefs = Preferences.userNodeForPackage(getClass()); + String curDir = prefs.get(SERVICE_EXPORT_DIR_PROPERTY, + System.getProperty("user.home")); + fileChooser.setDialogTitle("Select file to export services to"); + + fileChooser.setFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() + || f.getName().toLowerCase().endsWith(EXTENSION); + } + + @Override + public String getDescription() { + return ".xml files"; + } + }); + + fileChooser.setCurrentDirectory(new File(curDir)); + + boolean tryAgain = true; + while (tryAgain) { + tryAgain = false; + int returnVal = fileChooser.showSaveDialog(parentComponent); + if (returnVal == APPROVE_OPTION) { + prefs.put(SERVICE_EXPORT_DIR_PROPERTY, fileChooser.getCurrentDirectory() + .toString()); + File file = fileChooser.getSelectedFile(); + if (!file.getName().toLowerCase().endsWith(EXTENSION)) { + String newName = file.getName() + EXTENSION; + file = new File(file.getParentFile(), newName); + } + + try { + if (file.exists()) { + String msg = "Are you sure you want to overwrite existing file " + + file + "?"; + int ret = showConfirmDialog(parentComponent, msg, + "File already exists", YES_NO_CANCEL_OPTION); + if (ret == NO_OPTION) { + tryAgain = true; + continue; + } else if (ret != YES_OPTION) { + logger.info("Service descriptions export: aborted overwrite of " + + file.getAbsolutePath()); + break; + } + } + exportServiceDescriptions(file); + break; + } catch (Exception ex) { + logger.error("Service descriptions export: failed to export services to " + + file.getAbsolutePath(), ex); + showMessageDialog( + parentComponent, + "Failed to export services to " + + file.getAbsolutePath(), "Error", + ERROR_MESSAGE); + break; + } + } + } + + if (parentComponent instanceof JButton) + // lose the focus from the button after performing the action + parentComponent.requestFocusInWindow(); + } + + private void exportServiceDescriptions(File file) { + // TODO: Open in separate thread to avoid hanging UI + serviceDescriptionRegistry.exportCurrentServiceDescriptions(file); + logger.info("Service descriptions export: saved to file " + + file.getAbsolutePath()); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java new file mode 100644 index 0000000..1542583 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromFileAction.java
@@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JOptionPane.CANCEL_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.JOptionPane.showOptionDialog; + +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.HashSet; +import java.util.prefs.Preferences; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; + +import org.apache.log4j.Logger; + +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +/** + * Action to import a list of service descriptions from an xml file + * into the Service Registry. Users have an option to completely + * replace the current services or just add the ones from the file + * to the current services. + * + * @author Alex Nenadic + */ +//FIXME this file assumes we're writing out as XML +@SuppressWarnings("serial") +public class ImportServiceDescriptionsFromFileAction extends AbstractAction{ + private static final String EXTENSION = ".xml"; + private static final String IMPORT_SERVICES = "Import services from file"; + private static final String SERVICE_IMPORT_DIR_PROPERTY = "serviceImportDir"; + private static final Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class); + + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public ImportServiceDescriptionsFromFileAction( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + super(IMPORT_SERVICES); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + private static final Object[] CHOICES = { "Add to current services", + "Replace current services", "Cancel" }; + + @Override + public void actionPerformed(ActionEvent e) { + JComponent parentComponent = null; + if (e.getSource() instanceof JComponent) + parentComponent = (JComponent) e.getSource(); + + if (ExportServiceDescriptionsAction.INHIBIT) { + showMessageDialog(parentComponent, + "Operation not currently working correctly", + "Not Implemented", ERROR_MESSAGE); + return; + } + + int choice = showOptionDialog( + parentComponent, + "Do you want to add the imported services to the current ones or replace the current ones?", + "Import services", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, + null, CHOICES, CHOICES[0]); + + if (choice != CANCEL_OPTION) { + JFileChooser fileChooser = new JFileChooser(); + Preferences prefs = Preferences.userNodeForPackage(getClass()); + String curDir = prefs.get(SERVICE_IMPORT_DIR_PROPERTY, System.getProperty("user.home")); + + fileChooser.setDialogTitle("Select file to import services from"); + + fileChooser.setFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return f.isDirectory() + || f.getName().toLowerCase().endsWith(EXTENSION); + } + + @Override + public String getDescription() { + return EXTENSION + " files"; + } + }); + + fileChooser.setCurrentDirectory(new File(curDir)); + + if (fileChooser.showOpenDialog(parentComponent) == APPROVE_OPTION) { + prefs.put(SERVICE_IMPORT_DIR_PROPERTY, fileChooser + .getCurrentDirectory().toString()); + File file = fileChooser.getSelectedFile(); + + try { + // Did user want to replace or add services? + importServices(file, choice == YES_OPTION); + } catch (Exception ex) { + logger.error( + "Service descriptions import: failed to import services from " + + file.getAbsolutePath(), ex); + showMessageDialog(parentComponent, + "Failed to import services from " + file.getAbsolutePath(), "Error", + ERROR_MESSAGE); + } + } + } + + if (parentComponent instanceof JButton) + // lose the focus from the button after performing the action + parentComponent.requestFocusInWindow(); + } + + private void importServices(final File file, final boolean addToCurrent) + throws Exception { + // TODO: Open in separate thread to avoid hanging UI + + if (!addToCurrent) + for (ServiceDescriptionProvider provider : new HashSet<>( + serviceDescriptionRegistry.getServiceDescriptionProviders())) + // remove all configurable service providers + if (provider instanceof ConfigurableServiceProvider) + serviceDescriptionRegistry + .removeServiceDescriptionProvider(provider); + + // import all providers from the file + serviceDescriptionRegistry.loadServiceProviders(file); + serviceDescriptionRegistry.saveServiceDescriptions(); + } +} +
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java new file mode 100644 index 0000000..0dbbe25 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/ImportServiceDescriptionsFromURLAction.java
@@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import static javax.swing.JOptionPane.CANCEL_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showInputDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.JOptionPane.showOptionDialog; + +import java.awt.event.ActionEvent; +import java.net.URL; +import java.util.HashSet; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JComponent; + +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +import org.apache.log4j.Logger; + +/** + * Action to import a list of service descriptions from an URL pointing + * to an xml file into the Service Registry. Users have an option to + * completely replace the current services or just add the ones from the + * file to the current services. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class ImportServiceDescriptionsFromURLAction extends AbstractAction{ + private static final String IMPORT_SERVICES_FROM_URL = "Import services from URL"; + private static final Logger logger = Logger.getLogger(ExportServiceDescriptionsAction.class); + + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public ImportServiceDescriptionsFromURLAction(ServiceDescriptionRegistry serviceDescriptionRegistry) { + super(IMPORT_SERVICES_FROM_URL); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + private static final Object[] CHOICES = { "Add to current services", + "Replace current services", "Cancel" }; + + @Override + public void actionPerformed(ActionEvent e) { + JComponent parentComponent = null; + if (e.getSource() instanceof JComponent) + parentComponent = (JComponent) e.getSource(); + + if (ExportServiceDescriptionsAction.INHIBIT) { + showMessageDialog(parentComponent, + "Operation not currently working correctly", + "Not Implemented", ERROR_MESSAGE); + return; + } + + int choice = showOptionDialog( + parentComponent, + "Do you want to add the imported services to the current ones or replace the current ones?", + "Import services", YES_NO_CANCEL_OPTION, QUESTION_MESSAGE, + null, CHOICES, CHOICES[0]); + + if (choice != CANCEL_OPTION) { + final String urlString = (String) showInputDialog(parentComponent, + "Enter the URL of the service descriptions file to import", + "Service Descriptions URL", QUESTION_MESSAGE, null, null, + "http://"); + try { + if (urlString != null && !urlString.isEmpty()) + // Did user want to replace or add services? + importServices(urlString, choice == YES_OPTION); + } catch (Exception ex) { + logger.error( + "Service descriptions import: failed to import services from " + + urlString, ex); + showMessageDialog(parentComponent, + "Failed to import services from " + urlString, "Error", + ERROR_MESSAGE); + } + } + + if (parentComponent instanceof JButton) + // lose the focus from the button after performing the action + parentComponent.requestFocusInWindow(); + } + + private void importServices(final String urlString, final boolean addToCurrent) + throws Exception { + // TODO: Open in separate thread to avoid hanging UI + URL url = new URL(urlString); + + if (!addToCurrent) + for (ServiceDescriptionProvider provider : new HashSet<>( + serviceDescriptionRegistry.getServiceDescriptionProviders())) + // remove all configurable service providers + if (provider instanceof ConfigurableServiceProvider) + serviceDescriptionRegistry + .removeServiceDescriptionProvider(provider); + + // import all providers from the URL + serviceDescriptionRegistry.loadServiceProviders(url); + serviceDescriptionRegistry.saveServiceDescriptions(); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java new file mode 100644 index 0000000..9c4c84b --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RefreshProviderRegistryAction.java
@@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +/** + * Action for refreshing the service provider registry. + * <p> + * This would typically re-parse WSDLs, etc. + * + * @see ServiceDescriptionRegistry#refresh() + * @author Stian Soiland-Reyes + */ +@SuppressWarnings("serial") +public class RefreshProviderRegistryAction extends AbstractAction { + private static final String REFRESH = "Reload services"; + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public RefreshProviderRegistryAction( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + super(REFRESH); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + @Override + public void actionPerformed(ActionEvent e) { + serviceDescriptionRegistry.refresh(); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java new file mode 100644 index 0000000..b6ba606 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveDefaultServicesAction.java
@@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +@SuppressWarnings("serial") +public class RemoveDefaultServicesAction extends AbstractAction { + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public RemoveDefaultServicesAction( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + super("Remove default service providers"); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + @Override + public void actionPerformed(ActionEvent e) { + for (ServiceDescriptionProvider provider : serviceDescriptionRegistry + .getDefaultServiceDescriptionProviders()) { + if (!(provider instanceof ConfigurableServiceProvider)) + continue; + serviceDescriptionRegistry + .removeServiceDescriptionProvider(provider); + } + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java new file mode 100644 index 0000000..bf0a771 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RemoveUserServicesAction.java
@@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import static javax.swing.JOptionPane.YES_NO_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JLabel; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +@SuppressWarnings("serial") +public class RemoveUserServicesAction extends AbstractAction { + private static final String CONFIRM_MESSAGE = "You are about to remove all services you have added. <br>" + + "Are you <b>really</b> sure you want to do this?"; + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public RemoveUserServicesAction( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + super("Remove all user added service providers"); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + @Override + public void actionPerformed(ActionEvent e) { + int option = showConfirmDialog(null, new JLabel("<html><body>" + + CONFIRM_MESSAGE + "</body></html>"), + "Confirm service deletion", YES_NO_OPTION); + + if (option == YES_OPTION) + for (ServiceDescriptionProvider provider : serviceDescriptionRegistry + .getUserAddedServiceProviders()) + serviceDescriptionRegistry + .removeServiceDescriptionProvider(provider); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java new file mode 100644 index 0000000..c7071ed --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/actions/RestoreDefaultServicesAction.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; + +@SuppressWarnings("serial") +public class RestoreDefaultServicesAction extends AbstractAction { + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public RestoreDefaultServicesAction( + ServiceDescriptionRegistry serviceDescriptionRegistry) { + super("Restore default service providers"); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + @Override + public void actionPerformed(ActionEvent e) { + for (ServiceDescriptionProvider provider : serviceDescriptionRegistry + .getDefaultServiceDescriptionProviders()) { + if (!(provider instanceof ConfigurableServiceProvider)) + continue; + serviceDescriptionRegistry.addServiceDescriptionProvider(provider); + } + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java new file mode 100644 index 0000000..f666877 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigPanel.java
@@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.config; + +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.WEST; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration; +import net.sf.taverna.t2.workbench.helper.Helper; + +@SuppressWarnings("serial") +public class ServiceDescriptionConfigPanel extends JPanel { + private static final String REMOVE_PERMANENTLY = "Allow permanent removal of default service providers"; + private static final String INCLUDE_DEFAULTS = "Include default service providers"; + + private final ServiceDescriptionsConfiguration config; + private JCheckBox includeDefaults; + private JCheckBox removePermanently; + private final ServiceDescriptionRegistry serviceDescRegistry; + + public ServiceDescriptionConfigPanel(ServiceDescriptionsConfiguration config, + ServiceDescriptionRegistry serviceDescRegistry) { + super(new GridBagLayout()); + this.config = config; + this.serviceDescRegistry = serviceDescRegistry; + initialize(); + } + + private void initialize() { + removeAll(); + + GridBagConstraints gbc = new GridBagConstraints(); + + // Title describing what kind of settings we are configuring here + JTextArea descriptionText = new JTextArea( + "Configure behaviour of default service providers in Service Panel"); + descriptionText.setLineWrap(true); + descriptionText.setWrapStyleWord(true); + descriptionText.setEditable(false); + descriptionText.setFocusable(false); + descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10)); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.anchor = WEST; + gbc.fill = HORIZONTAL; + add(descriptionText, gbc); + + includeDefaults = new JCheckBox(INCLUDE_DEFAULTS); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.anchor = WEST; + gbc.fill = NONE; + gbc.insets = new Insets(10, 0, 0, 0); + add(includeDefaults, gbc); + + removePermanently = new JCheckBox(REMOVE_PERMANENTLY); + gbc.gridx = 0; + gbc.gridy = 2; + gbc.insets = new Insets(0, 0, 0, 0); + add(removePermanently, gbc); + + // Filler + gbc.gridx = 0; + gbc.gridy = 3; + gbc.weighty = 1; + gbc.weightx = 1; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 0, 0, 0); + add(createButtonPanel(), gbc); + + setFields(config); + } + + /** + * Create the panel to contain the buttons + * + * @return + */ + private JPanel createButtonPanel() { + final JPanel panel = new JPanel(); + + /** + * The helpButton shows help about the current component + */ + JButton helpButton = new JButton(new AbstractAction("Help") { + @Override + public void actionPerformed(ActionEvent arg0) { + Helper.showHelp(panel); + } + }); + panel.add(helpButton); + + /** + * The resetButton changes the property values shown to those + * corresponding to the configuration currently applied. + */ + JButton resetButton = new JButton(new AbstractAction("Reset") { + @Override + public void actionPerformed(ActionEvent arg0) { + setFields(config); + } + }); + panel.add(resetButton); + + /** + * The applyButton applies the shown field values to the + * {@link HttpProxyConfiguration} and saves them for future. + */ + JButton applyButton = new JButton(new AbstractAction("Apply") { + @Override + public void actionPerformed(ActionEvent arg0) { + applySettings(); + setFields(config); + } + }); + panel.add(applyButton); + + return panel; + } + + protected void applySettings() { + // Include default service providers + config.setIncludeDefaults(includeDefaults.isSelected()); + for (ServiceDescriptionProvider provider : serviceDescRegistry + .getDefaultServiceDescriptionProviders()) { + if (! (provider instanceof ConfigurableServiceProvider)) + continue; + if (config.isIncludeDefaults()) + serviceDescRegistry.addServiceDescriptionProvider(provider); + else + serviceDescRegistry.removeServiceDescriptionProvider(provider); + } + + // Allow permanent removal of default service providers + config.setRemovePermanently(removePermanently.isSelected()); + } + + /** + * Set the shown configuration field values to those currently in use + * (i.e. last saved configuration). + * + */ + private void setFields(ServiceDescriptionsConfiguration configurable) { + includeDefaults.setSelected(configurable.isIncludeDefaults()); + removePermanently.setSelected(configurable.isRemovePermanently()); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java new file mode 100644 index 0000000..8746b54 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java
@@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.config; + +import javax.swing.JPanel; + +import uk.org.taverna.configuration.Configurable; +import uk.org.taverna.configuration.ConfigurationUIFactory; + +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration; + +public class ServiceDescriptionConfigUIFactory implements ConfigurationUIFactory { + private ServiceDescriptionsConfiguration serviceDescriptionsConfiguration; + private ServiceDescriptionRegistry serviceDescriptionRegistry; + + @Override + public boolean canHandle(String uuid) { + return uuid.equals(serviceDescriptionsConfiguration.getUUID()); + } + + @Override + public Configurable getConfigurable() { + return serviceDescriptionsConfiguration; + } + + @Override + public JPanel getConfigurationPanel() { + return new ServiceDescriptionConfigPanel(serviceDescriptionsConfiguration, serviceDescriptionRegistry); + } + + public void setServiceDescriptionRegistry(ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + } + + public void setServiceDescriptionsConfiguration(ServiceDescriptionsConfiguration serviceDescriptionsConfiguration) { + this.serviceDescriptionsConfiguration = serviceDescriptionsConfiguration; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java new file mode 100644 index 0000000..f975778 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java
@@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.menu; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JPopupMenu; + +import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanel; +import net.sf.taverna.t2.workbench.ui.servicepanel.actions.AddServiceProviderAction; + +/** + * A menu that provides a set up menu actions for adding new service providers + * to the Service Panel. + * <p> + * The Actions are discovered from the {@link ServiceDescriptionProvider}s found + * through the SPI. + * + * @author Stuart Owen + * @author Stian Soiland-Reyes + * @author Alan R Williams + * + * @see ServiceDescription + * @see ServicePanel + * @see ServiceDescriptionRegistry#addServiceDescriptionProvider(ServiceDescriptionProvider) + */ +@SuppressWarnings("serial") +public class AddServiceProviderMenu extends JButton { + public static class ServiceProviderComparator implements + Comparator<ServiceDescriptionProvider> { + @Override + public int compare(ServiceDescriptionProvider o1, + ServiceDescriptionProvider o2) { + return o1.getName().toLowerCase().compareTo( + o2.getName().toLowerCase()); + } + } + + private final static String ADD_SERVICE_PROVIDER_MENU_NAME = "Import new services"; + + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public AddServiceProviderMenu(ServiceDescriptionRegistry serviceDescriptionRegistry) { + super(); + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + + final Component c = createCustomComponent(); + setAction(new AbstractAction(ADD_SERVICE_PROVIDER_MENU_NAME) { + @Override + public void actionPerformed(ActionEvent e) { + ((JPopupMenu) c).show(AddServiceProviderMenu.this, 0, + AddServiceProviderMenu.this.getHeight()); + } + }); + } + + private Component createCustomComponent() { + JPopupMenu addServiceMenu = new JPopupMenu( + ADD_SERVICE_PROVIDER_MENU_NAME); + addServiceMenu.setToolTipText("Add a new service provider"); + boolean isEmpty = true; + List<ConfigurableServiceProvider> providers = new ArrayList<>( + serviceDescriptionRegistry.getUnconfiguredServiceProviders()); + Collections.sort(providers, new ServiceProviderComparator()); + for (ConfigurableServiceProvider provider : providers) { + /* + * Skip BioCatalogue's ConfigurableServiceProviderS as they should + * not be used to add servcie directlry but rather though the + * Service Catalogue perspective + */ + if (provider.getId().toLowerCase().contains("servicecatalogue")) + continue; + + AddServiceProviderAction addAction = new AddServiceProviderAction( + provider, this); + addAction.setServiceDescriptionRegistry(serviceDescriptionRegistry); + addServiceMenu.add(addAction); + isEmpty = false; + } + if (isEmpty) + addServiceMenu.setEnabled(false); + return addServiceMenu; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java new file mode 100644 index 0000000..e67e8f5 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java
@@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.tree; + +import javax.swing.tree.DefaultMutableTreeNode; + +public interface Filter { + boolean pass(DefaultMutableTreeNode node); + + String filterRepresentation(String original); + + void setSuperseded(boolean superseded); + + boolean isSuperseded(); +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java new file mode 100644 index 0000000..21f43c5 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java
@@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.tree; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.folderClosedIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.folderOpenIcon; + +import java.awt.Component; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + +@SuppressWarnings("serial") +public class FilterTreeCellRenderer extends DefaultTreeCellRenderer { + private Filter filter = null; + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean sel, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + + super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, + row, hasFocus); + Filter filter = getFilter(); + if (filter != null) + setText(filter.filterRepresentation(getText())); + if (expanded) + setIcon(folderOpenIcon); + else + setIcon(folderClosedIcon); + return this; + } + + public Filter getFilter() { + return filter; + } + + public void setFilter(Filter currentFilter) { + this.filter = currentFilter; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java new file mode 100644 index 0000000..191ed66 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java
@@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.tree; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; + +import org.apache.log4j.Logger; + +public final class FilterTreeModel extends DefaultTreeModel { + private static final long serialVersionUID = -8931308369832839862L; + private static final Logger logger = Logger + .getLogger(FilterTreeModel.class); + + Filter currentFilter; + + public FilterTreeModel(FilterTreeNode node) { + this(node, null); + } + + public FilterTreeModel(FilterTreeNode node, Filter filter) { + super(node); + currentFilter = filter; + node.setFilter(filter); + } + + public void setFilter(Filter filter) { + if (root != null) { + currentFilter = filter; + ((FilterTreeNode) root).setFilter(filter); + Object[] path = { root }; + fireTreeStructureChanged(this, path, null, null); + } + } + + @Override + public int getChildCount(Object parent) { + if (parent instanceof FilterTreeNode) + return (((FilterTreeNode) parent).getChildCount()); + return 0; + } + + @Override + public Object getChild(Object parent, int index) { + if (parent instanceof FilterTreeNode) + return (((FilterTreeNode) parent).getChildAt(index)); + return null; + } + + /** + * @return the currentFilter + */ + public Filter getCurrentFilter() { + return currentFilter; + } + + public TreePath getTreePathForObjectPath(List<Object> path) { + List<FilterTreeNode> resultList = new ArrayList<>(); + FilterTreeNode current = (FilterTreeNode) root; + resultList.add(current); + for (int i = 1; (i < path.size()) && (current != null); i++) { + logger.debug("Looking in " + current.getUserObject() + " for " + path.get(i)); + current = current.getChildForObject(path.get(i)); + if (current != null) + resultList.add(current); + } + if (current != null) + return new TreePath(resultList.toArray()); + return null; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java new file mode 100644 index 0000000..83fd439 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java
@@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.tree; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.tree.DefaultMutableTreeNode; + +import org.apache.log4j.Logger; + +public class FilterTreeNode extends DefaultMutableTreeNode { + private static final long serialVersionUID = 1933553584349932151L; + @SuppressWarnings("unused") + private static Logger logger = Logger.getLogger(FilterTreeNode.class); + + private Filter filter; + private boolean passed = true; + private List<FilterTreeNode> filteredChildren = new ArrayList<>(); + + public FilterTreeNode(Object userObject) { + super(userObject); + userObject.toString(); + } + + public Filter getFilter() { + return filter; + } + + public void setFilter(Filter filter) { + if ((filter == null) || !filter.isSuperseded()) { + this.filter = filter; + passed = false; + filteredChildren.clear(); + if (filter == null) { + passed = true; + passFilterDown(null); + } else if (filter.pass(this)) { + passed = true; + passFilterDown(null); + } else { + passFilterDown(filter); + passed = filteredChildren.size() != 0; + } + } + } + + private void passFilterDown(Filter filter) { + int realChildCount = super.getChildCount(); + for (int i = 0; i < realChildCount; i++) { + FilterTreeNode realChild = (FilterTreeNode) super.getChildAt(i); + realChild.setFilter(filter); + if (realChild.isPassed()) + filteredChildren.add(realChild); + } + } + + public void add(FilterTreeNode node) { + super.add(node); + node.setFilter(filter); + // TODO work up + if (node.isPassed()) + filteredChildren.add(node); + } + + @Override + public void remove(int childIndex) { + if (filter != null) + // as child indexes might be inconsistent.. + throw new IllegalStateException("Can't remove while the filter is active"); + super.remove(childIndex); + } + + @Override + public int getChildCount() { + if (filter == null) + return super.getChildCount(); + return filteredChildren.size(); + } + + @Override + public FilterTreeNode getChildAt(int index) { + if (filter == null) + return (FilterTreeNode) super.getChildAt(index); + return filteredChildren.get(index); + } + + public boolean isPassed() { + return passed; + } + + public Set<FilterTreeNode> getLeaves() { + Set<FilterTreeNode> result = new HashSet<>(); + if (super.getChildCount() == 0) { + result.add(this); + return result; + } + + for (int i = 0; i < super.getChildCount(); i++) { + FilterTreeNode child = (FilterTreeNode) super.getChildAt(i); + result.addAll(child.getLeaves()); + } + return result; + } + + public FilterTreeNode getChildForObject(Object userObject) { + FilterTreeNode result = null; + for (int i=0; (i < super.getChildCount()) && (result == null); i++) { + FilterTreeNode child = (FilterTreeNode) super.getChildAt(i); + Object nodeObject = child.getUserObject(); +// logger.info("nodeObject is a " + nodeObject.getClass() + " - " + +// "userObject is a " + userObject.getClass()); + if (nodeObject.toString().equals(userObject.toString())) { + result = child; +// logger.info(nodeObject + " is equal to " + userObject); +// } else { +// logger.info(nodeObject + " is not equal to " + userObject); + } + } + return result; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java new file mode 100644 index 0000000..a5adfe9 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.tree; + +import javax.swing.tree.DefaultTreeSelectionModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +public class FilterTreeSelectionModel extends DefaultTreeSelectionModel{ + private static final long serialVersionUID = 3127644524735089630L; + + public FilterTreeSelectionModel(){ + super(); + setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + } + + @Override + public void setSelectionPath(TreePath path) { + /* + * Nothing happens here - only calls to mySetSelectionPath() will have + * the effect of a node being selected. + */ + } + + public void mySetSelectionPath(TreePath path) { + super.setSelectionPath(path); + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java new file mode 100644 index 0000000..8baa0eb --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java
@@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.tree; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class MyFilter implements Filter { + private static final String HTML_MATCH_END = "</font><font color=\"black\">"; + private static final String HTML_MATCH_START = "</font><font color=\"red\">"; + private static final String HTML_POSTFIX = "</font></html>"; + private static final String HTML_PREFIX = "<html><font color=\"black\">"; + + private String filterString; + private boolean superseded; + private String filterLowerCase; + + public MyFilter(String filterString) { + this.filterString = filterString; + this.filterLowerCase = filterString.toLowerCase(); + this.superseded = false; + } + + private boolean basicFilter(DefaultMutableTreeNode node) { + if (filterString.isEmpty()) + return true; + return node.getUserObject().toString().toLowerCase() + .contains(filterLowerCase); + } + + @Override + public boolean pass(DefaultMutableTreeNode node) { + return basicFilter(node); + } + + @Override + public String filterRepresentation(String original) { + StringBuilder sb = new StringBuilder(HTML_PREFIX); + int from = 0; + String originalLowerCase = original.toLowerCase(); + int index = originalLowerCase.indexOf(filterLowerCase, from); + while (index > -1) { + sb.append(original.substring(from, index)); + sb.append(HTML_MATCH_START); + sb.append(original.substring(index, + index + filterLowerCase.length())); + sb.append(HTML_MATCH_END); + from = index + filterLowerCase.length(); + index = originalLowerCase.indexOf(filterLowerCase, from); + } + if (from < original.length()) + sb.append(original.substring(from, original.length())); + return sb.append(HTML_POSTFIX).toString(); + } + + /** + * @return the superseded + */ + @Override + public boolean isSuperseded() { + return superseded; + } + + /** + * @param superseded + * the superseded to set + */ + @Override + public void setSuperseded(boolean superseded) { + this.superseded = superseded; + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java new file mode 100644 index 0000000..46eca53 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java
@@ -0,0 +1,371 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.servicepanel.tree; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.WEST; +import static java.awt.Color.GRAY; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NONE; +import static javax.swing.SwingUtilities.invokeLater; +import static net.sf.taverna.t2.lang.ui.EdgeLineBorder.TOP; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; + +import net.sf.taverna.t2.lang.ui.EdgeLineBorder; + +import org.apache.log4j.Logger; + +@SuppressWarnings("serial") +public abstract class TreePanel extends JPanel { + private static int MAX_EXPANSION = 100; + private static final int SEARCH_WIDTH = 15; + private static Logger logger = Logger.getLogger(TreePanel.class); + + protected Set<List<Object>> expandedPaths = new HashSet<>(); + protected FilterTreeModel filterTreeModel; + protected JTextField searchField = new JTextField(SEARCH_WIDTH); + protected JTree tree = new JTree(); + protected JScrollPane treeScrollPane; + + private String availableObjectsString = ""; + private String matchingObjectsString = ""; + private String noMatchingObjectsString = ""; + + private TreeExpandCollapseListener treeExpandListener = new TreeExpandCollapseListener(); + private Object filterLock = new Object(); + + public TreePanel(FilterTreeModel treeModel) { + filterTreeModel = treeModel; + } + + public void expandTreePaths() throws InterruptedException, + InvocationTargetException { +// Filter appliedFilter = filterTreeModel.getCurrentFilter(); +// if (appliedFilter == null) { + for (int i = 0; (i < tree.getRowCount()) && (i < MAX_EXPANSION); i++) + tree.expandRow(i); +// } else { +// boolean rowsFinished = false; +// for (int i = 0; (!appliedFilter.isSuperseded()) && (!rowsFinished) +// && (i < MAX_EXPANSION); i++) { +// TreePath tp = tree.getPathForRow(i); +// if (tp == null) { +// rowsFinished = true; +// } else { +// if (!appliedFilter.pass((DefaultMutableTreeNode) tp +// .getLastPathComponent())) { +// tree.expandRow(i); +// } +// } +// } +// } + } + + public void expandAll(FilterTreeNode node, boolean expand) { + @SuppressWarnings("unused") + FilterTreeNode root = (FilterTreeNode) tree.getModel().getRoot(); + + // Traverse tree from root + expandAll(new TreePath(node.getPath()), expand); + } + + @SuppressWarnings("rawtypes") + private void expandAll(TreePath parent, boolean expand) { + // Traverse children + FilterTreeNode node = (FilterTreeNode) parent.getLastPathComponent(); + if (node.getChildCount() >= 0) + for (Enumeration e=node.children(); e.hasMoreElements(); ) { + FilterTreeNode n = (FilterTreeNode) e.nextElement(); + TreePath path = parent.pathByAddingChild(n); + expandAll(path, expand); + } + + // Expansion or collapse must be done bottom-up + if (expand) + tree.expandPath(parent); + else + tree.collapsePath(parent); + } + + protected void initialize() { + setLayout(new BorderLayout()); + treeScrollPane = new JScrollPane(tree); + tree.setModel(filterTreeModel); + tree.addTreeExpansionListener(treeExpandListener); + tree.setCellRenderer(createCellRenderer()); + tree.setSelectionModel(new FilterTreeSelectionModel()); + + JPanel topPanel = new JPanel(); + topPanel.setBorder(new CompoundBorder(new EdgeLineBorder(TOP, GRAY), new EmptyBorder(10, 5, 0, 5))); + topPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + + JLabel filterLabel = new JLabel("Filter: "); + c.fill = NONE; + c.gridx = 0; + c.gridy = 0; + c.weightx = 0.0; + c.anchor = GridBagConstraints.LINE_START; + topPanel.add(filterLabel, c); + + c.fill = HORIZONTAL; + c.gridx = 1; + c.gridy = 0; + c.weightx = 1.0; + topPanel.add(searchField, c); + + + c.fill = NONE; + c.gridx = 2; + c.gridy = 0; + c.weightx = 0.0; + final JButton clearButton = new JButton("Clear"); + clearButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + searchField.setText(""); + invokeLater(new RunFilter()); + clearButton.getParent().requestFocusInWindow();// so that the button does not stay focused after it is clicked on and did its action + } + }); + topPanel.add(clearButton, c); + + c.gridx = 3; + c.weightx = 0.2; + topPanel.add(new JPanel(), c); + + JPanel topExtraPanel = new JPanel(new BorderLayout()); + + topExtraPanel.add(topPanel, NORTH); + + Component extraComponent = createExtraComponent(); + if (extraComponent != null) { + JPanel extraPanel = new JPanel(); + extraPanel.setLayout(new BorderLayout()); + extraPanel.add(extraComponent, WEST); + topExtraPanel.add(extraPanel, CENTER); + } + + add(topExtraPanel, NORTH); + add(treeScrollPane, CENTER); + + searchField.addKeyListener(new SearchFieldKeyAdapter()); + } + + protected Component createExtraComponent() { + return null; + } + + protected TreeCellRenderer createCellRenderer() { + return new FilterTreeCellRenderer(); + } + + public void runFilter() throws InterruptedException, + InvocationTargetException { + /* + * Special lock object, don't do a synchronized model, as the lock on + * JComponent might deadlock when painting the panel - see comments at + * http://www.mygrid.org.uk/dev/issues/browse/T2-1438 + */ + synchronized (filterLock) { + tree.removeTreeExpansionListener(treeExpandListener); + String text = searchField.getText(); + FilterTreeNode root = (FilterTreeNode) tree.getModel().getRoot(); + if (text.isEmpty()) { + setFilter(null); + root.setUserObject(getAvailableObjectsString()); + filterTreeModel.nodeChanged(root); + for (List<Object> tp : expandedPaths) { + // for (int i = 0; i < tp.length; i++) + // logger.info("Trying to expand " + tp[i]); + tree.expandPath(filterTreeModel.getTreePathForObjectPath(tp)); + } + } else { + setFilter(createFilter(text)); + root.setUserObject(root.getChildCount() > 0 ? getMatchingObjectsString() + : getNoMatchingObjectsString()); + filterTreeModel.nodeChanged(root); + expandTreePaths(); + } + tree.addTreeExpansionListener(treeExpandListener); + } + } + + /** + * @return the availableObjectsString + */ + public String getAvailableObjectsString() { + return availableObjectsString; + } + + /** + * @param availableObjectsString the availableObjectsString to set + */ + public void setAvailableObjectsString(String availableObjectsString) { + this.availableObjectsString = availableObjectsString; + } + + /** + * @return the matchingObjectsString + */ + public String getMatchingObjectsString() { + return matchingObjectsString; + } + + /** + * @param matchingObjectsString the matchingObjectsString to set + */ + public void setMatchingObjectsString(String matchingObjectsString) { + this.matchingObjectsString = matchingObjectsString; + } + + /** + * @return the noMatchingObjectsString + */ + public String getNoMatchingObjectsString() { + return noMatchingObjectsString; + } + + /** + * @param noMatchingObjectsString the noMatchingObjectsString to set + */ + public void setNoMatchingObjectsString(String noMatchingObjectsString) { + this.noMatchingObjectsString = noMatchingObjectsString; + } + + public Filter createFilter(String text) { + return new MyFilter(text); + } + + public void setFilter(Filter filter) { + if (tree.getCellRenderer() instanceof FilterTreeCellRenderer) + ((FilterTreeCellRenderer)tree.getCellRenderer()).setFilter(filter); + filterTreeModel.setFilter(filter); + } + + protected class ExpandRowRunnable implements Runnable { + int rowNumber; + + public ExpandRowRunnable(int rowNumber) { + this.rowNumber = rowNumber; + } + + @Override + public void run() { + tree.expandRow(rowNumber); + } + } + + protected class RunFilter implements Runnable { + @Override + public void run() { + Filter oldFilter = filterTreeModel.getCurrentFilter(); + if (oldFilter != null) + oldFilter.setSuperseded(true); + try { + runFilter(); + } catch (InterruptedException e) { + Thread.interrupted(); + } catch (InvocationTargetException e) { + logger.error("", e); + } + } + } + + protected class SearchFieldKeyAdapter extends KeyAdapter { + private final Runnable runFilterRunnable; + Timer timer = new Timer("Search field timer", true); + + private SearchFieldKeyAdapter() { + this.runFilterRunnable = new RunFilter(); + } + + @Override + public void keyReleased(KeyEvent e) { + timer.cancel(); + timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + invokeLater(runFilterRunnable); + } + }, 500); + } + } + + private void noteExpansions() { + expandedPaths.clear(); + TreePath rootPath = new TreePath(filterTreeModel.getRoot()); + for (Enumeration<TreePath> e = tree.getExpandedDescendants(rootPath); e.hasMoreElements();) { + List<Object> userObjects = new ArrayList<>(); + Object[] expandedPath = e.nextElement().getPath(); + for (int i = 0; i < expandedPath.length; i++) { + FilterTreeNode node = (FilterTreeNode) expandedPath[i]; +// logger.info("The object in the path is a " + expandedPath[i].getClass()); + userObjects.add(node.getUserObject()); +// logger.info("Added " + node.getUserObject() + " to path"); + } + expandedPaths.add(userObjects); + } + } + + protected class TreeExpandCollapseListener implements TreeExpansionListener { + @Override + public void treeCollapsed(TreeExpansionEvent event) { + noteExpansions(); + } + + @Override + public void treeExpanded(TreeExpansionEvent event) { + noteExpansions(); + } + } +}
diff --git a/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI new file mode 100644 index 0000000..bb87331 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanelComponentFactory \ No newline at end of file
diff --git a/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context-osgi.xml b/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context-osgi.xml new file mode 100644 index 0000000..2d96b28 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context-osgi.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="ServiceDescriptionConfigUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" /> + + <service ref="ServicePanelComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + <reference id="serviceDescriptionRegistry" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry"/> + <reference id="serviceDescriptionsConfiguration" interface="net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration"/> + <reference id="serviceRegistry" interface="uk.org.taverna.commons.services.ServiceRegistry" /> + +</beans:beans>
diff --git a/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context.xml b/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context.xml new file mode 100644 index 0000000..f0a11c1 --- /dev/null +++ b/taverna-workbench-activity-palette-ui/src/main/resources/META-INF/spring/activity-palette-ui-context.xml
@@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="ServiceDescriptionConfigUIFactory" + class="net.sf.taverna.t2.workbench.ui.servicepanel.config.ServiceDescriptionConfigUIFactory"> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="serviceDescriptionsConfiguration" ref="serviceDescriptionsConfiguration" /> + </bean> + + <bean id="ServicePanelComponentFactory" + class="net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanelComponentFactory"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="serviceDescriptionRegistry" ref="serviceDescriptionRegistry" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> + +</beans>
diff --git a/taverna-workbench-activity-tools/pom.xml b/taverna-workbench-activity-tools/pom.xml new file mode 100644 index 0000000..c84a263 --- /dev/null +++ b/taverna-workbench-activity-tools/pom.xml
@@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-tools</artifactId> + <packaging>bundle</packaging> + <name>Activity tools</name> + <description>Tools useful for ui-activitys</description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-activity-tools/src/main/java/net/sf/taverna/t2/workbench/activitytools/AbstractConfigureActivityMenuAction.java b/taverna-workbench-activity-tools/src/main/java/net/sf/taverna/t2/workbench/activitytools/AbstractConfigureActivityMenuAction.java new file mode 100644 index 0000000..4744774 --- /dev/null +++ b/taverna-workbench-activity-tools/src/main/java/net/sf/taverna/t2/workbench/activitytools/AbstractConfigureActivityMenuAction.java
@@ -0,0 +1,64 @@ +package net.sf.taverna.t2.workbench.activitytools; + +import static javax.swing.Action.NAME; + +import java.awt.Frame; +import java.net.URI; + +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.ui.Utils; + +public abstract class AbstractConfigureActivityMenuAction extends AbstractContextualMenuAction { + private static final URI configureSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configure"); + + protected Scufl2Tools scufl2Tools = new Scufl2Tools(); + protected final URI activityType; + + public AbstractConfigureActivityMenuAction(URI activityType) { + super(configureSection, 50); + this.activityType = activityType; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() && findActivity() != null; + } + + protected Activity findActivity() { + if (getContextualSelection() == null) + return null; + Object selection = getContextualSelection().getSelection(); + if (selection instanceof Activity) { + Activity activity = (Activity) selection; + if (activity.getType().equals(activityType)) + return activity; + } + if (selection instanceof Processor) { + Processor processor = (Processor) selection; + Profile profile = processor.getParent().getParent().getMainProfile(); + for (ProcessorBinding processorBinding : scufl2Tools.processorBindingsForProcessor(processor, profile)) + if (processorBinding.getBoundActivity().getType().equals(activityType)) + return processorBinding.getBoundActivity(); + } + return null; + } + + protected Frame getParentFrame() { + return Utils.getParentFrame(getContextualSelection() + .getRelativeToComponent()); + } + + protected void addMenuDots(Action configAction) { + String oldName = (String) configAction.getValue(NAME); + if (!oldName.endsWith("..")) + configAction.putValue(NAME, oldName + "..."); + } +} \ No newline at end of file
diff --git a/taverna-workbench-configuration-api/pom.xml b/taverna-workbench-configuration-api/pom.xml new file mode 100644 index 0000000..81e819f --- /dev/null +++ b/taverna-workbench-configuration-api/pom.xml
@@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>configuration-api</artifactId> + <packaging>bundle</packaging> + <name>Configuration Management API</name> + <description>General configuration management</description> + + <dependencies> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-api</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/colour/ColourManager.java b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/colour/ColourManager.java new file mode 100644 index 0000000..4d5356f --- /dev/null +++ b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/colour/ColourManager.java
@@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (C) 2011 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.configuration.colour; + +import java.awt.Color; + +import uk.org.taverna.configuration.Configurable; + +/** + * @author David Withers + */ +public interface ColourManager extends Configurable { + /** + * Builds a Color that has been configured and associated with the given + * String (usually an object type). + * + * @return the associated Color, or if nothing is associated returns + * {@link Color#WHITE}. + */ + Color getPreferredColour(String itemKey); + + void setPreferredColour(String itemKey, Color colour); +} \ No newline at end of file
diff --git a/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/mimetype/MimeTypeManager.java b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/mimetype/MimeTypeManager.java new file mode 100644 index 0000000..f0ae0d3 --- /dev/null +++ b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/mimetype/MimeTypeManager.java
@@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.configuration.mimetype; + +import java.util.Map; + +import uk.org.taverna.configuration.Configurable; + +public interface MimeTypeManager extends Configurable { + @Override + String getCategory(); + + @Override + Map<String, String> getDefaultPropertyMap(); + + @Override + String getUUID(); + + @Override + String getDisplayName(); + + @Override + String getFilePrefix(); +}
diff --git a/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/WorkbenchConfiguration.java b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/WorkbenchConfiguration.java new file mode 100644 index 0000000..461ba5c --- /dev/null +++ b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/WorkbenchConfiguration.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (C) 2011 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.configuration.workbench; + +import uk.org.taverna.configuration.Configurable; + +/** + * @author David Withers + */ +public interface WorkbenchConfiguration extends Configurable { + boolean getCaptureConsole(); + + void setCaptureConsole(boolean captureConsole); + + boolean getWarnInternalErrors(); + + void setWarnInternalErrors(boolean warnInternalErrors); + + int getMaxMenuItems(); + + void setMaxMenuItems(int maxMenuItems); + + String getDotLocation(); + + void setDotLocation(String dotLocation); +}
diff --git a/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/ui/T2ConfigurationFrame.java b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/ui/T2ConfigurationFrame.java new file mode 100644 index 0000000..577484f --- /dev/null +++ b/taverna-workbench-configuration-api/src/main/java/net/sf/taverna/t2/workbench/configuration/workbench/ui/T2ConfigurationFrame.java
@@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.configuration.workbench.ui; + +/** + * @author David Withers + */ +public interface T2ConfigurationFrame { + void showFrame(); + + void showConfiguration(String name); +} \ No newline at end of file
diff --git a/taverna-workbench-configuration-impl/pom.xml b/taverna-workbench-configuration-impl/pom.xml new file mode 100644 index 0000000..19356bb --- /dev/null +++ b/taverna-workbench-configuration-impl/pom.xml
@@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>configuration-impl</artifactId> + <packaging>bundle</packaging> + <name>Configuration Management Implementations</name> + <description>General configuration management</description> + + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>configuration-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-impl</artifactId> + <version>0.1.1-SNAPSHOT</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-impl</artifactId> + <version>0.1.1-SNAPSHOT</version> + <scope>test</scope> + </dependency> + </dependencies> + +</project>
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java new file mode 100644 index 0000000..0e63a4a --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationImpl.java
@@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; + +import org.apache.log4j.Logger; + +import uk.org.taverna.configuration.AbstractConfigurable; +import uk.org.taverna.configuration.ConfigurationManager; +import uk.org.taverna.configuration.app.ApplicationConfiguration; + +/** + * An implementation of Configurable for general Workbench configuration + * properties + * + * @author Stuart Owen + * @author Stian Soiland-Reyes + */ +public class WorkbenchConfigurationImpl extends AbstractConfigurable implements + WorkbenchConfiguration { + private static Logger logger = Logger + .getLogger(WorkbenchConfiguration.class); + private static final int DEFAULT_MAX_MENU_ITEMS = 20; + public static final String TAVERNA_DOTLOCATION = "taverna.dotlocation"; + public static final String MAX_MENU_ITEMS = "taverna.maxmenuitems"; + public static final String WARN_INTERNAL_ERRORS = "taverna.warninternal"; + public static final String CAPTURE_CONSOLE = "taverna.captureconsole"; + private static final String BIN = "bin"; + private static final String BUNDLE_CONTENTS = "Contents"; + private static final String BUNDLE_MAC_OS = "MacOS"; + private static final String DOT_EXE = "dot.exe"; + private static final String DOT_FALLBACK = "dot"; + public static String uuid = "c14856f0-5967-11dd-ae16-0800200c9a66"; + private static final String MAC_OS_X = "Mac OS X"; + private static final String WIN32I386 = "win32i386"; + private static final String WINDOWS = "Windows"; + + private ApplicationConfiguration applicationConfiguration; + + /** + * Constructs a new <code>WorkbenchConfigurationImpl</code>. + * + * @param configurationManager + */ + public WorkbenchConfigurationImpl(ConfigurationManager configurationManager) { + super(configurationManager); + } + + Map<String, String> defaultWorkbenchProperties = null; + Map<String, String> workbenchProperties = new HashMap<String, String>(); + + @Override + public String getCategory() { + return "general"; + } + + @Override + public Map<String, String> getDefaultPropertyMap() { + if (defaultWorkbenchProperties == null) { + defaultWorkbenchProperties = new HashMap<>(); + String dotLocation = System.getProperty(TAVERNA_DOTLOCATION) != null ? System + .getProperty(TAVERNA_DOTLOCATION) : getDefaultDotLocation(); + if (dotLocation != null) + defaultWorkbenchProperties + .put(TAVERNA_DOTLOCATION, dotLocation); + defaultWorkbenchProperties.put(MAX_MENU_ITEMS, + Integer.toString(DEFAULT_MAX_MENU_ITEMS)); + defaultWorkbenchProperties.put(WARN_INTERNAL_ERRORS, + Boolean.FALSE.toString()); + defaultWorkbenchProperties.put(CAPTURE_CONSOLE, + Boolean.TRUE.toString()); + } + return defaultWorkbenchProperties; + } + + @Override + public String getDisplayName() { + return "Workbench"; + } + + @Override + public String getFilePrefix() { + return "Workbench"; + } + + @Override + public String getUUID() { + return uuid; + } + + @Override + public boolean getWarnInternalErrors() { + String property = getProperty(WARN_INTERNAL_ERRORS); + return Boolean.parseBoolean(property); + } + + @Override + public boolean getCaptureConsole() { + String property = getProperty(CAPTURE_CONSOLE); + return Boolean.parseBoolean(property); + } + + @Override + public void setWarnInternalErrors(boolean warnInternalErrors) { + setProperty(WARN_INTERNAL_ERRORS, Boolean.toString(warnInternalErrors)); + } + + @Override + public void setCaptureConsole(boolean captureConsole) { + setProperty(CAPTURE_CONSOLE, Boolean.toString(captureConsole)); + } + + @Override + public void setMaxMenuItems(int maxMenuItems) { + if (maxMenuItems < 2) + throw new IllegalArgumentException( + "Maximum menu items must be at least 2"); + setProperty(MAX_MENU_ITEMS, Integer.toString(maxMenuItems)); + } + + @Override + public int getMaxMenuItems() { + String property = getProperty(MAX_MENU_ITEMS); + try { + int maxMenuItems = Integer.parseInt(property); + if (maxMenuItems >= 2) + return maxMenuItems; + logger.warn(MAX_MENU_ITEMS + " can't be less than 2"); + } catch (NumberFormatException ex) { + logger.warn("Invalid number for " + MAX_MENU_ITEMS + ": " + + property); + } + // We'll return the default instead + return DEFAULT_MAX_MENU_ITEMS; + } + + @Override + public String getDotLocation() { + return getProperty(TAVERNA_DOTLOCATION); + } + + @Override + public void setDotLocation(String dotLocation) { + setProperty(TAVERNA_DOTLOCATION, dotLocation); + } + + private String getDefaultDotLocation() { + if (applicationConfiguration == null) + return null; + File startupDir = applicationConfiguration.getStartupDir(); + if (startupDir == null) + return DOT_FALLBACK; + + String os = System.getProperty("os.name"); + if (os.equals(MAC_OS_X)) + if (startupDir.getParentFile() != null) { + File contentsDir = startupDir.getParentFile().getParentFile(); + if (contentsDir != null + && contentsDir.getName().equalsIgnoreCase( + BUNDLE_CONTENTS)) { + File dot = new File(new File(contentsDir, BUNDLE_MAC_OS), + DOT_FALLBACK); + if (dot.exists()) + return dot.getAbsolutePath(); + } + } else if (os.startsWith(WINDOWS)) { + File binWin386Dir = new File(new File(startupDir, BIN), + WIN32I386); + File dot = new File(binWin386Dir, DOT_EXE); + if (dot.exists()) + return dot.getAbsolutePath(); + } + return DOT_FALLBACK; + } + + /** + * Sets the applicationConfiguration. + * + * @param applicationConfiguration + * the new value of applicationConfiguration + */ + public void setApplicationConfiguration( + ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + defaultWorkbenchProperties = null; + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationPanel.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationPanel.java new file mode 100644 index 0000000..ecddc35 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationPanel.java
@@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration; + +import static java.awt.GridBagConstraints.*; +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.helper.Helper.showHelp; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.openIcon; + +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; + +import org.apache.log4j.Logger; + +@SuppressWarnings("serial") +public class WorkbenchConfigurationPanel extends JPanel { + private static final String RESTART_MSG = "For the new configuration to be fully applied, it is advised to restart Taverna."; + private static final String DOT_PATH_MSG = "Path to Graphviz executable <code>dot</code>:"; + private static final String CONTEXT_MENU_SIZE_MSG = "Maximum number of services/ports in right-click menu:"; + private static Logger logger = Logger + .getLogger(WorkbenchConfigurationUIFactory.class); + + private JTextField dotLocation = new JTextField(25); + private JTextField menuItems = new JTextField(10); + private JCheckBox warnInternal = new JCheckBox("Warn on internal errors"); + private JCheckBox captureConsole = new JCheckBox( + "Capture output on stdout/stderr to log file"); + + private final WorkbenchConfiguration workbenchConfiguration; + + public WorkbenchConfigurationPanel( + WorkbenchConfiguration workbenchConfiguration) { + super(); + this.workbenchConfiguration = workbenchConfiguration; + initComponents(); + } + + private static JLabel htmlLabel(String html) { + return new JLabel("<html><body>" + html + "</body></html>"); + } + + private void initComponents() { + this.setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + + // Title describing what kind of settings we are configuring here + JTextArea descriptionText = new JTextArea( + "General Workbench configuration"); + descriptionText.setLineWrap(true); + descriptionText.setWrapStyleWord(true); + descriptionText.setEditable(false); + descriptionText.setFocusable(false); + descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10)); + gbc.anchor = WEST; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.fill = HORIZONTAL; + this.add(descriptionText, gbc); + + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 2; + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.insets = new Insets(10, 5, 0, 0); + gbc.fill = NONE; + this.add(htmlLabel(DOT_PATH_MSG), gbc); + + dotLocation.setText(workbenchConfiguration.getDotLocation()); + gbc.gridy++; + gbc.gridwidth = 1; + gbc.weightx = 1.0; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.fill = HORIZONTAL; + this.add(dotLocation, gbc); + + JButton browseButton = new JButton(); + gbc.gridx = 1; + gbc.weightx = 0.0; + gbc.fill = NONE; + this.add(browseButton, gbc); + browseButton.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + System.setProperty("com.apple.macos.use-file-dialog-packages", + "false"); + JFileChooser fileChooser = new JFileChooser(); + fileChooser.putClientProperty( + "JFileChooser.appBundleIsTraversable", "always"); + fileChooser.putClientProperty( + "JFileChooser.packageIsTraversable", "always"); + + fileChooser.setDialogTitle("Browse for dot"); + + fileChooser.resetChoosableFileFilters(); + fileChooser.setAcceptAllFileFilterUsed(false); + + fileChooser.setMultiSelectionEnabled(false); + + int returnVal = fileChooser + .showOpenDialog(WorkbenchConfigurationPanel.this); + if (returnVal == APPROVE_OPTION) + dotLocation.setText(fileChooser.getSelectedFile() + .getAbsolutePath()); + } + }); + browseButton.setIcon(openIcon); + + gbc.gridx = 0; + gbc.gridy++; + gbc.gridwidth = 2; + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.insets = new Insets(10, 5, 0, 0); + gbc.fill = HORIZONTAL; + this.add(htmlLabel(CONTEXT_MENU_SIZE_MSG), gbc); + + menuItems.setText(Integer.toString(workbenchConfiguration + .getMaxMenuItems())); + gbc.gridy++; + gbc.weightx = 1.0; + gbc.gridwidth = 1; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.fill = HORIZONTAL; + this.add(menuItems, gbc); + + gbc.gridx = 0; + gbc.gridy++; + gbc.gridwidth = 2; + gbc.weightx = 1.0; + gbc.fill = HORIZONTAL; + gbc.insets = new Insets(10, 0, 0, 0); + warnInternal + .setSelected(workbenchConfiguration.getWarnInternalErrors()); + this.add(warnInternal, gbc); + + gbc.gridy++; + gbc.insets = new Insets(0, 0, 10, 0); + captureConsole.setSelected(workbenchConfiguration.getCaptureConsole()); + this.add(captureConsole, gbc); + + // Add the buttons panel + gbc.gridx = 0; + gbc.gridy++; + gbc.gridwidth = 3; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = BOTH; + gbc.anchor = SOUTH; + this.add(getButtonsPanel(), gbc); + } + + private Component getButtonsPanel() { + final JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.CENTER)); + + /** + * The helpButton shows help about the current component + */ + JButton helpButton = new JButton(new AbstractAction("Help") { + @Override + public void actionPerformed(ActionEvent arg0) { + showHelp(panel); + } + }); + panel.add(helpButton); + + /** + * The resetButton changes the property values shown to those + * corresponding to the configuration currently applied. + */ + JButton resetButton = new JButton(new AbstractAction("Reset") { + @Override + public void actionPerformed(ActionEvent arg0) { + resetFields(); + } + }); + panel.add(resetButton); + + JButton applyButton = new JButton(new AbstractAction("Apply") { + @Override + public void actionPerformed(ActionEvent arg0) { + String menus = menuItems.getText(); + try { + workbenchConfiguration.setMaxMenuItems(Integer + .valueOf(menus)); + } catch (IllegalArgumentException e) { + String message = "Invalid menu items number " + menus + + ":\n" + e.getLocalizedMessage(); + showMessageDialog(panel, message, "Invalid menu items", + WARNING_MESSAGE); + return; + } + + workbenchConfiguration.setCaptureConsole(captureConsole + .isSelected()); + workbenchConfiguration.setWarnInternalErrors(warnInternal + .isSelected()); + workbenchConfiguration.setDotLocation(dotLocation.getText()); + try { + showMessageDialog(panel, RESTART_MSG, "Restart adviced", + INFORMATION_MESSAGE); + } catch (Exception e) { + logger.error("Error storing updated configuration", e); + } + } + }); + panel.add(applyButton); + return panel; + } + + /** + * Resets the shown field values to those currently set (last saved) in the + * configuration. + * + * @param configurable + */ + private void resetFields() { + menuItems.setText(Integer.toString(workbenchConfiguration + .getMaxMenuItems())); + dotLocation.setText(workbenchConfiguration.getDotLocation()); + warnInternal + .setSelected(workbenchConfiguration.getWarnInternalErrors()); + captureConsole.setSelected(workbenchConfiguration.getCaptureConsole()); + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationUIFactory.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationUIFactory.java new file mode 100644 index 0000000..263233f --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/WorkbenchConfigurationUIFactory.java
@@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration; + +import javax.swing.JPanel; + +import uk.org.taverna.configuration.Configurable; +import uk.org.taverna.configuration.ConfigurationUIFactory; + +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; + +public class WorkbenchConfigurationUIFactory implements ConfigurationUIFactory { + private WorkbenchConfiguration workbenchConfiguration; + + @Override + public boolean canHandle(String uuid) { + return uuid.equals(workbenchConfiguration.getUUID()); + } + + @Override + public JPanel getConfigurationPanel() { + return new WorkbenchConfigurationPanel(workbenchConfiguration); + } + + @Override + public Configurable getConfigurable() { + return workbenchConfiguration; + } + + public void setWorkbenchConfiguration( + WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerImpl.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerImpl.java new file mode 100644 index 0000000..4c03dbe --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerImpl.java
@@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration.colour; + +import static java.awt.Color.WHITE; +import static java.awt.Color.decode; + +import java.awt.Color; +import java.util.HashMap; +import java.util.Map; + +import uk.org.taverna.configuration.AbstractConfigurable; +import uk.org.taverna.configuration.ConfigurationManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; + +/** + * A factory class that determines the colour that a Colourable UI component + * should be displayed as, according to a schema configured by the user. + * + * @author Stuart Owen + * @author Ian Dunlop + * @see Colourable + */ +public class ColourManagerImpl extends AbstractConfigurable implements + ColourManager { + // Names of things that may be coloured + private static final String WORKFLOW_PORT_OBJECT = "uk.org.taverna.scufl2.api.port.WorkflowPort"; + private static final String PROCESSOR_PORT_OBJECT = "uk.org.taverna.scufl2.api.port.ProcessorPort"; + private static final String PROCESSOR_OBJECT = "uk.org.taverna.scufl2.api.core.Processor"; + private static final String MERGE_OBJECT = "net.sf.taverna.t2.workflowmodel.Merge"; + private static final String NONEXECUTABLE_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/nonExecutable"; + private static final String XML_SPLITTER_OUT_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/xml-splitter/out"; + private static final String XML_SPLITTER_IN_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/xml-splitter/in"; + private static final String LOCALWORKER_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/localworker"; + private static final String WSDL_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/wsdl"; + private static final String CONSTANT_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/constant"; + private static final String SOAPLAB_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/soaplab"; + private static final String RSHELL_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/rshell"; + private static final String NESTED_WORKFLOW = "http://ns.taverna.org.uk/2010/activity/nested-workflow"; + private static final String MOBY_PARSER_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomoby/parser"; + private static final String MOBY_OBJECT_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomoby/object"; + private static final String MOBY_SERVICE_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomoby/service"; + private static final String BIOMART_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/biomart"; + private static final String BEANSHELL_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/beanshell"; + private static final String APICONSUMER_ACTIVITY = "http://ns.taverna.org.uk/2010/activity/apiconsumer"; + + // Names of colours used + private static final String burlywood2 = "#deb887"; + private static final String darkgoldenrod1 = "#ffb90f"; + private static final String darkolivegreen3 = "#a2cd5a"; + private static final String gold = "#ffd700"; + private static final String grey = "#777777"; + private static final String lightcyan2 = "#d1eeee"; + private static final String lightgoldenrodyellow = "#fafad2"; + // light purple non standard + private static final String lightpurple = "#ab92ea"; + private static final String lightsteelblue = "#b0c4de"; + private static final String mediumorchid2 = "#d15fee"; + private static final String palegreen = "#98fb98"; + private static final String pink = "#ffc0cb"; + private static final String purplish = "#8070ff"; + // ShadedLabel.Orange + private static final String shadedorange = "#eece8f"; + // ShadedLabel.Green + private static final String shadedgreen = "#a1c69d"; + // slightly lighter than the real steelblue4 + private static final String steelblue4 = "#648faa"; + private static final String turquoise = "#77aadd"; + private static final String white = "#ffffff"; + + private Map<String, String> defaultPropertyMap; + private Map<Object, Color> cachedColours; + + public ColourManagerImpl(ConfigurationManager configurationManager) { + super(configurationManager); + initialiseDefaults(); + } + + @Override + public String getCategory() { + return "colour"; + } + + @Override + public Map<String, String> getDefaultPropertyMap() { + if (defaultPropertyMap == null) + initialiseDefaults(); + return defaultPropertyMap; + } + + @Override + public String getDisplayName() { + return "Colour Management"; + } + + @Override + public String getFilePrefix() { + return "ColourManagement"; + } + + /** + * Unique identifier for this ColourManager + */ + @Override + public String getUUID() { + return "a2148420-5967-11dd-ae16-0800200c9a66"; + } + + private void initialiseDefaults() { + defaultPropertyMap = new HashMap<>(); + cachedColours = new HashMap<>(); + + defaultPropertyMap.put(APICONSUMER_ACTIVITY, palegreen); + defaultPropertyMap.put(BEANSHELL_ACTIVITY, burlywood2); + defaultPropertyMap.put(BIOMART_ACTIVITY, lightcyan2); + defaultPropertyMap.put(CONSTANT_ACTIVITY, lightsteelblue); + defaultPropertyMap.put(LOCALWORKER_ACTIVITY, mediumorchid2); + defaultPropertyMap.put(MOBY_SERVICE_ACTIVITY, darkgoldenrod1); + defaultPropertyMap.put(MOBY_OBJECT_ACTIVITY, gold); + defaultPropertyMap.put(MOBY_PARSER_ACTIVITY, white); + defaultPropertyMap.put(NESTED_WORKFLOW, pink); + defaultPropertyMap.put(RSHELL_ACTIVITY, steelblue4); + defaultPropertyMap.put(SOAPLAB_ACTIVITY, lightgoldenrodyellow); + defaultPropertyMap.put(WSDL_ACTIVITY, darkolivegreen3); + defaultPropertyMap.put(XML_SPLITTER_IN_ACTIVITY, lightpurple); + defaultPropertyMap.put(XML_SPLITTER_OUT_ACTIVITY, lightpurple); + + defaultPropertyMap.put(NONEXECUTABLE_ACTIVITY, grey); + + defaultPropertyMap.put(MERGE_OBJECT, turquoise); + defaultPropertyMap.put(PROCESSOR_OBJECT, shadedgreen); + defaultPropertyMap.put(PROCESSOR_PORT_OBJECT, purplish); + defaultPropertyMap.put(WORKFLOW_PORT_OBJECT, shadedorange); + } + + @Override + public Color getPreferredColour(String itemKey) { + Color colour = cachedColours.get(itemKey); + if (colour == null) { + String colourString = (String) getProperty(itemKey); + colour = colourString == null ? WHITE : decode(colourString); + cachedColours.put(itemKey, colour); + } + return colour; + } + + @Override + public void setPreferredColour(String itemKey, Color colour) { + cachedColours.put(itemKey, colour); + } + + @Override + public void restoreDefaults() { + super.restoreDefaults(); + if (cachedColours == null) + cachedColours = new HashMap<>(); + else + cachedColours.clear(); + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/mimetype/MimeTypeManagerImpl.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/mimetype/MimeTypeManagerImpl.java new file mode 100644 index 0000000..0ff6c65 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/mimetype/MimeTypeManagerImpl.java
@@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration.mimetype; + +import java.util.HashMap; +import java.util.Map; + +import net.sf.taverna.t2.workbench.configuration.mimetype.MimeTypeManager; +import uk.org.taverna.configuration.AbstractConfigurable; +import uk.org.taverna.configuration.ConfigurationManager; + +public class MimeTypeManagerImpl extends AbstractConfigurable implements + MimeTypeManager { + /** + * Constructs a new <code>MimeTypeManagerImpl</code>. + * + * @param configurationManager + */ + public MimeTypeManagerImpl(ConfigurationManager configurationManager) { + super(configurationManager); + } + + @Override + public String getCategory() { + return "Mime Type"; + } + + @Override + public Map<String, String> getDefaultPropertyMap() { + HashMap<String, String> map = new HashMap<>(); + map.put("text/plain", "Plain Text"); + map.put("text/xml", "XML Text"); + map.put("text/html", "HTML Text"); + map.put("text/rtf", "Rich Text Format"); + map.put("text/x-graphviz", "Graphviz Dot File"); + map.put("image/png", "PNG Image"); + map.put("image/jpeg", "JPEG Image"); + map.put("image/gif", "GIF Image"); + map.put("application/octet-stream", "Binary Data"); + map.put("application/zip", "Zip File"); + map.put("chemical/x-swissprot", "SWISSPROT Flat File"); + map.put("chemical/x-embl-dl-nucleotide", "EMBL Flat File"); + map.put("chemical/x-ppd", "PPD File"); + map.put("chemical/seq-aa-genpept", "Genpept Protein"); + map.put("chemical/seq-na-genbank", "Genbank Nucleotide"); + map.put("chemical/x-pdb", "PDB 3D Structure File"); + return map; + } + + @Override + public String getUUID() { + return "b9277fa0-5967-11dd-ae16-0800200c9a66"; + } + + @Override + public String getDisplayName() { + return "Mime Type Manager"; + } + + @Override + public String getFilePrefix() { + return "MimeTypeManagerImpl"; + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/T2ConfigurationFrameImpl.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/T2ConfigurationFrameImpl.java new file mode 100644 index 0000000..4910f78 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/T2ConfigurationFrameImpl.java
@@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration.ui; + +import static javax.swing.JSplitPane.HORIZONTAL_SPLIT; +import static net.sf.taverna.t2.workbench.helper.HelpCollator.registerComponent; +import static net.sf.taverna.t2.workbench.helper.Helper.setKeyCatcher; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.ListModel; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame; + +import org.apache.log4j.Logger; + +import uk.org.taverna.configuration.ConfigurationUIFactory; + +public class T2ConfigurationFrameImpl implements T2ConfigurationFrame { + private static Logger logger = Logger.getLogger(T2ConfigurationFrameImpl.class); + private static final int FRAME_WIDTH = 700; + private static final int FRAME_HEIGHT = 450; + + private List<ConfigurationUIFactory> configurationUIFactories = new ArrayList<>(); + + private JFrame frame; + private JSplitPane splitPane; + private JList<ConfigurableItem> list; + + public T2ConfigurationFrameImpl() { + } + + @Override + public void showFrame() { + getFrame().setVisible(true); + } + + @Override + public void showConfiguration(String name) { + showFrame(); + ListModel<ConfigurableItem> lm = list.getModel(); + for (int i = 0; i < lm.getSize(); i++) + if (lm.getElementAt(i).toString().equals(name)) { + list.setSelectedIndex(i); + break; + } + } + + private JFrame getFrame() { + if (frame != null) + return frame; + + frame = new JFrame(); + setKeyCatcher(frame); + registerComponent(frame); + frame.setLayout(new BorderLayout()); + + /* + * Split pane to hold list of properties (on the left) and their + * configurable options (on the right) + */ + splitPane = new JSplitPane(HORIZONTAL_SPLIT); + splitPane.setBorder(null); + + list = getConfigurationList(); + JScrollPane jspList = new JScrollPane(list); + jspList.setBorder(new EmptyBorder(5, 5, 5, 5)); + jspList.setMinimumSize(new Dimension(150, + jspList.getPreferredSize().height)); + + splitPane.setLeftComponent(jspList); + splitPane.setRightComponent(new JPanel()); + splitPane.setDividerSize(1); + + // select first item if one exists + if (list.getModel().getSize() > 0) + list.setSelectedValue(list.getModel().getElementAt(0), true); + + frame.add(splitPane); + frame.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT)); + return frame; + } + + private JList<ConfigurableItem> getConfigurationList() { + if (list != null) + return list; + + list = new JList<>(); + list.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if (list.getSelectedValue() instanceof ConfigurableItem) { + ConfigurableItem item = (ConfigurableItem) list + .getSelectedValue(); + setMainPanel(item.getPanel()); + } + + /* + * Keep the split pane's divider at its current position - but + * looks ugly. The problem with divider moving from its current + * position after selecting an item from the list on the left is + * that the right hand side panels are loaded dynamically and it + * seems there is nothing we can do about it - it's just the + * JSplitPane's behaviour + */ + // splitPane.setDividerLocation(splitPane.getLastDividerLocation()); + } + }); + list.setListData(getListItems()); + return list; + } + + private void setMainPanel(JPanel panel) { + panel.setBorder(new EmptyBorder(15, 15, 15, 15)); + splitPane.setRightComponent(panel); + } + + public void setConfigurationUIFactories( + List<ConfigurationUIFactory> configurationUIFactories) { + this.configurationUIFactories = configurationUIFactories; + } + + private ConfigurableItem[] getListItems() { + List<ConfigurableItem> arrayList = new ArrayList<>(); + for (ConfigurationUIFactory fac : configurationUIFactories) { + String name = fac.getConfigurable().getDisplayName(); + if (name != null) { + logger.info("Adding configurable for name: " + name); + arrayList.add(new ConfigurableItem(fac)); + } else { + logger.warn("The configurable " + fac.getConfigurable().getClass() + + " has a null name"); + } + } + // Sort the list alphabetically + ConfigurableItem[] array = arrayList.toArray(new ConfigurableItem[0]); + Arrays.sort(array, new Comparator<ConfigurableItem>() { + @Override + public int compare(ConfigurableItem item1, ConfigurableItem item2) { + return item1.toString().compareToIgnoreCase(item2.toString()); + } + }); + return array; + } + + public void update(Object service, Map<?, ?> properties) { + getConfigurationList().setListData(getListItems()); + if (frame != null) { + frame.revalidate(); + frame.repaint(); + // select first item if one exists + if (list.getModel().getSize() > 0) + list.setSelectedValue(list.getModel().getElementAt(0), true); + } + } + + class ConfigurableItem { + private final ConfigurationUIFactory factory; + + public ConfigurableItem(ConfigurationUIFactory factory) { + this.factory = factory; + } + + public JPanel getPanel() { + return factory.getConfigurationPanel(); + } + + @Override + public String toString() { + return factory.getConfigurable().getDisplayName(); + } + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchConfigurationMenu.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchConfigurationMenu.java new file mode 100644 index 0000000..453f0c0 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchConfigurationMenu.java
@@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration.ui; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame; + +public class WorkbenchConfigurationMenu extends AbstractMenuAction { + private static final String MAC_OS_X = "Mac OS X"; + + private T2ConfigurationFrame t2ConfigurationFrame; + + public WorkbenchConfigurationMenu() { + super(URI.create("http://taverna.sf.net/2008/t2workbench/menu#preferences"), + 100); + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction("Preferences") { + @Override + public void actionPerformed(ActionEvent event) { + t2ConfigurationFrame.showFrame(); + } + }; + } + + @Override + public boolean isEnabled() { + return !MAC_OS_X.equalsIgnoreCase(System.getProperty("os.name")); + } + + public void setT2ConfigurationFrame(T2ConfigurationFrame t2ConfigurationFrame) { + this.t2ConfigurationFrame = t2ConfigurationFrame; + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchPreferencesSection.java b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchPreferencesSection.java new file mode 100644 index 0000000..d131ac3 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ui/WorkbenchPreferencesSection.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration.ui; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +public class WorkbenchPreferencesSection extends AbstractMenuSection { + private static final URI FILE_MENU = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#file"); + private static final URI PREFERENCES_MENU_ITEM = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#preferences"); + + public WorkbenchPreferencesSection() { + super(FILE_MENU, 100, PREFERENCES_MENU_ITEM); + } +}
diff --git a/taverna-workbench-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..3b51dd4 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,2 @@ +net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchPreferencesSection +net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchConfigurationMenu
diff --git a/taverna-workbench-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-workbench-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory new file mode 100644 index 0000000..4af55ec --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.ui.impl.configuration.WorkbenchConfigurationUIFactory \ No newline at end of file
diff --git a/taverna-workbench-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context-osgi.xml b/taverna-workbench-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context-osgi.xml new file mode 100644 index 0000000..29aea44 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context-osgi.xml
@@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="t2ConfigurationFrame" interface="net.sf.taverna.t2.workbench.configuration.workbench.ui.T2ConfigurationFrame" /> + + <service ref="WorkbenchConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" /> + + <service ref="WorkbenchPreferencesSection" auto-export="interfaces" /> + <service ref="WorkbenchConfigurationMenu" auto-export="interfaces" /> + + <service ref="ColourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + <service ref="WorkbenchConfiguration" interface="net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration" /> + <service ref="MimeTypeManager" interface="net.sf.taverna.t2.workbench.configuration.mimetype.MimeTypeManager" /> + + <reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" /> + <reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" /> + + <list id="configurationUIFactories" interface="uk.org.taverna.configuration.ConfigurationUIFactory" cardinality="0..N"> + <listener ref="t2ConfigurationFrame" bind-method="update" unbind-method="update"/> + </list> + +</beans:beans>
diff --git a/taverna-workbench-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context.xml b/taverna-workbench-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context.xml new file mode 100644 index 0000000..40da7fd --- /dev/null +++ b/taverna-workbench-configuration-impl/src/main/resources/META-INF/spring/configuration-impl-context.xml
@@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="t2ConfigurationFrame" class="net.sf.taverna.t2.workbench.ui.impl.configuration.ui.T2ConfigurationFrameImpl"> + <property name="configurationUIFactories" ref="configurationUIFactories" /> + </bean> + + <bean id="WorkbenchConfigurationUIFactory" class="net.sf.taverna.t2.workbench.ui.impl.configuration.WorkbenchConfigurationUIFactory"> + <property name="workbenchConfiguration"> + <ref local="WorkbenchConfiguration"/> + </property> + </bean> + + <bean id="WorkbenchPreferencesSection" class="net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchPreferencesSection" /> + <bean id="WorkbenchConfigurationMenu" class="net.sf.taverna.t2.workbench.ui.impl.configuration.ui.WorkbenchConfigurationMenu"> + <property name="t2ConfigurationFrame"> + <ref local="t2ConfigurationFrame"/> + </property> + </bean> + + <bean id="ColourManager" class="net.sf.taverna.t2.workbench.ui.impl.configuration.colour.ColourManagerImpl"> + <constructor-arg ref="configurationManager"/> + </bean> + <bean id="WorkbenchConfiguration" class="net.sf.taverna.t2.workbench.ui.impl.configuration.WorkbenchConfigurationImpl"> + <constructor-arg ref="configurationManager"/> + <property name="applicationConfiguration" ref="applicationConfiguration" /> + </bean> + <bean id="MimeTypeManager" class="net.sf.taverna.t2.workbench.ui.impl.configuration.mimetype.MimeTypeManagerImpl"> + <constructor-arg ref="configurationManager"/> + </bean> + +</beans>
diff --git a/taverna-workbench-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ConfigurationManagerTest.java b/taverna-workbench-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ConfigurationManagerTest.java new file mode 100644 index 0000000..1202c11 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/ConfigurationManagerTest.java
@@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.File; +import java.util.UUID; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.ui.impl.configuration.colour.ColourManagerImpl; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import uk.org.taverna.configuration.app.impl.ApplicationConfigurationImpl; +import uk.org.taverna.configuration.impl.ConfigurationManagerImpl; + +public class ConfigurationManagerTest { + + ConfigurationManagerImpl configurationManager; + + @Before + public void setup() { + configurationManager = new ConfigurationManagerImpl(new ApplicationConfigurationImpl()); + } + + @Test + public void createConfigManager() { + assertNotNull("Config Manager should not be null", configurationManager); + } + + @Ignore("Hardcoded /Users/Ian") //FIXME: update test to work using File.createTempFile(...) + @Test + public void populateConfigOfColourmanager() { + ColourManager manager= new ColourManagerImpl(null); + + manager.setProperty("colour.first", "25"); + manager.setProperty("colour.second", "223"); + + configurationManager.setBaseConfigLocation(new File("/Users/Ian/scratch")); + try { + configurationManager.store(manager); + } catch (Exception e1) { + e1.printStackTrace(); + } + + ColourManager manager2 = new ColourManagerImpl(configurationManager); + + try { + configurationManager.populate(manager2); + } catch (Exception e) { + e.printStackTrace(); + } + + + assertEquals("Properties do not match", manager2.getProperty("colour.first"), manager.getProperty("colour.first")); + assertEquals("Properties do not match", manager2.getProperty("colour.second"), manager.getProperty("colour.second")); + + + } + + @Test + public void saveColoursForDummyColourable() { + String dummy = ""; + ColourManager manager=new ColourManagerImpl(configurationManager); + manager.setProperty(dummy.getClass().getCanonicalName(), "#000000"); + + File f = new File(System.getProperty("java.io.tmpdir")); + File d = new File(f, UUID.randomUUID().toString()); + d.mkdir(); + configurationManager.setBaseConfigLocation(d); + try { + configurationManager.store(manager); + } catch (Exception e1) { + e1.printStackTrace(); + } + + try { + configurationManager.populate(manager); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + } + +}
diff --git a/taverna-workbench-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerTest.java b/taverna-workbench-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerTest.java new file mode 100644 index 0000000..0239ea8 --- /dev/null +++ b/taverna-workbench-configuration-impl/src/test/java/net/sf/taverna/t2/workbench/ui/impl/configuration/colour/ColourManagerTest.java
@@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.configuration.colour; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.awt.Color; +import java.io.File; +import java.util.UUID; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; + +import org.junit.Before; +import org.junit.Test; + +import uk.org.taverna.configuration.Configurable; +import uk.org.taverna.configuration.app.impl.ApplicationConfigurationImpl; +import uk.org.taverna.configuration.impl.ConfigurationManagerImpl; + +public class ColourManagerTest { + + private ConfigurationManagerImpl configurationManager; + + @Before + public void setup() { + configurationManager = new ConfigurationManagerImpl(new ApplicationConfigurationImpl()); + + File f = new File(System.getProperty("java.io.tmpdir")); + File d = new File(f, UUID.randomUUID().toString()); + d.mkdir(); + configurationManager.setBaseConfigLocation(d); + } + + @Test + public void testGetPreferredColourEqualsWhite() throws Exception { + String dummy = new String(); + + Color c = new ColourManagerImpl(configurationManager).getPreferredColour(dummy); + assertEquals("The default colour should be WHITE", Color.WHITE, c); + } + + @Test + public void testConfigurableness() throws Exception { + ColourManager manager = new ColourManagerImpl(configurationManager); + assertTrue(manager instanceof Configurable); + + assertEquals("wrong category", "colour", manager.getCategory()); + assertEquals("wrong name", "Colour Management", manager.getDisplayName()); + assertEquals("wrong UUID", "a2148420-5967-11dd-ae16-0800200c9a66", + manager.getUUID()); + assertNotNull("there is no default property map", manager + .getDefaultPropertyMap()); + } + + @Test + public void saveAsWrongArrayType() throws Exception { + String dummy = ""; + ColourManager manager = new ColourManagerImpl(configurationManager); + manager.setProperty(dummy.getClass().getCanonicalName(), "#ffffff"); + + File baseLoc = File.createTempFile("test", "scratch"); + baseLoc.delete(); + assertTrue("Could not make directory " + baseLoc, baseLoc.mkdir()); + configurationManager.setBaseConfigLocation(baseLoc); + configurationManager.store(manager); + configurationManager.populate(manager); + manager.getPreferredColour(dummy); + } + +}
diff --git a/taverna-workbench-contextual-views-api/pom.xml b/taverna-workbench-contextual-views-api/pom.xml new file mode 100644 index 0000000..5b8608f --- /dev/null +++ b/taverna-workbench-contextual-views-api/pom.xml
@@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <packaging>bundle</packaging> + <name>Contextual Views API</name> + <description>Contextual views for the activities</description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-icons-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-palette-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>configuration-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + <dependency> + <groupId>uk.org.taverna.commons</groupId> + <artifactId>taverna-services-api</artifactId> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>org.apache.log4j</groupId> + <artifactId>com.springsource.org.apache.log4j</artifactId> + </dependency> + </dependencies> + <repositories> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository + </url> + </repository> + <repository> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + <id>mygrid-snapshot-repository</id> + <name>myGrid Snapshot Repository</name> + <url>http://www.mygrid.org.uk/maven/snapshot-repository</url> + </repository> + </repositories> +</project>
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityConfigurationAction.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityConfigurationAction.java new file mode 100644 index 0000000..4d2fcff --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityConfigurationAction.java
@@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.actions.activity; + +import java.util.List; +import java.util.WeakHashMap; + +import javax.swing.AbstractAction; +import javax.swing.JDialog; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.servicedescriptions.ServiceDescription; +import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.events.ClosingDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ActivityConfigurationDialog; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +@SuppressWarnings("serial") +public abstract class ActivityConfigurationAction extends AbstractAction { + private static WeakHashMap<Activity, ActivityConfigurationDialog> configurationDialogs = new WeakHashMap<>(); + private static DataflowCloseListener listener; + + protected Activity activity; + private final ServiceDescriptionRegistry serviceDescriptionRegistry; + + public ActivityConfigurationAction(Activity activity, + ActivityIconManager activityIconManager, + ServiceDescriptionRegistry serviceDescriptionRegistry) { + this.activity = activity; + this.serviceDescriptionRegistry = serviceDescriptionRegistry; + putValue(SMALL_ICON, + activityIconManager.iconForActivity(activity.getType())); + } + + protected Activity getActivity() { + return activity; + } + + protected ServiceDescription getServiceDescription() { + return serviceDescriptionRegistry.getServiceDescription(activity + .getType()); + } + + protected static void setDialog(Activity activity, + ActivityConfigurationDialog dialog, FileManager fileManager) { + if (listener == null) { + listener = new DataflowCloseListener(); + /* + * Ensure that the DataflowCloseListener is the first notified + * listener. Otherwise you cannot save the configurations. + */ + List<Observer<FileManagerEvent>> existingListeners = fileManager + .getObservers(); + fileManager.addObserver(listener); + for (Observer<FileManagerEvent> observer : existingListeners) + if (!observer.equals(listener)) { + fileManager.removeObserver(observer); + fileManager.addObserver(observer); + } + } + if (configurationDialogs.containsKey(activity)) { + ActivityConfigurationDialog currentDialog = configurationDialogs + .get(activity); + if (!currentDialog.equals(dialog) && currentDialog.isVisible()) + currentDialog.setVisible(false); + } + configurationDialogs.put(activity, dialog); + dialog.setVisible(true); + } + + public static void clearDialog(Activity activity) { + if (configurationDialogs.containsKey(activity)) { + ActivityConfigurationDialog currentDialog = configurationDialogs + .get(activity); + if (currentDialog.isVisible()) + currentDialog.setVisible(false); + configurationDialogs.remove(activity); + currentDialog.dispose(); + } + } + + protected static void clearDialog(JDialog dialog) { + if (configurationDialogs.containsValue(dialog)) { + if (dialog.isVisible()) + dialog.setVisible(false); + for (Activity activity : configurationDialogs.keySet()) + if (configurationDialogs.get(activity).equals(dialog)) + configurationDialogs.remove(activity); + dialog.dispose(); + } + } + + public static boolean closeDialog(Activity activity) { + boolean closeIt = true; + if (configurationDialogs.containsKey(activity)) { + ActivityConfigurationDialog currentDialog = configurationDialogs + .get(activity); + if (currentDialog.isVisible()) + closeIt = currentDialog.closeDialog(); + if (closeIt) + configurationDialogs.remove(activity); + } + return closeIt; + } + + public static ActivityConfigurationDialog getDialog(Activity activity) { + return configurationDialogs.get(activity); + } + + private static class DataflowCloseListener implements + Observer<FileManagerEvent> { + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + @Override + public void notify(Observable<FileManagerEvent> sender, + FileManagerEvent message) throws Exception { + if (message instanceof ClosingDataflowEvent) { + ClosingDataflowEvent closingDataflowEvent = (ClosingDataflowEvent) message; + if (closingDataflowEvent.isAbortClose()) + return; + closingDataflow(closingDataflowEvent, + ((ClosingDataflowEvent) message).getDataflow()); + } + } + + private void closingDataflow(ClosingDataflowEvent event, + WorkflowBundle bundle) { + Profile profile = bundle.getMainProfile(); + for (Workflow workflow : bundle.getWorkflows()) + for (Processor p : workflow.getProcessors()) { + ProcessorBinding processorBinding = scufl2Tools + .processorBindingForProcessor(p, profile); + Activity activity = processorBinding.getBoundActivity(); + if (!closeDialog(activity)) + event.setAbortClose(true); + } + } + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityContextualView.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityContextualView.java new file mode 100644 index 0000000..f063c02 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/ActivityContextualView.java
@@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.actions.activity; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; + +/** + * A contextual view specific to an Activity. Concrete subclasses must + * initialise the view by calling {@link #initView()}. + * <p> + * The implementation provides a view based upon the properties set in the + * Configuration + * + * @author Stuart Owen + * @author Ian Dunlop + * + * @see Activity + * @see ContextualView + */ +@SuppressWarnings("serial") +public abstract class ActivityContextualView extends ContextualView { + private Activity activity; + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + /** + * Constructs an instance of the view. + * <p> + * The constructor parameter for the implementation of this class should + * define the specific Activity type itself. + * + * @param activity + */ + protected ActivityContextualView(Activity activity) { + super(); + this.activity = activity; + } + + public Activity getActivity() { + return activity; + } + + public Configuration getConfigBean() { + return scufl2Tools.configurationFor(activity, activity.getParent()); + } + + @Override + public abstract void refreshView(); +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/HTMLBasedActivityContextualView.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/HTMLBasedActivityContextualView.java new file mode 100644 index 0000000..de3df33 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/actions/activity/HTMLBasedActivityContextualView.java
@@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.actions.activity; + +import static net.sf.taverna.t2.lang.ui.HtmlUtils.buildTableOpeningTag; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.createEditorPane; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.getHtmlHead; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.panelForHtml; + +import javax.swing.JComponent; +import javax.swing.JEditorPane; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import uk.org.taverna.scufl2.api.activity.Activity; + +@SuppressWarnings("serial") +public abstract class HTMLBasedActivityContextualView extends ActivityContextualView { + private static final String BEANSHELL_URI = "http://ns.taverna.org.uk/2010/activity/beanshell"; + private static final String LOCALWORKER_URI = "http://ns.taverna.org.uk/2010/activity/localworker"; + private JEditorPane editorPane; + private final ColourManager colourManager; + + public HTMLBasedActivityContextualView(Activity activity, ColourManager colourManager) { + super(activity); + this.colourManager = colourManager; + initView(); + } + + @Override + public JComponent getMainFrame() { + editorPane = createEditorPane(buildHtml()); + return panelForHtml(editorPane); + } + + private String buildHtml() { + StringBuilder html = new StringBuilder(getHtmlHead(getBackgroundColour())); + html.append(buildTableOpeningTag()); + html.append("<tr><th colspan=\"2\">").append(getViewTitle()).append("</th></tr>"); + html.append(getRawTableRowsHtml()).append("</table>"); + html.append("</body></html>"); + return html.toString(); + } + + protected abstract String getRawTableRowsHtml(); + + public String getBackgroundColour() { + String activityType = getActivity().getType().toString(); + if (LOCALWORKER_URI.equals(activityType)) + if (getConfigBean().getJson().get("isAltered").booleanValue()) + return (String) colourManager.getProperty(BEANSHELL_URI); + String colour = (String) colourManager.getProperty(activityType); + return colour == null ? "#ffffff" : colour; + } + + /** + * Update the html view with the latest information in the configuration + * bean + */ + @Override + public void refreshView() { + editorPane.setText(buildHtml()); + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/AddLayerFactorySPI.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/AddLayerFactorySPI.java new file mode 100644 index 0000000..6b0245c --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/AddLayerFactorySPI.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews; + +import java.net.URI; + +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.core.Processor; + +/** + * SPI for adding dispatch stack layers to a processor, such as + * {@link net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Loop}. + * <p> + * Buttons or similar will be added in the processor contextual view. + * + * @author Stian Soiland-Reyes + */ +public interface AddLayerFactorySPI { + boolean canAddLayerFor(Processor proc); + + Action getAddLayerActionFor(Processor proc); + + boolean canCreateLayerClass(URI dispatchLayerType); +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/ContextualView.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/ContextualView.java new file mode 100644 index 0000000..45efaab --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/ContextualView.java
@@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews; + +import static java.awt.BorderLayout.CENTER; + +import java.awt.BorderLayout; +import java.awt.Frame; + +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JPanel; + +/** + * An abstract class defining the base container to hold a contextual view over + * Dataflow element. + * <p> + * The specific implementation of this class to support a given dataflow element + * needs to implement the {@link #getMainFrame()} and {@link #getViewTitle()}. + * <p> + * If a view is associated with an action handler to configure this component, + * then the {@link #getConfigureAction(Frame) getConfigureAction} handler must + * be over-ridden. If this returns null then the configure button is left + * disabled and it is not possible to configure the element. + * + * @author Stuart Owen + * @author Ian Dunlop + * @author Alan R Williams + */ +@SuppressWarnings("serial") +public abstract class ContextualView extends JPanel { + /** + * When implemented, this method should define the main frame that is placed + * in this container, and provides a static view of the Dataflow element. + * + * @return a JComponent that represents the dataflow element. + */ + public abstract JComponent getMainFrame(); + + /** + * @return a String providing a title for the view + */ + public abstract String getViewTitle(); + + /** + * Allows the item to be configured, but returning an action handler that + * will be invoked when selecting to configure. By default this is provided + * by a button. + * <p> + * If there is no ability to configure the given item, then this should + * return null. + * + * @param owner + * the owning dialog to be used when displaying dialogues for + * configuration options + * @return an action that allows the element being viewed to be configured. + */ + public Action getConfigureAction(Frame owner) { + return null; + } + + /** + * This <i>must</i> be called by any sub-classes after they have initialised + * their own view since it gets their main panel and adds it to the main + * contextual view. If you don't do this you will get a very empty frame + * popping up! + */ + public void initView() { + setLayout(new BorderLayout()); + add(getMainFrame(), CENTER); + setName(getViewTitle()); + } + + public abstract void refreshView(); + + public abstract int getPreferredPosition(); + + public static String getTextFromDepth(String kind, Integer depth) { + String labelText = "The last prediction said the " + kind; + if (depth == null) { + labelText += " would not transmit a value"; + } else if (depth == -1) { + labelText += " was invalid/unpredicted"; + } else if (depth == 0) { + labelText += " would carry a single value"; + } else { + labelText += " would carry a list of depth " + depth; + } + return labelText; + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationDialog.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationDialog.java new file mode 100644 index 0000000..c4c77b7 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationDialog.java
@@ -0,0 +1,474 @@ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Cursor.DEFAULT_CURSOR; +import static java.awt.Cursor.WAIT_CURSOR; +import static java.awt.Cursor.getPredefinedCursor; +import static java.lang.Math.max; +import static javax.swing.JOptionPane.CANCEL_OPTION; +import static javax.swing.JOptionPane.NO_OPTION; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_NO_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow; +import static net.sf.taverna.t2.workbench.helper.Helper.showHelp; +import static net.sf.taverna.t2.workbench.ui.actions.activity.ActivityConfigurationAction.clearDialog; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.ui.DeselectingButton; +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowRedoEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowUndoEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workflow.edits.AddChildEdit; +import net.sf.taverna.t2.workflow.edits.AddProcessorInputPortEdit; +import net.sf.taverna.t2.workflow.edits.AddProcessorOutputPortEdit; +import net.sf.taverna.t2.workflow.edits.ChangeDepthEdit; +import net.sf.taverna.t2.workflow.edits.ChangeGranularDepthEdit; +import net.sf.taverna.t2.workflow.edits.ChangeJsonEdit; +import net.sf.taverna.t2.workflow.edits.RemoveChildEdit; +import net.sf.taverna.t2.workflow.edits.RemoveProcessorInputPortEdit; +import net.sf.taverna.t2.workflow.edits.RemoveProcessorOutputPortEdit; +import net.sf.taverna.t2.workflow.edits.RenameEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.ActivityPort; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +@SuppressWarnings("serial") +public class ActivityConfigurationDialog extends HelpEnabledDialog { + private enum PortType { + INPUT, OUTPUT + } + + protected static Logger logger = Logger.getLogger(ActivityConfigurationDialog.class); + private static final Scufl2Tools scufl2Tools = new Scufl2Tools(); + + private final EditManager editManager; + + private Activity activity; + private ActivityConfigurationPanel panel; + protected WorkflowBundle owningWorkflowBundle; + protected Processor owningProcessor; + private Observer<EditManagerEvent> observer; + Dimension minimalSize = null; + Dimension buttonPanelSize = null; + JPanel buttonPanel; + protected JButton applyButton; + + public ActivityConfigurationDialog(Activity a, ActivityConfigurationPanel p, + EditManager editManager) { + super(getMainWindow(), "Configuring " + a.getClass().getSimpleName(), + false, null); + this.activity = a; + this.panel = p; + this.editManager = editManager; + + owningWorkflowBundle = activity.getParent().getParent(); + owningProcessor = findProcessor(a); + + setTitle(getRelativeName(owningWorkflowBundle, activity)); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + setLayout(new BorderLayout()); + + add(panel, BorderLayout.CENTER); + + buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.setBorder(new EmptyBorder(5, 20, 5, 5)); + + JButton helpButton = new DeselectingButton("Help", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + showHelp(panel); + } + }); + buttonPanel.add(helpButton); + + applyButton = new DeselectingButton("Apply", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + /* + * For the moment it always does an apply as what should be + * happening is that the apply button only becomes available + * when the configuration has changed. However, many + * configuration panels are not set up to detected changes + */ + // if (panel.isConfigurationChanged()) { + if (checkPanelValues()) + applyConfiguration(); + // } else { + // logger.info("Ignoring apply"); + // } + } + }); + buttonPanel.add(applyButton); + + JButton closeButton = new DeselectingButton("Close", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + closeDialog(); + } + }); + buttonPanel.add(closeButton); + + add(buttonPanel, SOUTH); + + this.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + requestFocusInWindow(); + panel.whenOpened(); + } + + @Override + public void windowClosing(WindowEvent e) { + closeDialog(); + } + }); + pack(); + minimalSize = getSize(); + setLocationRelativeTo(null); + setResizable(true); + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + int newWidth = max(getWidth(), minimalSize.width); + int newHeight = max(getHeight(), minimalSize.height); + setSize(new Dimension(newWidth, newHeight)); + } + }); + + observer = new Observer<EditManagerEvent>() { + @Override + public void notify(Observable<EditManagerEvent> sender, EditManagerEvent message) + throws Exception { + logger.info("sender is a " + sender.getClass().getCanonicalName()); + logger.info("message is a " + message.getClass().getCanonicalName()); + Edit<?> edit = message.getEdit(); + logger.info(edit.getClass().getCanonicalName()); + considerEdit(message, edit); + } + }; + editManager.addObserver(observer); + } + + private boolean checkPanelValues() { + boolean result = false; + try { + setCursor(getPredefinedCursor(WAIT_CURSOR)); + result = panel.checkValues(); + } finally { + setCursor(getPredefinedCursor(DEFAULT_CURSOR)); + } + return result; + } + + private void considerEdit(EditManagerEvent message, Edit<?> edit) { + // boolean result = false; + if (edit instanceof CompoundEdit) { + for (Edit<?> subEdit : ((CompoundEdit) edit).getChildEdits()) + considerEdit(message, subEdit); + return; + } + + Object subject = edit.getSubject(); + if (subject == owningProcessor) { + // panel.reevaluate(); + setTitle(getRelativeName(owningWorkflowBundle, activity)); + } else if (subject == owningWorkflowBundle) { + for (Workflow workflow : owningWorkflowBundle.getWorkflows()) + if (!workflow.getProcessors().contains(owningProcessor)) + clearDialog(activity); + } else if (subject == activity) { + if (message instanceof DataFlowUndoEvent) { + logger.info("undo of activity edit found"); + panel.refreshConfiguration(); + } else if (message instanceof DataFlowRedoEvent) { + logger.info("redo of activity edit found"); + panel.refreshConfiguration(); + } + } + } + + protected void configureActivity(ObjectNode json, List<ActivityPortConfiguration> inputPorts, + List<ActivityPortConfiguration> outputPorts) { + configureActivity(owningWorkflowBundle, activity, json, inputPorts, outputPorts); + } + + public void configureActivity(WorkflowBundle workflowBundle, Activity activity, + ObjectNode json, List<ActivityPortConfiguration> inputPorts, + List<ActivityPortConfiguration> outputPorts) { + try { + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + Profile profile = activity.getParent(); + List<ProcessorBinding> processorBindings = scufl2Tools + .processorBindingsToActivity(activity); + Configuration configuration = scufl2Tools.configurationFor(activity, profile); + editList.add(new ChangeJsonEdit(configuration, json)); + + configurePorts(activity, editList, processorBindings, inputPorts, PortType.INPUT); + configurePorts(activity, editList, processorBindings, outputPorts, PortType.OUTPUT); + editManager.doDataflowEdit(workflowBundle, new CompoundEdit(editList)); + } catch (IllegalStateException | EditException e) { + logger.error(e); + } + } + + private void configurePorts(Activity activity, List<Edit<?>> editList, + List<ProcessorBinding> processorBindings, + List<ActivityPortConfiguration> portDefinitions, PortType portType) { + Set<ActivityPort> ports = new HashSet<>(); + for (ActivityPort activityPort : portType == PortType.INPUT ? activity + .getInputPorts() : activity.getOutputPorts()) + ports.add(activityPort); + for (ActivityPortConfiguration portDefinition : portDefinitions) { + String portName = portDefinition.getName(); + int portDepth = portDefinition.getDepth(); + int granularPortDepth = portDefinition.getGranularDepth(); + ActivityPort activityPort = portDefinition.getActivityPort(); + if (activityPort == null) { + // no activity port so add a new one + if (portType == PortType.INPUT) + createInputPort(activity, editList, processorBindings, portDefinition); + else + createOutputPort(activity, editList, processorBindings, portDefinition); + } else { + ports.remove(activityPort); + // check if port has changed + for (ProcessorBinding processorBinding : processorBindings) + if (portType == PortType.INPUT) + for (ProcessorInputPortBinding portBinding : processorBinding + .getInputPortBindings()) { + if (!portBinding.getBoundActivityPort().equals( + activityPort)) + continue; + InputProcessorPort processorPort = portBinding + .getBoundProcessorPort(); + if (!activityPort.getName().equals(portName)) + // port name changed + if (processorPort.getName().equals(activityPort.getName())) + // default mapping so change processor port + editList.add(new RenameEdit<>(processorPort, portName)); + if (!processorPort.getDepth().equals(portDepth)) + // port depth changed + editList.add(new ChangeDepthEdit<>( + processorPort, portDepth)); + } + else + for (ProcessorOutputPortBinding portBinding : processorBinding + .getOutputPortBindings()) { + if (!portBinding.getBoundActivityPort().equals( + activityPort)) + continue; + OutputProcessorPort processorPort = portBinding + .getBoundProcessorPort(); + if (!activityPort.getName().equals(portName)) + // port name changed + if (processorPort.getName().equals( + activityPort.getName())) + // default mapping so change processor port + editList.add(new RenameEdit<>( + processorPort, portName)); + if (!processorPort.getDepth().equals(portDepth)) + // port depth changed + editList.add(new ChangeDepthEdit<>( + processorPort, portDepth)); + if (!processorPort.getGranularDepth().equals( + granularPortDepth)) + // port granular depth changed + editList.add(new ChangeGranularDepthEdit<>( + processorPort, granularPortDepth)); + } + if (!activityPort.getName().equals(portName)) + // port name changed + editList.add(new RenameEdit<>(activityPort, portName)); + if (!activityPort.getDepth().equals(portDepth)) + // port depth changed + editList.add(new ChangeDepthEdit<>(activityPort, portDepth)); + if (activityPort instanceof OutputActivityPort) { + OutputActivityPort outputActivityPort = (OutputActivityPort) activityPort; + Integer granularDepth = outputActivityPort + .getGranularDepth(); + if (granularDepth == null + || !granularDepth.equals(granularPortDepth)) + // granular port depth changed + editList.add(new ChangeGranularDepthEdit<>( + outputActivityPort, granularPortDepth)); + } + } + } + + // remove any unconfigured ports + for (ActivityPort activityPort : ports) { + // remove processor ports and bindings + for (ProcessorBinding processorBinding : processorBindings) + if (portType.equals(PortType.INPUT)) + for (ProcessorInputPortBinding portBinding : processorBinding + .getInputPortBindings()) { + if (portBinding.getBoundActivityPort().equals(activityPort)) { + editList.add(new RemoveProcessorInputPortEdit(processorBinding + .getBoundProcessor(), portBinding.getBoundProcessorPort())); + editList.add(new RemoveChildEdit<>(processorBinding, + portBinding)); + } + } + else + for (ProcessorOutputPortBinding portBinding : processorBinding + .getOutputPortBindings()) + if (portBinding.getBoundActivityPort().equals(activityPort)) { + editList.add(new RemoveProcessorOutputPortEdit(processorBinding + .getBoundProcessor(), portBinding.getBoundProcessorPort())); + editList.add(new RemoveChildEdit<>(processorBinding, + portBinding)); + } + // remove activity port + editList.add(new RemoveChildEdit<Activity>(activity, activityPort)); + } + } + + private void createInputPort(Activity activity, List<Edit<?>> editList, + List<ProcessorBinding> processorBindings, + ActivityPortConfiguration portDefinition) { + InputActivityPort actPort = new InputActivityPort(null, + portDefinition.getName()); + actPort.setDepth(portDefinition.getDepth()); + // add port to activity + editList.add(new AddChildEdit<>(activity, actPort)); + for (ProcessorBinding processorBinding : processorBindings) { + Processor processor = processorBinding.getBoundProcessor(); + // add a new processor port + InputProcessorPort procPort = new InputProcessorPort(); + procPort.setName(portDefinition.getName()); + procPort.setDepth(portDefinition.getDepth()); + editList.add(new AddProcessorInputPortEdit(processor, procPort)); + // add a new port binding + ProcessorInputPortBinding binding = new ProcessorInputPortBinding(); + binding.setBoundProcessorPort(procPort); + binding.setBoundActivityPort(actPort); + editList.add(new AddChildEdit<>(processorBinding, binding)); + } + } + + private void createOutputPort(Activity activity, List<Edit<?>> editList, + List<ProcessorBinding> processorBindings, + ActivityPortConfiguration portDefinition) { + OutputActivityPort actPort = new OutputActivityPort(null, + portDefinition.getName()); + actPort.setDepth(portDefinition.getDepth()); + actPort.setGranularDepth(portDefinition.getGranularDepth()); + // add port to activity + editList.add(new AddChildEdit<Activity>(activity, actPort)); + for (ProcessorBinding processorBinding : processorBindings) { + Processor processor = processorBinding.getBoundProcessor(); + // add a new processor port + OutputProcessorPort procPort = new OutputProcessorPort(); + procPort.setName(portDefinition.getName()); + procPort.setDepth(portDefinition.getDepth()); + procPort.setGranularDepth(portDefinition.getGranularDepth()); + editList.add(new AddProcessorOutputPortEdit(processor, procPort)); + // add a new port binding + ProcessorOutputPortBinding binding = new ProcessorOutputPortBinding(); + binding.setBoundProcessorPort(procPort); + binding.setBoundActivityPort(actPort); + editList.add(new AddChildEdit<>(processorBinding, binding)); + } + } + + protected static Processor findProcessor(Activity activity) { + for (ProcessorBinding processorBinding : scufl2Tools + .processorBindingsToActivity(activity)) + return processorBinding.getBoundProcessor(); + return null; + } + + public static String getRelativeName(WorkflowBundle workflowBundle, Activity activity) { + StringBuilder relativeName = new StringBuilder(""); + if (workflowBundle != null) { + Workflow workflow = workflowBundle.getMainWorkflow(); + if (workflow != null) { + relativeName.append(workflow.getName()); + relativeName.append(":"); + } + } + Processor processor = findProcessor(activity); + if (processor != null) + relativeName.append(processor.getName()); + return relativeName.toString(); + } + + public boolean closeDialog() { + if (panel.isConfigurationChanged()) { + String relativeName = getRelativeName(owningWorkflowBundle, activity); + if (checkPanelValues()) { + int answer = showConfirmDialog(this, + "Do you want to save the configuration of " + relativeName + "?", + relativeName, YES_NO_CANCEL_OPTION); + if (answer == YES_OPTION) { + applyConfiguration(); + } else if (answer == CANCEL_OPTION) { + return false; + } + } else if (showConfirmDialog( + this, + "New configuration could not be saved. Do you still want to close?", + relativeName, YES_NO_OPTION) == NO_OPTION) + return false; + } + panel.whenClosed(); + clearDialog(activity); + return true; + } + + private void applyConfiguration() { + panel.noteConfiguration(); + configureActivity(panel.getJson(), panel.getInputPorts(), + panel.getOutputPorts()); + panel.refreshConfiguration(); + } + + @Override + public void dispose() { + super.dispose(); + editManager.removeObserver(observer); + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationPanel.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationPanel.java new file mode 100644 index 0000000..cf7f42a --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityConfigurationPanel.java
@@ -0,0 +1,214 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.JPanel; + +import org.apache.log4j.Logger; + +import uk.org.taverna.commons.services.ActivityTypeNotFoundException; +import uk.org.taverna.commons.services.InvalidConfigurationException; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.port.ActivityPort; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * @author alanrw + */ +@SuppressWarnings("serial") +public abstract class ActivityConfigurationPanel extends JPanel { + private static final Logger logger = Logger.getLogger(ActivityConfigurationPanel.class); + private final static Scufl2Tools scufl2Tools = new Scufl2Tools(); + + // protected final URITools uriTools = new URITools(); + private final Activity activity; + private final Configuration configuration; + private final List<ActivityPortConfiguration> inputPorts; + private final List<ActivityPortConfiguration> outputPorts; + protected ObjectNode json; + + public ActivityConfigurationPanel(Activity activity) { + this(activity, scufl2Tools.configurationFor(activity, + activity.getParent())); + } + + public ActivityConfigurationPanel(Activity activity, + Configuration configuration) { + this.activity = activity; + this.configuration = configuration; + inputPorts = new ArrayList<>(); + outputPorts = new ArrayList<>(); + } + + /** + * Initializes the configuration panel. This method is also used to discard + * any changes and reset the panel to its initial state. Subclasses should + * implement this method to set up the panel and must call + * <tt>super.initialise()</tt> first. + */ + protected void initialise() { + json = configuration.getJson().deepCopy(); + inputPorts.clear(); + for (InputActivityPort activityPort : activity.getInputPorts()) + inputPorts.add(new ActivityPortConfiguration(activityPort)); + outputPorts.clear(); + for (OutputActivityPort activityPort : activity.getOutputPorts()) + outputPorts.add(new ActivityPortConfiguration(activityPort)); + } + + public abstract boolean checkValues(); + + public abstract void noteConfiguration(); + + public boolean isConfigurationChanged() { + noteConfiguration(); + if (portsChanged(inputPorts, activity.getInputPorts().size())) + return true; + if (portsChanged(outputPorts, activity.getOutputPorts().size())) + return true; + return !json.equals(configuration.getJson()); + } + + public Configuration getConfiguration() { + return configuration; + } + + public ObjectNode getJson() { + return json; + } + + protected void setJson(ObjectNode json) { + this.json = json; + } + + public void refreshConfiguration() { + initialise(); + } + + public void whenOpened() { + } + + public void whenClosed() { + } + + /** + * Convenience method for getting simple String property values. + * + * @param name + * the property name + * @return the property value + */ + protected String getProperty(String name) { + JsonNode jsonNode = json.get(name); + if (jsonNode == null) + return null; + return json.get(name).asText(); + } + + /** + * Convenience method for setting simple String property values. + * + * @param name + * the property name + * @param value + * the property value + */ + protected void setProperty(String name, String value) { + json.put(name, value); + } + + public List<ActivityPortConfiguration> getInputPorts() { + return inputPorts; + } + + public List<ActivityPortConfiguration> getOutputPorts() { + return outputPorts; + } + + protected void configureInputPorts(ServiceRegistry serviceRegistry) { + try { + Map<String, InputActivityPort> newInputPorts = new HashMap<>(); + for (InputActivityPort port : serviceRegistry + .getActivityInputPorts(getActivity().getType(), getJson())) + newInputPorts.put(port.getName(), port); + List<ActivityPortConfiguration> inputPorts = getInputPorts(); + for (ActivityPortConfiguration portConfig : new ArrayList<>( + inputPorts)) + if (newInputPorts.containsKey(portConfig.getName())) { + InputActivityPort port = newInputPorts.remove(portConfig + .getName()); + portConfig.setDepth(port.getDepth()); + } else + inputPorts.remove(portConfig); + for (InputActivityPort newPort : newInputPorts.values()) + inputPorts.add(new ActivityPortConfiguration(newPort.getName(), + newPort.getDepth())); + } catch (InvalidConfigurationException | ActivityTypeNotFoundException e) { + logger.warn("Error configuring input ports", e); + } + } + + protected void configureOutputPorts(ServiceRegistry serviceRegistry) { + try { + Map<String, OutputActivityPort> newOutputPorts = new HashMap<>(); + for (OutputActivityPort port : serviceRegistry + .getActivityOutputPorts(getActivity().getType(), getJson())) + newOutputPorts.put(port.getName(), port); + List<ActivityPortConfiguration> outputPorts = getOutputPorts(); + for (ActivityPortConfiguration portConfig : new ArrayList<>( + outputPorts)) + if (newOutputPorts.containsKey(portConfig.getName())) { + OutputActivityPort port = newOutputPorts.remove(portConfig + .getName()); + portConfig.setDepth(port.getDepth()); + portConfig.setGranularDepth(port.getGranularDepth()); + } else + outputPorts.remove(portConfig); + for (OutputActivityPort newPort : newOutputPorts.values()) + outputPorts.add(new ActivityPortConfiguration( + newPort.getName(), newPort.getDepth())); + } catch (InvalidConfigurationException | ActivityTypeNotFoundException e) { + logger.warn("Error configuring output ports", e); + } + } + + private boolean portsChanged(List<ActivityPortConfiguration> portDefinitions, int ports) { + int checkedPorts = 0; + for (ActivityPortConfiguration portDefinition : portDefinitions) { + String portName = portDefinition.getName(); + int portDepth = portDefinition.getDepth(); + ActivityPort activityPort = portDefinition.getActivityPort(); + if (activityPort == null) + // new port added + return true; + if (!activityPort.getName().equals(portName)) + // port name changed + return true; + if (!activityPort.getDepth().equals(portDepth)) + // port depth changed + return true; + checkedPorts++; + } + if (checkedPorts < ports) + // ports deleted + return true; + return false; + } + + public Activity getActivity() { + return activity; + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityPortConfiguration.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityPortConfiguration.java new file mode 100644 index 0000000..6b23fd5 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ActivityPortConfiguration.java
@@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import uk.org.taverna.scufl2.api.port.ActivityPort; + +/** + * + * + * @author David Withers + */ +public class ActivityPortConfiguration { + + private ActivityPort activityPort; + + private String name; + + private int depth; + + private int granularDepth; + + public ActivityPortConfiguration(ActivityPort activityPort) { + this.activityPort = activityPort; + name = activityPort.getName(); + depth = activityPort.getDepth(); + } + + public ActivityPortConfiguration(String name, int depth) { + this(name, depth, depth); + } + + public ActivityPortConfiguration(String name, int depth, int granularDepth) { + this.name = name; + this.depth = depth; + this.granularDepth = granularDepth; + } + + public ActivityPort getActivityPort() { + return activityPort; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getDepth() { + return depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + + public int getGranularDepth() { + return granularDepth; + } + + public void setGranularDepth(int granularDepth) { + this.granularDepth = granularDepth; + } + +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactory.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactory.java new file mode 100644 index 0000000..b5d29d7 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactory.java
@@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import java.util.List; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; + +/** + * Defines a factory class that when associated with a selected object creates a + * {@link ContextualView} for that selection. + * <p> + * This factory acts as an SPI to find {@link ContextualView}s for a given + * Activity and other workflow components. + * </p> + * + * @author Stuart Owen + * @author Ian Dunlop + * @author Stian Soiland-Reyes + * + * + * @param <SelectionType> + * - the selection type this factory is associated with + * + * @see ContextualView + * @see ContextualViewFactoryRegistry + */ +public interface ContextualViewFactory<SelectionType> { + /** + * @param selection + * - the object for which ContextualViews needs to be generated + * @return instance of {@link ContextualView} + */ + public List<ContextualView> getViews(SelectionType selection); + + /** + * Used by the SPI system to find the correct factory that can handle the + * given object type. + * + * @param selection + * @return true if this factory relates to the given selection type + * @see ContextualViewFactoryRegistry + */ + public boolean canHandle(Object selection); +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactoryRegistry.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactoryRegistry.java new file mode 100644 index 0000000..305f3c0 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ContextualViewFactoryRegistry.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (C) 2011 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import java.util.List; + +/** + * A registry for discovering ActivityViewFactories for a given object, + * like an {@link net.sf.taverna.t2.workflowmodel.processor.activity.Activity}. + * + * @author David Withers + */ +public interface ContextualViewFactoryRegistry { + /** + * Discover and return the ContextualViewFactory associated to the provided + * object. This is accomplished by returning the discovered + * {@link ContextualViewFactory#canHandle(Object)} that returns true for + * that Object. + * + * @param object + * @return + * @see ContextualViewFactory#canHandle(Object) + */ + public <T> List<ContextualViewFactory<? super T>> getViewFactoriesForObject(T object); +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/DependencyConfigurationPanel.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/DependencyConfigurationPanel.java new file mode 100644 index 0000000..c28cb55 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/DependencyConfigurationPanel.java
@@ -0,0 +1,293 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.Color.RED; +import static java.awt.GridBagConstraints.FIRST_LINE_START; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.event.ItemEvent.DESELECTED; +import static java.awt.event.ItemEvent.SELECTED; +import static java.util.Arrays.asList; +import static javax.swing.Box.createRigidArea; +import static javax.swing.BoxLayout.PAGE_AXIS; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.BoxLayout; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.EmptyBorder; + +/** + * Component for configuring activities that require dependencies. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class DependencyConfigurationPanel extends JPanel { + private String classLoaderSharing; + private List<String> localDependencies; + private File libDir; + + public DependencyConfigurationPanel(String classLoaderSharing, + List<String> localDependencies, File libDir) { + this.classLoaderSharing = classLoaderSharing; + this.localDependencies = localDependencies; + this.libDir = libDir; + setLayout(new BoxLayout(this, PAGE_AXIS)); + + // Create panel with classloading options + JPanel classloadingPanel = new ClassloadingPanel(); + // Create panel for selecting jar files + JPanel jarFilesPanel = new JarFilesPanel(); + + add(classloadingPanel); + add(createRigidArea(new Dimension(0,10))); + add(jarFilesPanel); + add(createRigidArea(new Dimension(0,10))); + + } + + public String getClassLoaderSharing() { + return classLoaderSharing; + } + + public List<String> getLocalDependencies() { + return localDependencies; + } + + // Classloading option 'workflow' + private static final String WORKFLOW = "Shared for whole workflow"; + // Classloading option 'system' + private static final String SYSTEM = "System classloader"; + + // Panel containing classloading options + private class ClassloadingPanel extends JPanel { + // Combobox with classloading options + private JComboBox<String> jcbClassloadingOption; + // Classloading option descriptions + private HashMap<String, String> classloadingDescriptions; + // JLabel with classloading option description + private JLabel jlClassloadingDescription; + + /* + * Panel containing a list of possible classloading options which users + * can select from + */ + private ClassloadingPanel() { + super(new GridBagLayout()); + jcbClassloadingOption = new JComboBox<>(new String[] { WORKFLOW, + SYSTEM }); + // Set the current classlaoding option based on the configuration bean + if ("workflow".equals(classLoaderSharing)) { + jcbClassloadingOption.setSelectedItem(WORKFLOW); + } else if ("system".equals(classLoaderSharing)) { + jcbClassloadingOption.setSelectedItem(SYSTEM); + } + + jcbClassloadingOption.addActionListener(new ActionListener(){ + // Fires up when combobox selection changes + @Override + public void actionPerformed(ActionEvent e) { + Object selectedItem = jcbClassloadingOption.getSelectedItem(); + jlClassloadingDescription.setText(classloadingDescriptions + .get(selectedItem)); + if (selectedItem.equals(WORKFLOW)) + classLoaderSharing = "workflow"; + else if (selectedItem.equals(SYSTEM)) + classLoaderSharing = "system"; + } + }); + //jcbClassloadingOption.setEnabled(false); + + classloadingDescriptions = new HashMap<>(); + classloadingDescriptions.put(WORKFLOW, "<html><small>" + + "Classes are shared across the whole workflow (with any service<br>" + + "also selecting this option), but are reinitialised for each workflow run.<br>" + + "This might be needed if a service passes objects to another, or <br>" + + "state is shared within static members of loaded classes." + + "</small></html>"); + classloadingDescriptions.put(SYSTEM, "<html><small><p>" + + "The (global) system classloader is used, any dependencies defined here are<br>" + + "made available globally on the first run. Note that if you are NOT using<br>" + + "the defaulf Taverna BootstrapClassLoader, any settings here will be disregarded." + + "</p><p>" + + "This is mainly useful if you are using JNI-based libraries. Note that <br>" + + "for JNI you also have to specify <code>-Djava.library.path</code> and <br>" + + "probably your operating system's dynamic library search path<br>" + + "<code>LD_LIBRARY_PATH</code> / <code>DYLD_LIBRARY_PATH</code> / <code>PATH</code> </p>" + + "</small></html>"); + + /* + * Set the current classlaoding description based on the item + * selected in the combobox. + */ + jlClassloadingDescription = new JLabel(classloadingDescriptions + .get(jcbClassloadingOption.getSelectedItem())); + + // Add components to the ClassloadingPanel + GridBagConstraints c = new GridBagConstraints(); + c.anchor = FIRST_LINE_START; + c.fill = HORIZONTAL; + c.gridx = 0; + c.insets = new Insets(10,0,0,0); + add(new JLabel("Classloader persistence"), c); + c.insets = new Insets(0,0,0,0); + add(jcbClassloadingOption, c); + c.insets = new Insets(0,30,0,0); + add(jlClassloadingDescription, c); + } + } + + // Panel for users to add local JAR dependencies (contains a list of jar files which users can select from) + private class JarFilesPanel extends JPanel { + private JLabel warning = new JLabel( + "<html>" + + "<center<font color='red'>" + + "Warning: Depending on local libraries makes this workflow<br>" + + "difficult or impossible to run for other users. Try depending<br>" + + "on artifacts from a public repository if possible.</font></center>" + + "</html>"); + + private JarFilesPanel() { + super(); + setMinimumSize(new Dimension(400, 150)); + setLayout(new BorderLayout()); + setBorder(new EmptyBorder(0,10,0,10)); + + JPanel labelPanel = new JPanel(); + labelPanel.setLayout(new BoxLayout(labelPanel, PAGE_AXIS)); + JLabel label = new JLabel("Local JAR files"); + JLabel libLabel = new JLabel("<html><small>" + libDir.getAbsolutePath() + + "</small></html>"); + labelPanel.add(label); + labelPanel.add(libLabel); + + add(labelPanel, NORTH); + add(new JScrollPane(jarFiles(), VERTICAL_SCROLLBAR_AS_NEEDED, + HORIZONTAL_SCROLLBAR_NEVER), CENTER); + + warning.setVisible(false); + /* + * We'll skip the warning until we actually have support for + * artifacts + */ + //add(warning); + updateWarning(); + } + + private void updateWarning() { + // Show warning if there is any local dependencies + warning.setVisible(!localDependencies.isEmpty()); + } + + public JPanel jarFiles() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, PAGE_AXIS)); + + // List of all jar files in the lib directory + List<String> jarFiles = asList(libDir + .list(new FileExtFilter(".jar"))); + /* + * We also add the list of jars that may have been configured + * sometime before but are now not present in the lib directory for + * some reason + */ + Set<String> missingLocalDeps = new HashSet<>(localDependencies); + missingLocalDeps.removeAll(jarFiles); + /* + * jarFiles and missingLocalDeps now contain two sets of files that + * do not intersect + */ + List<String> jarFilesList = new ArrayList<>(); + // Put them all together + jarFilesList.addAll(jarFiles); + jarFilesList.addAll(missingLocalDeps); + Collections.sort(jarFilesList); + + if (jarFilesList.isEmpty()) { + panel.add(new JLabel("<html><small>To depend on a JAR file, " + + "copy it to the above-mentioned folder.</small></html>")); + return panel; + } + + for (String jarFile : jarFilesList) { + JCheckBox checkBox = new JCheckBox(jarFile); + // Has it already been selected in some previous configuring? + checkBox.setSelected(localDependencies.contains(jarFile)); + checkBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + JCheckBox box = (JCheckBox) e.getSource(); + if (e.getStateChange() == SELECTED) + localDependencies.add(box.getText()); + else if (e.getStateChange() == DESELECTED) + localDependencies.remove(box.getText()); + updateWarning(); + } + }); + panel.add(checkBox); + // The jar may not be in the lib directory, so warn the user + if (!new File(libDir, jarFile).exists()) { + checkBox.setForeground(RED); + checkBox.setText(checkBox.getText() + " (missing file!)"); + } + } + return panel; + } + } + + public static class FileExtFilter implements FilenameFilter { + final String ext; + + public FileExtFilter(String ext) { + this.ext = ext; + } + + @Override + public boolean accept(File dir, String name) { + return name.endsWith(ext); + } + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListConfigurationComponent.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListConfigurationComponent.java new file mode 100644 index 0000000..c0e3aab --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListConfigurationComponent.java
@@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.EAST; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.FlowLayout.RIGHT; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +/** + * @author David Withers + */ +@SuppressWarnings("serial") +public abstract class ListConfigurationComponent<T> extends JPanel { + private static final String REMOVE = "Remove"; + private static final String ADD = "Add"; + + private String name; + private List<T> items; + private JPanel listPanel; + + public ListConfigurationComponent(String name, List<T> items) { + this.name = name; + setLayout(new BorderLayout()); + + listPanel = new JPanel(new ListLayout()); + JPanel buttonPanel = new JPanel(new FlowLayout(RIGHT)); + buttonPanel.add(new JButton(createAddAction())); + + add(new JScrollPane(listPanel), CENTER); + add(buttonPanel, SOUTH); + + setItems(items); + } + + protected void setItems(List<T> items) { + this.items = items; + listPanel.removeAll(); + for (T item : items) + addItemComponent(item); + } + + protected void addItem(T item) { + items.add(item); + addItemComponent(item); + } + + protected void addItemComponent(T item) { + JComponent itemPanel = new JPanel(new BorderLayout()); + itemPanel.add(createItemComponent(item), CENTER); + itemPanel.add(new JButton(createRemoveAction(item)), EAST); + listPanel.add(itemPanel); + listPanel.revalidate(); + listPanel.repaint(); + } + + protected void removeItem(T item) { + int index = items.indexOf(item); + if (index >= 0) { + items.remove(index); + listPanel.remove(index); + listPanel.revalidate(); + listPanel.repaint(); + } + } + + private Action createRemoveAction(final T item) { + return new AbstractAction(REMOVE) { + @Override + public void actionPerformed(ActionEvent e) { + removeItem(item); + } + }; + } + + private Action createAddAction() { + return new AbstractAction(ADD + " " + name) { + @Override + public void actionPerformed(ActionEvent e) { + addItem(createDefaultItem()); + } + }; + } + + protected abstract Component createItemComponent(T item); + + protected abstract T createDefaultItem(); +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListLayout.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListLayout.java new file mode 100644 index 0000000..0ce35b5 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ListLayout.java
@@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.LayoutManager; + +/** + * Lays out components vertically using their preferred height and the available + * width. + * + * @author David Withers + */ +public class ListLayout implements LayoutManager { + private static final int DEFAULT_GAP = 5; + private final int gap; + + public ListLayout() { + this(DEFAULT_GAP); + } + + public ListLayout(int gap) { + this.gap = gap; + } + + @Override + public void removeLayoutComponent(Component comp) { + } + + @Override + public void addLayoutComponent(String name, Component comp) { + } + + @Override + public void layoutContainer(Container parent) { + Insets insets = parent.getInsets(); + int x = insets.left; + int y = insets.top; + int width = parent.getWidth() - insets.left - insets.right; + Component[] components = parent.getComponents(); + for (int i = 0; i < components.length; i++) { + components[i].setLocation(x, y); + components[i].setSize(width, + components[i].getPreferredSize().height); + y = y + gap + components[i].getHeight(); + } + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + Insets insets = parent.getInsets(); + int minimumWidth = 0; + int minimumHeight = 0; + Component[] components = parent.getComponents(); + for (int i = 0; i < components.length; i++) { + Dimension size = components[i].getPreferredSize(); + if (size.width > minimumWidth) + minimumWidth = size.width; + minimumHeight = minimumHeight + size.height + gap; + } + minimumWidth = minimumWidth + insets.left + insets.right; + minimumHeight = minimumHeight + insets.top + insets.bottom; + + return new Dimension(minimumWidth, minimumHeight); + } + + @Override + public Dimension preferredLayoutSize(Container parent) { + return minimumLayoutSize(parent); + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/MultiPageActivityConfigurationPanel.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/MultiPageActivityConfigurationPanel.java new file mode 100644 index 0000000..19cd180 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/MultiPageActivityConfigurationPanel.java
@@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import static java.awt.BorderLayout.CENTER; + +import java.awt.BorderLayout; +import java.awt.Component; + +import javax.swing.JTabbedPane; + +import uk.org.taverna.scufl2.api.activity.Activity; + +/** + * Component for configuring activities that have multiple configuration pages. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public abstract class MultiPageActivityConfigurationPanel extends + ActivityConfigurationPanel { + private JTabbedPane tabbedPane; + + /** + * Constructs a new <code>MultiPageActivityConfigurationPanel</code>. + * + * @param activity + */ + public MultiPageActivityConfigurationPanel(Activity activity) { + super(activity); + setLayout(new BorderLayout()); + tabbedPane = new JTabbedPane(); + add(tabbedPane, CENTER); + } + + public void addPage(String name, Component component) { + tabbedPane.addTab(name, component); + } + + public void removePage(String name) { + tabbedPane.removeTabAt(tabbedPane.indexOfTab(name)); + } + + public void removeAllPages() { + tabbedPane.removeAll(); + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ScriptConfigurationComponent.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ScriptConfigurationComponent.java new file mode 100644 index 0000000..8cb7652 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ScriptConfigurationComponent.java
@@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Color.WHITE; +import static java.awt.Font.PLAIN; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.lang.ui.FileTools.readStringFromFile; +import static net.sf.taverna.t2.lang.ui.FileTools.saveStringToFile; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JTextPane; + +import net.sf.taverna.t2.lang.ui.KeywordDocument; +import net.sf.taverna.t2.lang.ui.LineEnabledTextPanel; +import net.sf.taverna.t2.lang.ui.LinePainter; +import net.sf.taverna.t2.lang.ui.NoWrapEditorKit; + +/** + * Component for configuring activities that have scripts. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class ScriptConfigurationComponent extends JPanel { + private JTextPane scriptTextArea; + + public ScriptConfigurationComponent(String script, Set<String> keywords, + Set<String> ports, final String scriptType, + final String fileExtension) { + this(script, keywords, ports, scriptType, fileExtension, ""); + } + + public ScriptConfigurationComponent(String script, Set<String> keywords, + Set<String> ports, final String scriptType, + final String fileExtension, final String resetScript) { + super(new BorderLayout()); + scriptTextArea = new JTextPane(); + new LinePainter(scriptTextArea, WHITE); + + final KeywordDocument doc = new KeywordDocument(keywords, ports); + + // NOTE: Due to T2-1145 - always set editor kit BEFORE setDocument + scriptTextArea.setEditorKit(new NoWrapEditorKit()); + scriptTextArea.setFont(new Font("Monospaced", PLAIN, 14)); + scriptTextArea.setDocument(doc); + scriptTextArea.setText(script); + scriptTextArea.setCaretPosition(0); + scriptTextArea.setPreferredSize(new Dimension(200, 100)); + + add(new LineEnabledTextPanel(scriptTextArea), CENTER); + + final JButton checkScriptButton = new JButton("Check script"); + checkScriptButton.setToolTipText("Check the " + scriptType + " script"); + checkScriptButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ex) { + showMessageDialog(ScriptConfigurationComponent.this, scriptType + + " script check not implemented", scriptType + + " script check", INFORMATION_MESSAGE); + } + }); + + JButton loadScriptButton = new JButton("Load script"); + loadScriptButton.setToolTipText("Load a " + scriptType + + " script from a file"); + loadScriptButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String newScript = readStringFromFile( + ScriptConfigurationComponent.this, "Load " + scriptType + + " script", fileExtension); + if (newScript != null) { + scriptTextArea.setText(newScript); + scriptTextArea.setCaretPosition(0); + } + } + }); + + JButton saveRScriptButton = new JButton("Save script"); + saveRScriptButton.setToolTipText("Save the " + scriptType + + " script to a file"); + saveRScriptButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + saveStringToFile(ScriptConfigurationComponent.this, "Save " + + scriptType + " script", fileExtension, + scriptTextArea.getText()); + } + }); + + JButton clearScriptButton = new JButton("Clear script"); + clearScriptButton.setToolTipText("Clear current script from the edit area"); + clearScriptButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (showConfirmDialog(ScriptConfigurationComponent.this, + "Do you really want to clear the script?", + "Clearing the script", YES_NO_OPTION) == YES_OPTION) + scriptTextArea.setText(resetScript); + } + }); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout()); + buttonPanel.add(checkScriptButton); + buttonPanel.add(loadScriptButton); + buttonPanel.add(saveRScriptButton); + buttonPanel.add(clearScriptButton); + + add(buttonPanel, SOUTH); + } + + public String getScript() { + return scriptTextArea.getText(); + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextField.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextField.java new file mode 100644 index 0000000..cf1ff96 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextField.java
@@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import javax.swing.JTextField; + +/** + * Adds a "<tt>valid</tt>" property to a JTextField. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class ValidatingTextField extends JTextField { + private boolean valid = true; + + public ValidatingTextField() { + } + + public ValidatingTextField(String text) { + super(text); + } + + @Override + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + if (this.valid != valid) { + boolean old = this.valid; + this.valid = valid; + firePropertyChange("valid", old, valid); + } + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextGroup.java b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextGroup.java new file mode 100644 index 0000000..7597f7c --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/ValidatingTextGroup.java
@@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +/** + * + * + * @author David Withers + */ +public class ValidatingTextGroup { + private Map<ValidatingTextField, DocumentListener> textComponents; + + public ValidatingTextGroup() { + textComponents = new HashMap<>(); + } + + public void addValidTextComponent(ValidatingTextField textComponent) { + setUniqueText(textComponent); + DocumentListener documentListener = new ValidatorDocumentListener(); + textComponent.getDocument().addDocumentListener(documentListener); + textComponents.put(textComponent, documentListener); + } + + public void addTextComponent(ValidatingTextField textComponent) { + DocumentListener documentListener = new ValidatorDocumentListener(); + textComponent.getDocument().addDocumentListener(documentListener); + textComponents.put(textComponent, documentListener); + validate(); + } + + public void removeTextComponent(ValidatingTextField textComponent) { + textComponent.getDocument().removeDocumentListener( + textComponents.remove(textComponent)); + validate(); + } + + private void setUniqueText(ValidatingTextField textComponent) { + String text = textComponent.getText(); + if (textExists(text)) { + // Remove any existing number suffix + String nameTemplate = text.replaceAll("_\\d+$", "_"); + long i = 1; + do { + text = nameTemplate + i++; + } while (textExists(text)); + + textComponent.setText(text); + } + } + + private void validate() { + Map<String, ValidatingTextField> textValues = new HashMap<>(); + Set<ValidatingTextField> maybeValid = new HashSet<>(); + for (ValidatingTextField textComponent : textComponents.keySet()) { + ValidatingTextField duplicate = textValues.get(textComponent + .getText()); + if (duplicate != null) { + duplicate.setValid(false); + maybeValid.remove(duplicate); + textComponent.setValid(false); + } else { + textValues.put(textComponent.getText(), textComponent); + maybeValid.add(textComponent); + } + } + for (ValidatingTextField textComponent : maybeValid) + textComponent.setValid(true); + } + + private boolean textExists(String text) { + for (ValidatingTextField currentTextComponent : textComponents.keySet()) + if (text.equals(currentTextComponent.getText())) + return true; + return false; + } + + class ValidatorDocumentListener implements DocumentListener { + @Override + public void insertUpdate(DocumentEvent e) { + validate(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + validate(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + validate(); + } + } +}
diff --git a/taverna-workbench-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI new file mode 100644 index 0000000..312f95b --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1,2 @@ +#net.sf.taverna.t2.workbench.ui.actions.activity.draggable.ActivityDraggerPaletteComponentFactory +#net.sf.taverna.t2.workbench.ui.views.contextualviews.DragActivitiesToHereComponentFactory
diff --git a/taverna-workbench-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI new file mode 100644 index 0000000..1448a49 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
@@ -0,0 +1,3 @@ +#net.sf.taverna.t2.workbench.ui.actions.activity.draggable.ActivityDraggerPaletteComponent +#net.sf.taverna.t2.workbench.ui.views.contextualviews.DragActivitiesToHereComponent +net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualViewComponent
diff --git a/taverna-workbench-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context-osgi.xml b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context-osgi.xml new file mode 100644 index 0000000..ab22b97 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context-osgi.xml
@@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + +</beans:beans>
diff --git a/taverna-workbench-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context.xml b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context.xml new file mode 100644 index 0000000..d662d87 --- /dev/null +++ b/taverna-workbench-contextual-views-api/src/main/resources/META-INF/spring/contextual-views-api-context.xml
@@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + +</beans>
diff --git a/taverna-workbench-contextual-views-impl/pom.xml b/taverna-workbench-contextual-views-impl/pom.xml new file mode 100644 index 0000000..1cafa80 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/pom.xml
@@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>contextual-views-impl</artifactId> + <packaging>bundle</packaging> + <name>Contextual Views Implementation</name> + <description>Contextual views for the activities</description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + <version>${scufl2.version}</version> + </dependency> + + <dependency> + <groupId>javax.help</groupId> + <artifactId>javahelp</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/impl/ContextualViewFactoryRegistryImpl.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/impl/ContextualViewFactoryRegistryImpl.java new file mode 100644 index 0000000..8bac354 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/activity/impl/ContextualViewFactoryRegistryImpl.java
@@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ + +/** + * @author Alan R Williams + */ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.impl; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry; + +/** + * An SPI registry for discovering ActivityViewFactories for a given object, + * like an {@link net.sf.taverna.t2.workflowmodel.processor.activity.Activity}. + * <p> + * For {@link ContextualViewFactory factories} to be found, its full qualified + * name needs to be defined as a resource file + * <code>/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualViewFactory</code> + * + * @author Stuart Owen + * @author Ian Dunlop + * @author Stian Soiland-Reyes + * + * @see ContextualViewFactory + */ +public class ContextualViewFactoryRegistryImpl implements + ContextualViewFactoryRegistry { + private List<ContextualViewFactory<?>> contextualViewFactories; + + /** + * Discover and return the ContextualViewFactory associated to the provided + * object. This is accomplished by returning the discovered + * {@link ContextualViewFactory#canHandle(Object)} that returns true for + * that Object. + * + * @param object + * @return + * @see ContextualViewFactory#canHandle(Object) + */ + @Override + public List<ContextualViewFactory<?>> getViewFactoriesForObject( + Object object) { + List<ContextualViewFactory<?>> result = new ArrayList<>(); + for (ContextualViewFactory<?> factory : contextualViewFactories) + if (factory.canHandle(object)) + result.add(factory); + return result; + } + + public void setContextualViewFactories( + List<ContextualViewFactory<?>> contextualViewFactories) { + this.contextualViewFactories = contextualViewFactories; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualView.java new file mode 100644 index 0000000..018a121 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualView.java
@@ -0,0 +1,263 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated; + +import static javax.swing.BoxLayout.Y_AXIS; + +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.regex.Pattern; + +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +import net.sf.taverna.t2.annotation.Annotated; +import net.sf.taverna.t2.annotation.AnnotationBeanSPI; +import net.sf.taverna.t2.lang.ui.DialogTextArea; +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.impl.ContextualViewComponent; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * This is a ContextualView that should be able to display and allow editing of + * Annotation information for any Annotated. At the moment it is only used for + * Dataflow. + * + * @author Alan R Williams + */ +@SuppressWarnings("serial") +class AnnotatedContextualView extends ContextualView { + private static final int WORKFLOW_NAME_LENGTH = 20; + public static final String VIEW_TITLE = "Annotations"; + private final static String MISSING_VALUE = "Type here to give details"; + private final static int DEFAULT_AREA_WIDTH = 60; + private final static int DEFAULT_AREA_ROWS = 8; + + private static Logger logger = Logger + .getLogger(AnnotatedContextualView.class); + private static PropertyResourceBundle prb = (PropertyResourceBundle) ResourceBundle + .getBundle("annotatedcontextualview"); + + // TODO convert to scufl2 + // private static AnnotationTools annotationTools = new AnnotationTools(); + + /** + * The object to which the Annotations apply + */ + private Annotated<?> annotated; + private SelectionManager selectionManager; + private EditManager editManager; + private boolean isStandalone = false; + private JPanel panel; + @SuppressWarnings("unused") + private final List<AnnotationBeanSPI> annotationBeans; + + public AnnotatedContextualView(Annotated<?> annotated, + EditManager editManager, SelectionManager selectionManager, + List<AnnotationBeanSPI> annotationBeans) { + super(); + this.editManager = editManager; + this.selectionManager = selectionManager; + this.annotationBeans = annotationBeans; + this.annotated = annotated; + + initialise(); + initView(); + } + + @Override + public void refreshView() { + initialise(); + } + + private void initialise() { + if (panel == null) { + panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, Y_AXIS)); + } else + panel.removeAll(); + populatePanel(); + revalidate(); + } + + @Override + public JComponent getMainFrame() { + return panel; + } + + @Override + public String getViewTitle() { + return VIEW_TITLE; + } + + private Map<String,String> getAnnotations() { + // TODO convert to scufl2 + Map<String, String> result = new HashMap<>(); + //for (Class<?> c : annotationTools.getAnnotatingClasses(annotated)) { + // String name = ""; + // try { + // name = prb.getString(c.getCanonicalName()); + // } catch (MissingResourceException e) { + // name = c.getCanonicalName(); + // } + // String value = annotationTools.getAnnotationString(annotated, c, + // MISSING_VALUE); + // result.put(name,value); + //} + return result; + } + public void populatePanel() { + JPanel scrollPanel = new JPanel(); + scrollPanel.setLayout(new BoxLayout(scrollPanel, Y_AXIS)); + panel.setBorder(new EmptyBorder(5, 5, 5, 5)); + Map<String,String>annotations = getAnnotations(); + for (String name : annotations.keySet()) { + JPanel subPanel = new JPanel(); + subPanel.setBorder(new TitledBorder(name)); + subPanel.add(createTextArea(String.class, annotations.get(name))); + scrollPanel.add(subPanel); + } + JScrollPane scrollPane = new JScrollPane(scrollPanel); + panel.add(scrollPane); + } + + private JScrollPane createTextArea(Class<?> c, String value) { + DialogTextArea area = new DialogTextArea(value); + area.setFocusable(true); + area.addFocusListener(new TextAreaFocusListener(area, c)); + area.setColumns(DEFAULT_AREA_WIDTH); + area.setRows(DEFAULT_AREA_ROWS); + area.setLineWrap(true); + area.setWrapStyleWord(true); + + return new JScrollPane(area); + } + + private class TextAreaFocusListener implements FocusListener { + String oldValue = null; + Class<?> annotationClass; + DialogTextArea area = null; + + public TextAreaFocusListener(DialogTextArea area, Class<?> c) { + annotationClass = c; + oldValue = area.getText(); + this.area = area; + } + + @Override + public void focusGained(FocusEvent e) { + if (area.getText().equals(MISSING_VALUE)) + area.setText(""); + } + + @Override + public void focusLost(FocusEvent e) { + String currentValue = area.getText(); + if (currentValue.isEmpty() || currentValue.equals(MISSING_VALUE)) { + currentValue = MISSING_VALUE; + area.setText(currentValue); + } + if (!currentValue.equals(oldValue)) { + if (currentValue == MISSING_VALUE) + currentValue = ""; + try { + WorkflowBundle currentDataflow = selectionManager + .getSelectedWorkflowBundle(); + List<Edit<?>> editList = new ArrayList<>(); + addWorkflowNameEdits(currentValue, currentDataflow, + editList); + if (!isStandalone) + ContextualViewComponent.selfGenerated = true; + editManager.doDataflowEdit(currentDataflow, + new CompoundEdit(editList)); + ContextualViewComponent.selfGenerated = false; + } catch (EditException e1) { + logger.warn("Can't set annotation", e1); + } + oldValue = area.getText(); + } + } + + private boolean isTitleAnnotation() { + // TODO convert to scufl2 + return prb.getString(annotationClass.getCanonicalName()).equals( + "Title"); + } + + // TODO convert to scufl2 + private void addWorkflowNameEdits(String currentValue, + WorkflowBundle currentDataflow, List<Edit<?>> editList) { + //editList.add(annotationTools.setAnnotationString(annotated, + // annotationClass, currentValue, edits)); + if (annotated == currentDataflow && isTitleAnnotation() + && !currentValue.isEmpty()) { + @SuppressWarnings("unused") + String sanitised = sanitiseName(currentValue); + //editList.add(edits.getUpdateDataflowNameEdit(currentDataflow, + // sanitised)); + } + } + } + + /** + * Checks that the name does not have any characters that are invalid for a + * processor name. + * <p> + * The resulting name must contain only the chars [A-Za-z_0-9]. + * + * @param name + * the original name + * @return the sanitised name + */ + private static String sanitiseName(String name) { + if (name.length() > WORKFLOW_NAME_LENGTH) + name = name.substring(0, WORKFLOW_NAME_LENGTH); + if (Pattern.matches("\\w++", name)) + return name; + StringBuilder temp = new StringBuilder(); + for (char c : name.toCharArray()) + temp.append(Character.isLetterOrDigit(c) || c == '_' ? c : '_'); + return temp.toString(); + } + + @Override + public int getPreferredPosition() { + return 500; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualViewFactory.java new file mode 100644 index 0000000..eb18803 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/annotated/AnnotatedContextualViewFactory.java
@@ -0,0 +1,43 @@ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated; + +import static java.util.Collections.singletonList; + +import java.util.List; + +import net.sf.taverna.t2.annotation.Annotated; +import net.sf.taverna.t2.annotation.AnnotationBeanSPI; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workflowmodel.processor.activity.Activity; + +public class AnnotatedContextualViewFactory implements + ContextualViewFactory<Annotated<?>> { + private EditManager editManager; + private List<AnnotationBeanSPI> annotationBeans; + private SelectionManager selectionManager; + + @Override + public boolean canHandle(Object selection) { + return ((selection instanceof Annotated) && !(selection instanceof Activity)); + } + + @Override + public List<ContextualView> getViews(Annotated<?> selection) { + return singletonList((ContextualView) new AnnotatedContextualView( + selection, editManager, selectionManager, annotationBeans)); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setAnnotationBeans(List<AnnotationBeanSPI> annotationBeans) { + this.annotationBeans = annotationBeans; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualView.java new file mode 100644 index 0000000..f9308b5 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualView.java
@@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.condition; + +import java.awt.FlowLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import uk.org.taverna.scufl2.api.core.BlockingControlLink; + +/** + * Contextual view for dataflow's control (condition) links. + * + * @author David Withers + */ +class ConditionContextualView extends ContextualView { + private static final long serialVersionUID = -894521200616176439L; + + private final BlockingControlLink condition; + private JPanel contitionView; + + public ConditionContextualView(BlockingControlLink condition) { + this.condition = condition; + initView(); + } + + @Override + public JComponent getMainFrame() { + refreshView(); + return contitionView; + } + + @Override + public String getViewTitle() { + return "Control link: " + condition.getBlock().getName() + + " runs after " + condition.getUntilFinished().getName(); + } + + @Override + public void refreshView() { + contitionView = new JPanel(new FlowLayout(FlowLayout.LEFT)); + contitionView.setBorder(new EmptyBorder(5, 5, 5, 5)); + JLabel label = new JLabel( + "<html><body><i>No details available.</i></body><html>"); + contitionView.add(label); + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualViewFactory.java new file mode 100644 index 0000000..ea69f1a --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/condition/ConditionContextualViewFactory.java
@@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.condition; + +import static java.util.Arrays.asList; + +import java.util.List; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workflowmodel.Condition; +import uk.org.taverna.scufl2.api.core.BlockingControlLink; + +/** + * A factory of contextual views for dataflow's condition links. + * + * @author David Withers + * + */ +public class ConditionContextualViewFactory implements + ContextualViewFactory<BlockingControlLink> { + @Override + public boolean canHandle(Object object) { + return object instanceof Condition; + } + + @Override + public List<ContextualView> getViews(BlockingControlLink condition) { + return asList(new ContextualView[] { new ConditionContextualView( + condition) }); + } + +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualView.java new file mode 100644 index 0000000..4a63868 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualView.java
@@ -0,0 +1,108 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow; + +import static net.sf.taverna.t2.lang.ui.HtmlUtils.buildTableOpeningTag; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.createEditorPane; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.getHtmlHead; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.panelForHtml; + +import javax.swing.JComponent; +import javax.swing.JEditorPane; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workflowmodel.Dataflow; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * @author alanrw + */ +@SuppressWarnings("serial") +class DataflowContextualView extends ContextualView { + private static int MAX_LENGTH = 50; + private static final String ELLIPSIS = "..."; + + private Workflow dataflow; + private JEditorPane editorPane; + private final FileManager fileManager; + private final ColourManager colourManager; + + public DataflowContextualView(Workflow dataflow, FileManager fileManager, + ColourManager colourManager) { + this.dataflow = dataflow; + this.fileManager = fileManager; + this.colourManager = colourManager; + initView(); + } + + @Override + public JComponent getMainFrame() { + editorPane = createEditorPane(buildHtml()); + return panelForHtml(editorPane); + } + + private String buildHtml() { + StringBuilder html = new StringBuilder(getHtmlHead(getBackgroundColour())); + html.append(buildTableOpeningTag()); + + html.append("<tr><td colspan=\"2\" align=\"center\"><b>Source</b></td></tr>"); + String source = "Newly created"; + if (fileManager.getDataflowSource(dataflow.getParent()) != null) + source = fileManager.getDataflowName(dataflow.getParent()); + + html.append("<tr><td colspan=\"2\" align=\"center\">").append(source) + .append("</td></tr>"); + if (!dataflow.getInputPorts().isEmpty()) { + html.append("<tr><th>Input Port Name</th><th>Depth</th></tr>"); + for (InputWorkflowPort dip : dataflow.getInputPorts()) + html.append("<tr><td>") + .append(dip.getName()) + .append("</td><td>") + .append(dip.getDepth() < 0 ? "invalid/unpredicted" + : dip.getDepth()).append("</td></tr>"); + } + if (!dataflow.getOutputPorts().isEmpty()) { + html.append("<tr><th>Output Port Name</th><th>Depth</th></tr>"); + for (OutputWorkflowPort dop : dataflow.getOutputPorts()) + html.append("<tr><td>") + .append(dop.getName()) + .append("</td><td>") + .append(/*(dop.getDepth() < 0 ?*/ "invalid/unpredicted" /*: dop.getDepth())*/) + .append("</td>" + "</tr>"); + } + + return html.append("</table>").append("</body></html>").toString(); + } + + public String getBackgroundColour() { + return colourManager.getDefaultPropertyMap().get( + Dataflow.class.toString()); + } + + @Override + public int getPreferredPosition() { + return 100; + } + + private String limitName(String fullName) { + if (fullName.length() <= MAX_LENGTH) + return fullName; + return fullName.substring(0, MAX_LENGTH - ELLIPSIS.length()) + ELLIPSIS; + } + + @Override + public String getViewTitle() { + return "Workflow " + limitName(dataflow.getName()); + } + + @Override + public void refreshView() { + editorPane.setText(buildHtml()); + repaint(); + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualViewFactory.java new file mode 100644 index 0000000..0d7f3c0 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflow/DataflowContextualViewFactory.java
@@ -0,0 +1,41 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * @author alanrw + */ +public class DataflowContextualViewFactory implements + ContextualViewFactory<Workflow> { + private FileManager fileManager; + private ColourManager colourManager; + + @Override + public boolean canHandle(Object selection) { + return selection instanceof Workflow; + } + + @Override + public List<ContextualView> getViews(Workflow selection) { + return Arrays.asList(new ContextualView[] { + new DataflowContextualView(selection, fileManager, colourManager)}); + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualView.java new file mode 100644 index 0000000..3f17a65 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualView.java
@@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowinputport; + +import static java.awt.FlowLayout.LEFT; + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; + +/** + * Contextual view for dataflow's input ports. + * + * @author Alex Nenadic + */ +class DataflowInputPortContextualView extends ContextualView{ + private static final long serialVersionUID = -8746856072335775933L; + + private InputWorkflowPort dataflowInputPort; + private JPanel dataflowInputPortView; + @SuppressWarnings("unused") + private FileManager fileManager; + + public DataflowInputPortContextualView(InputWorkflowPort inputport, + FileManager fileManager) { + this.dataflowInputPort = inputport; + this.fileManager = fileManager; + initView(); + } + + @Override + public JComponent getMainFrame() { + refreshView(); + return dataflowInputPortView; + } + + @Override + public String getViewTitle() { + return "Workflow input port: " + dataflowInputPort.getName(); + } + + @Override + public void refreshView() { + dataflowInputPortView = new JPanel(new FlowLayout(LEFT)); + dataflowInputPortView.setBorder(new EmptyBorder(5, 5, 5, 5)); + JLabel label = new JLabel(getTextFromDepth("port", + dataflowInputPort.getDepth())); + dataflowInputPortView.add(label); + } + + @SuppressWarnings("serial") + @Override + public Action getConfigureAction(Frame owner) { + return new AbstractAction("Update prediction") { + @Override + public void actionPerformed(ActionEvent e) { + // fileManager.getCurrentDataflow().checkValidity(); + refreshView(); + } + }; + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualViewFactory.java new file mode 100644 index 0000000..5dc5434 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowinputport/DataflowInputPortContextualViewFactory.java
@@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowinputport; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; + +/** + * A factory of contextual views for dataflow's input ports. + * + * @author Alex Nenadic + */ +public class DataflowInputPortContextualViewFactory implements + ContextualViewFactory<InputWorkflowPort> { + private FileManager fileManager; + + @Override + public boolean canHandle(Object object) { + return object instanceof InputWorkflowPort; + } + + @Override + public List<ContextualView> getViews(InputWorkflowPort inputport) { + return Arrays.asList(new ContextualView[] { + new DataflowInputPortContextualView(inputport, fileManager)}); + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualView.java new file mode 100644 index 0000000..9ba55fe --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualView.java
@@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowoutputport; + +import static java.awt.FlowLayout.LEFT; + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; + +/** + * Contextual view for dataflow's output ports. + * + * @author Alex Nenadic + */ +public class DataflowOutputPortContextualView extends ContextualView { + private static final long serialVersionUID = 5496014085110553051L; + + private OutputWorkflowPort dataflowOutputPort; + private JPanel dataflowOutputPortView; + @SuppressWarnings("unused") + private FileManager fileManager; + + public DataflowOutputPortContextualView(OutputWorkflowPort outputport, + FileManager fileManager) { + this.dataflowOutputPort = outputport; + this.fileManager = fileManager; + initView(); + } + + @Override + public JComponent getMainFrame() { + refreshView(); + return dataflowOutputPortView; + } + + @Override + public String getViewTitle() { + return "Workflow output port: " + dataflowOutputPort.getName(); + } + + @Override + public void refreshView() { + dataflowOutputPortView = new JPanel(new FlowLayout(LEFT)); + dataflowOutputPortView.setBorder(new EmptyBorder(5,5,5,5)); + JLabel label = new JLabel(getTextForLabel()); + dataflowOutputPortView.add(label); + } + + private String getTextForLabel() { + //FIXME + //return getTextFromDepth("port", dataflowOutputPort.getDepth()); + return "Fix depth for OutputWorkflowPort"; + } + + private void updatePrediction() { + //FIXME + // fileManager.getCurrentDataflow().checkValidity(); + } + + @Override + @SuppressWarnings("serial") + public Action getConfigureAction(Frame owner) { + return new AbstractAction("Update prediction") { + @Override + public void actionPerformed(ActionEvent e) { + updatePrediction(); + refreshView(); + } + }; + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualViewFactory.java new file mode 100644 index 0000000..20ac960 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/dataflowoutputport/DataflowOutputPortContextualViewFactory.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowoutputport; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workflowmodel.DataflowOutputPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * A factory of contextual views for dataflow's output ports. + * + * @author Alex Nenadic + */ +public class DataflowOutputPortContextualViewFactory implements + ContextualViewFactory<OutputWorkflowPort> { + private FileManager fileManager; + + @Override + public boolean canHandle(Object object) { + return object instanceof DataflowOutputPort; + } + + @Override + public List<ContextualView> getViews(OutputWorkflowPort outputport) { + return Arrays.asList(new ContextualView[] { + new DataflowOutputPortContextualView(outputport, fileManager)}); + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualView.java new file mode 100644 index 0000000..daa3414 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualView.java
@@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.datalink; + +import static java.awt.FlowLayout.LEFT; + +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import uk.org.taverna.scufl2.api.core.DataLink; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; + +/** + * Contextual view for dataflow's datalinks. + * + * @author Alex Nenadic + * @author Alan R Williams + */ +class DatalinkContextualView extends ContextualView { + private static final long serialVersionUID = -5031256519235454876L; + + private DataLink datalink; + private JPanel datalinkView; + @SuppressWarnings("unused") + private final FileManager fileManager; + + public DatalinkContextualView(DataLink datalink, FileManager fileManager) { + this.datalink = datalink; + this.fileManager = fileManager; + initView(); + } + + @Override + public JComponent getMainFrame() { + refreshView(); + return datalinkView; + } + + @Override + public String getViewTitle() { + return "Data link: " + datalink.getReceivesFrom().getName() + " -> " + datalink.getSendsTo().getName(); + } + + @Override + public void refreshView() { + datalinkView = new JPanel(new FlowLayout(LEFT)); + datalinkView.setBorder(new EmptyBorder(5,5,5,5)); + JLabel label = new JLabel (getTextForLabel()); + datalinkView.add(label); + } + + private String getTextForLabel() { + //FIXME + // return getTextFromDepth("link", datalink.getResolvedDepth()); + return "Fix DataLink resolved depth"; + } + + private void updatePrediction() { + //FIXME + // fileManager.getCurrentDataflow().checkValidity(); + } + + @Override + @SuppressWarnings("serial") + public Action getConfigureAction(Frame owner) { + return new AbstractAction("Update prediction") { + @Override + public void actionPerformed(ActionEvent e) { + updatePrediction(); + refreshView(); + } + }; + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualViewFactory.java new file mode 100644 index 0000000..fa8bf96 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/datalink/DatalinkContextualViewFactory.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.datalink; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workflowmodel.Datalink; +import uk.org.taverna.scufl2.api.core.DataLink; + +/** + * A factory of contextual views for dataflow's datalinks. + * + * @author Alex Nenadic + */ +public class DatalinkContextualViewFactory implements + ContextualViewFactory<DataLink> { + private FileManager fileManager; + + @Override + public boolean canHandle(Object object) { + return object instanceof Datalink; + } + + @Override + public List<ContextualView> getViews(DataLink datalink) { + return Arrays.asList(new ContextualView[] { + new DatalinkContextualView(datalink, fileManager)}); + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponent.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponent.java new file mode 100644 index 0000000..11306d0 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponent.java
@@ -0,0 +1,389 @@ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.impl; + +import static java.awt.GridBagConstraints.BOTH; +import static java.awt.GridBagConstraints.CENTER; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.LINE_START; +import static java.awt.GridBagConstraints.NONE; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.BLUE; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.ORANGE; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.minusIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.plusIcon; + +import java.awt.Color; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.Timer; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.lang.ui.ShadedLabel; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; +import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent; +import net.sf.taverna.t2.workbench.ui.Utils; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +@SuppressWarnings("serial") +public class ContextualViewComponent extends JScrollPane implements UIComponentSPI { + /** delay before contextual view is redrawn */ + private static final int DELAY = 250; + private static final Color[] colors = new Color[] { BLUE, GREEN, ORANGE }; + // HACK ALERT! + public static boolean selfGenerated = false; + + private Observer<DataflowSelectionMessage> dataflowSelectionListener = new DataflowSelectionListener(); + private SelectionManager selectionManager; + private ContextualViewFactoryRegistry contextualViewFactoryRegistry; + GridBagConstraints gbc; + protected Map<JPanel, SectionLabel> panelToLabelMap = new HashMap<>(); + private String lastOpenedSectionName = ""; + private JPanel mainPanel; + private List<JPanel> shownComponents = null; + int colorIndex = 0; + private Timer updateSelectionTimer = null; + private Object lastSelectedObject = null; + + private static final Comparator<ContextualView> viewComparator = new Comparator<ContextualView>() { + @Override + public int compare(ContextualView o1, ContextualView o2) { + return o1.getPreferredPosition() - o2.getPreferredPosition(); + } + }; + + public ContextualViewComponent(EditManager editManager, + SelectionManager selectionManager, + ContextualViewFactoryRegistry contextualViewFactoryRegistry) { + this.selectionManager = selectionManager; + this.contextualViewFactoryRegistry = contextualViewFactoryRegistry; + updateSelectionTimer = new Timer(DELAY, updateSelectionListener); + updateSelectionTimer.setRepeats(false); + + initialise(); + + editManager.addObserver(new EditManagerObserver()); + selectionManager.addObserver(new SelectionManagerObserver()); + } + + @Override + public ImageIcon getIcon() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getName() { + return "Details"; + } + + private void initialise() { + mainPanel = new JPanel(new GridBagLayout()); + this.setViewportView(mainPanel); + } + + @Override + public void onDisplay() { + } + + @Override + public void onDispose() { + updateSelectionTimer.stop(); + } + + @SuppressWarnings("unchecked") + private void updateContextualView(List<ContextualViewFactory<?>> viewFactories, + Object selection) { + if (selection == lastSelectedObject) + return; + lastSelectedObject = selection; + mainPanel = new JPanel(new GridBagLayout()); + panelToLabelMap.clear(); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.weightx = 0.1; + gbc.fill = HORIZONTAL; + + gbc.gridy = 0; + shownComponents = new ArrayList<>(); + List<ContextualView> views = new ArrayList<>(); + for (ContextualViewFactory<?> cvf : viewFactories) + views.addAll(((ContextualViewFactory<Object>) cvf) + .getViews(selection)); + Collections.sort(views, viewComparator); + colorIndex = 0; + if (views.isEmpty()) + mainPanel.add(new JLabel("No details available")); + else + populateContextualView(viewFactories, gbc, views); + gbc.weighty = 0.1; + gbc.fill = BOTH; + mainPanel.add(new JPanel(), gbc); + // mainPanel.revalidate(); + // mainPanel.repaint(); + this.setViewportView(mainPanel); + // this.revalidate(); + // this.repaint(); + } + + private void populateContextualView( + List<ContextualViewFactory<?>> viewFactories, + GridBagConstraints gbc, List<ContextualView> views) { + JPanel firstPanel = null; + JPanel lastOpenedSection = null; + for (ContextualView view : views) { + SectionLabel label = new SectionLabel(view.getViewTitle(), nextColor()); + mainPanel.add(label, gbc); + gbc.gridy++; + JPanel subPanel = new JPanel(); + if (view.getViewTitle().equals(lastOpenedSectionName)) + lastOpenedSection = subPanel; + subPanel.setLayout(new GridBagLayout()); + + GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0.1; + constraints.weighty = 0; + constraints.anchor = CENTER; + constraints.fill = HORIZONTAL; + + subPanel.add(view, constraints); + Frame frame = Utils.getParentFrame(this); + Action configureAction = view.getConfigureAction(frame); + if (configureAction != null) { + JButton configButton = new JButton(configureAction); + if (configButton.getText() == null + || configButton.getText().isEmpty()) + configButton.setText("Configure"); + constraints.gridy++; + constraints.fill = NONE; + constraints.anchor = LINE_START; + subPanel.add(configButton, constraints); + } + if (firstPanel == null) + firstPanel = subPanel; + mainPanel.add(subPanel, gbc); + shownComponents.add(subPanel); + gbc.gridy++; + if (viewFactories.size() != 1) + makeCloseable(subPanel, label); + else { + lastOpenedSectionName = label.getText(); + lastOpenedSection = subPanel; + panelToLabelMap.put(subPanel, label); + subPanel.setVisible(false); + } + } + if (lastOpenedSection != null) + openSection(lastOpenedSection); + else if (firstPanel != null) + openSection(firstPanel); + } + + private void clearContextualView() { + lastSelectedObject = null; + mainPanel = new JPanel(new GridBagLayout()); + mainPanel.add(new JLabel("No details available")); + this.setViewportView(mainPanel); + this.revalidate(); + } + + public void updateSelection(Object selectedItem) { + findContextualView(selectedItem); + } + + private Runnable updateSelectionRunnable = new Runnable() { + @Override + public void run() { + Object selection = getSelection(); + if (selection == null) + clearContextualView(); + else + updateSelection(selection); + } + }; + + private ActionListener updateSelectionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(updateSelectionRunnable); + } + }; + + public void updateSelection() { + updateSelectionTimer.restart(); + } + + private Object getSelection() { + WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle(); + + /* + * If there is no currently opened dataflow, clear the contextual view + * panel + */ + if (workflowBundle == null) { + return null; + } + DataflowSelectionModel selectionModel = selectionManager + .getDataflowSelectionModel(workflowBundle); + Set<Object> selection = selectionModel.getSelection(); + + /* + * If the dataflow is opened but no component of the dataflow is + * selected, clear the contextual view panel + */ + if (selection.isEmpty()) + return null; + return selection.iterator().next(); + } + + private void findContextualView(Object selection) { + List<ContextualViewFactory<?>> viewFactoriesForBeanType = contextualViewFactoryRegistry + .getViewFactoriesForObject(selection); + updateContextualView(viewFactoriesForBeanType, selection); + } + + private final class SelectionManagerObserver extends SwingAwareObserver<SelectionManagerEvent> { + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, SelectionManagerEvent message) { + if (message instanceof WorkflowBundleSelectionEvent) + bundleSelected((WorkflowBundleSelectionEvent) message); + } + + private void bundleSelected(WorkflowBundleSelectionEvent event) { + WorkflowBundle oldBundle = event + .getPreviouslySelectedWorkflowBundle(); + WorkflowBundle newBundle = event.getSelectedWorkflowBundle(); + + if (oldBundle != null) + selectionManager.getDataflowSelectionModel(oldBundle) + .removeObserver(dataflowSelectionListener); + if (newBundle != null) + selectionManager.getDataflowSelectionModel(newBundle) + .addObserver(dataflowSelectionListener); + lastSelectedObject = null; + updateSelection(); + } + } + + private final class DataflowSelectionListener extends SwingAwareObserver<DataflowSelectionMessage> { + @Override + public void notifySwing(Observable<DataflowSelectionMessage> sender, + DataflowSelectionMessage message) { + updateSelection(); + } + } + + private final class EditManagerObserver extends SwingAwareObserver<EditManagerEvent> { + @Override + public void notifySwing(Observable<EditManagerEvent> sender, EditManagerEvent message) { + Object selection = getSelection(); + if ((selection != lastSelectedObject) && !selfGenerated) { + lastSelectedObject = null; + refreshView(); + } + } + } + + public void refreshView() { + if (mainPanel != null) + updateSelection(); + } + + private final class SectionLabel extends ShadedLabel { + private JLabel expand; + + private SectionLabel(String text, Color colour) { + super(text, colour); + expand = new JLabel(minusIcon); + add(expand, 0); + setExpanded(true); + } + + public void setExpanded(boolean expanded) { + if (expanded) + expand.setIcon(minusIcon); + else + expand.setIcon(plusIcon); + } + } + + private void makeCloseable(JPanel panel, SectionLabel label) { + panel.setVisible(false); + if (panelToLabelMap.get(panel) != label) { + panelToLabelMap.put(panel, label); + // Only add mouse listener once + label.addMouseListener(new SectionOpener(panel)); + } + } + + protected class SectionOpener extends MouseAdapter { + private final JPanel sectionToOpen; + + public SectionOpener(JPanel sectionToOpen) { + this.sectionToOpen = sectionToOpen; + } + + @Override + public void mouseClicked(MouseEvent e) { + openSection(sectionToOpen); + } + } + + public synchronized void openSection(JPanel sectionToOpen) { + lastOpenedSectionName = ""; + for (Entry<JPanel, SectionLabel> entry : panelToLabelMap.entrySet()) { + JPanel section = entry.getKey(); + SectionLabel sectionLabel = entry.getValue(); + + if (section != sectionToOpen) + section.setVisible(false); + else { + section.setVisible(!section.isVisible()); + if (section.isVisible()) + lastOpenedSectionName = sectionLabel.getText(); + } + sectionLabel.setExpanded(section.isVisible()); + } + this.revalidate(); + this.repaint(); + } + + private Color nextColor() { + if (colorIndex >= colors.length) + colorIndex = 0; + return colors[colorIndex++]; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponentFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponentFactory.java new file mode 100644 index 0000000..db43a0d --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/impl/ContextualViewComponentFactory.java
@@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (C) 2007-2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.impl; + +import javax.swing.ImageIcon; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; + +public class ContextualViewComponentFactory implements UIComponentFactorySPI { + private EditManager editManager; + private SelectionManager selectionManager; + private ContextualViewFactoryRegistry contextualViewFactoryRegistry; + + @Override + public UIComponentSPI getComponent() { + return new ContextualViewComponent(editManager, selectionManager, + contextualViewFactoryRegistry); + } + + @Override + public ImageIcon getIcon() { + return null; + } + + @Override + public String getName() { + return "Details"; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setContextualViewFactoryRegistry( + ContextualViewFactoryRegistry contextualViewFactoryRegistry) { + this.contextualViewFactoryRegistry = contextualViewFactoryRegistry; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualView.java new file mode 100644 index 0000000..c1b3d06 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualView.java
@@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.inputport; + +import static java.awt.FlowLayout.LEFT; + +import java.awt.FlowLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; + +/** + * Contextual view for dataflow procerssor's input ports. + * + * @author Alex Nenadic + */ +class InputPortContextualView extends ContextualView { + private static final String NO_DETAILS_AVAILABLE_HTML = "<html><body>" + + "<i>No details available.</i>" + "</body><html>"; + private static final long serialVersionUID = -7743029534480678624L; + + private InputActivityPort inputPort; + private JPanel inputPortView; + + public InputPortContextualView(InputActivityPort inputport) { + this.inputPort = inputport; + initView(); + } + + @Override + public JComponent getMainFrame() { + refreshView(); + return inputPortView; + } + + @Override + public String getViewTitle() { + return "Service input port: " + inputPort.getName(); + } + + @Override + public void refreshView() { + inputPortView = new JPanel(new FlowLayout(LEFT)); + inputPortView.setBorder(new EmptyBorder(5, 5, 5, 5)); + JLabel label = new JLabel(NO_DETAILS_AVAILABLE_HTML); + inputPortView.add(label); + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualViewFactory.java new file mode 100644 index 0000000..490e5b7 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/inputport/InputPortContextualViewFactory.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.inputport; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.port.InputActivityPort; + +/** + * A factory of contextual views for dataflow proessor's (i.e. its associated + * activity's) input ports. + * + * @author Alex Nenadic + */ +public class InputPortContextualViewFactory implements + ContextualViewFactory<InputActivityPort> { + @Override + public boolean canHandle(Object object) { + return object instanceof InputActivityPort; + } + + @Override + public List<ContextualView> getViews(InputActivityPort inputport) { + return Arrays.asList(new ContextualView[] { + new InputPortContextualView(inputport)}); + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationAction.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationAction.java new file mode 100644 index 0000000..567cc4b --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationAction.java
@@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.merge; + +import java.awt.event.ActionEvent; +import java.util.List; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.ReorderMergePositionsEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.DataLink; + +/** + * Configuration action for a Merge. This action changes the order of + * merge's incoming ports. + * + * @author Alex Nenadic + * + */ +@SuppressWarnings("serial") +class MergeConfigurationAction extends AbstractAction { + private static Logger logger = Logger + .getLogger(MergeConfigurationAction.class); + + private final List<DataLink> reorderedDataLinksList; + private final List<DataLink> datalinks; + private final EditManager editManager; + private final SelectionManager selectionManager; + + MergeConfigurationAction(List<DataLink> datalinks, + List<DataLink> reorderedDataLinksList, EditManager editManager, + SelectionManager selectionManager) { + this.datalinks = datalinks; + this.reorderedDataLinksList = reorderedDataLinksList; + this.editManager = editManager; + this.selectionManager = selectionManager; + } + + @Override + public void actionPerformed(ActionEvent e) { + ReorderMergePositionsEdit edit = new ReorderMergePositionsEdit( + datalinks, reorderedDataLinksList); + + WorkflowBundle bundle = selectionManager.getSelectedWorkflowBundle(); + + try { + editManager.doDataflowEdit(bundle, edit); + } catch (IllegalStateException ex1) { + logger.error("Could not configure merge", ex1); + } catch (EditException ex2) { + logger.error("Could not configure merge", ex2); + } + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationView.java new file mode 100644 index 0000000..66eeb3e --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeConfigurationView.java
@@ -0,0 +1,233 @@ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.merge; + +import static java.awt.BorderLayout.EAST; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.lang.Math.max; +import static javax.swing.BoxLayout.Y_AXIS; +import static javax.swing.ListSelectionModel.SINGLE_SELECTION; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS; +import static javax.swing.SwingConstants.CENTER; +import static javax.swing.SwingConstants.LEFT; +import static javax.swing.SwingConstants.RIGHT; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.downArrowIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.upArrowIcon; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.FontMetrics; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.BoxLayout; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.DataLink; + +@SuppressWarnings("serial") +public class MergeConfigurationView extends HelpEnabledDialog { + private static final String TITLE = "<html><body><b>Order of incoming links</b></body></html>"; + + private List<DataLink> dataLinks; + private List<DataLink> reorderedDataLinks; + /** Ordered list of labels for dataLinks to be displayed to the user */ + private DefaultListModel<String> labelListModel; + /** JList that displays the labelListModel */ + JList<String> list; + /** Button to push the dataLink up the list */ + private JButton upButton; + /** Button to push the dataLink down the list */ + private JButton downButton; + private final EditManager editManager; + private final SelectionManager selectionManager; + + public MergeConfigurationView(List<DataLink> dataLinks, EditManager editManager, + SelectionManager selectionManager) { + super((Frame)null, "Merge Configuration", true); + + this.dataLinks = new ArrayList<>(dataLinks); + reorderedDataLinks = new ArrayList<>(dataLinks); + this.editManager = editManager; + this.selectionManager = selectionManager; + labelListModel = new DefaultListModel<>(); + for (DataLink dataLink : dataLinks) + labelListModel.addElement(dataLink.toString()); + + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JPanel listPanel = new JPanel(); + listPanel.setLayout(new BorderLayout()); + listPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10), + new EtchedBorder())); + + JLabel title = new JLabel(TITLE); + title.setBorder(new EmptyBorder(5, 5, 5, 5)); + listPanel.add(title, NORTH); + + list = new JList<>(labelListModel); + list.setSelectionMode(SINGLE_SELECTION); + list.setVisibleRowCount(-1); + list.addListSelectionListener(new ListSelectionListener() { + /** + * Enable and disable up and down buttons based on which item in the + * list is selected + */ + @Override + public void valueChanged(ListSelectionEvent e) { + int index = list.getSelectedIndex(); + if ((index == -1) || (index == 0 && labelListModel.size() == 0)) { + // nothing selected or only one item in the list + upButton.setEnabled(false); + downButton.setEnabled(false); + } else { + upButton.setEnabled(index > 0); + downButton.setEnabled(index < labelListModel.size() - 1); + } + } + }); + + final JScrollPane listScroller = new JScrollPane(list); + listScroller.setBorder(new EmptyBorder(5, 5, 5, 5)); + listScroller.setBackground(listPanel.getBackground()); + listScroller.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_ALWAYS); + listScroller.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS); + // Set the size of scroll pane to make all list items visible + FontMetrics fm = listScroller.getFontMetrics(this.getFont()); + int listScrollerHeight = fm.getHeight() * labelListModel.size() + 75; //+75 just in case + listScroller.setPreferredSize(new Dimension(listScroller + .getPreferredSize().width, max(listScrollerHeight, + listScroller.getPreferredSize().height))); + listPanel.add(listScroller, BorderLayout.CENTER); + + JPanel upDownButtonPanel = new JPanel(); + upDownButtonPanel.setLayout(new BoxLayout(upDownButtonPanel, Y_AXIS)); + upDownButtonPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + + upButton = new JButton(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int index = list.getSelectedIndex(); + if (index != -1) { + // Swap the labels + String label = (String) labelListModel.elementAt(index); + labelListModel.set(index, labelListModel.get(index - 1)); + labelListModel.set(index - 1, label); + // Swap the dataLinks + DataLink dataLink = reorderedDataLinks.get(index); + reorderedDataLinks.set(index, + reorderedDataLinks.get(index - 1)); + reorderedDataLinks.set(index - 1, dataLink); + // Make the pushed item selected + list.setSelectedIndex(index - 1); + // Refresh the list + listScroller.repaint(); + listScroller.revalidate(); + } + } + }); + upButton.setIcon(upArrowIcon); + upButton.setText("Up"); + // Place text to the right of icon, vertically centered + upButton.setVerticalTextPosition(CENTER); + upButton.setHorizontalTextPosition(RIGHT); + // Set the horizontal alignment of the icon and text + upButton.setHorizontalAlignment(LEFT); + upButton.setEnabled(false); + upDownButtonPanel.add(upButton); + + downButton = new JButton(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + int index = list.getSelectedIndex(); + if (index != -1) { + // Swap the labels + String label = (String) labelListModel.elementAt(index); + labelListModel.set(index, labelListModel.get(index + 1)); + labelListModel.set(index + 1, label); + // Swap the dataLinks + DataLink dataLink = reorderedDataLinks.get(index); + reorderedDataLinks.set(index, + reorderedDataLinks.get(index + 1)); + reorderedDataLinks.set(index + 1, dataLink); + // Make the pushed item selected + list.setSelectedIndex(index + 1); + // Refresh the list + list.repaint(); + listScroller.revalidate(); + } + } + }); + downButton.setIcon(downArrowIcon); + downButton.setText("Down"); + // Place text to the right of icon, vertically centered + downButton.setVerticalTextPosition(CENTER); + downButton.setHorizontalTextPosition(RIGHT); + // Set the horizontal alignment of the icon and text + downButton.setHorizontalAlignment(LEFT); + downButton.setEnabled(false); + // set the up button to be of the same size as down button + upButton.setPreferredSize(downButton.getPreferredSize()); + upButton.setMaximumSize(downButton.getPreferredSize()); + upButton.setMinimumSize(downButton.getPreferredSize()); + upDownButtonPanel.add(downButton); + + listPanel.add(upDownButtonPanel, EAST); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + JButton jbOK = new JButton("OK"); + jbOK.addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + new MergeConfigurationAction(dataLinks, reorderedDataLinks, + editManager, selectionManager).actionPerformed(e); + closeDialog(); + } + }); + + JButton jbCancel = new JButton("Cancel"); + jbCancel.addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + closeDialog(); + } + }); + + buttonPanel.add(jbOK); + buttonPanel.add(jbCancel); + + getContentPane().add(listPanel, BorderLayout.CENTER); + getContentPane().add(buttonPanel, SOUTH); + pack(); + } + + /** + * Close the dialog. + */ + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualView.java new file mode 100644 index 0000000..deb09fb --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualView.java
@@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.merge; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.FlowLayout.LEFT; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.buildTableOpeningTag; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.createEditorPane; +import static net.sf.taverna.t2.lang.ui.HtmlUtils.getHtmlHead; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JEditorPane; +import javax.swing.JPanel; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workflowmodel.Merge; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.DataLink; + +/** + * Contextual view for a {@link Merge}. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +class MergeContextualView extends ContextualView { + @SuppressWarnings("unused") + private DataLink dataLink; + private List<DataLink> datalinks; + @SuppressWarnings("unused") + private WorkflowBundle workflow; + private JEditorPane editorPane; + private final EditManager editManager; + private final ColourManager colourManager; + private final SelectionManager selectionManager; + + // TODO inject from Spring via factory? + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public MergeContextualView(DataLink dataLink, EditManager editManager, + SelectionManager selectionManager, ColourManager colourManager) { + this.dataLink = dataLink; + this.selectionManager = selectionManager; + datalinks = scufl2Tools.datalinksTo(dataLink.getSendsTo()); + this.editManager = editManager; + this.colourManager = colourManager; + workflow = selectionManager.getSelectedWorkflowBundle(); + initView(); + } + + @Override + public JComponent getMainFrame() { + editorPane = createEditorPane(buildHtml()); + return panelForHtml(editorPane); + } + + @Override + public String getViewTitle() { + return "Merge Position"; + } + + /** + * Update the view with the latest information from the configuration bean. + */ + @Override + public void refreshView() { + editorPane.setText(buildHtml()); + repaint(); + } + + private String buildHtml() { + StringBuilder html = new StringBuilder( + getHtmlHead(getBackgroundColour())); + html.append(buildTableOpeningTag()) + .append("<tr><td colspan=\"2\"><b>") + .append(getViewTitle()) + .append("</b></td></tr>") + .append("<tr><td colspan=\"2\"><b>Ordered incoming links</b></td></tr>"); + + int counter = 1; + for (DataLink datalink : datalinks) + html.append("<tr><td>").append(counter++).append(".</td><td>") + .append(datalink).append("</td></tr>"); + + return html.append("</table>").append("</body></html>").toString(); + } + + protected JPanel panelForHtml(JEditorPane editorPane) { + final JPanel panel = new JPanel(); + + JPanel buttonPanel = new JPanel(new FlowLayout(LEFT)); + + JButton configureButton = new JButton(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + MergeConfigurationView mergeConfigurationView = new MergeConfigurationView( + datalinks, editManager, selectionManager); + mergeConfigurationView.setLocationRelativeTo(panel); + mergeConfigurationView.setVisible(true); + } + }); + configureButton.setText("Configure"); + buttonPanel.add(configureButton); + + panel.setLayout(new BorderLayout()); + panel.add(editorPane, CENTER); + panel.add(buttonPanel, SOUTH); + return panel; + } + + public String getBackgroundColour() { + return colourManager.getDefaultPropertyMap().get( + "net.sf.taverna.t2.workflowmodel.Merge"); + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualViewFactory.java new file mode 100644 index 0000000..712b183 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/merge/MergeContextualViewFactory.java
@@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.merge; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.core.DataLink; + +/** + * A factory of contextual views for dataflow's merges. + * + * @author Alex Nenadic + */ +public class MergeContextualViewFactory implements ContextualViewFactory<DataLink> { + private EditManager editManager; + private SelectionManager selectionManager; + private ColourManager colourManager; + + @Override + public boolean canHandle(Object object) { + return object instanceof DataLink + && ((DataLink) object).getMergePosition() != null; + } + + @Override + public List<ContextualView> getViews(DataLink merge) { + return Arrays.asList(new ContextualView[] { + new MergeContextualView(merge, editManager, selectionManager, colourManager)}); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualView.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualView.java new file mode 100644 index 0000000..f2c7861 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualView.java
@@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.outputport; + +import static java.awt.FlowLayout.LEFT; + +import java.awt.FlowLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort; + +/** + * Contextual view for dataflow procerssor's output ports. + * + * @author Alex Nenadic + */ +public class OutputPortContextualView extends ContextualView { + private static final String NO_DETAILS_AVAILABLE_HTML = "<html><body>" + + "<i>No details available.</i>" + "</body><html>"; + private static final long serialVersionUID = -7743029534480678624L; + + private ActivityOutputPort outputPort; + private JPanel outputPortView; + + public OutputPortContextualView(ActivityOutputPort outputport) { + this.outputPort = outputport; + initView(); + } + + @Override + public JComponent getMainFrame() { + refreshView(); + return outputPortView; + } + + @Override + public String getViewTitle() { + return "Service output port: " + outputPort.getName(); + } + + @Override + public void refreshView() { + outputPortView = new JPanel(new FlowLayout(LEFT)); + outputPortView.setBorder(new EmptyBorder(5,5,5,5)); + JLabel label = new JLabel(NO_DETAILS_AVAILABLE_HTML); + outputPortView.add(label); + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualViewFactory.java b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualViewFactory.java new file mode 100644 index 0000000..71e2cd4 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/outputport/OutputPortContextualViewFactory.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.outputport; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort; + +/** + * A factory of contextual views for dataflow proessor's (i.e. its associated + * activity's) output ports. + * + * @author Alex Nenadic + */ +public class OutputPortContextualViewFactory implements + ContextualViewFactory<ActivityOutputPort> { + @Override + public boolean canHandle(Object object) { + return object instanceof ActivityOutputPort; + } + + @Override + public List<ContextualView> getViews(ActivityOutputPort outputport) { + return Arrays.asList(new ContextualView[] { + new OutputPortContextualView(outputport)}); + } +}
diff --git a/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..7744cb3 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1,9 @@ +net.sf.taverna.t2.workbench.ui.views.contextualviews.outputport.OutputPortContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.inputport.InputPortContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowoutputport.DataflowOutputPortContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowinputport.DataflowInputPortContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.datalink.DatalinkContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.condition.ConditionContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.merge.MergeContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated.AnnotatedContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow.DataflowContextualViewFactory
diff --git a/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI new file mode 100644 index 0000000..a564691 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualViewComponentFactory \ No newline at end of file
diff --git a/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context-osgi.xml b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context-osgi.xml new file mode 100644 index 0000000..767943d --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context-osgi.xml
@@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="ContextualViewComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" /> + + <service ref="OutputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="InputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="DataflowOutputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="DataflowInputPortContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="DatalinkContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="ConditionContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="MergeContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="AnnotatedContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="DataflowContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <service ref="ContextualViewFactoryRegistry" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + <reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + + <list id="annotationBeans" interface="net.sf.taverna.t2.annotation.AnnotationBeanSPI" /> + <list id="contextualViewFactories" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" cardinality="0..N" /> + +</beans:beans>
diff --git a/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context.xml b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context.xml new file mode 100644 index 0000000..18bbd36 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/resources/META-INF/spring/contextual-views-impl-context.xml
@@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="ContextualViewComponentFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.impl.ContextualViewComponentFactory"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="contextualViewFactoryRegistry" ref="ContextualViewFactoryRegistry"/> + </bean> + + <bean id="OutputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.outputport.OutputPortContextualViewFactory" /> + <bean id="InputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.inputport.InputPortContextualViewFactory" /> + <bean id="DataflowOutputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowoutputport.DataflowOutputPortContextualViewFactory"> + <property name="fileManager" ref="fileManager" /> + </bean> + <bean id="DataflowInputPortContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflowinputport.DataflowInputPortContextualViewFactory"> + <property name="fileManager" ref="fileManager" /> + </bean> + <bean id="DatalinkContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.datalink.DatalinkContextualViewFactory"> + <property name="fileManager" ref="fileManager" /> + </bean> + <bean id="ConditionContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.condition.ConditionContextualViewFactory" /> + <bean id="MergeContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.merge.MergeContextualViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="colourManager" ref="colourManager" /> + </bean> + <bean id="AnnotatedContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated.AnnotatedContextualViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="annotationBeans" ref ="annotationBeans"/> + </bean> + <bean id="DataflowContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.dataflow.DataflowContextualViewFactory"> + <property name="fileManager" ref="fileManager" /> + <property name="colourManager" ref="colourManager" /> + </bean> + + <bean id="ContextualViewFactoryRegistry" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.impl.ContextualViewFactoryRegistryImpl"> + <property name="contextualViewFactories" ref="contextualViewFactories" /> + </bean> + +</beans>
diff --git a/taverna-workbench-contextual-views-impl/src/main/resources/annotatedcontextualview.properties b/taverna-workbench-contextual-views-impl/src/main/resources/annotatedcontextualview.properties new file mode 100644 index 0000000..e3196f6 --- /dev/null +++ b/taverna-workbench-contextual-views-impl/src/main/resources/annotatedcontextualview.properties
@@ -0,0 +1,4 @@ +net.sf.taverna.t2.annotation.annotationbeans.FreeTextDescription: Description +net.sf.taverna.t2.annotation.annotationbeans.Author: Author +net.sf.taverna.t2.annotation.annotationbeans.DescriptiveTitle: Title +net.sf.taverna.t2.annotation.annotationbeans.ExampleValue: Example \ No newline at end of file
diff --git a/taverna-workbench-contextual-views/pom.xml b/taverna-workbench-contextual-views/pom.xml new file mode 100644 index 0000000..2a07c7e --- /dev/null +++ b/taverna-workbench-contextual-views/pom.xml
@@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>contextual-views</artifactId> + <packaging>bundle</packaging> + <name>Contextual views</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualView.java b/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualView.java new file mode 100644 index 0000000..61f6dd6 --- /dev/null +++ b/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualView.java
@@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (C) 2007-2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.processor; + +import static java.awt.GridBagConstraints.CENTER; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.LINE_START; +import static java.awt.GridBagConstraints.NONE; +import static net.sf.taverna.t2.workbench.ui.Utils.getParentFrame; + +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.List; + +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; + +/** + * View of a processor, including it's iteration stack, activities, etc. + * + * @author Stian Soiland-Reyes + * @author Alan R Williams + */ +@SuppressWarnings("serial") +public class ProcessorActivitiesContextualView extends ContextualView { + private static final String ABSTRACT_PROCESSOR_MSG = "<strong>Abstract processor</strong><br>" + + "<i>No services. This will not execute.</i>"; + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + protected JPanel mainPanel = new JPanel(); + protected Processor processor; + private final ContextualViewFactoryRegistry contextualViewFactoryRegistry; + private final SelectionManager selectionManager; + + public ProcessorActivitiesContextualView(Processor processor, + ContextualViewFactoryRegistry contextualViewFactoryRegistry, + SelectionManager selectionManager) { + super(); + this.processor = processor; + this.contextualViewFactoryRegistry = contextualViewFactoryRegistry; + this.selectionManager = selectionManager; + initialise(); + initView(); + } + + @Override + public void refreshView() { + initialise(); + this.revalidate(); + } + + private synchronized void initialise() { + mainPanel.removeAll(); + mainPanel.setLayout(new GridBagLayout()); + + GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridx = 0; + constraints.gridy = 0; + constraints.weightx = 0.1; + constraints.weighty = 0; + + List<ProcessorBinding> processorBindings = scufl2Tools + .processorBindingsForProcessor(processor, + selectionManager.getSelectedProfile()); + if (processorBindings.isEmpty()) { + JLabel noActivitiesLabel = new JLabel("<html>" + + ABSTRACT_PROCESSOR_MSG + "</html>"); + constraints.fill = NONE; + constraints.anchor = LINE_START; + mainPanel.add(noActivitiesLabel, constraints); + } else + for (ProcessorBinding processorBinding : processorBindings) + addViewForBinding(constraints, processorBinding); + mainPanel.revalidate(); + mainPanel.repaint(); + this.revalidate(); + this.repaint(); + } + + private void addViewForBinding(GridBagConstraints constraints, + ProcessorBinding processorBinding) { + Activity activity = processorBinding.getBoundActivity(); + List<ContextualViewFactory<? super Activity>> viewFactoryForBeanType = contextualViewFactoryRegistry + .getViewFactoriesForObject(activity); + if (viewFactoryForBeanType.isEmpty()) + return; + // TODO why a list when we only use the first, twice, and assume non-empty too? + ContextualView view = (ContextualView) viewFactoryForBeanType.get(0) + .getViews(activity).get(0); + + constraints.anchor = CENTER; + constraints.fill = HORIZONTAL; + mainPanel.add(view, constraints); + Frame frame = getParentFrame(this); + Action configureAction = view.getConfigureAction(frame); + if (configureAction != null) { + constraints.gridy++; + constraints.fill = NONE; + constraints.anchor = LINE_START; + JButton configureButton = new JButton(configureAction); + if (configureButton.getText() == null + || configureButton.getText().isEmpty()) + configureButton.setText("Configure"); + mainPanel.add(configureButton, constraints); + } + constraints.gridy++; + } + + @Override + public JComponent getMainFrame() { + return mainPanel; + } + + @Override + public String getViewTitle() { + return "Service"; + } + + @Override + public int getPreferredPosition() { + return 100; + } +}
diff --git a/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualViewFactory.java b/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualViewFactory.java new file mode 100644 index 0000000..8e28d4a --- /dev/null +++ b/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorActivitiesContextualViewFactory.java
@@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2007-2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.processor; + +import java.util.ArrayList; +import java.util.List; + +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; + +/** + * SPI factory for creating a {@link ProcessorContextualView}. + * + * @author Stian Soiland-Reyes + * @author Alan R Williams + */ +public class ProcessorActivitiesContextualViewFactory implements + ContextualViewFactory<Processor> { + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + private ContextualViewFactoryRegistry contextualViewFactoryRegistry; + private SelectionManager selectionManager; + + @Override + public boolean canHandle(Object selection) { + return selection instanceof Processor; + } + + @Override + public List<ContextualView> getViews(Processor selection) { + List<ContextualView> result = new ArrayList<>(); + List<ProcessorBinding> processorBindings = scufl2Tools + .processorBindingsForProcessor(selection, + selectionManager.getSelectedProfile()); + for (ProcessorBinding processorBinding : processorBindings) { + Activity activity = processorBinding.getBoundActivity(); + for (ContextualViewFactory<? super Activity> cvf : contextualViewFactoryRegistry + .getViewFactoriesForObject(activity)) + result.addAll(cvf.getViews(activity)); + } + return result; + } + + public void setContextualViewFactoryRegistry( + ContextualViewFactoryRegistry contextualViewFactoryRegistry) { + this.contextualViewFactoryRegistry = contextualViewFactoryRegistry; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorPredictedBehaviorContextualViewFactory.java b/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorPredictedBehaviorContextualViewFactory.java new file mode 100644 index 0000000..0ef4f79 --- /dev/null +++ b/taverna-workbench-contextual-views/src/main/java/net/sf/taverna/t2/workbench/ui/views/contextualviews/processor/ProcessorPredictedBehaviorContextualViewFactory.java
@@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (C) 2007-2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.views.contextualviews.processor; + +import static java.util.Collections.singletonList; +import static javax.swing.BoxLayout.Y_AXIS; + +import java.util.List; + +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JEditorPane; +import javax.swing.JPanel; + +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.common.NamedSet; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; + +/** + * How to get a panel describing what Taverna predicts the depth of the ports of + * a processor to be. + * + * @author Stian Soiland-Reyes + */ +public class ProcessorPredictedBehaviorContextualViewFactory implements + ContextualViewFactory<Processor> { + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + @Override + public boolean canHandle(Object selection) { + return selection instanceof Processor; + } + + @Override + @SuppressWarnings("serial") + public List<ContextualView> getViews(final Processor selection) { + class ProcessorPredictedBehaviorContextualView extends ContextualView { + protected JPanel mainPanel = new JPanel(); + protected Processor processor; + + public ProcessorPredictedBehaviorContextualView() { + super(); + refreshView(); + initView(); + } + + @Override + public void refreshView() { + initialise(); + this.revalidate(); + } + + private synchronized void initialise() { + mainPanel.removeAll(); + mainPanel.setLayout(new BoxLayout(mainPanel, Y_AXIS)); + + StringBuilder html = new StringBuilder("<html><head>"); + addStyle(html); + html.append("</head><body>"); + + NamedSet<InputProcessorPort> inputs = processor.getInputPorts(); + if (!inputs.isEmpty()) { + html.append("<table border=1><tr><th>Input Port Name</th>") + .append("<th>Size of data</th>").append("</tr>"); + for (InputProcessorPort ip : inputs) { + html.append("<tr><td>").append(ip.getName()) + .append("</td><td>"); + List<DataLink> incomingDataLinks = scufl2Tools + .datalinksTo(ip); + if (incomingDataLinks.isEmpty()) + html.append("No value"); + else { + int depth = getDepth(incomingDataLinks.get(0)); + if (depth == -1) + html.append("Invalid"); + else if (depth == 0) + html.append("Single value"); + else + html.append("List of depth ").append(depth); + } + html.append("</td></tr>"); + } + html.append("</table>"); + } + NamedSet<OutputProcessorPort> outputs = processor + .getOutputPorts(); + if (!outputs.isEmpty()) { + html.append("<table border=1><tr><th>Output Port Name</th>") + .append("<th>Size of data</th>").append("</tr>"); + for (OutputProcessorPort op : outputs) { + html.append("<tr><td>").append(op.getName()) + .append("</td><td>"); + List<DataLink> outgoingDataLinks = scufl2Tools + .datalinksFrom(op); + if (outgoingDataLinks.isEmpty()) + html.append("No value"); + else { + int depth = getDepth(outgoingDataLinks.get(0)); + if (depth == -1) + html.append("Invalid/unpredicted"); + else if (depth == 0) + html.append("Single value"); + else + html.append("List of depth ").append(depth); + } + html.append("</td></tr>"); + } + html.append("</table>"); + } + if (inputs.isEmpty() && outputs.isEmpty()) + html.append("<p>No port behavior predicted</p>"); + html.append("</body></html>"); + JEditorPane editorPane = new JEditorPane("text/html", + html.toString()); + editorPane.setEditable(false); + mainPanel.add(editorPane); + + mainPanel.revalidate(); + mainPanel.repaint(); + this.revalidate(); + this.repaint(); + } + + protected void addStyle(StringBuilder html) { + html.append("<style type='text/css'>") + .append("table {align:center; border:solid black 1px;") + .append("width:100%; height:100%; overflow:auto;}") + .append("</style>"); + } + + @Override + public JComponent getMainFrame() { + return mainPanel; + } + + @Override + public String getViewTitle() { + return "Predicted behavior"; + } + + @Override + public int getPreferredPosition() { + return 300; + } + } + + return singletonList((ContextualView) new ProcessorPredictedBehaviorContextualView()); + } + + private int getDepth(DataLink datalink) { + // TODO calculate actual depth + return -1; + } +}
diff --git a/taverna-workbench-contextual-views/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-workbench-contextual-views/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..3aa7ee0 --- /dev/null +++ b/taverna-workbench-contextual-views/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1,4 @@ +#net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorDispatchStackContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorPredictedBehaviorContextualViewFactory +net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorActivitiesContextualViewFactory \ No newline at end of file
diff --git a/taverna-workbench-contextual-views/src/main/resources/META-INF/spring/contextual-views-context-osgi.xml b/taverna-workbench-contextual-views/src/main/resources/META-INF/spring/contextual-views-context-osgi.xml new file mode 100644 index 0000000..932b541 --- /dev/null +++ b/taverna-workbench-contextual-views/src/main/resources/META-INF/spring/contextual-views-context-osgi.xml
@@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="ProcessorPredictedBehaviorContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + <service ref="ProcessorActivitiesContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <reference id="contextualViewFactoryRegistry" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + +</beans:beans>
diff --git a/taverna-workbench-contextual-views/src/main/resources/META-INF/spring/contextual-views-context.xml b/taverna-workbench-contextual-views/src/main/resources/META-INF/spring/contextual-views-context.xml new file mode 100644 index 0000000..7f53cb8 --- /dev/null +++ b/taverna-workbench-contextual-views/src/main/resources/META-INF/spring/contextual-views-context.xml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="ProcessorPredictedBehaviorContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorPredictedBehaviorContextualViewFactory" /> + <bean id="ProcessorActivitiesContextualViewFactory" class="net.sf.taverna.t2.workbench.ui.views.contextualviews.processor.ProcessorActivitiesContextualViewFactory"> + <property name="contextualViewFactoryRegistry" ref="contextualViewFactoryRegistry" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + +</beans>
diff --git a/taverna-workbench-credential-manager-ui/pom.xml b/taverna-workbench-credential-manager-ui/pom.xml new file mode 100644 index 0000000..601c546 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/pom.xml
@@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>credential-manager-ui</artifactId> + <packaging>bundle</packaging> + <name>Credential Manager UI</name> + <description> + Integrates the Credential Manager into the Workbench + </description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.security</groupId> + <artifactId>credential-manager</artifactId> + <version>2.0.1-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <!-- <dependency> + <groupId>BrowserLauncher2</groupId> + <artifactId>BrowserLauncher2</artifactId> + <version>1.3</version> + </dependency> --> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${commons.io.version}</version> + </dependency> + </dependencies> + <repositories> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository</url> + </repository> + </repositories> +</project>
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CMStrings.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CMStrings.java new file mode 100644 index 0000000..3f6664c --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CMStrings.java
@@ -0,0 +1,7 @@ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +interface CMStrings { + String ALERT_TITLE = "Credential Manager Alert"; + String ERROR_TITLE = "Credential Manager Error"; + String WARN_TITLE = "Credential Manager Warning"; +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ChangeMasterPasswordDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ChangeMasterPasswordDialog.java new file mode 100644 index 0000000..26086bc --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ChangeMasterPasswordDialog.java
@@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Font.PLAIN; +import static javax.swing.BoxLayout.Y_AXIS; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; + +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Dialog used by users to change their master password for the Credential + * Manager. + */ +@SuppressWarnings("serial") +public class ChangeMasterPasswordDialog extends NonBlockedHelpEnabledDialog { + /** Old password entry field */ + private JPasswordField oldPasswordField; + /** New password entry field */ + private JPasswordField newPasswordField; + /** New password confirmation entry field */ + private JPasswordField newPasswordConfirmField; + /** The entered new password */ + private String password = null; + /** Instructions to the users as to what to do in the dialog */ + private String instructions; + private final CredentialManager credentialManager; + + public ChangeMasterPasswordDialog(JFrame parent, String title, + boolean modal, String instructions, + CredentialManager credentialManager) { + super(parent, title, modal, null); + this.instructions = instructions; + this.credentialManager = credentialManager; + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JLabel instructionsLabel = new JLabel(instructions); + instructionsLabel.setFont(new Font(null, PLAIN, 11)); + + JPanel instructionsPanel = new JPanel(); + instructionsPanel.setLayout(new BoxLayout(instructionsPanel, Y_AXIS)); + instructionsPanel.add(instructionsLabel); + instructionsPanel.setBorder(new EmptyBorder(10, 5, 10, 0)); + + JLabel oldPasswordLabel = new JLabel("Old master password"); + oldPasswordLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + + JLabel newPasswordLabel = new JLabel("New master password"); + newPasswordLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + + JLabel newPasswordConfirmLabel = new JLabel( + "Confirm new master password"); + newPasswordConfirmLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + + oldPasswordField = new JPasswordField(15); + newPasswordField = new JPasswordField(15); + newPasswordConfirmField = new JPasswordField(15); + + JPanel jpPassword = new JPanel(new GridLayout(0, 2, 5, 5)); + jpPassword.add(oldPasswordLabel); + jpPassword.add(oldPasswordField); + jpPassword.add(newPasswordLabel); + jpPassword.add(newPasswordField); + jpPassword.add(newPasswordConfirmLabel); + jpPassword.add(newPasswordConfirmField); + + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10), + new EtchedBorder())); + mainPanel.add(instructionsPanel, NORTH); + mainPanel.add(jpPassword, CENTER); + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okPressed(); + } + }); + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + getContentPane().add(mainPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + getRootPane().setDefaultButton(okButton); + pack(); + } + + /** + * Get the password set in the dialog or null if none was set. + */ + public String getPassword() { + return password; + } + + /** + * Check that the user has provided the correct old master password, that + * the user has supplied the new password and confirmed it and that it is + * not empty. If all is OK, stores the new password in the password field. + * + */ + private boolean checkPassword() { + String oldPassword = new String(oldPasswordField.getPassword()); + + if (oldPassword.length() == 0) { + // old password must not be empty + showMessageDialog(this, + "You must provide your current master password", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + + try { + if (!credentialManager.confirmMasterPassword(oldPassword)) { + showMessageDialog(this, + "You have provided an incorrect master password", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + } catch (Exception e) { + showMessageDialog( + this, + "Credential Manager could not verify your current master password", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + + String newPassword = new String(newPasswordField.getPassword()); + String newPasswordConfirm = new String( + newPasswordConfirmField.getPassword()); + + if (!newPassword.equals(newPasswordConfirm)) { + // passwords do not match + showMessageDialog(this, "Passwords do not match", WARN_TITLE, + WARNING_MESSAGE); + return false; + } + + if (newPassword.isEmpty()) { + // passwords match but are empty + showMessageDialog(this, "The new master password cannot be empty", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + + // passwords match and not empty + password = newPassword; + return true; + } + + private void okPressed() { + if (checkPassword()) + closeDialog(); + } + + private void cancelPressed() { + /* + * Set the password to null as it might have changed in the meantime if + * user entered something then cancelled. + */ + password = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateDialog.java new file mode 100644 index 0000000..6558562 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateDialog.java
@@ -0,0 +1,520 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Color.WHITE; +import static java.awt.Font.BOLD; +import static java.awt.Font.PLAIN; +import static java.awt.GridBagConstraints.LINE_START; +import static javax.security.auth.x500.X500Principal.RFC2253; + +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.math.BigInteger; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; + +import net.sf.taverna.t2.lang.ui.DialogTextArea; +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.security.credentialmanager.ParsedDistinguishedName; +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +import org.apache.log4j.Logger; + +/** + * Displays the details of a X.509 certificate and asks user if they want to + * trust it. This is normally invoked by the Taverna's TrustManager when trying + * to confirm the trust in the remote server during SSL handshake. + */ +@SuppressWarnings("serial") +public class ConfirmTrustedCertificateDialog extends NonBlockedHelpEnabledDialog { + private static Logger logger = Logger.getLogger(ConfirmTrustedCertificateDialog.class); + + /** The certificate to display */ + private X509Certificate cert; + /** User's decision as whether to trust this service's certificate or not */ + private boolean shouldTrust; + /** + * Should the decision also be saved in Credential Manager? Actually - it is + * always saved now as it was really hard to implement trusting for one + * connection only - so we can either "trust" or "not" trust but not + * "trust once". + */ + private boolean shouldSave = false; + private final DistinguishedNameParser dnParser; + + public ConfirmTrustedCertificateDialog(Frame parent, String title, + boolean modal, X509Certificate crt, DistinguishedNameParser dnParser) { + super(parent, title, modal); + this.cert = crt; + this.dnParser = dnParser; + initComponents(); + } + + public ConfirmTrustedCertificateDialog(Dialog parent, String title, + boolean modal, X509Certificate crt, DistinguishedNameParser dnParser) + throws CMException { + super(parent, title, modal); + this.cert = crt; + this.dnParser = dnParser; + initComponents(); + } + + private void initComponents(){ + // title panel + JPanel titlePanel = new JPanel(new BorderLayout()); + titlePanel.setBackground(WHITE); + JLabel titleLabel = new JLabel("View service's certificate"); + titleLabel.setFont(titleLabel.getFont().deriveFont(BOLD, 13.5f)); + titleLabel.setBorder(new EmptyBorder(10, 10, 0, 10)); + + DialogTextArea titleMessage = new DialogTextArea(); + titleMessage.setMargin(new Insets(5, 20, 10, 10)); + titleMessage.setFont(titleMessage.getFont().deriveFont(11f)); + titleMessage.setEditable(false); + titleMessage.setFocusable(false); + titlePanel.setBorder( new EmptyBorder(10, 10, 0, 10)); + titlePanel.add(titleLabel, NORTH); + titlePanel.add(titleMessage, CENTER); + + // Certificate details: + + ParsedDistinguishedName subjectDN = dnParser.parseDN(cert + .getSubjectX500Principal().getName(RFC2253)); + ParsedDistinguishedName issuerDN = dnParser.parseDN(cert + .getIssuerX500Principal().getName(RFC2253)); + JPanel certificatePanel = createCertificateDetailsPanel(subjectDN, issuerDN); + titleMessage.setText("The service host " + subjectDN.getCN() + " requires HTTPS connection and has identified itself with the certificate below.\n" + + "Do you want to trust this service? (Refusing to trust means you will not be able to invoke services on this host from a workflow.)"); + + // OK button + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + +// final JButton trustButton = new JButton("Trust once"); +// trustButton.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent evt) { +// trustPressed(); +// } +// }); + + //final JButton trustAlwaysButton = new JButton("Trust always"); + final JButton trustAlwaysButton = new JButton("Trust"); + trustAlwaysButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + trustAlwaysPressed(); + } + }); + + final JButton dontTrustButton = new JButton("Do not trust"); + dontTrustButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + dontTrustPressed(); + } + }); + + //jpButtons.add(trustButton); + buttonsPanel.add(trustAlwaysButton); + buttonsPanel.add(dontTrustButton); + + getContentPane().add(titlePanel, NORTH); + getContentPane().add(certificatePanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + setResizable(false); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + getRootPane().setDefaultButton(trustAlwaysButton); + pack(); + } + + private JPanel createCertificateDetailsPanel(ParsedDistinguishedName subjectDN, ParsedDistinguishedName issuerDN) { + /* + * Grid Bag Constraints templates for labels (column 1) and values + * (column 2) of certificate details + */ + GridBagConstraints gbc_labels = new GridBagConstraints(); + gbc_labels.gridx = 0; + gbc_labels.ipadx = 20; + gbc_labels.gridwidth = 1; + gbc_labels.gridheight = 1; + gbc_labels.insets = new Insets(2, 15, 2, 2); + gbc_labels.anchor = LINE_START; + + GridBagConstraints gbc_values = new GridBagConstraints(); + gbc_values.gridx = 1; + gbc_values.gridwidth = 1; + gbc_values.gridheight = 1; + gbc_values.insets = new Insets(2, 5, 2, 2); + gbc_values.anchor = LINE_START; + + /* + * Netscape Certificate Type non-critical extension (if any) defines the + * intended uses of the certificate - to make it look like Firefox's + * view certificate dialog + * + * From openssl's documentation: "The [above] extension is non standard, + * Netscape specific and largely obsolete. Their use in new applications + * is discouraged." + * + * TODO replace with "basicConstraints, keyUsage and extended key usage + * extensions which are now used instead." + */ +// byte[] intendedUses = cert.getExtensionValue("2.16.840.1.113730.1.1"); // Netscape Certificate Type OID +// JLabel intendedUsesLabel = null; +// JTextField intendedUsesTextField = null; +// JPanel intendedUsesPanel = null; +// GridBagConstraints gbc_intendedUsesLabel = null; +// if (intendedUses != null) { +// intendedUsesLabel = new JLabel( +// "This certificate has been approved for the following uses:"); +// intendedUsesLabel.setFont(new Font(null, Font.BOLD, 11)); +// intendedUsesLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); +// +// intendedUsesTextField = new JTextField(45); +// intendedUsesTextField.setText(CMUtils.getIntendedCertificateUses(intendedUses)); +// intendedUsesTextField.setEditable(false); +// intendedUsesTextField.setFont(new Font(null, Font.PLAIN, 11)); +// +// intendedUsesPanel = new JPanel(new BorderLayout()); +// intendedUsesPanel.add(intendedUsesLabel, BorderLayout.NORTH); +// intendedUsesPanel.add(intendedUsesTextField, BorderLayout.CENTER); +// JSeparator separator = new JSeparator(JSeparator.HORIZONTAL); +// intendedUsesPanel.add(separator, BorderLayout.SOUTH); +// +// gbc_intendedUsesLabel = (GridBagConstraints) gbc_labels.clone(); +// gbc_intendedUsesLabel.gridy = 0; +// gbc_intendedUsesLabel.gridwidth = 2; // takes two columns +// gbc_intendedUsesLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets +// } + + // Issued To + JLabel issuedToLabel = new JLabel("Issued To"); + issuedToLabel.setFont(new Font(null, BOLD, 11)); + GridBagConstraints gbc_issuedTo = (GridBagConstraints) gbc_labels + .clone(); + gbc_issuedTo.gridy = 1; + gbc_issuedTo.gridwidth = 2; // takes two columns + gbc_issuedTo.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets + // Subject's Distinguished Name (DN) + // Extract the CN, O, OU and EMAILADDRESS fields + String subjectCN = subjectDN.getCN(); + String subjectOrg = subjectDN.getO(); + String subjectOU = subjectDN.getOU(); + // String sEMAILADDRESS = CMUtils.getEmilAddress(); + // Subject's Common Name (CN) + JLabel subjectCNLabel = new JLabel("Common Name (CN)"); + subjectCNLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_subjectCNLabel = (GridBagConstraints) gbc_labels.clone(); + gbc_subjectCNLabel.gridy = 2; + JLabel subjectCNValue = new JLabel(subjectCN); + subjectCNValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_subjectCNValue = (GridBagConstraints) gbc_values + .clone(); + gbc_subjectCNValue.gridy = 2; + // Subject's Organisation (O) + JLabel subjectOrgLabel = new JLabel("Organisation (O)"); + subjectOrgLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_subjectOrgLabel = (GridBagConstraints) gbc_labels.clone(); + gbc_subjectOrgLabel.gridy = 3; + JLabel subjectOrgValue = new JLabel(subjectOrg); + subjectOrgValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_subjectOrgValue = (GridBagConstraints) gbc_values + .clone(); + gbc_subjectOrgValue.gridy = 3; + // Subject's Organisation Unit (OU) + JLabel subjectOULabel = new JLabel("Organisation Unit (OU)"); + subjectOULabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_subjectOULabel = (GridBagConstraints) gbc_labels.clone(); + gbc_subjectOULabel.gridy = 4; + JLabel subjectOUValue = new JLabel(subjectOU); + subjectOUValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_subjectOUValue = (GridBagConstraints) gbc_values + .clone(); + gbc_subjectOUValue.gridy = 4; + // E-mail Address + // JLabel jlEmail = new JLabel("E-mail Address"); + // jlEmail.setFont(new Font(null, Font.PLAIN, 11)); + // GridBagConstraints gbc_jlEmail = (GridBagConstraints) + // gbcLabel.clone(); + // gbc_jlEmail.gridy = 5; + // JLabel jlEmailValue = new JLabel(sEMAILADDRESS); + // jlEmailValue.setFont(new Font(null, Font.PLAIN, 11)); + // GridBagConstraints gbc_jlEmailValue = (GridBagConstraints) + // gbcValue.clone(); + // gbc_jlEmailValue.gridy = 5; + // Serial Number + JLabel snLabel = new JLabel("Serial Number"); + snLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_snLabel = (GridBagConstraints) gbc_labels.clone(); + gbc_snLabel.gridy = 6; + JLabel snValue = new JLabel(); + // Get the hexadecimal serial number + StringBuilder strBuff = new StringBuilder(new BigInteger(1, cert + .getSerialNumber().toByteArray()).toString(16).toUpperCase()); + // Place colons at every two hexadecimal characters + if (strBuff.length() > 2) + for (int iCnt = 2; iCnt < strBuff.length(); iCnt += 3) + strBuff.insert(iCnt, ':'); + snValue.setText(strBuff.toString()); + snValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_snValue = (GridBagConstraints) gbc_values + .clone(); + gbc_snValue.gridy = 6; + // Certificate version number + JLabel versionLabel = new JLabel("Version"); + versionLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_versionLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_versionLabel.gridy = 7; + JLabel versionValue = new JLabel(Integer.toString(cert.getVersion())); + versionValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_versionValue = (GridBagConstraints) gbc_values + .clone(); + gbc_versionValue.gridy = 7; + + // Issued By + JLabel issuedByLabel = new JLabel("Issued By"); + issuedByLabel.setFont(new Font(null, BOLD, 11)); + GridBagConstraints gbc_issuedByLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_issuedByLabel.gridy = 8; + gbc_issuedByLabel.gridwidth = 2; // takes two columns + gbc_issuedByLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets + // Issuer's Distinguished Name (DN) + // Extract the CN, O and OU fields for the issuer + String issuerCN = issuerDN.getCN(); + String issuerOrg = issuerDN.getO(); + String issuerOU = issuerDN.getOU(); + // Issuer's Common Name (CN) + JLabel issuerCNLabel = new JLabel("Common Name (CN)"); + issuerCNLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuerCNLabel = (GridBagConstraints) gbc_labels.clone(); + gbc_issuerCNLabel.gridy = 9; + JLabel issuerCNValue = new JLabel(issuerCN); + issuerCNValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuerCNValue = (GridBagConstraints) gbc_values + .clone(); + gbc_issuerCNValue.gridy = 9; + // Issuer's Organisation (O) + JLabel issuerOrgLabel = new JLabel("Organisation (O)"); + issuerOrgLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuerOrgLabel = (GridBagConstraints) gbc_labels.clone(); + gbc_issuerOrgLabel.gridy = 10; + JLabel issuerOrgValue = new JLabel(issuerOrg); + issuerOrgValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuerOrgValue = (GridBagConstraints) gbc_values + .clone(); + gbc_issuerOrgValue.gridy = 10; + // Issuer's Organisation Unit (OU) + JLabel issuerOULabel = new JLabel("Organisation Unit (OU)"); + issuerOULabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuerOULabel = (GridBagConstraints) gbc_labels.clone(); + gbc_issuerOULabel.gridy = 11; + JLabel issuerOUValue = new JLabel(issuerOU); + issuerOUValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuerOUValue = (GridBagConstraints) gbc_values + .clone(); + gbc_issuerOUValue.gridy = 11; + + // Validity + JLabel validityLabel = new JLabel("Validity"); + validityLabel.setFont(new Font(null, BOLD, 11)); + GridBagConstraints gbc_validityLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_validityLabel.gridy = 12; + gbc_validityLabel.gridwidth = 2; // takes two columns + gbc_validityLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets + // Issued On + JLabel issuedOnLabel = new JLabel("Issued On"); + issuedOnLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuedOnLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_issuedOnLabel.gridy = 13; + JLabel issuedOnValue = new JLabel(cert.getNotBefore().toString()); + issuedOnValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_issuedOnValue = (GridBagConstraints) gbc_values + .clone(); + gbc_issuedOnValue.gridy = 13; + // Expires On + JLabel expiresOnLabel = new JLabel("Expires On"); + expiresOnLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_expiresOnLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_expiresOnLabel.gridy = 14; + JLabel expiresOnValue = new JLabel(cert.getNotAfter().toString()); + expiresOnValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_expiresOnValue = (GridBagConstraints) gbc_values + .clone(); + gbc_expiresOnValue.gridy = 14; + + // Fingerprints + byte[] binaryCertificateEncoding = new byte[0]; + try { + // each certificate has one binary encoding; for X.509 certs it is DER + binaryCertificateEncoding = cert.getEncoded(); + } catch (CertificateEncodingException ex) { + logger.error("Could not get the encoded form of the certificate.", ex); + } + JLabel fingerprintsLabel = new JLabel("Fingerprints"); + fingerprintsLabel.setFont(new Font(null, BOLD, 11)); + GridBagConstraints gbc_fingerprintsLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_fingerprintsLabel.gridy = 15; + gbc_fingerprintsLabel.gridwidth = 2; // takes two columns + gbc_fingerprintsLabel.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets + // SHA-1 Fingerprint + JLabel sha1FingerprintLabel = new JLabel("SHA1 Fingerprint"); + sha1FingerprintLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_sha1FingerprintLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_sha1FingerprintLabel.gridy = 16; + JLabel sha1FingerprintValue = new JLabel( + dnParser.getMessageDigestAsFormattedString( + binaryCertificateEncoding, "SHA1")); + sha1FingerprintValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_sha1FingerprintValue = (GridBagConstraints) gbc_values + .clone(); + gbc_sha1FingerprintValue.gridy = 16; + // MD5 Fingerprint + JLabel md5FingerprintLabel = new JLabel("MD5 Fingerprint"); + md5FingerprintLabel.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_md5FingerprinLabel = (GridBagConstraints) gbc_labels + .clone(); + gbc_md5FingerprinLabel.gridy = 17; + JLabel md5FingerprintValue = new JLabel( + dnParser.getMessageDigestAsFormattedString( + binaryCertificateEncoding, "MD5")); + md5FingerprintValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_md5FingerprintValue = (GridBagConstraints) gbc_values + .clone(); + gbc_md5FingerprintValue.gridy = 17; + + /* + * Empty label to add a bit space at the bottom of the panel to make it + * look like Firefox's view certificate dialog + */ + JLabel emptyLabel = new JLabel(""); + GridBagConstraints gbc_emptyLabel = (GridBagConstraints) gbc_labels.clone(); + gbc_emptyLabel.gridy = 18; + gbc_emptyLabel.gridwidth = 2; // takes two columns + gbc_emptyLabel.ipady = 40; + + JPanel certificatePanel = new JPanel(new GridBagLayout()); + certificatePanel.setBorder(new CompoundBorder(new EmptyBorder(15, 15, 15, + 15), new EtchedBorder())); + +// if (intendedUses != null) +// certificatePanel.add(intendedUsesPanel, gbc_intendedUsesLabel); + certificatePanel.add(issuedToLabel, gbc_issuedTo); // Issued To + certificatePanel.add(subjectCNLabel, gbc_subjectCNLabel); + certificatePanel.add(subjectCNValue, gbc_subjectCNValue); + certificatePanel.add(subjectOrgLabel, gbc_subjectOrgLabel); + certificatePanel.add(subjectOrgValue, gbc_subjectOrgValue); + certificatePanel.add(subjectOULabel, gbc_subjectOULabel); + certificatePanel.add(subjectOUValue, gbc_subjectOUValue); + // jpCertificate.add(jlEmail, gbc_jlEmail); + // jpCertificate.add(jlEmailValue, gbc_jlEmailValue); + certificatePanel.add(snLabel, gbc_snLabel); + certificatePanel.add(snValue, gbc_snValue); + certificatePanel.add(versionLabel, gbc_versionLabel); + certificatePanel.add(versionValue, gbc_versionValue); + certificatePanel.add(issuedByLabel, gbc_issuedByLabel); // Issued By + certificatePanel.add(issuerCNLabel, gbc_issuerCNLabel); + certificatePanel.add(issuerCNValue, gbc_issuerCNValue); + certificatePanel.add(issuerOrgLabel, gbc_issuerOrgLabel); + certificatePanel.add(issuerOrgValue, gbc_issuerOrgValue); + certificatePanel.add(issuerOULabel, gbc_issuerOULabel); + certificatePanel.add(issuerOUValue, gbc_issuerOUValue); + certificatePanel.add(validityLabel, gbc_validityLabel); // Validity + certificatePanel.add(issuedOnLabel, gbc_issuedOnLabel); + certificatePanel.add(issuedOnValue, gbc_issuedOnValue); + certificatePanel.add(expiresOnLabel, gbc_expiresOnLabel); + certificatePanel.add(expiresOnValue, gbc_expiresOnValue); + certificatePanel.add(fingerprintsLabel, gbc_fingerprintsLabel); // Fingerprints + certificatePanel.add(sha1FingerprintLabel, gbc_sha1FingerprintLabel); + certificatePanel.add(sha1FingerprintValue, gbc_sha1FingerprintValue); + certificatePanel.add(md5FingerprintLabel, gbc_md5FingerprinLabel); + certificatePanel.add(md5FingerprintValue, gbc_md5FingerprintValue); + // Empty label to get some vertical space on the frame + certificatePanel.add(emptyLabel, gbc_emptyLabel); + return certificatePanel; + } + +// private void trustPressed() { +// shouldTrust = true; +// shouldSave = false; +// closeDialog(); +// } + + private void trustAlwaysPressed() { + shouldTrust = true; + shouldSave = true; + closeDialog(); + } + + private void dontTrustPressed() { + shouldTrust = false; + shouldSave = false; + closeDialog(); + } + + public void closeDialog() { + setVisible(false); + dispose(); + } + + public boolean shouldTrust() { + return shouldTrust; + } + + public boolean shouldSave() { + return shouldSave; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateUI.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateUI.java new file mode 100644 index 0000000..0845543 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ConfirmTrustedCertificateUI.java
@@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; + +import java.awt.Frame; +import java.security.cert.X509Certificate; + +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.security.credentialmanager.TrustConfirmationProvider; + +import org.apache.log4j.Logger; + +/** + * @author Stian Soiland-Reyes + */ +public class ConfirmTrustedCertificateUI implements TrustConfirmationProvider { + private static Logger logger = Logger + .getLogger(ConfirmTrustedCertificateUI.class); + + private DistinguishedNameParser dnParser; + + @Override + public Boolean shouldTrustCertificate(X509Certificate[] chain) { + boolean trustConfirm = false; + logger.info("Asking the user if they want to trust a certificate."); + // Ask user if they want to trust this service + ConfirmTrustedCertificateDialog confirmCertTrustDialog = new ConfirmTrustedCertificateDialog( + (Frame) null, "Untrusted HTTPS connection", true, + (X509Certificate) chain[0], dnParser); + confirmCertTrustDialog.setLocationRelativeTo(null); + confirmCertTrustDialog.setVisible(true); + trustConfirm = confirmCertTrustDialog.shouldTrust(); +// trustConfirm.setShouldSave(confirmCertTrustDialog.shouldSave()); + if (!confirmCertTrustDialog.shouldTrust()) + showMessageDialog( + null, + "As you refused to trust this host, you will not be able to use its services from a workflow.", + "Untrusted HTTPS connection", INFORMATION_MESSAGE); + + return trustConfirm; + } + + /** + * @param dnParser + * the dnParser to set + */ + public void setDistinguishedNameParser(DistinguishedNameParser dnParser) { + this.dnParser = dnParser; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUI.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUI.java new file mode 100644 index 0000000..41d7a15 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUI.java
@@ -0,0 +1,1512 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.PAGE_END; +import static java.awt.Dialog.ModalExclusionType.APPLICATION_EXCLUDE; +import static java.awt.Toolkit.getDefaultToolkit; +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.NO_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; +import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.KEYSTORE; +import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.TRUSTSTORE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Image; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.net.URI; +import java.security.Key; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.prefs.Preferences; + +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.TableColumn; + +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType; +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.security.credentialmanager.UsernamePassword; + +import org.apache.log4j.Logger; +import org.bouncycastle.openssl.PEMReader; +import org.bouncycastle.openssl.PEMWriter; + +/** + * Provides a UI for the Credential Manager for users to manage their + * credentials saved by the Credential Manager in Taverna's Keystore and + * Trustore. Credentials include username and passwords pairs, key pairs, proxy + * key pairs and trusted certificates of CA's and s. Credentials are stored in + * two Bouncy Castle "UBER"-type keystores: the Keystore (containing passwords + * and (normal and proxy) key pairs) and the Truststore (containing trusted + * certificates). + * + * Inspired by the Portlecle tool (http://portecle.sourceforge.net/) + * and Firefox's Certificate Manager. + * + * @author Alex Nenadic + */ + +@SuppressWarnings("serial") +public class CredentialManagerUI extends JFrame { + private static Logger logger = Logger.getLogger(CredentialManagerUI.class); + /** Default tabbed pane width */ + private static final int DEFAULT_FRAME_WIDTH = 650; + /** Default tabbed pane height */ + private static final int DEFAULT_FRAME_HEIGHT = 400; + /** Credential Manager icon (when frame is minimised)*/ + private static final Image credManagerIconImage = getDefaultToolkit() + .createImage( + CredentialManagerUI.class + .getResource("/images/cred_manager_transparent.png")); + + /** + * Credential Manager to manage all operations on the Keystore and + * Truststore + */ + public final CredentialManager credManager; + private final DistinguishedNameParser dnParser; + + ////////////// Tabs ////////////// + + /** + * Tabbed pane to hold tables containing various entries in the Keystore and + * Truststore + */ + private JTabbedPane keyStoreTabbedPane; + /** Tab 1: holds passwords table */ + private JPanel passwordsTab = new JPanel(new BorderLayout(10, 10)); + /** Tab 1: name */ + public static final String PASSWORDS = "Passwords"; + /** Tab 2: holds key pairs (user certificates) table */ + private JPanel keyPairsTab = new JPanel(new BorderLayout(10, 10)); + /** Tab 2: name */ + public static final String KEYPAIRS = "Your Certificates"; + /** Tab 3: holds trusted certificates table */ + private JPanel trustedCertificatesTab = new JPanel(new BorderLayout(10, 10)); + /** Tab 3: name */ + public static final String TRUSTED_CERTIFICATES = "Trusted Certificates"; + + ////////////// Tables ////////////// + + /** Password entries' table */ + private JTable passwordsTable; + /** Key pair entries' table */ + private JTable keyPairsTable; + /** Trusted certificate entries' table */ + private JTable trustedCertsTable; + /** Password entry column type */ + public static final String PASSWORD_ENTRY_TYPE = "Password"; + /** Key pair entry column type */ + public static final String KEY_PAIR_ENTRY_TYPE = "Key Pair"; + /** Trusted cert entry column type */ + public static final String TRUST_CERT_ENTRY_TYPE = "Trusted Certificate"; + + /** + * Overrides the Object's clone method to prevent the singleton object to be + * cloned. + */ + @Override + public Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + /** + * Creates a new Credential Manager UI's frame. + */ + public CredentialManagerUI(CredentialManager credentialManager, + DistinguishedNameParser dnParser) { + credManager = credentialManager; + this.dnParser = dnParser; + setModalExclusionType(APPLICATION_EXCLUDE); + // Initialise the UI components + initComponents(); + } + + private void initComponents() { + /* + * Initialise the tabbed pane that contains the tabs with tabular + * representations of the Keystore's content. + */ + keyStoreTabbedPane = new JTabbedPane(); + /* + * Initialise the tab containing the table for username/password entries + * from the Keystore + */ + passwordsTable = initTable(PASSWORDS, passwordsTab); + /* + * Initialise the tab containing the table for key pair entries from the + * Keystore + */ + keyPairsTable = initTable(KEYPAIRS, keyPairsTab); + /* + * Initialise the tab containing the table for proxy entries from the + * Keystore + */ + //proxiesTable = initTable(PROXIES, proxiesTab); + /* + * Initialise the tab containing the table for trusted certificate + * entries from the Truststore + */ + trustedCertsTable = initTable(TRUSTED_CERTIFICATES, + trustedCertificatesTab); + /* + * Set the size of the tabbed pane to the preferred size - the size of + * the main application frame depends on it. + */ + keyStoreTabbedPane.setPreferredSize(new Dimension(DEFAULT_FRAME_WIDTH, + DEFAULT_FRAME_HEIGHT)); + + JPanel globalButtons = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton resetJavaAuthCache = new JButton("Clear HTTP authentication"); + resetJavaAuthCache.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clearAuthenticationCache(); + } + }); + globalButtons.add(resetJavaAuthCache); + + // Button for changing Credential Manager's master password + JButton changeMasterPasswordButton = new JButton( + "Change master password"); + changeMasterPasswordButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + changeMasterPassword(); + } + }); + globalButtons.add(changeMasterPasswordButton); + + // Add change master password to the main application frame + getContentPane().add(globalButtons, NORTH); + // Add tabbed pane to the main application frame + getContentPane().add(keyStoreTabbedPane, CENTER); + + // Handle application close + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeFrame(); + } + }); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + + pack(); + + // Centre the frame in the centre of the screen + setLocationRelativeTo(null); + + // Set the frame's icon + setIconImage(credManagerIconImage); + + // Set the frame's title + setTitle("Credential Manager"); + + // setModal(true); + // setVisible(true); + } + + protected void clearAuthenticationCache() { + if (!credManager.resetAuthCache()) + showMessageDialog( + this, + "Java's internal HTTP authentication cache could not be cleared. \n\n" + + "Taverna can only clear the cache using an undocumented Java API \n" + + "that might not work if you are using a Java VM other than \n" + + "Java 6 from Sun. You can restarting Taverna to clear the cache.", + "Could not clear authentication cache", ERROR_MESSAGE); + else + showMessageDialog( + this, + "Java's internal HTTP authentication cache has been cleared. \n\n" + + "You might also need to edit or delete individual \n" + + "password entries in the credential manager \n" + + "if a relevant password has previously been saved.", + "Cleared authentication cache", INFORMATION_MESSAGE); + } + + protected void changeMasterPassword() { + ChangeMasterPasswordDialog changePasswordDialog = new ChangeMasterPasswordDialog( + this, "Change master password", true, + "Change master password for Credential Manager", credManager); + changePasswordDialog.setLocationRelativeTo(null); + changePasswordDialog.setVisible(true); + String password = changePasswordDialog.getPassword(); + if (password == null) // user cancelled + return; // do nothing + + try { + credManager.changeMasterPassword(password); + showMessageDialog(this, "Master password changed sucessfully", + ALERT_TITLE, INFORMATION_MESSAGE); + } catch (CMException cme) { + /* + * Failed to change the master password for Credential Manager - + * warn the user + */ + String exMessage = "Failed to change master password for Credential Manager"; + logger.error(exMessage); + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + } + } + + /** + * Initialise the tabs and tables with the content from the Keystore and Truststore. + */ + private JTable initTable(String tableType, JPanel tab) { + JTable table = null; + + if (tableType.equals(PASSWORDS)) { // Passwords table + // The Passwords table's data model + PasswordsTableModel passwordsTableModel = new PasswordsTableModel(credManager); + // The table itself + table = new JTable(passwordsTableModel); + + /* + * Set the password and alias columns of the Passwords table to be + * invisible by removing them from the column model (they will still + * present in the table model) + * + * Remove the last column first + */ + TableColumn aliasColumn = table.getColumnModel().getColumn(5); + table.getColumnModel().removeColumn(aliasColumn); + TableColumn passwordColumn = table.getColumnModel().getColumn(4); + table.getColumnModel().removeColumn(passwordColumn); + TableColumn lastModifiedDateColumn = table.getColumnModel().getColumn(3); + table.getColumnModel().removeColumn(lastModifiedDateColumn); + + // Buttons + JButton newPasswordButton = new JButton("New"); + newPasswordButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + newPassword(); + } + }); + + final JButton viewPasswordButton = new JButton("Details"); + viewPasswordButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + viewPassword(); + } + }); + viewPasswordButton.setEnabled(false); + + final JButton editPasswordButton = new JButton("Edit"); + editPasswordButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + editPassword(); + } + }); + editPasswordButton.setEnabled(false); + + final JButton deletePasswordButton = new JButton("Delete"); + deletePasswordButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deletePassword(); + } + }); + deletePasswordButton.setEnabled(false); + + /* + * Selection listener for passwords table to enable/disable action + * buttons accordingly + */ + class PasswordsTableSelectionListner implements + ListSelectionListener { + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getSource() != passwordsTable.getSelectionModel()) + return; + if (passwordsTable.getSelectedRow() == -1) { + // nothing is selected + viewPasswordButton.setEnabled(false); + editPasswordButton.setEnabled(false); + deletePasswordButton.setEnabled(false); + } else { + if (!viewPasswordButton.isEnabled()) + viewPasswordButton.setEnabled(true); + if (!editPasswordButton.isEnabled()) + editPasswordButton.setEnabled(true); + if (!deletePasswordButton.isEnabled()) + deletePasswordButton.setEnabled(true); + } + } + } + table.getSelectionModel().addListSelectionListener(new PasswordsTableSelectionListner()); + + // Panel to hold the buttons + JPanel bp = new JPanel(); + bp.add(viewPasswordButton); + bp.add(editPasswordButton); + bp.add(newPasswordButton); + bp.add(deletePasswordButton); + + // Add button panel to the tab + tab.add(bp, PAGE_END); + + } else if (tableType.equals(KEYPAIRS)) { // Key Pairs tab + // The Key Pairs table's data model + KeyPairsTableModel keyPairsTableModel = new KeyPairsTableModel(credManager); + // The table itself + table = new JTable(keyPairsTableModel); + + /* + * Set the alias and service URIs columns of the KayPairs table to + * be invisible by removing them from the column model (they will + * still present in the table model) + * + * Remove the last column first + */ + TableColumn aliasColumn = table.getColumnModel().getColumn(6); + table.getColumnModel().removeColumn(aliasColumn); + TableColumn serviceURIsColumn = table.getColumnModel().getColumn(5); + table.getColumnModel().removeColumn(serviceURIsColumn); + TableColumn lastModifiedDateColumn = table.getColumnModel().getColumn(4); + table.getColumnModel().removeColumn(lastModifiedDateColumn); + + // Buttons + final JButton viewKeyPairButton = new JButton("Details"); + viewKeyPairButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + viewCertificate(); + } + }); + viewKeyPairButton.setEnabled(false); + + JButton importKeyPairButton = new JButton("Import"); + importKeyPairButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + importKeyPair(); + } + }); + + final JButton exportKeyPairButton = new JButton("Export"); + exportKeyPairButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportKeyPair(); + } + }); + exportKeyPairButton.setEnabled(false); + + final JButton deleteKeyPairButton = new JButton("Delete"); + deleteKeyPairButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteKeyPair(); + } + }); + deleteKeyPairButton.setEnabled(false); + + /* + * Selection listener for key pairs table to enable/disable action + * buttons accordingly + */ + class KeyPairsTableSelectionListner implements + ListSelectionListener { + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getSource() != keyPairsTable.getSelectionModel()) + return; + if (keyPairsTable.getSelectedRow() == -1) { + // nothing is selected + viewKeyPairButton.setEnabled(false); + exportKeyPairButton.setEnabled(false); + deleteKeyPairButton.setEnabled(false); + } else { + if (!viewKeyPairButton.isEnabled()) + viewKeyPairButton.setEnabled(true); + if (!exportKeyPairButton.isEnabled()) + exportKeyPairButton.setEnabled(true); + if (!deleteKeyPairButton.isEnabled()) + deleteKeyPairButton.setEnabled(true); + } + } + } + table.getSelectionModel().addListSelectionListener( + new KeyPairsTableSelectionListner()); + + // Panel to hold the buttons + JPanel bp = new JPanel(); + bp.add(viewKeyPairButton); + bp.add(importKeyPairButton); + bp.add(exportKeyPairButton); + bp.add(deleteKeyPairButton); + + // Add button panel to the tab + tab.add(bp, PAGE_END); + } else if (tableType.equals(TRUSTED_CERTIFICATES)) { // Certificates tab + + // The Trusted Certificate table's data model + TrustedCertsTableModel trustedCertificatesTableModel = new TrustedCertsTableModel(credManager); + // The table itself + table = new JTable(trustedCertificatesTableModel); + + /* + * Set the alias columns of the Trusted Certs table to be invisible + * by removing them from the column model (they will still be + * present in the table model) + * + * Remove the last column first + */ + TableColumn aliasColumn = table.getColumnModel().getColumn(5); + table.getColumnModel().removeColumn(aliasColumn); + TableColumn lastModifiedDateColumn = table.getColumnModel().getColumn(4); + table.getColumnModel().removeColumn(lastModifiedDateColumn); + + // Buttons + final JButton viewTrustedCertificateButton = new JButton("Details"); + viewTrustedCertificateButton + .addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + viewCertificate(); + } + }); + viewTrustedCertificateButton.setEnabled(false); + + JButton importTrustedCertificateButton = new JButton("Import"); + importTrustedCertificateButton + .addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + importTrustedCertificate(); + } + }); + + final JButton exportTrustedCertificateButton = new JButton("Export"); + exportTrustedCertificateButton + .addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportTrustedCertificate(); + } + }); + exportTrustedCertificateButton.setEnabled(false); + + final JButton deleteTrustedCertificateButton = new JButton("Delete"); + deleteTrustedCertificateButton + .addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteTrustedCertificate(); + } + }); + deleteTrustedCertificateButton.setEnabled(false); + + // Selection listener for trusted certs table to enable/disable action buttons accordingly + class TrustedCertsTableSelectionListener implements + ListSelectionListener { + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getSource() != trustedCertsTable.getSelectionModel()) + return; + if (trustedCertsTable.getSelectedRow() == -1) { + // nothing is selected + viewTrustedCertificateButton.setEnabled(false); + exportTrustedCertificateButton.setEnabled(false); + deleteTrustedCertificateButton.setEnabled(false); + } else { + if (!viewTrustedCertificateButton.isEnabled()) + viewTrustedCertificateButton.setEnabled(true); + if (!exportTrustedCertificateButton.isEnabled()) + exportTrustedCertificateButton.setEnabled(true); + if (!deleteTrustedCertificateButton.isEnabled()) + deleteTrustedCertificateButton.setEnabled(true); + } + } + } + table.getSelectionModel().addListSelectionListener( + new TrustedCertsTableSelectionListener()); + + // Panel to hold the buttons + JPanel bp = new JPanel(); + bp.add(viewTrustedCertificateButton); + bp.add(importTrustedCertificateButton); + bp.add(exportTrustedCertificateButton); + bp.add(deleteTrustedCertificateButton); + + // Add button panel to the tab + tab.add(bp, PAGE_END); + } else { + throw new RuntimeException("Unknown table type " + tableType); + } + + table.setShowGrid(false); + table.setRowMargin(0); + table.getColumnModel().setColumnMargin(0); + table.getTableHeader().setReorderingAllowed(false); + table.setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS); + // Top accommodates entry icons with 2 pixels spare space (images are + // 16x16 pixels) + table.setRowHeight(18); + + // Add custom renderrers for the table headers and cells + for (int iCnt = 0; iCnt < table.getColumnCount(); iCnt++) { + TableColumn column = table.getColumnModel().getColumn(iCnt); + column.setHeaderRenderer(new TableHeaderRenderer()); + column.setCellRenderer(new TableCellRenderer()); + } + + // Make the first column small and not resizable (it holds icons to + // represent different entry types) + TableColumn typeCol = table.getColumnModel().getColumn(0); + typeCol.setResizable(false); + typeCol.setMinWidth(20); + typeCol.setMaxWidth(20); + typeCol.setPreferredWidth(20); + + // Set the size for the second column + // (i.e. Service URI column of Passwords table, and + // Certificate Name column of the Kay Pairs and Trusted Certificates tables) + // We do not care about the size of other columns. + TableColumn secondCol = table.getColumnModel().getColumn(1); + secondCol.setMinWidth(20); + secondCol.setMaxWidth(10000); + secondCol.setPreferredWidth(300); + + // Put the table into a scroll pane + JScrollPane jspTableScrollPane = new JScrollPane(table, + VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + jspTableScrollPane.getViewport().setBackground(table.getBackground()); + + // Put the scroll pane on the tab panel + tab.add(jspTableScrollPane, CENTER); + jspTableScrollPane.setBorder(new EmptyBorder(3, 3, 3, 3)); + + /* + * Add mouse listeners to show an entry's details if it is + * double-clicked + */ + table.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent evt) { + tableDoubleClick(evt); + } + }); + + // Add the tab to the tabbed pane + keyStoreTabbedPane.addTab(tableType, tab); + + return table; + } + + /** + * Displays the details of the username/password pair entry - this includes + * showing the plaintext password and service URI for this entry. + */ + private void viewPassword() { + // Which username/password pair entry has been selected, if any? + int iRow = passwordsTable.getSelectedRow(); + if (iRow == -1) // no row currently selected + return; + + // Get current values for service URI, username and password + String serviceURI = (String) passwordsTable.getValueAt(iRow, 1); // current entry's service URI + + String username = (String) passwordsTable.getValueAt(iRow, 2); // current entry's username + + /* + * Because the password column is not visible we call the getValueAt + * method on the table model rather than at the JTable + */ + String password = (String) passwordsTable.getModel() + .getValueAt(iRow, 4); // current entry's password value + + // Let the user view service URI, username and password of the entry + ViewUsernamePasswordEntryDialog viewServicePassDialog = new ViewUsernamePasswordEntryDialog( + this, serviceURI, username, password); + + viewServicePassDialog.setLocationRelativeTo(this); + viewServicePassDialog.setVisible(true); + } + + /** + * Lets a user insert a new username/password/service URI tuple to the + * Keystore. + */ + private void newPassword() { + URI serviceURI = null; // service URI + String username = null; // username + String password = null; // password + + // Loop until the user cancels or enters everything correctly + while (true) { + /* + * Let the user insert a new password entry (by specifying service + * URI, username and password) + */ + NewEditPasswordEntryDialog newPasswordDialog = new NewEditPasswordEntryDialog( + this, "New username and password for a service", true, + serviceURI, username, password, credManager); + newPasswordDialog.setLocationRelativeTo(this); + newPasswordDialog.setVisible(true); + + serviceURI = newPasswordDialog.getServiceURI(); // get service URI + username = newPasswordDialog.getUsername(); // get username + password = newPasswordDialog.getPassword(); // get password + + if (password == null) { // user cancelled - any of the above three + // fields is null + // do nothing + return; + } + + /* + * Check if a password entry with the given service URI already + * exists in the Keystore. We ask this here as the user may wish to + * overwrite the existing password entry. Checking for key pair + * entries' URIs is done in the NewEditPasswordEntry dialog. + */ + + /* + * Get list of service URIs for all the password entries in the + * Keystore + */ + List<URI> serviceURIs = null; + try { + serviceURIs = credManager + .getServiceURIsForAllUsernameAndPasswordPairs(); + } catch (CMException cme) { + showMessageDialog(this, "Failed to get service URIs for all username and password pairs " + + "to check if the entered service URI already exists", + ERROR_TITLE, ERROR_MESSAGE); + return; + } + if (serviceURIs.contains(serviceURI)) { // if such a URI already + // exists + // Ask if the user wants to overwrite it + int answer = showConfirmDialog( + this, + "Credential Manager already contains a password entry with the same service URI.\n" + + "Do you want to overwrite it?", + ALERT_TITLE, + YES_NO_OPTION); + + // Add the new password entry in the Keystore + try { + if (answer == YES_OPTION) { + credManager.addUsernameAndPasswordForService( + new UsernamePassword(username, password), + serviceURI); + break; + } + } catch (CMException cme) { + showMessageDialog( + this, + "Credential Manager failed to insert a new username and password pair", + ERROR_TITLE, ERROR_MESSAGE); + } + /* + * Otherwise show the same window with the entered service URI, + * username and password values + */ + } else + // Add the new password entry in the Keystore + try { + credManager.addUsernameAndPasswordForService(new UsernamePassword(username, + password), serviceURI); + break; + } catch (CMException cme) { + showMessageDialog( + this, + "Credential Manager failed to insert a new username and password pair", + ERROR_TITLE, ERROR_MESSAGE); + } + } + } + + /** + * Lets a user insert a new username/password pair for a given service URI + * to the Keystore. + */ + public void newPasswordForService(URI serviceURI) { + /* + * As this method can be called from outside of Credential Manager UI, + * e.g. from wsdl-activity-ui or rshell-activity-ui to pop up a dialog + * to ask the user for username and password, we also want to make sure + * the main Credential Manager UI Dialog is visible as it may be clearer + * to the user what is going on + */ + if (!isVisible() || getState() == ICONIFIED) + setVisible(true); + + // Make sure password tab is selected as this method may + // be called from outside of Credential Manager UI. + keyStoreTabbedPane.setSelectedComponent(passwordsTab); + + String username = null; // username + String password = null; // password + + // Loop until the user cancels or enters everything correctly + while (true) { + +// if(!this.isVisible()){ // if Cred Man UI is already showing but e.g. obscured by another window or minimised +// // Do not bring it up! +// } // actually we now want to show it as it makes it clearer to the user what is going on + + // Let the user insert a new password entry for the given service + // URI (by specifying username and password) + NewEditPasswordEntryDialog newPasswordDialog = new NewEditPasswordEntryDialog( + this, "New username and password for a service", true, + serviceURI, username, password, credManager); + newPasswordDialog.setLocationRelativeTo(this); + newPasswordDialog.setVisible(true); + + serviceURI = newPasswordDialog.getServiceURI(); // get service URI + username = newPasswordDialog.getUsername(); // get username + password = newPasswordDialog.getPassword(); // get password + + if (password == null) // user cancelled - any of the above three + // fields is null + // do nothing + return; + + /* + * Check if a password entry with the given service URI already + * exists in the Keystore. We ask this here as the user may wish to + * overwrite the existing password entry. Checking for key pair + * entries' URIs is done in the NewEditPasswordEntry dialog. + */ + + // Get list of service URIs for all the password entries in the + // Keystore + List<URI> serviceURIs = null; + try { + serviceURIs = credManager + .getServiceURIsForAllUsernameAndPasswordPairs(); + } catch (CMException cme) { + showMessageDialog(this, "Failed to get service URIs for all username and password pairs " + + "to check if the entered service URI already exists", + ERROR_TITLE, ERROR_MESSAGE); + return; + } + if (serviceURIs.contains(serviceURI)) { // if such a URI already + // exists + // Ask if the user wants to overwrite it + int answer = showConfirmDialog( + this, + "Credential Manager already contains a password entry with the same service URI.\n" + + "Do you want to overwrite it?", ALERT_TITLE, + YES_NO_OPTION); + + // Add the new password entry in the Keystore + try { + if (answer == YES_OPTION) { + credManager.addUsernameAndPasswordForService( + new UsernamePassword(username, password), + serviceURI); + break; + } + } catch (CMException cme) { + String exMessage = "Credential Manager failed to insert a new username and password pair"; + showMessageDialog(this, exMessage, ERROR_TITLE, + ERROR_MESSAGE); + } + // Otherwise show the same window with the entered service + // URI, username and password values + } else + // Add the new password entry in the Keystore + try { + credManager.addUsernameAndPasswordForService(new UsernamePassword(username, + password), serviceURI); + break; + } catch (CMException cme) { + showMessageDialog(this, "Credential Manager failed to insert a new username and password pair", + ERROR_TITLE, + ERROR_MESSAGE); + } + } + } + + /** + * Lets a user edit a username and password entry or their related service + * URI to the Keystore. + */ + private void editPassword() { + // Which password entry has been selected? + int iRow = passwordsTable.getSelectedRow(); + if (iRow == -1) { // no row currently selected + return; + } + + // Get current values for service URI, username and password + URI serviceURI = URI.create((String) passwordsTable.getValueAt(iRow, 1)); // current entry's service URI + + String username = (String) passwordsTable.getValueAt(iRow, 2); // current entry's username + + /* + * Because the password column is not visible we call the getValueAt + * method on the table model rather than at the JTable + */ + String password = (String) passwordsTable.getModel() + .getValueAt(iRow, 4); // current entry's password value + + while (true) { // loop until user cancels or enters everything correctly + // Let the user edit service URI, username or password of a password entry + NewEditPasswordEntryDialog editPasswordDialog = new NewEditPasswordEntryDialog( + this, "Edit username and password for a service", true, + serviceURI, username, password, credManager); + + editPasswordDialog.setLocationRelativeTo(this); + editPasswordDialog.setVisible(true); + + // New values + URI newServiceURI = editPasswordDialog.getServiceURI(); // get new service URI + String newUsername = editPasswordDialog.getUsername(); // get new username + String newPassword = editPasswordDialog.getPassword(); // get new password + + if (newPassword == null) // user cancelled - any of the above three + // fields is null + // do nothing + return; + + // Is anything actually modified? + boolean isModified = !serviceURI.equals(newServiceURI) + || !username.equals(newUsername) + || !password.equals(newPassword); + + if (isModified) { + /* + * Check if a different password entry with the new URI (i.e. + * alias) already exists in the Keystore We ask this here as the + * user may wish to overwrite that other password entry. + */ + + // Get list of URIs for all passwords in the Keystore + List<URI> serviceURIs = null; + try { + serviceURIs = credManager + .getServiceURIsForAllUsernameAndPasswordPairs(); + } catch (CMException cme) { + showMessageDialog(this, "Failed to get service URIs for all username and password pairs " + + "to check if the modified entry already exists", + ERROR_TITLE, + ERROR_MESSAGE); + return; + } + + // If the modified service URI already exists and is not the + // currently selected one + if (!newServiceURI.equals(serviceURI) + && serviceURIs.contains(newServiceURI)) { + int answer = showConfirmDialog( + this, + "The Keystore already contains username and password pair for the entered service URI.\n" + + "Do you want to overwrite it?", + ALERT_TITLE, YES_NO_OPTION); + + try { + if (answer == YES_OPTION) { + /* + * Overwrite that other entry entry and save the new + * one in its place. Also remove the current one + * that we are editing - as it is replacing the + * other entry. + */ + credManager + .deleteUsernameAndPasswordForService(serviceURI); + credManager.addUsernameAndPasswordForService( + new UsernamePassword(newUsername, + newPassword), newServiceURI); + break; + } + } catch (CMException cme) { + showMessageDialog( + this, + "Failed to update the username and password pair in the Keystore", + ERROR_TITLE, ERROR_MESSAGE); + } + // Otherwise show the same window with the entered + // service URI, username and password values + } else + try { + if (!newServiceURI.equals(serviceURI)) + credManager + .deleteUsernameAndPasswordForService(serviceURI); + credManager.addUsernameAndPasswordForService( + new UsernamePassword(newUsername, newPassword), newServiceURI); + break; + } catch (CMException cme) { + showMessageDialog( + this, + "Failed to update the username and password pair in the Keystore", + ERROR_TITLE, ERROR_MESSAGE); + } + } else // nothing actually modified + break; + } + } + + /** + * Lets the user delete the selected username and password entries from the + * Keystore. + */ + private void deletePassword() { + // Which entries have been selected? + int[] selectedRows = passwordsTable.getSelectedRows(); + if (selectedRows.length == 0) // no password entry selected + return; + + // Ask user to confirm the deletion + if (showConfirmDialog( + null, + "Are you sure you want to delete the selected username and password entries?", + ALERT_TITLE, YES_NO_OPTION) != YES_OPTION) + return; + + String exMessage = null; + for (int i = selectedRows.length - 1; i >= 0; i--) { // delete from backwards + // Get service URI for the current entry + URI serviceURI = URI.create((String) passwordsTable.getValueAt(selectedRows[i], 1)); + // current entry's service URI + try { + // Delete the password entry from the Keystore + credManager.deleteUsernameAndPasswordForService(serviceURI); + } catch (CMException cme) { + exMessage = "Failed to delete the username and password pair from the Keystore"; + } + } + if (exMessage != null) + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + } + + /** + * Shows the contents of a (user or trusted) certificate. + */ + private void viewCertificate() { + int selectedRow = -1; + String alias = null; + X509Certificate certToView = null; + ArrayList<String> serviceURIs = null; + KeystoreType keystoreType = null; + + // Are we showing user's public key certificate? + if (keyPairsTab.isShowing()) { + keystoreType = KEYSTORE; + selectedRow = keyPairsTable.getSelectedRow(); + + if (selectedRow != -1) + /* + * Because the alias column is not visible we call the + * getValueAt method on the table model rather than at the + * JTable + */ + alias = (String) keyPairsTable.getModel().getValueAt(selectedRow, 6); // current entry's Keystore alias + } + // Are we showing trusted certificate? + else if (trustedCertificatesTab.isShowing()) { + keystoreType = TRUSTSTORE; + selectedRow = trustedCertsTable.getSelectedRow(); + + if (selectedRow != -1) + /* + * Get the selected trusted certificate entry's Truststore alias + * Alias column is invisible so we get the value from the table + * model + */ + alias = (String) trustedCertsTable.getModel().getValueAt( + selectedRow, 5); + } + + try { + if (selectedRow != -1) { // something has been selected + // Get the entry's certificate + certToView = dnParser.convertCertificate(credManager + .getCertificate(keystoreType, alias)); + + // Show the certificate's contents to the user + ViewCertDetailsDialog viewCertDetailsDialog = new ViewCertDetailsDialog( + this, "Certificate details", true, certToView, + serviceURIs, dnParser); + viewCertDetailsDialog.setLocationRelativeTo(this); + viewCertDetailsDialog.setVisible(true); + } + } catch (CMException cme) { + String exMessage = "Failed to get certificate details to display to the user"; + logger.error(exMessage, cme); + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + } + } + + /** + * Lets a user import a key pair from a PKCS #12 keystore file to the + * Keystore. + */ + private void importKeyPair() { + /* + * Let the user choose a PKCS #12 file (keystore) containing a public + * and private key pair to import + */ + File importFile = selectImportExportFile( + "PKCS #12 file to import from", // title + new String[] { ".p12", ".pfx" }, // array of file extensions + // for the file filter + "PKCS#12 Files (*.p12, *.pfx)", // description of the filter + "Import", // text for the file chooser's approve button + "keyPairDir"); // preference string for saving the last chosen directory + + if (importFile == null) + return; + + // The PKCS #12 keystore is not a file + if (!importFile.isFile()) { + showMessageDialog(this, "Your selection is not a file", + ALERT_TITLE, WARNING_MESSAGE); + return; + } + + // Get the user to enter the password that was used to encrypt the + // private key contained in the PKCS #12 file + GetPasswordDialog getPasswordDialog = new GetPasswordDialog(this, + "Import key pair entry", true, + "Enter the password that was used to encrypt the PKCS #12 file"); + getPasswordDialog.setLocationRelativeTo(this); + getPasswordDialog.setVisible(true); + + String pkcs12Password = getPasswordDialog.getPassword(); + + if (pkcs12Password == null) // user cancelled + return; + else if (pkcs12Password.isEmpty()) // empty password + // FIXME: Maybe user did not have the password set for the private key??? + return; + + try { + // Load the PKCS #12 keystore from the file + // (this is using the BouncyCastle provider !!!) + KeyStore pkcs12Keystore = credManager.loadPKCS12Keystore(importFile, + pkcs12Password); + + /* + * Display the import key pair dialog supplying all the private keys + * stored in the PKCS #12 file (normally there will be only one + * private key inside, but could be more as this is a keystore after + * all). + */ + NewKeyPairEntryDialog importKeyPairDialog = new NewKeyPairEntryDialog( + this, "Credential Manager", true, pkcs12Keystore, dnParser); + importKeyPairDialog.setLocationRelativeTo(this); + importKeyPairDialog.setVisible(true); + + // Get the private key and certificate chain of the key pair + Key privateKey = importKeyPairDialog.getPrivateKey(); + Certificate[] certChain = importKeyPairDialog.getCertificateChain(); + + if (privateKey == null || certChain == null) + // User did not select a key pair for import or cancelled + return; + + /* + * Check if a key pair entry with the same alias already exists in + * the Keystore + */ + if (credManager.hasKeyPair(privateKey, certChain) + && showConfirmDialog(this, + "The keystore already contains the key pair entry with the same private key.\n" + + "Do you want to overwrite it?", + ALERT_TITLE, YES_NO_OPTION) != YES_OPTION) + return; + + // Place the private key and certificate chain into the Keystore + credManager.addKeyPair(privateKey, certChain); + + // Display success message + showMessageDialog(this, "Key pair import successful", ALERT_TITLE, + INFORMATION_MESSAGE); + } catch (Exception ex) { // too many exceptions to catch separately + String exMessage = "Failed to import the key pair entry to the Keystore. " + + ex.getMessage(); + logger.error(exMessage, ex); + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + } + } + + /** + * Lets a user export user's private and public key pair to a PKCS #12 + * keystore file. + */ + private void exportKeyPair() { + // Which key pair entry has been selected? + int selectedRow = keyPairsTable.getSelectedRow(); + if (selectedRow == -1) // no row currently selected + return; + + // Get the key pair entry's Keystore alias + String alias = (String) keyPairsTable.getModel().getValueAt(selectedRow, 6); + + // Let the user choose a PKCS #12 file (keystore) to export public and + // private key pair to + File exportFile = selectImportExportFile("Select a file to export to", // title + new String[] { ".p12", ".pfx" }, // array of file extensions + // for the file filter + "PKCS#12 Files (*.p12, *.pfx)", // description of the filter + "Export", // text for the file chooser's approve button + "keyPairDir"); // preference string for saving the last chosen directory + + if (exportFile == null) + return; + + // If file already exist - ask the user if he wants to overwrite it + if (exportFile.isFile() + && showConfirmDialog(this, + "The file with the given name already exists.\n" + + "Do you want to overwrite it?", ALERT_TITLE, + YES_NO_OPTION) == NO_OPTION) + return; + + // Get the user to enter the password for the PKCS #12 keystore file + GetPasswordDialog getPasswordDialog = new GetPasswordDialog(this, + "Credential Manager", true, + "Enter the password for protecting the exported key pair"); + getPasswordDialog.setLocationRelativeTo(this); + getPasswordDialog.setVisible(true); + + String pkcs12Password = getPasswordDialog.getPassword(); + + if (pkcs12Password == null) { // user cancelled or empty password + // Warn the user + showMessageDialog( + this, + "You must supply a password for protecting the exported key pair.", + ALERT_TITLE, INFORMATION_MESSAGE); + return; + } + + // Export the key pair + try { + credManager.exportKeyPair(alias, exportFile, pkcs12Password); + showMessageDialog(this, "Key pair export successful", ALERT_TITLE, + INFORMATION_MESSAGE); + } catch (CMException cme) { + showMessageDialog(this, cme.getMessage(), ERROR_TITLE, + ERROR_MESSAGE); + } + } + + /** + * Lets a user delete selected key pair entries from the Keystore. + */ + private void deleteKeyPair() { + // Which entries have been selected? + int[] selectedRows = keyPairsTable.getSelectedRows(); + if (selectedRows.length == 0) // no key pair entry selected + return; + + // Ask user to confirm the deletion + if (showConfirmDialog(null, + "Are you sure you want to delete the selected key pairs?", + ALERT_TITLE, YES_NO_OPTION) != YES_OPTION) + return; + + String exMessage = null; + for (int i = selectedRows.length - 1; i >= 0; i--) { // delete from backwards + // Get the alias for the current entry + String alias = (String) keyPairsTable.getModel().getValueAt( + selectedRows[i], 6); + try { + // Delete the key pair entry from the Keystore + credManager.deleteKeyPair(alias); + } catch (CMException cme) { + logger.warn("failed to delete " + alias, cme); + exMessage = "Failed to delete the key pair(s) from the Keystore"; + } + } + if (exMessage != null) + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + } + + /** + * Lets a user import a trusted certificate from a PEM or DER encoded file + * into the Truststore. + */ + private void importTrustedCertificate() { + // Let the user choose a file containing trusted certificate(s) to + // import + File certFile = selectImportExportFile( + "Certificate file to import from", // title + new String[] { ".pem", ".crt", ".cer", ".der", "p7", ".p7c" }, // file extensions filters + "Certificate Files (*.pem, *.crt, , *.cer, *.der, *.p7, *.p7c)", // filter descriptions + "Import", // text for the file chooser's approve button + "trustedCertDir"); // preference string for saving the last chosen directory + if (certFile == null) + return; + + // Load the certificate(s) from the file + ArrayList<X509Certificate> trustCertsList = new ArrayList<>(); + CertificateFactory cf; + try { + cf = CertificateFactory.getInstance("X.509"); + } catch (Exception e) { + // Nothing we can do! Things are badly misconfigured + cf = null; + } + + if (cf != null) { + try (FileInputStream fis = new FileInputStream(certFile)) { + for (Certificate cert : cf.generateCertificates(fis)) + trustCertsList.add((X509Certificate) cert); + } catch (Exception cex) { + // Do nothing + } + + if (trustCertsList.size() == 0) { + // Could not load certificates as any of the above types + try (FileInputStream fis = new FileInputStream(certFile); + PEMReader pr = new PEMReader( + new InputStreamReader(fis), null, cf + .getProvider().getName())) { + /* + * Try as openssl PEM format - which sligtly differs from + * the one supported by JCE + */ + Object cert; + while ((cert = pr.readObject()) != null) + if (cert instanceof X509Certificate) + trustCertsList.add((X509Certificate) cert); + } catch (Exception cex) { + // do nothing + } + } + } + + if (trustCertsList.size() == 0) { + /* Failed to load certifcate(s) using any of the known encodings */ + showMessageDialog(this, + "Failed to load certificate(s) using any of the known encodings -\n" + + "file format not recognised.", ERROR_TITLE, + ERROR_MESSAGE); + return; + } + + // Show the list of certificates contained in the file for the user to + // select the ones to import + NewTrustCertsDialog importTrustCertsDialog = new NewTrustCertsDialog(this, + "Credential Manager", true, trustCertsList, dnParser); + + importTrustCertsDialog.setLocationRelativeTo(this); + importTrustCertsDialog.setVisible(true); + List<X509Certificate> selectedTrustCerts = importTrustCertsDialog + .getTrustedCertificates(); // user-selected trusted certs to import + + // If user cancelled or did not select any cert to import + if (selectedTrustCerts == null || selectedTrustCerts.isEmpty()) + return; + + try { + for (X509Certificate cert : selectedTrustCerts) + // Import the selected trusted certificates + credManager.addTrustedCertificate(cert); + + // Display success message + showMessageDialog(this, "Trusted certificate(s) import successful", + ALERT_TITLE, INFORMATION_MESSAGE); + } catch (CMException cme) { + String exMessage = "Failed to import trusted certificate(s) to the Truststore"; + logger.error(exMessage, cme); + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + } + } + + /** + * Lets the user export one (at the moment) or more (in future) trusted + * certificate entries to a PEM-encoded file. + */ + private boolean exportTrustedCertificate() { + // Which trusted certificate has been selected? + int selectedRow = trustedCertsTable.getSelectedRow(); + if (selectedRow == -1) // no row currently selected + return false; + + // Get the trusted certificate entry's Keystore alias + String alias = (String) trustedCertsTable.getModel() + .getValueAt(selectedRow, 3); + // the alias column is invisible so we get the value from the table + // model + + // Let the user choose a file to export to + File exportFile = selectImportExportFile("Select a file to export to", // title + new String[] { ".pem" }, // array of file extensions for the + // file filter + "Certificate Files (*.pem)", // description of the filter + "Export", // text for the file chooser's approve button + "trustedCertDir"); // preference string for saving the last chosen directory + if (exportFile == null) + return false; + + // If file already exist - ask the user if he wants to overwrite it + if (exportFile.isFile() + && showConfirmDialog(this, + "The file with the given name already exists.\n" + + "Do you want to overwrite it?", ALERT_TITLE, + YES_NO_OPTION) == NO_OPTION) + return false; + + // Export the trusted certificate + try (PEMWriter pw = new PEMWriter(new FileWriter(exportFile))) { + // Get the trusted certificate + pw.writeObject(credManager.getCertificate(TRUSTSTORE, alias)); + } catch (Exception ex) { + String exMessage = "Failed to export the trusted certificate from the Truststore."; + logger.error(exMessage, ex); + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + return false; + } + showMessageDialog(this, "Trusted certificate export successful", + ALERT_TITLE, INFORMATION_MESSAGE); + return true; + } + + /** + * Lets a user delete the selected trusted certificate entries from the + * Truststore. + */ + private void deleteTrustedCertificate() { + // Which entries have been selected? + int[] selectedRows = trustedCertsTable.getSelectedRows(); + if (selectedRows.length == 0) // no trusted cert entry selected + return; + + // Ask user to confirm the deletion + if (showConfirmDialog( + null, + "Are you sure you want to delete the selected trusted certificate(s)?", + ALERT_TITLE, YES_NO_OPTION) != YES_OPTION) + return; + + String exMessage = null; + for (int i = selectedRows.length - 1; i >= 0; i--) { // delete from backwards + // Get the alias for the current entry + String alias = (String) trustedCertsTable.getModel().getValueAt( + selectedRows[i], 5); + try { + // Delete the trusted certificate entry from the Truststore + credManager.deleteTrustedCertificate(alias); + } catch (CMException cme) { + exMessage = "Failed to delete the trusted certificate(s) from the Truststore"; + logger.error(exMessage, cme); + } + } + if (exMessage != null) + showMessageDialog(this, exMessage, ERROR_TITLE, ERROR_MESSAGE); + } + + /** + * If double click on a table occured - show the + * details of the table entry. + */ + private void tableDoubleClick(MouseEvent evt) { + if (evt.getClickCount() > 1) { // is it a double click? + // Which row was clicked on (if any)? + Point point = new Point(evt.getX(), evt.getY()); + int row = ((JTable) evt.getSource()).rowAtPoint(point); + if (row == -1) + return; + // Which table the click occured on? + if (((JTable) evt.getSource()).getModel() instanceof PasswordsTableModel) + // Passwords table + viewPassword(); + else if (((JTable) evt.getSource()).getModel() instanceof KeyPairsTableModel) + // Key pairs table + viewCertificate(); + else + // Trusted certificates table + viewCertificate(); + } + } + + /** + * Lets the user select a file to export to or import from a key pair or a + * certificate. + */ + private File selectImportExportFile(String title, String[] filter, + String description, String approveButtonText, String prefString) { + Preferences prefs = Preferences + .userNodeForPackage(CredentialManagerUI.class); + String keyPairDir = prefs.get(prefString, + System.getProperty("user.home")); + JFileChooser fileChooser = new JFileChooser(); + fileChooser.addChoosableFileFilter(new CryptoFileFilter(filter, + description)); + fileChooser.setDialogTitle(title); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setCurrentDirectory(new File(keyPairDir)); + + if (fileChooser.showDialog(this, approveButtonText) != APPROVE_OPTION) + return null; + + File selectedFile = fileChooser.getSelectedFile(); + prefs.put(prefString, fileChooser.getCurrentDirectory().toString()); + return selectedFile; + } + + private void closeFrame() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUILauncher.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUILauncher.java new file mode 100644 index 0000000..cdcabb7 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CredentialManagerUILauncher.java
@@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static javax.swing.SwingUtilities.invokeLater; + +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + +/** + * Test launcher for Credential Manager GUI (so it does not have to be + * launched from Taverna). + * + * @author Alexandra Nenadic + */ +public class CredentialManagerUILauncher extends JFrame { + private static final long serialVersionUID = 2079805060170251148L; + + private final ImageIcon launchCMIcon = new ImageIcon( + CredentialManagerUILauncher.class + .getResource("/images/cred_manager.png")); + + public CredentialManagerUILauncher() { + JPanel jpLaunch = new JPanel(); + jpLaunch.setPreferredSize(new Dimension(300, 120)); + + JLabel jlLaunch = new JLabel("T2: Launch Credential Manager GUI"); + + JButton jbLaunch = new JButton(); + jbLaunch.setIcon(launchCMIcon); + jbLaunch.setToolTipText("Launches Credential Manager"); + jbLaunch.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + CredentialManagerUI cmGUI = new CredentialManagerUI(null, null); + if (cmGUI != null) + cmGUI.setVisible(true); + } + }); + + jpLaunch.add(jlLaunch); + jpLaunch.add(jbLaunch); + + getContentPane().add(jpLaunch, CENTER); + + // Handle application close + setDefaultCloseOperation(EXIT_ON_CLOSE); + + pack(); + + // Centre the frame in the centre of the desktop + setLocationRelativeTo(null); + // Set the frame's title + setTitle("Credential Manager GUI Launcher"); + setVisible(true); + } + + /** + * Launcher for the Credential Manager GUI. + */ + public static void main(String[] args) { + // Create and show GUI on the event handler thread + invokeLater(new Runnable(){ + @Override + public void run() { + new CredentialManagerUILauncher(); + } + }); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CryptoFileFilter.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CryptoFileFilter.java new file mode 100644 index 0000000..d58aa8a --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/CryptoFileFilter.java
@@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import javax.swing.filechooser.FileFilter; + +/** + * File filter for filtering against various file extensions. Crypto files + * normally contain a private key (and optionally its certificate chain) or a + * public key certificate (and optionally its certificate chain). + * + * .p12 or .pfx are PKCS #12 keystore files containing private key and its + * public key (+cert chain); .pem are ASN.1 PEM-encoded files containing one (or + * more concatenated) public key certificate(s); .der are ASN.1 DER-encoded + * files containing one public key certificate; .cer are CER-encoded files + * containing one ore more DER-encoded certificates; .crt files are either + * encoded as binary DER or as ASCII PEM. .p7 and .p7c are PKCS #7 certificate + * chain files (i.e. SignedData structure without data, just certificate(s)). + */ +public class CryptoFileFilter extends FileFilter { + // Description of the filter + private String description; + + // Array of file extensions to filter against + private List<String> exts; + + public CryptoFileFilter(String[] extList, String desc) { + exts = Arrays.asList(extList); + this.description = desc; + } + + @Override + public boolean accept(File file) { + if (file.isDirectory()) + return true; + if (file.isFile()) + for (String ext : exts) + if (file.getName().toLowerCase().endsWith(ext)) + return true; + return false; + } + + public void setDescription(String desc) { + this.description = desc; + } + + @Override + public String getDescription() { + return this.description; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetMasterPasswordDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetMasterPasswordDialog.java new file mode 100644 index 0000000..b6f623e --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetMasterPasswordDialog.java
@@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static javax.swing.BoxLayout.Y_AXIS; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Dialog used for getting a master password for Credential Manager from the + * users. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class GetMasterPasswordDialog extends NonBlockedHelpEnabledDialog { + /** Password entry field */ + private JPasswordField passwordField; + /** The entered password */ + private String password = null; + /** Text giving user the instructions what to do in the dialog */ + private String instructions; + + public GetMasterPasswordDialog(String instructions) { + super((Frame) null, "Enter master password", true); + this.instructions = instructions; + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JLabel instructionsLabel = new JLabel(instructions); + // instructionsLabel.setFont(new Font(null, Font.PLAIN, 11)); + + JPanel instructionsPanel = new JPanel(); + instructionsPanel.setLayout(new BoxLayout(instructionsPanel, Y_AXIS)); + instructionsPanel.add(instructionsLabel); + instructionsPanel.setBorder(new EmptyBorder(10, 5, 10, 0)); + + JLabel passwordLabel = new JLabel("Password"); + passwordLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + + passwordField = new JPasswordField(15); + JPanel passwordPanel = new JPanel(new GridLayout(1, 1, 5, 5)); + passwordPanel.add(passwordLabel); + passwordPanel.add(passwordField); + + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + mainPanel.add(instructionsPanel, NORTH); + mainPanel.add(passwordPanel, CENTER); + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okPressed(); + } + }); + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + getContentPane().add(mainPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + getRootPane().setDefaultButton(okButton); + pack(); + } + + /** + * Get the password entered in the dialog. + */ + public String getPassword() { + return password; + } + + /** + * Check that the entered password is not empty and store the entered + * password. + */ + private boolean checkPassword() { + password = new String(passwordField.getPassword()); + + if (password.isEmpty()) { + showMessageDialog(this, "The password cannot be empty", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + + return true; + } + + private void okPressed() { + if (checkPassword()) + closeDialog(); + } + + private void cancelPressed() { + /* + * Set the password to null as it might have changed in the meantime if + * user entered something then cancelled. + */ + password = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetPasswordDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetPasswordDialog.java new file mode 100644 index 0000000..45a0f88 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/GetPasswordDialog.java
@@ -0,0 +1,168 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Font.PLAIN; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * A general dialog for entering a password. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class GetPasswordDialog extends NonBlockedHelpEnabledDialog { + /** Instructions for user explaining the purpose of the password */ + private String instructions = null; + /* Password entry password field */ + private JPasswordField passwordField; + /* Stores the password entered */ + private String password = null; + + public GetPasswordDialog(JFrame parent, String title, boolean modal, + String instr) { + super(parent, title, modal); + instructions = instr; + initComponents(); + } + + public GetPasswordDialog(JDialog parent, String title, boolean modal, + String instr) { + super(parent, title, modal); + instructions = instr; + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JLabel passwordLabel = new JLabel("Password"); + passwordField = new JPasswordField(15); + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okPressed(); + } + }); + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + + JLabel instructionsLabel; // Instructions + if (instructions != null) { + instructionsLabel = new JLabel(instructions); + instructionsLabel.setFont(new Font(null, PLAIN, 11)); + instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(instructionsLabel, NORTH); + } + + JPanel passwordPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + passwordPanel.add(passwordLabel); + passwordPanel.add(passwordField); + passwordPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + getContentPane().add(passwordPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + getRootPane().setDefaultButton(okButton); + pack(); + } + + /** + * Get the password entered in the dialog. + */ + public String getPassword() { + return password; + } + + /** + * Check that the password entered is not empty and store the entered + * password. + */ + private boolean checkPassword() { + password = new String(passwordField.getPassword()); + + if (password.isEmpty()) { + showMessageDialog(this, "The password cannot be empty", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + + return true; + } + + private void okPressed() { + if (checkPassword()) + closeDialog(); + } + + private void cancelPressed() { + password = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/KeyPairsTableModel.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/KeyPairsTableModel.java new file mode 100644 index 0000000..712cdf6 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/KeyPairsTableModel.java
@@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.KEYSTORE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.KEY_PAIR_ENTRY_TYPE; + +import java.util.TreeMap; + +import javax.swing.JFrame; +import javax.swing.table.AbstractTableModel; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.security.credentialmanager.KeystoreChangedEvent; + +import org.apache.log4j.Logger; + +/** + * The table model used to display the Keystore's key pair entries. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class KeyPairsTableModel extends AbstractTableModel implements Observer<KeystoreChangedEvent> { + private static final Logger logger = Logger.getLogger(KeyPairsTableModel.class); + + /** Column names*/ + private String[] columnNames; + /** Table data*/ + private Object[][] data; + private CredentialManager credManager; + + public KeyPairsTableModel(CredentialManager credentialManager) { + credManager = credentialManager; + + if (credManager == null) { + /* Failed to instantiate Credential Manager - warn the user and exit */ + String sMessage = "Failed to instantiate Credential Manager. "; + logger.error("CM GUI: " + sMessage); + showMessageDialog(new JFrame(), sMessage, + ERROR_TITLE, ERROR_MESSAGE); + return; + } + + data = new Object[0][0]; + columnNames = new String[] { + "Entry Type", // type of the Keystore entry + "Owner", // owner's common name + "Issuer", // issuer's common name + "Serial Number", // public key certificate's serial number + "Last Modified", // last modified date of the entry + "URLs", // the invisible column holding the list of URLs associated with this entry + "Alias" // the invisible column holding the actual alias in the Keystore + }; + + try { + load(); + } catch (CMException cme) { + String sMessage = "Failed to load key pairs"; + logger.error(sMessage, cme); + showMessageDialog(new JFrame(), sMessage, + ERROR_TITLE, ERROR_MESSAGE); + return; + } + + // Start observing changes to the Keystore + credManager.addObserver(this); + } + + /** + * Load the table model with the key pair entries from the Keystore. + */ + public void load() throws CMException { + // Place key pair entries' aliases in a tree map to sort them + TreeMap<String, String> sortedAliases = new TreeMap<>(); + + for (String alias: credManager.getAliases(KEYSTORE)) + /* + * We are only interested in key pair entries here. + * + * Alias for such entries is constructed as + * "keypair#<CERT_SERIAL_NUMBER>#<CERT_COMMON_NAME>" where + */ + if (alias.startsWith("keypair#")) + sortedAliases.put(alias, alias); + + // Create one table row for each key pair entry + data = new Object[sortedAliases.size()][7]; + + /* + * Iterate through the sorted aliases (if any), retrieving the key pair + * entries and populating the table model + */ + int iCnt = 0; + for (String alias : sortedAliases.values()) { + /* + * Populate the type column - it is set with an integer but a custom + * cell renderer will cause a suitable icon to be displayed + */ + data[iCnt][0] = KEY_PAIR_ENTRY_TYPE; + + /* + * Split the alias string to extract owner, issuer and serial number + * alias = + * "keypair#"<SUBJECT_COMMON_NAME>"#"<ISSUER_COMMON_NAME>"#"<SERIAL_NUMBER> + */ + String[] aliasComponents = alias.split("#"); + + // Populate the owner column extracted from the alias + data[iCnt][1] = aliasComponents[1]; + + // Populate the issuer column extracted from the alias + data[iCnt][2] = aliasComponents[2]; + + // Populate the serial number column extracted from the alias + data[iCnt][3] = aliasComponents[3]; + + // Populate the modified date column ("UBER" keystore type supports creation date) + //data[iCnt][4] = credManager.getEntryCreationDate(CredentialManager.KEYSTORE, alias); + + // Populate the invisible URLs list column + //data[iCnt][5] = credManager.getServiceURLsForKeyPair(alias); + + // Populate the invisible alias column + data[iCnt][6] = alias; + + iCnt++; + } + + fireTableDataChanged(); + } + + /** + * Get the number of columns in the table. + */ + @Override + public int getColumnCount() { + return columnNames.length; + } + + /** + * Get the number of rows in the table. + */ + @Override + public int getRowCount() { + return data.length; + } + + /** + * Get the name of the column at the given position. + */ + @Override + public String getColumnName(int iCol) { + return columnNames[iCol]; + } + + /** + * Get the cell value at the given row and column position. + */ + @Override + public Object getValueAt(int iRow, int iCol) { + return data[iRow][iCol]; + } + + /** + * Get the class at of the cells at the given column position. + */ + @Override + public Class<? extends Object> getColumnClass(int iCol) { + return getValueAt(0, iCol).getClass(); + } + + /** + * Is the cell at the given row and column position editable? + */ + @Override + public boolean isCellEditable(int iRow, int iCol) { + // The table is always read-only + return false; + } + + @Override + public void notify(Observable<KeystoreChangedEvent> sender, + KeystoreChangedEvent message) throws Exception { + // reload the table + if (message.keystoreType.equals(KEYSTORE)) + load(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewEditPasswordEntryDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewEditPasswordEntryDialog.java new file mode 100644 index 0000000..3e80f8f --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewEditPasswordEntryDialog.java
@@ -0,0 +1,397 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.WEST; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.JPanel; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; + +import org.apache.log4j.Logger; + +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Dialog used for editing or entering new service URI, username or password for + * a password entry. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class NewEditPasswordEntryDialog extends NonBlockedHelpEnabledDialog +{ + private static final Logger logger = Logger + .getLogger(NewEditPasswordEntryDialog.class); + /** 'Edit' mode constant - the dialog is in the 'edit' entry mode */ + private static final String EDIT_MODE = "EDIT"; + /** 'New' mode constant - the dialog is in the 'new' entry mode */ + private static final String NEW_MODE = "NEW"; + + /** + * Mode of this dialog - {@link #NEW_MODE} for entering new password entry + * and {@link #EDIT_MODE} for editting an existing password entry + */ + String mode; + /** Service URI field */ + private JTextField serviceURIField; + /** Username field */ + private JTextField usernameField; + /** First password entry field */ + private JPasswordField passwordField; + /** Password confirmation entry field */ + private JPasswordField passwordConfirmField; + /** Stores service URI entered */ + private URI serviceURI; + /** Stores previous service URI for {@link #EDIT_MODE} */ + private URI serviceURIOld; + /** Stores username entered */ + private String username; + /** Stores password entered*/ + private String password; + private CredentialManager credentialManager; + + public NewEditPasswordEntryDialog(JFrame parent, String title, + boolean modal, URI currentURI, String currentUsername, + String currentPassword, CredentialManager credentialManager) { + super(parent, title, modal); + serviceURI = currentURI; + username = currentUsername; + password = currentPassword; + this.credentialManager = credentialManager; + if (serviceURI == null && username == null && password == null) { + // if passed values are all null + mode = NEW_MODE; // dialog is for entering a new password entry + } else { + mode = EDIT_MODE; // dialog is for editing an existing entry + serviceURIOld = currentURI; + } + initComponents(); + } + + public NewEditPasswordEntryDialog(JDialog parent, String title, + boolean modal, URI currentURI, String currentUsername, + String currentPassword, CredentialManager credentialManager) { + super(parent, title, modal); + serviceURI = currentURI; + username = currentUsername; + password = currentPassword; + this.credentialManager = credentialManager; + if (serviceURI == null && username == null && password == null) { + // if passed values are all null + mode = NEW_MODE; // dialog is for entering new password entry + } else { + mode = EDIT_MODE; // dialog is for editing existing entry + serviceURIOld = currentURI; + } + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JLabel serviceURILabel = new JLabel("Service URI"); + serviceURILabel.setBorder(new EmptyBorder(0,5,0,0)); + + JLabel usernameLabel = new JLabel("Username"); + usernameLabel.setBorder(new EmptyBorder(0,5,0,0)); + + JLabel passwordLabel = new JLabel("Password"); + passwordLabel.setBorder(new EmptyBorder(0,5,0,0)); + + JLabel passwordConfirmLabel = new JLabel("Confirm password"); + passwordConfirmLabel.setBorder(new EmptyBorder(0,5,0,0)); + + serviceURIField = new JTextField(); + //jtfServiceURI.setBorder(new EmptyBorder(0,0,0,5)); + + usernameField = new JTextField(15); + //jtfUsername.setBorder(new EmptyBorder(0,0,0,5)); + + passwordField = new JPasswordField(15); + //jpfFirstPassword.setBorder(new EmptyBorder(0,0,0,5)); + + passwordConfirmField = new JPasswordField(15); + //jpfConfirmPassword.setBorder(new EmptyBorder(0,0,0,5)); + + //If in EDIT_MODE - populate the fields with current values + if (mode.equals(EDIT_MODE)) { + serviceURIField.setText(serviceURI.toASCIIString()); + usernameField.setText(username); + passwordField.setText(password); + passwordConfirmField.setText(password); + } + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okPressed(); + } + }); + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + + JPanel passwordPanel = new JPanel(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.weighty = 0.0; + + gbc.weightx = 0.0; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 0); + passwordPanel.add(serviceURILabel, gbc); + + gbc.weightx = 1.0; + gbc.gridx = 1; + gbc.gridy = 0; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 5); + passwordPanel.add(serviceURIField, gbc); + + gbc.weightx = 0.0; + gbc.gridx = 0; + gbc.gridy = 1; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 0); + passwordPanel.add(usernameLabel, gbc); + + gbc.weightx = 1.0; + gbc.gridx = 1; + gbc.gridy = 1; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 5); + passwordPanel.add(usernameField, gbc); + + gbc.weightx = 0.0; + gbc.gridx = 0; + gbc.gridy = 2; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 0); + passwordPanel.add(passwordLabel, gbc); + + gbc.weightx = 1.0; + gbc.gridx = 1; + gbc.gridy = 2; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 5); + passwordPanel.add(passwordField, gbc); + + gbc.weightx = 0.0; + gbc.gridx = 0; + gbc.gridy = 3; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 0); + passwordPanel.add(passwordConfirmLabel, gbc); + + gbc.weightx = 1.0; + gbc.gridx = 1; + gbc.gridy = 3; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 5); + passwordPanel.add(passwordConfirmField, gbc); + + passwordPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, + 10), new EtchedBorder())); + + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + getContentPane().add(passwordPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + //setResizable(false); + getRootPane().setDefaultButton(okButton); + pack(); + } + + /** + * Get the username entered in the dialog. + */ + public String getUsername() { + return username; + } + + /** + * Get the service URI entered in the dialog. + */ + public URI getServiceURI() { + return serviceURI; + } + + /** + * Get the password entered in the dialog. + */ + public String getPassword() { + return password; + } + + /** + * Checks that the user has entered a non-empty service URI, a non-empty + * username, a non-empty password and that an entry with the same URI + * already does not already exist in the Keystore. Store the new password. + */ + private boolean checkControls() { + String serviceURIString = new String(serviceURIField.getText()); + if (serviceURIString.isEmpty()) { + showMessageDialog(this, "Service URI cannot be empty", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + try { + serviceURI = new URI(serviceURIString); + } catch (URISyntaxException e) { + showMessageDialog(this, "Service URI is not a valid URI", + WARN_TITLE, WARNING_MESSAGE); + return false; + } + + username = new String(usernameField.getText()); + if (username.isEmpty()) { + showMessageDialog(this, "Username cannot be empty", WARN_TITLE, + WARNING_MESSAGE); + return false; + } + + String firstPassword = new String(passwordField.getPassword()); + String confirmPassword = new String(passwordConfirmField.getPassword()); + + if (!firstPassword.equals(confirmPassword)) { + // passwords do not match + showMessageDialog(this, "Passwords do not match", WARN_TITLE, + WARNING_MESSAGE); + return false; + } + if (firstPassword.isEmpty()) { + // passwords match but are empty + showMessageDialog(this, "Password cannot be empty", WARN_TITLE, + WARNING_MESSAGE); + return false; + } + + // passwords the same and non-empty + password = firstPassword; + + // Check if the entered service URL is already associated with another password entry in the Keystore + List<URI> uriList = null; + try { + uriList = credentialManager.getServiceURIsForAllUsernameAndPasswordPairs(); + } catch (CMException cme) { + // Failed to instantiate Credential Manager - warn the user and exit + String exMessage = "Failed to instantiate Credential Manager to check for duplicate service URIs."; + logger.error(exMessage, cme); + showMessageDialog(new JFrame(), exMessage, ERROR_TITLE, + ERROR_MESSAGE); + return false; + } + + if (uriList != null) { // should not be null really (although can be empty). Check anyway. + if (mode.equals(EDIT_MODE)) // edit mode + // Remove the current entry's service URI from the list + uriList.remove(serviceURIOld); + + if (uriList.contains(serviceURI)) { // found another entry for this service URI + // Warn the user and exit + showMessageDialog( + this, + "The entered service URI is already associated with another password entry", + ALERT_TITLE, WARNING_MESSAGE); + return false; + } + } + + return true; + } + + private void okPressed() { + if (checkControls()) + closeDialog(); + } + + private void cancelPressed() { + // Set all fields to null to indicate that cancel button was pressed + serviceURI = null; + username = null; + password = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewKeyPairEntryDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewKeyPairEntryDialog.java new file mode 100644 index 0000000..36e3015 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewKeyPairEntryDialog.java
@@ -0,0 +1,304 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.BorderLayout.WEST; +import static java.awt.Font.PLAIN; +import static javax.swing.BoxLayout.Y_AXIS; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.ListSelectionModel.SINGLE_SELECTION; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Allows the user import a key pair from a PKCS #12 file (keystore). + */ +@SuppressWarnings("serial") +class NewKeyPairEntryDialog extends NonBlockedHelpEnabledDialog { + //private static final Logger logger = Logger.getLogger(NewKeyPairEntryDialog.class); + + /** List of key pairs available for import */ + private JList<String> keyPairsJList; + /** PKCS #12 keystore */ + private KeyStore pkcs12KeyStore; + /** Private key part of the key pair chosen by the user for import */ + private Key privateKey; + /** Certificate chain part of the key pair chosen by the user for import */ + private Certificate[] certificateChain; + /** Key pair alias to be used for this entry in the Keystore */ + private String alias; + private final DistinguishedNameParser dnParser; + + public NewKeyPairEntryDialog(JFrame parent, String title, boolean modal, + KeyStore pkcs12KeyStore, DistinguishedNameParser dnParser) + throws CMException { + super(parent, title, modal); + this.pkcs12KeyStore = pkcs12KeyStore; + this.dnParser = dnParser; + initComponents(); + } + + public NewKeyPairEntryDialog(JDialog parent, String title, boolean modal, + KeyStore pkcs12KeyStore, DistinguishedNameParser dnParser) + throws CMException { + super(parent, title, modal); + this.pkcs12KeyStore = pkcs12KeyStore; + this.dnParser = dnParser; + initComponents(); + } + + /** + * Get the private part of the key pair. + */ + public Key getPrivateKey() { + return privateKey; + } + + /** + * Get the certificate chain part of the key pair. + */ + public Certificate[] getCertificateChain() { + return certificateChain; + } + + /** + * Get the keystore alias of the key pair. + */ + public String getAlias() { + return alias; + } + + private void initComponents() throws CMException { + // Instructions + JLabel instructionsLabel = new JLabel("Select a key pair to import:"); + instructionsLabel.setFont(new Font(null, PLAIN, 11)); + instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); + JPanel instructionsPanel = new JPanel(new BorderLayout()); + instructionsPanel.add(instructionsLabel, WEST); + + // Import button + final JButton importButton = new JButton("Import"); + importButton.setEnabled(false); + importButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + importPressed(); + } + }); + + // Certificate details button + final JButton certificateDetailsButton = new JButton("Details"); + certificateDetailsButton.setEnabled(false); + certificateDetailsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + certificateDetailsPressed(); + } + }); + + // List to hold keystore's key pairs + keyPairsJList = new JList<>(); + keyPairsJList.setSelectionMode(SINGLE_SELECTION); + keyPairsJList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent evt) { + boolean enabled = keyPairsJList.getSelectedIndex() >= 0; + importButton.setEnabled(enabled); + certificateDetailsButton.setEnabled(enabled); + } + }); + + // Put the key list into a scroll pane + JScrollPane keyPairsScrollPane = new JScrollPane(keyPairsJList, + VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + keyPairsScrollPane.getViewport().setBackground( + keyPairsJList.getBackground()); + + JPanel keyPairsPanel = new JPanel(); + keyPairsPanel.setLayout(new BoxLayout(keyPairsPanel, Y_AXIS)); + keyPairsPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + instructionsPanel.setAlignmentY(LEFT_ALIGNMENT); + keyPairsPanel.add(instructionsPanel); + keyPairsScrollPane.setAlignmentY(LEFT_ALIGNMENT); + keyPairsPanel.add(keyPairsScrollPane); + + // Cancel button + final JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + buttonsPanel.add(certificateDetailsButton); + buttonsPanel.add(importButton); + buttonsPanel.add(cancelButton); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(keyPairsPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + // Populate the list + populateKeyPairList(); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + getRootPane().setDefaultButton(importButton); + pack(); + } + + /** + * Populate the key pair list with the PKCS #12 keystore's key pair aliases. + */ + private void populateKeyPairList() throws CMException { + try { + List<String> keyPairAliases = new ArrayList<>(); + + Enumeration<String> aliases = pkcs12KeyStore.aliases(); + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + + if (pkcs12KeyStore.isKeyEntry(alias)) { + pkcs12KeyStore.getKey(alias, new char[] {}); + Certificate[] certs = pkcs12KeyStore + .getCertificateChain(alias); + if (certs != null && certs.length != 0) + keyPairAliases.add(alias); + } + } + + if (!keyPairAliases.isEmpty()) { + keyPairsJList.setListData(keyPairAliases.toArray(new String[0])); + keyPairsJList.setSelectedIndex(0); + } else + // No key pairs were found - warn the user + showMessageDialog(this, + "No private key pairs were found in the file", + ALERT_TITLE, WARNING_MESSAGE); + } catch (GeneralSecurityException ex) { + throw new CMException("Problem occured while reading the PKCS #12 file.", + ex); + } + } + + /** + * Display the selected key pair's certificate. + */ + private void certificateDetailsPressed() { + try { + String alias = (String) keyPairsJList.getSelectedValue(); + + // Convert the certificate object into an X509Certificate object. + X509Certificate cert = dnParser.convertCertificate(pkcs12KeyStore + .getCertificate(alias)); + + ViewCertDetailsDialog viewCertificateDialog = new ViewCertDetailsDialog( + this, "Certificate details", true, (X509Certificate) cert, + null, dnParser); + viewCertificateDialog.setLocationRelativeTo(this); + viewCertificateDialog.setVisible(true); + } catch (Exception ex) { + showMessageDialog(this, + "Failed to obtain certificate details to show", + ALERT_TITLE, WARNING_MESSAGE); + closeDialog(); + } + } + + public void importPressed() { + String alias = (String) keyPairsJList.getSelectedValue(); + try { + privateKey = pkcs12KeyStore.getKey(alias, new char[] {}); + certificateChain = pkcs12KeyStore.getCertificateChain(alias); + this.alias = alias; + } catch (Exception ex) { + showMessageDialog( + this, + "Failed to load the private key and certificate chain from the PKCS #12 file.", + ERROR_TITLE, ERROR_MESSAGE); + } + + closeDialog(); + } + + public void cancelPressed() { + /* + * Set everything to null, just in case some of the values have been set + * previously and the user pressed 'cancel' after that. + */ + privateKey = null; + certificateChain = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } + +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewTrustCertsDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewTrustCertsDialog.java new file mode 100644 index 0000000..26854ec --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/NewTrustCertsDialog.java
@@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.BorderLayout.WEST; +import static java.awt.Font.PLAIN; +import static javax.security.auth.x500.X500Principal.RFC2253; +import static javax.swing.BoxLayout.Y_AXIS; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ALERT_TITLE; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.security.cert.X509Certificate; +import java.util.ArrayList; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.security.credentialmanager.ParsedDistinguishedName; +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Allows the user to import one or more trusted certificates from a file. + */ +@SuppressWarnings("serial") +public class NewTrustCertsDialog extends NonBlockedHelpEnabledDialog { + private JList<String> trustedCertsJList; + /** List of trusted certs read from the file and available for import */ + private ArrayList<X509Certificate> availableTrustedCerts = new ArrayList<>(); + /** List of trusted certs selected for import */ + private ArrayList<X509Certificate> selectedTrustedCerts; + private final DistinguishedNameParser dnParser; + + public NewTrustCertsDialog(JFrame parent, String title, boolean modal, + ArrayList<X509Certificate> lCerts, DistinguishedNameParser dnParser) { + super(parent, title, modal); + availableTrustedCerts = lCerts; + this.dnParser = dnParser; + initComponents(); + } + + public NewTrustCertsDialog(JDialog parent, String title, boolean modal, + ArrayList<X509Certificate> lCerts, DistinguishedNameParser dnParser) { + super(parent, title, modal); + availableTrustedCerts = lCerts; + this.dnParser = dnParser; + initComponents(); + } + + private void initComponents() { + // Instructions + JLabel instructionsLabel = new JLabel( + "Select one or more certificates for import:"); + instructionsLabel.setFont(new Font(null, PLAIN, 11)); + instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); + JPanel instructionsPanel = new JPanel(new BorderLayout()); + instructionsPanel.add(instructionsLabel, WEST); + + // Import button + final JButton importButton = new JButton("Import"); + importButton.setEnabled(false); + importButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + importPressed(); + } + }); + + // Certificate details button + final JButton certificateDetailsButton = new JButton( + "Certificate Details"); + certificateDetailsButton.setEnabled(false); + certificateDetailsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + certificateDetailsPressed(); + } + }); + + // List with trusted certs' aliases + trustedCertsJList = new JList<>(); + trustedCertsJList.setSelectionMode(MULTIPLE_INTERVAL_SELECTION); + trustedCertsJList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent evt) { + boolean enabled = trustedCertsJList.getSelectedIndex() >= 0; + importButton.setEnabled(enabled); + certificateDetailsButton.setEnabled(enabled); + } + }); + // Populate the list - get the certificate subjects' CNs + ArrayList<String> cns = new ArrayList<>(); + for (int i = 0; i < availableTrustedCerts.size(); i++) { + String subjectDN = ((X509Certificate) availableTrustedCerts.get(i)) + .getSubjectX500Principal().getName(RFC2253); + ParsedDistinguishedName parsedDN = dnParser.parseDN(subjectDN); + String subjectCN = parsedDN.getCN(); + cns.add(i, subjectCN); + } + trustedCertsJList.setListData(cns.toArray(new String[0])); + trustedCertsJList.setSelectedIndex(0); + + // Put the list into a scroll pane + JScrollPane trustedCertsScrollPanel = new JScrollPane( + trustedCertsJList, VERTICAL_SCROLLBAR_AS_NEEDED, + HORIZONTAL_SCROLLBAR_AS_NEEDED); + trustedCertsScrollPanel.getViewport().setBackground( + trustedCertsJList.getBackground()); + + JPanel trustedCertsPanel = new JPanel(); + trustedCertsPanel.setLayout(new BoxLayout(trustedCertsPanel, Y_AXIS)); + trustedCertsPanel.setBorder(new CompoundBorder(new CompoundBorder( + new EmptyBorder(5, 5, 5, 5), new EtchedBorder()), + new EmptyBorder(5, 5, 5, 5))); + + instructionsPanel.setAlignmentY(LEFT_ALIGNMENT); + trustedCertsPanel.add(instructionsPanel); + trustedCertsScrollPanel.setAlignmentY(LEFT_ALIGNMENT); + trustedCertsPanel.add(trustedCertsScrollPanel); + certificateDetailsButton.setAlignmentY(RIGHT_ALIGNMENT); + trustedCertsPanel.add(certificateDetailsButton); + + // Cancel button + final JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + + JPanel jpButtons = new JPanel(new FlowLayout(FlowLayout.CENTER)); + jpButtons.add(importButton); + jpButtons.add(cancelButton); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(trustedCertsPanel, CENTER); + getContentPane().add(jpButtons, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + getRootPane().setDefaultButton(importButton); + pack(); + } + + /** + * Shows the selected key pair's certificate. + */ + private void certificateDetailsPressed() { + try { + int i = trustedCertsJList.getSelectedIndex(); + + X509Certificate cert = (X509Certificate) availableTrustedCerts + .get(i); + + ViewCertDetailsDialog viewCertificateDialog = new ViewCertDetailsDialog( + this, "Certificate details", true, cert, null, dnParser); + viewCertificateDialog.setLocationRelativeTo(this); + viewCertificateDialog.setVisible(true); + } catch (Exception ex) { + showMessageDialog(this, + "Failed to obtain certificate details to show", + ALERT_TITLE, WARNING_MESSAGE); + closeDialog(); + } + } + + /** + * Get the trusted certificates selected for import. + */ + public ArrayList<X509Certificate> getTrustedCertificates() { + return selectedTrustedCerts; + } + + /** + * Store the selected trusted certs. + */ + public void importPressed() { + int[] selectedValues = trustedCertsJList.getSelectedIndices(); + selectedTrustedCerts = new ArrayList<>(); + for (int i = 0; i < selectedValues.length; i++) + selectedTrustedCerts.add(availableTrustedCerts + .get(selectedValues[i])); + closeDialog(); + } + + public void cancelPressed() { + /* + * Set selectedTrustCerts to null to indicate that user has cancelled + * the import + */ + selectedTrustedCerts = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/PasswordsTableModel.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/PasswordsTableModel.java new file mode 100644 index 0000000..9715fc6 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/PasswordsTableModel.java
@@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.KEYSTORE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.PASSWORD_ENTRY_TYPE; + +import java.net.URI; +import java.util.TreeMap; + +import javax.swing.JFrame; +import javax.swing.table.AbstractTableModel; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.security.credentialmanager.KeystoreChangedEvent; +import net.sf.taverna.t2.security.credentialmanager.UsernamePassword; + +import org.apache.log4j.Logger; + +/** + * The table model used to display the Keystore's username/password pair + * entries. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class PasswordsTableModel extends AbstractTableModel implements + Observer<KeystoreChangedEvent> { + private static final Logger logger = Logger + .getLogger(PasswordsTableModel.class); + + // Column names + private String[] columnNames; + // Table data + private Object[][] data; + private CredentialManager credManager; + + public PasswordsTableModel(CredentialManager credentialManager) { + credManager = credentialManager; + if (credentialManager == null) { + // Failed to instantiate Credential Manager - warn the user and exit + String sMessage = "Failed to instantiate Credential Manager. "; + logger.error("CM GUI: " + sMessage); + showMessageDialog(new JFrame(), sMessage, ERROR_TITLE, + ERROR_MESSAGE); + return; + } + + data = new Object[0][0]; + columnNames = new String[] { "Entry Type", // type of the Keystore entry + "Service URL", // the service url, part of the actual alias in + // the Keystore + "Username", // username for the service, part of the password + // entry in the Keystore + "Last Modified", // last modified date of the entry + "Password", // the invisible column holding the password value + // of the password entry in the Keystore + "Alias" // the invisible column holding the Keystore alias of + // the entry + }; + + try { + load(); + } catch (CMException cme) { + String sMessage = "Failed to load username and password pairs"; + logger.error(sMessage); + showMessageDialog(new JFrame(), sMessage, ERROR_TITLE, + ERROR_MESSAGE); + return; + } + + // Start observing changes to the Keystore + credManager.addObserver(this); + } + + /** + * Load the PasswordsTableModel with the password entries from the Keystore. + */ + public void load() throws CMException { + // Place password entries' aliases in a tree map to sort them + TreeMap<String, String> aliases = new TreeMap<>(); + + for (String alias : credManager.getAliases(KEYSTORE)) + /* + * We are only interested in username/password entries here. Alias + * for such entries is constructed as "password#"<SERVICE_URL> where + * service URL is the service this username/password pair is to be + * used for. + */ + if (alias.startsWith("password#")) + aliases.put(alias, alias); + + // Create one table row for each password entry + data = new Object[aliases.size()][6]; + + /* + * Iterate through the sorted aliases, retrieving the password entries + * and populating the table model + */ + int iCnt = 0; + for (String alias : aliases.values()) { + /* + * Populate the type column - it is set with an integer but a custom + * cell renderer will cause a suitable icon to be displayed + */ + data[iCnt][0] = PASSWORD_ENTRY_TYPE; + + /* + * Populate the service URL column as a substring of alias from the + * first occurrence of '#' till the end of the string + */ + String serviceURL = alias.substring(alias.indexOf('#') + 1); + data[iCnt][1] = serviceURL; + + /* + * Get the username and password pair from the Keystore. They are + * returned in a single string in format + * <USERNAME><SEPARATOR_CHARACTER><PASSWORD> + */ + UsernamePassword usernamePassword = credManager + .getUsernameAndPasswordForService(URI.create(serviceURL), + false, ""); + String username = usernamePassword.getUsername(); + String password = usernamePassword.getPasswordAsString(); + + // Populate the username column + data[iCnt][2] = username; + + // Populate the last modified date column ("UBER" keystore type + // supports creation date) + // data[iCnt][3] = + // credManager.getEntryCreationDate(CredentialManager.KEYSTORE, + // alias); + + // Populate the invisible password column + data[iCnt][4] = password; + + // Populate the invisible alias column + data[iCnt][5] = alias; + + iCnt++; + } + + fireTableDataChanged(); + } + + /** + * Get the number of columns in the table. + */ + @Override + public int getColumnCount() { + return columnNames.length; + } + + /** + * Get the number of rows in the table. + */ + @Override + public int getRowCount() { + return data.length; + } + + /** + * Get the name of the column at the given position. + */ + @Override + public String getColumnName(int iCol) { + return columnNames[iCol]; + } + + /** + * Get the cell value at the given row and column position. + */ + @Override + public Object getValueAt(int iRow, int iCol) { + return data[iRow][iCol]; + } + + /** + * Get the class at of the cells at the given column position. + */ + @Override + public Class<? extends Object> getColumnClass(int iCol) { + return getValueAt(0, iCol).getClass(); + } + + /** + * Is the cell at the given row and column position editable? + */ + @Override + public boolean isCellEditable(int iRow, int iCol) { + // The table is always read-only + return false; + } + + @Override + public void notify(Observable<KeystoreChangedEvent> sender, + KeystoreChangedEvent message) throws Exception { + // reload the table + if (message.keystoreType.equals(KEYSTORE)) + load(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/SetMasterPasswordDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/SetMasterPasswordDialog.java new file mode 100644 index 0000000..bae6068 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/SetMasterPasswordDialog.java
@@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Font.PLAIN; +import static javax.swing.BoxLayout.Y_AXIS; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.WARN_TITLE; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; + +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Dialog used for user to set a master password for Credential Manager. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class SetMasterPasswordDialog extends NonBlockedHelpEnabledDialog { + /** Password entry field */ + private JPasswordField passwordField; + /** Password confirmation entry field */ + private JPasswordField passwordConfirmField; + /** The entered password */ + private String password = null; + /** Instructions for the user */ + private String instructions; + + public SetMasterPasswordDialog(JFrame parent, String title, boolean modal, + String instructions) { + super(parent, title, modal); + this.instructions = instructions; + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JLabel instructionsLabel = new JLabel(instructions); + instructionsLabel.setFont(new Font(null, PLAIN, 11)); + + JPanel instructionsPanel = new JPanel(); + instructionsPanel.setLayout(new BoxLayout(instructionsPanel, Y_AXIS)); + instructionsPanel.add(instructionsLabel); + instructionsPanel.setBorder(new EmptyBorder(10, 5, 10, 0)); + + JLabel passwordLabel = new JLabel("Master password"); + passwordLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + + JLabel passwordConfirmLabel = new JLabel("Confirm master password"); + passwordConfirmLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + + passwordField = new JPasswordField(15); + passwordConfirmField = new JPasswordField(15); + + JPanel passwordPanel = new JPanel(new GridLayout(2, 2, 5, 5)); + passwordPanel.add(passwordLabel); + passwordPanel.add(passwordField); + passwordPanel.add(passwordConfirmLabel); + passwordPanel.add(passwordConfirmField); + + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, 10), + new EtchedBorder())); + mainPanel.add(instructionsPanel, NORTH); + mainPanel.add(passwordPanel, CENTER); + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okPressed(); + } + }); + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + getContentPane().add(mainPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + getRootPane().setDefaultButton(okButton); + pack(); + } + + public String getPassword() { + return password; + } + + /** + * Check that the user has entered a non-empty password and store the new + * password. + */ + private boolean checkPassword() { + String firstPassword = new String(passwordField.getPassword()); + String confirmPassword = new String(passwordConfirmField.getPassword()); + + if (!firstPassword.equals(confirmPassword)) { + showMessageDialog(this, "The passwords do not match", WARN_TITLE, + WARNING_MESSAGE); + return false; + } + if (firstPassword.isEmpty()) { + // passwords match but are empty + showMessageDialog(this, "The password cannot be empty", WARN_TITLE, + WARNING_MESSAGE); + return false; + } + + // passwords match and not empty + password = firstPassword; + return true; + } + + private void okPressed() { + if (checkPassword()) + closeDialog(); + } + + private void cancelPressed() { + /* + * Set the password to null as it might have changed in the meantime if + * user entered something then cancelled. + */ + password = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableCellRenderer.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableCellRenderer.java new file mode 100644 index 0000000..0eaae99 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableCellRenderer.java
@@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.KEY_PAIR_ENTRY_TYPE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.PASSWORD_ENTRY_TYPE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.TRUST_CERT_ENTRY_TYPE; + +import java.awt.Component; +//import java.text.DateFormat; +//import java.util.Date; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.border.EmptyBorder; +import javax.swing.table.DefaultTableCellRenderer; +//import net.sf.taverna.t2.workbench.ui.credentialmanager.KeyPairsTableModel; +//import net.sf.taverna.t2.workbench.ui.credentialmanager.PasswordsTableModel; +//import net.sf.taverna.t2.workbench.ui.credentialmanager.TrustedCertsTableModel; + +/** + * Custom cell renderer for the cells of the tables displaying + * Keystore/Truststore contents. + * + * @author Alex Nenadic + */ +public class TableCellRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = -3983986682794010259L; + + private final ImageIcon passwordEntryIcon = new ImageIcon( + TableCellRenderer.class.getResource("/images/table/key_entry.png")); + private final ImageIcon keypairEntryIcon = new ImageIcon( + TableCellRenderer.class + .getResource("/images/table/keypair_entry.png")); + private final ImageIcon trustcertEntryIcon = new ImageIcon( + TableCellRenderer.class + .getResource("/images/table/trustcert_entry.png")); + + /** + * Get the rendered cell for the supplied value and column. + */ + @Override + public Component getTableCellRendererComponent(JTable keyStoreTable, + Object value, boolean bIsSelected, boolean bHasFocus, int iRow, + int iCol) { + JLabel cell = (JLabel) super.getTableCellRendererComponent( + keyStoreTable, value, bIsSelected, bHasFocus, iRow, iCol); + + if (value != null) { + // Type column - display an icon representing the type + if (iCol == 0) + configureTypeColumn(value, cell); + // Last Modified column - format date (if date supplied) + /*else if (((keyStoreTable.getModel() instanceof PasswordsTableModel) && (iCol == 3)) || + ((keyStoreTable.getModel() instanceof KeyPairsTableModel) && (iCol == 4))|| + ((keyStoreTable.getModel() instanceof TrustedCertsTableModel) && (iCol == 4))){ + if (value instanceof Date) { + // Include timezone + cell.setText(DateFormat.getDateTimeInstance(DateFormat.MEDIUM, + DateFormat.LONG).format((Date) value)); + } else { + cell.setText(value.toString()); + } + }*/ + // Other columns - just use their text values + else + cell.setText(value.toString()); + } + + cell.setBorder(new EmptyBorder(0, 5, 0, 5)); + return cell; + } + + private void configureTypeColumn(Object value, JLabel cell) { + ImageIcon icon = null; + // The cell is in the first column of Passwords table + if (PASSWORD_ENTRY_TYPE.equals(value)) { + icon = passwordEntryIcon; // key (i.e. password) entry image + } + // The cell is in the first column of Key Pairs table + else if (KEY_PAIR_ENTRY_TYPE.equals(value)) { + icon = keypairEntryIcon; // key pair entry image + } + // The cell is in the first column of Trusted Certificates table + else if (TRUST_CERT_ENTRY_TYPE.equals(value)) { + icon = trustcertEntryIcon; // trust. certificate entry image + } + + cell.setIcon(icon); + cell.setText(""); + cell.setVerticalAlignment(CENTER); + cell.setHorizontalAlignment(CENTER); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableHeaderRenderer.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableHeaderRenderer.java new file mode 100644 index 0000000..8070b98 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TableHeaderRenderer.java
@@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static javax.swing.border.BevelBorder.RAISED; + +import java.awt.Component; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.border.BevelBorder; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.table.DefaultTableCellRenderer; + +import net.sf.taverna.t2.workbench.ui.credentialmanager.KeyPairsTableModel; +import net.sf.taverna.t2.workbench.ui.credentialmanager.PasswordsTableModel; +import net.sf.taverna.t2.workbench.ui.credentialmanager.TableHeaderRenderer; +import net.sf.taverna.t2.workbench.ui.credentialmanager.TrustedCertsTableModel; + +/** + * Custom cell renderer for the headers of the tables displaying + * the Keystore/Truststore contents. + */ +@SuppressWarnings("serial") +public class TableHeaderRenderer extends DefaultTableCellRenderer { + private final ImageIcon entryTypeIcon = new ImageIcon( + TableHeaderRenderer.class + .getResource("/images/table/entry_heading.png")); + + @Override + public Component getTableCellRendererComponent(JTable jtKeyStoreTable, + Object value, boolean bIsSelected, boolean bHasFocus, int iRow, + int iCol) { + // Get header renderer + JLabel header = (JLabel) jtKeyStoreTable.getColumnModel().getColumn(iCol).getHeaderRenderer(); + + // The entry type header contains an icon for every table + if (iCol == 0) { + header.setText(""); + header.setIcon(entryTypeIcon); // entry type icon (header for the first column of the table) + header.setHorizontalAlignment(CENTER); + header.setVerticalAlignment(CENTER); + header.setToolTipText("Entry type"); + } + // All other headers contain text + else { + header.setText((String) value); + header.setHorizontalAlignment(LEFT); + + // Passwords table + if (jtKeyStoreTable.getModel() instanceof PasswordsTableModel){ + if (iCol == 1) //Service URL column + header.setToolTipText("URL of the service username and password will be used for"); + else if (iCol == 2) // Username column + header.setToolTipText("Username for the service"); + } + // Key pairs table + else if (jtKeyStoreTable.getModel() instanceof KeyPairsTableModel) { + if (iCol == 1) // Owner + header.setToolTipText("Certificate's owner"); + else if (iCol == 2) // Issuer + header.setToolTipText("Certificate's issuer"); + else if (iCol == 3) // Serial number + header.setToolTipText("Certificate's serial number"); + } + // Trusted certs table + else if (jtKeyStoreTable.getModel() instanceof TrustedCertsTableModel) { + if (iCol == 1) // Owner + header.setToolTipText("Certificate's owner"); + else if (iCol == 2) // Issuer + header.setToolTipText("Certificate's issuer"); + else if (iCol == 3) // Serial number + header.setToolTipText("Certificate's serial number"); + } + } + header.setBorder(new CompoundBorder(new BevelBorder(RAISED), + new EmptyBorder(0, 5, 0, 5))); + return header; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TrustedCertsTableModel.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TrustedCertsTableModel.java new file mode 100644 index 0000000..5189eb2 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/TrustedCertsTableModel.java
@@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.security.credentialmanager.CredentialManager.KeystoreType.TRUSTSTORE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CMStrings.ERROR_TITLE; +import static net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI.TRUST_CERT_ENTRY_TYPE; + +import java.util.Set; +import java.util.TreeSet; + +import javax.swing.JFrame; +import javax.swing.table.AbstractTableModel; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.security.credentialmanager.KeystoreChangedEvent; + +import org.apache.log4j.Logger; + +/** + * The table model used to display the Keystore's trusted certificate entries. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class TrustedCertsTableModel extends AbstractTableModel implements + Observer<KeystoreChangedEvent> { + private static final Logger logger = Logger + .getLogger(TrustedCertsTableModel.class); + + // Column names + private String[] columnNames; + // Table data + private Object[][] data; + private CredentialManager credManager; + + public TrustedCertsTableModel(CredentialManager credentialManager) { + credManager = credentialManager; + if (credentialManager == null) { + // Failed to instantiate Credential Manager - warn the user and exit + String sMessage = "Failed to instantiate Credential Manager. "; + logger.error("CM GUI: "+ sMessage); + showMessageDialog(new JFrame(), sMessage, ERROR_TITLE, + ERROR_MESSAGE); + return; + } + + data = new Object[0][0]; + columnNames = new String[] { + "Entry Type", // type of the Keystore entry + "Owner", // owner's common name + "Issuer", // issuer's common name + "Serial Number", // public key certificate's serial number + "Last Modified", // last modified date of the entry + "Alias" // the invisible column holding the actual alias in the Keystore + }; + + try { + load(); + } catch (CMException cme) { + String sMessage = "Failed to load trusted certificates"; + logger.error(sMessage); + showMessageDialog(new JFrame(), sMessage, ERROR_TITLE, + ERROR_MESSAGE); + return; + } + + // Start observing changes to the Keystore + credManager.addObserver(this); + } + + /** + * Load the TrustCertsTableModel with trusted certificate entries from the Keystore. + */ + public void load() throws CMException { + /* + * Place trusted certificate entries' aliases in a tree map to sort them + */ + Set<String> aliases = new TreeSet<>(); + for (String alias : credManager.getAliases(TRUSTSTORE)) + /* + * We are only interested in trusted certificate entries here. Alias + * for such entries is constructed as + * "trustedcert#<CERT_SERIAL_NUMBER>#<CERT_COMMON_NAME>" + */ + if (alias.startsWith("trustedcert#")) + aliases.add(alias); + + /* + * Create one table row for each trusted certificate entry Each row has + * 4 fields - type, owner name, last modified data and the invisible + * alias + */ + data = new Object[aliases.size()][6]; + + /* + * Iterate through the sorted aliases, retrieving the trusted + * certificate entries and populating the table model + */ + int i = 0; + for (String alias : aliases) { + /* + * Populate the type column - it is set with an integer but a custom + * cell renderer will cause a suitable icon to be displayed + */ + data[i][0] = TRUST_CERT_ENTRY_TYPE; + + /* + * Split the alias string to extract owner, issuer and serial number + * alias = + * "trustedcert#<CERT_SUBJECT_COMMON_NAME>"#"<CERT_ISSUER_COMMON_NAME>" + * #"<CERT_SERIAL_NUMBER> + */ + String[] aliasComponents = alias.split("#"); + + // Populate the owner column extracted from the alias + data[i][1] = aliasComponents[1]; + + // Populate the issuer column extracted from the alias + data[i][2] = aliasComponents[2]; + + // Populate the serial number column extracted from the alias + data[i][3] = aliasComponents[3]; + + // Populate the modified date column + //data[iCnt][4] = credManager.getEntryCreationDate(CredentialManager.TRUSTSTORE, alias); + + // Populate the invisible alias column + data[i][5] = alias; + + i++; + } + + fireTableDataChanged(); + } + + /** + * Get the number of columns in the table. + */ + @Override + public int getColumnCount() { + return columnNames.length; + } + + /** + * Get the number of rows in the table. + */ + @Override + public int getRowCount() { + return data.length; + } + + /** + * Get the name of the column at the given position. + */ + @Override + public String getColumnName(int iCol) { + return columnNames[iCol]; + } + + /** + * Get the cell value at the given row and column position. + */ + @Override + public Object getValueAt(int iRow, int iCol) { + return data[iRow][iCol]; + } + + /** + * Get the class at of the cells at the given column position. + */ + @Override + public Class<? extends Object> getColumnClass(int iCol) { + return getValueAt(0, iCol).getClass(); + } + + /** + * Is the cell at the given row and column position editable? + */ + @Override + public boolean isCellEditable(int iRow, int iCol) { + // The table is always read-only + return false; + } + + @Override + public void notify(Observable<KeystoreChangedEvent> sender, + KeystoreChangedEvent message) throws Exception { + // reload the table + if (message.keystoreType.equals(TRUSTSTORE)) + load(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewCertDetailsDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewCertDetailsDialog.java new file mode 100644 index 0000000..953ed2d --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewCertDetailsDialog.java
@@ -0,0 +1,509 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Font.BOLD; +import static java.awt.Font.PLAIN; +import static java.awt.GridBagConstraints.LINE_START; +import static javax.security.auth.x500.X500Principal.RFC2253; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.SwingUtilities.invokeLater; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.math.BigInteger; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; + +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.security.credentialmanager.ParsedDistinguishedName; +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Displays the details of a X.509 certificate. + * + * Inspired by the Portlecle tool (http://portecle.sourceforge.net/). and the + * view certificate dialog from Firefox's Certificate Manager. + */ +@SuppressWarnings("serial") +public class ViewCertDetailsDialog extends NonBlockedHelpEnabledDialog { + // Logger + //private static Logger logger = Logger.getLogger(ViewCertDetailsDialog.class); + + /** Stores certificate to display*/ + private X509Certificate cert; + /** Stores list of serviceURLs to display*/ + private ArrayList<String> serviceURLs; + private final DistinguishedNameParser dnParser; + + /** + * Creates new ViewCertDetailsDialog dialog where the parent is a frame. + */ + public ViewCertDetailsDialog(JFrame parent, String title, boolean modal, + X509Certificate crt, ArrayList<String> serviceURLs, + DistinguishedNameParser dnParser) throws CMException { + super(parent, title, modal); + this.cert = crt; + this.serviceURLs = serviceURLs; + this.dnParser = dnParser; + initComponents(); + } + + /** + * Creates new ViewCertDetailsDialog dialog where the parent is a dialog. + */ + public ViewCertDetailsDialog(JDialog parent, String title, boolean modal, + X509Certificate crt, ArrayList<String> urlList, + DistinguishedNameParser dnParser) throws CMException { + super(parent, title, modal); + cert = crt; + serviceURLs = urlList; + this.dnParser = dnParser; + initComponents(); + } + + /** + * Initialise the dialog's GUI components. + * + * @throws CMException + * A problem was encountered getting the certificates' details + */ + private void initComponents() throws CMException { + // Certificate details: + + // Grid Bag Constraints templates for labels (column 1) and + // values (column 2) of certificate details + GridBagConstraints gbcLabel = new GridBagConstraints(); + gbcLabel.gridx = 0; + gbcLabel.ipadx = 20; + gbcLabel.gridwidth = 1; + gbcLabel.gridheight = 1; + gbcLabel.insets = new Insets(2, 15, 2, 2); + gbcLabel.anchor = LINE_START; + + GridBagConstraints gbcValue = new GridBagConstraints(); + gbcValue.gridx = 1; + gbcValue.gridwidth = 1; + gbcValue.gridheight = 1; + gbcValue.insets = new Insets(2, 5, 2, 2); + gbcValue.anchor = LINE_START; + + /* + * Netscape Certificate Type non-critical extension (if any) defines the + * intended uses of the certificate - to make it look like firefox's + * view certificate dialog. From openssl's documentation: "The [above] + * extension is non standard, Netscape specific and largely obsolete. + * Their use in new applications is discouraged." + * + * TODO replace with "basicConstraints, keyUsage and extended key usage + * extensions which are now used instead." + */ +// byte[] intendedUses = cert.getExtensionValue("2.16.840.1.113730.1.1"); //Netscape Certificate Type OID/* +// JLabel jlIntendedUses = null; +// JTextField jtfIntendedUsesValue = null; +// JPanel jpUses = null; +// GridBagConstraints gbc_jpUses = null; +// if (intendedUses != null) +// { +// jlIntendedUses = new JLabel("This certificate has been approved for the following uses:"); +// jlIntendedUses.setFont(new Font(null, Font.BOLD, 11)); +// jlIntendedUses.setBorder(new EmptyBorder(5,5,5,5)); +// +// jtfIntendedUsesValue = new JTextField(45); +// jtfIntendedUsesValue.setText(CMUtils.getIntendedCertificateUses(intendedUses)); +// jtfIntendedUsesValue.setEditable(false); +// jtfIntendedUsesValue.setFont(new Font(null, Font.PLAIN, 11)); +// +// jpUses = new JPanel(new BorderLayout()); +// jpUses.add(jlIntendedUses, BorderLayout.NORTH); +// jpUses.add(jtfIntendedUsesValue, BorderLayout.CENTER); +// JSeparator jsp = new JSeparator(JSeparator.HORIZONTAL); +// jpUses.add(jsp, BorderLayout.SOUTH); +// +// gbc_jpUses = (GridBagConstraints) gbcLabel.clone(); +// gbc_jpUses.gridy = 0; +// gbc_jpUses.gridwidth = 2; //takes two columns +// gbc_jpUses.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets +// +// } + + //Issued To + JLabel jlIssuedTo = new JLabel("Issued To"); + jlIssuedTo.setFont(new Font(null, Font.BOLD, 11)); + GridBagConstraints gbc_jlIssuedTo = (GridBagConstraints) gbcLabel.clone(); + gbc_jlIssuedTo.gridy = 1; + gbc_jlIssuedTo.gridwidth = 2; //takes two columns + gbc_jlIssuedTo.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets + + // Distinguished Name (DN) + String sDN = cert.getSubjectX500Principal().getName(RFC2253); + ParsedDistinguishedName parsedDN = dnParser.parseDN(sDN); + // Extract the CN, O, OU and EMAILADDRESS fields + String sCN = parsedDN.getCN(); + String sOrg = parsedDN.getO(); + String sOU = parsedDN.getOU(); + //String sEMAILADDRESS = CMX509Util.getEmilAddress(); + + // Common Name (CN) + JLabel jlCN = new JLabel("Common Name (CN)"); + jlCN.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlCN = (GridBagConstraints) gbcLabel.clone(); + gbc_jlCN.gridy = 2; + JLabel jlCNValue = new JLabel(sCN); + jlCNValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlCNValue = (GridBagConstraints) gbcValue.clone(); + gbc_jlCNValue.gridy = 2; + + // Organisation (O) + JLabel jlOrg = new JLabel("Organisation (O)"); + jlOrg.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlOrg = (GridBagConstraints) gbcLabel.clone(); + gbc_jlOrg.gridy = 3; + JLabel jlOrgValue = new JLabel(sOrg); + jlOrgValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlOrgValue = (GridBagConstraints) gbcValue.clone(); + gbc_jlOrgValue.gridy = 3; + + // Organisation Unit (OU) + JLabel jlOU = new JLabel("Organisation Unit (OU)"); + jlOU.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlOU = (GridBagConstraints) gbcLabel.clone(); + gbc_jlOU.gridy = 4; + JLabel jlOUValue = new JLabel(sOU); + jlOUValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlOUValue = (GridBagConstraints) gbcValue.clone(); + gbc_jlOUValue.gridy = 4; + + // E-mail Address + //JLabel jlEmail = new JLabel("E-mail Address"); + //jlEmail.setFont(new Font(null, PLAIN, 11)); + //GridBagConstraints gbc_jlEmail = (GridBagConstraints) gbcLabel.clone(); + //gbc_jlEmail.gridy = 5; + //JLabel jlEmailValue = new JLabel(sEMAILADDRESS); + //jlEmailValue.setFont(new Font(null, PLAIN, 11)); + //GridBagConstraints gbc_jlEmailValue = (GridBagConstraints) gbcValue.clone(); + //gbc_jlEmailValue.gridy = 5; + + // Serial Number + JLabel jlSN = new JLabel("Serial Number"); + jlSN.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlSN = (GridBagConstraints) gbcLabel.clone(); + gbc_jlSN.gridy = 6; + JLabel jlSNValue = new JLabel(); + // Get the hexadecimal serial number + StringBuilder strBuff = new StringBuilder(new BigInteger(1, + cert.getSerialNumber().toByteArray()).toString(16).toUpperCase()); + // Place colons at every two hexadecimal characters + if (strBuff.length() > 2) + for (int iCnt = 2; iCnt < strBuff.length(); iCnt += 3) + strBuff.insert(iCnt, ':'); + jlSNValue.setText(strBuff.toString()); + jlSNValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlSNValue = (GridBagConstraints) gbcValue.clone(); + gbc_jlSNValue.gridy = 6; + + // Version + JLabel jlVersion = new JLabel("Version"); + jlVersion.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlVersion = (GridBagConstraints) gbcLabel.clone(); + gbc_jlVersion.gridy = 7; + JLabel jlVersionValue = new JLabel(Integer.toString(cert.getVersion())); + jlVersionValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlVersionValue = (GridBagConstraints) gbcValue.clone(); + gbc_jlVersionValue.gridy = 7; + + // Issued By + JLabel jlIssuedBy = new JLabel("Issued By"); + jlIssuedBy.setFont(new Font(null, BOLD, 11)); + GridBagConstraints gbc_jlIssuedBy = (GridBagConstraints) gbcLabel.clone(); + gbc_jlIssuedBy.gridy = 8; + gbc_jlIssuedBy.gridwidth = 2; //takes two columns + gbc_jlIssuedBy.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets + + // Distinguished Name (DN) + String iDN = cert.getIssuerX500Principal().getName(RFC2253); + parsedDN = dnParser.parseDN(iDN); + // Extract the CN, O and OU fields + String iCN = parsedDN.getCN(); + String iOrg = parsedDN.getO(); + String iOU = parsedDN.getOU(); + + // Common Name (CN) + JLabel jlICN = new JLabel("Common Name (CN)"); + jlICN.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlICN = (GridBagConstraints) gbcLabel.clone(); + gbc_jlICN.gridy = 9; + JLabel jlICNValue = new JLabel(iCN); + jlICNValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlICNValue = (GridBagConstraints) gbcValue + .clone(); + gbc_jlICNValue.gridy = 9; + + // Organisation (O) + JLabel jlIOrg = new JLabel("Organisation (O)"); + jlIOrg.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlIOrg = (GridBagConstraints) gbcLabel.clone(); + gbc_jlIOrg.gridy = 10; + JLabel jlIOrgValue = new JLabel(iOrg); + jlIOrgValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlIOrgValue = (GridBagConstraints) gbcValue + .clone(); + gbc_jlIOrgValue.gridy = 10; + + // Organisation Unit (OU) + JLabel jlIOU = new JLabel("Organisation Unit (OU)"); + jlIOU.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlIOU = (GridBagConstraints) gbcLabel.clone(); + gbc_jlIOU.gridy = 11; + JLabel jlIOUValue = new JLabel(iOU); + jlIOUValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlIOUValue = (GridBagConstraints) gbcValue + .clone(); + gbc_jlIOUValue.gridy = 11; + + // Validity + JLabel jlValidity = new JLabel("Validity"); + jlValidity.setFont(new Font(null, BOLD, 11)); + GridBagConstraints gbc_jlValidity = (GridBagConstraints) gbcLabel + .clone(); + gbc_jlValidity.gridy = 12; + gbc_jlValidity.gridwidth = 2; // takes two columns + gbc_jlValidity.insets = new Insets(5, 5, 5, 5);// has slightly bigger insets + + // Issued On + JLabel jlIssuedOn = new JLabel("Issued On"); + jlIssuedOn.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlIssuedOn = (GridBagConstraints) gbcLabel + .clone(); + gbc_jlIssuedOn.gridy = 13; + JLabel jlIssuedOnValue = new JLabel(cert.getNotBefore().toString()); + jlIssuedOnValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlIssuedOnValue = (GridBagConstraints) gbcValue + .clone(); + gbc_jlIssuedOnValue.gridy = 13; + + // Expires On + JLabel jlExpiresOn = new JLabel("Expires On"); + jlExpiresOn.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlExpiresOn = (GridBagConstraints) gbcLabel + .clone(); + gbc_jlExpiresOn.gridy = 14; + JLabel jlExpiresOnValue = new JLabel(cert.getNotAfter().toString()); + jlExpiresOnValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlExpiresOnValue = (GridBagConstraints) gbcValue + .clone(); + gbc_jlExpiresOnValue.gridy = 14; + + // Fingerprints + byte[] certBinaryEncoding; + try { + certBinaryEncoding = cert.getEncoded(); + } catch (CertificateEncodingException ex) { + throw new CMException( + "Could not get the encoded form of the certificate.", ex); + } + JLabel jlFingerprints = new JLabel("Fingerprints"); + jlFingerprints.setFont(new Font(null, BOLD, 11)); + GridBagConstraints gbc_jlFingerprints = (GridBagConstraints) gbcLabel.clone(); + gbc_jlFingerprints.gridy = 15; + gbc_jlFingerprints.gridwidth = 2; //takes two columns + gbc_jlFingerprints.insets = new Insets(5, 5, 5, 5);//has slightly bigger insets + + // SHA-1 Fingerprint + JLabel jlSHA1Fingerprint = new JLabel("SHA1 Fingerprint"); + jlSHA1Fingerprint.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlSHA1Fingerprint = (GridBagConstraints) gbcLabel.clone(); + gbc_jlSHA1Fingerprint.gridy = 16; + JLabel jlSHA1FingerprintValue = new JLabel(dnParser.getMessageDigestAsFormattedString(certBinaryEncoding, "SHA1")); + jlSHA1FingerprintValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlSHA1FingerprintValue = (GridBagConstraints) gbcValue.clone(); + gbc_jlSHA1FingerprintValue.gridy = 16; + + // MD5 Fingerprint + JLabel jlMD5Fingerprint = new JLabel("MD5 Fingerprint"); + jlMD5Fingerprint.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlMD5Fingerprint = (GridBagConstraints) gbcLabel.clone(); + gbc_jlMD5Fingerprint.gridy = 17; + JLabel jlMD5FingerprintValue = new JLabel(dnParser.getMessageDigestAsFormattedString(certBinaryEncoding, "MD5")); + jlMD5FingerprintValue.setFont(new Font(null, PLAIN, 11)); + GridBagConstraints gbc_jlMD5FingerprintValue = (GridBagConstraints) gbcValue.clone(); + gbc_jlMD5FingerprintValue.gridy = 17; + + /* + * Empty label to add a bit space at the bottom of the panel to make it + * look like firefox's view certificate dialog + */ + JLabel jlEmpty = new JLabel(""); + GridBagConstraints gbc_jlEmpty = (GridBagConstraints) gbcLabel.clone(); + gbc_jlEmpty.gridy = 18; + gbc_jlEmpty.gridwidth = 2; // takes two columns + gbc_jlEmpty.ipady = 40; + + JPanel jpCertificate = new JPanel(new GridBagLayout()); + jpCertificate.setBorder(new CompoundBorder(new EmptyBorder(15, 15, 15, + 15), new EtchedBorder())); + +// if (intendedUses != null){ +// jpCertificate.add(jpUses, gbc_jpUses); +// } + jpCertificate.add(jlIssuedTo, gbc_jlIssuedTo); // Issued To + jpCertificate.add(jlCN, gbc_jlCN); + jpCertificate.add(jlCNValue, gbc_jlCNValue); + jpCertificate.add(jlOrg, gbc_jlOrg); + jpCertificate.add(jlOrgValue, gbc_jlOrgValue); + jpCertificate.add(jlOU, gbc_jlOU); + jpCertificate.add(jlOUValue, gbc_jlOUValue); + //jpCertificate.add(jlEmail, gbc_jlEmail); + //jpCertificate.add(jlEmailValue, gbc_jlEmailValue); + jpCertificate.add(jlSN, gbc_jlSN); + jpCertificate.add(jlSNValue, gbc_jlSNValue); + jpCertificate.add(jlVersion, gbc_jlVersion); + jpCertificate.add(jlVersionValue, gbc_jlVersionValue); + jpCertificate.add(jlIssuedBy, gbc_jlIssuedBy); //Issued By + jpCertificate.add(jlICN, gbc_jlICN); + jpCertificate.add(jlICNValue, gbc_jlICNValue); + jpCertificate.add(jlIOrg, gbc_jlIOrg); + jpCertificate.add(jlIOrgValue, gbc_jlIOrgValue); + jpCertificate.add(jlIOU, gbc_jlIOU); + jpCertificate.add(jlIOUValue, gbc_jlIOUValue); + jpCertificate.add(jlValidity, gbc_jlValidity); //Validity + jpCertificate.add(jlIssuedOn, gbc_jlIssuedOn); + jpCertificate.add(jlIssuedOnValue, gbc_jlIssuedOnValue); + jpCertificate.add(jlExpiresOn, gbc_jlExpiresOn); + jpCertificate.add(jlExpiresOnValue, gbc_jlExpiresOnValue); + jpCertificate.add(jlFingerprints, gbc_jlFingerprints); //Fingerprints + jpCertificate.add(jlSHA1Fingerprint, gbc_jlSHA1Fingerprint); + jpCertificate.add(jlSHA1FingerprintValue, gbc_jlSHA1FingerprintValue); + jpCertificate.add(jlMD5Fingerprint, gbc_jlMD5Fingerprint); + jpCertificate.add(jlMD5FingerprintValue, gbc_jlMD5FingerprintValue); + jpCertificate.add(jlEmpty, gbc_jlEmpty); //Empty label to get some vertical space on the frame + + // List of serviceURLs + JPanel jpURLs = null; // Panel to hold the URL list + if (serviceURLs != null) { //if service serviceURLs are not null (even if empty - show empty list) + + jpURLs = new JPanel(new BorderLayout()); + jpURLs.setBorder(new CompoundBorder( + new EmptyBorder(0, 15, 0, 15), new EtchedBorder())); + // Label + JLabel jlServiceURLs = new JLabel ("Service URLs this key pair will be used for:"); + jlServiceURLs.setFont(new Font(null, Font.BOLD, 11)); + jlServiceURLs.setBorder(new EmptyBorder(5,5,5,5)); + + // New empty service serviceURLs list + DefaultListModel<String> jltModel = new DefaultListModel<>(); + JList<String> jltServiceURLs = new JList<>(jltModel); + for (String url : serviceURLs) + jltModel.addElement(url); + // don't show more than 5 otherwise the window is too big + jltServiceURLs.setVisibleRowCount(5); + + // Scroll pane for service serviceURLs + JScrollPane jspServiceURLs = new JScrollPane(jltServiceURLs, + VERTICAL_SCROLLBAR_AS_NEEDED, + HORIZONTAL_SCROLLBAR_AS_NEEDED); + jspServiceURLs.getViewport().setBackground( + jltServiceURLs.getBackground()); + + jpURLs.add(jlServiceURLs, NORTH); + jpURLs.add(jspServiceURLs, CENTER); + + // Put it on the main content pane + getContentPane().add(jpURLs, CENTER); + } + + // OK button + JPanel jpOK = new JPanel(new FlowLayout(FlowLayout.CENTER)); + + final JButton jbOK = new JButton("OK"); + jbOK.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okPressed(); + } + }); + + jpOK.add(jbOK); + + /* + * Put it all together (panel with URL list is already added, if it was + * not null) + */ + getContentPane().add(jpCertificate, NORTH); + getContentPane().add(jpOK, SOUTH); + + // Resizing wreaks havoc + setResizable(false); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + getRootPane().setDefaultButton(jbOK); + + pack(); + + invokeLater(new Runnable() { + @Override + public void run() { + jbOK.requestFocus(); + } + }); + } + + private void okPressed() { + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewUsernamePasswordEntryDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewUsernamePasswordEntryDialog.java new file mode 100644 index 0000000..7c92842 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/ViewUsernamePasswordEntryDialog.java
@@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.WEST; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; + +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Dialog used for viewing service URL, username and password. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class ViewUsernamePasswordEntryDialog extends + NonBlockedHelpEnabledDialog { + /** Service URL field */ + private JTextField serviceURLField; + /** Username field */ + private JTextField usernameField; + /** Password field */ + private JTextField passwordField; + /** Service URL value */ + private String serviceURL; + /** Service username value */ + private String username; + /** Service password value */ + private String password; + + public ViewUsernamePasswordEntryDialog(JFrame parent, String currentURL, + String currentUsername, String currentPassword) { + super(parent, "View username and password for a service", true); + serviceURL = currentURL; + username = currentUsername; + password = currentPassword; + initComponents(); + } + + public ViewUsernamePasswordEntryDialog(JDialog parent, String currentURL, + String currentUsername, String currentPassword) { + super(parent, "View username and password for a service", true); + serviceURL = currentURL; + username = currentUsername; + password = currentPassword; + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JLabel serviceURLLabel = new JLabel("Service URL"); + serviceURLLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + JLabel usernameLabel = new JLabel("Username"); + usernameLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + JLabel passwordLabel = new JLabel("Password"); + passwordLabel.setBorder(new EmptyBorder(0, 5, 0, 0)); + + // Populate the fields with values and disable user input + serviceURLField = new JTextField(); + serviceURLField.setText(serviceURL); + serviceURLField.setEditable(false); + + usernameField = new JTextField(15); + usernameField.setText(username); + usernameField.setEditable(false); + + passwordField = new JTextField(15); + passwordField.setText(password); + passwordField.setEditable(false); + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + closeDialog(); + } + }); + + JPanel fieldsPanel = new JPanel(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.weighty = 0.0; + + gbc.weightx = 0.0; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 0); + fieldsPanel.add(serviceURLLabel, gbc); + + gbc.weightx = 1.0; + gbc.gridx = 1; + gbc.gridy = 0; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 5); + fieldsPanel.add(serviceURLField, gbc); + + gbc.weightx = 0.0; + gbc.gridx = 0; + gbc.gridy = 1; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 0); + fieldsPanel.add(usernameLabel, gbc); + + gbc.weightx = 1.0; + gbc.gridx = 1; + gbc.gridy = 1; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 5); + fieldsPanel.add(usernameField, gbc); + + gbc.weightx = 0.0; + gbc.gridx = 0; + gbc.gridy = 2; + gbc.fill = NONE; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 0); + fieldsPanel.add(passwordLabel, gbc); + + gbc.weightx = 1.0; + gbc.gridx = 1; + gbc.gridy = 2; + gbc.fill = HORIZONTAL; + gbc.anchor = WEST; + gbc.insets = new Insets(5, 10, 0, 5); + fieldsPanel.add(passwordField, gbc); + + fieldsPanel.setBorder(new CompoundBorder( + new EmptyBorder(10, 10, 10, 10), new EtchedBorder())); + + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + buttonsPanel.add(okButton); + + getContentPane().add(fieldsPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + // setResizable(false); + getRootPane().setDefaultButton(okButton); + pack(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/WarnUserAboutJCEPolicyDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/WarnUserAboutJCEPolicyDialog.java new file mode 100644 index 0000000..c826c8f --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/WarnUserAboutJCEPolicyDialog.java
@@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.Desktop.getDesktop; +import static javax.swing.border.EtchedBorder.LOWERED; +import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED; +import static org.apache.commons.io.FileUtils.touch; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.Document; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.StyleSheet; + +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +import org.apache.log4j.Logger; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; + +/** + * Dialog that warns user that they need to install unlimited cryptography + * strength policy for Java. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class WarnUserAboutJCEPolicyDialog extends NonBlockedHelpEnabledDialog { + private static final Logger logger = Logger + .getLogger(WarnUserAboutJCEPolicyDialog.class); + + private JCheckBox doNotWarnMeAgainCheckBox; + private final ApplicationConfiguration applicationConfiguration; + private final DistinguishedNameParser dnParser; + + public WarnUserAboutJCEPolicyDialog( + ApplicationConfiguration applicationConfiguration, + DistinguishedNameParser dnParser) { + super((Frame) null, + "Java Unlimited Strength Cryptography Policy Warning", true); + this.applicationConfiguration = applicationConfiguration; + this.dnParser = dnParser; + initComponents(); + } + + // For testing + public static void main(String[] args) { + WarnUserAboutJCEPolicyDialog dialog = new WarnUserAboutJCEPolicyDialog( + null, null); + dialog.setVisible(true); + } + + private void initComponents() { + // Base font for all components on the form + Font baseFont = new JLabel("base font").getFont().deriveFont(11f); + + // Message saying that updates are available + JPanel messagePanel = new JPanel(new BorderLayout()); + messagePanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, + 10), new EtchedBorder(LOWERED))); + + JEditorPane message = new JEditorPane(); + message.setEditable(false); + message.setBackground(this.getBackground()); + message.setFocusable(false); + HTMLEditorKit kit = new HTMLEditorKit(); + message.setEditorKit(kit); + StyleSheet styleSheet = kit.getStyleSheet(); + //styleSheet.addRule("body {font-family:"+baseFont.getFamily()+"; font-size:"+baseFont.getSize()+";}"); // base font looks bigger when rendered as HTML + styleSheet.addRule("body {font-family:" + baseFont.getFamily() + + "; font-size:10px;}"); + Document doc = kit.createDefaultDocument(); + message.setDocument(doc); + message.setText("<html><body>In order for Taverna's security features to function properly - you need to install<br>" + + "'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy'. <br><br>" + + "If you do not already have it, for <b>Java 6</b> you can get it from:<br>" + + "<a href=\"http://www.oracle.com/technetwork/java/javase/downloads/index.html\">http://www.oracle.com/technetwork/java/javase/downloads/index.html</a><br<br>" + + "Installation instructions are contained in the bundle you download." + + "</body><html>"); + message.addHyperlinkListener(new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent he) { + HyperlinkEvent.EventType type = he.getEventType(); + if (type == ACTIVATED) + // Open a Web browser + try { + getDesktop().browse(he.getURL().toURI()); +// BrowserLauncher launcher = new BrowserLauncher(); +// launcher.openURLinBrowser(he.getURL().toString()); + } catch (Exception ex) { + logger.error("Failed to launch browser to fetch JCE " + + he.getURL()); + } + } + }); + message.setBorder(new EmptyBorder(5, 5, 5, 5)); + messagePanel.add(message, CENTER); + + doNotWarnMeAgainCheckBox = new JCheckBox("Do not warn me again"); + doNotWarnMeAgainCheckBox.setFont(baseFont.deriveFont(12f)); + messagePanel.add(doNotWarnMeAgainCheckBox, SOUTH); + + // Buttons + JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + JButton okButton = new JButton("OK"); + okButton.setFont(baseFont); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + okPressed(); + } + }); + + buttonsPanel.add(okButton); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(messagePanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + pack(); + setResizable(false); + // Center the dialog on the screen (we do not have the parent) + Dimension dimension = getToolkit().getScreenSize(); + Rectangle abounds = getBounds(); + setLocation((dimension.width - abounds.width) / 2, + (dimension.height - abounds.height) / 2); + setSize(getPreferredSize()); + } + + private static final String DO_NOT_WARN_ABOUT_JCE_POLICY = "do_not_warn_about_JCE_policy"; + public static boolean warnedUser = false; // have we already warned user for + // this run + + /** + * Warn user that they need to install Java Cryptography Extension (JCE) + * Unlimited Strength Jurisdiction Policy if they want Credential Manager to + * function properly. + */ + public static void warnUserAboutJCEPolicy( + ApplicationConfiguration applicationConfiguration, + DistinguishedNameParser dnParser) { + /* + * Do not pop up a dialog if we are running headlessly. If we have + * warned the user and they do not want us to remind them again - exit. + */ + if (warnedUser || GraphicsEnvironment.isHeadless() + || doNotWarnFile(applicationConfiguration, dnParser).exists()) + return; + + WarnUserAboutJCEPolicyDialog warnDialog = new WarnUserAboutJCEPolicyDialog( + applicationConfiguration, dnParser); + warnDialog.setVisible(true); + warnedUser = true; + } + + private static File doNotWarnFile( + ApplicationConfiguration applicationConfiguration, + DistinguishedNameParser dnParser) { + return new File( + dnParser.getCredentialManagerDefaultDirectory(applicationConfiguration), + DO_NOT_WARN_ABOUT_JCE_POLICY); + } + + protected void okPressed() { + try { + if (doNotWarnMeAgainCheckBox.isSelected()) + touch(doNotWarnFile(applicationConfiguration, dnParser)); + } catch (IOException e) { + logger.error( + "Failed to touch the 'Do not want me about JCE unilimited security policy' file.", + e); + } + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } + +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/action/CredentialManagerAction.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/action/CredentialManagerAction.java new file mode 100644 index 0000000..c86ad27 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/action/CredentialManagerAction.java
@@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.action; + +import static javax.swing.SwingUtilities.invokeLater; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.ImageIcon; + +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.workbench.ui.credentialmanager.CredentialManagerUI; + +//import javax.swing.SwingUtilities; + +@SuppressWarnings("serial") +public class CredentialManagerAction extends AbstractAction { + private static ImageIcon ICON = new ImageIcon( + CredentialManagerAction.class + .getResource("/images/cred_manager16x16.png")); + + private CredentialManagerUI cmUI; + private final CredentialManager credentialManager; + private final DistinguishedNameParser dnParser; + + public CredentialManagerAction(CredentialManager credentialManager, + DistinguishedNameParser dnParser) { + super("Credential Manager", ICON); + this.credentialManager = credentialManager; + this.dnParser = dnParser; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (cmUI != null) { + cmUI.setVisible(true); + return; + } + + invokeLater(new Runnable() { + @Override + public void run() { + cmUI = new CredentialManagerUI(credentialManager, dnParser); + cmUI.setVisible(true); + } + }); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/menu/CredentialManagerMenu.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/menu/CredentialManagerMenu.java new file mode 100644 index 0000000..1eaf82b --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/menu/CredentialManagerMenu.java
@@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.menu; + +import java.net.URI; + +import javax.swing.Action; + +//import org.apache.log4j.Logger; + +//import net.sf.taverna.t2.security.credentialmanager.CMException; +//import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.ui.credentialmanager.action.CredentialManagerAction; + +public class CredentialManagerMenu extends AbstractMenuAction { + private static final String MENU_URI = "http://taverna.sf.net/2008/t2workbench/menu#advanced"; + + private CredentialManager credentialManager; + private DistinguishedNameParser dnParser; + + // private static Logger logger = Logger.getLogger(CredentialManagerMenu.class); + + public CredentialManagerMenu() { + super(URI.create(MENU_URI), 60); + /* This is now done in the initialise SSL startup hook - no need to do it here. + // Force initialisation at startup + try { + CredentialManager.getInstance(); + } catch (CMException e) { + logger.error("Could not initialise SSL properties for SSL connections from Taverna.", e); + } + */ + } + + @Override + protected Action createAction() { + return new CredentialManagerAction(credentialManager, dnParser); + } + + public void setCredentialManager(CredentialManager credentialManager) { + this.credentialManager = credentialManager; + } + + /** + * @param dnParser + * the dnParser to set + */ + public void setDistinguishedNameParser(DistinguishedNameParser dnParser) { + this.dnParser = dnParser; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserJavaTruststorePasswordProvider.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserJavaTruststorePasswordProvider.java new file mode 100644 index 0000000..9ddd9a7 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserJavaTruststorePasswordProvider.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2008-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import net.sf.taverna.t2.security.credentialmanager.JavaTruststorePasswordProvider; + +/** + * An implementation of the {@link JavaTruststorePasswordProvider} that pops up a + * dialog and asks the user to provide the password. + * + * @author Alex Nenadic + * + */ +public class AskUserJavaTruststorePasswordProvider implements JavaTruststorePasswordProvider{ + + @Override + public String getJavaTruststorePassword() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setJavaTruststorePassword(String password) { + // TODO Auto-generated method stub + + } + +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserMasterPasswordProvider.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserMasterPasswordProvider.java new file mode 100644 index 0000000..55b7d5b --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserMasterPasswordProvider.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2008-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider; + +public class AskUserMasterPasswordProvider implements MasterPasswordProvider{ + +// @Override +// public boolean canProvideMasterPassword() { +// // TODO Auto-generated method stub +// return false; +// } + private int priority = 100; + + @Override + public String getMasterPassword(boolean firstTime) { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getProviderPriority() { + return priority; + } + + @Override + public void setMasterPassword(String password) { + // TODO Auto-generated method stub + } + +// @Override +// public void setProviderPriority(int priority) { +// this.priority = priority; +// } + +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserServiceUsernameAndPasswordProvider.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserServiceUsernameAndPasswordProvider.java new file mode 100644 index 0000000..b43d184 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserServiceUsernameAndPasswordProvider.java
@@ -0,0 +1,23 @@ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import java.net.URI; + +import net.sf.taverna.t2.security.credentialmanager.ServiceUsernameAndPasswordProvider; +import net.sf.taverna.t2.security.credentialmanager.UsernamePassword; + +public class AskUserServiceUsernameAndPasswordProvider implements ServiceUsernameAndPasswordProvider{ + + @Override + public UsernamePassword getServiceUsernameAndPassword(URI serviceURI, String requestMessage) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setServiceUsernameAndPassword(URI serviceURI, + UsernamePassword usernamePassword) { + // TODO Auto-generated method stub + + } + +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserTrustConfirmationProvider.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserTrustConfirmationProvider.java new file mode 100644 index 0000000..824764d --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/AskUserTrustConfirmationProvider.java
@@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (C) 2008-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import java.security.cert.X509Certificate; + +import net.sf.taverna.t2.security.credentialmanager.TrustConfirmationProvider; + +public class AskUserTrustConfirmationProvider implements TrustConfirmationProvider { + + @Override + public Boolean shouldTrustCertificate(X509Certificate[] chain) { + // TODO Auto-generated method stub + return null; + } + +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/GetPasswordDialog.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/GetPasswordDialog.java new file mode 100644 index 0000000..851e900 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/GetPasswordDialog.java
@@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static java.awt.BorderLayout.SOUTH; +import static java.awt.FlowLayout.LEFT; +import static java.awt.FlowLayout.RIGHT; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; + +import net.sf.taverna.t2.workbench.helper.NonBlockedHelpEnabledDialog; + +/** + * Dialog for entering user's username and password. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class GetPasswordDialog extends NonBlockedHelpEnabledDialog { + /** + * Whether we should ask user to save their username and password using + * Credential Manager + */ + private boolean shouldAskUserToSave; + /** Username field */ + private JTextField usernameField; + /** Password field */ + private JPasswordField passwordField; + /** + * Whether user wished to save the username and password using Credential + * Manager + */ + private JCheckBox saveCheckBox; + /** The entered username */ + private String username; + /** The entered password */ + private String password; + /** Instructions to the user */ + private String instructions; + + public GetPasswordDialog(String instructions, boolean shouldAskUserToSave) { + super((Frame) null, "Enter username and password", true); + this.instructions = instructions; + this.shouldAskUserToSave = shouldAskUserToSave; + initComponents(); + } + + private void initComponents() { + getContentPane().setLayout(new BorderLayout()); + + JLabel instructionsLabel = new JLabel(instructions); + instructionsLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); + JPanel jpInstructions = new JPanel(new FlowLayout(LEFT)); + jpInstructions.add(instructionsLabel); + + JLabel usernameLabel = new JLabel("Username"); + usernameLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); + JLabel passwordLabel = new JLabel("Password"); + passwordLabel.setBorder(new EmptyBorder(5, 5, 5, 5)); + + usernameField = new JTextField(15); + passwordField = new JPasswordField(15); + + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okPressed(); + } + }); + + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelPressed(); + } + }); + + // Central panel with username/password fields and a "Do you want to Save?" checkbox + JPanel mainPanel = new JPanel(new BorderLayout()); + + JPanel passwordPanel = new JPanel(new GridLayout(2, 2, 5, 5)); + passwordPanel.add(usernameLabel); + passwordPanel.add(usernameField); + passwordPanel.add(passwordLabel); + passwordPanel.add(passwordField); + mainPanel.add(passwordPanel, CENTER); + + // If user wants to save this username and password + saveCheckBox = new JCheckBox(); + saveCheckBox.setBorder(new EmptyBorder(5, 5, 5, 5)); + saveCheckBox.setSelected(true); + saveCheckBox + .setText("Use Credential Manager to save this username and password"); + if (shouldAskUserToSave) { + JPanel jpSaveCheckBox = new JPanel(new FlowLayout(LEFT)); + jpSaveCheckBox.add(saveCheckBox); + mainPanel.add(jpSaveCheckBox, SOUTH); + } + + passwordPanel.setBorder(new CompoundBorder(new EmptyBorder(10, 10, 10, + 10), new EtchedBorder())); + + JPanel buttonsPanel = new JPanel(new FlowLayout(RIGHT)); + buttonsPanel.add(okButton); + buttonsPanel.add(cancelButton); + + passwordPanel.setMinimumSize(new Dimension(300, 100)); + + getContentPane().add(jpInstructions, NORTH); + getContentPane().add(mainPanel, CENTER); + getContentPane().add(buttonsPanel, SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + getRootPane().setDefaultButton(okButton); + pack(); + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + /** + * Check if user wishes to save username and pasword using the Credential + * Manager. + */ + public boolean shouldSaveUsernameAndPassword() { + return saveCheckBox.isSelected(); + } + + private boolean checkControls() { + username = usernameField.getText(); + if (username.length() == 0) { + showMessageDialog(this, "Username cannot be empty", "Warning", + WARNING_MESSAGE); + return false; + } + + password = new String(passwordField.getPassword()); + if (password.length() == 0) { // password empty + showMessageDialog(this, "Password cannot be empty", "Warning", + WARNING_MESSAGE); + + return false; + } + + return true; + } + + private void okPressed() { + if (checkControls()) + closeDialog(); + } + + private void cancelPressed() { + // Set all fields to null to indicate that cancel button was pressed + username = null; + password = null; + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } + + public void setUsername(String username) { + this.username = username; + usernameField.setText(username); + } + + public void setPassword(String password) { + this.password = password; + passwordField.setText(password); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/SimpleMasterPasswordProvider.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/SimpleMasterPasswordProvider.java new file mode 100644 index 0000000..4b9950e --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/SimpleMasterPasswordProvider.java
@@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (C) 2008-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider; + +/** + * A simple implementation of {@link MasterPasswordProvider} that just provides + * a master password that can be obtained and set from outside the provider. + * + * @author Alex Nenadic + */ +public class SimpleMasterPasswordProvider implements MasterPasswordProvider { + private String masterPassword; + private int priority = 200; + + @Override + public String getMasterPassword(boolean firstTime) { + return masterPassword; + } + + @Override + public void setMasterPassword(String masterPassword){ + this.masterPassword = masterPassword; + } + + @Override + public int getProviderPriority() { + return priority; + } + +// @Override +// public void setProviderPriority(int priority) { +// this.priority = priority; +// } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIMasterPasswordProvider.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIMasterPasswordProvider.java new file mode 100644 index 0000000..81b2e5c --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIMasterPasswordProvider.java
@@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (C) 2009-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import java.awt.GraphicsEnvironment; + +import javax.swing.JFrame; +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; + +import net.sf.taverna.t2.security.credentialmanager.JavaTruststorePasswordProvider; +import net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider; +import net.sf.taverna.t2.workbench.ui.credentialmanager.GetMasterPasswordDialog; +import net.sf.taverna.t2.workbench.ui.credentialmanager.SetMasterPasswordDialog; +import net.sf.taverna.t2.workbench.ui.credentialmanager.WarnUserAboutJCEPolicyDialog; + +/** + * A UI pop-up that asks user for a master password for Credential Manager. + * + * @author Alex Nenadic + * @author Stian Soiland-Reyes + * + */ +public class UIMasterPasswordProvider implements MasterPasswordProvider, JavaTruststorePasswordProvider { + + private ApplicationConfiguration applicationConfiguration; + + private DistinguishedNameParser dnParser; + + @Override + public String getJavaTruststorePassword() { + if (GraphicsEnvironment.isHeadless()) { + return null; + } + + GetMasterPasswordDialog getPasswordDialog = new GetMasterPasswordDialog( + "Credential Manager needs to copy certificates from Java truststore. " + + "Please enter your password."); + getPasswordDialog.setLocationRelativeTo(null); + getPasswordDialog.setVisible(true); + String javaTruststorePassword = getPasswordDialog.getPassword(); + return javaTruststorePassword; + } + + @Override + public void setJavaTruststorePassword(String password) { + } + + @Override + public String getMasterPassword(boolean firstTime) { + + // Check if this Taverna run is headless (i.e. Taverna Server or Taverna + // from command line) - do not do anything here if it is as we do not + // want + // any windows popping up even if they could + if (GraphicsEnvironment.isHeadless()) { + return null; + } + + // Pop up a warning about Java Cryptography Extension (JCE) + // Unlimited Strength Jurisdiction Policy + WarnUserAboutJCEPolicyDialog.warnUserAboutJCEPolicy(applicationConfiguration, dnParser); + + if (firstTime) { + // Ask user to set the master password for Credential Manager (only + // the first time) + SetMasterPasswordDialog setPasswordDialog = new SetMasterPasswordDialog( + (JFrame) null, "Set master password", true, + "Set master password for Credential Manager"); + setPasswordDialog.setLocationRelativeTo(null); + setPasswordDialog.setVisible(true); + return setPasswordDialog.getPassword(); + } else { + // Ask user to provide a master password for Credential Manager + GetMasterPasswordDialog getPasswordDialog = new GetMasterPasswordDialog( + "Enter master password for Credential Manager"); + getPasswordDialog.setLocationRelativeTo(null); + getPasswordDialog.setVisible(true); + return getPasswordDialog.getPassword(); + } + } + + @Override + public void setMasterPassword(String password) { + } + + @Override + public int getProviderPriority() { + return 100; + } + + /** + * Sets the applicationConfiguration. + * + * @param applicationConfiguration the new value of applicationConfiguration + */ + public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + } + + /** + * @param dnParser the dnParser to set + */ + public void setDistinguishedNameParser(DistinguishedNameParser dnParser) { + this.dnParser = dnParser; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIUsernamePasswordProvider.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIUsernamePasswordProvider.java new file mode 100644 index 0000000..60318d0 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/password/UIUsernamePasswordProvider.java
@@ -0,0 +1,92 @@ +package net.sf.taverna.t2.workbench.ui.credentialmanager.password; + +import static java.awt.GraphicsEnvironment.isHeadless; + +import java.net.URI; +import java.net.URISyntaxException; + +import net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser; +import net.sf.taverna.t2.security.credentialmanager.ServiceUsernameAndPasswordProvider; +import net.sf.taverna.t2.security.credentialmanager.UsernamePassword; + +import org.apache.log4j.Logger; + +public class UIUsernamePasswordProvider implements + ServiceUsernameAndPasswordProvider { + private static final Logger logger = Logger + .getLogger(UIUsernamePasswordProvider.class); + + private DistinguishedNameParser dnParser; + + public boolean canProvideUsernamePassword(URI serviceURI) { + return !isHeadless(); + } + + @Override + public UsernamePassword getServiceUsernameAndPassword(URI serviceURI, + String requestingPrompt) { + URI displayURI = serviceURI; + + try { + displayURI = dnParser.setFragmentForURI(displayURI, null); + displayURI = dnParser.setUserInfoForURI(displayURI, null); + } catch (URISyntaxException e) { + logger.warn("Could not strip fragment/userinfo from " + serviceURI, + e); + } + + StringBuilder message = new StringBuilder(); + message.append("<html><body>The Taverna Credential Manager could not find a "); + message.append("username and password for the service at:"); + message.append("<br><br><code>"); + message.append(displayURI); + message.append("</code>"); + if (requestingPrompt != null && !requestingPrompt.isEmpty()) { + message.append("<p><i>"); + message.append(requestingPrompt); + message.append("</i>"); + } + message.append("<br><br>Please provide the username and password.</body></html>"); + + GetPasswordDialog getPasswordDialog = new GetPasswordDialog( + message.toString(), true); + getPasswordDialog.setLocationRelativeTo(null); + if (serviceURI.getRawUserInfo() != null + && serviceURI.getRawUserInfo().length() > 1) { + String userInfo = serviceURI.getRawUserInfo(); + String[] userPassword = userInfo.split(":", 2); + if (userPassword.length == 2) { + getPasswordDialog.setUsername(userPassword[0]); + getPasswordDialog.setPassword(userPassword[1]); + } + } + getPasswordDialog.setVisible(true); + + String username = getPasswordDialog.getUsername(); // get username + String password = getPasswordDialog.getPassword(); // get password + boolean shouldSaveUsernameAndPassword = getPasswordDialog + .shouldSaveUsernameAndPassword(); + if (username == null || password == null) + // user cancelled - any of the above two variables is null + return null; + + UsernamePassword credential = new UsernamePassword(); + credential.setUsername(username); + credential.setPassword(password.toCharArray()); + credential.setShouldSave(shouldSaveUsernameAndPassword); + return credential; + } + + @Override + public void setServiceUsernameAndPassword(URI serviceURI, + UsernamePassword usernamePassword) { + } + + /** + * @param dnParser + * the dnParser to set + */ + public void setDistinguishedNameParser(DistinguishedNameParser dnParser) { + this.dnParser = dnParser; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/InitialiseSSLStartupHook.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/InitialiseSSLStartupHook.java new file mode 100644 index 0000000..1e4073c --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/InitialiseSSLStartupHook.java
@@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (C) 2007-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + */ +package net.sf.taverna.t2.workbench.ui.credentialmanager.startup; + +import org.apache.log4j.Logger; + +import net.sf.taverna.t2.security.credentialmanager.CMException; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.workbench.StartupSPI; + +/** + * + * Startup hook to initialise SSL socket factory used by Taverna for creating + * HTTPS connections. + * + * @author Alex Nenadic + * @author Stian Soiland-Reyes + */ +public class InitialiseSSLStartupHook implements StartupSPI { + private static final Logger logger = Logger + .getLogger(InitialiseSSLStartupHook.class); + + private CredentialManager credManager; + + @Override + public int positionHint() { + return 25; + } + + @Override + public boolean startup() { + logger.info("Initialising SSL socket factory for SSL connections from Taverna."); + try { + credManager.initializeSSL(); + } catch (CMException e) { + logger.error( + "Could not initialise the SSL socket factory (for creating SSL connections)" + + " using Taverna's keystores.", e); + } + return true; + } + + public void setCredentialManager(CredentialManager credManager) { + this.credManager = credManager; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/SetCredManAuthenticatorStartupHook.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/SetCredManAuthenticatorStartupHook.java new file mode 100644 index 0000000..29ec9b6 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/startup/SetCredManAuthenticatorStartupHook.java
@@ -0,0 +1,24 @@ +package net.sf.taverna.t2.workbench.ui.credentialmanager.startup; + +import java.net.Authenticator; +import net.sf.taverna.t2.security.credentialmanager.CredentialManager; +import net.sf.taverna.t2.workbench.StartupSPI; + +public class SetCredManAuthenticatorStartupHook implements StartupSPI { + private CredentialManager credManager; + + @Override + public int positionHint() { + return 50; + } + + @Override + public boolean startup() { + Authenticator.setDefault(credManager.getAuthenticator()); + return true; + } + + public void setCredentialManager(CredentialManager credManager) { + this.credManager = credManager; + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarAction.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarAction.java new file mode 100644 index 0000000..d515809 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarAction.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar; + +import static net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar.CredentialManagerToolbarSection.CREDENTIAL_MANAGER_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.ui.credentialmanager.action.CredentialManagerAction; + +public class CredentialManagerToolbarAction extends AbstractMenuAction { + private static final String ENTRY_URI = "http://taverna.sf.net/2008/t2workbench/toolbar#credentialManagerAction"; + + public CredentialManagerToolbarAction() { + super(CREDENTIAL_MANAGER_TOOLBAR_SECTION, 100, URI.create(ENTRY_URI)); + } + + @Override + protected Action createAction() { + // need to add CredentialManager if toolbar is ever used + return new CredentialManagerAction(null, null); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarSection.java b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarSection.java new file mode 100644 index 0000000..e5367be --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/java/net/sf/taverna/t2/workbench/ui/credentialmanager/toolbar/CredentialManagerToolbarSection.java
@@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar; + +import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +public class CredentialManagerToolbarSection extends AbstractMenuSection { + private static final String ENTRY_URI = "http://taverna.sf.net/2008/t2workbench/toolbar#credentialManagerSection"; + /** {@value #ENTRY_URI} */ + public static URI CREDENTIAL_MANAGER_TOOLBAR_SECTION = URI + .create(ENTRY_URI); + + public CredentialManagerToolbarSection() { + super(DEFAULT_TOOL_BAR, 300, CREDENTIAL_MANAGER_TOOLBAR_SECTION); + } +}
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.security.credentialmanager.CredentialProviderSPI b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.security.credentialmanager.CredentialProviderSPI new file mode 100644 index 0000000..725aa11 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.security.credentialmanager.CredentialProviderSPI
@@ -0,0 +1,3 @@ +net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIUsernamePasswordProvider +net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIMasterPasswordProvider +net.sf.taverna.t2.workbench.ui.credentialmanager.ConfirmTrustedCertificateUI
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..3743c2f --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,3 @@ +net.sf.taverna.t2.workbench.ui.credentialmanager.menu.CredentialManagerMenu +#net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar.CredentialManagerToolbarAction +#net.sf.taverna.t2.workbench.ui.credentialmanager.toolbar.CredentialManagerToolbarSection \ No newline at end of file
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.StartupSPI b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.StartupSPI new file mode 100644 index 0000000..b43772c --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.StartupSPI
@@ -0,0 +1,2 @@ +net.sf.taverna.t2.workbench.ui.credentialmanager.startup.InitialiseSSLStartupHook +net.sf.taverna.t2.workbench.ui.credentialmanager.startup.SetCredManAuthenticatorStartupHook
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context-osgi.xml b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context-osgi.xml new file mode 100644 index 0000000..f595eda --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context-osgi.xml
@@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="askUserMasterPasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider" /> + + <service ref="simpleMasterPasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider" /> + + <service ref="askUserJavaTruststorePasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.JavaTruststorePasswordProvider" /> + + <service ref="askUserServiceUsernameAndPasswordProvider" interface="net.sf.taverna.t2.security.credentialmanager.ServiceUsernameAndPasswordProvider" /> + + <service ref="askUserTrustConfirmationProvider" interface="net.sf.taverna.t2.security.credentialmanager.TrustConfirmationProvider" /> + + <service ref="UIUsernamePasswordProvider" auto-export="interfaces" /> + + <service ref="UIMasterPasswordProvider" auto-export="interfaces" /> + + <service ref="ConfirmTrustedCertificateUI" auto-export="interfaces" /> + + <service ref="InitialiseSSLStartupHook" interface="net.sf.taverna.t2.workbench.StartupSPI" /> + + <service ref="SetCredManAuthenticatorStartupHook" interface="net.sf.taverna.t2.workbench.StartupSPI" /> + + <service ref="CredentialManagerMenu" auto-export="interfaces" /> + + <reference id="CredentialManager" interface="net.sf.taverna.t2.security.credentialmanager.CredentialManager" /> + + <reference id="distinguishedNameParser" interface="net.sf.taverna.t2.security.credentialmanager.DistinguishedNameParser" /> + + <reference id="ApplicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" /> + +</beans:beans>
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context.xml b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context.xml new file mode 100644 index 0000000..0e54c93 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/META-INF/spring/credential-manager-ui-context.xml
@@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="askUserMasterPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserMasterPasswordProvider" /> + + <bean id="simpleMasterPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.SimpleMasterPasswordProvider" /> + + <bean id="askUserJavaTruststorePasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserJavaTruststorePasswordProvider" /> + + <bean id="askUserServiceUsernameAndPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserServiceUsernameAndPasswordProvider" /> + + <bean id="askUserTrustConfirmationProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.AskUserTrustConfirmationProvider" /> + + <bean id="MasterPasswordProviderComparator" class="net.sf.taverna.t2.security.credentialmanager.MasterPasswordProvider$ProviderComparator" /> + + <bean id="UIUsernamePasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIUsernamePasswordProvider" > + <property name="distinguishedNameParser" ref="distinguishedNameParser" /> + </bean> + + <bean id="UIMasterPasswordProvider" class="net.sf.taverna.t2.workbench.ui.credentialmanager.password.UIMasterPasswordProvider"> + <property name="applicationConfiguration" ref="ApplicationConfiguration" /> + <property name="distinguishedNameParser" ref="distinguishedNameParser" /> + </bean> + + <bean id="ConfirmTrustedCertificateUI" class="net.sf.taverna.t2.workbench.ui.credentialmanager.ConfirmTrustedCertificateUI"> + <property name="distinguishedNameParser" ref="distinguishedNameParser" /> + </bean> + + <bean id="InitialiseSSLStartupHook" class="net.sf.taverna.t2.workbench.ui.credentialmanager.startup.InitialiseSSLStartupHook"> + <property name="credentialManager" ref="CredentialManager" /> + </bean> + + <bean id="SetCredManAuthenticatorStartupHook" class="net.sf.taverna.t2.workbench.ui.credentialmanager.startup.SetCredManAuthenticatorStartupHook" > + <property name="credentialManager" ref="CredentialManager" /> + </bean> + + <bean id="CredentialManagerMenu" class="net.sf.taverna.t2.workbench.ui.credentialmanager.menu.CredentialManagerMenu" > + <property name="credentialManager" ref="CredentialManager" /> + <property name="distinguishedNameParser" ref="distinguishedNameParser" /> + </bean> + +</beans>
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager.png b/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager.png new file mode 100644 index 0000000..48cd63c --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager.png Binary files differ
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager16x16.png b/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager16x16.png new file mode 100644 index 0000000..c6e73b5 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager16x16.png Binary files differ
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager_transparent.png b/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager_transparent.png new file mode 100644 index 0000000..1e89bde --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/images/cred_manager_transparent.png Binary files differ
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/images/table/entry_heading.png b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/entry_heading.png new file mode 100644 index 0000000..8b59845 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/entry_heading.png Binary files differ
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/images/table/key_entry.png b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/key_entry.png new file mode 100644 index 0000000..1fd18c6 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/key_entry.png Binary files differ
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/images/table/keypair_entry.png b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/keypair_entry.png new file mode 100644 index 0000000..8fd3e8b --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/keypair_entry.png Binary files differ
diff --git a/taverna-workbench-credential-manager-ui/src/main/resources/images/table/trustcert_entry.png b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/trustcert_entry.png new file mode 100644 index 0000000..0f110e1 --- /dev/null +++ b/taverna-workbench-credential-manager-ui/src/main/resources/images/table/trustcert_entry.png Binary files differ
diff --git a/taverna-workbench-data-management-config-ui/pom.xml b/taverna-workbench-data-management-config-ui/pom.xml new file mode 100644 index 0000000..4afb4ad --- /dev/null +++ b/taverna-workbench-data-management-config-ui/pom.xml
@@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>data-management-config-ui</artifactId> + <packaging>bundle</packaging> + <name>Data management configuration UI components</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <!--<dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>run-ui</artifactId> + <version>${project.version}</version> + </dependency> + --> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-database-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + + <!-- <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-aop</artifactId> + <version>${spring.version}</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + <version>${aspectj.version}</version> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + <version>${aspectj.version}</version> + </dependency> --> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationPanel.java b/taverna-workbench-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationPanel.java new file mode 100644 index 0000000..b705362 --- /dev/null +++ b/taverna-workbench-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationPanel.java
@@ -0,0 +1,304 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.reference.config; + +import static java.awt.Color.RED; +import static java.awt.Font.PLAIN; +import static java.awt.GridBagConstraints.BOTH; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.RELATIVE; +import static java.awt.GridBagConstraints.WEST; +import static net.sf.taverna.t2.workbench.helper.Helper.showHelp; + +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.sql.Connection; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.lang.ui.DialogTextArea; +import uk.org.taverna.configuration.database.DatabaseConfiguration; +import uk.org.taverna.configuration.database.DatabaseManager; + +@SuppressWarnings("serial") +public class DataManagementConfigurationPanel extends JPanel { + private DatabaseConfiguration configuration; + private DatabaseManager databaseManager; + + private JCheckBox enableProvenance; + private JCheckBox enableInMemory; + private JButton helpButton; + private JButton resetButton; + private JButton applyButton; + private JTextArea storageText; + private JTextArea exposeDatanatureText; + private JCheckBox exposeDatanatureBox; + private DialogTextArea enableInMemoryTextDisabled; + + public DataManagementConfigurationPanel(DatabaseConfiguration configuration, DatabaseManager databaseManager) { + this.configuration = configuration; + this.databaseManager = databaseManager; + + setLayout(generateLayout()); + resetFields(); + } + + private static final boolean ADD_WARNING_LISTENERS = false; + + private GridBagLayout generateLayout() { + GridBagLayout gridbag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + enableProvenance = new JCheckBox("Enable provenance capture"); + DialogTextArea enableProvenanceText = new DialogTextArea( + "Disabling provenance will prevent you from being able to view intermediate results, but does give a performance benefit."); + Font plain = enableProvenanceText.getFont().deriveFont(PLAIN, 11); + enableProvenanceText.setLineWrap(true); + enableProvenanceText.setWrapStyleWord(true); + enableProvenanceText.setEditable(false); + enableProvenanceText.setFocusable(false); + enableProvenanceText.setOpaque(false); + enableProvenanceText.setFont(plain); + + enableInMemory = new JCheckBox("In-memory storage"); + DialogTextArea enableInMemoryText = new DialogTextArea( + "Data will not be stored between workbench sessions. If you run workflows passing larger amounts of data, try disabling in-memory storage, which can reduce execution performance, but also Taverna's memory consumption. "); + enableInMemoryText.setLineWrap(true); + enableInMemoryText.setWrapStyleWord(true); + enableInMemoryText.setEditable(false); + enableInMemoryText.setFocusable(false); + enableInMemoryText.setOpaque(false); + enableInMemoryText.setFont(plain); + + enableInMemoryTextDisabled = new DialogTextArea( + "If you enable in-memory storage of data when provenance collection is turned on then provenance will not be available after you shutdown Taverna as the in-memory data will be lost."); + enableInMemoryTextDisabled.setLineWrap(true); + enableInMemoryTextDisabled.setWrapStyleWord(true); + enableInMemoryTextDisabled.setEditable(false); + enableInMemoryTextDisabled.setFocusable(false); + enableInMemoryTextDisabled.setOpaque(false); + enableInMemoryTextDisabled.setFont(plain); + enableInMemoryTextDisabled.setForeground(RED); + enableInMemoryTextDisabled.setVisible(false); + + // Disable warning as inMemory is default + // To re-enable - also see resetFields() + + if (ADD_WARNING_LISTENERS) { + enableInMemory.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + enableInMemoryTextDisabled.setVisible(enableProvenance + .isSelected() && enableInMemory.isSelected()); + } + }); + enableProvenance.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + enableInMemoryTextDisabled.setVisible(enableProvenance + .isSelected() && enableInMemory.isSelected()); + } + }); + } + + storageText = new JTextArea( + "Select how Taverna stores the data and provenance produced when a workflow is run. This includes workflow results and intermediate results."); + storageText.setLineWrap(true); + storageText.setWrapStyleWord(true); + storageText.setEditable(false); + storageText.setFocusable(false); + storageText.setBorder(new EmptyBorder(10, 10, 10, 10)); + storageText.setFont(plain); + + JComponent portPanel = createDerbyServerStatusComponent(); + + c.anchor = WEST; + c.insets = new Insets(0, 0, 10, 0); + c.gridx = 0; + c.gridy = RELATIVE; + c.weightx = 0.0; + c.weighty = 0.0; + c.fill = HORIZONTAL; + gridbag.setConstraints(storageText, c); + add(storageText); + + c.ipady = 0; + c.insets = new Insets(0, 0, 5, 0); + c.fill = NONE; + gridbag.setConstraints(enableProvenance, c); + add(enableProvenance); + + c.insets = new Insets(0, 20, 15, 20); + c.fill = HORIZONTAL; + gridbag.setConstraints(enableProvenanceText, c); + add(enableProvenanceText); + + c.insets = new Insets(0, 0, 5, 0); + c.fill = GridBagConstraints.NONE; + gridbag.setConstraints(enableInMemory, c); + add(enableInMemory); + + c.insets = new Insets(0, 20, 15, 20); + c.fill = HORIZONTAL; + gridbag.setConstraints(enableInMemoryText, c); + add(enableInMemoryText); + + c.insets = new Insets(0, 20, 15, 20); + c.fill = HORIZONTAL; + gridbag.setConstraints(enableInMemoryTextDisabled, c); + add(enableInMemoryTextDisabled); + + c.insets = new Insets(0, 20, 15, 20); + gridbag.setConstraints(portPanel, c); + add(portPanel); + + c.insets = new Insets(0, 0, 5, 0); + c.fill = NONE; + exposeDatanatureBox = new JCheckBox( + "Allow setting of input data encoding"); + gridbag.setConstraints(exposeDatanatureBox, c); + add(exposeDatanatureBox); + + exposeDatanatureText = new JTextArea( + "Select if you want to control how Taverna handles files read as input data"); + exposeDatanatureText.setLineWrap(true); + exposeDatanatureText.setWrapStyleWord(true); + exposeDatanatureText.setEditable(false); + exposeDatanatureText.setFocusable(false); + exposeDatanatureText.setOpaque(false); + exposeDatanatureText.setFont(plain); + + c.insets = new Insets(0, 20, 15, 20); + c.fill = HORIZONTAL; + gridbag.setConstraints(exposeDatanatureText, c); + add(exposeDatanatureText); + + JPanel buttonPanel = createButtonPanel(); + c.weightx = 1.0; + c.weighty = 1.0; + c.fill = BOTH; + c.insets = new Insets(0, 0, 5, 0); + gridbag.setConstraints(buttonPanel, c); + add(buttonPanel); + return gridbag; + } + + private JComponent createDerbyServerStatusComponent() { + DialogTextArea textArea = new DialogTextArea(); + boolean running; + + try (Connection connection = databaseManager.getConnection()) { + running = databaseManager.isRunning(); + } catch (Exception e) { + running = false; + } + + if (running) + textArea.setText("The database is currently running on port: " + + configuration.getCurrentPort() + "."); + else + textArea.setText("Unable to retrieve a database connection - " + + "the database is not available."); + + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + textArea.setEditable(false); + textArea.setFocusable(false); + textArea.setOpaque(false); + textArea.setAlignmentX(CENTER_ALIGNMENT); + textArea.setFont(textArea.getFont().deriveFont(PLAIN, 11)); + textArea.setVisible(configuration.getStartInternalDerbyServer()); + return textArea; + } + + // for testing only +// public static void main(String[] args) { +// JDialog dialog = new JDialog(); +// dialog.add(new DataManagementConfigurationPanel()); +// dialog.setModal(true); +// dialog.setSize(500, 300); +// dialog.setVisible(true); +// System.exit(0); +// } + + public void resetFields() { + enableInMemory.setSelected(configuration.isInMemory()); + enableProvenance.setSelected(configuration.isProvenanceEnabled()); + exposeDatanatureBox.setSelected(configuration.isExposeDatanature()); + + if (ADD_WARNING_LISTENERS) { + enableInMemoryTextDisabled.setVisible(enableProvenance.isSelected() + && enableInMemory.isSelected()); + } + } + + /*private boolean workflowInstances() { + return DataflowRunsComponent.getInstance().getRunListCount()>0; + }*/ + + private void applySettings() { + configuration.setProvenanceEnabled(enableProvenance.isSelected()); + configuration.setInMemory(enableInMemory.isSelected()); + configuration.setExposeDatanature(exposeDatanatureBox.isSelected()); + } + + private JPanel createButtonPanel() { + final JPanel panel = new JPanel(); + + helpButton = new JButton(new AbstractAction("Help") { + @Override + public void actionPerformed(ActionEvent arg0) { + showHelp(panel); + } + }); + panel.add(helpButton); + + resetButton = new JButton(new AbstractAction("Reset") { + @Override + public void actionPerformed(ActionEvent arg0) { + resetFields(); + } + }); + panel.add(resetButton); + + applyButton = new JButton(new AbstractAction("Apply") { + @Override + public void actionPerformed(ActionEvent arg0) { + applySettings(); + resetFields(); + } + }); + panel.add(applyButton); + + return panel; + } +}
diff --git a/taverna-workbench-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationUIFactory.java b/taverna-workbench-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationUIFactory.java new file mode 100644 index 0000000..2799c7e --- /dev/null +++ b/taverna-workbench-data-management-config-ui/src/main/java/net/sf/taverna/t2/workbench/reference/config/DataManagementConfigurationUIFactory.java
@@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.reference.config; + +import javax.swing.JPanel; + +import uk.org.taverna.configuration.Configurable; +import uk.org.taverna.configuration.ConfigurationUIFactory; +import uk.org.taverna.configuration.database.DatabaseConfiguration; +import uk.org.taverna.configuration.database.DatabaseManager; + +public class DataManagementConfigurationUIFactory implements + ConfigurationUIFactory { + private DatabaseConfiguration databaseConfiguration; + private DatabaseManager databaseManager; + + private DataManagementConfigurationPanel configPanel; + + @Override + public boolean canHandle(String uuid) { + return uuid.equals(getConfigurable().getUUID()); + } + + @Override + public JPanel getConfigurationPanel() { + if (configPanel == null) + configPanel = new DataManagementConfigurationPanel( + databaseConfiguration, databaseManager); + configPanel.resetFields(); + return configPanel; + } + + @Override + public Configurable getConfigurable() { + return databaseConfiguration; + } + + public void setDatabaseConfiguration( + DatabaseConfiguration databaseConfiguration) { + this.databaseConfiguration = databaseConfiguration; + } + + public void setDatabaseManager(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + } +}
diff --git a/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory new file mode 100644 index 0000000..8afa6ca --- /dev/null +++ b/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.reference.config.DataManagementConfigurationUIFactory \ No newline at end of file
diff --git a/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context-osgi.xml b/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context-osgi.xml new file mode 100644 index 0000000..89f84a7 --- /dev/null +++ b/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context-osgi.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="DataManagementConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" /> + + <reference id="databaseConfiguration" interface="uk.org.taverna.configuration.database.DatabaseConfiguration" /> + <reference id="databaseManager" interface="uk.org.taverna.configuration.database.DatabaseManager" /> + +</beans:beans>
diff --git a/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context.xml b/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context.xml new file mode 100644 index 0000000..f9f40ed --- /dev/null +++ b/taverna-workbench-data-management-config-ui/src/main/resources/META-INF/spring/data-management-config-ui-context.xml
@@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="DataManagementConfigurationUIFactory" class="net.sf.taverna.t2.workbench.reference.config.DataManagementConfigurationUIFactory"> + <property name="databaseConfiguration" ref="databaseConfiguration"/> + <property name="databaseManager" ref="databaseManager"/> + </bean> + +</beans>
diff --git a/taverna-workbench-design-ui/pom.xml b/taverna-workbench-design-ui/pom.xml new file mode 100644 index 0000000..f88f676 --- /dev/null +++ b/taverna-workbench-design-ui/pom.xml
@@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>design-ui</artifactId> + <name>Design UI</name> + <packaging>bundle</packaging> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>activity-icons-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddConditionAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddConditionAction.java new file mode 100644 index 0000000..510775f --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddConditionAction.java
@@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.AddChildEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.BlockingControlLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; + +/** + * Action for adding a condition to the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class AddConditionAction extends DataflowEditAction { + private static final Logger logger = Logger.getLogger(AddConditionAction.class); + private static final Scufl2Tools scufl2Tools = new Scufl2Tools(); + + private Processor control; + private Processor target; + + public AddConditionAction(Workflow dataflow, Processor control, + Processor target, Component component, EditManager editManager, + SelectionManager selectionManager, + ActivityIconManager activityIconManager) { + super(dataflow, component, editManager, selectionManager); + this.control = control; + this.target = target; + ProcessorBinding processorBinding = scufl2Tools + .processorBindingForProcessor(control, dataflow.getParent() + .getMainProfile()); + putValue(SMALL_ICON, + activityIconManager.iconForActivity(processorBinding + .getBoundActivity().getType())); + putValue(NAME, control.getName()); + } + + @Override + public void actionPerformed(ActionEvent event) { + try { + BlockingControlLink controlLink = new BlockingControlLink(); + controlLink.setUntilFinished(control); + controlLink.setBlock(target); + editManager.doDataflowEdit(dataflow.getParent(), + new AddChildEdit<>(dataflow, controlLink)); + } catch (EditException e) { + logger.debug("Create control link between '" + control.getName() + + "' and '" + target.getName() + "' failed"); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowInputAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowInputAction.java new file mode 100644 index 0000000..ff56997 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowInputAction.java
@@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Set; + +import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog; +import net.sf.taverna.t2.workbench.design.ui.DataflowInputPortPanel; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.AddWorkflowInputPortEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; + +/** + * Action for adding an input port to the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class AddDataflowInputAction extends DataflowEditAction { + private static final Logger logger = Logger + .getLogger(AddDataflowInputAction.class); + + public AddDataflowInputAction(Workflow dataflow, Component component, + EditManager editManager, SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + putValue(SMALL_ICON, WorkbenchIcons.inputIcon); + putValue(NAME, "Workflow input port"); + putValue(SHORT_DESCRIPTION, "Add workflow input port"); + } + + @Override + public void actionPerformed(ActionEvent event) { + try { + Set<String> usedInputPorts = new HashSet<>(); + for (InputWorkflowPort inputPort : dataflow.getInputPorts()) + usedInputPorts.add(inputPort.getName()); + + DataflowInputPortPanel inputPanel = new DataflowInputPortPanel(); + + ValidatingUserInputDialog vuid = new ValidatingUserInputDialog( + "Add Workflow Input Port", inputPanel); + vuid.addTextComponentValidation(inputPanel.getPortNameField(), + "Set the workflow input port name.", usedInputPorts, + "Duplicate workflow input port name.", + "[\\p{L}\\p{Digit}_.]+", + "Invalid workflow input port name."); + vuid.addMessageComponent(inputPanel.getSingleValueButton(), + "Set the input port type."); + vuid.addMessageComponent(inputPanel.getListValueButton(), + "Set the input port list depth."); + vuid.setSize(new Dimension(400, 250)); + + inputPanel.setPortDepth(0); + + if (vuid.show(component)) { + InputWorkflowPort dataflowInputPort = new InputWorkflowPort(); + dataflowInputPort.setName(inputPanel.getPortName()); + dataflowInputPort.setDepth(inputPanel.getPortDepth()); + editManager.doDataflowEdit(dataflow.getParent(), + new AddWorkflowInputPortEdit(dataflow, + dataflowInputPort)); + } + } catch (EditException e) { + logger.warn("Adding a new workflow input port failed"); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowOutputAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowOutputAction.java new file mode 100644 index 0000000..98bf8be --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/AddDataflowOutputAction.java
@@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Set; + +import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog; +import net.sf.taverna.t2.workbench.design.ui.DataflowOutputPortPanel; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.AddWorkflowOutputPortEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * Action for adding an output port to the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class AddDataflowOutputAction extends DataflowEditAction { + private static final Logger logger = Logger + .getLogger(AddDataflowOutputAction.class); + + public AddDataflowOutputAction(Workflow dataflow, Component component, + EditManager editManager, SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + putValue(SMALL_ICON, WorkbenchIcons.outputIcon); + putValue(NAME, "Workflow output port"); + putValue(SHORT_DESCRIPTION, "Add workflow output port"); + } + + @Override + public void actionPerformed(ActionEvent event) { + try { + Set<String> usedOutputPorts = new HashSet<>(); + for (OutputWorkflowPort outputPort : dataflow.getOutputPorts()) + usedOutputPorts.add(outputPort.getName()); + + DataflowOutputPortPanel inputPanel = new DataflowOutputPortPanel(); + + ValidatingUserInputDialog vuid = new ValidatingUserInputDialog( + "Add Workflow Output Port", inputPanel); + vuid.addTextComponentValidation(inputPanel.getPortNameField(), + "Set the workflow output port name.", usedOutputPorts, + "Duplicate workflow output port name.", + "[\\p{L}\\p{Digit}_.]+", + "Invalid workflow output port name."); + vuid.setSize(new Dimension(400, 200)); + + if (vuid.show(component)) { + String portName = inputPanel.getPortName(); + OutputWorkflowPort dataflowOutputPort = new OutputWorkflowPort(); + dataflowOutputPort.setName(portName); + editManager.doDataflowEdit(dataflow.getParent(), + new AddWorkflowOutputPortEdit(dataflow, + dataflowOutputPort)); + } + } catch (EditException e) { + logger.debug("Create workflow output port failed", e); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/DataflowEditAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/DataflowEditAction.java new file mode 100644 index 0000000..a2ca5ea --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/DataflowEditAction.java
@@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import java.awt.Component; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * Abstract superclass of dataflow edit actions. + * + * @author David Withers + */ +public abstract class DataflowEditAction extends AbstractAction { + private static final long serialVersionUID = -1155192575675025091L; + + protected final SelectionManager selectionManager; + protected EditManager editManager; + protected DataflowSelectionModel dataflowSelectionModel; + protected Workflow dataflow; + protected Component component; + protected Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public DataflowEditAction(Workflow dataflow, Component component, + EditManager editManager, SelectionManager selectionManager) { + this.dataflow = dataflow; + this.component = component; + this.editManager = editManager; + this.selectionManager = selectionManager; + dataflowSelectionModel = selectionManager + .getDataflowSelectionModel(dataflow.getParent()); + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowInputPortAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowInputPortAction.java new file mode 100644 index 0000000..e4513d2 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowInputPortAction.java
@@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog; +import net.sf.taverna.t2.workbench.design.ui.DataflowInputPortPanel; +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.ChangeDepthEdit; +import net.sf.taverna.t2.workflow.edits.RenameEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; + +/** + * Action for editing a dataflow input port. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class EditDataflowInputPortAction extends DataflowEditAction { + private static Logger logger = Logger + .getLogger(EditDataflowInputPortAction.class); + + private InputWorkflowPort port; + + public EditDataflowInputPortAction(Workflow dataflow, + InputWorkflowPort port, Component component, + EditManager editManager, SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.port = port; + putValue(SMALL_ICON, WorkbenchIcons.renameIcon); + putValue(NAME, "Edit workflow input port..."); + } + + @Override + public void actionPerformed(ActionEvent e) { + Set<String> usedInputPorts = new HashSet<>(); + for (InputWorkflowPort usedInputPort : dataflow.getInputPorts()) + if (!usedInputPort.getName().equals(port.getName())) + usedInputPorts.add(usedInputPort.getName()); + + DataflowInputPortPanel inputPanel = new DataflowInputPortPanel(); + + ValidatingUserInputDialog vuid = new ValidatingUserInputDialog( + "Edit Workflow Input Port", inputPanel); + vuid.addTextComponentValidation(inputPanel.getPortNameField(), + "Set the workflow input port name.", usedInputPorts, + "Duplicate workflow input port name.", "[\\p{L}\\p{Digit}_.]+", + "Invalid workflow input port name."); + vuid.addMessageComponent(inputPanel.getSingleValueButton(), + "Set the input port type."); + vuid.addMessageComponent(inputPanel.getListValueButton(), + "Set the input port list depth."); + vuid.setSize(new Dimension(400, 250)); + + inputPanel.setPortName(port.getName()); + inputPanel.setPortDepth(port.getDepth()); + + try { + if (vuid.show(component)) + changeInputPort(inputPanel); + } catch (EditException e1) { + logger.warn("Rename workflow input port failed", e1); + } + } + + private void changeInputPort(DataflowInputPortPanel inputPanel) + throws EditException { + List<Edit<?>> editList = new ArrayList<>(); + String portName = inputPanel.getPortName(); + if (!portName.equals(port.getName())) + editList.add(new RenameEdit<>(port, portName)); + int portDepth = inputPanel.getPortDepth(); + if (portDepth != port.getDepth()) + editList.add(new ChangeDepthEdit<>(port, portDepth)); + if (editList.size() == 1) + editManager.doDataflowEdit(dataflow.getParent(), editList.get(0)); + else if (editList.size() > 1) + editManager.doDataflowEdit(dataflow.getParent(), new CompoundEdit( + editList)); + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowOutputPortAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowOutputPortAction.java new file mode 100644 index 0000000..da7c0e2 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/EditDataflowOutputPortAction.java
@@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Set; + +import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog; +import net.sf.taverna.t2.workbench.design.ui.DataflowOutputPortPanel; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.RenameEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * Action for editing a dataflow output port. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class EditDataflowOutputPortAction extends DataflowEditAction { + private static final Logger logger = Logger + .getLogger(EditDataflowOutputPortAction.class); + + private OutputWorkflowPort port; + + public EditDataflowOutputPortAction(Workflow dataflow, + OutputWorkflowPort port, Component component, + EditManager editManager, SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.port = port; + putValue(SMALL_ICON, WorkbenchIcons.renameIcon); + putValue(NAME, "Edit workflow output port..."); + } + + @Override + public void actionPerformed(ActionEvent e) { + Set<String> usedOutputPorts = new HashSet<>(); + for (OutputWorkflowPort usedOutputPort : dataflow.getOutputPorts()) + if (!usedOutputPort.getName().equals(port.getName())) + usedOutputPorts.add(usedOutputPort.getName()); + + DataflowOutputPortPanel inputPanel = new DataflowOutputPortPanel(); + + ValidatingUserInputDialog vuid = new ValidatingUserInputDialog( + "Edit Workflow Output Port", inputPanel); + vuid.addTextComponentValidation(inputPanel.getPortNameField(), + "Set the workflow output port name.", usedOutputPorts, + "Duplicate workflow output port name.", + "[\\p{L}\\p{Digit}_.]+", "Invalid workflow output port name."); + vuid.setSize(new Dimension(400, 200)); + + inputPanel.setPortName(port.getName()); + + try { + if (vuid.show(component)) + changeOutputPort(inputPanel); + } catch (EditException ex) { + logger.debug("Rename workflow output port failed", ex); + } + } + + private void changeOutputPort(DataflowOutputPortPanel inputPanel) + throws EditException { + editManager.doDataflowEdit(dataflow.getParent(), new RenameEdit<>(port, + inputPanel.getPortName())); + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveConditionAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveConditionAction.java new file mode 100644 index 0000000..89036f0 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveConditionAction.java
@@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.RemoveChildEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.ControlLink; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * Action for removing a condition from the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class RemoveConditionAction extends DataflowEditAction { + private static final Logger logger = Logger + .getLogger(RemoveConditionAction.class); + + private ControlLink controlLink; + + public RemoveConditionAction(Workflow dataflow, ControlLink controlLink, + Component component, EditManager editManager, + SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.controlLink = controlLink; + putValue(SMALL_ICON, deleteIcon); + putValue(NAME, "Delete control link"); + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + dataflowSelectionModel.removeSelection(controlLink); + editManager.doDataflowEdit(dataflow.getParent(), + new RemoveChildEdit<>(dataflow, controlLink)); + } catch (EditException e1) { + logger.debug("Delete control link failed", e1); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowInputPortAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowInputPortAction.java new file mode 100644 index 0000000..5483ea5 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowInputPortAction.java
@@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit; +import net.sf.taverna.t2.workflow.edits.RemoveWorkflowInputPortEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; + +/** + * Action for removing an input port from the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class RemoveDataflowInputPortAction extends DataflowEditAction { + private static Logger logger = Logger + .getLogger(RemoveDataflowInputPortAction.class); + + private InputWorkflowPort port; + + public RemoveDataflowInputPortAction(Workflow dataflow, + InputWorkflowPort port, Component component, + EditManager editManager, SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.port = port; + putValue(SMALL_ICON, deleteIcon); + putValue(NAME, "Delete workflow input port"); + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + dataflowSelectionModel.removeSelection(port); + List<DataLink> datalinks = scufl2Tools.datalinksFrom(port); + if (datalinks.isEmpty()) + editManager.doDataflowEdit(dataflow.getParent(), + new RemoveWorkflowInputPortEdit(dataflow, port)); + else { + List<Edit<?>> editList = new ArrayList<>(); + for (DataLink datalink : datalinks) + editList.add(new RemoveDataLinkEdit(dataflow, datalink)); + editList.add(new RemoveWorkflowInputPortEdit(dataflow, port)); + editManager.doDataflowEdit(dataflow.getParent(), + new CompoundEdit(editList)); + } + } catch (EditException e1) { + logger.debug("Delete workflow input port failed", e1); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowOutputPortAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowOutputPortAction.java new file mode 100644 index 0000000..ed91d41 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDataflowOutputPortAction.java
@@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit; +import net.sf.taverna.t2.workflow.edits.RemoveWorkflowOutputPortEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * Action for removing an output port from the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class RemoveDataflowOutputPortAction extends DataflowEditAction { + private static final Logger logger = Logger + .getLogger(RemoveDataflowOutputPortAction.class); + + private OutputWorkflowPort port; + + public RemoveDataflowOutputPortAction(Workflow dataflow, + OutputWorkflowPort port, Component component, + EditManager editManager, SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.port = port; + putValue(SMALL_ICON, deleteIcon); + putValue(NAME, "Delete workflow output port"); + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + dataflowSelectionModel.removeSelection(port); + List<DataLink> datalinks = scufl2Tools.datalinksTo(port); + if (datalinks.isEmpty()) + editManager.doDataflowEdit(dataflow.getParent(), + new RemoveWorkflowOutputPortEdit(dataflow, port)); + else { + List<Edit<?>> editList = new ArrayList<>(); + for (DataLink datalink : datalinks) + editList.add(new RemoveDataLinkEdit(dataflow, datalink)); + editList.add(new RemoveWorkflowOutputPortEdit(dataflow, port)); + editManager.doDataflowEdit(dataflow.getParent(), + new CompoundEdit(editList)); + } + } catch (EditException ex) { + logger.debug("Delete workflow output port failed", ex); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDatalinkAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDatalinkAction.java new file mode 100644 index 0000000..e4df75d --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveDatalinkAction.java
@@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * Action for removing a datalink from the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class RemoveDatalinkAction extends DataflowEditAction { + private static final Logger logger = Logger.getLogger(RemoveDatalinkAction.class); + + private DataLink datalink; + + public RemoveDatalinkAction(Workflow dataflow, DataLink datalink, + Component component, EditManager editManager, + SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.datalink = datalink; + putValue(SMALL_ICON, deleteIcon); + putValue(NAME, "Delete data link"); + } + + @Override + public void actionPerformed(ActionEvent ev) { + try { + dataflowSelectionModel.removeSelection(datalink); + editManager.doDataflowEdit(dataflow.getParent(), + new RemoveDataLinkEdit(dataflow, datalink)); + } catch (EditException ex) { + logger.debug("Delete data link failed", ex); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveProcessorAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveProcessorAction.java new file mode 100644 index 0000000..063a346 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RemoveProcessorAction.java
@@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.RemoveChildEdit; +import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.NamedSet; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.BlockingControlLink; +import uk.org.taverna.scufl2.api.core.ControlLink; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +/** + * Action for removing a processor from the dataflow. + * + * @author David Withers + */ +@SuppressWarnings("serial") +public class RemoveProcessorAction extends DataflowEditAction { + private static final Logger logger = Logger + .getLogger(RemoveProcessorAction.class); + + private Processor processor; + + public RemoveProcessorAction(Workflow dataflow, Processor processor, + Component component, EditManager editManager, + SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.processor = processor; + putValue(SMALL_ICON, deleteIcon); + putValue(NAME, "Delete service"); + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + dataflowSelectionModel.removeSelection(processor); + + NamedSet<InputProcessorPort> inputPorts = processor.getInputPorts(); + NamedSet<OutputProcessorPort> outputPorts = processor + .getOutputPorts(); + List<BlockingControlLink> controlLinksBlocking = scufl2Tools + .controlLinksBlocking(processor); + List<BlockingControlLink> controlLinksWaitingFor = scufl2Tools + .controlLinksWaitingFor(processor); + List<Edit<?>> editList = new ArrayList<>(); + for (InputProcessorPort inputPort : inputPorts) + for (DataLink datalink : scufl2Tools.datalinksTo(inputPort)) + editList.add(new RemoveDataLinkEdit(dataflow, datalink)); + for (OutputProcessorPort outputPort : outputPorts) + for (DataLink datalink : scufl2Tools.datalinksFrom(outputPort)) + editList.add(new RemoveDataLinkEdit(dataflow, datalink)); + for (ControlLink controlLink : controlLinksBlocking) + editList.add(new RemoveChildEdit<>(dataflow, controlLink)); + for (ControlLink controlLink : controlLinksWaitingFor) + editList.add(new RemoveChildEdit<>(dataflow, controlLink)); + + for (Profile profile : dataflow.getParent().getProfiles()) { + List<ProcessorBinding> processorBindings = scufl2Tools + .processorBindingsForProcessor(processor, profile); + for (ProcessorBinding processorBinding : processorBindings) { + Activity boundActivity = processorBinding + .getBoundActivity(); + List<ProcessorBinding> processorBindingsToActivity = scufl2Tools + .processorBindingsToActivity(boundActivity); + if (processorBindingsToActivity.size() == 1) { + editList.add(new RemoveChildEdit<>(profile, + boundActivity)); + for (Configuration configuration : scufl2Tools + .configurationsFor(boundActivity, profile)) + editList.add(new RemoveChildEdit<Profile>(profile, + configuration)); + } + editList.add(new RemoveChildEdit<Profile>(profile, + processorBinding)); + } + } + for (Profile profile : dataflow.getParent().getProfiles()) { + List<Configuration> configurations = scufl2Tools + .configurationsFor(processor, profile); + for (Configuration configuration : configurations) + editList.add(new RemoveChildEdit<>(profile, configuration)); + } + if (editList.isEmpty()) + editManager.doDataflowEdit(dataflow.getParent(), + new RemoveChildEdit<>(dataflow, processor)); + else { + editList.add(new RemoveChildEdit<>(dataflow, processor)); + editManager.doDataflowEdit(dataflow.getParent(), + new CompoundEdit(editList)); + } + } catch (EditException e1) { + logger.error("Delete processor failed", e1); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RenameProcessorAction.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RenameProcessorAction.java new file mode 100644 index 0000000..5b5b733 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/actions/RenameProcessorAction.java
@@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.actions; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.HashSet; +import java.util.Set; + +import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog; +import net.sf.taverna.t2.workbench.design.ui.ProcessorPanel; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.RenameEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * Action for renaming a processor. + * + * @author David Withers + */ +public class RenameProcessorAction extends DataflowEditAction { + + private static final long serialVersionUID = 1L; + + private static Logger logger = Logger + .getLogger(RenameProcessorAction.class); + + private Processor processor; + + public RenameProcessorAction(Workflow dataflow, Processor processor, + Component component, EditManager editManager, + SelectionManager selectionManager) { + super(dataflow, component, editManager, selectionManager); + this.processor = processor; + putValue(SMALL_ICON, WorkbenchIcons.renameIcon); + putValue(NAME, "Rename service..."); + } + + @Override + public void actionPerformed(ActionEvent e) { + Set<String> usedProcessors = new HashSet<>(); + for (Processor usedProcessor : dataflow.getProcessors()) + if (!usedProcessor.getName().equals(processor.getName())) + usedProcessors.add(usedProcessor.getName()); + + ProcessorPanel inputPanel = new ProcessorPanel(); + + ValidatingUserInputDialog vuid = new ValidatingUserInputDialog( + "Rename service", inputPanel); + vuid.addTextComponentValidation(inputPanel.getProcessorNameField(), + "Set the service name.", usedProcessors, "Duplicate service.", + "[\\p{L}\\p{Digit}_.]+", "Invalid service name."); + vuid.setSize(new Dimension(400, 200)); + + inputPanel.setProcessorName(processor.getName()); + + try { + if (vuid.show(component)) + changeProcessorName(inputPanel); + } catch (EditException e1) { + logger.debug("Rename service (processor) failed", e1); + } + } + + private void changeProcessorName(ProcessorPanel inputPanel) + throws EditException { + String processorName = inputPanel.getProcessorName(); + editManager.doDataflowEdit(dataflow.getParent(), new RenameEdit<>( + processor, processorName)); + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowInputPortPanel.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowInputPortPanel.java new file mode 100644 index 0000000..e578ef2 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowInputPortPanel.java
@@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.ui; + +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.NORTHWEST; +import static java.awt.GridBagConstraints.WEST; +import static java.awt.event.ItemEvent.SELECTED; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.ButtonGroup; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerNumberModel; +import javax.swing.border.EmptyBorder; + +/** + * UI for creating/editing dataflow input ports. + * + * @author David Withers + */ +public class DataflowInputPortPanel extends JPanel { + private static final long serialVersionUID = 2650486705615513458L; + + private JTextField portNameField; + private JRadioButton singleValueButton; + private JRadioButton listValueButton; + private JSpinner listDepthSpinner; + + public DataflowInputPortPanel() { + super(new GridBagLayout()); + + portNameField = new JTextField(); + singleValueButton = new JRadioButton("Single value"); + listValueButton = new JRadioButton("List of depth "); + listDepthSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); + + setBorder(new EmptyBorder(10, 10, 10, 10)); + + GridBagConstraints constraints = new GridBagConstraints(); + + constraints.anchor = WEST; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.ipadx = 10; + add(new JLabel("Name:"), constraints); + + constraints.gridx = 1; + constraints.gridwidth = 2; + constraints.ipadx = 0; + constraints.weightx = 1d; + constraints.fill = HORIZONTAL; + add(portNameField, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + constraints.gridwidth = 1; + constraints.weightx = 0d; + constraints.fill = NONE; + constraints.ipadx = 10; + constraints.insets = new Insets(10, 0, 0, 0); + add(new JLabel("Type:"), constraints); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(singleValueButton); + buttonGroup.add(listValueButton); + + final JLabel helpLabel = new JLabel( + "Depth 1 is a list, 2 is a list of lists, etc."); + helpLabel.setFont(helpLabel.getFont().deriveFont(11f)); + + singleValueButton.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + boolean selected = (e.getStateChange() == SELECTED); + listDepthSpinner.setEnabled(!selected); + helpLabel.setEnabled(!selected); + } + }); + + constraints.gridx = 1; + constraints.gridwidth = 2; + constraints.ipadx = 0; + add(singleValueButton, constraints); + constraints.gridy = 2; + constraints.gridwidth = 1; + constraints.insets = new Insets(0, 0, 0, 0); + add(listValueButton, constraints); + constraints.gridx = 2; + add(listDepthSpinner, constraints); + constraints.gridx = 1; + constraints.gridy = 3; + constraints.gridwidth = 2; + constraints.weighty = 1d; + constraints.anchor = NORTHWEST; + constraints.insets = new Insets(0, 20, 0, 0); + add(helpLabel, constraints); + } + + /** + * Returns the portNameField. + * + * @return the portNameField + */ + public JTextField getPortNameField() { + return portNameField; + } + + /** + * Returns the singleValueButton. + * + * @return the singleValueButton + */ + public JRadioButton getSingleValueButton() { + return singleValueButton; + } + + /** + * Returns the listValueButton. + * + * @return the listValueButton + */ + public JRadioButton getListValueButton() { + return listValueButton; + } + + /** + * Returns the port name. + * + * @return the port name + */ + public String getPortName() { + return portNameField.getText(); + } + + /** + * Sets the port name. + * + * @param name + * the name of the port + */ + public void setPortName(String name) { + portNameField.setText(name); + // Select the text + if (!name.isEmpty()) { + portNameField.setSelectionStart(0); + portNameField.setSelectionEnd(name.length()); + } + } + + /** + * Returns the port depth. + * + * @return the port depth + */ + public int getPortDepth() { + if (singleValueButton.isSelected()) + return 0; + return (Integer) listDepthSpinner.getValue(); + } + + /** + * Sets the port depth. + * + * @param depth + * the depth of the port + */ + public void setPortDepth(int depth) { + if (depth == 0) { + singleValueButton.setSelected(true); + } else { + listValueButton.setSelected(true); + listDepthSpinner.setValue(depth); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowOutputPortPanel.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowOutputPortPanel.java new file mode 100644 index 0000000..59ac0f7 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/DataflowOutputPortPanel.java
@@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.ui; + +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.VERTICAL; +import static java.awt.GridBagConstraints.WEST; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; + +/** + * UI for creating/editing dataflow output ports. + * + * @author David Withers + */ +public class DataflowOutputPortPanel extends JPanel { + private static final long serialVersionUID = -2542858679939965553L; + + private JTextField portNameField; + + public DataflowOutputPortPanel() { + super(new GridBagLayout()); + + portNameField = new JTextField(); + + setBorder(new EmptyBorder(10, 10, 10, 10)); + + GridBagConstraints constraints = new GridBagConstraints(); + + constraints.anchor = WEST; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.ipadx = 10; + add(new JLabel("Name:"), constraints); + + constraints.gridx = 1; + constraints.gridwidth = 2; + constraints.ipadx = 0; + constraints.weightx = 1d; + constraints.fill = HORIZONTAL; + add(portNameField, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + constraints.fill = VERTICAL; + constraints.weighty = 1d; + add(new JPanel(), constraints); + } + + /** + * Returns the portNameField. + * + * @return the portNameField + */ + public JTextField getPortNameField() { + return portNameField; + } + + /** + * Returns the port name. + * + * @return the port name + */ + public String getPortName() { + return portNameField.getText(); + } + + /** + * Sets the port name. + * + * @param name the name of the port + */ + public void setPortName(String name) { + portNameField.setText(name); + // Select the text + if (!name.isEmpty()) { + portNameField.setSelectionStart(0); + portNameField.setSelectionEnd(name.length()); + } + } +}
diff --git a/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/ProcessorPanel.java b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/ProcessorPanel.java new file mode 100644 index 0000000..4528727 --- /dev/null +++ b/taverna-workbench-design-ui/src/main/java/net/sf/taverna/t2/workbench/design/ui/ProcessorPanel.java
@@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.design.ui; + +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.VERTICAL; +import static java.awt.GridBagConstraints.WEST; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; + +/** + * UI for editing processors. + * + * @author David Withers + */ +public class ProcessorPanel extends JPanel { + private static final long serialVersionUID = 260705376633425003L; + + private JTextField processorNameField; + + public ProcessorPanel() { + super(new GridBagLayout()); + + processorNameField = new JTextField(); + + setBorder(new EmptyBorder(10, 10, 10, 10)); + + GridBagConstraints constraints = new GridBagConstraints(); + + constraints.anchor = WEST; + constraints.gridx = 0; + constraints.gridy = 0; + constraints.ipadx = 10; + add(new JLabel("Name:"), constraints); + + constraints.gridx = 1; + constraints.gridwidth = 2; + constraints.ipadx = 0; + constraints.weightx = 1d; + constraints.fill = HORIZONTAL; + add(processorNameField, constraints); + + constraints.gridx = 0; + constraints.gridy = 1; + constraints.fill = VERTICAL; + constraints.weighty = 1d; + add(new JPanel(), constraints); + } + + /** + * Returns the processorNameField. + * + * @return the processorNameField + */ + public JTextField getProcessorNameField() { + return processorNameField; + } + + /** + * Returns the processor name. + * + * @return the processor name + */ + public String getProcessorName() { + return processorNameField.getText(); + } + + /** + * Sets the processor name. + * + * @param name + * the name of the processor + */ + public void setProcessorName(String name) { + processorNameField.setText(name); + } +}
diff --git a/taverna-workbench-edits-api/pom.xml b/taverna-workbench-edits-api/pom.xml new file mode 100644 index 0000000..1b52630 --- /dev/null +++ b/taverna-workbench-edits-api/pom.xml
@@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <packaging>bundle</packaging> + <name>Edits API</name> + <description>API for doing workflow edits and undo.</description> + <dependencies> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/CompoundEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/CompoundEdit.java new file mode 100644 index 0000000..58c7add --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/CompoundEdit.java
@@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits; + +import java.util.ArrayList; +import java.util.List; + +import uk.org.taverna.scufl2.api.common.WorkflowBean; + +/** + * Implementation of Edit which contains an ordered list of child edits. Child + * edits are applied collectively and in order, any failure in any child edit + * causes an undo of previously applied children and a propogation of the edit + * exception. + * + * @author Tom Oinn + */ +public class CompoundEdit implements Edit<WorkflowBean> { + private final transient List<Edit<?>> childEdits; + private transient boolean applied = false; + + /** + * Create a new compound edit with no existing Edit objects. + * + */ + public CompoundEdit() { + this.childEdits = new ArrayList<>(); + } + + /** + * Create a new compound edit with the specified edits as children. + */ + public CompoundEdit(List<Edit<?>> edits) { + this.childEdits = edits; + } + + /** + * Get the list of edits. + * @return a live-editable list. + */ + public List<Edit<?>> getChildEdits() { + return childEdits; + } + + /** + * Attempts to call the doEdit method of all child edits. If any of those + * children throws an EditException any successful edits are rolled back and + * the exception is rethrown as the cause of a new EditException from the + * CompoundEdit + */ + @Override + public synchronized WorkflowBean doEdit() throws EditException { + if (isApplied()) + throw new EditException("Cannot apply an edit more than once!"); + List<Edit<?>> doneEdits = new ArrayList<>(); + try { + for (Edit<?> edit : childEdits) { + edit.doEdit(); + /* + * Insert the done edit at position 0 in the list so we can + * iterate over the list in the normal order if we need to + * rollback, this ensures that the most recent edit is first. + */ + doneEdits.add(0, edit); + } + applied = true; + } catch (EditException ee) { + for (Edit<?> undoMe : doneEdits) + undoMe.undo(); + applied = false; + throw new EditException("Failed child of compound edit", ee); + } + return null; + } + + /** + * There is no explicit subject for a compound edit, so this method always + * returns null. + */ + @Override + public Object getSubject() { + return null; + } + + /** + * Rolls back all child edits in reverse order + */ + @Override + public synchronized void undo() { + for (int i = childEdits.size() - 1; i >= 0; i--) + // Undo child edits in reverse order + childEdits.get(i).undo(); + applied = false; + } + + @Override + public boolean isApplied() { + return applied; + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/Edit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/Edit.java new file mode 100644 index 0000000..fad211e --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/Edit.java
@@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits; + +import uk.org.taverna.scufl2.api.common.WorkflowBean; + +/** + * The workflow object model exposed by this API is read only. Properties of the + * model can only be changed through implementations of this interface, this + * ensures a consistant approach to grouped edits (transactions) and undo / redo + * support within the UI. It also potentially allows for capture of editing + * provenance where a workflow is repurposed or created from an aggregate of + * several others. + * + * @author Tom Oinn + */ +public interface Edit<TargetType extends WorkflowBean> { + /** + * Perform the edit + * + * @throws EditException + * if the edit fails. If an edit throws EditException it should + * try to ensure the subject is unaltered. Where this is + * impossible consider breaking edits down into a compound edit. + */ + TargetType doEdit() throws EditException; + + /** + * Undo the edit, reverting the subject to the state it was in prior to the + * edit + */ + void undo(); + + /** + * Return the object to which this edit applies + * + * @return + */ + Object getSubject(); + + /** + * Has the edit been applied yet? + * + * @return true if and only if the edit has been successfully applied to the + * subject + */ + boolean isApplied(); +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditException.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditException.java new file mode 100644 index 0000000..3c61d84 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditException.java
@@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits; + +/** + * Superclass of all exceptions thrown when altering the workflow model through + * the edit manager. + * + * @author Tom Oinn + */ +@SuppressWarnings("serial") +public class EditException extends Exception { + public EditException(String string) { + super(string); + } + + public EditException(String string, Throwable cause) { + super(string, cause); + } + + public EditException(Throwable t) { + super(t); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditManager.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditManager.java new file mode 100644 index 0000000..ce8b917 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/EditManager.java
@@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; + +/** + * Manager that can handle {@link Edit edits} for a {@link WorkflowBundle}. + * <p> + * Edits to a workflow that are to be undoable or redoable should be created by + * using {@link EditManager#getEdits()} to get an {@link Edits} object. Using + * this to create {@link Edit}s, instead of calling {@link Edit#doEdit()}, use + * {@link EditManager#doDataflowEdit(WorkflowBundle, Edit)} to associate the + * edit with the specified Dataflow. + * <p> + * It is possible to undo a series of edits done on a particular dataflow in + * this way by using {@link #undoDataflowEdit(WorkflowBundle)}. If one or more + * undoes have been performed, they can be redone step by step using + * {@link #redoDataflowEdit(WorkflowBundle)}. Note that it is no longer possible + * to call {@link #redoDataflowEdit(WorkflowBundle)} after a + * {@link #doDataflowEdit(WorkflowBundle, Edit)}. + * <p> + * The EditManager is {@link Observable}. If you + * {@linkplain Observable#addObserver(net.sf.taverna.t2.lang.observer.Observer) + * add an observer} you can be notified on {@linkplain DataflowEditEvent edits}, + * {@linkplain DataFlowUndoEvent undoes} and {@linkplain DataFlowRedoEvent + * redoes}. + * + * @author Stian Soiland-Reyes + */ +public interface EditManager extends Observable<EditManagerEvent> { + /** + * <code>true</code> if {@link #redoDataflowEdit(WorkflowBundle)} on the + * given dataflow would redo the last undone edit. If there are no previous + * edits, return <code>false</code>. + * + * @param dataflow + * {@link WorkflowBundle} which last affecting edit is to be + * undone + * @return <code>true</code if and only if + * {@link #redoDataflowEdit(WorkflowBundle)} would undo + */ + boolean canRedoDataflowEdit(WorkflowBundle dataflow); + + /** + * <code>true</code> if {@link #undoDataflowEdit(WorkflowBundle)} on the + * given dataflow would undo the last edit. If there are no previous edits, + * return <code>false</code>. + * + * @param dataflow + * {@link WorkflowBundle} which last affecting edit is to be + * undone + * @return <code>true</code if {@link #undoDataflowEdit(WorkflowBundle)} + * would undo + */ + boolean canUndoDataflowEdit(WorkflowBundle dataflow); + + /** + * Do an {@link Edit} affecting the given {@link WorkflowBundle}. + * <p> + * The edit is {@link Edit#doEdit() performed} and the edit can later be + * undone using {@link EditManager#undoDataflowEdit(WorkflowBundle)}. + * <p> + * Note that any events previously undone with + * {@link EditManager#undoDataflowEdit(WorkflowBundle)} for the given + * dataflow can no longer be + * {@link EditManager#redoDataflowEdit(WorkflowBundle) redone} after calling + * this method. + * + * @see EditManager#undoDataflowEdit(WorkflowBundle) + * @param dataflow + * {@link WorkflowBundle} this edit is affecting + * @param edit + * {@link Edit} that should be done using {@link Edit#doEdit()}. + * @throws EditException + * If {@link Edit#doEdit()} fails + */ + void doDataflowEdit(WorkflowBundle dataflow, Edit<?> edit) + throws EditException; + + /** + * Redo the last {@link Edit} that was undone using + * {@link #undoDataflowEdit(WorkflowBundle)}. + * <p> + * Note that the {@link EditManager} might only be able to redo a reasonable + * number of steps. + * <p> + * It is not possible to use {@link #redoDataflowEdit(WorkflowBundle)} after + * a {@link #doDataflowEdit(WorkflowBundle, Edit)} affecting the same + * {@link WorkflowBundle}, or if no edits have been undone yet. No action + * would be taken in these cases. + * + * @param dataflow + * {@link WorkflowBundle} which last affecting edit is to be + * redone + * @throws EditException + * If {@link Edit#doEdit()} fails + */ + void redoDataflowEdit(WorkflowBundle dataflow) throws EditException; + + /** + * Undo the last {@link Edit} affecting the given {@link WorkflowBundle}. + * <p> + * This can be called in succession until there are no more known undoes. + * Note that the {@link EditManager} might only be able to undo a reasonable + * number of steps. + * <p> + * The last edit must have been performed using + * {@link EditManager#doDataflowEdit(WorkflowBundle, Edit)} or + * {@link EditManager#redoDataflowEdit(WorkflowBundle)}. The undo is done + * using {@link Edit#undo()}. If no edits have been performed for the + * dataflow yet, no action is taken. + * <p> + * Undoes can be redone using {@link #redoDataflowEdit(WorkflowBundle)}. + * + * @param dataflow + * {@link WorkflowBundle} which last affecting edit is to be + * undone + */ + void undoDataflowEdit(WorkflowBundle dataflow); + + /** + * An event about an {@link Edit} on a {@link WorkflowBundle}, accessible + * through {@link AbstractDataflowEditEvent#getEdit()} and + * {@link AbstractDataflowEditEvent#getDataFlow()}. + */ + public static abstract class AbstractDataflowEditEvent implements + EditManagerEvent { + private final WorkflowBundle dataFlow; + private final Edit<?> edit; + + public AbstractDataflowEditEvent(WorkflowBundle dataFlow, Edit<?> edit) { + if (dataFlow == null || edit == null) + throw new NullPointerException( + "Dataflow and/or Edit can't be null"); + this.dataFlow = dataFlow; + this.edit = edit; + } + + /** + * The {@link WorkflowBundle} this event affected. + * + * @return A {@link WorkflowBundle} + */ + public WorkflowBundle getDataFlow() { + return dataFlow; + } + + /** + * The {@link Edit} that was performed, undoed or redone on the + * {@link #getDataFlow() dataflow}. + * + * @return An {@link Edit} + */ + @Override + public Edit<?> getEdit() { + return edit; + } + } + + /** + * An event sent when an {@link Edit} has been performed on a + * {@link WorkflowBundle}. + * + */ + public static class DataflowEditEvent extends AbstractDataflowEditEvent { + public DataflowEditEvent(WorkflowBundle dataFlow, Edit<?> edit) { + super(dataFlow, edit); + } + } + + /** + * An event sent when a previously undone {@link Edit} has been redone on a + * {@link WorkflowBundle}. + * + */ + public static class DataFlowRedoEvent extends AbstractDataflowEditEvent { + public DataFlowRedoEvent(WorkflowBundle dataFlow, Edit<?> edit) { + super(dataFlow, edit); + } + } + + /** + * An event sent when an {@link Edit} has been undone on a + * {@link WorkflowBundle}. + * + */ + public static class DataFlowUndoEvent extends AbstractDataflowEditEvent { + public DataFlowUndoEvent(WorkflowBundle dataFlow, Edit<?> edit) { + super(dataFlow, edit); + } + } + + /** + * An event given to {@link Observer}s registered with + * {@link Observable#addObserver(Observer)}. + */ + public interface EditManagerEvent { + public Edit<?> getEdit(); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/package-info.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/package-info.java new file mode 100644 index 0000000..c1df7c9 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workbench/edits/package-info.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +/** + * A {@link net.sf.taverna.t2.workbench.edits.EditManager} that can manage + * {@link net.sf.taverna.t2.workflowmodel.Edit}s performed from the UI. + * <p> + * To perform an edit that is to be undoable, use + * {@link EditManager#doDataflowEdit(net.sf.taverna.t2.workflowmodel.Dataflow, net.sf.taverna.t2.workflowmodel.Edit)} + * instead of {@link net.sf.taverna.t2.workflowmodel.Edit#doEdit()}. Such edits + * can be + * {@link EditManager#undoDataflowEdit(net.sf.taverna.t2.workflowmodel.Dataflow) undone} + * and + * {@link EditManager#redoDataflowEdit(net.sf.taverna.t2.workflowmodel.Dataflow) redone}. + * </p> + * <p> + * Edits are organised by {@link net.sf.taverna.t2.workflowmodel.Dataflow} so + * that if a user changes the active workflow in the Workbench and does "Undo" - + * that would undo the last undo done related to that workflow. + * </p> + * <p> + * The {@link net.sf.taverna.t2.workbench.edits.impl} implementation of the + * EditManager is discovered by {@link net.sf.taverna.t2.workbench.edits.EditManager#getInstance()}. The + * implementation also includes {@link net.sf.taverna.t2.ui.menu.MenuComponent}s + * for Undo and Redo. + * </p> + * + * @author Stian Soiland-Reyes + * + */ +package net.sf.taverna.t2.workbench.edits;
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AbstractEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AbstractEdit.java new file mode 100644 index 0000000..e6c8e68 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AbstractEdit.java
@@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2007-2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.common.WorkflowBean; + +/** + * An abstract {@link Edit} implementation that checks if an edit has been + * applied or not. + * + * @author Stian Soiland-Reyes + * + * @param <Subject> + * Subject of this edit + */ +public abstract class AbstractEdit<Subject extends WorkflowBean> implements + Edit<Subject> { + private boolean applied = false; + private final Subject subject; + protected final Scufl2Tools scufl2Tools = new Scufl2Tools(); + + /** + * Construct an AbstractEdit. + * + * @param subjectType + * The expected implementation type of the subject. The edit will + * not go through unless the subject is an instance of this type. + * If the edit don't care about the implementation type, provide + * the official SubjectInterface instead. + * @param subject + * The subject of this edit + */ + public AbstractEdit(Subject subject) { + if (subject == null && !isNullSubjectAllowed()) + throw new IllegalArgumentException( + "Cannot construct an edit with null subject"); + this.subject = subject; + } + + protected boolean isNullSubjectAllowed() { + return false; + } + + @Override + public final Subject doEdit() throws EditException { + if (applied) + throw new EditException("Edit has already been applied!"); + try { + synchronized (subject) { + doEditAction(subject); + applied = true; + return this.subject; + } + } catch (EditException ee) { + applied = false; + throw ee; + } + } + + /** + * Do the actual edit here + * + * @param subject + * The instance to which the edit applies + * @throws EditException + */ + protected abstract void doEditAction(Subject subject) + throws EditException; + + /** + * Undo any edit effects here + * + * @param subject + * The instance to which the edit applies + */ + protected abstract void undoEditAction(Subject subject); + + @Override + public final Subject getSubject() { + return subject; + } + + @Override + public final boolean isApplied() { + return applied; + } + + @Override + public final void undo() { + if (!applied) + throw new RuntimeException( + "Attempt to undo edit that was never applied"); + synchronized (subject) { + undoEditAction(subject); + applied = false; + } + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityEdit.java new file mode 100644 index 0000000..c6ffa7c --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityEdit.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; + +/** + * Creates a ProcessorBinding binding for the Activity and Processor and adds the binding to the + * Profile containing the Activity. + * + * @author David Withers + */ +public class AddActivityEdit extends AbstractEdit<Processor> { + private Activity activity; + private ProcessorBinding addedProcessorBinding; + + public AddActivityEdit(Processor processor, Activity activity) { + super(processor); + this.activity = activity; + } + + @Override + protected void doEditAction(Processor processor) { + ProcessorBinding binding = new ProcessorBinding(); + binding.setBoundProcessor(processor); + binding.setBoundActivity(activity); + binding.setParent(activity.getParent()); + addedProcessorBinding = binding; + } + + @Override + protected void undoEditAction(Processor processor) { + addedProcessorBinding.setParent(null); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityInputPortMappingEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityInputPortMappingEdit.java new file mode 100644 index 0000000..9a7e8b7 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityInputPortMappingEdit.java
@@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import java.util.ArrayList; +import java.util.List; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; + +public class AddActivityInputPortMappingEdit extends AbstractEdit<Activity> { + private final InputProcessorPort inputProcessorPort; + private final InputActivityPort inputActivityPort; + private List<ProcessorInputPortBinding> portBindings; + + public AddActivityInputPortMappingEdit(Activity activity, + InputProcessorPort inputProcessorPort, + InputActivityPort inputActivityPort) { + super(activity); + this.inputProcessorPort = inputProcessorPort; + this.inputActivityPort = inputActivityPort; + } + + @Override + protected void doEditAction(Activity activity) { + portBindings = new ArrayList<>(); + for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) + portBindings.add(new ProcessorInputPortBinding(binding, + inputProcessorPort, inputActivityPort)); + } + + @Override + protected void undoEditAction(Activity activity) { + for (ProcessorInputPortBinding binding : portBindings) + binding.setParent(null); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityOutputPortMappingEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityOutputPortMappingEdit.java new file mode 100644 index 0000000..edafe8e --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddActivityOutputPortMappingEdit.java
@@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import java.util.ArrayList; +import java.util.List; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; + +public class AddActivityOutputPortMappingEdit extends AbstractEdit<Activity> { + private final OutputProcessorPort outputProcessorPort; + private final OutputActivityPort outputActivityPort; + private List<ProcessorOutputPortBinding> portBindings; + + public AddActivityOutputPortMappingEdit(Activity activity, + OutputProcessorPort outputProcessorPort, + OutputActivityPort outputActivityPort) { + super(activity); + this.outputProcessorPort = outputProcessorPort; + this.outputActivityPort = outputActivityPort; + } + + @Override + protected void doEditAction(Activity activity) { + portBindings = new ArrayList<>(); + for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) + portBindings.add(new ProcessorOutputPortBinding(binding, + outputActivityPort, outputProcessorPort)); + } + + @Override + protected void undoEditAction(Activity activity) { + for (ProcessorOutputPortBinding binding : portBindings) + binding.setParent(null); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddChildEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddChildEdit.java new file mode 100644 index 0000000..abd8d9f --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddChildEdit.java
@@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.common.Child; +import uk.org.taverna.scufl2.api.common.WorkflowBean; + +/** + * Adds a child to a parent. + * + * @author David Withers + */ +public class AddChildEdit<T extends WorkflowBean> extends AbstractEdit<T> { + private Child<T> child; + + public AddChildEdit(T parent, Child<T> child) { + super(parent); + this.child = child; + } + + @Override + protected void doEditAction(T parent) { + child.setParent(parent); + } + + @Override + protected void undoEditAction(T parent) { + child.setParent(null); + } + + public Child<T> getChild() { + return child; + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddDataLinkEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddDataLinkEdit.java new file mode 100644 index 0000000..10ff290 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddDataLinkEdit.java
@@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import java.util.List; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.iterationstrategy.CrossProduct; +import uk.org.taverna.scufl2.api.iterationstrategy.DotProduct; +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyParent; +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode; +import uk.org.taverna.scufl2.api.iterationstrategy.PortNode; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.ReceiverPort; + +/** + * Adds a DataLink to a Workflow. + * <p> + * Handles setting the merge position of all dataLinks with the same receiver port. + * <p> + * Modifies the processor's iteration strategy or when the first DataLink is added. + * + * @author David Withers + */ +public class AddDataLinkEdit extends AbstractEdit<Workflow> { + private DataLink dataLink; + private PortNode portNode; + + public AddDataLinkEdit(Workflow workflow, DataLink dataLink) { + super(workflow); + this.dataLink = dataLink; + } + + @Override + protected void doEditAction(Workflow workflow) { + ReceiverPort sink = dataLink.getSendsTo(); + List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink); + if (datalinksTo.size() > 0) { + if (datalinksTo.size() == 1) + datalinksTo.get(0).setMergePosition(0); + dataLink.setMergePosition(datalinksTo.size()); + } else { + dataLink.setMergePosition(null); + if (sink instanceof InputProcessorPort) { + InputProcessorPort inputProcessorPort = (InputProcessorPort) sink; + for (IterationStrategyTopNode node : inputProcessorPort.getParent().getIterationStrategyStack()) { + portNode = new PortNode(node, inputProcessorPort); + portNode.setDesiredDepth(inputProcessorPort.getDepth()); + break; + } + } + } + dataLink.setParent(workflow); + } + + @Override + protected void undoEditAction(Workflow workflow) { + dataLink.setParent(null); + ReceiverPort sink = dataLink.getSendsTo(); + List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink); + if (datalinksTo.size() == 1) + datalinksTo.get(0).setMergePosition(null); + else if (datalinksTo.isEmpty()&&portNode != null) { + IterationStrategyParent parent = portNode.getParent(); + if (parent instanceof DotProduct) + ((DotProduct) parent).remove(portNode); + else if (parent instanceof CrossProduct) + ((CrossProduct) parent).remove(portNode); + } + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyEdit.java new file mode 100644 index 0000000..8677352 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyEdit.java
@@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2011 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack; +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode; + +/** + * Adds an IterationStrategyTopNode to an IterationStrategyStack. + * + * @author David Withers + */ +public class AddIterationStrategyEdit extends AbstractEdit<IterationStrategyStack> { + private final IterationStrategyTopNode iterationStrategyTopNode; + + public AddIterationStrategyEdit(IterationStrategyStack iterationStrategyStack, + IterationStrategyTopNode iterationStrategyTopNode) { + super(iterationStrategyStack); + this.iterationStrategyTopNode = iterationStrategyTopNode; + } + + @Override + public void doEditAction(IterationStrategyStack iterationStrategyStack) { + iterationStrategyStack.add(iterationStrategyTopNode); + } + + @Override + public void undoEditAction(IterationStrategyStack iterationStrategyStack) { + iterationStrategyStack.remove(iterationStrategyTopNode); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyInputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyInputPortEdit.java new file mode 100644 index 0000000..bb51fc6 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddIterationStrategyInputPortEdit.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2011 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack; +import uk.org.taverna.scufl2.api.iterationstrategy.PortNode; + +/** + * Adds an iteration strategy input port node to an iteration strategy. + * + * @author David Withers + */ +public class AddIterationStrategyInputPortEdit extends + AbstractEdit<IterationStrategyStack> { + private final PortNode portNode; + + public AddIterationStrategyInputPortEdit( + IterationStrategyStack iterationStrategy, PortNode portNode) { + super(iterationStrategy); + this.portNode = portNode; + } + + @Override + public void doEditAction(IterationStrategyStack iterationStrategy) { + portNode.setParent(iterationStrategy.get(0)); + } + + @Override + public void undoEditAction(IterationStrategyStack iterationStrategy) { + portNode.setParent(null); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorEdit.java new file mode 100644 index 0000000..04cf73b --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorEdit.java
@@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * Adds a Processor to a Workflow. + * + * @author Stuart Owen + * @author David Withers + */ +public class AddProcessorEdit extends AddChildEdit<Workflow> { + private Processor processor; + + public AddProcessorEdit(Workflow workflow, Processor processor) { + super(workflow, processor); + this.processor = processor; + } + + @Override + protected void doEditAction(Workflow workflow) { + getSubject().getProcessors().addWithUniqueName(processor); + super.doEditAction(workflow); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorInputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorInputPortEdit.java new file mode 100644 index 0000000..f722ddc --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorInputPortEdit.java
@@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; + +/** + * Adds an input port to a processor. + * + * @author Tom Oinn + * @author David Withers + */ +public class AddProcessorInputPortEdit extends AddChildEdit<Processor> { + private final InputProcessorPort port; + + public AddProcessorInputPortEdit(Processor processor, InputProcessorPort port) { + super(processor, port); + this.port = port; + } + + @Override + protected void doEditAction(Processor processor) { + processor.getInputPorts().addWithUniqueName(port); + super.doEditAction(processor); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorOutputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorOutputPortEdit.java new file mode 100644 index 0000000..eec51b1 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddProcessorOutputPortEdit.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; + +/** + * Adds an output port to a processor. + * + * @author Tom Oinn + * @author David Withers + */ +public class AddProcessorOutputPortEdit extends AddChildEdit<Processor> { + private final OutputProcessorPort port; + + public AddProcessorOutputPortEdit(Processor processor, + OutputProcessorPort port) { + super(processor, port); + this.port = port; + } + + @Override + protected void doEditAction(Processor processor) { + processor.getOutputPorts().addWithUniqueName(port); + super.doEditAction(processor); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowInputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowInputPortEdit.java new file mode 100644 index 0000000..91a9153 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowInputPortEdit.java
@@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW; + +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Adds an input port to a workflow. + * + * @author David Withers + */ +public class AddWorkflowInputPortEdit extends AbstractEdit<Workflow> { + private final InputWorkflowPort port; + private final CompoundEdit nestedPortEdit = new CompoundEdit(); + + public AddWorkflowInputPortEdit(Workflow workflow, InputWorkflowPort port) { + super(workflow); + this.port = port; + WorkflowBundle workflowBundle = workflow.getParent(); + if (workflowBundle != null) + for (Profile profile : workflowBundle.getProfiles()) + for (Activity activity : profile.getActivities()) + if (activity.getType().equals(NESTED_WORKFLOW)) + for (Configuration c : scufl2Tools.configurationsFor( + activity, profile)) + defineEditsForOneConfiguration(workflow, port, + workflowBundle, activity, c); + } + + private void defineEditsForOneConfiguration(Workflow workflow, + InputWorkflowPort port, WorkflowBundle workflowBundle, + Activity activity, Configuration c) { + List<Edit<?>> edits = nestedPortEdit.getChildEdits(); + JsonNode nested = c.getJson().get("nestedWorkflow"); + Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName( + nested.asText()); + + if (nestedWorkflow == workflow) { + InputActivityPort activityPort = new InputActivityPort(); + activityPort.setName(port.getName()); + activityPort.setDepth(port.getDepth()); + edits.add(new AddChildEdit<>(activity, activityPort)); + + for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) { + Processor processor = binding.getBoundProcessor(); + InputProcessorPort processorPort = new InputProcessorPort(); + processorPort.setName(port.getName()); + processorPort.setDepth(port.getDepth()); + edits.add(new AddProcessorInputPortEdit(processor, + processorPort)); + + ProcessorInputPortBinding portBinding = new ProcessorInputPortBinding(); + portBinding.setBoundProcessorPort(processorPort); + portBinding.setBoundActivityPort(activityPort); + edits.add(new AddChildEdit<>(binding, portBinding)); + } + } + } + + @Override + protected void doEditAction(Workflow workflow) throws EditException { + workflow.getInputPorts().addWithUniqueName(port); + port.setParent(workflow); + nestedPortEdit.doEdit(); + } + + @Override + protected void undoEditAction(Workflow workflow) { + port.setParent(null); + nestedPortEdit.undo(); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowOutputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowOutputPortEdit.java new file mode 100644 index 0000000..7e505f1 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/AddWorkflowOutputPortEdit.java
@@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW; + +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Adds an output port to a workflow. + * + * @author David Withers + */ +public class AddWorkflowOutputPortEdit extends AbstractEdit<Workflow> { + private final OutputWorkflowPort port; + private final CompoundEdit nestedPortEdit = new CompoundEdit(); + + public AddWorkflowOutputPortEdit(Workflow workflow, OutputWorkflowPort port) { + super(workflow); + this.port = port; + WorkflowBundle workflowBundle = workflow.getParent(); + if (workflowBundle != null) + for (Profile profile : workflowBundle.getProfiles()) + for (Activity activity : profile.getActivities()) + if (activity.getType().equals(NESTED_WORKFLOW)) + for (Configuration c : scufl2Tools.configurationsFor( + activity, profile)) + defineEditsForOneConfiguration(workflow, port, + workflowBundle, activity, c); + } + + private void defineEditsForOneConfiguration(Workflow workflow, + OutputWorkflowPort port, WorkflowBundle workflowBundle, + Activity activity, Configuration c) { + List<Edit<?>> edits = nestedPortEdit.getChildEdits(); + JsonNode nested = c.getJson().get("nestedWorkflow"); + Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName( + nested.asText()); + if (nestedWorkflow == workflow) { + OutputActivityPort activityPort = new OutputActivityPort(); + activityPort.setName(port.getName()); + activityPort.setDepth(0); + activityPort.setGranularDepth(0); + edits.add(new AddChildEdit<>(activity, activityPort)); + + for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) { + Processor processor = binding.getBoundProcessor(); + OutputProcessorPort processorPort = new OutputProcessorPort(); + processorPort.setName(port.getName()); + processorPort.setDepth(0); + processorPort.setGranularDepth(0); + edits.add(new AddProcessorOutputPortEdit(processor, + processorPort)); + + ProcessorOutputPortBinding portBinding = new ProcessorOutputPortBinding(); + portBinding.setBoundProcessorPort(processorPort); + portBinding.setBoundActivityPort(activityPort); + edits.add(new AddChildEdit<>(binding, portBinding)); + } + } + } + + @Override + protected void doEditAction(Workflow workflow) throws EditException { + workflow.getOutputPorts().addWithUniqueName(port); + port.setParent(workflow); + nestedPortEdit.doEdit(); + } + + @Override + protected void undoEditAction(Workflow workflow) { + port.setParent(null); + nestedPortEdit.undo(); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeDepthEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeDepthEdit.java new file mode 100644 index 0000000..071b653 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeDepthEdit.java
@@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.ActivityPort; +import uk.org.taverna.scufl2.api.port.DepthPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Changes the depth of a port. + * + * @author David Withers + */ +public class ChangeDepthEdit<T extends DepthPort> extends AbstractEdit<T> { + private Integer newDepth, oldDepth; + + public ChangeDepthEdit(T depthPort, Integer newDepth) { + super(depthPort); + this.newDepth = newDepth; + oldDepth = depthPort.getDepth(); + } + + @Override + protected void doEditAction(T depthPort) { + depthPort.setDepth(newDepth); + if (depthPort instanceof InputWorkflowPort) + checkNestedPortDepths((InputWorkflowPort) depthPort, newDepth); + } + + @Override + protected void undoEditAction(T depthPort) { + depthPort.setDepth(oldDepth); + if (depthPort instanceof InputWorkflowPort) + checkNestedPortDepths((InputWorkflowPort) depthPort, oldDepth); + } + + private void checkNestedPortDepths(InputWorkflowPort workflowPort, + Integer depth) { + Workflow workflow = workflowPort.getParent(); + if (workflow != null) { + WorkflowBundle workflowBundle = workflow.getParent(); + if (workflowBundle != null) + for (Profile profile : workflowBundle.getProfiles()) + for (Activity activity : profile.getActivities()) + if (activity.getType().equals(NESTED_WORKFLOW)) + for (Configuration c : scufl2Tools + .configurationsFor(activity, profile)) + checkOneConfiguration(workflowPort, depth, + workflow, workflowBundle, activity, c); + } + } + + private void checkOneConfiguration(InputWorkflowPort workflowPort, + Integer depth, Workflow workflow, WorkflowBundle workflowBundle, + Activity activity, Configuration c) { + JsonNode nested = c.getJson().get("nestedWorkflow"); + Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName( + nested.asText()); + if (nestedWorkflow != workflow) + return; + + ActivityPort activityPort = activity.getInputPorts().getByName( + workflowPort.getName()); + activityPort.setDepth(depth); + for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) + for (ProcessorInputPortBinding portBinding : binding + .getInputPortBindings()) + if (portBinding.getBoundActivityPort() == activityPort) { + InputProcessorPort processorPort = portBinding + .getBoundProcessorPort(); + processorPort.setDepth(depth); + } + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeGranularDepthEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeGranularDepthEdit.java new file mode 100644 index 0000000..e9f1b54 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeGranularDepthEdit.java
@@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.port.GranularDepthPort; + +/** + * Changes the granular depth of a port. + * + * @author David Withers + */ +public class ChangeGranularDepthEdit<T extends GranularDepthPort> extends + AbstractEdit<T> { + private Integer newGranularDepth, oldGranularDepth; + + public ChangeGranularDepthEdit(T granularDepthPort, Integer newGranularDepth) { + super(granularDepthPort); + this.newGranularDepth = newGranularDepth; + oldGranularDepth = granularDepthPort.getGranularDepth(); + } + + @Override + protected void doEditAction(T granularDepthPort) { + granularDepthPort.setGranularDepth(newGranularDepth); + } + + @Override + protected void undoEditAction(T granularDepthPort) { + granularDepthPort.setGranularDepth(oldGranularDepth); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeJsonEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeJsonEdit.java new file mode 100644 index 0000000..2c79ff3 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ChangeJsonEdit.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.configurations.Configuration; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Changes the JSON of a configuration. + * + * @author David Withers + */ +public class ChangeJsonEdit extends AbstractEdit<Configuration> { + private JsonNode oldJson, newJson; + + public ChangeJsonEdit(Configuration configuration, JsonNode newJson) { + super(configuration); + this.newJson = newJson; + newJson = configuration.getJson(); + } + + @Override + protected void doEditAction(Configuration configuration) { + configuration.setJson(newJson); + } + + @Override + protected void undoEditAction(Configuration configuration) { + configuration.setJson(oldJson); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ClearIterationStrategyStackEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ClearIterationStrategyStackEdit.java new file mode 100644 index 0000000..956857c --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ClearIterationStrategyStackEdit.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2011 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack; + +/** + * Removes all the iteration strategies from an iteration strategy stack. + * + * @author David Withers + */ +public class ClearIterationStrategyStackEdit extends + AbstractEdit<IterationStrategyStack> { + private IterationStrategyStack oldIterationStrategyStack; + + public ClearIterationStrategyStackEdit( + IterationStrategyStack iterationStrategyStack) { + super(iterationStrategyStack); + } + + @Override + protected void doEditAction(IterationStrategyStack iterationStrategyStack) { + oldIterationStrategyStack = new IterationStrategyStack(); + oldIterationStrategyStack.addAll(iterationStrategyStack); + iterationStrategyStack.clear(); + } + + @Override + public void undoEditAction(IterationStrategyStack iterationStrategyStack) { + iterationStrategyStack.addAll(oldIterationStrategyStack); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ConfigureEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ConfigureEdit.java new file mode 100644 index 0000000..f4274b0 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ConfigureEdit.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.common.Configurable; +import uk.org.taverna.scufl2.api.configurations.Configuration; + +/** + * An Edit that configures a {@link Configurable} with a given + * {@link Configuration}. + * + * @author David Withers + */ +public class ConfigureEdit<ConfigurableType extends Configurable> extends + AbstractEdit<ConfigurableType> { + private final Configuration oldConfiguration; + private final Configuration newConfiguration; + + public ConfigureEdit(ConfigurableType configurable, + Configuration oldConfiguration, Configuration newConfiguration) { + super(configurable); + this.oldConfiguration = oldConfiguration; + this.newConfiguration = newConfiguration; + } + + @Override + protected void doEditAction(ConfigurableType configurable) { + oldConfiguration.setConfigures(null); + newConfiguration.setConfigures(configurable); + } + + @Override + protected void undoEditAction(ConfigurableType configurable) { + oldConfiguration.setConfigures(configurable); + newConfiguration.setConfigures(null); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityEdit.java new file mode 100644 index 0000000..1350d88 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityEdit.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; + +/** + * Remove an Activity from a Processor. + * + * @author alanrw + */ +public class RemoveActivityEdit extends AbstractEdit<Processor> { + private Activity activityToRemove; + private ProcessorBinding removedProcessorBinding; + + public RemoveActivityEdit(Processor processor, Activity activity) { + super(processor); + this.activityToRemove = activity; + } + + @Override + protected void doEditAction(Processor processor) { + for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activityToRemove)) + if (binding.getBoundProcessor().equals(processor)) { + removedProcessorBinding = binding; + removedProcessorBinding.setParent(null); + } + } + + @Override + protected void undoEditAction(Processor processor) { + removedProcessorBinding.setParent(activityToRemove.getParent()); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityInputPortMappingEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityInputPortMappingEdit.java new file mode 100644 index 0000000..ce058bf --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityInputPortMappingEdit.java
@@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; + +public class RemoveActivityInputPortMappingEdit extends AbstractEdit<Activity> { + private final InputActivityPort inputActivityPort; + private ProcessorInputPortBinding removedPortBinding; + private ProcessorBinding processorBinding; + + public RemoveActivityInputPortMappingEdit(Activity activity, + InputActivityPort inputActivityPort) { + super(activity); + this.inputActivityPort = inputActivityPort; + } + + @Override + protected void doEditAction(Activity activity) { + removedPortBinding = scufl2Tools.processorPortBindingForPort( + inputActivityPort, activity.getParent()); + processorBinding = removedPortBinding.getParent(); + removedPortBinding.setParent(null); + } + + @Override + protected void undoEditAction(Activity activity) { + removedPortBinding.setParent(processorBinding); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityOutputPortMappingEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityOutputPortMappingEdit.java new file mode 100644 index 0000000..8639dbd --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveActivityOutputPortMappingEdit.java
@@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; + +public class RemoveActivityOutputPortMappingEdit extends AbstractEdit<Activity> { + private final OutputActivityPort outputActivityPort; + private ProcessorOutputPortBinding removedPortBinding; + private ProcessorBinding processorBinding; + + public RemoveActivityOutputPortMappingEdit(Activity activity, + OutputActivityPort outputActivityPort) { + super(activity); + this.outputActivityPort = outputActivityPort; + } + + @Override + protected void doEditAction(Activity activity) { + removedPortBinding = scufl2Tools.processorPortBindingForPort( + outputActivityPort, activity.getParent()); + processorBinding = removedPortBinding.getParent(); + removedPortBinding.setParent(null); + } + + @Override + protected void undoEditAction(Activity activity) { + removedPortBinding.setParent(processorBinding); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveChildEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveChildEdit.java new file mode 100644 index 0000000..1b9d5d2 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveChildEdit.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.common.Child; +import uk.org.taverna.scufl2.api.common.WorkflowBean; + +/** + * Removes a child from a parent. + * + * @author David Withers + */ +public class RemoveChildEdit<T extends WorkflowBean> extends AbstractEdit<T> { + private Child<T> child; + + public RemoveChildEdit(T parent, Child<T> child) { + super(parent); + this.child = child; + } + + @Override + protected void doEditAction(T parent) { + child.setParent(null); + } + + @Override + protected void undoEditAction(T parent) { + child.setParent(parent); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveDataLinkEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveDataLinkEdit.java new file mode 100644 index 0000000..9f8d243 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveDataLinkEdit.java
@@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import java.util.List; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyNode; +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyParent; +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyTopNode; +import uk.org.taverna.scufl2.api.iterationstrategy.PortNode; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.ReceiverPort; + +/** + * Remove a DataLink from a Workflow. + * <p> + * Handles setting the merge position of all dataLinks with the same receiver port. + * + * @author David Withers + */ +public class RemoveDataLinkEdit extends AbstractEdit<Workflow> { + private final DataLink dataLink; + private PortNode portNode; + private int portPosition; + private IterationStrategyTopNode parent; + + public RemoveDataLinkEdit(Workflow workflow, DataLink dataLink) { + super(workflow); + this.dataLink = dataLink; + } + + @Override + protected void doEditAction(Workflow workflow) { + dataLink.setParent(null); + ReceiverPort sink = dataLink.getSendsTo(); + List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink); + if (datalinksTo.isEmpty()) { + if (sink instanceof InputProcessorPort) { + InputProcessorPort port = (InputProcessorPort) sink; + for (IterationStrategyTopNode topNode : port.getParent().getIterationStrategyStack()) { + portNode = findPortNode(topNode, port); + if (portNode != null) { + IterationStrategyParent parentNode = portNode.getParent(); + if (parentNode instanceof IterationStrategyTopNode) { + parent = (IterationStrategyTopNode) parentNode; + portPosition = parent.indexOf(portNode); + parent.remove(portNode); + } + break; + } + } + } + } else if (datalinksTo.size() == 1) { + datalinksTo.get(0).setMergePosition(null); + } else { + for (int i = 0; i < datalinksTo.size(); i++) + datalinksTo.get(i).setMergePosition(i); + } + } + + @Override + protected void undoEditAction(Workflow workflow) { + ReceiverPort sink = dataLink.getSendsTo(); + List<DataLink> datalinksTo = scufl2Tools.datalinksTo(sink); + if (dataLink.getMergePosition() != null) + for (int i = dataLink.getMergePosition(); i < datalinksTo.size(); i++) + datalinksTo.get(i).setMergePosition(i + 1); + if (portNode != null) { + parent.add(portPosition, portNode); + portNode.setParent(parent); + } + dataLink.setParent(workflow); + } + + private PortNode findPortNode(IterationStrategyTopNode topNode, + InputProcessorPort port) { + for (IterationStrategyNode node : topNode) { + if (node instanceof PortNode) { + PortNode portNode = (PortNode) node; + if (port.equals(portNode.getInputProcessorPort())) + return portNode; + } else if (node instanceof IterationStrategyTopNode) { + IterationStrategyTopNode iterationStrategyTopNode = (IterationStrategyTopNode) node; + PortNode result = findPortNode(iterationStrategyTopNode, port); + if (result != null) + return result; + } + } + return null; + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorInputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorInputPortEdit.java new file mode 100644 index 0000000..497f700 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorInputPortEdit.java
@@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; + +public class RemoveProcessorInputPortEdit extends RemoveChildEdit<Processor> { + public RemoveProcessorInputPortEdit(Processor processor, + InputProcessorPort port) { + super(processor, port); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorOutputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorOutputPortEdit.java new file mode 100644 index 0000000..72a1238 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveProcessorOutputPortEdit.java
@@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; + +public class RemoveProcessorOutputPortEdit extends RemoveChildEdit<Processor> { + public RemoveProcessorOutputPortEdit(Processor processor, + OutputProcessorPort port) { + super(processor, port); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowInputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowInputPortEdit.java new file mode 100644 index 0000000..a6ac8e4 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowInputPortEdit.java
@@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW; + +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Removes an input port from a workflow. + * + * @author David Withers + */ +public class RemoveWorkflowInputPortEdit extends AbstractEdit<Workflow> { + private final InputWorkflowPort port; + private final CompoundEdit nestedPortEdit = new CompoundEdit(); + + public RemoveWorkflowInputPortEdit(Workflow workflow, InputWorkflowPort port) { + super(workflow); + this.port = port; + WorkflowBundle workflowBundle = workflow.getParent(); + if (workflowBundle != null) + for (Profile profile : workflowBundle.getProfiles()) + for (Activity activity : profile.getActivities()) + if (activity.getType().equals(NESTED_WORKFLOW)) + for (Configuration c : scufl2Tools.configurationsFor( + activity, profile)) + defineEditsForConfiguration(workflow, port, + workflowBundle, activity, c); + } + + private void defineEditsForConfiguration(Workflow workflow, + InputWorkflowPort port, WorkflowBundle workflowBundle, + Activity activity, Configuration c) { + List<Edit<?>> edits = nestedPortEdit.getChildEdits(); + JsonNode nested = c.getJson().get("nestedWorkflow"); + Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName( + nested.asText()); + if (nestedWorkflow != workflow) + return; + + InputActivityPort activityPort = activity.getInputPorts().getByName( + port.getName()); + edits.add(new RemoveChildEdit<>(activity, activityPort)); + + for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) { + Processor processor = binding.getBoundProcessor(); + for (ProcessorInputPortBinding portBinding : binding + .getInputPortBindings()) + if (portBinding.getBoundActivityPort() == activityPort) { + InputProcessorPort processorPort = portBinding + .getBoundProcessorPort(); + edits.add(new RemoveProcessorInputPortEdit(processor, + processorPort)); + edits.add(new RemoveChildEdit<>(binding, portBinding)); + } + } + } + + @Override + protected void doEditAction(Workflow workflow) throws EditException { + port.setParent(null); + nestedPortEdit.doEdit(); + } + + @Override + protected void undoEditAction(Workflow workflow) { + port.setParent(workflow); + nestedPortEdit.undo(); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowOutputPortEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowOutputPortEdit.java new file mode 100644 index 0000000..2c71da6 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RemoveWorkflowOutputPortEdit.java
@@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW; + +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Removes an output port from a workflow. + * + * @author David Withers + */ +public class RemoveWorkflowOutputPortEdit extends AbstractEdit<Workflow> { + private final OutputWorkflowPort port; + private final CompoundEdit nestedPortEdit = new CompoundEdit(); + + public RemoveWorkflowOutputPortEdit(Workflow workflow, + OutputWorkflowPort port) { + super(workflow); + this.port = port; + WorkflowBundle workflowBundle = workflow.getParent(); + if (workflowBundle != null) + for (Profile profile : workflowBundle.getProfiles()) + for (Activity activity : profile.getActivities()) + if (activity.getType().equals(NESTED_WORKFLOW)) + for (Configuration c : scufl2Tools.configurationsFor( + activity, profile)) + defineEditsForConfiguration(workflow, port, + workflowBundle, activity, c); + } + + private void defineEditsForConfiguration(Workflow workflow, + OutputWorkflowPort port, WorkflowBundle workflowBundle, + Activity activity, Configuration c) { + List<Edit<?>> edits = nestedPortEdit.getChildEdits(); + JsonNode nested = c.getJson().get("nestedWorkflow"); + Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName( + nested.asText()); + if (nestedWorkflow != workflow) + return; + + OutputActivityPort activityPort = activity.getOutputPorts().getByName( + port.getName()); + edits.add(new RemoveChildEdit<>(activity, activityPort)); + for (ProcessorBinding processorBinding : scufl2Tools + .processorBindingsToActivity(activity)) + for (ProcessorOutputPortBinding portBinding : processorBinding + .getOutputPortBindings()) + if (portBinding.getBoundActivityPort() == activityPort) { + OutputProcessorPort processorPort = portBinding + .getBoundProcessorPort(); + edits.add(new RemoveProcessorOutputPortEdit( + processorBinding.getBoundProcessor(), processorPort)); + edits.add(new RemoveChildEdit<>(processorBinding, + portBinding)); + } + } + + @Override + protected void doEditAction(Workflow workflow) throws EditException { + port.setParent(null); + nestedPortEdit.doEdit(); + } + + @Override + protected void undoEditAction(Workflow workflow) { + port.setParent(workflow); + nestedPortEdit.undo(); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RenameEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RenameEdit.java new file mode 100644 index 0000000..beb4913 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/RenameEdit.java
@@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Named; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.ActivityPort; +import uk.org.taverna.scufl2.api.port.InputPort; +import uk.org.taverna.scufl2.api.port.ProcessorPort; +import uk.org.taverna.scufl2.api.port.WorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorInputPortBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * Renames a Named WorkflowBean. + * + * @author David Withers + */ +public class RenameEdit<T extends Named> extends AbstractEdit<T> { + private String oldName, newName; + + public RenameEdit(T named, String newName) { + super(named); + this.newName = newName; + oldName = named.getName(); + } + + @Override + protected void doEditAction(T named) { + named.setName(newName); + if (named instanceof WorkflowPort) + checkNestedPortNames((WorkflowPort) named, oldName, newName); + } + + @Override + protected void undoEditAction(T named) { + named.setName(oldName); + if (named instanceof WorkflowPort) + checkNestedPortNames((WorkflowPort) named, newName, oldName); + } + + private void checkNestedPortNames(WorkflowPort workflowPort, String oldName, String newName) { + Workflow workflow = workflowPort.getParent(); + if (workflow == null) + return; + WorkflowBundle workflowBundle = workflow.getParent(); + if (workflowBundle == null) + return; + for (Profile profile : workflowBundle.getProfiles()) + for (Activity activity : profile.getActivities()) + if (activity.getType().equals(NESTED_WORKFLOW)) + for (Configuration c : scufl2Tools.configurationsFor(activity, profile)) + changeActivityPortName(workflowPort, oldName, + newName, workflow, workflowBundle, activity, c); + } + + private void changeActivityPortName(WorkflowPort workflowPort, + String oldName, String newName, Workflow workflow, + WorkflowBundle workflowBundle, Activity activity, Configuration c) { + JsonNode nested = c.getJson().get("nestedWorkflow"); + Workflow nestedWorkflow = workflowBundle.getWorkflows().getByName( + nested.asText()); + if (nestedWorkflow != workflow) + return; + + ActivityPort activityPort; + if (workflowPort instanceof InputPort) { + activityPort = activity.getInputPorts().getByName(oldName); + changeProcessorInputPortName(oldName, newName, activity, + activityPort); + } else { + activityPort = activity.getOutputPorts().getByName(oldName); + changeProcessorOutputPortName(oldName, newName, activity, + activityPort); + } + activityPort.setName(newName); + } + + private void changeProcessorInputPortName(String oldName, String newName, + Activity activity, ActivityPort activityPort) { + bindings: for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) + for (ProcessorInputPortBinding portBinding : binding + .getInputPortBindings()) + if (portBinding.getBoundActivityPort() == activityPort) { + ProcessorPort processorPort = portBinding + .getBoundProcessorPort(); + if (processorPort.getName().equals(oldName)) { + processorPort.setName(newName); + continue bindings; + } + } + } + + private void changeProcessorOutputPortName(String oldName, String newName, + Activity activity, ActivityPort activityPort) { + bindings: for (ProcessorBinding binding : scufl2Tools + .processorBindingsToActivity(activity)) + for (ProcessorOutputPortBinding portBinding : binding + .getOutputPortBindings()) + if (portBinding.getBoundActivityPort() == activityPort) { + ProcessorPort processorPort = portBinding + .getBoundProcessorPort(); + if (processorPort.getName().equals(oldName)) { + processorPort.setName(newName); + continue bindings; + } + } + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ReorderMergePositionsEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ReorderMergePositionsEdit.java new file mode 100644 index 0000000..49caa32 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/ReorderMergePositionsEdit.java
@@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.EditException; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.port.ReceiverPort; + +/** + * Change datalink merge positions based on ordered list of data links. + * + * @author David Withers + * @author Stian Soiland-Reyes + */ +public class ReorderMergePositionsEdit extends AbstractEdit<ReceiverPort> { + private List<DataLink> newMergePositions; + private final List<DataLink> oldMergePositions; + + public ReorderMergePositionsEdit(List<DataLink> dataLinks, + List<DataLink> newMergePositions) { + super(dataLinks.get(0).getSendsTo()); + this.oldMergePositions = dataLinks; + this.newMergePositions = newMergePositions; + } + + @Override + protected void doEditAction(ReceiverPort subject) throws EditException { + for (int i = 0; i < newMergePositions.size(); i++) + newMergePositions.get(i).setMergePosition(i); + } + + @Override + protected void undoEditAction(ReceiverPort subject) { + for (int i = 0; i < oldMergePositions.size(); i++) + oldMergePositions.get(i).setMergePosition(i); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/SetIterationStrategyStackEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/SetIterationStrategyStackEdit.java new file mode 100644 index 0000000..e16b95f --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/SetIterationStrategyStackEdit.java
@@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.iterationstrategy.IterationStrategyStack; + +/** + * Set the iteration strategy + * + * @author Stian Soiland-Reyes + */ +public class SetIterationStrategyStackEdit extends AbstractEdit<Processor> { + private final IterationStrategyStack iterationStrategyStack; + private IterationStrategyStack oldStrategyStack; + + public SetIterationStrategyStackEdit(Processor processor, + IterationStrategyStack iterationStrategyStack) { + super(processor); + this.iterationStrategyStack = iterationStrategyStack; + } + + @Override + protected void doEditAction(Processor processor) { + oldStrategyStack = processor.getIterationStrategyStack(); + processor.setIterationStrategyStack(iterationStrategyStack); + } + + @Override + protected void undoEditAction(Processor processor) { + processor.setIterationStrategyStack(oldStrategyStack); + } +}
diff --git a/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/UpdateDataflowInternalIdentifierEdit.java b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/UpdateDataflowInternalIdentifierEdit.java new file mode 100644 index 0000000..f246d60 --- /dev/null +++ b/taverna-workbench-edits-api/src/main/java/net/sf/taverna/t2/workflow/edits/UpdateDataflowInternalIdentifierEdit.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workflow.edits; + +import java.net.URI; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +public class UpdateDataflowInternalIdentifierEdit extends + AbstractEdit<WorkflowBundle> { + private URI newId; + private URI oldId; + + public UpdateDataflowInternalIdentifierEdit(WorkflowBundle dataflow, + URI newId) { + super(dataflow); + this.newId = newId; + this.oldId = dataflow.getGlobalBaseURI(); + } + + @Override + protected void doEditAction(WorkflowBundle dataflow) { + dataflow.setGlobalBaseURI(newId); + } + + @Override + protected void undoEditAction(WorkflowBundle dataflow) { + dataflow.setGlobalBaseURI(oldId); + } +}
diff --git a/taverna-workbench-edits-impl/pom.xml b/taverna-workbench-edits-impl/pom.xml new file mode 100644 index 0000000..b5b725a --- /dev/null +++ b/taverna-workbench-edits-impl/pom.xml
@@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>edits-impl</artifactId> + <packaging>bundle</packaging> + <name>Edits implementation</name> + <description> + Implementation for doing workflow edits and undo. + </description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + <version>${scufl2.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/EditManagerImpl.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/EditManagerImpl.java new file mode 100644 index 0000000..f97d36c --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/EditManagerImpl.java
@@ -0,0 +1,285 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.sf.taverna.t2.lang.observer.MultiCaster; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Implementation of {@link EditManager}. + * + * @author Stian Soiland-Reyes + */ +public class EditManagerImpl implements EditManager { + private static Logger logger = Logger.getLogger(EditManagerImpl.class); + + private MultiCaster<EditManagerEvent> multiCaster = new MultiCaster<>(this); + private Map<WorkflowBundle, DataflowEdits> editsForDataflow = new HashMap<>(); + + @Override + public void addObserver(Observer<EditManagerEvent> observer) { + multiCaster.addObserver(observer); + } + + @Override + public boolean canRedoDataflowEdit(WorkflowBundle dataflow) { + DataflowEdits edits = getEditsForDataflow(dataflow); + return edits.canRedo(); + } + + @Override + public boolean canUndoDataflowEdit(WorkflowBundle dataflow) { + DataflowEdits edits = getEditsForDataflow(dataflow); + return edits.canUndo(); + } + + @Override + public void doDataflowEdit(WorkflowBundle dataflow, Edit<?> edit) + throws EditException { + // We do the edit before we notify the observers + DataflowEdits edits = getEditsForDataflow(dataflow); + synchronized (edits) { + // Make sure the edits are in the order they were performed + edit.doEdit(); + edits.addEdit(edit); + } + multiCaster.notify(new DataflowEditEvent(dataflow, edit)); + } + + @Override + public List<Observer<EditManagerEvent>> getObservers() { + return multiCaster.getObservers(); + } + + @Override + public void redoDataflowEdit(WorkflowBundle dataflow) throws EditException { + DataflowEdits edits = getEditsForDataflow(dataflow); + Edit<?> edit; + synchronized (edits) { + if (!edits.canRedo()) + return; + edit = edits.getLastUndo(); + edit.doEdit(); + edits.addRedo(edit); + } + multiCaster.notify(new DataFlowRedoEvent(dataflow, edit)); + } + + @Override + public void removeObserver(Observer<EditManagerEvent> observer) { + multiCaster.removeObserver(observer); + } + + @Override + public void undoDataflowEdit(WorkflowBundle dataflow) { + DataflowEdits edits = getEditsForDataflow(dataflow); + Edit<?> edit; + synchronized (edits) { + if (!edits.canUndo()) + return; + edit = edits.getLastEdit(); + edit.undo(); + edits.addUndo(edit); + } + logger.info("Undoing an edit"); + multiCaster.notify(new DataFlowUndoEvent(dataflow, edit)); + } + + /** + * Get the set of edits for a given dataflow, creating if neccessary. + * + * @param dataflow + * Dataflow the edits relate to + * @return A {@link DataflowEdits} instance to keep edits for the given + * dataflow + */ + protected synchronized DataflowEdits getEditsForDataflow(WorkflowBundle dataflow) { + DataflowEdits edits = editsForDataflow.get(dataflow); + if (edits == null) { + edits = new DataflowEdits(); + editsForDataflow.put(dataflow, edits); + } + return edits; + } + + /** + * A set of edits and undoes for a {@link Dataflow} + * + * @author Stian Soiland-Reyes + * + */ + public class DataflowEdits { + /** + * List of edits that have been performed and can be undone. + */ + private List<Edit<?>> edits = new ArrayList<>(); + /** + * List of edits that have been undone and can be redone + */ + private List<Edit<?>> undoes = new ArrayList<>(); + + /** + * Add an {@link Edit} that has been done by the EditManager. + * <p> + * This can later be retrieved using {@link #getLastEdit()}. After + * calling this {@link #canRedo()} will be false. + * + * @param edit + * {@link Edit} that has been undone + */ + public synchronized void addEdit(Edit<?> edit) { + addEditOrRedo(edit, false); + } + + /** + * Add an {@link Edit} that has been redone by the EditManager. + * <p> + * The {@link Edit} must be the same as the last undo returned through + * {@link #getLastUndo()}. + * <p> + * This method works like {@link #addEdit(Edit)} except that instead of + * removing all possible redoes, only the given {@link Edit} is removed. + * + * @param edit + * {@link Edit} that has been redone + */ + public synchronized void addRedo(Edit<?> edit) { + addEditOrRedo(edit, true); + } + + /** + * Add an {@link Edit} that has been undone by the EditManager. + * <p> + * After calling this method {@link #canRedo()} will be true, and the + * edit can be retrieved using {@link #getLastUndo()}. + * </p> + * <p> + * The {@link Edit} must be the last edit returned from + * {@link #getLastEdit()}, after calling this method + * {@link #getLastEdit()} will return the previous edit or + * {@link #canUndo()} will be false if there are no more edits. + * + * @param edit + * {@link Edit} that has been undone + */ + public synchronized void addUndo(Edit<?> edit) { + int lastIndex = edits.size() - 1; + if (lastIndex < 0 || !edits.get(lastIndex).equals(edit)) + throw new IllegalArgumentException("Can't undo unknown edit " + + edit); + undoes.add(edit); + edits.remove(lastIndex); + } + + /** + * True if there are undone events that can be redone. + * + * @return <code>true</code> if there are undone events + */ + public boolean canRedo() { + return !undoes.isEmpty(); + } + + /** + * True if there are edits that can be undone and later added with + * {@link #addUndo(Edit)}. + * + * @return <code>true</code> if there are edits that can be undone + */ + public boolean canUndo() { + return !edits.isEmpty(); + } + + /** + * Get the last edit that can be undone. This edit was the last one to + * be added with {@link #addEdit(Edit)} or {@link #addRedo(Edit)}. + * + * @return The last added {@link Edit} + * @throws IllegalStateException + * If there are no more edits (Check with {@link #canUndo()} + * first) + * + */ + public synchronized Edit<?> getLastEdit() throws IllegalStateException { + if (edits.isEmpty()) + throw new IllegalStateException("No more edits"); + int lastEdit = edits.size() - 1; + return edits.get(lastEdit); + } + + /** + * Get the last edit that can be redone. This edit was the last one to + * be added with {@link #addUndo(Edit)}. + * + * @return The last undone {@link Edit} + * @throws IllegalStateException + * If there are no more edits (Check with {@link #canRedo()} + * first) + * + */ + public synchronized Edit<?> getLastUndo() throws IllegalStateException { + if (undoes.isEmpty()) + throw new IllegalStateException("No more undoes"); + int lastUndo = undoes.size() - 1; + return undoes.get(lastUndo); + } + + /** + * Add an edit or redo. Common functionallity called by + * {@link #addEdit(Edit)} and {@link #addRedo(Edit)}. + * + * @see #addEdit(Edit) + * @see #addRedo(Edit) + * @param edit + * The {@link Edit} to add + * @param isRedo + * True if this is a redo + */ + protected void addEditOrRedo(Edit<?> edit, boolean isRedo) { + edits.add(edit); + if (undoes.isEmpty()) + return; + if (isRedo) { + // It's a redo, remove only the last one + int lastUndoIndex = undoes.size() - 1; + Edit<?> lastUndo = undoes.get(lastUndoIndex); + if (!edit.equals(lastUndo)) + throw new IllegalArgumentException( + "Can only redo last undo"); + undoes.remove(lastUndoIndex); + } else + // It's a new edit, remove all redos + undoes.clear(); + } + } +}
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/AbstractUndoAction.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/AbstractUndoAction.java new file mode 100644 index 0000000..97d14a6 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/AbstractUndoAction.java
@@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl.menu; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_Y; +import static java.awt.event.KeyEvent.VK_Z; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.redoIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.undoIcon; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; +import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * @author David Withers + */ +@SuppressWarnings("serial") +public abstract class AbstractUndoAction extends AbstractAction { + protected EditManager editManager; + private SelectionManager selectionManager; + + public AbstractUndoAction(String label, EditManager editManager) { + super(label); + this.editManager = editManager; + if (label.equals("Undo")) { + this.putValue(SMALL_ICON, undoIcon); + this.putValue(SHORT_DESCRIPTION, "Undo an action"); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_Z, getDefaultToolkit() + .getMenuShortcutKeyMask())); + } else if (label.equals("Redo")) { + this.putValue(SMALL_ICON, redoIcon); + this.putValue(SHORT_DESCRIPTION, "Redo an action"); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_Y, getDefaultToolkit() + .getMenuShortcutKeyMask())); + } + editManager.addObserver(new EditManagerObserver()); + updateStatus(); + } + + @Override + public void actionPerformed(ActionEvent e) { + WorkflowBundle workflowBundle = getCurrentDataflow(); + if (workflowBundle != null) + performUndoOrRedo(workflowBundle); + } + + /** + * Check if action should be enabled or disabled and update its status. + */ + public void updateStatus() { + WorkflowBundle workflowBundle = getCurrentDataflow(); + if (workflowBundle == null) + setEnabled(false); + setEnabled(isActive(workflowBundle)); + } + + /** + * Retrieve the current dataflow from the {@link ModelMap}, or + * <code>null</code> if no workflow is active. + * + * @return The current {@link Dataflow} + */ + protected WorkflowBundle getCurrentDataflow() { + if (selectionManager == null) + return null; + return selectionManager.getSelectedWorkflowBundle(); + } + + /** + * Return <code>true</code> if the action should be enabled when the given + * {@link Dataflow} is the current, ie. if it's undoable or redoable. + * + * @param dataflow + * Current {@link Dataflow} + * @return <code>true</code> if the action should be enabled. + */ + protected abstract boolean isActive(WorkflowBundle workflowBundle); + + /** + * Called by {@link #actionPerformed(ActionEvent)} when the current dataflow + * is not <code>null</code>. + * + * @param dataflow + * {@link Dataflow} on which to undo or redo + */ + protected abstract void performUndoOrRedo(WorkflowBundle workflowBundle); + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + if (selectionManager != null) + selectionManager.addObserver(new SelectionManagerObserver()); + } + + /** + * Update the status if there's been an edit done on the current workflow. + * + */ + protected class EditManagerObserver implements Observer<EditManagerEvent> { + @Override + public void notify(Observable<EditManagerEvent> sender, + EditManagerEvent message) throws Exception { + if (!(message instanceof AbstractDataflowEditEvent)) + return; + AbstractDataflowEditEvent dataflowEdit = (AbstractDataflowEditEvent) message; + if (dataflowEdit.getDataFlow().equals(dataflowEdit.getDataFlow())) + // It's an edit that could effect our undoability + updateStatus(); + } + } + + private final class SelectionManagerObserver extends + SwingAwareObserver<SelectionManagerEvent> { + private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective"; + + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, + SelectionManagerEvent message) { + if (message instanceof WorkflowBundleSelectionEvent) + updateStatus(); + else if (message instanceof PerspectiveSelectionEvent) { + PerspectiveSelectionEvent perspectiveSelectionEvent = (PerspectiveSelectionEvent) message; + if (DESIGN_PERSPECTIVE_ID.equals(perspectiveSelectionEvent + .getSelectedPerspective().getID())) + updateStatus(); + else + setEnabled(false); + } + } + } +}
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/RedoMenuAction.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/RedoMenuAction.java new file mode 100644 index 0000000..2abc139 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/RedoMenuAction.java
@@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl.menu; + +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection.UNDO_SECTION_URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Redo the previous {@link Edit} done on the current workflow using the + * {@link EditManager}. + * + * @author Stian Soiland-Reyes + */ +public class RedoMenuAction extends AbstractMenuAction { + private static Logger logger = Logger.getLogger(RedoMenuAction.class); + private final EditManager editManager; + private SelectionManager selectionManager; + private AbstractUndoAction undoAction; + + public RedoMenuAction(EditManager editManager) { + super(UNDO_SECTION_URI, 20); + this.editManager = editManager; + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + undoAction = new AbstractUndoAction("Redo", editManager) { + @Override + protected boolean isActive(WorkflowBundle workflowBundle) { + return editManager.canRedoDataflowEdit(workflowBundle); + } + + @Override + protected void performUndoOrRedo(WorkflowBundle workflowBundle) { + try { + editManager.redoDataflowEdit(workflowBundle); + } catch (EditException | RuntimeException e) { + logger.warn("Could not redo for " + workflowBundle, e); + showMessageDialog(null, "Could not redo for workflow " + + workflowBundle + ":\n" + e, "Could not redo", + ERROR_MESSAGE); + } + } + }; + undoAction.setSelectionManager(selectionManager); + return undoAction; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + if (undoAction != null) + undoAction.setSelectionManager(selectionManager); + } +}
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuAction.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuAction.java new file mode 100644 index 0000000..e1242b3 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuAction.java
@@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl.menu; + +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection.UNDO_SECTION_URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Undo the last {@link Edit} done on the current workflow using the + * {@link EditManager}. + * + * @author Stian Soiland-Reyes + * + */ +public class UndoMenuAction extends AbstractMenuAction { + private static Logger logger = Logger.getLogger(UndoMenuAction.class); + private final EditManager editManager; + private SelectionManager selectionManager; + private AbstractUndoAction undoAction; + + public UndoMenuAction(EditManager editManager) { + super(UNDO_SECTION_URI, 10); + this.editManager = editManager; + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + undoAction = new AbstractUndoAction("Undo", editManager) { + @Override + protected boolean isActive(WorkflowBundle workflowBundle) { + return editManager.canUndoDataflowEdit(workflowBundle); + } + + @Override + protected void performUndoOrRedo(WorkflowBundle workflowBundle) { + try { + editManager.undoDataflowEdit(workflowBundle); + } catch (RuntimeException e) { + logger.warn("Could not undo for " + workflowBundle, e); + showMessageDialog(null, "Could not undo for workflow " + + workflowBundle + ":\n" + e, "Could not undo", + ERROR_MESSAGE); + } + } + }; + undoAction.setSelectionManager(selectionManager); + return undoAction; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + if (undoAction != null) + undoAction.setSelectionManager(selectionManager); + } +}
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuSection.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuSection.java new file mode 100644 index 0000000..b83a650 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/menu/UndoMenuSection.java
@@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl.menu; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * A section of the Edit menu that contains {@link UndoMenuSection undo} and + * {@link RedoMenuAction redo}. + * + * @author Stian Soiland-Reyes + */ +public class UndoMenuSection extends AbstractMenuSection { + public static final URI UNDO_SECTION_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/edits#undoSection"); + public static final URI EDIT_MENU_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#edit"); + + public UndoMenuSection() { + super(EDIT_MENU_URI, 10, UNDO_SECTION_URI); + } +}
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/EditToolbarSection.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/EditToolbarSection.java new file mode 100644 index 0000000..9eea85a --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/EditToolbarSection.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl.toolbar; + +import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +public class EditToolbarSection extends AbstractMenuSection { + public static final URI EDIT_TOOLBAR_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#editToolbarSection"); + + public EditToolbarSection() { + super(DEFAULT_TOOL_BAR, 60, EDIT_TOOLBAR_SECTION); + } +}
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/RedoToolbarAction.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/RedoToolbarAction.java new file mode 100644 index 0000000..09c0058 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/RedoToolbarAction.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl.toolbar; + +import static net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection.EDIT_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.impl.menu.RedoMenuAction; + +public class RedoToolbarAction extends AbstractMenuAction { + private static final URI EDIT_TOOLBAR_REDO_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#editToolbarRedo"); + private final RedoMenuAction redoMenuAction; + + public RedoToolbarAction(RedoMenuAction redoMenuAction) { + super(EDIT_TOOLBAR_SECTION, 20, EDIT_TOOLBAR_REDO_URI); + this.redoMenuAction = redoMenuAction; + } + + @Override + protected Action createAction() { + return redoMenuAction.getAction(); + } +} \ No newline at end of file
diff --git a/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/UndoToolbarAction.java b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/UndoToolbarAction.java new file mode 100644 index 0000000..8e31ed3 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/java/net/sf/taverna/t2/workbench/edits/impl/toolbar/UndoToolbarAction.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl.toolbar; + +import static net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection.EDIT_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuAction; + +public class UndoToolbarAction extends AbstractMenuAction { + private static final URI EDIT_TOOLBAR_UNDO_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#editToolbarUndo"); + private final UndoMenuAction undoMenuAction; + + public UndoToolbarAction(UndoMenuAction undoMenuAction) { + super(EDIT_TOOLBAR_SECTION, 10, EDIT_TOOLBAR_UNDO_URI); + this.undoMenuAction = undoMenuAction; + } + + @Override + protected Action createAction() { + return undoMenuAction.getAction(); + } +}
diff --git a/taverna-workbench-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..6938308 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,6 @@ +net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection +net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuAction +net.sf.taverna.t2.workbench.edits.impl.menu.RedoMenuAction +net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection +net.sf.taverna.t2.workbench.edits.impl.toolbar.UndoToolbarAction +net.sf.taverna.t2.workbench.edits.impl.toolbar.RedoToolbarAction
diff --git a/taverna-workbench-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.edits.EditManager b/taverna-workbench-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.edits.EditManager new file mode 100644 index 0000000..92ee088 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.edits.EditManager
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl
diff --git a/taverna-workbench-edits-impl/src/main/resources/META-INF/spring/edits-impl-context-osgi.xml b/taverna-workbench-edits-impl/src/main/resources/META-INF/spring/edits-impl-context-osgi.xml new file mode 100644 index 0000000..8eb7041 --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/resources/META-INF/spring/edits-impl-context-osgi.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="UndoMenuSection" auto-export="interfaces" /> + <service ref="UndoMenuAction" auto-export="interfaces" /> + <service ref="RedoMenuAction" auto-export="interfaces" /> + <service ref="EditToolbarSection" auto-export="interfaces" /> + <service ref="UndoToolbarAction" auto-export="interfaces" /> + <service ref="RedoToolbarAction" auto-export="interfaces" /> + + <service ref="EditManagerImpl" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" cardinality="0..1" /> + +</beans:beans>
diff --git a/taverna-workbench-edits-impl/src/main/resources/META-INF/spring/edits-impl-context.xml b/taverna-workbench-edits-impl/src/main/resources/META-INF/spring/edits-impl-context.xml new file mode 100644 index 0000000..33f0b7b --- /dev/null +++ b/taverna-workbench-edits-impl/src/main/resources/META-INF/spring/edits-impl-context.xml
@@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="UndoMenuSection" class="net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuSection" /> + <bean id="UndoMenuAction" class="net.sf.taverna.t2.workbench.edits.impl.menu.UndoMenuAction"> + <constructor-arg name="editManager"> + <ref local="EditManagerImpl" /> + </constructor-arg> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RedoMenuAction" class="net.sf.taverna.t2.workbench.edits.impl.menu.RedoMenuAction"> + <constructor-arg name="editManager"> + <ref local="EditManagerImpl" /> + </constructor-arg> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="EditToolbarSection" class="net.sf.taverna.t2.workbench.edits.impl.toolbar.EditToolbarSection" /> + <bean id="UndoToolbarAction" class="net.sf.taverna.t2.workbench.edits.impl.toolbar.UndoToolbarAction"> + <constructor-arg> + <ref local="UndoMenuAction" /> + </constructor-arg> + </bean> + <bean id="RedoToolbarAction" class="net.sf.taverna.t2.workbench.edits.impl.toolbar.RedoToolbarAction"> + <constructor-arg> + <ref local="RedoMenuAction" /> + </constructor-arg> + </bean> + + <bean id="EditManagerImpl" class="net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl" /> + +</beans>
diff --git a/taverna-workbench-edits-impl/src/test/java/net/sf/taverna/t2/workbench/edits/impl/TestEditManagerImpl.java b/taverna-workbench-edits-impl/src/test/java/net/sf/taverna/t2/workbench/edits/impl/TestEditManagerImpl.java new file mode 100644 index 0000000..9123671 --- /dev/null +++ b/taverna-workbench-edits-impl/src/test/java/net/sf/taverna/t2/workbench/edits/impl/TestEditManagerImpl.java
@@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.edits.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowRedoEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.DataFlowUndoEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.DataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workflow.edits.AddProcessorEdit; + +import org.junit.Before; +import org.junit.Test; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class TestEditManagerImpl { + + private Workflow dataflow; + + private EditManagerObserver editManagerObserver = new EditManagerObserver(); + + private Processor processor; + + @Test + public void addProcessor() throws Exception { + EditManager editManager = new EditManagerImpl(); + editManager.addObserver(editManagerObserver); + + Edit<Workflow> edit = new AddProcessorEdit(dataflow, processor); + assertFalse("Edit was already applied", edit.isApplied()); + assertTrue("Did already add processor", dataflow.getProcessors() + .isEmpty()); + + editManager.doDataflowEdit(dataflow.getParent(), edit); + assertTrue("Edit was not applied", edit.isApplied()); + assertEquals("Did not add processor", processor, dataflow.getProcessors().first()); + + // Should have received the edit event + assertEquals("Incorrect number of events", 1, + editManagerObserver.events.size()); + EditManagerEvent event = editManagerObserver.events.get(0); + assertTrue("Event was not a DataflowEditEvent", + event instanceof DataflowEditEvent); + DataflowEditEvent dataEditEvent = (DataflowEditEvent) event; + assertEquals("Event did not have correct workflow", dataflow, + dataEditEvent.getDataFlow().getWorkflows().first()); + assertEquals("Event did not have correct edit", edit, dataEditEvent + .getEdit()); + + } + + @Test + public void undoAddProcessor() throws Exception { + EditManager editManager = new EditManagerImpl(); + editManager.addObserver(editManagerObserver); + + Edit<Workflow> edit = new AddProcessorEdit(dataflow, processor); + editManager.doDataflowEdit(dataflow.getParent(), edit); + + assertFalse("Did not add processor", dataflow.getProcessors().isEmpty()); + editManager.undoDataflowEdit(dataflow.getParent()); + assertTrue("Did not undo add processor", dataflow.getProcessors() + .isEmpty()); + + // Should have received the undo event + assertEquals("Incorrect number of events", 2, + editManagerObserver.events.size()); + EditManagerEvent event = editManagerObserver.events.get(1); + assertTrue("Event was not a DataflowEditEvent", + event instanceof DataFlowUndoEvent); + DataFlowUndoEvent dataEditEvent = (DataFlowUndoEvent) event; + assertEquals("Event did not have correct workflow", dataflow, + dataEditEvent.getDataFlow().getWorkflows().first()); + assertEquals("Event did not have correct edit", edit, dataEditEvent + .getEdit()); + assertFalse("Edit was still applied", edit.isApplied()); + } + + @Test + public void multipleUndoesRedoes() throws Exception { + EditManager editManager = new EditManagerImpl(); + editManager.addObserver(editManagerObserver); + + Workflow dataflowA = createDataflow(); + Workflow dataflowB = createDataflow(); + Workflow dataflowC = createDataflow(); + + Processor processorA1 = createProcessor(); + Processor processorA2 = createProcessor(); + Processor processorA3 = createProcessor(); + Processor processorB1 = createProcessor(); + Processor processorC1 = createProcessor(); + + Edit<Workflow> edit = new AddProcessorEdit(dataflowA, processorA1); + editManager.doDataflowEdit(dataflowA.getParent(), edit); + + edit = new AddProcessorEdit(dataflowB, processorB1); + editManager.doDataflowEdit(dataflowB.getParent(), edit); + + edit = new AddProcessorEdit(dataflowA, processorA2); + editManager.doDataflowEdit(dataflowA.getParent(), edit); + + edit = new AddProcessorEdit(dataflowC, processorC1); + editManager.doDataflowEdit(dataflowC.getParent(), edit); + + edit = new AddProcessorEdit(dataflowA, processorA3); + editManager.doDataflowEdit(dataflowA.getParent(), edit); + + + + assertFalse("Did not add processors", dataflowA.getProcessors().isEmpty()); + assertEquals(3, dataflowA.getProcessors().size()); + editManager.undoDataflowEdit(dataflowA.getParent()); + assertEquals(2, dataflowA.getProcessors().size()); + editManager.undoDataflowEdit(dataflowA.getParent()); + assertEquals(1, dataflowA.getProcessors().size()); + editManager.undoDataflowEdit(dataflowA.getParent()); + assertEquals(0, dataflowA.getProcessors().size()); + + assertEquals(1, dataflowB.getProcessors().size()); + assertEquals(1, dataflowC.getProcessors().size()); + + assertTrue(editManager.canUndoDataflowEdit(dataflowC.getParent())); + editManager.undoDataflowEdit(dataflowC.getParent()); + assertFalse(editManager.canUndoDataflowEdit(dataflowC.getParent())); + editManager.undoDataflowEdit(dataflowC.getParent()); // extra one + assertFalse(editManager.canUndoDataflowEdit(dataflowC.getParent())); + + + assertEquals(1, dataflowB.getProcessors().size()); + assertEquals(0, dataflowC.getProcessors().size()); + + editManager.undoDataflowEdit(dataflowB.getParent()); + assertEquals(0, dataflowA.getProcessors().size()); + assertEquals(0, dataflowB.getProcessors().size()); + assertEquals(0, dataflowC.getProcessors().size()); + + editManager.redoDataflowEdit(dataflowA.getParent()); + assertEquals(1, dataflowA.getProcessors().size()); + + editManager.redoDataflowEdit(dataflowA.getParent()); + assertEquals(2, dataflowA.getProcessors().size()); + + editManager.redoDataflowEdit(dataflowA.getParent()); + assertEquals(3, dataflowA.getProcessors().size()); + + // does not affect it + editManager.redoDataflowEdit(dataflowA.getParent()); + assertEquals(3, dataflowA.getProcessors().size()); + assertEquals(0, dataflowB.getProcessors().size()); + assertEquals(0, dataflowC.getProcessors().size()); + } + + @Test + public void emptyUndoDoesNotFail() throws Exception { + EditManager editManager = new EditManagerImpl(); + editManager.addObserver(editManagerObserver); + editManager.undoDataflowEdit(dataflow.getParent()); + } + + @Test + public void extraUndoesDoesNotFail() throws Exception { + EditManager editManager = new EditManagerImpl(); + editManager.addObserver(editManagerObserver); + + Edit<Workflow> edit = new AddProcessorEdit(dataflow, processor); + editManager.doDataflowEdit(dataflow.getParent(), edit); + + assertFalse("Did not add processor", dataflow.getProcessors().isEmpty()); + editManager.undoDataflowEdit(dataflow.getParent()); + assertTrue("Did not undo add processor", dataflow.getProcessors() + .isEmpty()); + editManager.undoDataflowEdit(dataflow.getParent()); + } + + @Before + public void makeDataflow() { + dataflow = createDataflow(); + } + + protected Workflow createDataflow() { + WorkflowBundle workflowBundle = new WorkflowBundle(); + Workflow workflow = new Workflow(); + workflow.setParent(workflowBundle); + return workflow; + } + + protected Processor createProcessor() { + Processor processor = new Processor(); + processor.setName("proc-" + UUID.randomUUID()); + return processor; + } + + @Before + public void makeProcessor() { + processor = createProcessor(); + } + + private class EditManagerObserver implements Observer<EditManagerEvent> { + + public List<EditManagerEvent> events = new ArrayList<>(); + + @Override + public void notify(Observable<EditManagerEvent> sender, + EditManagerEvent message) throws Exception { + events.add(message); + if (message instanceof DataflowEditEvent) { + DataflowEditEvent dataflowEdit = (DataflowEditEvent) message; + assertTrue("Edit was not applied on edit event", dataflowEdit + .getEdit().isApplied()); + } else if (message instanceof DataFlowUndoEvent) { + DataFlowUndoEvent dataflowUndo = (DataFlowUndoEvent) message; + assertFalse("Edit was applied on undo event", dataflowUndo + .getEdit().isApplied()); + } else if (message instanceof DataFlowRedoEvent) { + DataFlowRedoEvent dataflowEdit = (DataFlowRedoEvent) message; + assertTrue("Edit was not applied on edit event", dataflowEdit + .getEdit().isApplied()); + } else { + fail("Unknown event: " + message); + } + } + } + +}
diff --git a/taverna-workbench-file-api/pom.xml b/taverna-workbench-file-api/pom.xml new file mode 100644 index 0000000..2b6205d --- /dev/null +++ b/taverna-workbench-file-api/pom.xml
@@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <packaging>bundle</packaging> + <name>File opening API</name> + <description> + API for doing file (ie. workflow) open/save in the workbench. + </description> + + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/AbstractDataflowPersistenceHandler.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/AbstractDataflowPersistenceHandler.java new file mode 100644 index 0000000..8dc34e8 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/AbstractDataflowPersistenceHandler.java
@@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file; + +import java.util.Collections; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.file.exceptions.SaveException; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +public abstract class AbstractDataflowPersistenceHandler implements + DataflowPersistenceHandler { + @Override + public List<FileType> getOpenFileTypes() { + return Collections.emptyList(); + } + + @Override + public List<FileType> getSaveFileTypes() { + return Collections.emptyList(); + } + + @Override + public List<Class<?>> getOpenSourceTypes() { + return Collections.emptyList(); + } + + @Override + public List<Class<?>> getSaveDestinationTypes() { + return Collections.emptyList(); + } + + @Override + public DataflowInfo openDataflow(FileType fileType, Object source) + throws OpenException { + throw new UnsupportedOperationException(); + } + + @Override + public DataflowInfo saveDataflow(WorkflowBundle workflowBundle, FileType fileType, + Object destination) throws SaveException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean wouldOverwriteDataflow(WorkflowBundle workflowBundle, FileType fileType, + Object destination, DataflowInfo lastDataflowInfo) { + throw new UnsupportedOperationException(); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowInfo.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowInfo.java new file mode 100644 index 0000000..c5eaa26 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowInfo.java
@@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file; + +import java.util.Date; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Information about a WorkflowBundle that has been opened by the + * {@link FileManager}. + * <p> + * This class, or a subclass of it, is used by + * {@link DataflowPersistenceHandler}s to keep information about where a + * {@link WorkflowBundle} came from or where it was saved to. + * + * @author Stian Soiland-Reyes + */ +public class DataflowInfo { + private final FileType fileType; + private final WorkflowBundle worflowBundle; + private final Date lastModified; + private final Object canonicalSource; + + public DataflowInfo(FileType fileType, Object canonicalSource, + WorkflowBundle worflowBundle, Date lastModified) { + this.fileType = fileType; + this.canonicalSource = canonicalSource; + this.worflowBundle = worflowBundle; + this.lastModified = lastModified; + } + + public DataflowInfo(FileType fileType, Object canonicalSource, + WorkflowBundle worflowBundle) { + this(fileType, canonicalSource, worflowBundle, null); + } + + /** + * Return the canonical source of where the WorkflowBundle was opened from + * or saved to. + * <p> + * This is not necessarily the source provided to + * {@link FileManager#openDataflow(FileType, Object)} or + * {@link FileManager#saveDataflow(WorkflowBundle, FileType, Object, boolean)} + * , but it's canonical version. + * <p> + * For instance, if a WorkflowBundle was opened from a + * File("relative/something.wfbundle) this canonical source would resolve + * the relative path. + * + * @return + */ + public Object getCanonicalSource() { + return canonicalSource; + } + + /** + * Return the WorkflowBundle that is open. + * + * @return The open WorkflowBundle + */ + public WorkflowBundle getDataflow() { + return worflowBundle; + } + + /** + * Get the last modified {@link Date} of the source at the time when it was + * opened/saved. + * <p> + * It is important that this value is checked on creation time, and not on + * demand. + * + * @return The {@link Date} of the source/destination's last modified + * timestamp, or <code>null</code> if unknown. + */ + public Date getLastModified() { + return lastModified; + } + + /** + * The {@link FileType} of this {@link WorkflowBundle} serialisation used + * for opening/saving. + * + * @return The {@link FileType}, for instance + * {@link net.sf.taverna.t2.workbench.file.impl.WorkflowBundleFileType} + */ + public FileType getFileType() { + return fileType; + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowPersistenceHandler.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowPersistenceHandler.java new file mode 100644 index 0000000..f8d2ddb --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/DataflowPersistenceHandler.java
@@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file; + +import java.io.File; +import java.net.URL; +import java.util.Collection; + +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.file.exceptions.SaveException; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * A handler for opening or saving {@link WorkflowBundle} from the + * {@link FileManager}. + * + * @author Stian Soiland-Reyes + */ +public interface DataflowPersistenceHandler { + /** + * A collection of supported file types for + * {@link #openDataflow(FileType, Object)}, or an empty collection if + * opening is not supported by this handler. + * + * @return A collection of supported {@link FileType}s for opening. + */ + Collection<FileType> getOpenFileTypes(); + + /** + * A collection of supported source classes for + * {@link #openDataflow(FileType, Object)}, or an empty collection if + * opening is not supported by this handler. + * <p> + * For example, a handler that supports sources opened from a {@link File} + * and {@link URL} could return + * <code>Arrays.asList(File.class, URL.class)</code> + * + * @return A collection of supported {@link Class}es of the open source + * types. + */ + Collection<Class<?>> getOpenSourceTypes(); + + /** + * A collection of supported destination classes for + * {@link #saveDataflow(Dataflow, FileType, Object)}, or an empty collection + * if saving is not supported by this handler. + * <p> + * For example, a handler that supports saving to destinations that are + * instances of a {@link File} could return + * <code>Arrays.asList(File.class)</code> + * + * @return A collection of supported {{@link Class}es of the save + * destination types. + */ + Collection<Class<?>> getSaveDestinationTypes(); + + /** + * A collection of supported file types for + * {@link #saveDataflow(WorkflowBundle, FileType, Object)}, or an empty + * collection if saving is not supported by this handler. + * + * @return A collection of supported {@link FileType}s for saving. + */ + Collection<FileType> getSaveFileTypes(); + + /** + * Open a dataflow from a source containing a dataflow of the given + * {@link FileType}. + * <p> + * The {@link FileType} will be equal to one of the types from + * {@link #getOpenFileTypes()}, and the source class will be one that is + * assignable to one of the classes from {@link #getOpenSourceTypes()}. + * + * @param fileType + * {@link FileType} determining which serialisation method has + * been used + * @param source + * Source for reading the WorkflowBundle + * @return {@link DataflowInfo} describing the opened WorkflowBundle, + * including the WorkflowBundle itself + * @throws OpenException + * If the WorkflowBundle could not be read, parsed or opened for + * any reason. + */ + DataflowInfo openDataflow(FileType fileType, Object source) + throws OpenException; + + /** + * Save a WorkflowBundle to a destination of the given {@link FileType}. + * <p> + * The {@link FileType} will be equal to one of the types from + * {@link #getSaveFileTypes()}, and the destination class will be one that + * is assignable to one of the classes from + * {@link #getSaveDestinationTypes()}. + * + * @param dataflow + * {@link WorkflowBundle} to be saved + * @param fileType + * {@link FileType} determining which serialisation method to use + * @param destination + * Destination for writing the WorkflowBundle + * @return {@link DataflowInfo} describing the saved WorkflowBundle, + * including the WorkflowBundle itself + * @throws OpenException + * If the WorkflowBundle could not be read, parsed or opened for + * any reason. + */ + DataflowInfo saveDataflow(WorkflowBundle dataflow, FileType fileType, + Object destination) throws SaveException; + + /** + * Return <code>true</code> if a call to + * {@link #saveDataflow(WorkflowBundle, FileType, Object)} would overwrite + * the destination, and the destination is different from last + * {@link #openDataflow(FileType, Object)} or + * {@link #saveDataflow(WorkflowBundle, FileType, Object)} of the given + * dataflow. + * + * @param dataflow + * {@link WorkflowBundle} that is to be saved + * @param fileType + * {@link FileType} for saving WorkflowBundle + * @param destination + * destination for writing WorkflowBundle + * @param lastDataflowInfo + * last provided {@link DataflowInfo} returned by + * {@link #openDataflow(FileType, Object)} or + * {@link #saveDataflow(WorkflowBundle, FileType, Object)}. (but + * not necessarily from this handler) + * @return <code>true</code> if the save would overwrite + */ + boolean wouldOverwriteDataflow(WorkflowBundle dataflow, FileType fileType, + Object destination, DataflowInfo lastDataflowInfo); +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileManager.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileManager.java new file mode 100644 index 0000000..f449bb5 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileManager.java
@@ -0,0 +1,573 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.List; + +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException; +import net.sf.taverna.t2.workbench.file.exceptions.SaveException; +import net.sf.taverna.t2.workbench.file.exceptions.UnsavedException; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Manager of open files (WorkflowBundleBundles) in the workbench. + * <p> + * A {@link WorkflowBundle} can be opened for the workbench using + * {@link #openDataflow(FileType, Object)} or {@link #openDataflow(WorkflowBundle)}. + * {@link Observer}s of the FileManager gets notified with an + * {@link OpenedDataflowEvent}. The opened workflow is also + * {@link #setCurrentDataflow(WorkflowBundle) made the current dataflow}, available + * through {@link #getCurrentDataflow()} or by observing the + * {@link net.sf.taverna.t2.lang.ui.ModelMap} for the model name + * {@link net.sf.taverna.t2.workbench.ModelMapConstants#CURRENT_DATAFLOW}. + * <p> + * A dataflow can be saved using + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}. Observers will be + * presented a {@link SavedDataflowEvent}. + * <p> + * If a dataflow was previously opened from a saveable destination or previously + * saved using {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}, + * {@link #saveDataflow(WorkflowBundle, boolean)} can be used to resave to that + * destination. + * <p> + * You can get the last opened/saved source and type using + * {@link #getDataflowSource(WorkflowBundle)} and {@link #getDataflowType(WorkflowBundle)}. + * <p> + * If the save methods are used with failOnOverwrite=true, an + * {@link OverwriteException} will be thrown if the destination file already + * exists and was not last written by a previous save on that dataflow. (This is + * typically checked using timestamps on the file). + * <p> + * A dataflow can be closed using {@link #closeDataflow(WorkflowBundle, boolean)}. A + * closed dataflow is no longer monitored for changes and can no longer be used + * with the other operations, except {@link #openDataflow(WorkflowBundle)}. + * <p> + * If a dataflow has been changed using the {@link EditManager}, + * {@link #isDataflowChanged(WorkflowBundle)} will return true until the next save. If + * the close methods are used with failOnUnsaved=true, an + * {@link UnsavedException} will be thrown if the dataflow has been changed. + * <p> + * The implementation of this interface is an OSGi Service. + * + * @author Stian Soiland-Reyes + */ +public interface FileManager extends Observable<FileManagerEvent> { + /** + * True if {@link #saveDataflow(WorkflowBundle, boolean)} can save the + * workflow, i.e., that there exists an SPI implementation of + * {@link DataflowPersistenceHandler} that can save to + * {@link #getDataflowSource(WorkflowBundle)} using + * {@link #getDataflowType(WorkflowBundle)}. + * + * @see #saveDataflow(WorkflowBundle, boolean) + * @param dataflow + * The dataflow to check + * @return <code>true</code> if the given dataflow can be saved without + * providing a destination and filetype + */ + boolean canSaveWithoutDestination(WorkflowBundle dataflow); + + /** + * Close the specified dataflow. + * <p> + * A closed dataflow can no longer be used with the save methods, and will + * disappear from the UI's list of open dataflows. + * <p> + * If no more dataflows would be open after the close, a new empty dataflow + * is opened as through {@link #newDataflow()}. + * <p> + * If the failOnUnsaved parameters is <code>true</code>, and + * {@link #isDataflowChanged(WorkflowBundle)} is <code>true</code>, an + * {@link UnsavedException} will be thrown, typically because the workflow + * has been changed using the {@link EditManager} since the last change. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link ClosedDataflowEvent}. + * + * @param dataflow + * {@link WorkflowBundle} to close + * @param failOnUnsaved + * If <code>true</code>, fail on unsaved changes + * @throws UnsavedException + * If failOnUnsaved was <code>true</code> and there has been + * changes to the dataflow since the last save + */ + boolean closeDataflow(WorkflowBundle dataflow, boolean failOnUnsaved) + throws UnsavedException; + + /** + * Get the current dataflow. + * <p> + * The current workflow is typically the one currently showed on the screen, + * and is also in {@link #getOpenDataflows()}. + * <p> + * The current dataflow is set through {@link #setCurrentDataflow(WorkflowBundle)} + * or the {@link net.sf.taverna.t2.lang.ui.ModelMap} using the key + * {@link net.sf.taverna.t2.workbench.ModelMapConstants#CURRENT_DATAFLOW}. + * + * @return The current dataflow, or <code>null</code> if no dataflow is + * current + */ + WorkflowBundle getCurrentDataflow(); + + /** + * Get the dataflow that was opened from or last saved to the given source. + * + * @param source + * The source as opened with or saved to + * {@link #openDataflow(FileType, Object)} + * @return The opened {@link WorkflowBundle} or <code>null</code> if no matching + * dataflow found. + */ + WorkflowBundle getDataflowBySource(Object source); + + /** + * Get a name to represent this dataflow. + * <p> + * The name will primarily be deduced from the source of where the workflow + * is opened from, unless {@link Object#toString()} is not overridden (for + * instance opened from an InputStream) or if the source is unknown, in + * which case the dataflow's internal name {@link WorkflowBundle#getName()} + * is returned. + * <p> + * The returned name can be used in listings like the WorkflowBundles menu, but is + * not guaranteed to be unique. (For instance a workflow could be opened + * twice from the same source). + * + * @param dataflow + * WorkflowBundle to get the name for + * @return The deduced workflow name + */ + String getDataflowName(WorkflowBundle dataflow); + + /** + * Returns the default name to use when creating new workflows. + * + * @return the default name to use when creating new workflows + */ + String getDefaultWorkflowName(); + + /** + * Get the last opened/saved source/destination for the given dataflow. + * <p> + * The source is the last source used with + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} for the given + * dataflow, or {@link #openDataflow(FileType, Object)} if it has not yet + * been saved. + * <p> + * If the given dataflow's last opened/saved location was unknown (opened + * with {@link #newDataflow()} or {@link #openDataflow(WorkflowBundle)}), return + * <code>null</code>. + * + * @param dataflow + * {@link WorkflowBundle} which file is to be returned + * @return The last opened/saved source for the given dataflow, or + * <code>null</code> if unknown. + */ + Object getDataflowSource(WorkflowBundle dataflow); + + /** + * Get the last opened/saved source/destination FileType for the given + * dataflow. + * <p> + * The type is the last {@link FileType} used with + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} for the given + * dataflow, or {@link #openDataflow(FileType, Object)} if it has not yet + * been saved. + * <p> + * If the given dataflow's last opened/saved file type was unknown (opened + * with {@link #newDataflow()} or {@link #openDataflow(WorkflowBundle)}), return + * <code>null</code>. + * + * @param dataflow + * {@link WorkflowBundle} which file is to be returned + * @return The last opened/saved {@link FileType} for the given dataflow, or + * <code>null</code> if unknown. + */ + FileType getDataflowType(WorkflowBundle dataflow); + + /** + * Get the list of currently open dataflows. This list of dataflows are + * typically displayed in the UI in the "WorkflowBundles" menu to allow switching + * the {@link #getCurrentDataflow() current dataflow}. + * + * @return A copy of the {@link List} of open {@link WorkflowBundle}s + */ + List<WorkflowBundle> getOpenDataflows(); + + /** + * Get a list of {@link FileFilter}s for supported {@link FileType}s that + * can be opened with any source class. + * + * @return A {@link List} of {@link FileFilter}s supported by + * {@link #openDataflow(FileType, Object)} + */ + List<FileFilter> getOpenFileFilters(); + + /** + * Get a list of {@link FileFilter}s for supported {@link FileType}s that + * can be opened with given source class. + * + * @param sourceClass + * Source class that can be opened from + * @return A {@link List} of {@link FileFilter}s supported by + * {@link #openDataflow(FileType, Object)} + */ + List<FileFilter> getOpenFileFilters(Class<?> sourceClass); + + /** + * Get a list of {@link FileFilter}s for supported {@link FileType}s that + * can be saved to any destination class. + * + * @return A {@link List} of {@link FileFilter}s supported by + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} + */ + List<FileFilter> getSaveFileFilters(); + + /** + * Get a list of {@link FileFilter}s for supported {@link FileType}s that + * can be saved to the given destination class. + * + * @param destinationClass + * Destination class that can be saved to + * @return A {@link List} of {@link FileFilter}s supported by + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} + */ + List<FileFilter> getSaveFileFilters(Class<?> destinationClass); + + /** + * Return <code>true</code> if the dataflow has been changed (through the + * {@link EditManager} or {@link #setDataflowChanged(WorkflowBundle, boolean)}) + * since last save. + * + * @param dataflow + * WorkflowBundle which changed status is to be checked + * @return <code>true</code> if the dataflow has been changed since last + * save. + */ + boolean isDataflowChanged(WorkflowBundle dataflow); + + /** + * True if the given dataflow has been opened and is in + * {@link #getOpenDataflows()}. + * + * @param dataflow + * Dataflow to check + * @return <code>true</code> if dataflow is open + */ + boolean isDataflowOpen(WorkflowBundle dataflow); + + /** + * Create and open a new, blank dataflow. The dataflow will not initially be + * marked as changed. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link OpenedDataflowEvent}. + * <p> + * Note, if the dataflow is later changed, it will not be possible to save + * it to any original location using + * {@link #saveDataflow(WorkflowBundle, boolean)}, only + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}. + * + * @return The newly opened blank {@link WorkflowBundle} + */ + WorkflowBundle newDataflow(); + + /** + * Open a {@link WorkflowBundle} instance that has been created outside the + * {@link FileManager}. The dataflow will not initially be marked as + * changed. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link OpenedDataflowEvent}. + * <p> + * Note, if the dataflow is later changed, it will not be possible to save + * it to its original location using + * {@link #saveDataflow(WorkflowBundle, boolean)}, only + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}. + * <p> + * Instead of using this option it is recommended to create your own + * {@link FileType} and/or source type and a + * {@link DataflowPersistenceHandler} to implement save and/or reopen + * (revert). + * <p> + * If there is only one workflow open before opening this workflow, and it + * is an unchanged blank workflow, the blank workflow will be closed. + * + * @param dataflow + * {@link WorkflowBundle} instance that is to be added as an open + * dataflow + */ + void openDataflow(WorkflowBundle dataflow); + + /** + * Open a dataflow from a source. The dataflow will not initially be marked + * as changed, and will be set as the new current workflow. + * <p> + * The file manager will find implementations of the SPI + * {@link DataflowPersistenceHandler} to perform the opening for the given file + * type and destination class. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link OpenedDataflowEvent}. + * <p> + * If there is only one workflow open before opening this workflow, and it + * is an unchanged blank workflow, the blank workflow will be closed. + * + * @param fileType + * The filetype, for instance + * {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}. + * The file type must be supported by an implementation of the + * SPI DataflowPersistenceHandler. + * @param source + * The source, for instance a {@link File} or {@link URL}. The + * source type must be supported by an implementation of + * DataflowPersistenceHandler. + * @return The opened {@link WorkflowBundle}. + * @throws OpenException + * If there was no matching DataflowPersistenceHandler found or + * the source could not be opened for any other reason, such as + * IO errors or syntax errors. + */ + WorkflowBundle openDataflow(FileType fileType, Object source) + throws OpenException; + + /** + * Open a dataflow from a source silently. The dataflow will not be listed + * as open, and will not be made the current workflow. + * <p> + * The file manager will find implementations of the SPI + * {@link DataflowPersistenceHandler} to perform the opening for the given file + * type and destination class. + * <p> + * Listeners will <strong>not</strong> be notified. + * + * @param fileType + * The filetype, for instance + * {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}. + * The file type must be supported by an implementation of the + * SPI DataflowPersistenceHandler. + * @param source + * The source, for instance a {@link File} or {@link URL}. The + * source type must be supported by an implementation of + * DataflowPersistenceHandler. + * @return The {@link DataflowInfo} describing the opened dataflow. + * @throws OpenException + * If there was no matching DataflowPersistenceHandler found or + * the source could not be opened for any other reason, such as + * IO errors or syntax errors. + */ + DataflowInfo openDataflowSilently(FileType fileType, Object source) + throws OpenException; + + /** + * Save the dataflow to the last saved destination and FileType from + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)} or the last + * opened source and FileType from {@link #openDataflow(FileType, Object)}. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link SavedDataflowEvent}. + * + * @param dataflow + * Dataflow to save. Dataflow must have been opened with + * {@link #openDataflow(FileType, Object)} or saved using + * {@link #saveDataflow(WorkflowBundle, FileType, Object, boolean)}. + * @param failOnOverwrite + * If <code>true</code>, an {@link OverwriteException} is thrown + * if a save would overwrite the destination because it has been + * changed since last open/save. + * @throws OverwriteException + * if failOnOverwrite was true, and a save would overwrite the + * destination because it has been changed since last open/save. + * The save was not performed. + * @throws SaveException + * If any other error occurs during saving, including the case + * that a dataflow is not connected to a source or destination, + * that there are no handlers (some source types can't be saved + * to, such as HTTP URLs), or any other IO error occurring while + * saving. + */ + void saveDataflow(WorkflowBundle dataflow, boolean failOnOverwrite) + throws SaveException, OverwriteException; + + /** + * Save the dataflow to the given destination using the given filetype. + * <p> + * The file manager will find implementations of the SPI + * {@link DataflowPersistenceHandler} to perform the save for the given file + * type and destination class. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link SavedDataflowEvent}. + * + * @param dataflow + * {@link Dataflow} to be saved + * @param fileType + * {@link FileType} to save dataflow as, for instance + * {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}. + * The file type must be supported by an SPI implementation of + * {@link DataflowPersistenceHandler}. + * @param destination + * Destination to save dataflow to, for instance a {@link File} + * @param failOnOverwrite + * If <code>true</code>, an {@link OverwriteException} is thrown + * if a save would overwrite the destination because it already + * exists, but was not opened or save to using the file manager + * for the given dataflow. (ie. a repeated call to this function + * should not throw an OverwriteException unless someone outside + * has modified the file) + * @throws OverwriteException + * if failOnOverwrite was true, and a save would overwrite the + * destination because it already existed, and was not last + * written to by a previous save. The save was not performed. + * @throws SaveException + * If any other error occurs during saving, including the case + * that a dataflow is not connected to a source or destination, + * that there are no handlers (some source types can't be saved + * to, such as HTTP URLs), or any other IO error occurring while + * saving. + */ + void saveDataflow(WorkflowBundle dataflow, FileType fileType, + Object destination, boolean failOnOverwrite) throws SaveException, + OverwriteException; + + /** + * Silently save the dataflow to the given destination using the given + * filetype. + * <p> + * The file manager will find implementations of the SPI + * {@link DataflowPersistenceHandler} to perform the save for the given file + * type and destination class. + * <p> + * Listeners will <strong>not</strong> be notified, and the dataflow does + * not previously have to be opened. getDataflowSource(), + * isDataflowChanged() etc will not be affected - as if the silent save + * never happened. + * + * @param dataflow + * {@link WorkflowBundle} to be saved + * @param fileType + * {@link FileType} to save dataflow as, for instance + * {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}. + * The file type must be supported by an SPI implementation of + * {@link DataflowPersistenceHandler}. + * @param destination + * Destination to save dataflow to, for instance a {@link File} + * @param failOnOverwrite + * If <code>true</code>, an {@link OverwriteException} is thrown + * if a save would overwrite the destination because it already + * exists, but was not opened or save to using the file manager + * for the given dataflow. (ie. a repeated call to this function + * should not throw an OverwriteException unless someone outside + * has modified the file) + * @return The {@link DataflowInfo} describing where the workflow was saved + * @throws OverwriteException + * if failOnOverwrite was true, and a save would overwrite the + * destination because it already existed, and was not last + * written to by a previous save. The save was not performed. + * @throws SaveException + * If any other error occurs during saving, including the case + * that a dataflow is not connected to a source or destination, + * that there are no handlers (some source types can't be saved + * to, such as HTTP URLs), or any other IO error occurring while + * saving. + */ + DataflowInfo saveDataflowSilently(WorkflowBundle dataflow, FileType fileType, + Object destination, boolean failOnOverwrite) throws SaveException, + OverwriteException; + + /** + * Set the current dataflow to the one provided. + * <p> + * The current dataflow can be retrieved using {@link #getCurrentDataflow()} + * . Note that opening a dataflow will normally also set it as the current + * dataflow. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link SetCurrentDataflowEvent}. + * <p> + * Note, the dataflow must already be open. If this is not the case, use one + * of the openDataflow() methods or + * {@link #setCurrentDataflow(WorkflowBundle, boolean)}. + * + * @see #setCurrentDataflow(WorkflowBundle, boolean) + * @param dataflow + * {@link WorkflowBundle} to be made current + */ + void setCurrentDataflow(WorkflowBundle dataflow); + + /** + * Set the current dataflow to the one provided. + * <p> + * The current dataflow can be retrieved using {@link #getCurrentDataflow()} + * . Note that opening a dataflow will normally also set it as the current + * dataflow. + * <p> + * Listeners registered using {@link Observable#addObserver(Observer)} will + * be notified with an {@link SetCurrentDataflowEvent}. + * <p> + * Unless <code>openIfNeeded</code> is <code>true</code>, the dataflow must + * already be open. + * + * @see #setCurrentDataflow(WorkflowBundle, boolean) + * @param dataflow + * {@link WorkflowBundle} to be made current + * @param openIfNeeded + * If <code>true</code>, open the dataflow if needed + */ + void setCurrentDataflow(WorkflowBundle dataflow, boolean openIfNeeded); + + /** + * Set a dataflow as changed or not. This changes the value returned by + * {@link #isDataflowChanged(WorkflowBundle)}. + * <p> + * This method can be used if the dataflow has been changed outside the + * {@link EditManager}. + * + * @param dataflow + * Dataflow which is to be marked + * @param isChanged + * <code>true</code> if the dataflow is to be marked as changed, + * <code>false</code> if it is to be marked as not changed. + */ + void setDataflowChanged(WorkflowBundle dataflow, boolean isChanged); + + /** + * Returns the canonical form of the source where the dataflow was opened + * from or saved to. The code for this method was devised based on + * {@link net.sf.taverna.t2.workbench.file.impl.T2DataflowOpener#openDataflow(FileType fileType, Object source)}. + */ + Object getCanonical(Object source) throws IllegalArgumentException, + URISyntaxException, IOException; +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileType.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileType.java new file mode 100644 index 0000000..001d82c --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/FileType.java
@@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file; + +/** + * A filetype to identify a way to (de)serialise a {@link WorkflowBundle} with + * the {@link FileManager}. + * <p> + * Two filetypes are considered equal if they share an extension or mime type or + * are the same instance. + * + * @see net.sf.taverna.t2.workbench.file.impl.WorkflowBundleFileType + * @author Stian Soiland-Reyes + */ +public abstract class FileType { + public abstract String getExtension(); + + public abstract String getMimeType(); + + public abstract String getDescription(); + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof FileType)) + return false; + FileType other = (FileType) obj; + if (getMimeType() != null && other.getMimeType() != null) + return getMimeType().equalsIgnoreCase(other.getMimeType()); + if (getExtension() != null && other.getExtension() != null) + return getExtension().equalsIgnoreCase(other.getExtension()); + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + getExtension().hashCode(); + hash = 31 * hash + getMimeType().hashCode(); + return hash; + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/AbstractDataflowEvent.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/AbstractDataflowEvent.java new file mode 100644 index 0000000..942e097 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/AbstractDataflowEvent.java
@@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.events; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Abstract FileManagerEvent that relates to a {@link WorkflowBundle} + * + * @see AbstractDataflowEvent + * @see ClosedDataflowEvent + * @see OpenedDataflowEvent + * @see SavedDataflowEvent + * @see SetCurrentDataflowEvent + * @author Stian Soiland-Reyes + */ +public abstract class AbstractDataflowEvent extends FileManagerEvent { + private final WorkflowBundle workflowBundle; + + public AbstractDataflowEvent(WorkflowBundle workflowBundle) { + this.workflowBundle = workflowBundle; + } + + public WorkflowBundle getDataflow() { + return workflowBundle; + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosedDataflowEvent.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosedDataflowEvent.java new file mode 100644 index 0000000..5e7e884 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosedDataflowEvent.java
@@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.events; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * {@link FileManagerEvent} that means a {@link WorkflowBundle} has been closed. + * + * @author Stian Soiland-Reyes + */ +public class ClosedDataflowEvent extends AbstractDataflowEvent { + public ClosedDataflowEvent(WorkflowBundle workflowBundle) { + super(workflowBundle); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosingDataflowEvent.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosingDataflowEvent.java new file mode 100644 index 0000000..094ea10 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/ClosingDataflowEvent.java
@@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.events; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * {@link FileManagerEvent} that means a {@link WorkflowBundle} is being closed. + * <i>This event is abortable;</i> if aborted, the close will not occur. + * + * @author Alan R Williams + */ +public class ClosingDataflowEvent extends AbstractDataflowEvent { + private boolean abortClose = false; + + public boolean isAbortClose() { + return abortClose; + } + + public void setAbortClose(boolean abortClose) { + this.abortClose = abortClose; + } + + public ClosingDataflowEvent(WorkflowBundle workflowBundle) { + super(workflowBundle); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/FileManagerEvent.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/FileManagerEvent.java new file mode 100644 index 0000000..84c3886 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/FileManagerEvent.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.events; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.workbench.file.FileManager; + +/** + * An event given to {@link FileManager} observers registered using + * {@link Observable#addObserver(net.sf.taverna.t2.lang.observer.Observer)}. + * + * @see AbstractDataflowEvent + * @see ClosedDataflowEvent + * @see OpenedDataflowEvent + * @see SavedDataflowEvent + * @see SetCurrentDataflowEvent + * @author Stian Soiland-Reyes + */ +public class FileManagerEvent { + +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/OpenedDataflowEvent.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/OpenedDataflowEvent.java new file mode 100644 index 0000000..b479a83 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/OpenedDataflowEvent.java
@@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.events; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * {@link FileManagerEvent} that means a dataflow has been opened + * + * @author Stian Soiland-Reyes + */ +public class OpenedDataflowEvent extends AbstractDataflowEvent { + public OpenedDataflowEvent(WorkflowBundle workflowBundle) { + super(workflowBundle); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SavedDataflowEvent.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SavedDataflowEvent.java new file mode 100644 index 0000000..f0f22ae --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SavedDataflowEvent.java
@@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.events; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * {@link FileManagerEvent} that means a {@link WorkflowBundle} has been saved. + * + * @author Stian Soiland-Reyes + */ +public class SavedDataflowEvent extends AbstractDataflowEvent { + public SavedDataflowEvent(WorkflowBundle workflowBundle) { + super(workflowBundle); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SetCurrentDataflowEvent.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SetCurrentDataflowEvent.java new file mode 100644 index 0000000..ff44cf2 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/events/SetCurrentDataflowEvent.java
@@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.events; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * {@link FileManagerEvent} that means a {@link WorkflowBundle} has been made + * current. + * + * @author Stian Soiland-Reyes + */ +public class SetCurrentDataflowEvent extends AbstractDataflowEvent { + public SetCurrentDataflowEvent(WorkflowBundle workflowBundle) { + super(workflowBundle); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/FileException.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/FileException.java new file mode 100644 index 0000000..a2cb9ce --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/FileException.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.exceptions; + +import net.sf.taverna.t2.workbench.file.FileManager; + +/** + * Superclass of exceptions thrown by the {@link FileManager}. + */ +@SuppressWarnings("serial") +public class FileException extends Exception { + public FileException() { + } + + public FileException(String message) { + super(message); + } + + public FileException(Throwable cause) { + super(cause); + } + + public FileException(String message, Throwable cause) { + super(message, cause); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OpenException.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OpenException.java new file mode 100644 index 0000000..057679b --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OpenException.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.exceptions; + +/** Indicate that something went wrong during opening a file */ +@SuppressWarnings("serial") +public class OpenException extends FileException { + public OpenException() { + } + + public OpenException(String message) { + super(message); + } + + public OpenException(Throwable cause) { + super(cause); + } + + public OpenException(String message, Throwable cause) { + super(message, cause); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OverwriteException.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OverwriteException.java new file mode 100644 index 0000000..6d410a3 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/OverwriteException.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.exceptions; + +/** Indicate that something could not be overwritten. */ +@SuppressWarnings("serial") +public class OverwriteException extends SaveException { + private final Object destination; + + public OverwriteException(Object destination) { + super("Save would overwrite existing destination " + destination); + this.destination = destination; + } + + public Object getDestination() { + return destination; + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/SaveException.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/SaveException.java new file mode 100644 index 0000000..8c4266f --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/SaveException.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.exceptions; + +/** Indicate that a workflow could not be saved. */ +@SuppressWarnings("serial") +public class SaveException extends FileException { + public SaveException() { + } + + public SaveException(String message) { + super(message); + } + + public SaveException(Throwable cause) { + super(cause); + } + + public SaveException(String message, Throwable cause) { + super(message, cause); + } +}
diff --git a/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/UnsavedException.java b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/UnsavedException.java new file mode 100644 index 0000000..41c01f8 --- /dev/null +++ b/taverna-workbench-file-api/src/main/java/net/sf/taverna/t2/workbench/file/exceptions/UnsavedException.java
@@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.exceptions; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** Indicate that a workflow bundle is not saved. */ +@SuppressWarnings("serial") +public class UnsavedException extends FileException { + private final WorkflowBundle workflowBundle; + + public UnsavedException(WorkflowBundle workflowBundle) { + super("WorkflowBundle was not saved: " + workflowBundle); + this.workflowBundle = workflowBundle; + } + + public WorkflowBundle getDataflow() { + return workflowBundle; + } +}
diff --git a/taverna-workbench-file-impl/pom.xml b/taverna-workbench-file-impl/pom.xml new file mode 100644 index 0000000..98dcce7 --- /dev/null +++ b/taverna-workbench-file-impl/pom.xml
@@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>file-impl</artifactId> + <packaging>bundle</packaging> + <name>File opening implementation</name> + <description> + Implementation for doing file (i.e. workflow) open/save in the + workbench. + </description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + <version>${scufl2.version}</version> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>com.springsource.org.apache.commons.lang</artifactId> + </dependency> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </dependency> + <dependency> + <groupId>org.jdom</groupId> + <artifactId>com.springsource.org.jdom</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>edits-impl</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-t2flow</artifactId> + <version>${scufl2.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-rdfxml</artifactId> + <version>${scufl2.version}</version> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowFromDataflowPersistenceHandler.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowFromDataflowPersistenceHandler.java new file mode 100644 index 0000000..86bc091 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowFromDataflowPersistenceHandler.java
@@ -0,0 +1,49 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.file.impl; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * @author alanrw + */ +public class DataflowFromDataflowPersistenceHandler extends + AbstractDataflowPersistenceHandler implements + DataflowPersistenceHandler { + private static final WorkflowBundleFileType WORKFLOW_BUNDLE_FILE_TYPE = new WorkflowBundleFileType(); + + @Override + public DataflowInfo openDataflow(FileType fileType, Object source) + throws OpenException { + if (!getOpenFileTypes().contains(fileType)) + throw new IllegalArgumentException("Unsupported file type " + + fileType); + + WorkflowBundle workflowBundle = (WorkflowBundle) source; + Date lastModified = null; + Object canonicalSource = null; + return new DataflowInfo(WORKFLOW_BUNDLE_FILE_TYPE, canonicalSource, + workflowBundle, lastModified); + } + + @Override + public List<FileType> getOpenFileTypes() { + return Arrays.<FileType> asList(WORKFLOW_BUNDLE_FILE_TYPE); + } + + @Override + public List<Class<?>> getOpenSourceTypes() { + return Arrays.<Class<?>> asList(Workflow.class); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowPersistenceHandlerRegistry.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowPersistenceHandlerRegistry.java new file mode 100644 index 0000000..39117e9 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/DataflowPersistenceHandlerRegistry.java
@@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import static org.apache.commons.collections.map.LazyMap.decorate; +import static org.apache.commons.lang.ClassUtils.getAllInterfaces; +import static org.apache.commons.lang.ClassUtils.getAllSuperclasses; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.FileType; + +import org.apache.commons.collections.Factory; + +// TODO: Cache lookups / build one massive structure +public class DataflowPersistenceHandlerRegistry { + private static final MapFactory MAP_FACTORY = new MapFactory(); + private static final SetFactory SET_FACTORY = new SetFactory(); + + @SuppressWarnings("unchecked") + protected static List<Class<?>> findAllParentClasses( + final Class<?> sourceClass) { + List<Class<?>> superClasses = new ArrayList<>(); + superClasses.add(sourceClass); + superClasses.addAll(getAllSuperclasses(sourceClass)); + superClasses.addAll(getAllInterfaces(sourceClass)); + return superClasses; + } + + private Map<Class<?>, Set<DataflowPersistenceHandler>> openClassToHandlers; + private Map<Class<?>, Set<FileType>> openClassToTypes; + private Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> openFileClassToHandler; + private Map<FileType, Set<DataflowPersistenceHandler>> openFileToHandler; + private Map<Class<?>, Set<DataflowPersistenceHandler>> saveClassToHandlers; + private Map<Class<?>, Set<FileType>> saveClassToTypes; + private Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> saveFileClassToHandler; + private Map<FileType, Set<DataflowPersistenceHandler>> saveFileToHandler; + + private List<DataflowPersistenceHandler> dataflowPersistenceHandlers; + + public DataflowPersistenceHandlerRegistry() { + } + + public Set<FileType> getOpenFileTypes() { + return getOpenFileClassToHandler().keySet(); + } + + public Set<FileType> getOpenFileTypesFor(Class<?> sourceClass) { + Set<FileType> fileTypes = new LinkedHashSet<>(); + for (Class<?> candidateClass : findAllParentClasses(sourceClass)) + fileTypes.addAll(getOpenClassToTypes().get(candidateClass)); + return fileTypes; + } + + public Set<DataflowPersistenceHandler> getOpenHandlersFor( + Class<? extends Object> sourceClass) { + Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>(); + for (Class<?> candidateClass : findAllParentClasses(sourceClass)) + handlers.addAll(getOpenClassToHandlers().get(candidateClass)); + return handlers; + } + + public Set<DataflowPersistenceHandler> getOpenHandlersFor( + FileType fileType, Class<? extends Object> sourceClass) { + Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>(); + for (Class<?> candidateClass : findAllParentClasses(sourceClass)) + handlers.addAll(getOpenFileClassToHandler().get(fileType).get( + candidateClass)); + return handlers; + } + + public Set<DataflowPersistenceHandler> getOpenHandlersForType( + FileType fileType) { + return getOpenFileToHandler().get(fileType); + } + + public synchronized Set<DataflowPersistenceHandler> getOpenHandlersForType( + FileType fileType, Class<?> sourceClass) { + Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>(); + for (Class<?> candidateClass : findAllParentClasses(sourceClass)) + handlers.addAll(getOpenFileClassToHandler().get(fileType).get( + candidateClass)); + return handlers; + } + + public Set<FileType> getSaveFileTypes() { + return getSaveFileClassToHandler().keySet(); + } + + public Set<FileType> getSaveFileTypesFor(Class<?> destinationClass) { + Set<FileType> fileTypes = new LinkedHashSet<>(); + for (Class<?> candidateClass : findAllParentClasses(destinationClass)) + fileTypes.addAll(getSaveClassToTypes().get(candidateClass)); + return fileTypes; + } + + public Set<DataflowPersistenceHandler> getSaveHandlersFor( + Class<? extends Object> destinationClass) { + Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>(); + for (Class<?> candidateClass : findAllParentClasses(destinationClass)) + handlers.addAll(getSaveClassToHandlers().get(candidateClass)); + return handlers; + } + + public Set<DataflowPersistenceHandler> getSaveHandlersForType( + FileType fileType, Class<?> destinationClass) { + Set<DataflowPersistenceHandler> handlers = new LinkedHashSet<>(); + for (Class<?> candidateClass : findAllParentClasses(destinationClass)) + handlers.addAll(getSaveFileClassToHandler().get(fileType).get( + candidateClass)); + return handlers; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private synchronized void createCollections() { + openFileClassToHandler = decorate(new HashMap(), MAP_FACTORY); + openFileToHandler = decorate(new HashMap(), SET_FACTORY); + openClassToTypes = decorate(new HashMap(), SET_FACTORY); + openClassToHandlers = decorate(new HashMap(), SET_FACTORY); + + saveFileClassToHandler = decorate(new HashMap(), MAP_FACTORY); + saveFileToHandler = decorate(new HashMap(), SET_FACTORY); + saveClassToTypes = decorate(new HashMap(), SET_FACTORY); + saveClassToHandlers = decorate(new HashMap(), SET_FACTORY); + } + + private Map<Class<?>, Set<DataflowPersistenceHandler>> getOpenClassToHandlers() { + return openClassToHandlers; + } + + private synchronized Map<Class<?>, Set<FileType>> getOpenClassToTypes() { + return openClassToTypes; + } + + private synchronized Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> getOpenFileClassToHandler() { + return openFileClassToHandler; + } + + private Map<FileType, Set<DataflowPersistenceHandler>> getOpenFileToHandler() { + return openFileToHandler; + } + + private Map<Class<?>, Set<DataflowPersistenceHandler>> getSaveClassToHandlers() { + return saveClassToHandlers; + } + + private synchronized Map<Class<?>, Set<FileType>> getSaveClassToTypes() { + return saveClassToTypes; + } + + private synchronized Map<FileType, Map<Class<?>, Set<DataflowPersistenceHandler>>> getSaveFileClassToHandler() { + return saveFileClassToHandler; + } + + /** + * Bind method for SpringDM. + * + * @param service + * @param properties + */ + public void update(Object service, Map<?, ?> properties) { + if (dataflowPersistenceHandlers != null) + updateColletions(); + } + + public synchronized void updateColletions() { + createCollections(); + for (DataflowPersistenceHandler handler : dataflowPersistenceHandlers) { + for (FileType openFileType : handler.getOpenFileTypes()) { + Set<DataflowPersistenceHandler> set = openFileToHandler + .get(openFileType); + set.add(handler); + for (Class<?> openClass : handler.getOpenSourceTypes()) { + openFileClassToHandler.get(openFileType).get(openClass) + .add(handler); + openClassToTypes.get(openClass).add(openFileType); + } + } + for (Class<?> openClass : handler.getOpenSourceTypes()) + openClassToHandlers.get(openClass).add(handler); + + for (FileType saveFileType : handler.getSaveFileTypes()) { + saveFileToHandler.get(saveFileType).add(handler); + for (Class<?> saveClass : handler.getSaveDestinationTypes()) { + saveFileClassToHandler.get(saveFileType).get(saveClass) + .add(handler); + saveClassToTypes.get(saveClass).add(saveFileType); + } + } + for (Class<?> openClass : handler.getSaveDestinationTypes()) + saveClassToHandlers.get(openClass).add(handler); + } + } + + public void setDataflowPersistenceHandlers( + List<DataflowPersistenceHandler> dataflowPersistenceHandlers) { + this.dataflowPersistenceHandlers = dataflowPersistenceHandlers; + } + + private static class MapFactory implements Factory { + @Override + @SuppressWarnings("rawtypes") + public Object create() { + return decorate(new HashMap(), SET_FACTORY); + } + } + + private static class SetFactory implements Factory { + @Override + public Object create() { + return new LinkedHashSet<Object>(); + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileDataflowInfo.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileDataflowInfo.java new file mode 100644 index 0000000..89ae39c --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileDataflowInfo.java
@@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import java.io.File; +import java.io.IOException; +import java.util.Date; + +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.FileType; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * Information about an open dataflow that was opened from or saved to a + * {@link File}. + * + * @see DataflowInfo + * @see FileManager + * @author Stian Soiland-Reyes + */ +public class FileDataflowInfo extends DataflowInfo { + private static Logger logger = Logger.getLogger(FileDataflowInfo.class); + + public FileDataflowInfo(FileType fileType, File source, + WorkflowBundle workflowBundle) { + super(fileType, canonicalFile(source), workflowBundle, + lastModifiedFile(source)); + } + + protected static Date lastModifiedFile(File file) { + long lastModifiedLong = file.lastModified(); + if (lastModifiedLong == 0) + return null; + return new Date(lastModifiedLong); + } + + public static File canonicalFile(File file) { + try { + return file.getCanonicalFile(); + } catch (IOException e) { + logger.warn("Could not find canonical file for " + file); + return file; + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileManagerImpl.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileManagerImpl.java new file mode 100644 index 0000000..aadb3f1 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileManagerImpl.java
@@ -0,0 +1,601 @@ +/******************************************************************************* + * Copyright (C) 2007-2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import static java.awt.GraphicsEnvironment.isHeadless; +import static java.util.Collections.singleton; +import static javax.swing.SwingUtilities.invokeAndWait; +import static javax.swing.SwingUtilities.isEventDispatchThread; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.lang.observer.MultiCaster; +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.ClosingDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException; +import net.sf.taverna.t2.workbench.file.exceptions.SaveException; +import net.sf.taverna.t2.workbench.file.exceptions.UnsavedException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.profiles.Profile; + +/** + * Implementation of {@link FileManager} + * + * @author Stian Soiland-Reyes + */ +public class FileManagerImpl implements FileManager { + private static Logger logger = Logger.getLogger(FileManagerImpl.class); + private static int nameIndex = 1; + + /** + * The last blank workflowBundle created using #newDataflow() until it has + * been changed - when this variable will be set to null again. Used to + * automatically close unmodified blank workflowBundles on open. + */ + private WorkflowBundle blankWorkflowBundle = null; + @SuppressWarnings("unused") + private EditManager editManager; + private EditManagerObserver editManagerObserver = new EditManagerObserver(); + protected MultiCaster<FileManagerEvent> observers = new MultiCaster<>(this); + /** + * Ordered list of open WorkflowBundle + */ + private LinkedHashMap<WorkflowBundle, OpenDataflowInfo> openDataflowInfos = new LinkedHashMap<>(); + private DataflowPersistenceHandlerRegistry dataflowPersistenceHandlerRegistry; + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + private WorkflowBundle currentWorkflowBundle; + + public DataflowPersistenceHandlerRegistry getPersistanceHandlerRegistry() { + return dataflowPersistenceHandlerRegistry; + } + + public FileManagerImpl(EditManager editManager) { + this.editManager = editManager; + editManager.addObserver(editManagerObserver); + } + + /** + * Add an observer to be notified of {@link FileManagerEvent}s, such as + * {@link OpenedDataflowEvent} and {@link SavedDataflowEvent}. + * + * {@inheritDoc} + */ + @Override + public void addObserver(Observer<FileManagerEvent> observer) { + observers.addObserver(observer); + } + + @Override + public boolean canSaveWithoutDestination(WorkflowBundle workflowBundle) { + OpenDataflowInfo dataflowInfo = getOpenDataflowInfo(workflowBundle); + if (dataflowInfo.getSource() == null) + return false; + Set<?> handlers = getPersistanceHandlerRegistry() + .getSaveHandlersForType( + dataflowInfo.getFileType(), + dataflowInfo.getDataflowInfo().getCanonicalSource() + .getClass()); + return !handlers.isEmpty(); + } + + @Override + public boolean closeDataflow(WorkflowBundle workflowBundle, + boolean failOnUnsaved) throws UnsavedException { + if (workflowBundle == null) + throw new NullPointerException("Dataflow can't be null"); + ClosingDataflowEvent message = new ClosingDataflowEvent(workflowBundle); + observers.notify(message); + if (message.isAbortClose()) + return false; + if ((failOnUnsaved && getOpenDataflowInfo(workflowBundle).isChanged())) + throw new UnsavedException(workflowBundle); + if (workflowBundle.equals(getCurrentDataflow())) { + // We'll need to change current workflowBundle + // Find best candidate to the left or right + List<WorkflowBundle> workflowBundles = getOpenDataflows(); + int openIndex = workflowBundles.indexOf(workflowBundle); + if (openIndex == -1) + throw new IllegalArgumentException("Workflow was not opened " + + workflowBundle); + + if (openIndex > 0) + setCurrentDataflow(workflowBundles.get(openIndex - 1)); + else if (openIndex == 0 && workflowBundles.size() > 1) + setCurrentDataflow(workflowBundles.get(1)); + else + // If it was the last one, start a new, empty workflowBundle + newDataflow(); + } + if (workflowBundle == blankWorkflowBundle) + blankWorkflowBundle = null; + openDataflowInfos.remove(workflowBundle); + observers.notify(new ClosedDataflowEvent(workflowBundle)); + return true; + } + + @Override + public WorkflowBundle getCurrentDataflow() { + return currentWorkflowBundle; + } + + @Override + public WorkflowBundle getDataflowBySource(Object source) { + for (Entry<WorkflowBundle, OpenDataflowInfo> infoEntry : openDataflowInfos + .entrySet()) { + OpenDataflowInfo info = infoEntry.getValue(); + if (source.equals(info.getSource())) + return infoEntry.getKey(); + } + // Not found + return null; + } + + @Override + public String getDataflowName(WorkflowBundle workflowBundle) { + Object source = null; + if (isDataflowOpen(workflowBundle)) + source = getDataflowSource(workflowBundle); + // Fallback + String name; + Workflow workflow = workflowBundle.getMainWorkflow(); + if (workflow != null) + name = workflow.getName(); + else + name = workflowBundle.getName(); + if (source == null) + return name; + if (source instanceof File) + return ((File) source).getAbsolutePath(); + else if (source instanceof URL) + return source.toString(); + + // Check if it has implemented a toString() method + Method toStringMethod = null; + Method toStringMethodFromObject = null; + try { + toStringMethod = source.getClass().getMethod("toString"); + toStringMethodFromObject = Object.class.getMethod("toString"); + } catch (Exception e) { + throw new IllegalStateException( + "Source did not implement Object.toString() " + source); + } + if (!toStringMethod.equals(toStringMethodFromObject)) + return source.toString(); + return name; + } + + @Override + public String getDefaultWorkflowName() { + return "Workflow" + (nameIndex++); + } + + @Override + public Object getDataflowSource(WorkflowBundle workflowBundle) { + return getOpenDataflowInfo(workflowBundle).getSource(); + } + + @Override + public FileType getDataflowType(WorkflowBundle workflowBundle) { + return getOpenDataflowInfo(workflowBundle).getFileType(); + } + + @Override + public List<Observer<FileManagerEvent>> getObservers() { + return observers.getObservers(); + } + + /** + * Get the {@link OpenDataflowInfo} for the given WorkflowBundle + * + * @throws NullPointerException + * if the WorkflowBundle was <code>null</code> + * @throws IllegalArgumentException + * if the WorkflowBundle was not open. + * @param workflowBundle + * WorkflowBundle which information is to be found + * @return The {@link OpenDataflowInfo} describing the WorkflowBundle + */ + protected synchronized OpenDataflowInfo getOpenDataflowInfo( + WorkflowBundle workflowBundle) { + if (workflowBundle == null) + throw new NullPointerException("Dataflow can't be null"); + OpenDataflowInfo info = openDataflowInfos.get(workflowBundle); + if (info == null) + throw new IllegalArgumentException("Workflow was not opened " + + workflowBundle); + return info; + } + + @Override + public List<WorkflowBundle> getOpenDataflows() { + return new ArrayList<>(openDataflowInfos.keySet()); + } + + @Override + public List<FileFilter> getOpenFileFilters() { + List<FileFilter> fileFilters = new ArrayList<>(); + + Set<FileType> fileTypes = getPersistanceHandlerRegistry() + .getOpenFileTypes(); + if (!fileTypes.isEmpty()) + fileFilters.add(new MultipleFileTypes(fileTypes, + "All supported workflows")); + for (FileType fileType : fileTypes) + fileFilters.add(new FileTypeFileFilter(fileType)); + return fileFilters; + } + + @Override + public List<FileFilter> getOpenFileFilters(Class<?> sourceClass) { + List<FileFilter> fileFilters = new ArrayList<>(); + for (FileType fileType : getPersistanceHandlerRegistry() + .getOpenFileTypesFor(sourceClass)) + fileFilters.add(new FileTypeFileFilter(fileType)); + return fileFilters; + } + + @Override + public List<FileFilter> getSaveFileFilters() { + List<FileFilter> fileFilters = new ArrayList<>(); + for (FileType fileType : getPersistanceHandlerRegistry() + .getSaveFileTypes()) + fileFilters.add(new FileTypeFileFilter(fileType)); + return fileFilters; + } + + @Override + public List<FileFilter> getSaveFileFilters(Class<?> destinationClass) { + List<FileFilter> fileFilters = new ArrayList<>(); + for (FileType fileType : getPersistanceHandlerRegistry() + .getSaveFileTypesFor(destinationClass)) + fileFilters.add(new FileTypeFileFilter(fileType)); + return fileFilters; + } + + @Override + public boolean isDataflowChanged(WorkflowBundle workflowBundle) { + return getOpenDataflowInfo(workflowBundle).isChanged(); + } + + @Override + public boolean isDataflowOpen(WorkflowBundle workflowBundle) { + return openDataflowInfos.containsKey(workflowBundle); + } + + @Override + public WorkflowBundle newDataflow() { + WorkflowBundle workflowBundle = new WorkflowBundle(); + workflowBundle.setMainWorkflow(new Workflow()); + workflowBundle.getMainWorkflow().setName(getDefaultWorkflowName()); + workflowBundle.setMainProfile(new Profile()); + scufl2Tools.setParents(workflowBundle); + blankWorkflowBundle = null; + openDataflowInternal(workflowBundle); + blankWorkflowBundle = workflowBundle; + observers.notify(new OpenedDataflowEvent(workflowBundle)); + return workflowBundle; + } + + @Override + public void openDataflow(WorkflowBundle workflowBundle) { + openDataflowInternal(workflowBundle); + observers.notify(new OpenedDataflowEvent(workflowBundle)); + } + + /** + * {@inheritDoc} + */ + @Override + public WorkflowBundle openDataflow(FileType fileType, Object source) + throws OpenException { + if (isHeadless()) + return performOpenDataflow(fileType, source); + + OpenDataflowRunnable r = new OpenDataflowRunnable(this, fileType, + source); + if (isEventDispatchThread()) { + r.run(); + } else + try { + invokeAndWait(r); + } catch (InterruptedException | InvocationTargetException e) { + throw new OpenException("Opening was interrupted", e); + } + OpenException thrownException = r.getException(); + if (thrownException != null) + throw thrownException; + return r.getDataflow(); + } + + public WorkflowBundle performOpenDataflow(FileType fileType, Object source) + throws OpenException { + DataflowInfo dataflowInfo; + WorkflowBundle workflowBundle; + dataflowInfo = openDataflowSilently(fileType, source); + workflowBundle = dataflowInfo.getDataflow(); + openDataflowInternal(workflowBundle); + getOpenDataflowInfo(workflowBundle).setOpenedFrom(dataflowInfo); + observers.notify(new OpenedDataflowEvent(workflowBundle)); + return workflowBundle; + } + + @Override + public DataflowInfo openDataflowSilently(FileType fileType, Object source) + throws OpenException { + Set<DataflowPersistenceHandler> handlers; + Class<? extends Object> sourceClass = source.getClass(); + + boolean unknownFileType = (fileType == null); + if (unknownFileType) + handlers = getPersistanceHandlerRegistry().getOpenHandlersFor( + sourceClass); + else + handlers = getPersistanceHandlerRegistry().getOpenHandlersFor( + fileType, sourceClass); + if (handlers.isEmpty()) + throw new OpenException("Unsupported file type or class " + + fileType + " " + sourceClass); + + Throwable lastException = null; + for (DataflowPersistenceHandler handler : handlers) { + Collection<FileType> fileTypes; + if (unknownFileType) + fileTypes = handler.getOpenFileTypes(); + else + fileTypes = singleton(fileType); + for (FileType candidateFileType : fileTypes) { + if (unknownFileType && (source instanceof File)) + /* + * If source is file but fileType was not explicitly set + * from the open workflow dialog - check the file extension + * and decide which handler to use based on that (so that we + * do not loop though all handlers) + */ + if (!((File) source).getPath().endsWith( + candidateFileType.getExtension())) + continue; + + try { + DataflowInfo openDataflow = handler.openDataflow( + candidateFileType, source); + WorkflowBundle workflowBundle = openDataflow.getDataflow(); + logger.info("Loaded workflow: " + workflowBundle.getName() + + " " + workflowBundle.getGlobalBaseURI() + + " from " + source + " using " + handler); + return openDataflow; + } catch (OpenException ex) { + logger.warn("Could not open workflow " + source + " using " + + handler + " of type " + candidateFileType); + lastException = ex; + } + } + } + throw new OpenException("Could not open workflow " + source + "\n", + lastException); + } + + /** + * Mark the WorkflowBundle as opened, and close the blank WorkflowBundle if + * needed. + * + * @param workflowBundle + * WorkflowBundle that has been opened + */ + protected void openDataflowInternal(WorkflowBundle workflowBundle) { + if (workflowBundle == null) + throw new NullPointerException("Dataflow can't be null"); + if (isDataflowOpen(workflowBundle)) + throw new IllegalArgumentException("Workflow is already open: " + + workflowBundle); + + openDataflowInfos.put(workflowBundle, new OpenDataflowInfo()); + setCurrentDataflow(workflowBundle); + if (openDataflowInfos.size() == 2 && blankWorkflowBundle != null) + /* + * Behave like a word processor and close the blank WorkflowBundle + * when another workflow has been opened + */ + try { + closeDataflow(blankWorkflowBundle, true); + } catch (UnsavedException e) { + logger.error("Blank workflow was modified " + + "and could not be closed"); + } + } + + @Override + public void removeObserver(Observer<FileManagerEvent> observer) { + observers.removeObserver(observer); + } + + @Override + public void saveDataflow(WorkflowBundle workflowBundle, + boolean failOnOverwrite) throws SaveException { + if (workflowBundle == null) + throw new NullPointerException("Dataflow can't be null"); + OpenDataflowInfo lastSave = getOpenDataflowInfo(workflowBundle); + if (lastSave.getSource() == null) + throw new SaveException("Can't save without source " + + workflowBundle); + saveDataflow(workflowBundle, lastSave.getFileType(), + lastSave.getSource(), failOnOverwrite); + } + + @Override + public void saveDataflow(WorkflowBundle workflowBundle, FileType fileType, + Object destination, boolean failOnOverwrite) throws SaveException { + DataflowInfo savedDataflow = saveDataflowSilently(workflowBundle, + fileType, destination, failOnOverwrite); + getOpenDataflowInfo(workflowBundle).setSavedTo(savedDataflow); + observers.notify(new SavedDataflowEvent(workflowBundle)); + } + + @Override + public DataflowInfo saveDataflowSilently(WorkflowBundle workflowBundle, + FileType fileType, Object destination, boolean failOnOverwrite) + throws SaveException, OverwriteException { + Set<DataflowPersistenceHandler> handlers; + + Class<? extends Object> destinationClass = destination.getClass(); + if (fileType != null) + handlers = getPersistanceHandlerRegistry().getSaveHandlersForType( + fileType, destinationClass); + else + handlers = getPersistanceHandlerRegistry().getSaveHandlersFor( + destinationClass); + + SaveException lastException = null; + for (DataflowPersistenceHandler handler : handlers) { + if (failOnOverwrite) { + OpenDataflowInfo openDataflowInfo = getOpenDataflowInfo(workflowBundle); + if (handler.wouldOverwriteDataflow(workflowBundle, fileType, + destination, openDataflowInfo.getDataflowInfo())) + throw new OverwriteException(destination); + } + try { + DataflowInfo savedDataflow = handler.saveDataflow( + workflowBundle, fileType, destination); + savedDataflow.getDataflow(); + logger.info("Saved workflow: " + workflowBundle.getName() + " " + + workflowBundle.getGlobalBaseURI() + " to " + + savedDataflow.getCanonicalSource() + " using " + + handler); + return savedDataflow; + } catch (SaveException ex) { + logger.warn("Could not save to " + destination + " using " + + handler); + lastException = ex; + } + } + + if (lastException == null) + throw new SaveException("Unsupported file type or class " + + fileType + " " + destinationClass); + throw new SaveException("Could not save to " + destination + ":\n" + + lastException.getLocalizedMessage(), lastException); + } + + @Override + public void setCurrentDataflow(WorkflowBundle workflowBundle) { + setCurrentDataflow(workflowBundle, false); + } + + @Override + public void setCurrentDataflow(WorkflowBundle workflowBundle, + boolean openIfNeeded) { + currentWorkflowBundle = workflowBundle; + if (!isDataflowOpen(workflowBundle)) { + if (!openIfNeeded) + throw new IllegalArgumentException("Workflow is not open: " + + workflowBundle); + openDataflow(workflowBundle); + return; + } + observers.notify(new SetCurrentDataflowEvent(workflowBundle)); + } + + @Override + public void setDataflowChanged(WorkflowBundle workflowBundle, + boolean isChanged) { + getOpenDataflowInfo(workflowBundle).setIsChanged(isChanged); + if (blankWorkflowBundle == workflowBundle) + blankWorkflowBundle = null; + } + + @Override + public Object getCanonical(Object source) throws IllegalArgumentException, + URISyntaxException, IOException { + Object canonicalSource = source; + + if (source instanceof URL) { + URL url = ((URL) source); + if (url.getProtocol().equalsIgnoreCase("file")) + canonicalSource = new File(url.toURI()); + } + + if (canonicalSource instanceof File) + canonicalSource = ((File) canonicalSource).getCanonicalFile(); + return canonicalSource; + } + + public void setDataflowPersistenceHandlerRegistry( + DataflowPersistenceHandlerRegistry dataflowPersistenceHandlerRegistry) { + this.dataflowPersistenceHandlerRegistry = dataflowPersistenceHandlerRegistry; + } + + /** + * Observe the {@link EditManager} for changes to open workflowBundles. A + * change of an open workflow would set it as changed using + * {@link FileManagerImpl#setDataflowChanged(Dataflow, boolean)}. + * + * @author Stian Soiland-Reyes + * + */ + private final class EditManagerObserver implements + Observer<EditManagerEvent> { + @Override + public void notify(Observable<EditManagerEvent> sender, + EditManagerEvent message) throws Exception { + if (message instanceof AbstractDataflowEditEvent) { + AbstractDataflowEditEvent dataflowEdit = (AbstractDataflowEditEvent) message; + WorkflowBundle workflowBundle = dataflowEdit.getDataFlow(); + /** + * TODO: on undo/redo - keep last event or similar to determine + * if workflow was saved before. See + * FileManagerTest#isChangedWithUndo(). + */ + setDataflowChanged(workflowBundle, true); + } + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileTypeFileFilter.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileTypeFileFilter.java new file mode 100644 index 0000000..6416163 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/FileTypeFileFilter.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.workbench.file.FileType; + +public class FileTypeFileFilter extends FileFilter { + private final FileType fileType; + + public FileTypeFileFilter(FileType fileType) { + this.fileType = fileType; + } + + @Override + public String getDescription() { + return fileType.getDescription(); + } + + @Override + public boolean accept(File file) { + if (file.isDirectory()) + // Don't grey out directories + return true; + if (fileType.getExtension() == null) + return false; + return file.getName().toLowerCase() + .endsWith("." + fileType.getExtension()); + } + + public FileType getFileType() { + return fileType; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/MultipleFileTypes.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/MultipleFileTypes.java new file mode 100644 index 0000000..c398805 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/MultipleFileTypes.java
@@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import java.io.File; +import java.util.Set; + +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.workbench.file.FileType; + +public class MultipleFileTypes extends FileFilter { + private String description; + private final Set<FileType> fileTypes; + + public MultipleFileTypes(Set<FileType> fileTypes, String description) { + this.fileTypes = fileTypes; + this.description = description; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public boolean accept(File file) { + if (file.isDirectory()) + return true; + + String lowerFileName = file.getName().toLowerCase(); + for (FileType fileType : fileTypes) { + if (fileType.getExtension() == null) + continue; + if (lowerFileName.endsWith(fileType.getExtension())) + return true; + } + return false; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInProgressDialog.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInProgressDialog.java new file mode 100644 index 0000000..dc08cff --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInProgressDialog.java
@@ -0,0 +1,88 @@ + +/******************************************************************************* + * Copyright (C) 2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import static java.awt.BorderLayout.CENTER; +import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.workingIcon; + +import java.awt.BorderLayout; +import java.awt.Dimension; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; + +/** + * Dialog that is popped up while we are opening a workflow. + * + * @author Alex Nenadic + * @author Alan R Williams + */ +@SuppressWarnings("serial") +public class OpenDataflowInProgressDialog extends HelpEnabledDialog { + private boolean userCancelled = false; + + public OpenDataflowInProgressDialog() { + super(getMainWindow(), "Opening workflow", true); + setResizable(false); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(new EmptyBorder(10,10,10,10)); + + JPanel textPanel = new JPanel(); + JLabel text = new JLabel(workingIcon); + text.setText("Opening workflow..."); + text.setBorder(new EmptyBorder(10,0,10,0)); + textPanel.add(text); + panel.add(textPanel, CENTER); + +/* + * Cancellation does not work when opening + + // Cancel button + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + userCancelled = true; + setVisible(false); + dispose(); + } + }); + JPanel cancelButtonPanel = new JPanel(); + cancelButtonPanel.add(cancelButton); + panel.add(cancelButtonPanel, BorderLayout.SOUTH); +*/ + setContentPane(panel); + setPreferredSize(new Dimension(300, 100)); + + pack(); + } + + public boolean hasUserCancelled() { + return userCancelled; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInfo.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInfo.java new file mode 100644 index 0000000..4a4a1e3 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowInfo.java
@@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import java.util.Date; + +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.FileType; + +/** + * Information about an open dataflow. + * + * @author Stian Soiland-Reyes + */ +public class OpenDataflowInfo { + private DataflowInfo dataflowInfo; + private boolean isChanged; + private Date openedAt; + + public OpenDataflowInfo() { + } + + public FileType getFileType() { + if (dataflowInfo == null) + return null; + return dataflowInfo.getFileType(); + } + + public Date getLastModified() { + if (dataflowInfo == null) + return null; + return dataflowInfo.getLastModified(); + } + + public Date getOpenedAtDate() { + return openedAt; + } + + public Object getSource() { + if (dataflowInfo == null) + return null; + return dataflowInfo.getCanonicalSource(); + } + + public boolean isChanged() { + return isChanged; + } + + public void setIsChanged(boolean isChanged) { + this.isChanged = isChanged; + } + + public synchronized void setOpenedFrom(DataflowInfo dataflowInfo) { + setDataflowInfo(dataflowInfo); + setOpenedAt(new Date()); + setIsChanged(false); + } + + public synchronized void setSavedTo(DataflowInfo dataflowInfo) { + setDataflowInfo(dataflowInfo); + setIsChanged(false); + } + + private void setDataflowInfo(DataflowInfo dataflowInfo) { + this.dataflowInfo = dataflowInfo; + } + + private void setOpenedAt(Date openedAt) { + this.openedAt = openedAt; + } + + public DataflowInfo getDataflowInfo() { + return dataflowInfo; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowRunnable.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowRunnable.java new file mode 100644 index 0000000..9d687b8 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowRunnable.java
@@ -0,0 +1,71 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.file.impl; + +import static java.lang.Thread.sleep; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.ui.SwingWorkerCompletionWaiter; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * @author alanrw + */ +public class OpenDataflowRunnable implements Runnable { + private final FileManagerImpl fileManager; + private final FileType fileType; + private final Object source; + private WorkflowBundle dataflow; + private OpenException e; + + public OpenDataflowRunnable(FileManagerImpl fileManager, FileType fileType, + Object source) { + this.fileManager = fileManager; + this.fileType = fileType; + this.source = source; + } + + @Override + public void run() { + OpenDataflowSwingWorker openDataflowSwingWorker = new OpenDataflowSwingWorker( + fileType, source, fileManager); + OpenDataflowInProgressDialog dialog = new OpenDataflowInProgressDialog(); + openDataflowSwingWorker + .addPropertyChangeListener(new SwingWorkerCompletionWaiter( + dialog)); + openDataflowSwingWorker.execute(); + + /* + * Give a chance to the SwingWorker to finish so we do not have to + * display the dialog + */ + try { + sleep(500); + } catch (InterruptedException e) { + this.e = new OpenException("Opening was interrupted"); + } + if (!openDataflowSwingWorker.isDone()) + dialog.setVisible(true); // this will block the GUI + boolean userCancelled = dialog.hasUserCancelled(); // see if user cancelled the dialog + + if (userCancelled) { + // Stop the OpenDataflowSwingWorker if it is still working + openDataflowSwingWorker.cancel(true); + dataflow = null; + this.e = new OpenException("Opening was cancelled"); + // exit + return; + } + dataflow = openDataflowSwingWorker.getDataflow(); + this.e = openDataflowSwingWorker.getException(); + } + + public WorkflowBundle getDataflow() { + return dataflow; + } + + public OpenException getException() { + return this.e; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowSwingWorker.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowSwingWorker.java new file mode 100644 index 0000000..4cbd2f8 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/OpenDataflowSwingWorker.java
@@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (C) 2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import javax.swing.SwingWorker; + +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +public class OpenDataflowSwingWorker extends + SwingWorker<WorkflowBundle, Object> { + @SuppressWarnings("unused") + private Logger logger = Logger.getLogger(OpenDataflowSwingWorker.class); + private FileType fileType; + private Object source; + private FileManagerImpl fileManagerImpl; + private WorkflowBundle workflowBundle; + private OpenException e = null; + + public OpenDataflowSwingWorker(FileType fileType, Object source, + FileManagerImpl fileManagerImpl) { + this.fileType = fileType; + this.source = source; + this.fileManagerImpl = fileManagerImpl; + } + + @Override + protected WorkflowBundle doInBackground() throws Exception { + try { + workflowBundle = fileManagerImpl.performOpenDataflow(fileType, + source); + } catch (OpenException e) { + this.e = e; + } + return workflowBundle; + } + + public WorkflowBundle getDataflow() { + return workflowBundle; + } + + public OpenException getException() { + return e; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2DataflowOpener.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2DataflowOpener.java new file mode 100644 index 0000000..bf37faf --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2DataflowOpener.java
@@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.io.ReaderException; +import uk.org.taverna.scufl2.api.io.WorkflowBundleIO; + +public class T2DataflowOpener extends AbstractDataflowPersistenceHandler + implements DataflowPersistenceHandler { + private static final T2FlowFileType T2_FLOW_FILE_TYPE = new T2FlowFileType(); + private static Logger logger = Logger.getLogger(T2DataflowOpener.class); + + private WorkflowBundleIO workflowBundleIO; + + @SuppressWarnings("resource") + @Override + public DataflowInfo openDataflow(FileType fileType, Object source) + throws OpenException { + if (!getOpenFileTypes().contains(fileType)) + throw new OpenException("Unsupported file type " + + fileType); + InputStream inputStream; + Date lastModified = null; + Object canonicalSource = source; + if (source instanceof InputStream) + inputStream = (InputStream) source; + else if (source instanceof File) + try { + inputStream = new FileInputStream((File) source); + } catch (FileNotFoundException e) { + throw new OpenException("Could not open file " + source + ":\n" + e.getLocalizedMessage(), e); + } + else if (source instanceof URL) { + URL url = ((URL) source); + try { + URLConnection connection = url.openConnection(); + connection.setRequestProperty("Accept", "text/xml"); + inputStream = connection.getInputStream(); + if (connection.getLastModified() != 0) + lastModified = new Date(connection.getLastModified()); + } catch (IOException e) { + throw new OpenException("Could not open connection to URL " + + source+ ":\n" + e.getLocalizedMessage(), e); + } + try { + if (url.getProtocol().equalsIgnoreCase("file")) + canonicalSource = new File(url.toURI()); + } catch (URISyntaxException e) { + logger.warn("Invalid file URI created from " + url); + } + } else { + throw new OpenException("Unsupported source type " + + source.getClass()); + } + + final WorkflowBundle workflowBundle; + try { + workflowBundle = openDataflowStream(inputStream); + } finally { + try { + if (!(source instanceof InputStream)) + // We created the stream, we'll close it + inputStream.close(); + } catch (IOException ex) { + logger.warn("Could not close inputstream " + inputStream, ex); + } + } + if (canonicalSource instanceof File) + return new FileDataflowInfo(T2_FLOW_FILE_TYPE, + (File) canonicalSource, workflowBundle); + return new DataflowInfo(T2_FLOW_FILE_TYPE, canonicalSource, + workflowBundle, lastModified); + } + + protected WorkflowBundle openDataflowStream(InputStream workflowXMLstream) + throws OpenException { + WorkflowBundle workflowBundle; + try { + workflowBundle = workflowBundleIO.readBundle(workflowXMLstream, null); + } catch (ReaderException e) { + throw new OpenException("Could not read the workflow", e); + } catch (IOException e) { + throw new OpenException("Could not open the workflow file for parsing", e); + } catch (Exception e) { + throw new OpenException("Error while opening workflow", e); + } + + return workflowBundle; + } + + @Override + public List<FileType> getOpenFileTypes() { + return Arrays.<FileType> asList(new T2FlowFileType()); + } + + @Override + public List<Class<?>> getOpenSourceTypes() { + return Arrays.<Class<?>> asList(InputStream.class, URL.class, + File.class); + } + + public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) { + this.workflowBundleIO = workflowBundleIO; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FileFilter.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FileFilter.java new file mode 100644 index 0000000..62b5892 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FileFilter.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +/** + * + */ +package net.sf.taverna.t2.workbench.file.impl; + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +public class T2FileFilter extends FileFilter { + @Override + public boolean accept(final File file) { + return file.getName().toLowerCase().endsWith(".t2flow"); + } + + @Override + public String getDescription() { + return "Taverna 2 workflows"; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FlowFileType.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FlowFileType.java new file mode 100644 index 0000000..a2774e4 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/T2FlowFileType.java
@@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import net.sf.taverna.t2.workbench.file.FileType; + +public class T2FlowFileType extends FileType { + public static final String APPLICATION_VND_TAVERNA_T2FLOW_XML = "application/vnd.taverna.t2flow+xml"; + + @Override + public String getDescription() { + return "Taverna 2 workflow"; + } + + @Override + public String getExtension() { + return "t2flow"; + } + + @Override + public String getMimeType() { + return APPLICATION_VND_TAVERNA_T2FLOW_XML; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileFilter.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileFilter.java new file mode 100644 index 0000000..d272b41 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileFilter.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +/** + * + */ +package net.sf.taverna.t2.workbench.file.impl; + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +public class WorkflowBundleFileFilter extends FileFilter { + @Override + public boolean accept(final File file) { + return file.getName().toLowerCase().endsWith(".wfbundle"); + } + + @Override + public String getDescription() { + return "Taverna 3 workflows"; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileType.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileType.java new file mode 100644 index 0000000..09b30b0 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleFileType.java
@@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import net.sf.taverna.t2.workbench.file.FileType; + +public class WorkflowBundleFileType extends FileType { + public static final String APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE = "application/vnd.taverna.scufl2.workflow-bundle"; + + @Override + public String getDescription() { + return "Taverna 3 workflow"; + } + + @Override + public String getExtension() { + return "wfbundle"; + } + + @Override + public String getMimeType() { + return APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleOpener.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleOpener.java new file mode 100644 index 0000000..7c54f7e --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleOpener.java
@@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.io.ReaderException; +import uk.org.taverna.scufl2.api.io.WorkflowBundleIO; + +public class WorkflowBundleOpener extends AbstractDataflowPersistenceHandler + implements DataflowPersistenceHandler { + private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new WorkflowBundleFileType(); + private static Logger logger = Logger.getLogger(WorkflowBundleOpener.class); + private WorkflowBundleIO workflowBundleIO; + + @SuppressWarnings("resource") + @Override + public DataflowInfo openDataflow(FileType fileType, Object source) + throws OpenException { + if (!getOpenFileTypes().contains(fileType)) + throw new OpenException("Unsupported file type " + fileType); + InputStream inputStream; + Date lastModified = null; + Object canonicalSource = source; + if (source instanceof InputStream) { + inputStream = (InputStream) source; + } else if (source instanceof File) { + try { + inputStream = new FileInputStream((File) source); + } catch (FileNotFoundException e) { + throw new OpenException("Could not open file " + source + ":\n" + + e.getLocalizedMessage(), e); + } + } else if (source instanceof URL) { + URL url = ((URL) source); + try { + URLConnection connection = url.openConnection(); + connection.setRequestProperty("Accept", "application/zip"); + inputStream = connection.getInputStream(); + if (connection.getLastModified() != 0) + lastModified = new Date(connection.getLastModified()); + } catch (IOException e) { + throw new OpenException("Could not open connection to URL " + + source + ":\n" + e.getLocalizedMessage(), e); + } + try { + if (url.getProtocol().equalsIgnoreCase("file")) + canonicalSource = new File(url.toURI()); + } catch (URISyntaxException e) { + logger.warn("Invalid file URI created from " + url); + } + } else + throw new OpenException("Unsupported source type " + + source.getClass()); + + final WorkflowBundle workflowBundle; + try { + workflowBundle = openDataflowStream(inputStream); + } finally { + // We created the stream, we'll close it + try { + if (!(source instanceof InputStream)) + inputStream.close(); + } catch (IOException ex) { + logger.warn("Could not close inputstream " + inputStream, ex); + } + } + if (canonicalSource instanceof File) + return new FileDataflowInfo(WF_BUNDLE_FILE_TYPE, + (File) canonicalSource, workflowBundle); + return new DataflowInfo(WF_BUNDLE_FILE_TYPE, canonicalSource, + workflowBundle, lastModified); + } + + protected WorkflowBundle openDataflowStream(InputStream inputStream) + throws OpenException { + WorkflowBundle workflowBundle; + try { + workflowBundle = workflowBundleIO.readBundle(inputStream, null); + } catch (ReaderException e) { + throw new OpenException("Could not read the workflow", e); + } catch (IOException e) { + throw new OpenException("Could not open the workflow for parsing", + e); + } catch (Exception e) { + throw new OpenException("Error while opening workflow", e); + } + + return workflowBundle; + } + + @Override + public List<FileType> getOpenFileTypes() { + return Arrays.<FileType> asList(WF_BUNDLE_FILE_TYPE); + } + + @Override + public List<Class<?>> getOpenSourceTypes() { + return Arrays.<Class<?>> asList(InputStream.class, URL.class, + File.class); + } + + public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) { + this.workflowBundleIO = workflowBundleIO; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleSaver.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleSaver.java new file mode 100644 index 0000000..f6b7108 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/WorkflowBundleSaver.java
@@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import static net.sf.taverna.t2.workbench.file.impl.WorkflowBundleFileType.APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import net.sf.taverna.t2.workbench.file.AbstractDataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.SaveException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.io.WorkflowBundleIO; + +public class WorkflowBundleSaver extends AbstractDataflowPersistenceHandler + implements DataflowPersistenceHandler { + private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new WorkflowBundleFileType(); + private static Logger logger = Logger.getLogger(WorkflowBundleSaver.class); + private WorkflowBundleIO workflowBundleIO; + + @Override + public DataflowInfo saveDataflow(WorkflowBundle workflowBundle, FileType fileType, + Object destination) throws SaveException { + if (!getSaveFileTypes().contains(fileType)) + throw new IllegalArgumentException("Unsupported file type " + + fileType); + OutputStream outStream; + if (destination instanceof File) + try { + outStream = new FileOutputStream((File) destination); + } catch (FileNotFoundException e) { + throw new SaveException("Can't create workflow file " + + destination + ":\n" + e.getLocalizedMessage(), e); + } + else if (destination instanceof OutputStream) + outStream = (OutputStream) destination; + else + throw new SaveException("Unsupported destination type " + + destination.getClass()); + + try { + saveDataflowToStream(workflowBundle, outStream); + } finally { + try { + // Only close if we opened the stream + if (!(destination instanceof OutputStream)) + outStream.close(); + } catch (IOException e) { + logger.warn("Could not close stream", e); + } + } + + if (destination instanceof File) + return new FileDataflowInfo(WF_BUNDLE_FILE_TYPE, (File) destination, + workflowBundle); + return new DataflowInfo(WF_BUNDLE_FILE_TYPE, destination, workflowBundle); + } + + protected void saveDataflowToStream(WorkflowBundle workflowBundle, + OutputStream fileOutStream) throws SaveException { + try { + workflowBundleIO.writeBundle(workflowBundle, fileOutStream, + APPLICATION_VND_TAVERNA_SCUFL2_WORKFLOW_BUNDLE); + } catch (Exception e) { + throw new SaveException("Can't write workflow:\n" + + e.getLocalizedMessage(), e); + } + } + + @Override + public List<FileType> getSaveFileTypes() { + return Arrays.<FileType> asList(WF_BUNDLE_FILE_TYPE); + } + + @Override + public List<Class<?>> getSaveDestinationTypes() { + return Arrays.<Class<?>> asList(File.class, OutputStream.class); + } + + @Override + public boolean wouldOverwriteDataflow(WorkflowBundle workflowBundle, FileType fileType, + Object destination, DataflowInfo lastDataflowInfo) { + if (!getSaveFileTypes().contains(fileType)) + throw new IllegalArgumentException("Unsupported file type " + + fileType); + if (!(destination instanceof File)) + return false; + + File file; + try { + file = ((File) destination).getCanonicalFile(); + } catch (IOException e) { + return false; + } + if (!file.exists()) + return false; + if (lastDataflowInfo == null) + return true; + Object lastDestination = lastDataflowInfo.getCanonicalSource(); + if (!(lastDestination instanceof File)) + return true; + File lastFile = (File) lastDestination; + if (!lastFile.getAbsoluteFile().equals(file)) + return true; + + Date lastModified = new Date(file.lastModified()); + if (lastModified.equals(lastDataflowInfo.getLastModified())) + return false; + return true; + } + + public void setWorkflowBundleIO(WorkflowBundleIO workflowBundleIO) { + this.workflowBundleIO = workflowBundleIO; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseAllWorkflowsAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseAllWorkflowsAction.java new file mode 100644 index 0000000..2309b9c --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseAllWorkflowsAction.java
@@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.InputEvent.SHIFT_DOWN_MASK; +import static java.awt.event.KeyEvent.VK_L; +import static java.awt.event.KeyEvent.VK_W; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.closeAllIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.Collections; +import java.util.List; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +@SuppressWarnings("serial") +public class CloseAllWorkflowsAction extends AbstractAction { + @SuppressWarnings("unused") + private static Logger logger = Logger.getLogger(CloseWorkflowAction.class); + private static final String CLOSE_ALL_WORKFLOWS = "Close all workflows"; + private FileManager fileManager; + private CloseWorkflowAction closeWorkflowAction; + + public CloseAllWorkflowsAction(EditManager editManager, FileManager fileManager) { + super(CLOSE_ALL_WORKFLOWS, closeAllIcon); + this.fileManager = fileManager; + closeWorkflowAction = new CloseWorkflowAction(editManager, fileManager); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_W, getDefaultToolkit().getMenuShortcutKeyMask() + | SHIFT_DOWN_MASK)); + putValue(MNEMONIC_KEY, VK_L); + } + + @Override + public void actionPerformed(ActionEvent event) { + Component parentComponent = null; + if (event.getSource() instanceof Component) + parentComponent = (Component) event.getSource(); + closeAllWorkflows(parentComponent); + } + + public boolean closeAllWorkflows(Component parentComponent) { + // Close in reverse so we can save nested workflows first + List<WorkflowBundle> workflowBundles = fileManager.getOpenDataflows(); + + Collections.reverse(workflowBundles); + + for (WorkflowBundle workflowBundle : workflowBundles) { + boolean success = closeWorkflowAction.closeWorkflow( + parentComponent, workflowBundle); + if (!success) + return false; + } + return true; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseWorkflowAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseWorkflowAction.java new file mode 100644 index 0000000..03c90a6 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/CloseWorkflowAction.java
@@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_C; +import static java.awt.event.KeyEvent.VK_W; +import static javax.swing.JOptionPane.CANCEL_OPTION; +import static javax.swing.JOptionPane.NO_OPTION; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.closeIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.exceptions.UnsavedException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +@SuppressWarnings("serial") +public class CloseWorkflowAction extends AbstractAction { + private static Logger logger = Logger.getLogger(CloseWorkflowAction.class); + private static final String CLOSE_WORKFLOW = "Close workflow"; + private final SaveWorkflowAction saveWorkflowAction; + private FileManager fileManager; + + public CloseWorkflowAction(EditManager editManager, FileManager fileManager) { + super(CLOSE_WORKFLOW, closeIcon); + this.fileManager = fileManager; + saveWorkflowAction = new SaveWorkflowAction(editManager, fileManager); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_W, getDefaultToolkit().getMenuShortcutKeyMask())); + putValue(MNEMONIC_KEY, VK_C); + + } + @Override + public void actionPerformed(ActionEvent e) { + Component parentComponent = null; + if (e.getSource() instanceof Component) + parentComponent = (Component) e.getSource(); + closeWorkflow(parentComponent, fileManager.getCurrentDataflow()); + } + + public boolean closeWorkflow(Component parentComponent, WorkflowBundle workflowBundle) { + if (workflowBundle == null) { + logger.warn("Attempted to close a null workflow"); + return false; + } + + try { + return fileManager.closeDataflow(workflowBundle, true); + } catch (UnsavedException e1) { + fileManager.setCurrentDataflow(workflowBundle); + String msg = "Do you want to save changes before closing the workflow " + + fileManager.getDataflowName(workflowBundle) + "?"; + switch (showConfirmDialog(parentComponent, msg, "Save workflow?", + YES_NO_CANCEL_OPTION)) { + case NO_OPTION: + try { + fileManager.closeDataflow(workflowBundle, false); + return true; + } catch (UnsavedException e2) { + logger.error("Unexpected UnsavedException while " + + "closing workflow", e2); + return false; + } + case YES_OPTION: + boolean saved = saveWorkflowAction.saveDataflow( + parentComponent, workflowBundle); + if (!saved) + return false; + return closeWorkflow(parentComponent, workflowBundle); + case CANCEL_OPTION: + default: + return false; + } + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/NewWorkflowAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/NewWorkflowAction.java new file mode 100644 index 0000000..3b9256a --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/NewWorkflowAction.java
@@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_N; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.newIcon; + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.workbench.file.FileManager; + +import org.apache.log4j.Logger; + +@SuppressWarnings("serial") +public class NewWorkflowAction extends AbstractAction { + @SuppressWarnings("unused") + private static Logger logger = Logger.getLogger(NewWorkflowAction.class); + private static final String NEW_WORKFLOW = "New workflow"; + private FileManager fileManager; + + public NewWorkflowAction(FileManager fileManager) { + super(NEW_WORKFLOW, newIcon); + this.fileManager = fileManager; + putValue(SHORT_DESCRIPTION, NEW_WORKFLOW); + putValue(MNEMONIC_KEY, KeyEvent.VK_N); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_N, getDefaultToolkit().getMenuShortcutKeyMask())); + } + + @Override + public void actionPerformed(ActionEvent e) { + fileManager.newDataflow(); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenNestedWorkflowAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenNestedWorkflowAction.java new file mode 100644 index 0000000..08030c7 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenNestedWorkflowAction.java
@@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import java.awt.Component; +import java.io.File; + +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * An action for opening a nested workflow from a file. + * + * @author Alex Nenadic + */ +public class OpenNestedWorkflowAction extends OpenWorkflowAction { + private static final long serialVersionUID = -5398423684000142379L; + private static Logger logger = Logger + .getLogger(OpenNestedWorkflowAction.class); + + public OpenNestedWorkflowAction(FileManager fileManager) { + super(fileManager); + } + + /** + * Opens a nested workflow from a file (should be one file even though the + * method takes a list of files - this is because it overrides the + * {@link OpenWorkflowAction#openWorkflows(Component, File[], FileType, OpenCallback) + * openWorkflows(...)} method). + */ + @Override + public void openWorkflows(final Component parentComponent, File[] files, + FileType fileType, OpenCallback openCallback) { + ErrorLoggingOpenCallbackWrapper callback = new ErrorLoggingOpenCallbackWrapper( + openCallback); + for (File file : files) + try { + callback.aboutToOpenDataflow(file); + WorkflowBundle workflowBundle = fileManager.openDataflow( + fileType, file); + callback.openedDataflow(file, workflowBundle); + } catch (final RuntimeException ex) { + logger.warn("Could not open workflow from " + file, ex); + if (!callback.couldNotOpenDataflow(file, ex)) + showErrorMessage(parentComponent, file, ex); + } catch (final OpenException ex) { + logger.warn("Could not open workflow from " + file, ex); + if (!callback.couldNotOpenDataflow(file, ex)) + showErrorMessage(parentComponent, file, ex); + return; + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowAction.java new file mode 100644 index 0000000..e2ecbd7 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowAction.java
@@ -0,0 +1,395 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_O; +import static java.util.prefs.Preferences.userNodeForPackage; +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JOptionPane.CANCEL_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.JOptionPane.showOptionDialog; +import static javax.swing.KeyStroke.getKeyStroke; +import static javax.swing.SwingUtilities.invokeLater; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.openIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.prefs.Preferences; + +import javax.swing.AbstractAction; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.file.impl.FileTypeFileFilter; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * An action for opening a workflow from a file. All file types exposed by the + * {@link FileManager} as compatible with the {@link File} type are supported. + * + * @author Stian Soiland-Reyes + */ +public class OpenWorkflowAction extends AbstractAction { + private static final long serialVersionUID = 103237694130052153L; + private static Logger logger = Logger.getLogger(OpenWorkflowAction.class); + private static final String OPEN_WORKFLOW = "Open workflow..."; + + public final OpenCallback DUMMY_OPEN_CALLBACK = new OpenCallbackAdapter(); + protected FileManager fileManager; + + public OpenWorkflowAction(FileManager fileManager) { + super(OPEN_WORKFLOW, openIcon); + this.fileManager = fileManager; + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_O, getDefaultToolkit().getMenuShortcutKeyMask())); + putValue(MNEMONIC_KEY, VK_O); + } + + @Override + public void actionPerformed(ActionEvent e) { + final Component parentComponent; + if (e.getSource() instanceof Component) + parentComponent = (Component) e.getSource(); + else + parentComponent = null; + openWorkflows(parentComponent); + } + + /** + * Pop up an Open-dialogue to select one or more workflow files to open. + * <p> + * Note that the file opening occurs in a separate thread. If you want to + * check if the file was opened or not, which workflow was opened, etc, use + * {@link #openWorkflows(Component, OpenCallback)} instead. + * + * @see #openWorkflows(Component, OpenCallback) + * @param parentComponent + * The UI parent component to use for pop up dialogues + * + * @return <code>false</code> if no files were selected or the dialogue was + * cancelled, or <code>true</code> if the process of opening one or + * more files has been started. + */ + public void openWorkflows(Component parentComponent) { + openWorkflows(parentComponent, DUMMY_OPEN_CALLBACK); + } + + /** + * Open an array of workflow files. + * + * @param parentComponent + * Parent component for UI dialogues + * @param files + * Array of files to be opened + * @param fileType + * {@link FileType} of the files that are to be opened, for + * instance + * {@link net.sf.taverna.t2.workbench.file.impl.T2FlowFileType}, + * or <code>null</code> to guess. + * @param openCallback + * An {@link OpenCallback} to be invoked during and after opening + * the file. Use {@link OpenWorkflowAction#DUMMY_OPEN_CALLBACK} + * if no callback is needed. + */ + public void openWorkflows(final Component parentComponent, File[] files, + FileType fileType, OpenCallback openCallback) { + ErrorLoggingOpenCallbackWrapper callback = new ErrorLoggingOpenCallbackWrapper( + openCallback); + for (File file : files) + try { + Object canonicalSource = fileManager.getCanonical(file); + WorkflowBundle alreadyOpen = fileManager.getDataflowBySource(canonicalSource); + if (alreadyOpen != null) { + /* + * The workflow from the same source is already opened - ask + * the user if they want to switch to it or open another + * copy... + */ + + Object[] options = { "Switch to opened", "Open new copy", + "Cancel" }; + switch (showOptionDialog( + null, + "The workflow from the same location is already opened.\n" + + "Do you want to switch to it or open a new copy?", + "File Manager Alert", YES_NO_CANCEL_OPTION, + QUESTION_MESSAGE, null, options, // the titles of buttons + options[0])) { // default button title + case YES_OPTION: + fileManager.setCurrentDataflow(alreadyOpen); + return; + case CANCEL_OPTION: + // do nothing + return; + } + // else open the workflow as usual + } + + callback.aboutToOpenDataflow(file); + WorkflowBundle workflowBundle = fileManager.openDataflow(fileType, file); + callback.openedDataflow(file, workflowBundle); + } catch (RuntimeException ex) { + logger.warn("Failed to open workflow from " + file, ex); + if (!callback.couldNotOpenDataflow(file, ex)) + showErrorMessage(parentComponent, file, ex); + } catch (Exception ex) { + logger.warn("Failed to open workflow from " + file, ex); + if (!callback.couldNotOpenDataflow(file, ex)) + showErrorMessage(parentComponent, file, ex); + return; + } + } + + /** + * Pop up an Open-dialogue to select one or more workflow files to open. + * + * @param parentComponent + * The UI parent component to use for pop up dialogues + * @param openCallback + * An {@link OpenCallback} to be called during the file opening. + * The callback will be invoked for each file that has been + * opened, as file opening happens in a separate thread that + * might execute after the return of this method. + * @return <code>false</code> if no files were selected or the dialogue was + * cancelled, or <code>true</code> if the process of opening one or + * more files has been started. + */ + public boolean openWorkflows(final Component parentComponent, + OpenCallback openCallback) { + JFileChooser fileChooser = new JFileChooser(); + Preferences prefs = userNodeForPackage(getClass()); + String curDir = prefs + .get("currentDir", System.getProperty("user.home")); + fileChooser.setDialogTitle(OPEN_WORKFLOW); + + fileChooser.resetChoosableFileFilters(); + fileChooser.setAcceptAllFileFilterUsed(false); + List<FileFilter> fileFilters = fileManager.getOpenFileFilters(); + if (fileFilters.isEmpty()) { + logger.warn("No file types found for opening workflow"); + showMessageDialog(parentComponent, + "No file types found for opening workflow.", "Error", + ERROR_MESSAGE); + return false; + } + for (FileFilter fileFilter : fileFilters) + fileChooser.addChoosableFileFilter(fileFilter); + fileChooser.setFileFilter(fileFilters.get(0)); + fileChooser.setCurrentDirectory(new File(curDir)); + fileChooser.setMultiSelectionEnabled(true); + + int returnVal = fileChooser.showOpenDialog(parentComponent); + if (returnVal == APPROVE_OPTION) { + prefs.put("currentDir", fileChooser.getCurrentDirectory() + .toString()); + final File[] selectedFiles = fileChooser.getSelectedFiles(); + if (selectedFiles.length == 0) { + logger.warn("No files selected"); + return false; + } + FileFilter fileFilter = fileChooser.getFileFilter(); + FileType fileType; + if (fileFilter instanceof FileTypeFileFilter) + fileType = ((FileTypeFileFilter) fileChooser.getFileFilter()) + .getFileType(); + else + // Unknown filetype, try all of them + fileType = null; + new FileOpenerThread(parentComponent, selectedFiles, fileType, + openCallback).start(); + return true; + } + return false; + } + + /** + * Show an error message if a file could not be opened + * + * @param parentComponent + * @param file + * @param throwable + */ + protected void showErrorMessage(final Component parentComponent, + final File file, final Throwable throwable) { + invokeLater(new Runnable() { + @Override + public void run() { + Throwable cause = throwable; + while (cause.getCause() != null) + cause = cause.getCause(); + showMessageDialog( + parentComponent, + "Failed to open workflow from " + file + ": \n" + + cause.getMessage(), "Warning", + WARNING_MESSAGE); + } + }); + + } + + /** + * Callback interface for openWorkflows(). + * <p> + * The callback will be invoked during the invocation of + * {@link OpenWorkflowAction#openWorkflows(Component, OpenCallback)} and + * {@link OpenWorkflowAction#openWorkflows(Component, File[], FileType, OpenCallback)} + * as file opening happens in a separate thread. + * + * @author Stian Soiland-Reyes + */ + public interface OpenCallback { + /** + * Called before a workflowBundle is to be opened from the given file + * + * @param file + * File which workflowBundle is to be opened + */ + void aboutToOpenDataflow(File file); + + /** + * Called if an exception happened while attempting to open the + * workflowBundle. + * + * @param file + * File which was attempted to be opened + * @param ex + * An {@link OpenException} or a {@link RuntimeException}. + * @return <code>true</code> if the error has been handled, or + * <code>false</code>3 if a UI warning dialogue is to be opened. + */ + boolean couldNotOpenDataflow(File file, Exception ex); + + /** + * Called when a workflowBundle has been successfully opened. The workflowBundle + * will be registered in {@link FileManager#getOpenDataflows()}. + * + * @param file + * File from which workflowBundle was opened + * @param workflowBundle + * WorkflowBundle that was opened + */ + void openedDataflow(File file, WorkflowBundle workflowBundle); + } + + /** + * Adapter for {@link OpenCallback} + * + * @author Stian Soiland-Reyes + */ + public static class OpenCallbackAdapter implements OpenCallback { + @Override + public void aboutToOpenDataflow(File file) { + } + + @Override + public boolean couldNotOpenDataflow(File file, Exception ex) { + return false; + } + + @Override + public void openedDataflow(File file, WorkflowBundle workflowBundle) { + } + } + + private final class FileOpenerThread extends Thread { + private final File[] files; + private final FileType fileType; + private final OpenCallback openCallback; + private final Component parentComponent; + + private FileOpenerThread(Component parentComponent, + File[] selectedFiles, FileType fileType, + OpenCallback openCallback) { + super("Opening workflows(s) " + Arrays.asList(selectedFiles)); + this.parentComponent = parentComponent; + this.files = selectedFiles; + this.fileType = fileType; + this.openCallback = openCallback; + } + + @Override + public void run() { + openWorkflows(parentComponent, files, fileType, openCallback); + } + } + + /** + * A wrapper for {@link OpenCallback} implementations that logs exceptions + * thrown without disrupting the caller of the callback. + * + * @author Stian Soiland-Reyes + */ + protected class ErrorLoggingOpenCallbackWrapper implements OpenCallback { + private final OpenCallback wrapped; + + public ErrorLoggingOpenCallbackWrapper(OpenCallback wrapped) { + this.wrapped = wrapped; + } + + @Override + public void aboutToOpenDataflow(File file) { + try { + wrapped.aboutToOpenDataflow(file); + } catch (RuntimeException wrapperEx) { + logger.warn("Failed OpenCallback " + wrapped + + ".aboutToOpenDataflow(File)", wrapperEx); + } + } + + @Override + public boolean couldNotOpenDataflow(File file, Exception ex) { + try { + return wrapped.couldNotOpenDataflow(file, ex); + } catch (RuntimeException wrapperEx) { + logger.warn("Failed OpenCallback " + wrapped + + ".couldNotOpenDataflow(File, Exception)", wrapperEx); + return false; + } + } + + @Override + public void openedDataflow(File file, WorkflowBundle workflowBundle) { + try { + wrapped.openedDataflow(file, workflowBundle); + } catch (RuntimeException wrapperEx) { + logger.warn("Failed OpenCallback " + wrapped + + ".openedDataflow(File, Dataflow)", wrapperEx); + } + } + } + +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowFromURLAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowFromURLAction.java new file mode 100644 index 0000000..e98a8f2 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/OpenWorkflowFromURLAction.java
@@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_L; +import static javax.swing.JOptionPane.CANCEL_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.QUESTION_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showInputDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.JOptionPane.showOptionDialog; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.openurlIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.net.URL; +import java.util.prefs.Preferences; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.workbench.file.FileManager; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +/** + * An action for opening a workflow from a url. + * + * @author David Withers + */ +public class OpenWorkflowFromURLAction extends AbstractAction { + private static final long serialVersionUID = 1474356457949961974L; + private static Logger logger = Logger + .getLogger(OpenWorkflowFromURLAction.class); + private static Preferences prefs = Preferences + .userNodeForPackage(OpenWorkflowFromURLAction.class); + private static final String PREF_CURRENT_URL = "currentUrl"; + private static final String ACTION_NAME = "Open workflow location..."; + private static final String ACTION_DESCRIPTION = "Open a workflow from the web into a new workflow"; + + private Component component; + private FileManager fileManager; + + public OpenWorkflowFromURLAction(final Component component, + FileManager fileManager) { + this.component = component; + this.fileManager = fileManager; + putValue(SMALL_ICON, openurlIcon); + putValue(NAME, ACTION_NAME); + putValue(SHORT_DESCRIPTION, ACTION_DESCRIPTION); + putValue(MNEMONIC_KEY, VK_L); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_L, getDefaultToolkit().getMenuShortcutKeyMask())); + } + + @Override + public void actionPerformed(ActionEvent e) { + String currentUrl = prefs.get(PREF_CURRENT_URL, "http://"); + + final String url = (String) showInputDialog(component, + "Enter the URL of a workflow definition to load", + "Workflow URL", QUESTION_MESSAGE, null, null, currentUrl); + if (url != null) + new Thread("OpenWorkflowFromURLAction") { + @Override + public void run() { + openFromURL(url); + } + }.start(); + } + + private void openFromURL(String urlString) { + try { + URL url = new URL(urlString); + + Object canonicalSource = fileManager.getCanonical(url); + WorkflowBundle alreadyOpen = fileManager + .getDataflowBySource(canonicalSource); + if (alreadyOpen != null) { + /* + * The workflow from the same source is already opened - ask the + * user if they want to switch to it or open another copy. + */ + + Object[] options = { "Switch to opened", "Open new copy", + "Cancel" }; + int iSelected = showOptionDialog( + null, + "The workflow from the same location is already opened.\n" + + "Do you want to switch to it or open a new copy?", + "File Manager Alert", YES_NO_CANCEL_OPTION, + QUESTION_MESSAGE, null, options, // the titles of buttons + options[0]); // default button title + + if (iSelected == YES_OPTION) { + fileManager.setCurrentDataflow(alreadyOpen); + return; + } else if (iSelected == CANCEL_OPTION) { + // do nothing + return; + } + // else open the workflow as usual + } + + fileManager.openDataflow(null, url); + prefs.put(PREF_CURRENT_URL, urlString); + } catch (Exception ex) { + logger.warn("Failed to open the workflow from url " + urlString + + " \n", ex); + showMessageDialog(component, + "Failed to open the workflow from url " + urlString + " \n" + + ex.getMessage(), "Error!", ERROR_MESSAGE); + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/PasswordInput.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/PasswordInput.java new file mode 100644 index 0000000..401a232 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/PasswordInput.java
@@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.EventQueue.invokeLater; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; + +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; + +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; + +/** + * Simple dialogue to handle username/password input for workflow URL requiring + * http authentication. + * + * @author Stuart Owen + * @author Stian Soiland-Reyes + * @author Alan R Williams + */ +@SuppressWarnings("serial") +public class PasswordInput extends HelpEnabledDialog { + private static Logger logger = Logger.getLogger(PasswordInput.class); + + private String password = null; + private String username = null; + private URL url = null; + private int tryCount = 0; + private final static int MAX_TRIES = 3; + + private JButton cancelButton; + private JLabel jLabel1; + private JLabel jLabel2; + private JLabel messageLabel; + private JButton okButton; + private JPasswordField passwordTextField; + private JLabel urlLabel; + private JTextField usernameTextField; + + public void setUrl(URL url) { + this.url = url; + urlLabel.setText(url.toExternalForm()); + } + + public String getPassword() { + return password; + } + + public String getUsername() { + return username; + } + + public PasswordInput(JFrame parent) { + super(parent, "Authorization", true, null); + initComponents(); + } + + /** Creates new form PasswordInput */ + public PasswordInput() { + super((JFrame) null, "Authorization", true, null); + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + private void initComponents() { + usernameTextField = new javax.swing.JTextField(); + cancelButton = new javax.swing.JButton(); + okButton = new javax.swing.JButton(); + passwordTextField = new javax.swing.JPasswordField(); + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + messageLabel = new javax.swing.JLabel(); + urlLabel = new javax.swing.JLabel(); + + getContentPane().setLayout(null); + + setModal(true); + // setResizable(false); + getContentPane().add(usernameTextField); + usernameTextField.setBounds(20, 80, 280, 22); + + cancelButton.setText("Cancel"); + cancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + getContentPane().add(cancelButton); + cancelButton.setBounds(230, 160, 75, 29); + + okButton.setText("OK"); + okButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + getContentPane().add(okButton); + okButton.setBounds(150, 160, 75, 29); + + getContentPane().add(passwordTextField); + passwordTextField.setBounds(20, 130, 280, 22); + + jLabel1.setText("Username"); + getContentPane().add(jLabel1); + jLabel1.setBounds(20, 60, 70, 16); + + jLabel2.setText("Password"); + getContentPane().add(jLabel2); + jLabel2.setBounds(20, 110, 70, 16); + + messageLabel.setText("A username and password is required for:"); + getContentPane().add(messageLabel); + messageLabel.setBounds(20, 10, 270, 20); + + urlLabel.setText("service"); + getContentPane().add(urlLabel); + urlLabel.setBounds(20, 30, 270, 16); + + pack(); + } + + private void okButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + String password = String.valueOf(passwordTextField.getPassword()); + String username = usernameTextField.getText(); + HttpURLConnection connection; + try { + connection = (HttpURLConnection) url.openConnection(); + String userPassword = username + ":" + password; + /* + * Note: non-latin1 support for basic auth is fragile/unsupported + * and must be MIME-encoded (RFC2047) according to + * https://bugzilla.mozilla.org/show_bug.cgi?id=41489 + */ + byte[] encoded = Base64.encodeBase64(userPassword + .getBytes("latin1")); + connection.setRequestProperty("Authorization", "Basic " + + new String(encoded, "ascii")); + connection.setRequestProperty("Accept", "text/xml"); + int code = connection.getResponseCode(); + + /* + * NB: myExperiment gives a 500 response for an invalid + * username/password + */ + if (code == 401 || code == 500) { + tryCount++; + showMessageDialog(this, "The username and password failed", + "Invalid username or password", ERROR_MESSAGE); + if (tryCount >= MAX_TRIES) { // close after 3 attempts. + this.password = null; + this.username = null; + this.setVisible(false); + } + } else { + this.username = username; + this.password = password; + this.setVisible(false); + } + } catch (IOException ex) { + logger.error("Could not get password", ex); + } + } + + private void cancelButtonActionPerformed(ActionEvent evt) { + this.password = null; + this.username = null; + this.setVisible(false); + } + + /** + * @param args + * the command line arguments + */ + public static void main(String args[]) { + invokeLater(new Runnable() { + @Override + public void run() { + new PasswordInput().setVisible(true); + } + }); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveAllWorkflowsAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveAllWorkflowsAction.java new file mode 100644 index 0000000..6b011d3 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveAllWorkflowsAction.java
@@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.InputEvent.SHIFT_DOWN_MASK; +import static java.awt.event.KeyEvent.VK_A; +import static java.awt.event.KeyEvent.VK_S; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.saveAllIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.Collections; +import java.util.List; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +@SuppressWarnings("serial") +public class SaveAllWorkflowsAction extends AbstractAction { + private final class FileManagerObserver implements + Observer<FileManagerEvent> { + @Override + public void notify(Observable<FileManagerEvent> sender, + FileManagerEvent message) throws Exception { + updateEnabled(); + } + } + + @SuppressWarnings("unused") + private static Logger logger = Logger + .getLogger(SaveAllWorkflowsAction.class); + private static final String SAVE_ALL_WORKFLOWS = "Save all workflows"; + + private final SaveWorkflowAction saveWorkflowAction; + private FileManager fileManager; + private FileManagerObserver fileManagerObserver = new FileManagerObserver(); + + public SaveAllWorkflowsAction(EditManager editManager, + FileManager fileManager) { + super(SAVE_ALL_WORKFLOWS, saveAllIcon); + this.fileManager = fileManager; + saveWorkflowAction = new SaveWorkflowAction(editManager, fileManager); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_S, getDefaultToolkit().getMenuShortcutKeyMask() + | SHIFT_DOWN_MASK)); + putValue(MNEMONIC_KEY, VK_A); + + fileManager.addObserver(fileManagerObserver); + updateEnabled(); + } + + public void updateEnabled() { + setEnabled(!(fileManager.getOpenDataflows().isEmpty())); + } + + @Override + public void actionPerformed(ActionEvent ev) { + Component parentComponent = null; + if (ev.getSource() instanceof Component) + parentComponent = (Component) ev.getSource(); + saveAllDataflows(parentComponent); + } + + public void saveAllDataflows(Component parentComponent) { + // Save in reverse so we save nested workflows first + List<WorkflowBundle> workflowBundles = fileManager.getOpenDataflows(); + Collections.reverse(workflowBundles); + + for (WorkflowBundle workflowBundle : workflowBundles) + if (!saveWorkflowAction.saveDataflow(parentComponent, + workflowBundle)) + break; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAction.java new file mode 100644 index 0000000..9776550 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAction.java
@@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_S; +import static javax.swing.JOptionPane.NO_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.saveIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent; +import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException; +import net.sf.taverna.t2.workbench.file.exceptions.SaveException; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +@SuppressWarnings("serial") +public class SaveWorkflowAction extends AbstractAction { + private static Logger logger = Logger.getLogger(SaveWorkflowAction.class); + private static final String SAVE_WORKFLOW = "Save workflow"; + + private final SaveWorkflowAsAction saveWorkflowAsAction; + private EditManagerObserver editManagerObserver = new EditManagerObserver(); + private FileManager fileManager; + private FileManagerObserver fileManagerObserver = new FileManagerObserver(); + + public SaveWorkflowAction(EditManager editManager, FileManager fileManager) { + super(SAVE_WORKFLOW, saveIcon); + this.fileManager = fileManager; + saveWorkflowAsAction = new SaveWorkflowAsAction(fileManager); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_S, getDefaultToolkit().getMenuShortcutKeyMask())); + putValue(MNEMONIC_KEY, VK_S); + editManager.addObserver(editManagerObserver); + fileManager.addObserver(fileManagerObserver); + updateEnabledStatus(fileManager.getCurrentDataflow()); + } + + @Override + public void actionPerformed(ActionEvent ev) { + Component parentComponent = null; + if (ev.getSource() instanceof Component) + parentComponent = (Component) ev.getSource(); + saveCurrentDataflow(parentComponent); + } + + public boolean saveCurrentDataflow(Component parentComponent) { + WorkflowBundle workflowBundle = fileManager.getCurrentDataflow(); + return saveDataflow(parentComponent, workflowBundle); + } + + public boolean saveDataflow(Component parentComponent, + WorkflowBundle workflowBundle) { + if (!fileManager.canSaveWithoutDestination(workflowBundle)) + return saveWorkflowAsAction.saveDataflow(parentComponent, + workflowBundle); + + try { + try { + fileManager.saveDataflow(workflowBundle, true); + Object workflowBundleSource = fileManager + .getDataflowSource(workflowBundle); + logger.info("Saved workflow " + workflowBundle + " to " + + workflowBundleSource); + return true; + } catch (OverwriteException ex) { + Object workflowBundleSource = fileManager + .getDataflowSource(workflowBundle); + logger.info("Workflow was changed on source: " + + workflowBundleSource); + fileManager.setCurrentDataflow(workflowBundle); + String msg = "Workflow destination " + workflowBundleSource + + " has been changed from elsewhere, " + + "are you sure you want to overwrite?"; + int ret = showConfirmDialog(parentComponent, msg, + "Workflow changed", YES_NO_CANCEL_OPTION); + if (ret == YES_OPTION) { + fileManager.saveDataflow(workflowBundle, false); + logger.info("Saved workflow " + workflowBundle + + " by overwriting " + workflowBundleSource); + return true; + } else if (ret == NO_OPTION) { + // Pop up Save As instead to choose another name + return saveWorkflowAsAction.saveDataflow(parentComponent, + workflowBundle); + } else { + logger.info("Aborted overwrite of " + workflowBundleSource); + return false; + } + } + } catch (SaveException ex) { + logger.warn("Could not save workflow " + workflowBundle, ex); + showMessageDialog(parentComponent, "Could not save workflow: \n\n" + + ex.getMessage(), "Warning", WARNING_MESSAGE); + return false; + } catch (RuntimeException ex) { + logger.warn("Could not save workflow " + workflowBundle, ex); + showMessageDialog(parentComponent, "Could not save workflow: \n\n" + + ex.getMessage(), "Warning", WARNING_MESSAGE); + return false; + } + } + + protected void updateEnabledStatus(WorkflowBundle workflowBundle) { + setEnabled(workflowBundle != null + && fileManager.isDataflowChanged(workflowBundle)); + } + + private final class EditManagerObserver implements + Observer<EditManagerEvent> { + @Override + public void notify(Observable<EditManagerEvent> sender, + EditManagerEvent message) throws Exception { + if (message instanceof AbstractDataflowEditEvent) { + WorkflowBundle workflowBundle = ((AbstractDataflowEditEvent) message) + .getDataFlow(); + if (workflowBundle == fileManager.getCurrentDataflow()) + updateEnabledStatus(workflowBundle); + } + } + } + + private final class FileManagerObserver implements + Observer<FileManagerEvent> { + @Override + public void notify(Observable<FileManagerEvent> sender, + FileManagerEvent message) throws Exception { + if (message instanceof SavedDataflowEvent) + updateEnabledStatus(((SavedDataflowEvent) message) + .getDataflow()); + else if (message instanceof SetCurrentDataflowEvent) + updateEnabledStatus(((SetCurrentDataflowEvent) message) + .getDataflow()); + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAsAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAsAction.java new file mode 100644 index 0000000..1872d5d --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/actions/SaveWorkflowAsAction.java
@@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.actions; + +import static java.awt.event.KeyEvent.VK_F6; +import static java.awt.event.KeyEvent.VK_S; +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.NO_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_CANCEL_OPTION; +import static javax.swing.JOptionPane.YES_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.saveAsIcon; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.net.URL; +import java.util.List; +import java.util.prefs.Preferences; + +import javax.swing.AbstractAction; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent; +import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException; +import net.sf.taverna.t2.workbench.file.exceptions.SaveException; +import net.sf.taverna.t2.workbench.file.impl.FileTypeFileFilter; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; + +@SuppressWarnings("serial") +public class SaveWorkflowAsAction extends AbstractAction { + private static final String SAVE_WORKFLOW_AS = "Save workflow as..."; + private static final String PREF_CURRENT_DIR = "currentDir"; + private static Logger logger = Logger.getLogger(SaveWorkflowAsAction.class); + private FileManager fileManager; + + public SaveWorkflowAsAction(FileManager fileManager) { + super(SAVE_WORKFLOW_AS, saveAsIcon); + this.fileManager = fileManager; + fileManager.addObserver(new FileManagerObserver()); + putValue(ACCELERATOR_KEY, getKeyStroke(VK_F6, 0)); + putValue(MNEMONIC_KEY, VK_S); + } + + @Override + public void actionPerformed(ActionEvent e) { + Component parentComponent = null; + if (e.getSource() instanceof Component) + parentComponent = (Component) e.getSource(); + WorkflowBundle workflowBundle = fileManager.getCurrentDataflow(); + if (workflowBundle == null) { + showMessageDialog(parentComponent, "No workflow open yet", + "No workflow to save", ERROR_MESSAGE); + return; + } + saveCurrentDataflow(parentComponent); + } + + public boolean saveCurrentDataflow(Component parentComponent) { + WorkflowBundle workflowBundle = fileManager.getCurrentDataflow(); + return saveDataflow(parentComponent, workflowBundle); + } + + private String determineFileName(final WorkflowBundle workflowBundle) { + String result; + Object source = fileManager.getDataflowSource(workflowBundle); + String fileName = null; + if (source instanceof File) + fileName = ((File) source).getName(); + else if (source instanceof URL) + fileName = ((URL) source).getPath(); + + if (fileName != null) { + int lastIndex = fileName.lastIndexOf("."); + if (lastIndex > 0) + fileName = fileName.substring(0, fileName.lastIndexOf(".")); + result = fileName; + } else { + Workflow mainWorkflow = workflowBundle.getMainWorkflow(); + if (mainWorkflow != null) + result = mainWorkflow.getName(); + else + result = workflowBundle.getName(); + } + return result; + } + + public boolean saveDataflow(Component parentComponent, WorkflowBundle workflowBundle) { + fileManager.setCurrentDataflow(workflowBundle); + JFileChooser fileChooser = new JFileChooser(); + Preferences prefs = Preferences.userNodeForPackage(getClass()); + String curDir = prefs + .get(PREF_CURRENT_DIR, System.getProperty("user.home")); + fileChooser.setDialogTitle(SAVE_WORKFLOW_AS); + + fileChooser.resetChoosableFileFilters(); + fileChooser.setAcceptAllFileFilterUsed(false); + + List<FileFilter> fileFilters = fileManager + .getSaveFileFilters(File.class); + if (fileFilters.isEmpty()) { + logger.warn("No file types found for saving workflow " + + workflowBundle); + showMessageDialog(parentComponent, + "No file types found for saving workflow.", "Error", + ERROR_MESSAGE); + return false; + } + for (FileFilter fileFilter : fileFilters) + fileChooser.addChoosableFileFilter(fileFilter); + fileChooser.setFileFilter(fileFilters.get(0)); + fileChooser.setCurrentDirectory(new File(curDir)); + + File possibleName = new File(determineFileName(workflowBundle)); + boolean tryAgain = true; + while (tryAgain) { + tryAgain = false; + fileChooser.setSelectedFile(possibleName); + int returnVal = fileChooser.showSaveDialog(parentComponent); + if (returnVal == APPROVE_OPTION) { + prefs.put(PREF_CURRENT_DIR, fileChooser.getCurrentDirectory() + .toString()); + File file = fileChooser.getSelectedFile(); + FileTypeFileFilter fileFilter = (FileTypeFileFilter) fileChooser + .getFileFilter(); + FileType fileType = fileFilter.getFileType(); + String extension = "." + fileType.getExtension(); + if (!file.getName().toLowerCase().endsWith(extension)) { + String newName = file.getName() + extension; + file = new File(file.getParentFile(), newName); + } + + // TODO: Open in separate thread to avoid hanging UI + try { + try { + fileManager.saveDataflow(workflowBundle, fileType, + file, true); + logger.info("Saved workflow " + workflowBundle + " to " + + file); + return true; + } catch (OverwriteException ex) { + logger.info("File already exists: " + file); + String msg = "Are you sure you want to overwrite existing file " + + file + "?"; + int ret = showConfirmDialog(parentComponent, msg, + "File already exists", YES_NO_CANCEL_OPTION); + if (ret == YES_OPTION) { + fileManager.saveDataflow(workflowBundle, fileType, + file, false); + logger.info("Saved workflow " + workflowBundle + + " by overwriting " + file); + return true; + } else if (ret == NO_OPTION) { + tryAgain = true; + continue; + } else { + logger.info("Aborted overwrite of " + file); + return false; + } + } + } catch (SaveException ex) { + logger.warn("Could not save workflow to " + file, ex); + showMessageDialog(parentComponent, + "Could not save workflow to " + file + ": \n\n" + + ex.getMessage(), "Warning", + WARNING_MESSAGE); + return false; + } + } + } + return false; + } + + protected void updateEnabledStatus(WorkflowBundle workflowBundle) { + setEnabled(workflowBundle != null); + } + + private final class FileManagerObserver implements Observer<FileManagerEvent> { + @Override + public void notify(Observable<FileManagerEvent> sender, + FileManagerEvent message) throws Exception { + if (message instanceof SetCurrentDataflowEvent) + updateEnabledStatus(((SetCurrentDataflowEvent) message) + .getDataflow()); + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java new file mode 100644 index 0000000..6c0be19 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/hooks/CloseWorkflowsOnShutdown.java
@@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (C) 2010 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.hooks; + +import net.sf.taverna.t2.workbench.ShutdownSPI; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.CloseAllWorkflowsAction; + +/** + * Close open workflows (and ask the user if she wants to save changes) on + * shutdown. + * + * @author Stian Soiland-Reyes + */ +public class CloseWorkflowsOnShutdown implements ShutdownSPI { + private CloseAllWorkflowsAction closeAllWorkflowsAction; + + public CloseWorkflowsOnShutdown(EditManager editManager, + FileManager fileManager) { + closeAllWorkflowsAction = new CloseAllWorkflowsAction(editManager, + fileManager); + } + + @Override + public int positionHint() { + /* + * Quite early, we don't want to do various clean-up in case the user + * clicks Cancel + */ + return 50; + } + + @Override + public boolean shutdown() { + return closeAllWorkflowsAction.closeAllWorkflows(null); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseAllMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseAllMenuAction.java new file mode 100644 index 0000000..e8e5252 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseAllMenuAction.java
@@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_URI; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.CloseAllWorkflowsAction; + +public class FileCloseAllMenuAction extends AbstractMenuAction { + private static final URI FILE_CLOSE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileCloseAll"); + private final EditManager editManager; + private final FileManager fileManager; + + public FileCloseAllMenuAction(EditManager editManager, + FileManager fileManager) { + super(FILE_URI, 39, FILE_CLOSE_URI); + this.editManager = editManager; + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new CloseAllWorkflowsAction(editManager, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseMenuAction.java new file mode 100644 index 0000000..a97219f --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileCloseMenuAction.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_URI; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.CloseWorkflowAction; + +public class FileCloseMenuAction extends AbstractMenuAction { + private static final URI FILE_CLOSE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileClose"); + private final EditManager editManager; + private final FileManager fileManager; + + public FileCloseMenuAction(EditManager editManager, FileManager fileManager) { + super(FILE_URI, 30, FILE_CLOSE_URI); + this.editManager = editManager; + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new CloseWorkflowAction(editManager, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileNewMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileNewMenuAction.java new file mode 100644 index 0000000..3a48e0d --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileNewMenuAction.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.NewWorkflowAction; + +public class FileNewMenuAction extends AbstractMenuAction { + private static final URI FILE_NEW_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileNew"); + private final FileManager fileManager; + + public FileNewMenuAction(FileManager fileManager) { + super(FILE_OPEN_SECTION_URI, 10, FILE_NEW_URI); + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new NewWorkflowAction(fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenFromURLMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenFromURLMenuAction.java new file mode 100644 index 0000000..9af1d6b --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenFromURLMenuAction.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowFromURLAction; + +public class FileOpenFromURLMenuAction extends AbstractMenuAction { + + private static final URI FILE_OPEN_FROM_URL_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileOpenURL"); + private final FileManager fileManager; + + public FileOpenFromURLMenuAction(FileManager fileManager) { + super(FILE_OPEN_SECTION_URI, 30, FILE_OPEN_FROM_URL_URI); + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new OpenWorkflowFromURLAction(null, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuAction.java new file mode 100644 index 0000000..4ee4e39 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuAction.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowAction; + +public class FileOpenMenuAction extends AbstractMenuAction { + private static final URI FILE_OPEN_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileOpen"); + private final FileManager fileManager; + + public FileOpenMenuAction(FileManager fileManager) { + super(FILE_OPEN_SECTION_URI, 20, FILE_OPEN_URI); + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new OpenWorkflowAction(fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuSection.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuSection.java new file mode 100644 index 0000000..46ef476 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenMenuSection.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +public class FileOpenMenuSection extends AbstractMenuSection { + public static final URI FILE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#file"); + public static final URI FILE_OPEN_SECTION_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileOpenSection"); + + public FileOpenMenuSection() { + super(FILE_URI, 20, FILE_OPEN_SECTION_URI); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenRecentMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenRecentMenuAction.java new file mode 100644 index 0000000..76ef759 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileOpenRecentMenuAction.java
@@ -0,0 +1,418 @@ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static java.awt.event.KeyEvent.VK_0; +import static java.awt.event.KeyEvent.VK_R; +import static javax.swing.Action.MNEMONIC_KEY; +import static javax.swing.Action.NAME; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.SwingUtilities.invokeLater; +import static net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection.FILE_OPEN_SECTION_URI; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.ui.menu.AbstractMenuCustom; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.FileType; +import net.sf.taverna.t2.workbench.file.events.AbstractDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.file.events.OpenedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.SavedDataflowEvent; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; + +import org.apache.log4j.Logger; +import org.jdom.Document; +import org.jdom.JDOMException; +import org.jdom.input.SAXBuilder; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +public class FileOpenRecentMenuAction extends AbstractMenuCustom implements + Observer<FileManagerEvent> { + public static final URI RECENT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileOpenRecent"); + private static final String CONF = "conf"; + private static Logger logger = Logger + .getLogger(FileOpenRecentMenuAction.class); + private static final String RECENT_WORKFLOWS_XML = "recentWorkflows.xml"; + private static final int MAX_ITEMS = 10; + + private FileManager fileManager; + private ApplicationConfiguration applicationConfiguration; + private JMenu menu; + private List<Recent> recents = new ArrayList<>(); + private Thread loadRecentThread; + + public FileOpenRecentMenuAction(FileManager fileManager) { + super(FILE_OPEN_SECTION_URI, 30, RECENT_URI); + this.fileManager = fileManager; + fileManager.addObserver(this); + } + + @Override + public void notify(Observable<FileManagerEvent> sender, + FileManagerEvent message) throws Exception { + FileManager fileManager = (FileManager) sender; + if (message instanceof OpenedDataflowEvent + || message instanceof SavedDataflowEvent) { + AbstractDataflowEvent dataflowEvent = (AbstractDataflowEvent) message; + WorkflowBundle dataflow = dataflowEvent.getDataflow(); + Object dataflowSource = fileManager.getDataflowSource(dataflow); + FileType dataflowType = fileManager.getDataflowType(dataflow); + addRecent(dataflowSource, dataflowType); + } + if (message instanceof ClosedDataflowEvent) + // Make sure enabled/disabled status is correct + updateRecentMenu(); + } + + public void updateRecentMenu() { + invokeLater(new Runnable() { + @Override + public void run() { + updateRecentMenuGUI(); + } + }); + saveRecent(); + } + + protected void addRecent(Object dataflowSource, FileType dataflowType) { + if (dataflowSource == null) + return; + if (!(dataflowSource instanceof Serializable)) { + logger.warn("Can't serialize workflow source for 'Recent workflows': " + + dataflowSource); + return; + } + synchronized (recents) { + Recent recent = new Recent((Serializable) dataflowSource, dataflowType); + if (recents.contains(recent)) + recents.remove(recent); + recents.add(0, recent); // Add to front + } + updateRecentMenu(); + } + + @Override + protected Component createCustomComponent() { + action = new DummyAction("Recent workflows"); + action.putValue(MNEMONIC_KEY, VK_R); + menu = new JMenu(action); + // Disabled until we have loaded the recent workflows + menu.setEnabled(false); + loadRecentThread = new Thread("Loading recent workflow menu") { + // Avoid hanging GUI initialization while deserialising + @Override + public void run() { + loadRecent(); + updateRecentMenu(); + } + }; + loadRecentThread.start(); + return menu; + } + + protected synchronized void loadRecent() { + File confDir = new File(applicationConfiguration.getApplicationHomeDir(), CONF); + confDir.mkdir(); + File recentFile = new File(confDir, RECENT_WORKFLOWS_XML); + if (!recentFile.isFile()) + return; + try { + loadRecent(recentFile); + } catch (JDOMException|IOException e) { + logger.warn("Could not read recent workflows from file " + + recentFile, e); + } + } + + private void loadRecent(File recentFile) throws FileNotFoundException, + IOException, JDOMException { + SAXBuilder builder = new SAXBuilder(); + @SuppressWarnings("unused") + Document document; + try (InputStream fileInputStream = new BufferedInputStream( + new FileInputStream(recentFile))) { + document = builder.build(fileInputStream); + } + synchronized (recents) { + recents.clear(); + //RecentDeserializer deserialiser = new RecentDeserializer(); + try { + // recents.addAll(deserialiser.deserializeRecent(document + // .getRootElement())); + } catch (Exception e) { + logger.warn("Could not read recent workflows from file " + + recentFile, e); + } + } + } + + protected synchronized void saveRecent() { + File confDir = new File(applicationConfiguration.getApplicationHomeDir(), CONF); + confDir.mkdir(); + File recentFile = new File(confDir, RECENT_WORKFLOWS_XML); + + try { + saveRecent(recentFile); +// } catch (JDOMException e) { +// logger.warn("Could not generate XML for recent workflows to file " +// + recentFile, e); + } catch (IOException e) { + logger.warn("Could not write recent workflows to file " + + recentFile, e); + } + } + + private void saveRecent(File recentFile) throws FileNotFoundException, + IOException { + // RecentSerializer serializer = new RecentSerializer(); + // XMLOutputter outputter = new XMLOutputter(); + + // Element serializedRecent; + synchronized (recents) { + if (recents.size() > MAX_ITEMS) + // Remove excess entries + recents.subList(MAX_ITEMS, recents.size()).clear(); + // serializedRecent = serializer.serializeRecent(recents); + } + try (OutputStream outputStream = new BufferedOutputStream( + new FileOutputStream(recentFile))) { + // outputter.output(serializedRecent, outputStream); + } + } + + protected void updateRecentMenuGUI() { + int items = 0; + menu.removeAll(); + synchronized (recents) { + for (Recent recent : recents) { + if (++items >= MAX_ITEMS) + break; + OpenRecentAction openRecentAction = new OpenRecentAction( + recent, fileManager); + if (fileManager.getDataflowBySource(recent.getDataflowSource()) != null) + openRecentAction.setEnabled(false); + // else setEnabled(true) + JMenuItem menuItem = new JMenuItem(openRecentAction); + if (items < 10) { + openRecentAction.putValue(NAME, items + " " + + openRecentAction.getValue(NAME)); + menuItem.setMnemonic(VK_0 + items); + } + menu.add(menuItem); + } + } + menu.setEnabled(items > 0); + menu.revalidate(); + } + + @SuppressWarnings("serial") + public static class OpenRecentAction extends AbstractAction implements + Runnable { + private final Recent recent; + private Component component = null; + private final FileManager fileManager; + + public OpenRecentAction(Recent recent, FileManager fileManager) { + this.recent = recent; + this.fileManager = fileManager; + Serializable source = recent.getDataflowSource(); + String name; + if (source instanceof File) + name = ((File) source).getAbsolutePath(); + else + name = source.toString(); + this.putValue(NAME, name); + this.putValue(SHORT_DESCRIPTION, "Open the workflow " + name); + } + + @Override + public void actionPerformed(ActionEvent e) { + component = null; + if (e.getSource() instanceof Component) + component = (Component) e.getSource(); + setEnabled(false); + new Thread(this, "Opening workflow from " + + recent.getDataflowSource()).start(); + } + + /** + * Opening workflow in separate thread + */ + @Override + public void run() { + final Serializable source = recent.getDataflowSource(); + try { + fileManager.openDataflow(recent.makefileType(), source); + } catch (OpenException ex) { + logger.warn("Failed to open the workflow from " + source + + " \n", ex); + showMessageDialog(component, + "Failed to open the workflow from url " + source + + " \n" + ex.getMessage(), "Error!", + ERROR_MESSAGE); + } finally { + setEnabled(true); + } + } + } + + @SuppressWarnings("serial") + public static class Recent implements Serializable { + private final class RecentFileType extends FileType { + @Override + public String getMimeType() { + return mimeType; + } + + @Override + public String getExtension() { + return extension; + } + + @Override + public String getDescription() { + return "File type " + extension + " " + mimeType; + } + } + + private Serializable dataflowSource; + private String mimeType; + private String extension; + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public Recent() { + } + + public FileType makefileType() { + if (mimeType == null && extension == null) + return null; + return new RecentFileType(); + } + + public Recent(Serializable dataflowSource, FileType dataflowType) { + setDataflowSource(dataflowSource); + if (dataflowType != null) { + setMimeType(dataflowType.getMimeType()); + setExtension(dataflowType.getExtension()); + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime + * result + + ((dataflowSource == null) ? 0 : dataflowSource.hashCode()); + result = prime * result + + ((extension == null) ? 0 : extension.hashCode()); + result = prime * result + + ((mimeType == null) ? 0 : mimeType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Recent)) + return false; + Recent other = (Recent) obj; + + if (dataflowSource == null) { + if (other.dataflowSource != null) + return false; + } else if (!dataflowSource.equals(other.dataflowSource)) + return false; + + if (extension == null) { + if (other.extension != null) + return false; + } else if (!extension.equals(other.extension)) + return false; + + if (mimeType == null) { + if (other.mimeType != null) + return false; + } else if (!mimeType.equals(other.mimeType)) + return false; + + return true; + } + + public Serializable getDataflowSource() { + return dataflowSource; + } + + public void setDataflowSource(Serializable dataflowSource) { + this.dataflowSource = dataflowSource; + } + + @Override + public String toString() { + return getDataflowSource() + ""; + } + } + + // TODO find new serialization +// protected static class RecentDeserializer extends AbstractXMLDeserializer { +// public Collection<Recent> deserializeRecent(Element el) { +// return (Collection<Recent>) super.createBean(el, getClass() +// .getClassLoader()); +// } +// } +// +// protected static class RecentSerializer extends AbstractXMLSerializer { +// public Element serializeRecent(List<Recent> x) throws JDOMException, +// IOException { +// Element beanAsElement = super.beanAsElement(x); +// return beanAsElement; +// } +// } + + public void setApplicationConfiguration( + ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAllMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAllMenuAction.java new file mode 100644 index 0000000..86edacb --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAllMenuAction.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection.FILE_SAVE_SECTION_URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.SaveAllWorkflowsAction; + +public class FileSaveAllMenuAction extends AbstractMenuAction { + private final EditManager editManager; + private final FileManager fileManager; + + public FileSaveAllMenuAction(EditManager editManager, + FileManager fileManager) { + super(FILE_SAVE_SECTION_URI, 30); + this.editManager = editManager; + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new SaveAllWorkflowsAction(editManager, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAsMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAsMenuAction.java new file mode 100644 index 0000000..77917c9 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveAsMenuAction.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection.FILE_SAVE_SECTION_URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.SaveWorkflowAsAction; + +public class FileSaveAsMenuAction extends AbstractMenuAction { + private final FileManager fileManager; + + public FileSaveAsMenuAction(FileManager fileManager) { + super(FILE_SAVE_SECTION_URI, 20); + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new SaveWorkflowAsAction(fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuAction.java new file mode 100644 index 0000000..eeaecb3 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuAction.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection.FILE_SAVE_SECTION_URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.SaveWorkflowAction; + +public class FileSaveMenuAction extends AbstractMenuAction { + private final EditManager editManager; + private final FileManager fileManager; + + public FileSaveMenuAction(EditManager editManager, FileManager fileManager) { + super(FILE_SAVE_SECTION_URI, 10); + this.editManager = editManager; + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new SaveWorkflowAction(editManager, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuSection.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuSection.java new file mode 100644 index 0000000..a75a855 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/FileSaveMenuSection.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +public class FileSaveMenuSection extends AbstractMenuSection { + public static final URI FILE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#file"); + public static final URI FILE_SAVE_SECTION_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileSaveSection"); + + public FileSaveMenuSection() { + super(FILE_URI, 40, FILE_SAVE_SECTION_URI); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/WorkflowsMenu.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/WorkflowsMenu.java new file mode 100644 index 0000000..e056572 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/menu/WorkflowsMenu.java
@@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.menu; + +import static java.awt.event.KeyEvent.VK_0; +import static java.awt.event.KeyEvent.VK_W; +import static javax.swing.Action.MNEMONIC_KEY; +import static javax.swing.SwingUtilities.invokeLater; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; + +import java.awt.Component; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.ButtonGroup; +import javax.swing.JMenu; +import javax.swing.JRadioButtonMenuItem; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.ui.menu.AbstractMenuCustom; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.events.AbstractDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; + +public class WorkflowsMenu extends AbstractMenuCustom { + private EditManagerObserver editManagerObserver = new EditManagerObserver(); + private FileManager fileManager; + private FileManagerObserver fileManagerObserver = new FileManagerObserver(); + + private JMenu workflowsMenu; + + public WorkflowsMenu(EditManager editManager, FileManager fileManager) { + super(DEFAULT_MENU_BAR, 900); + this.fileManager = fileManager; + fileManager.addObserver(fileManagerObserver); + editManager.addObserver(editManagerObserver); + } + + @Override + protected Component createCustomComponent() { + DummyAction action = new DummyAction("Workflows"); + action.putValue(MNEMONIC_KEY, VK_W); + + workflowsMenu = new JMenu(action); + + updateWorkflowsMenu(); + return workflowsMenu; + } + + public void updateWorkflowsMenu() { + invokeLater(new Runnable() { + @Override + public void run() { + updateWorkflowsMenuUI(); + } + }); + } + + protected void updateWorkflowsMenuUI() { + workflowsMenu.setEnabled(false); + workflowsMenu.removeAll(); + ButtonGroup workflowsGroup = new ButtonGroup(); + + int i = 0; + WorkflowBundle currentDataflow = fileManager.getCurrentDataflow(); + for (WorkflowBundle workflowBundle : fileManager.getOpenDataflows()) { + String name = fileManager.getDataflowName(workflowBundle); + if (fileManager.isDataflowChanged(workflowBundle)) + name = "*" + name; + // A counter + name = ++i + " " + name; + + SwitchWorkflowAction switchWorkflowAction = new SwitchWorkflowAction( + name, workflowBundle); + if (i < 10) + switchWorkflowAction.putValue(MNEMONIC_KEY, new Integer(VK_0 + + i)); + + JRadioButtonMenuItem switchWorkflowMenuItem = new JRadioButtonMenuItem( + switchWorkflowAction); + workflowsGroup.add(switchWorkflowMenuItem); + if (workflowBundle.equals(currentDataflow)) + switchWorkflowMenuItem.setSelected(true); + workflowsMenu.add(switchWorkflowMenuItem); + } + if (i == 0) + workflowsMenu.add(new NoWorkflowsOpen()); + workflowsMenu.setEnabled(true); + workflowsMenu.revalidate(); + } + + private final class EditManagerObserver implements + Observer<EditManagerEvent> { + @Override + public void notify(Observable<EditManagerEvent> sender, + EditManagerEvent message) throws Exception { + if (message instanceof AbstractDataflowEditEvent) + updateWorkflowsMenu(); + } + } + + private final class FileManagerObserver implements + Observer<FileManagerEvent> { + @Override + public void notify(Observable<FileManagerEvent> sender, + FileManagerEvent message) throws Exception { + if (message instanceof AbstractDataflowEvent) + updateWorkflowsMenu(); + // TODO: Don't rebuild whole menu + } + } + + @SuppressWarnings("serial") + private final class NoWorkflowsOpen extends AbstractAction { + private NoWorkflowsOpen() { + super("No workflows open"); + setEnabled(false); + } + + @Override + public void actionPerformed(ActionEvent e) { + // No-op + } + } + + @SuppressWarnings("serial") + private final class SwitchWorkflowAction extends AbstractAction { + private final WorkflowBundle workflowBundle; + + private SwitchWorkflowAction(String name, WorkflowBundle workflowBundle) { + super(name); + this.workflowBundle = workflowBundle; + } + + @Override + public void actionPerformed(ActionEvent e) { + fileManager.setCurrentDataflow(workflowBundle); + } + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/CloseToolbarAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/CloseToolbarAction.java new file mode 100644 index 0000000..68ef3f9 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/CloseToolbarAction.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.toolbar; + +import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.CloseWorkflowAction; + +/** + * Action to close the current workflow. + * + * @author Alex Nenadic + */ +public class CloseToolbarAction extends AbstractMenuAction { + private static final URI FILE_CLOSE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarClose"); + private final EditManager editManager; + private final FileManager fileManager; + + public CloseToolbarAction(EditManager editManager, FileManager fileManager) { + super(FILE_TOOLBAR_SECTION, 30, FILE_CLOSE_URI); + this.editManager = editManager; + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new CloseWorkflowAction(editManager, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/FileToolbarMenuSection.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/FileToolbarMenuSection.java new file mode 100644 index 0000000..257d590 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/FileToolbarMenuSection.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.toolbar; + +import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +public class FileToolbarMenuSection extends AbstractMenuSection { + public static final URI FILE_TOOLBAR_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarSection"); + + public FileToolbarMenuSection() { + super(DEFAULT_TOOL_BAR, 10, FILE_TOOLBAR_SECTION); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/NewToolbarAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/NewToolbarAction.java new file mode 100644 index 0000000..2c8e922 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/NewToolbarAction.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.toolbar; + +import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.NewWorkflowAction; + +public class NewToolbarAction extends AbstractMenuAction { + private static final URI FILE_NEW_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarNew"); + private final FileManager fileManager; + + public NewToolbarAction(FileManager fileManager) { + super(FILE_TOOLBAR_SECTION, 10, FILE_NEW_URI); + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new NewWorkflowAction(fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenToolbarAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenToolbarAction.java new file mode 100644 index 0000000..ae99509 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenToolbarAction.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.toolbar; + +import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowAction; + +public class OpenToolbarAction extends AbstractMenuAction { + private static final URI FILE_OPEN_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarOpen"); + private final FileManager fileManager; + + public OpenToolbarAction(FileManager fileManager) { + super(FILE_TOOLBAR_SECTION, 20, FILE_OPEN_URI); + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new OpenWorkflowAction(fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenWorkflowFromURLToolbarAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenWorkflowFromURLToolbarAction.java new file mode 100644 index 0000000..2554063 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/OpenWorkflowFromURLToolbarAction.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.toolbar; + +import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.OpenWorkflowFromURLAction; + +public class OpenWorkflowFromURLToolbarAction extends AbstractMenuAction { + private static final URI FILE_OPEN_FROM_URL_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarOpenFromURL"); + private final FileManager fileManager; + + public OpenWorkflowFromURLToolbarAction(FileManager fileManager) { + super(FILE_TOOLBAR_SECTION, 25, FILE_OPEN_FROM_URL_URI); + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new OpenWorkflowFromURLAction(null, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/SaveToolbarAction.java b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/SaveToolbarAction.java new file mode 100644 index 0000000..53ba720 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/java/net/sf/taverna/t2/workbench/file/impl/toolbar/SaveToolbarAction.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl.toolbar; + +import static net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection.FILE_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.actions.SaveWorkflowAction; + +public class SaveToolbarAction extends AbstractMenuAction { + private static final URI FILE_SAVE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#fileToolbarSave"); + private final EditManager editManager; + private final FileManager fileManager; + + public SaveToolbarAction(EditManager editManager, FileManager fileManager) { + super(FILE_TOOLBAR_SECTION, 40, FILE_SAVE_URI); + this.editManager = editManager; + this.fileManager = fileManager; + } + + @Override + protected Action createAction() { + return new SaveWorkflowAction(editManager, fileManager); + } +}
diff --git a/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..100915c --- /dev/null +++ b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,20 @@ +net.sf.taverna.t2.workbench.file.impl.menu.FileCloseMenuAction +net.sf.taverna.t2.workbench.file.impl.menu.FileNewMenuAction +net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuAction +net.sf.taverna.t2.workbench.file.impl.menu.FileOpenFromURLMenuAction +net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection +net.sf.taverna.t2.workbench.file.impl.menu.FileOpenRecentMenuAction +net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection +net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuAction +net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAllMenuAction +net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAsMenuAction + +net.sf.taverna.t2.workbench.file.impl.menu.WorkflowsMenu +net.sf.taverna.t2.workbench.file.impl.menu.FileCloseAllMenuAction + +net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection +net.sf.taverna.t2.workbench.file.impl.toolbar.NewToolbarAction +net.sf.taverna.t2.workbench.file.impl.toolbar.OpenToolbarAction +net.sf.taverna.t2.workbench.file.impl.toolbar.OpenWorkflowFromURLToolbarAction +net.sf.taverna.t2.workbench.file.impl.toolbar.SaveToolbarAction +net.sf.taverna.t2.workbench.file.impl.toolbar.CloseToolbarAction
diff --git a/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI new file mode 100644 index 0000000..cc53d36 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ShutdownSPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.file.impl.hooks.CloseWorkflowsOnShutdown
diff --git a/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler new file mode 100644 index 0000000..cfd1c7a --- /dev/null +++ b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler
@@ -0,0 +1,2 @@ +net.sf.taverna.t2.workbench.file.impl.T2DataflowOpener +net.sf.taverna.t2.workbench.file.impl.DataflowFromDataflowPersistenceHandler \ No newline at end of file
diff --git a/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.FileManager b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.FileManager new file mode 100644 index 0000000..656feeb --- /dev/null +++ b/taverna-workbench-file-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.file.FileManager
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.file.impl.FileManagerImpl \ No newline at end of file
diff --git a/taverna-workbench-file-impl/src/main/resources/META-INF/spring/file-impl-context-osgi.xml b/taverna-workbench-file-impl/src/main/resources/META-INF/spring/file-impl-context-osgi.xml new file mode 100644 index 0000000..7c6e290 --- /dev/null +++ b/taverna-workbench-file-impl/src/main/resources/META-INF/spring/file-impl-context-osgi.xml
@@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="FileCloseMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.close" /> + </service-properties> + </service> + <service ref="FileNewMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.new" /> + </service-properties> + </service> + <service ref="FileOpenMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.open" /> + </service-properties> + </service> + <service ref="FileOpenFromURLMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.open.url" /> + </service-properties> + </service> + <service ref="FileOpenMenuSection" auto-export="interfaces" /> + <service ref="FileOpenRecentMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.open.recent" /> + </service-properties> + </service> + <service ref="FileSaveMenuSection" auto-export="interfaces" /> + <service ref="FileSaveMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.save" /> + </service-properties> + </service> + <service ref="FileSaveAllMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.save.all" /> + </service-properties> + </service> + <service ref="FileSaveAsMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.save.as" /> + </service-properties> + </service> + <service ref="WorkflowsMenu" auto-export="interfaces" /> + <service ref="FileCloseAllMenuAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="file.close.all" /> + </service-properties> + </service> + <service ref="FileToolbarMenuSection" auto-export="interfaces" /> + <service ref="NewToolbarAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="toolbar.new" /> + </service-properties> + </service> + <service ref="OpenToolbarAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="toolbar.open" /> + </service-properties> + </service> + <service ref="OpenWorkflowFromURLToolbarAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="toolbar.open.url" /> + </service-properties> + </service> + <service ref="SaveToolbarAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="toolbar.save" /> + </service-properties> + </service> + <service ref="CloseToolbarAction" auto-export="interfaces"> + <service-properties> + <beans:entry key="menu.action" value="toolbar.close" /> + </service-properties> + </service> + + <service ref="T2DataflowOpener" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" /> + + <service ref="WorkflowBundleOpener" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" /> + <service ref="WorkflowBundleSaver" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" /> + + <service ref="CloseWorkflowsOnShutdown" interface="net.sf.taverna.t2.workbench.ShutdownSPI" /> + + <service ref="FileManagerImpl" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" /> + <reference id="workflowBundleIO" interface="uk.org.taverna.scufl2.api.io.WorkflowBundleIO" /> + + <list id="dataflowPersistenceHandlers" interface="net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler" cardinality="0..N"> + <listener ref="DataflowPersistenceHandlerRegistry" bind-method="update" unbind-method="update" /> + </list> +</beans:beans>
diff --git a/taverna-workbench-file-impl/src/main/resources/META-INF/spring/file-impl-context.xml b/taverna-workbench-file-impl/src/main/resources/META-INF/spring/file-impl-context.xml new file mode 100644 index 0000000..493df5f --- /dev/null +++ b/taverna-workbench-file-impl/src/main/resources/META-INF/spring/file-impl-context.xml
@@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="FileCloseMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileCloseMenuAction"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileNewMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileNewMenuAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileOpenMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileOpenFromURLMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenFromURLMenuAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileOpenMenuSection" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenMenuSection" /> + <bean id="FileOpenRecentMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileOpenRecentMenuAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + <property name="applicationConfiguration" ref="applicationConfiguration"/> + </bean> + <bean id="FileSaveMenuSection" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuSection" /> + <bean id="FileSaveMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveMenuAction"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileSaveAllMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAllMenuAction"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileSaveAsMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileSaveAsMenuAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="WorkflowsMenu" class="net.sf.taverna.t2.workbench.file.impl.menu.WorkflowsMenu"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileCloseAllMenuAction" class="net.sf.taverna.t2.workbench.file.impl.menu.FileCloseAllMenuAction"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="FileToolbarMenuSection" class="net.sf.taverna.t2.workbench.file.impl.toolbar.FileToolbarMenuSection" /> + <bean id="NewToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.NewToolbarAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="OpenToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.OpenToolbarAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="OpenWorkflowFromURLToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.OpenWorkflowFromURLToolbarAction"> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="SaveToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.SaveToolbarAction"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + <bean id="CloseToolbarAction" class="net.sf.taverna.t2.workbench.file.impl.toolbar.CloseToolbarAction"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + + <bean id="T2DataflowOpener" class="net.sf.taverna.t2.workbench.file.impl.T2DataflowOpener"> + <property name="workflowBundleIO" ref="workflowBundleIO"/> + </bean> + + <bean id="WorkflowBundleOpener" class="net.sf.taverna.t2.workbench.file.impl.WorkflowBundleOpener"> + <property name="workflowBundleIO" ref="workflowBundleIO"/> + </bean> + <bean id="WorkflowBundleSaver" class="net.sf.taverna.t2.workbench.file.impl.WorkflowBundleSaver"> + <property name="workflowBundleIO" ref="workflowBundleIO"/> + </bean> + + <bean id="CloseWorkflowsOnShutdown" class="net.sf.taverna.t2.workbench.file.impl.hooks.CloseWorkflowsOnShutdown"> + <constructor-arg ref="editManager" /> + <constructor-arg> + <ref local="FileManagerImpl" /> + </constructor-arg> + </bean> + + <bean id="FileManagerImpl" class="net.sf.taverna.t2.workbench.file.impl.FileManagerImpl"> + <constructor-arg name="editManager" ref="editManager" /> + <property name="dataflowPersistenceHandlerRegistry"> + <ref local="DataflowPersistenceHandlerRegistry"/> + </property> + </bean> + + <bean id="DataflowPersistenceHandlerRegistry" class="net.sf.taverna.t2.workbench.file.impl.DataflowPersistenceHandlerRegistry"> + <property name="dataflowPersistenceHandlers" ref="dataflowPersistenceHandlers" /> + </bean> + + +</beans>
diff --git a/taverna-workbench-file-impl/src/test/java/net/sf/taverna/t2/workbench/file/impl/FileManagerTest.java b/taverna-workbench-file-impl/src/test/java/net/sf/taverna/t2/workbench/file/impl/FileManagerTest.java new file mode 100644 index 0000000..691b278 --- /dev/null +++ b/taverna-workbench-file-impl/src/test/java/net/sf/taverna/t2/workbench/file/impl/FileManagerTest.java
@@ -0,0 +1,385 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.file.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl; +import net.sf.taverna.t2.workbench.file.DataflowInfo; +import net.sf.taverna.t2.workbench.file.DataflowPersistenceHandler; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.file.events.SetCurrentDataflowEvent; +import net.sf.taverna.t2.workbench.file.exceptions.OpenException; +import net.sf.taverna.t2.workbench.file.exceptions.OverwriteException; +import net.sf.taverna.t2.workflow.edits.AddProcessorEdit; +import net.sf.taverna.t2.workflow.edits.RenameEdit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.io.WorkflowBundleIO; +import uk.org.taverna.scufl2.api.io.WorkflowBundleReader; +import uk.org.taverna.scufl2.api.io.WorkflowBundleWriter; +import uk.org.taverna.scufl2.rdfxml.RDFXMLReader; +import uk.org.taverna.scufl2.rdfxml.RDFXMLWriter; +import uk.org.taverna.scufl2.translator.t2flow.T2FlowReader; + +public class FileManagerTest { + + private static final WorkflowBundleFileType WF_BUNDLE_FILE_TYPE = new WorkflowBundleFileType(); + private static final T2FlowFileType T2_FLOW_FILE_TYPE = new T2FlowFileType(); + + private static final String DUMMY_WORKFLOW_T2FLOW = "dummy-workflow.t2flow"; + + private FileManagerImpl fileManager; + private EditManager editManager; + + private FileManagerObserver fileManagerObserver= new FileManagerObserver();; + + @Test + public void close() throws Exception { + assertTrue("Non-empty set of open dataflows", fileManager + .getOpenDataflows().isEmpty()); + WorkflowBundle dataflow = openDataflow(); + assertEquals("Unexpected list of open dataflows", Arrays + .asList(dataflow), fileManager.getOpenDataflows()); + fileManager.closeDataflow(dataflow, true); + assertNotSame(dataflow, fileManager.getOpenDataflows().get(0)); + assertTrue("Did not insert empty dataflow after close", fileManager + .getOpenDataflows().get(0).getMainWorkflow().getProcessors().isEmpty()); + } + + @Test + public void openRemovesEmptyDataflow() throws Exception { + WorkflowBundle newDataflow = fileManager.newDataflow(); + assertEquals("Unexpected list of open dataflows", Arrays + .asList(newDataflow), fileManager.getOpenDataflows()); + WorkflowBundle dataflow = openDataflow(); + // Should have removed newDataflow + assertEquals("Unexpected list of open dataflows", Arrays + .asList(dataflow), fileManager.getOpenDataflows()); + } + + @Test + public void isChanged() throws Exception { + WorkflowBundle dataflow = openDataflow(); + assertFalse("Dataflow should not have changed", fileManager + .isDataflowChanged(dataflow)); + + // Do a change + Processor emptyProcessor = new Processor(); + Edit<Workflow> addProcessorEdit = new AddProcessorEdit(dataflow.getMainWorkflow(), + emptyProcessor); + editManager.doDataflowEdit(dataflow, addProcessorEdit); + assertTrue("Dataflow should have changed", fileManager + .isDataflowChanged(dataflow)); + + // Save it with the change + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.deleteOnExit(); + dataflowFile.delete(); + + fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false); + assertFalse("Dataflow should no longer be marked as changed", + fileManager.isDataflowChanged(dataflow)); + } + + @Ignore("Undo support for ischanged not yet implemented") + @Test + public void isChangedWithUndo() throws Exception { + WorkflowBundle dataflow = openDataflow(); + // Do a change + Processor emptyProcessor = new Processor(); + Edit<Workflow> addProcessorEdit = new AddProcessorEdit(dataflow.getMainWorkflow(), + emptyProcessor); + editManager.doDataflowEdit(dataflow, addProcessorEdit); + assertTrue("Dataflow should have changed", fileManager + .isDataflowChanged(dataflow)); + editManager.undoDataflowEdit(dataflow); + assertFalse( + "Dataflow should no longer be marked as changed after undo", + fileManager.isDataflowChanged(dataflow)); + editManager.redoDataflowEdit(dataflow); + assertTrue("Dataflow should have changed after redo before save", + fileManager.isDataflowChanged(dataflow)); + + // Save it with the change + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.deleteOnExit(); + dataflowFile.delete(); + fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false); + assertFalse("Dataflow should no longer be marked as changed", + fileManager.isDataflowChanged(dataflow)); + + editManager.undoDataflowEdit(dataflow); + assertTrue("Dataflow should have changed after undo", fileManager + .isDataflowChanged(dataflow)); + fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false); + editManager.redoDataflowEdit(dataflow); + assertTrue("Dataflow should have changed after redo after save", + fileManager.isDataflowChanged(dataflow)); + } + + @Test + public void isListed() throws Exception { + assertTrue("Non-empty set of open data flows", fileManager + .getOpenDataflows().isEmpty()); + WorkflowBundle dataflow = openDataflow(); + assertEquals("Unexpected list of open dataflows", Arrays + .asList(dataflow), fileManager.getOpenDataflows()); + } + + /** + * Always uses a <strong>new</strong> file manager instead of the instance + * one from {@link FileManager#getInstance()}. + * + * @see #getFileManagerInstance() + * + */ + @Before + public void makeFileManager() { + System.setProperty("java.awt.headless", "true"); + editManager = new EditManagerImpl(); + fileManager = new FileManagerImpl(editManager); + fileManagerObserver = new FileManagerObserver(); + fileManager.addObserver(fileManagerObserver); + WorkflowBundleIO workflowBundleIO = new WorkflowBundleIO(); + workflowBundleIO.setReaders(Arrays.<WorkflowBundleReader>asList(new RDFXMLReader(), new T2FlowReader())); + workflowBundleIO.setWriters(Arrays.<WorkflowBundleWriter>asList(new RDFXMLWriter())); + T2DataflowOpener t2DataflowOpener = new T2DataflowOpener(); + t2DataflowOpener.setWorkflowBundleIO(workflowBundleIO); + WorkflowBundleOpener workflowBundleOpener = new WorkflowBundleOpener(); + workflowBundleOpener.setWorkflowBundleIO(workflowBundleIO); + WorkflowBundleSaver workflowBundleSaver = new WorkflowBundleSaver(); + workflowBundleSaver.setWorkflowBundleIO(workflowBundleIO); + DataflowPersistenceHandlerRegistry dataflowPersistenceHandlerRegistry = new DataflowPersistenceHandlerRegistry(); + dataflowPersistenceHandlerRegistry.setDataflowPersistenceHandlers(Arrays.asList( + new DataflowPersistenceHandler[] {t2DataflowOpener, workflowBundleOpener, workflowBundleSaver})); + dataflowPersistenceHandlerRegistry.updateColletions(); + fileManager.setDataflowPersistenceHandlerRegistry(dataflowPersistenceHandlerRegistry); + } + + @Test + public void open() throws Exception { + assertTrue("ModelMapObserver already contained messages", + fileManagerObserver.messages.isEmpty()); + WorkflowBundle dataflow = openDataflow(); + assertNotNull("Dataflow was not loaded", dataflow); + assertEquals("Loaded dataflow was not set as current dataflow", + dataflow, fileManager.getCurrentDataflow()); + assertFalse("ModelMapObserver did not contain message", + fileManagerObserver.messages.isEmpty()); + assertEquals("ModelMapObserver contained unexpected messages", 2, + fileManagerObserver.messages.size()); + FileManagerEvent event = fileManagerObserver.messages.get(0); + assertTrue(event instanceof SetCurrentDataflowEvent); + assertEquals(dataflow, ((SetCurrentDataflowEvent) event).getDataflow()); + } + + @Test + public void openSilently() throws Exception { + assertTrue("ModelMapObserver already contained messages", + fileManagerObserver.messages.isEmpty()); + URL url = getClass().getResource(DUMMY_WORKFLOW_T2FLOW); + DataflowInfo info = fileManager.openDataflowSilently(T2_FLOW_FILE_TYPE, url); + + WorkflowBundle dataflow = info.getDataflow(); + assertNotNull("Dataflow was not loaded", dataflow); + + assertNotSame("Loaded dataflow was set as current dataflow", + dataflow, fileManager.getCurrentDataflow()); + assertTrue("ModelMapObserver contained unexpected messages", + fileManagerObserver.messages.isEmpty()); + } + + @Test + public void canSaveDataflow() throws Exception { + WorkflowBundle savedDataflow = openDataflow(); + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.deleteOnExit(); + dataflowFile.delete(); + fileManager.saveDataflow(savedDataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true); + assertTrue(fileManager.canSaveWithoutDestination(savedDataflow)); + fileManager.saveDataflow(savedDataflow, true); + fileManager.closeDataflow(savedDataflow, true); + + WorkflowBundle otherFlow = fileManager.openDataflow(WF_BUNDLE_FILE_TYPE, dataflowFile.toURI() + .toURL()); + assertTrue(fileManager.canSaveWithoutDestination(otherFlow)); + } + + @Test + public void save() throws Exception { + WorkflowBundle savedDataflow = openDataflow(); + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.deleteOnExit(); + dataflowFile.delete(); + assertFalse("File should not exist", dataflowFile.isFile()); + fileManager.saveDataflow(savedDataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false); + assertTrue("File should exist", dataflowFile.isFile()); + WorkflowBundle loadedDataflow = fileManager.openDataflow(WF_BUNDLE_FILE_TYPE, dataflowFile.toURI() + .toURL()); + assertNotSame("Dataflow was not reopened", savedDataflow, + loadedDataflow); + assertEquals("Unexpected number of processors in saved dataflow", 1, + savedDataflow.getMainWorkflow().getProcessors().size()); + assertEquals("Unexpected number of processors in loaded dataflow", 1, + loadedDataflow.getMainWorkflow().getProcessors().size()); + + Processor savedProcessor = savedDataflow.getMainWorkflow().getProcessors().first(); + Processor loadedProcessor = loadedDataflow.getMainWorkflow().getProcessors().first(); + assertEquals("Loaded processor had wrong name", savedProcessor + .getName(), loadedProcessor.getName()); + + // TODO convert to scufl2 +// BeanshellActivity savedActivity = (BeanshellActivity) savedProcessor +// .getActivityList().get(0); +// BeanshellActivity loadedActivity = (BeanshellActivity) loadedProcessor +// .getActivityList().get(0); +// String savedScript = savedActivity.getConfiguration().getScript(); +// String loadedScript = loadedActivity.getConfiguration().getScript(); +// assertEquals("Unexpected saved script", +// "String output = input + \"XXX\";", savedScript); +// assertEquals("Loaded script did not matched saved script", savedScript, +// loadedScript); + } + + @Test + public void saveSilent() throws Exception { + assertTrue("ModelMapObserver contained unexpected messages", + fileManagerObserver.messages.isEmpty()); + + URL url = getClass().getResource(DUMMY_WORKFLOW_T2FLOW); + DataflowInfo info = fileManager.openDataflowSilently(T2_FLOW_FILE_TYPE, url); + WorkflowBundle dataflow = info.getDataflow(); + assertTrue("ModelMapObserver contained unexpected messages", + fileManagerObserver.messages.isEmpty()); + + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.deleteOnExit(); + dataflowFile.delete(); + assertFalse("File should not exist", dataflowFile.isFile()); + + fileManager.saveDataflowSilently(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, false); + assertTrue("File should exist", dataflowFile.isFile()); + + assertTrue("ModelMapObserver contained unexpected messages", + fileManagerObserver.messages.isEmpty()); + + } + + @Test + public void saveOverwriteAgain() throws Exception { + WorkflowBundle dataflow = openDataflow(); + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.delete(); + dataflowFile.deleteOnExit(); + // File did NOT exist, should not fail + fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true); + + Processor processor = dataflow.getMainWorkflow().getProcessors().first(); + Edit<Processor> renameEdit = new RenameEdit<Processor>(processor, + processor.getName() + "-changed"); + editManager.doDataflowEdit(dataflow, renameEdit); + + // Last save was OURs, so should *not* fail - even if we now use + // the specific saveDataflow() method + fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true); + + //Thread.sleep(1500); + WorkflowBundle otherFlow = openDataflow(); + // Saving another flow to same file should still fail + try { + fileManager.saveDataflow(otherFlow,WF_BUNDLE_FILE_TYPE, dataflowFile, true); + fail("Should have thrown OverwriteException"); + } catch (OverwriteException ex) { + // Expected + } + } + + @Test(expected = OverwriteException.class) + public void saveOverwriteWarningFails() throws Exception { + WorkflowBundle dataflow = openDataflow(); + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.deleteOnExit(); + // Should fail as file already exists + fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true); + } + + @Test + public void saveOverwriteWarningWorks() throws Exception { + WorkflowBundle dataflow = openDataflow(); + File dataflowFile = File.createTempFile("test", ".t2flow"); + dataflowFile.delete(); + dataflowFile.deleteOnExit(); + // File did NOT exist, should not fail + fileManager.saveDataflow(dataflow, WF_BUNDLE_FILE_TYPE, dataflowFile, true); + } + + @After + public void stopListeningToModelMap() { + fileManager.removeObserver(fileManagerObserver); + } + + protected WorkflowBundle openDataflow() throws OpenException { + URL url = getClass().getResource(DUMMY_WORKFLOW_T2FLOW); + assertNotNull(url); + WorkflowBundle dataflow = fileManager.openDataflow(T2_FLOW_FILE_TYPE, url); + assertNotNull(dataflow); + return dataflow; + } + + private final class FileManagerObserver implements Observer<FileManagerEvent> { + protected List<FileManagerEvent> messages = new ArrayList<FileManagerEvent>(); + + @Override + public void notify(Observable<FileManagerEvent> sender, FileManagerEvent message) throws Exception { + messages.add(message); + if (message instanceof SetCurrentDataflowEvent) { + assertTrue("Dataflow was not listed as open when set current", + fileManager.getOpenDataflows().contains( + ((SetCurrentDataflowEvent) message).getDataflow())); + } + } + } + +}
diff --git a/taverna-workbench-file-impl/src/test/resources/net/sf/taverna/t2/workbench/file/impl/dummy-workflow.t2flow b/taverna-workbench-file-impl/src/test/resources/net/sf/taverna/t2/workbench/file/impl/dummy-workflow.t2flow new file mode 100644 index 0000000..b9a1075 --- /dev/null +++ b/taverna-workbench-file-impl/src/test/resources/net/sf/taverna/t2/workbench/file/impl/dummy-workflow.t2flow
@@ -0,0 +1,157 @@ +<workflow xmlns="http://taverna.sf.net/2008/xml/t2flow" version="1" producedBy="test"> + <dataflow id="ec0991ba-275c-49ed-b1d6-38534180fb7c" role="top"> + <name>simple_workflow_with_input</name> + <inputPorts> + <port> + <name>input</name> + <depth>0</depth> + <granularDepth>0</granularDepth> + </port> + </inputPorts> + <outputPorts> + <port> + <name>output</name> + </port> + </outputPorts> + <processors> + <processor> + <name>Concat_XXX</name> + <inputPorts> + <port> + <name>input</name> + <depth>0</depth> + </port> + </inputPorts> + <outputPorts> + <port> + <name>output</name> + <depth>0</depth> + <granularDepth>0</granularDepth> + </port> + </outputPorts> + <annotations /> + <activities> + <activity> + <class> + net.sf.taverna.t2.activities.beanshell.BeanshellActivity + </class> + <inputMap> + <map from="input" to="input" /> + </inputMap> + <outputMap> + <map from="output" to="output" /> + </outputMap> + <configBean encoding="xstream"> + <net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean + xmlns=""> + <script>String output = input + "XXX";</script> + <dependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>java.lang.String</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>input</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>0</granularDepth> + <name>output</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> + </net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean> + </configBean> + </activity> + </activities> + <dispatchStack> + <dispatchLayer> + <class> + net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize + </class> + <configBean encoding="xstream"> + <net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig + xmlns=""> + <maxJobs>1</maxJobs> + </net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig> + </configBean> + </dispatchLayer> + <dispatchLayer> + <class> + net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce + </class> + <configBean encoding="xstream"> + <null xmlns="" /> + </configBean> + </dispatchLayer> + <dispatchLayer> + <class> + net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover + </class> + <configBean encoding="xstream"> + <null xmlns="" /> + </configBean> + </dispatchLayer> + <dispatchLayer> + <class> + net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry + </class> + <configBean encoding="xstream"> + <net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig + xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>0</initialDelay> + <maxDelay>0</maxDelay> + <maxRetries>0</maxRetries> + </net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig> + </configBean> + </dispatchLayer> + <dispatchLayer> + <class> + net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke + </class> + <configBean encoding="xstream"> + <null xmlns="" /> + </configBean> + </dispatchLayer> + </dispatchStack> + <iterationStrategyStack> + <iteration> + <strategy> + <port name="input" depth="0" /> + </strategy> + </iteration> + </iterationStrategyStack> + </processor> + </processors> + <conditions /> + <datalinks> + <datalink> + <sink type="processor"> + <processor>Concat_XXX</processor> + <port>input</port> + </sink> + <source type="dataflow"> + <port>input</port> + </source> + </datalink> + <datalink> + <sink type="dataflow"> + <port>output</port> + </sink> + <source type="processor"> + <processor>Concat_XXX</processor> + <port>output</port> + </source> + </datalink> + </datalinks> + </dataflow> +</workflow>
diff --git a/taverna-workbench-graph-model/pom.xml b/taverna-workbench-graph-model/pom.xml new file mode 100644 index 0000000..8e255ba --- /dev/null +++ b/taverna-workbench-graph-model/pom.xml
@@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>graph-model</artifactId> + <packaging>bundle</packaging> + <name>Graph Model</name> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>javacc-maven-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>javacc</id> + <goals> + <goal>jjtree-javacc</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + org.codehaus.mojo + </groupId> + <artifactId> + javacc-maven-plugin + </artifactId> + <versionRange> + [2.6,) + </versionRange> + <goals> + <goal>jjtree-javacc</goal> + </goals> + </pluginExecutionFilter> + <action> + <execute> + <runOnIncremental>false</runOnIncremental> + </execute> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>configuration-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>configuration-impl</artifactId> + <version>${t2.ui.impl.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>io</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.batik</groupId> + <artifactId>batik-osgi</artifactId> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>${commons.beanutils.version}</version> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DefaultGraphEventManager.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DefaultGraphEventManager.java new file mode 100644 index 0000000..d434e48 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DefaultGraphEventManager.java
@@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import static javax.swing.SwingUtilities.convertPointFromScreen; +import static javax.swing.SwingUtilities.invokeLater; +import static net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle.ALL; +import static net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle.NONE; + +import java.awt.Component; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.net.URI; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +import net.sf.taverna.t2.ui.menu.MenuManager; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; + +/** + * Manager for handling UI events on GraphElements. + * + * @author David Withers + */ +public class DefaultGraphEventManager implements GraphEventManager { + private static final URI NESTED_WORKFLOW_URI = URI + .create("http://ns.taverna.org.uk/2010/activity/nested-workflow"); + + private GraphController graphController; + private Component component; + private JPopupMenu menu; + private MenuManager menuManager; + + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + /** + * Constructs a new instance of GraphEventManager. + * + * @param graphController + * @param component + * component to use when displaying popup menus + */ + public DefaultGraphEventManager(GraphController graphController, Component component, + MenuManager menuManager) { + this.graphController = graphController; + this.component = component; + this.menuManager = menuManager; + } + + @Override + public void mouseClicked(final GraphElement graphElement, short button, boolean altKey, + boolean ctrlKey, boolean metaKey, final int x, final int y, int screenX, int screenY) { + Object dataflowObject = graphElement.getWorkflowBean(); + + // For both left and right click - add to selection model + if (graphController.getDataflowSelectionModel() != null) + graphController.getDataflowSelectionModel().addSelection(dataflowObject); + + if ((button != 2) && !ctrlKey)return; + + // If this was a right click - show a pop-up as well + if (dataflowObject == null) + menu = menuManager.createContextMenu(graphController.getWorkflow(), + graphController.getWorkflow(), component); + else { + menu = menuManager.createContextMenu(graphController.getWorkflow(), + dataflowObject, component); + if (dataflowObject instanceof Processor) { + final Processor processor = (Processor) dataflowObject; + ProcessorBinding processorBinding = scufl2Tools + .processorBindingForProcessor(processor, + graphController.getProfile()); + final Activity activity = processorBinding.getBoundActivity(); + if (menu == null) + menu = new JPopupMenu(); + if (graphElement instanceof GraphNode) { + defineMenuForGraphElement(graphElement, x, y, processor, + activity); + } else if (graphElement instanceof Graph) { + defineMenuForGraphBackground(activity); + } + } + } + + if (menu != null) { + final Point p = new Point(screenX, screenY); + convertPointFromScreen(p, component); + invokeLater(new Runnable() { + @Override + public void run() { + menu.show(component, p.x, p.y); + } + }); + } + } + + @SuppressWarnings("serial") + private void defineMenuForGraphBackground(final Activity activity) { + if (activity.getType().equals(NESTED_WORKFLOW_URI)) { + menu.addSeparator(); + menu.add(new JMenuItem(new AbstractAction("Hide nested workflow") { + @Override + public void actionPerformed(ActionEvent ev) { + graphController.setExpandNestedDataflow(activity, false); + graphController.redraw(); + } + })); + } + } + + @SuppressWarnings("serial") + private void defineMenuForGraphElement(final GraphElement graphElement, + final int x, final int y, final Processor processor, + final Activity activity) { + if (graphController.getPortStyle(processor).equals(NONE)) { + menu.addSeparator(); + menu.add(new JMenuItem(new AbstractAction("Show ports") { + @Override + public void actionPerformed(ActionEvent ev) { + graphController.setPortStyle(processor, ALL); + graphController.redraw(); + } + })); + } else if (graphController.getPortStyle(processor).equals(ALL)) { + menu.addSeparator(); + menu.add(new JMenuItem(new AbstractAction("Hide ports") { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setPortStyle(processor, NONE); + graphController.redraw(); + } + })); + } + + if (activity.getType().equals(NESTED_WORKFLOW_URI)) { + menu.addSeparator(); + menu.add(new JMenuItem(new AbstractAction("Show nested workflow") { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setExpandNestedDataflow(activity, true); + graphController.redraw(); + } + })); + } + + menu.addSeparator(); + + GraphNode graphNode = (GraphNode) graphElement; + + List<GraphNode> sourceNodes = graphNode.getSourceNodes(); + if (sourceNodes.size() == 1) { + final GraphNode sourceNode = sourceNodes.get(0); + if (sourceNode.getLabel() != null) { + menu.add(new JMenuItem(new AbstractAction("Link from output '" + + sourceNode.getLabel() + "'") { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.startEdgeCreation(sourceNode, + new Point(x, y)); + } + })); + } + } else if (sourceNodes.size() > 0) { + JMenu linkMenu = new JMenu("Link from output..."); + menu.add(linkMenu); + for (final GraphNode sourceNode : sourceNodes) { + linkMenu.add(new JMenuItem(new AbstractAction(sourceNode + .getLabel()) { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.startEdgeCreation(sourceNode, + new Point(x, y)); + } + })); + } + } + + List<GraphNode> sinkNodes = graphNode.getSinkNodes(); + if (sinkNodes.size() == 1) { + final GraphNode sinkNode = sinkNodes.get(0); + if (sinkNode.getLabel() != null) { + menu.add(new JMenuItem(new AbstractAction("Link to input '" + + sinkNode.getLabel() + "'") { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.startEdgeCreation(sinkNode, new Point( + x, y)); + } + })); + } + } else if (sinkNodes.size() > 0) { + JMenu linkMenu = new JMenu("Link to input..."); + menu.add(linkMenu); + for (final GraphNode sinkNode : sinkNodes) { + linkMenu.add(new JMenuItem(new AbstractAction(sinkNode + .getLabel()) { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.startEdgeCreation(sinkNode, new Point( + x, y)); + } + })); + } + } + } + + @Override + public void mouseDown(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + if (button == 0) + graphController.startEdgeCreation(graphElement, new Point(x, y)); + } + + @Override + public void mouseUp(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, final int x, + final int y, int screenX, int screenY) { + if (button == 0) + graphController.stopEdgeCreation(graphElement, new Point(screenX, + screenY)); + } + + @Override + public void mouseMoved(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + graphController.moveEdgeCreationTarget(graphElement, new Point(x, y)); + } + + @Override + public void mouseOver(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + if (graphElement.getWorkflowBean() != null) + graphElement.setActive(true); + } + + @Override + public void mouseOut(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + if (graphElement.getWorkflowBean() != null) + graphElement.setActive(false); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DotWriter.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DotWriter.java new file mode 100644 index 0000000..07cdbad --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/DotWriter.java
@@ -0,0 +1,253 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import static java.lang.String.format; +import static net.sf.taverna.t2.workbench.models.graph.Graph.Alignment.HORIZONTAL; +import static net.sf.taverna.t2.workbench.models.graph.Graph.Alignment.VERTICAL; +import static net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle.NONE; +import static net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape.RECORD; + +import java.awt.Color; +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment; + +/** + * Writer for creating a graphical representation of a Graph in the DOT language. + * + * @author David Withers + */ +public class DotWriter { + private static final String EOL = System.getProperty("line.separator"); + + private Writer writer; + + /** + * Constructs a new instance of DotWriter. + * + * @param writer + */ + public DotWriter(Writer writer) { + this.writer = writer; + } + + /** + * Writes a graphical representation of a Graph in the DOT language to a Writer. + * + * @param graph + * @throws IOException + */ + public void writeGraph(Graph graph) throws IOException { + writeLine("digraph \"" + graph.getId() + "\" {"); + + // Overall graph style + writeLine(" graph ["); + writeLine(" bgcolor=\"" + getHexValue(graph.getFillColor()) + "\""); + writeLine(" color=\"black\""); + writeLine(" fontsize=\"10\""); + writeLine(" labeljust=\"left\""); + writeLine(" clusterrank=\"local\""); + writeLine(" ranksep=\"0.22\""); + writeLine(" nodesep=\"0.05\""); + // Set left to right view if alignment is horizontal + if (graph.getAlignment().equals(HORIZONTAL)) + writeLine(" rankdir=\"LR\""); + writeLine(" ]"); + + // Overall node style + writeLine(" node ["); + writeLine(" fontname=\"Helvetica\""); + writeLine(" fontsize=\"10\""); + writeLine(" fontcolor=\"black\""); + writeLine(" shape=\"record\""); + writeLine(" height=\"0\""); + writeLine(" width=\"0\""); + writeLine(" color=\"black\""); + writeLine(" fillcolor=\"lightgoldenrodyellow\""); + writeLine(" style=\"filled\""); + writeLine(" ];"); + + // Overall edge style + writeLine(" edge ["); + writeLine(" fontname=\"Helvetica\""); + writeLine(" fontsize=\"8\""); + writeLine(" fontcolor=\"black\""); + writeLine(" color=\"black\""); + writeLine(" ];"); + + for (GraphNode node : graph.getNodes()) { + if (node.isExpanded()) + writeSubGraph(node.getGraph(), " "); + else + writeNode(node, graph.getAlignment(), " "); + } + + for (Graph subGraph : graph.getSubgraphs()) + writeSubGraph(subGraph, " "); + + for (GraphEdge edge : graph.getEdges()) + writeEdges(edge, graph.getAlignment(), " "); + + writeLine("}"); + } + + private void writeSubGraph(Graph graph, String indent) throws IOException { + writeLine(format("%ssubgraph \"cluster_%s\" {", indent, graph.getId())); + writeLine(format("%s rank=\"same\"", indent)); + + StringBuilder style = new StringBuilder(); + if (graph.getFillColor() != null) { + writeLine(format("%s fillcolor=\"%s\"", indent, + getHexValue(graph.getFillColor()))); + style.append("filled"); + } + if (graph.getLineStyle() != null) { + style.append(style.length() == 0 ? "" : ","); + if (graph.getLineStyle().equals(NONE)) + style.append("invis"); + else + style.append(graph.getLineStyle().toString().toLowerCase()); + } + writeLine(format("%s style=\"%s\"", indent, style)); + + if (graph.getLabel() != null) + writeLine(format("%s label=\"%s\"", indent, graph.getLabel())); + + for(GraphNode node : graph.getNodes()) { + if (node.isExpanded()) + writeSubGraph(node.getGraph(), indent + " "); + else + writeNode(node, graph.getAlignment(), indent + " "); + } + + for (Graph subGraph : graph.getSubgraphs()) + writeSubGraph(subGraph, indent + " "); + + for (GraphEdge edge : graph.getEdges()) + writeEdges(edge, graph.getAlignment(), indent + " "); + + writeLine(indent + "}"); + } + + private void writeEdges(GraphEdge edge, Alignment alignment, String indent) throws IOException { + GraphNode source = edge.getSource(); + GraphNode sink = edge.getSink(); + String sourceId = "\"" + source.getId() + "\""; + String sinkId = "\"" + sink.getId() + "\""; + + if (source.getParent() instanceof GraphNode) { + GraphNode parent = (GraphNode) source.getParent(); + sourceId = "\"" + parent.getId() + "\":" + sourceId; + } + if (sink.getParent() instanceof GraphNode) { + GraphNode parent = (GraphNode) sink.getParent(); + sinkId = "\"" + parent.getId() + "\":" + sinkId; + } + /* + * the compass point is required with newer versions of dot (e.g. + * 2.26.3) but is not compatible with older versions (e.g. 1.3) + */ + if (alignment.equals(HORIZONTAL)) { + sourceId = sourceId + ":e"; + sinkId = sinkId + ":w"; + } else { + sourceId = sourceId + ":s"; + sinkId = sinkId + ":n"; + } + writeLine(format("%s%s -> %s [", indent, sourceId, sinkId)); + writeLine(format("%s arrowhead=\"%s\"", indent, edge + .getArrowHeadStyle().toString().toLowerCase())); + writeLine(format("%s, arrowtail=\"%s\"", indent, edge + .getArrowTailStyle().toString().toLowerCase())); + if (edge.getColor() != null) + writeLine(format("%s color=\"%s\"", indent, + getHexValue(edge.getColor()))); + writeLine(format("%s]", indent)); + } + + private void writeNode(GraphNode node, Alignment alignment, String indent) throws IOException { + writeLine(format("%s\"%s\" [", indent, node.getId())); + + StringBuilder style = new StringBuilder(); + if (node.getFillColor() != null) { + writeLine(format("%s fillcolor=\"%s\"", indent, + getHexValue(node.getFillColor()))); + style.append("filled"); + } + if (node.getLineStyle() != null) { + style.append(style.length() == 0 ? "" : ","); + style.append(node.getLineStyle().toString().toLowerCase()); + } + writeLine(format("%s style=\"%s\"", indent, style)); + + writeLine(format("%s shape=\"%s\"", indent, node.getShape().toString().toLowerCase())); + writeLine(format("%s width=\"%s\"", indent, node.getWidth() / 72f)); + writeLine(format("%s height=\"%s\"", indent, node.getHeight() / 72f)); + + if (node.getShape().equals(RECORD)) { + StringBuilder labelString = new StringBuilder(); + if (alignment.equals(VERTICAL)) { + labelString.append("{{"); + addNodeLabels(node.getSinkNodes(), labelString); + labelString.append("}|").append(node.getLabel()).append("|{"); + addNodeLabels(node.getSourceNodes(), labelString); + labelString.append("}}"); + } else { + labelString.append(node.getLabel()).append("|{{"); + addNodeLabels(node.getSinkNodes(), labelString); + labelString.append("}|{"); + addNodeLabels(node.getSourceNodes(), labelString); + labelString.append("}}"); + } + writeLine(format("%s label=\"%s\"", indent, labelString)); + } else { + writeLine(format("%s label=\"%s\"", indent, node.getLabel())); + } + + writeLine(format("%s];", indent)); + } + + private void addNodeLabels(List<GraphNode> nodes, StringBuilder labelString) { + String sep = ""; + for (GraphNode node : nodes) + if (node.getLabel() != null) { + labelString.append(sep); + labelString.append("<"); + labelString.append(node.getId()); + labelString.append(">"); + labelString.append(node.getLabel()); + sep = "|"; + } + } + + private String getHexValue(Color color) { + return format("#%02x%02x%02x", color.getRed(), color.getGreen(), + color.getBlue()); + } + + private void writeLine(String line) throws IOException { + writer.write(line); + writer.write(EOL); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/Graph.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/Graph.java new file mode 100644 index 0000000..0ff3852 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/Graph.java
@@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A graph model of a dataflow. + * + * @author David Withers + */ +public class Graph extends GraphShapeElement { + public enum Alignment { + HORIZONTAL, VERTICAL + } + + private List<GraphNode> nodes = new ArrayList<>(); + private Set<GraphEdge> edges = new HashSet<>(); + private Set<Graph> subgraphs = new HashSet<>(); + private Alignment alignment = Alignment.VERTICAL; + + /** + * Constructs a Graph that uses the specified GraphEventManager to handle + * any user generated events on GraphElements. + * + * @param eventManager + */ + public Graph(GraphController graphController) { + super(graphController); + } + + /** + * Adds an edge to the Graph and sets its parent to be this Graph. + * + * @param edge + * the edge to add + */ + public void addEdge(GraphEdge edge) { + edge.setParent(this); + edges.add(edge); + } + + /** + * Adds a node to the Graph and sets its parent to be this Graph. + * + * @param node + * the node to add + */ + public void addNode(GraphNode node) { + node.setParent(this); + nodes.add(node); + } + + /** + * Adds a subgraph to the Graph and sets its parent to be this Graph. + * + * @param subgraph + * the subgraph to add + */ + public void addSubgraph(Graph subgraph) { + subgraph.setParent(this); + subgraphs.add(subgraph); + } + + /** + * Returns the alignment of the Graph. + * + * @return the alignment of the Graph + */ + public Alignment getAlignment() { + return alignment; + } + + /** + * Returns the edges contained in the Graph. + * + * @return the edges contained in the Graph + */ + public Set<GraphEdge> getEdges() { + return Collections.unmodifiableSet(edges); + } + + /** + * Returns the nodes contained in the Graph. + * + * @return the nodes contained in the Graph + */ + public List<GraphNode> getNodes() { + return Collections.unmodifiableList(nodes); + } + + /** + * Returns the subgraphs contained in the Graph. + * + * @return the subgraphs contained in the Graph + */ + public Set<Graph> getSubgraphs() { + return Collections.unmodifiableSet(subgraphs); + } + + /** + * Removes an edge from the Graph. + * + * @param edge + * the edge to remove + * @return true if the edge is removed from the Graph + */ + public boolean removeEdge(GraphEdge edge) { + return edges.remove(edge); + } + + /** + * Removes a node from the Graph. + * + * @param node + * the node to remove + * @return true if the node is removed from the Graph + */ + public boolean removeNode(GraphNode node) { + return nodes.remove(node); + } + + /** + * Removes a subgraph from the Graph. + * + * @param subgraph + * the subgraph to remove + * @return true if the subgraph is removed from the Graph + */ + public boolean removeSubgraph(Graph subgraph) { + return subgraphs.remove(subgraph); + } + + /** + * Sets the alignment of the Graph. + * + * @param alignment + * the new alignment + */ + public void setAlignment(Alignment alignment) { + this.alignment = alignment; + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphColorManager.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphColorManager.java new file mode 100644 index 0000000..1f44076 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphColorManager.java
@@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import java.awt.Color; +import java.lang.reflect.InvocationTargetException; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; + +import org.apache.commons.beanutils.PropertyUtils; + +import uk.org.taverna.scufl2.api.activity.Activity; + +/** + * Manages the colour of elements in a graph. + * + * @author David Withers + * @author Start Owen + */ +public class GraphColorManager { + private static final String BEANSHELL = "http://ns.taverna.org.uk/2010/activity/beanshell"; + private static final String LOCALWORKER = "http://ns.taverna.org.uk/2010/activity/localworker"; + + private static Color[] subGraphFillColors = new Color[] { + Color.decode("#ffffff"), Color.decode("#f0f8ff"), + Color.decode("#faebd7"), Color.decode("#f5f5dc") }; + + /** + * Returns the colour associated with the Activity. + * + * For unknown activities Color.WHITE is returned. + * + * For {@link LocalworkerActivity} which have been user configured use the + * BeanshellActivity colour + * + * @return the colour associated with the Activity + */ + public static Color getFillColor(Activity activity, ColourManager colourManager) { + try { + if (activity.getType().equals(LOCALWORKER)) { + // To avoid compile time dependency - read isAltered property as bean + if (Boolean.TRUE.equals(PropertyUtils.getProperty(activity, "altered"))) { + Color colour = colourManager.getPreferredColour(BEANSHELL); + return colour; + } + } + } catch (IllegalAccessException | InvocationTargetException + | NoSuchMethodException e) { + } + Color colour = colourManager.getPreferredColour(activity.getType().toASCIIString()); + return colour; + } + + public static Color getSubGraphFillColor(int depth) { + return subGraphFillColors[depth % subGraphFillColors.length]; + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphController.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphController.java new file mode 100644 index 0000000..0fb87a4 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphController.java
@@ -0,0 +1,1276 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import static javax.swing.JOptionPane.PLAIN_MESSAGE; +import static javax.swing.JOptionPane.showInputDialog; +import static javax.swing.JOptionPane.showMessageDialog; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment; +import net.sf.taverna.t2.workbench.models.graph.GraphEdge.ArrowStyle; +import net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle; +import net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage; +import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit; +import net.sf.taverna.t2.workflow.edits.RemoveDataLinkEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.NamedSet; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.common.WorkflowBean; +import uk.org.taverna.scufl2.api.core.BlockingControlLink; +import uk.org.taverna.scufl2.api.core.ControlLink; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.InputPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import uk.org.taverna.scufl2.api.port.Port; +import uk.org.taverna.scufl2.api.port.ProcessorPort; +import uk.org.taverna.scufl2.api.port.ReceiverPort; +import uk.org.taverna.scufl2.api.port.SenderPort; +import uk.org.taverna.scufl2.api.port.WorkflowPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +/** + * @author David Withers + */ +public abstract class GraphController implements + Observer<DataflowSelectionMessage> { + public enum PortStyle { + ALL { + @Override + Shape inputShape() { + return Shape.INVHOUSE; + } + + @Override + Shape outputShape() { + return Shape.HOUSE; + } + + @Override + Shape processorShape() { + return Shape.RECORD; + } + }, + BOUND { + @Override + Shape inputShape() { + return Shape.INVHOUSE; + } + + @Override + Shape outputShape() { + return Shape.HOUSE; + } + + @Override + Shape processorShape() { + return Shape.RECORD; + } + }, + NONE { + @Override + Shape inputShape() { + return Shape.BOX; + } + + @Override + Shape outputShape() { + return Shape.BOX; + } + + @Override + Shape processorShape() { + return Shape.BOX; + } + }, + BLOB { + @Override + Shape inputShape() { + return Shape.CIRCLE; + } + + @Override + Shape outputShape() { + return Shape.CIRCLE; + } + + @Override + Shape processorShape() { + return Shape.CIRCLE; + } + }; + + abstract Shape inputShape(); + + abstract Shape outputShape(); + + abstract Shape processorShape(); + + Shape mergeShape() { + return Shape.CIRCLE; + } + } + + private static Logger logger = Logger.getLogger(GraphController.class); + + private Map<String, GraphElement> idToElement = new HashMap<>(); + private Map<WorkflowBean, GraphElement> workflowToGraph = new HashMap<>(); + private Map<Port, GraphNode> ports = new HashMap<>(); + private Map<Graph, GraphNode> inputControls = new HashMap<>(); + private Map<Graph, GraphNode> outputControls = new HashMap<>(); + private Map<Port, Port> nestedWorkflowPorts = new HashMap<>(); + private Map<WorkflowPort, ProcessorPort> workflowPortToProcessorPort = new HashMap<>(); + private Map<Port, Processor> portToProcessor = new HashMap<>(); + + private EditManager editManager; + private final Workflow workflow; + private final Profile profile; + private DataflowSelectionModel dataflowSelectionModel; + private GraphEventManager graphEventManager; + private Component componentForPopups; + + // graph settings + private PortStyle portStyle = PortStyle.NONE; + private Map<Processor, PortStyle> processorPortStyle = new HashMap<>(); + private Alignment alignment = Alignment.VERTICAL; + private boolean expandNestedDataflows = true; + private Map<Activity, Boolean> dataflowExpansion = new HashMap<>(); + protected Map<String, GraphElement> graphElementMap = new HashMap<>(); + protected GraphElement edgeCreationSource, edgeCreationSink; + protected GraphEdge edgeMoveElement; + protected boolean edgeCreationFromSource = false; + protected boolean edgeCreationFromSink = false; + private Graph graph; + private boolean interactive; + private final ColourManager colourManager; + + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public GraphController(Workflow workflow, Profile profile, + boolean interactive, Component componentForPopups, + EditManager editManager, MenuManager menuManager, + ColourManager colourManager) { + this(workflow, profile, interactive, componentForPopups, + Alignment.VERTICAL, PortStyle.NONE, editManager, menuManager, + colourManager); + } + + public GraphController(Workflow workflow, Profile profile, + boolean interactive, Component componentForPopups, + Alignment alignment, PortStyle portStyle, EditManager editManager, + MenuManager menuManager, ColourManager colourManager) { + this.workflow = workflow; + this.profile = profile; + this.interactive = interactive; + this.componentForPopups = componentForPopups; + this.alignment = alignment; + this.portStyle = portStyle; + this.editManager = editManager; + this.colourManager = colourManager; + this.graphEventManager = new DefaultGraphEventManager(this, + componentForPopups, menuManager); + graph = generateGraph(); + } + + public abstract Graph createGraph(); + + public abstract GraphNode createGraphNode(); + + public abstract GraphEdge createGraphEdge(); + + public void mapElement(String id, GraphElement element) { + idToElement.put(id, element); + } + + public GraphElement getElement(String id) { + return idToElement.get(id); + } + + public Graph getGraph() { + return graph; + } + + public abstract void redraw(); + + /** + * Generates a graph model of a dataflow. + * + * @return + */ + public Graph generateGraph() { + workflowToGraph.clear(); + ports.clear(); + inputControls.clear(); + outputControls.clear(); + nestedWorkflowPorts.clear(); + workflowPortToProcessorPort.clear(); + graphElementMap.clear(); + portToProcessor.clear(); + return generateGraph(workflow, "", workflow.getName(), 0); + } + + private Graph generateGraph(Workflow dataflow, String prefix, String name, + int depth) { + Graph graph = createGraph(); + graph.setId(prefix + name); + graph.setAlignment(getAlignment()); + if (getPortStyle().equals(PortStyle.BLOB) || depth == 0) + graph.setLabel(""); + else + graph.setLabel(name); + graph.setFillColor(GraphColorManager.getSubGraphFillColor(depth)); + if (depth == 0) + graph.setLineStyle(LineStyle.NONE); + else + graph.setLineStyle(LineStyle.SOLID); + graph.setColor(Color.BLACK); + graph.setShape(Shape.BOX); + + if (depth == 0) + graph.setWorkflowBean(dataflow); + if (interactive) + graph.setWorkflowBean(dataflow); + + // processors + for (Processor processor : dataflow.getProcessors()) + graph.addNode(generateProcessorNode(processor, graph.getId(), depth)); + + // dataflow outputs + NamedSet<OutputWorkflowPort> outputPorts = dataflow.getOutputPorts(); + if (outputPorts.size() > 0 || depth > 0) + graph.addSubgraph(generateOutputsGraph(outputPorts, graph.getId(), + graph, depth)); + + // dataflow inputs + NamedSet<InputWorkflowPort> inputPorts = dataflow.getInputPorts(); + if (inputPorts.size() > 0 || depth > 0) + graph.addSubgraph(generateInputsGraph(inputPorts, graph.getId(), + graph, depth)); + + // datalinks + for (DataLink datalink : dataflow.getDataLinks()) { + GraphEdge edge = generateDataLinkEdge(datalink, depth); + if (edge != null) + graph.addEdge(edge); + } + + // controlLinks + for (ControlLink controlLink : dataflow.getControlLinks()) + if (controlLink instanceof BlockingControlLink) { + GraphEdge edge = generateControlLinkEdge( + (BlockingControlLink) controlLink, depth); + if (edge != null) + graph.addEdge(edge); + } + + graphElementMap.put(graph.getId(), graph); + return graph; + } + + public void transformGraph(Graph oldGraph, Graph newGraph) { + oldGraph.setAlignment(newGraph.getAlignment()); + transformGraphElement(oldGraph, newGraph); + List<GraphEdge> oldEdges = new ArrayList<>(oldGraph.getEdges()); + List<GraphEdge> newEdges = new ArrayList<>(newGraph.getEdges()); + for (GraphEdge oldEdge : oldEdges) { + int index = newEdges.indexOf(oldEdge); + if (index >= 0) { + GraphEdge newEdge = newEdges.remove(index); + oldEdge.setPath(newEdge.getPath()); + workflowToGraph.put(oldEdge.getWorkflowBean(), oldEdge); + } else + oldGraph.removeEdge(oldEdge); + } + List<GraphNode> newNodes = new ArrayList<>(newGraph.getNodes()); + List<GraphNode> oldNodes = new ArrayList<>(oldGraph.getNodes()); + for (GraphNode oldNode : oldNodes) { + int index = newNodes.indexOf(oldNode); + if (index >= 0) { + GraphNode newNode = newNodes.remove(index); + oldNode.setExpanded(newNode.isExpanded()); + List<GraphNode> newSourceNodes = new ArrayList<>( + newNode.getSourceNodes()); + List<GraphNode> oldSourceNodes = new ArrayList<>( + oldNode.getSourceNodes()); + for (GraphNode oldSourceNode : oldSourceNodes) { + int sourceNodeIndex = newSourceNodes.indexOf(oldSourceNode); + if (sourceNodeIndex >= 0) { + GraphNode newSourceNode = newSourceNodes + .remove(sourceNodeIndex); + transformGraphElement(oldSourceNode, newSourceNode); + } else + oldNode.removeSourceNode(oldSourceNode); + } + for (GraphNode sourceNode : newSourceNodes) + oldNode.addSourceNode(sourceNode); + List<GraphNode> newSinkNodes = new ArrayList<>( + newNode.getSinkNodes()); + List<GraphNode> oldSinkNodes = new ArrayList<>( + oldNode.getSinkNodes()); + for (GraphNode oldSinkNode : oldSinkNodes) { + int sinkNodeIndex = newSinkNodes.indexOf(oldSinkNode); + if (sinkNodeIndex >= 0) { + GraphNode newSinkNode = newSinkNodes + .remove(sinkNodeIndex); + transformGraphElement(oldSinkNode, newSinkNode); + } else + oldNode.removeSinkNode(oldSinkNode); + } + for (GraphNode sinkNode : newSinkNodes) + oldNode.addSinkNode(sinkNode); + Graph oldSubGraph = oldNode.getGraph(); + Graph newSubGraph = newNode.getGraph(); + if (oldSubGraph != null && newSubGraph != null) + transformGraph(oldSubGraph, newSubGraph); + transformGraphElement(oldNode, newNode); + } else + oldGraph.removeNode(oldNode); + } + List<Graph> newSubGraphs = new ArrayList<>(newGraph.getSubgraphs()); + List<Graph> oldSubGraphs = new ArrayList<>(oldGraph.getSubgraphs()); + for (Graph oldSubGraph : oldSubGraphs) { + int index = newSubGraphs.indexOf(oldSubGraph); + if (index >= 0) { + Graph newSubGraph = newSubGraphs.remove(index); + transformGraph(oldSubGraph, newSubGraph); + } else + oldGraph.removeSubgraph(oldSubGraph); + } + for (GraphNode node : newNodes) + oldGraph.addNode(node); + for (Graph graph : newSubGraphs) + oldGraph.addSubgraph(graph); + for (GraphEdge newEdge : newEdges) + oldGraph.addEdge(newEdge); + } + + public void transformGraphElement(GraphShapeElement oldGraphElement, + GraphShapeElement newGraphElement) { + oldGraphElement.setWorkflowBean(newGraphElement.getWorkflowBean()); + oldGraphElement.setShape(newGraphElement.getShape()); + oldGraphElement.setSize(newGraphElement.getSize()); + oldGraphElement.setPosition(newGraphElement.getPosition()); + oldGraphElement.setLabel(newGraphElement.getLabel()); + oldGraphElement.setLabelPosition(newGraphElement.getLabelPosition()); + oldGraphElement.setLineStyle(newGraphElement.getLineStyle()); + oldGraphElement.setOpacity(newGraphElement.getOpacity()); + oldGraphElement.setVisible(newGraphElement.isVisible()); + oldGraphElement.setColor(newGraphElement.getColor()); + oldGraphElement.setFillColor(newGraphElement.getFillColor()); + workflowToGraph.put(oldGraphElement.getWorkflowBean(), oldGraphElement); + } + + public void filterGraph(Set<?> dataflowEntities) { + Set<GraphElement> graphElements = new HashSet<>(); + for (Entry<WorkflowBean, GraphElement> entry : workflowToGraph + .entrySet()) + if (!dataflowEntities.contains(entry.getKey())) + graphElements.add(entry.getValue()); + filterGraph(getGraph(), graphElements); + } + + private void filterGraph(Graph graph, Set<GraphElement> graphElements) { + for (GraphNode node : graph.getNodes()) { + node.setFiltered(graphElements.contains(node)); + Graph subgraph = node.getGraph(); + if (subgraph != null) + if (graphElements.contains(subgraph)) { + removeFilter(subgraph); + subgraph.setFiltered(true); + } else { + subgraph.setFiltered(false); + filterGraph(subgraph, graphElements); + } + } + for (GraphEdge edge : graph.getEdges()) + edge.setFiltered(graphElements.contains(edge)); + for (Graph subgraph : graph.getSubgraphs()) + if (graphElements.contains(subgraph)) { + removeFilter(subgraph); + subgraph.setFiltered(true); + } else { + subgraph.setFiltered(false); + filterGraph(subgraph, graphElements); + } + } + + public void removeFilter() { + for (Entry<WorkflowBean, GraphElement> entry : workflowToGraph + .entrySet()) + entry.getValue().setFiltered(false); + } + + private void removeFilter(Graph graph) { + for (GraphNode node : graph.getNodes()) { + node.setOpacity(1f); + Graph subgraph = node.getGraph(); + if (subgraph != null) { + subgraph.setFiltered(false); + removeFilter(subgraph); + } + } + for (GraphEdge edge : graph.getEdges()) + edge.setFiltered(false); + for (Graph subgraph : graph.getSubgraphs()) { + subgraph.setFiltered(false); + removeFilter(subgraph); + } + } + + private GraphEdge generateControlLinkEdge(BlockingControlLink condition, + int depth) { + GraphEdge edge = null; + GraphElement source = workflowToGraph.get(condition.getUntilFinished()); + GraphElement sink = workflowToGraph.get(condition.getBlock()); + if (source != null && sink != null) { + edge = createGraphEdge(); + if (source instanceof Graph) + edge.setSource(outputControls.get(source)); + else if (source instanceof GraphNode) + edge.setSource((GraphNode) source); + if (sink instanceof Graph) + edge.setSink(inputControls.get(sink)); + else if (sink instanceof GraphNode) + edge.setSink((GraphNode) sink); + String sourceId = edge.getSource().getId(); + String sinkId = edge.getSink().getId(); + edge.setId(sourceId + "->" + sinkId); + edge.setLineStyle(LineStyle.SOLID); + edge.setColor(Color.decode("#505050")); + edge.setFillColor(null); + edge.setArrowHeadStyle(ArrowStyle.DOT); + if (depth == 0) + edge.setWorkflowBean(condition); + if (interactive) + edge.setWorkflowBean(condition); + workflowToGraph.put(condition, edge); + graphElementMap.put(edge.getId(), edge); + } + return edge; + } + + private GraphEdge generateDataLinkEdge(DataLink datalink, int depth) { + GraphEdge edge = null; + Port sourcePort = datalink.getReceivesFrom(); + Port sinkPort = datalink.getSendsTo(); + if (nestedWorkflowPorts.containsKey(sourcePort)) + sourcePort = nestedWorkflowPorts.get(sourcePort); + if (nestedWorkflowPorts.containsKey(sinkPort)) + sinkPort = nestedWorkflowPorts.get(sinkPort); + GraphNode sourceNode = ports.get(sourcePort); + GraphNode sinkNode = ports.get(sinkPort); + if (sourceNode != null && sinkNode != null) { + edge = createGraphEdge(); + edge.setSource(sourceNode); + edge.setSink(sinkNode); + + StringBuilder id = new StringBuilder(); + if (sourceNode.getParent() instanceof GraphNode) { + id.append(sourceNode.getParent().getId()); + id.append(":"); + id.append(sourceNode.getId()); + } else + id.append(sourceNode.getId()); + id.append("->"); + if (sinkNode.getParent() instanceof GraphNode) { + id.append(sinkNode.getParent().getId()); + id.append(":"); + id.append(sinkNode.getId()); + } else + id.append(sinkNode.getId()); + edge.setId(id.toString()); + edge.setLineStyle(LineStyle.SOLID); + edge.setColor(Color.BLACK); + edge.setFillColor(Color.BLACK); + if (depth == 0) + edge.setWorkflowBean(datalink); + if (interactive) + edge.setWorkflowBean(datalink); + workflowToGraph.put(datalink, edge); + graphElementMap.put(edge.getId(), edge); + } + return edge; + } + + private Graph generateInputsGraph(NamedSet<InputWorkflowPort> inputPorts, + String prefix, Graph graph, int depth) { + Graph inputs = createGraph(); + inputs.setId(prefix + "sources"); + inputs.setColor(Color.BLACK); + inputs.setFillColor(null); + inputs.setShape(Shape.BOX); + inputs.setLineStyle(LineStyle.DOTTED); + if (getPortStyle().equals(PortStyle.BLOB)) + inputs.setLabel(""); + else + inputs.setLabel("Workflow input ports"); + + GraphNode triangle = createGraphNode(); + triangle.setId(prefix + "WORKFLOWINTERNALSOURCECONTROL"); + triangle.setLabel(""); + triangle.setShape(Shape.TRIANGLE); + triangle.setSize(new Dimension((int) (0.2f * 72), (int) ((Math.sin(Math + .toRadians(60)) * 0.2) * 72))); + triangle.setFillColor(Color.decode("#ff4040")); + triangle.setColor(Color.BLACK); + triangle.setLineStyle(LineStyle.SOLID); + inputs.addNode(triangle); + inputControls.put(graph, triangle); + + for (InputWorkflowPort inputWorkflowPort : inputPorts) { + GraphNode inputNode = createGraphNode(); + inputNode.setId(prefix + "WORKFLOWINTERNALSOURCE_" + + inputWorkflowPort.getName()); + if (getPortStyle().equals(PortStyle.BLOB)) { + inputNode.setLabel(""); + inputNode.setSize(new Dimension((int) (0.3f * 72), + (int) (0.3f * 72))); + } else + inputNode.setLabel(inputWorkflowPort.getName()); + inputNode.setShape(getPortStyle().inputShape()); + inputNode.setColor(Color.BLACK); + inputNode.setLineStyle(LineStyle.SOLID); + inputNode.setFillColor(Color.decode("#8ed6f0")); + if (depth == 0) + inputNode.setInteractive(true); + if (interactive) + inputNode.setInteractive(true); + if (depth < 2) { + inputNode.setWorkflowBean(inputWorkflowPort); + if (workflowPortToProcessorPort.containsKey(inputWorkflowPort)) { + ProcessorPort port = workflowPortToProcessorPort + .get(inputWorkflowPort); + inputNode.setWorkflowBean(port); + workflowToGraph.put(port, inputNode); + } else { + inputNode.setWorkflowBean(inputWorkflowPort); + workflowToGraph.put(inputWorkflowPort, inputNode); + } + } + ports.put(inputWorkflowPort, inputNode); + inputs.addNode(inputNode); + graphElementMap.put(inputNode.getId(), inputNode); + } + return inputs; + } + + private Graph generateOutputsGraph( + NamedSet<OutputWorkflowPort> outputPorts, String prefix, + Graph graph, int depth) { + Graph outputs = createGraph(); + outputs.setId(prefix + "sinks"); + outputs.setColor(Color.BLACK); + outputs.setFillColor(null); + outputs.setShape(Shape.BOX); + outputs.setLineStyle(LineStyle.DOTTED); + if (getPortStyle().equals(PortStyle.BLOB)) + outputs.setLabel(""); + else + outputs.setLabel("Workflow output ports"); + + GraphNode triangle = createGraphNode(); + triangle.setId(prefix + "WORKFLOWINTERNALSINKCONTROL"); + triangle.setLabel(""); + triangle.setShape(Shape.INVTRIANGLE); + triangle.setSize(new Dimension((int) (0.2f * 72), (int) ((Math.sin(Math + .toRadians(60)) * 0.2) * 72))); + triangle.setFillColor(Color.decode("#66cd00")); + triangle.setColor(Color.BLACK); + triangle.setLineStyle(LineStyle.SOLID); + outputs.addNode(triangle); + outputControls.put(graph, triangle); + + for (OutputWorkflowPort outputWorkflowPort : outputPorts) { + GraphNode outputNode = createGraphNode(); + outputNode.setId(prefix + "WORKFLOWINTERNALSINK_" + + outputWorkflowPort.getName()); + if (getPortStyle().equals(PortStyle.BLOB)) { + outputNode.setLabel(""); + outputNode.setSize(new Dimension((int) (0.3f * 72), + (int) (0.3f * 72))); + } else + outputNode.setLabel(outputWorkflowPort.getName()); + outputNode.setShape(getPortStyle().outputShape()); + outputNode.setColor(Color.BLACK); + outputNode.setLineStyle(LineStyle.SOLID); + outputNode.setFillColor(Color.decode("#8ed6f0")); + if (depth == 0) + outputNode.setInteractive(true); + if (interactive) + outputNode.setInteractive(true); + if (depth < 2) { + if (workflowPortToProcessorPort.containsKey(outputWorkflowPort)) { + ProcessorPort port = workflowPortToProcessorPort + .get(outputWorkflowPort); + outputNode.setWorkflowBean(port); + workflowToGraph.put(port, outputNode); + } else { + outputNode.setWorkflowBean(outputWorkflowPort); + workflowToGraph.put(outputWorkflowPort, outputNode); + } + } + ports.put(outputWorkflowPort, outputNode); + outputs.addNode(outputNode); + graphElementMap.put(outputNode.getId(), outputNode); + } + return outputs; + } + + private GraphNode generateProcessorNode(Processor processor, String prefix, + int depth) { + // Blatantly ignoring any other activities for now + ProcessorBinding processorBinding = scufl2Tools + .processorBindingForProcessor(processor, profile); + Activity activity = processorBinding.getBoundActivity(); + @SuppressWarnings("unused") + URI activityType = activity.getType(); + + GraphNode node = createGraphNode(); + node.setId(prefix + processor.getName()); + if (getPortStyle().equals(PortStyle.BLOB)) { + node.setLabel(""); + node.setSize(new Dimension((int) (0.3f * 72), (int) (0.3f * 72))); + } else + node.setLabel(processor.getName()); + node.setShape(getPortStyle(processor).processorShape()); + node.setColor(Color.BLACK); + node.setLineStyle(LineStyle.SOLID); + // if (activityType.equals(URI.create(NonExecutableActivity.URI))) { + // if (activityType.equals(URI.create(DisabledActivity.URI))) { + // node.setFillColor(GraphColorManager + // .getFillColor(((DisabledActivity) activity) + // .getActivity(), colourManager)); + // } else { + // node.setFillColor(GraphColorManager + // .getFillColor(activityType, colourManager)); + // } + // node.setOpacity(0.3f); + // } else + node.setFillColor(GraphColorManager.getFillColor(activity, + colourManager)); + + // check whether the nested workflow processors should be clickable or + // not, if top level workflow then should be clickable regardless + if (depth == 0) { + node.setInteractive(true); + node.setWorkflowBean(processor); + } + if (interactive) { + node.setInteractive(true); + node.setWorkflowBean(processor); + } + + if (scufl2Tools.containsNestedWorkflow(processor, profile) + && expandNestedDataflow(activity)) { + Workflow subDataflow = scufl2Tools.nestedWorkflowForProcessor( + processor, profile); + + NamedSet<InputWorkflowPort> inputWorkflowPorts = subDataflow + .getInputPorts(); + for (InputActivityPort inputActivityPort : activity.getInputPorts()) { + InputWorkflowPort inputWorkflowPort = inputWorkflowPorts + .getByName(inputActivityPort.getName()); + InputProcessorPort inputProcessorPort = scufl2Tools + .processorPortBindingForPort(inputActivityPort, profile) + .getBoundProcessorPort(); + nestedWorkflowPorts.put(inputProcessorPort, inputWorkflowPort); + workflowPortToProcessorPort.put(inputWorkflowPort, + inputProcessorPort); + processorBinding.getInputPortBindings(); + } + + NamedSet<OutputWorkflowPort> outputWorkflowPorts = subDataflow + .getOutputPorts(); + for (OutputActivityPort outputActivityPort : activity + .getOutputPorts()) { + OutputWorkflowPort outputWorkflowPort = outputWorkflowPorts + .getByName(outputActivityPort.getName()); + OutputProcessorPort outputProcessorPort = scufl2Tools + .processorPortBindingForPort(outputActivityPort, + profile).getBoundProcessorPort(); + nestedWorkflowPorts + .put(outputProcessorPort, outputWorkflowPort); + workflowPortToProcessorPort.put(outputWorkflowPort, + outputProcessorPort); + } + + Graph subGraph = generateGraph(subDataflow, prefix, + processor.getName(), depth + 1); + // TODO why does this depth matter? + if (depth == 0) + subGraph.setWorkflowBean(processor); + if (interactive) + subGraph.setWorkflowBean(processor); + node.setGraph(subGraph); + node.setExpanded(true); + + workflowToGraph.put(processor, subGraph); + } else { + graphElementMap.put(node.getId(), node); + workflowToGraph.put(processor, node); + } + + NamedSet<InputProcessorPort> inputPorts = processor.getInputPorts(); + if (inputPorts.size() == 0) { + GraphNode portNode = createGraphNode(); + portNode.setShape(Shape.BOX); + portNode.setColor(Color.BLACK); + portNode.setFillColor(node.getFillColor()); + portNode.setLineStyle(LineStyle.SOLID); + node.addSinkNode(portNode); + } else + for (InputPort inputPort : inputPorts) { + GraphNode portNode = createGraphNode(); + portNode.setId("i" + inputPort.getName().replaceAll("\\.", "")); + portNode.setLabel(inputPort.getName()); + portNode.setShape(Shape.BOX); + portNode.setColor(Color.BLACK); + portNode.setFillColor(node.getFillColor()); + portNode.setLineStyle(LineStyle.SOLID); + if (depth == 0) + portNode.setWorkflowBean(inputPort); + if (interactive) + portNode.setWorkflowBean(inputPort); + if (!node.isExpanded()) + workflowToGraph.put(inputPort, portNode); + ports.put(inputPort, portNode); + node.addSinkNode(portNode); + graphElementMap.put(portNode.getId(), portNode); + // portToActivity.put(inputPort, activity); + portToProcessor.put(inputPort, processor); + } + + NamedSet<OutputProcessorPort> outputPorts = processor.getOutputPorts(); + if (outputPorts.size() == 0) { + GraphNode portNode = createGraphNode(); + portNode.setShape(Shape.BOX); + portNode.setColor(Color.BLACK); + portNode.setFillColor(node.getFillColor()); + portNode.setLineStyle(LineStyle.SOLID); + node.addSourceNode(portNode); + } else + for (OutputPort outputPort : outputPorts) { + GraphNode portNode = createGraphNode(); + portNode.setId("o" + outputPort.getName().replaceAll("\\.", "")); + portNode.setLabel(outputPort.getName()); + portNode.setShape(Shape.BOX); + portNode.setColor(Color.BLACK); + portNode.setFillColor(node.getFillColor()); + portNode.setLineStyle(LineStyle.SOLID); + if (depth == 0) + portNode.setWorkflowBean(outputPort); + if (interactive) + portNode.setWorkflowBean(outputPort); + if (!node.isExpanded()) + workflowToGraph.put(outputPort, portNode); + ports.put(outputPort, portNode); + node.addSourceNode(portNode); + graphElementMap.put(portNode.getId(), portNode); + // portToActivity.put(outputPort, activity); + portToProcessor.put(outputPort, processor); + } + + return node; + } + + /** + * Returns the dataflow. + * + * @return the dataflow + */ + public Workflow getWorkflow() { + return workflow; + } + + public Profile getProfile() { + return profile; + } + + /** + * Returns the dataflowSelectionModel. + * + * @return the dataflowSelectionModel + */ + public DataflowSelectionModel getDataflowSelectionModel() { + return dataflowSelectionModel; + } + + /** + * Sets the dataflowSelectionModel. + * + * @param dataflowSelectionModel + * the new dataflowSelectionModel + */ + public void setDataflowSelectionModel( + DataflowSelectionModel dataflowSelectionModel) { + if (this.dataflowSelectionModel != null) + this.dataflowSelectionModel.removeObserver(this); + this.dataflowSelectionModel = dataflowSelectionModel; + this.dataflowSelectionModel.addObserver(this); + } + + /** + * Sets the proportion of the node's jobs that have been completed. + * + * @param nodeId + * the id of the node + * @param complete + * the proportion of the nodes's jobs that have been completed, a + * value between 0.0 and 1.0 + */ + public void setNodeCompleted(String nodeId, float complete) { + if (graphElementMap.containsKey(nodeId)) { + GraphElement graphElement = graphElementMap.get(nodeId); + graphElement.setCompleted(complete); + } + } + + public void setEdgeActive(String edgeId, boolean active) { + } + + /** + * Returns the alignment. + * + * @return the alignment + */ + public Alignment getAlignment() { + return alignment; + } + + /** + * Returns the portStyle. + * + * @return the portStyle + */ + public PortStyle getPortStyle() { + return portStyle; + } + + /** + * Returns the portStyle for a processor. + * + * @return the portStyle for a processor + */ + public PortStyle getPortStyle(Processor processor) { + if (processorPortStyle.containsKey(processor)) + return processorPortStyle.get(processor); + return portStyle; + } + + /** + * Sets the alignment. + * + * @param alignment + * the new alignment + */ + public void setAlignment(Alignment alignment) { + this.alignment = alignment; + } + + /** + * Sets the portStyle. + * + * @param style + * the new portStyle + */ + public void setPortStyle(PortStyle portStyle) { + this.portStyle = portStyle; + processorPortStyle.clear(); + } + + /** + * Sets the portStyle for a processor. + * + * @param style + * the new portStyle for the processor + */ + public void setPortStyle(Processor processor, PortStyle portStyle) { + processorPortStyle.put(processor, portStyle); + } + + /** + * Shut down any processing and update threads related to this controller. + * + */ + public void shutdown() { + } + + /** + * Returns true if the default is to expand nested workflows. + * + * @return true if the default is to expand nested workflows + */ + public boolean expandNestedDataflows() { + return expandNestedDataflows; + } + + /** + * Returns true if the nested dataflow should be expanded. + * + * @param dataflow + * @return true if the nested dataflow should be expanded + */ + public boolean expandNestedDataflow(Activity dataflow) { + if (dataflowExpansion.containsKey(dataflow)) + return dataflowExpansion.get(dataflow); + return expandNestedDataflows; + } + + /** + * Sets the default for expanding nested workflows. + * + * @param expand + * the default for expanding nested workflows + */ + public void setExpandNestedDataflows(boolean expand) { + dataflowExpansion.clear(); + this.expandNestedDataflows = expand; + } + + /** + * Sets whether the nested dataflow should be expanded. + * + * @param expand + * whether the nested dataflow should be expanded + * @param dataflow + * the nested dataflow + */ + public void setExpandNestedDataflow(Activity dataflow, boolean expand) { + dataflowExpansion.put(dataflow, expand); + } + + private boolean isSingleOutputProcessor(Object dataflowObject) { + boolean result = false; + if (dataflowObject instanceof Processor) { + Processor processor = (Processor) dataflowObject; + result = processor.getOutputPorts().size() == 1; + } + return result; + } + + public boolean startEdgeCreation(GraphElement graphElement, Point point) { + if (!edgeCreationFromSource && !edgeCreationFromSink) { + Object dataflowObject = graphElement.getWorkflowBean(); + if (dataflowObject instanceof ReceiverPort) { + edgeCreationSink = graphElement; + edgeCreationFromSink = true; + } else if (dataflowObject instanceof SenderPort + || isSingleOutputProcessor(dataflowObject)) { + edgeCreationSource = graphElement; + edgeCreationFromSource = true; + } else if (graphElement instanceof GraphEdge) { + GraphEdge edge = (GraphEdge) graphElement; + edgeCreationSource = edge.getSource(); + edgeCreationFromSource = true; + edgeMoveElement = edge; + } + } + return edgeCreationFromSource || edgeCreationFromSink; + } + + public boolean moveEdgeCreationTarget(GraphElement graphElement, Point point) { + boolean edgeValid = false; + Object dataflowObject = graphElement.getWorkflowBean(); + if (edgeCreationFromSink) { + if (graphElement instanceof GraphNode) { + Object sinkObject = edgeCreationSink.getWorkflowBean(); + if (dataflowObject instanceof OutputPort) { + Processor sourceProcessor = portToProcessor + .get(dataflowObject); + if (sourceProcessor != null) { + Processor sinkProcessor = null; + if (sinkObject instanceof Processor) + sinkProcessor = (Processor) sinkObject; + else if (portToProcessor.containsKey(sinkObject)) + sinkProcessor = portToProcessor.get(sinkObject); + if (sinkProcessor != null) { + Set<Processor> possibleSinkProcessors = scufl2Tools + .possibleDownStreamProcessors(workflow, + sourceProcessor); + if (possibleSinkProcessors.contains(sinkProcessor)) { + edgeCreationSource = graphElement; + edgeValid = true; + } + } + if (sinkObject instanceof OutputWorkflowPort) { + edgeCreationSource = graphElement; + edgeValid = true; + } + } + } else if (dataflowObject instanceof InputWorkflowPort) { + edgeCreationSource = graphElement; + edgeValid = true; + } else if (dataflowObject instanceof Processor) { + Processor sourceProcessor = (Processor) dataflowObject; + Processor sinkProcessor = null; + if (sinkObject instanceof Processor) + sinkProcessor = (Processor) sinkObject; + else if (portToProcessor.containsKey(sinkObject)) + sinkProcessor = portToProcessor.get(sinkObject); + if (sinkProcessor != null) { + Set<Processor> possibleSinkProcessors = scufl2Tools + .possibleDownStreamProcessors(workflow, + sourceProcessor); + if (possibleSinkProcessors.contains(sinkProcessor)) { + edgeCreationSource = graphElement; + edgeValid = true; + } + } + if (sinkObject instanceof OutputWorkflowPort) { + edgeCreationSource = graphElement; + edgeValid = true; + } + } + } + if (!edgeValid) + edgeCreationSource = null; + } else if (edgeCreationFromSource) { + if (graphElement instanceof GraphNode) { + Object sourceObject = edgeCreationSource.getWorkflowBean(); + if (dataflowObject instanceof InputPort) { + Processor sinkProcessor = portToProcessor + .get(dataflowObject); + if (sinkProcessor != null) { + Processor sourceProcessor = null; + if (sourceObject instanceof Processor) + sourceProcessor = (Processor) sourceObject; + else if (portToProcessor.containsKey(sourceObject)) + sourceProcessor = portToProcessor.get(sourceObject); + if (sourceProcessor != null) { + Set<Processor> possibleSourceProcessors = scufl2Tools + .possibleUpStreamProcessors(workflow, + sinkProcessor); + if (possibleSourceProcessors + .contains(sourceProcessor)) { + edgeCreationSink = graphElement; + edgeValid = true; + } + } + if (sourceObject instanceof InputWorkflowPort) { + edgeCreationSink = graphElement; + edgeValid = true; + } + } + } else if (dataflowObject instanceof OutputWorkflowPort) { + if (sourceObject != null) { + edgeCreationSink = graphElement; + edgeValid = true; + } + } else if (dataflowObject instanceof Processor) { + Processor sinkProcessor = (Processor) dataflowObject; + Processor sourceProcessor = null; + if (sourceObject instanceof Processor) + sourceProcessor = (Processor) sourceObject; + else if (portToProcessor.containsKey(sourceObject)) + sourceProcessor = portToProcessor.get(sourceObject); + if (sourceProcessor != null) { + Set<Processor> possibleSourceProcessors = scufl2Tools + .possibleUpStreamProcessors(workflow, + sinkProcessor); + if (possibleSourceProcessors.contains(sourceProcessor)) { + edgeCreationSink = graphElement; + edgeValid = true; + } + } + if (sourceObject instanceof InputWorkflowPort) { + edgeCreationSink = graphElement; + edgeValid = true; + } + } + } + if (!edgeValid) + edgeCreationSink = null; + } + return edgeValid; + } + + public boolean stopEdgeCreation(GraphElement graphElement, Point point) { + boolean edgeCreated = false; + if (edgeCreationSource != null && edgeCreationSink != null) { + SenderPort source = null; + ReceiverPort sink = null; + Object sourceDataflowObject = edgeCreationSource.getWorkflowBean(); + Object sinkDataflowObject = edgeCreationSink.getWorkflowBean(); + if (sourceDataflowObject instanceof SenderPort) + source = (SenderPort) sourceDataflowObject; + else if (sourceDataflowObject instanceof Processor) { + Processor processor = (Processor) sourceDataflowObject; + source = showPortOptions(processor.getOutputPorts(), "output", + componentForPopups, point); + } + if (sinkDataflowObject instanceof ReceiverPort) + sink = (ReceiverPort) sinkDataflowObject; + else if (sinkDataflowObject instanceof Processor) { + Processor processor = (Processor) sinkDataflowObject; + sink = showPortOptions(processor.getInputPorts(), "input", + componentForPopups, point); + } + if (source != null && sink != null) { + Edit<?> edit = null; + if (edgeMoveElement == null) { + DataLink dataLink = new DataLink(); + dataLink.setReceivesFrom(source); + dataLink.setSendsTo(sink); + edit = new AddDataLinkEdit(workflow, dataLink); + } else { + Object existingSink = edgeMoveElement.getSink() + .getWorkflowBean(); + if (existingSink != sink) { + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + DataLink existingDataLink = (DataLink) edgeMoveElement + .getWorkflowBean(); + DataLink newDataLink = new DataLink(); + newDataLink.setReceivesFrom(existingDataLink + .getReceivesFrom()); + newDataLink.setSendsTo(sink); + editList.add(new RemoveDataLinkEdit(workflow, + existingDataLink)); + editList.add(new AddDataLinkEdit(workflow, newDataLink)); + edit = new CompoundEdit(editList); + } + } + try { + if (edit != null) { + editManager.doDataflowEdit(workflow.getParent(), edit); + edgeCreated = true; + } + } catch (EditException e) { + logger.debug("Failed to create datalink from '" + + source.getName() + "' to '" + sink.getName() + + "'"); + } + } + } + edgeCreationSource = null; + edgeCreationSink = null; + edgeMoveElement = null; + edgeCreationFromSource = false; + edgeCreationFromSink = false; + + return edgeCreated; + } + + private <T extends Port> T showPortOptions(NamedSet<T> ports, + String portType, Component component, Point point) { + T result = null; + if (ports.size() == 0) { + showMessageDialog(component, "Service has no " + portType + + " ports to connect to"); + } else if (ports.size() == 1) + result = ports.first(); + else { + Object[] portNames = ports.getNames().toArray(); + String portName = (String) showInputDialog(component, "Select an " + + portType + " port", "Port Chooser", PLAIN_MESSAGE, null, + portNames, portNames[0]); + if (portName != null) + result = ports.getByName(portName); + } + return result; + + } + + public void resetSelection() { + if (dataflowSelectionModel != null) + for (Object dataflowElement : dataflowSelectionModel.getSelection()) { + GraphElement graphElement = workflowToGraph + .get(dataflowElement); + if (graphElement != null) + graphElement.setSelected(true); + } + } + + public void setIteration(String nodeId, int iteration) { + if (graphElementMap.containsKey(nodeId)) { + GraphElement graphElement = graphElementMap.get(nodeId); + graphElement.setIteration(iteration); + } + } + + public void setErrors(String nodeId, int errors) { + if (graphElementMap.containsKey(nodeId)) { + GraphElement graphElement = graphElementMap.get(nodeId); + graphElement.setErrors(errors); + } + } + + @Override + public void notify(Observable<DataflowSelectionMessage> sender, + DataflowSelectionMessage message) throws Exception { + GraphElement graphElement = workflowToGraph.get(message.getElement()); + if (graphElement != null) + graphElement.setSelected(message.getType().equals( + DataflowSelectionMessage.Type.ADDED)); + } + + /** + * Returns the GraphEventManager. + * + * @return the GraphEventManager + */ + public GraphEventManager getGraphEventManager() { + return graphEventManager; + } + + /** + * Sets the GraphEventManager. + * + * @param graphEventManager + * the new GraphEventManager + */ + public void setGraphEventManager(GraphEventManager graphEventManager) { + this.graphEventManager = graphEventManager; + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEdge.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEdge.java new file mode 100644 index 0000000..d1348d2 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEdge.java
@@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import java.awt.Point; +import java.util.List; + +/** + * An edge connecting two nodes in a graph. + * + * @author David Withers + */ +public class GraphEdge extends GraphElement { + public enum ArrowStyle {NONE, NORMAL, DOT} + + private GraphNode source; + private GraphNode sink; + private ArrowStyle arrowHeadStyle = ArrowStyle.NORMAL; + private ArrowStyle arrowTailStyle = ArrowStyle.NONE; + private List<Point> path; + + /** + * Constructs a new instance of Edge. + * + */ + public GraphEdge(GraphController graphController) { + super(graphController); + } + + /** + * Returns the source. + * + * @return the source + */ + public GraphNode getSource() { + return source; + } + + /** + * Sets the source. + * + * @param source the new source + */ + public void setSource(GraphNode source) { + this.source = source; + } + + /** + * Returns the sink. + * + * @return the sink + */ + public GraphNode getSink() { + return sink; + } + + /** + * Sets the sink. + * + * @param sink the new sink + */ + public void setSink(GraphNode sink) { + this.sink = sink; + } + + /** + * Returns the arrowHeadStyle. + * + * @return the arrowHeadStyle + */ + public ArrowStyle getArrowHeadStyle() { + return arrowHeadStyle; + } + + /** + * Sets the arrowHeadStyle. + * + * @param arrowHeadStyle the new arrowHeadStyle + */ + public void setArrowHeadStyle(ArrowStyle arrowHeadStyle) { + this.arrowHeadStyle = arrowHeadStyle; + } + + /** + * Returns the arrowTailStyle. + * + * @return the arrowTailStyle + */ + public ArrowStyle getArrowTailStyle() { + return arrowTailStyle; + } + + /** + * Sets the arrowTailStyle. + * + * @param arrowTailStyle the new arrowTailStyle + */ + public void setArrowTailStyle(ArrowStyle arrowTailStyle) { + this.arrowTailStyle = arrowTailStyle; + } + + /** + * Returns the path. + * + * @return the path + */ + public List<Point> getPath() { + return path; + } + + /** + * Sets the path. + * + * @param path the new path + */ + public void setPath(List<Point> path) { + this.path = path; + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphElement.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphElement.java new file mode 100644 index 0000000..8bb7bc8 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphElement.java
@@ -0,0 +1,430 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import java.awt.Color; +import java.awt.Point; + +import uk.org.taverna.scufl2.api.common.WorkflowBean; + +/** + * An element of a graph. + * + * @author David Withers + */ +public abstract class GraphElement { + public enum LineStyle { + NONE, SOLID, DOTTED + } + + private String id; + private String label; + private Point labelPosition; + private LineStyle lineStyle = LineStyle.SOLID; + private Color color = Color.BLACK; + private Color fillColor; + private float opacity = 1f; + private GraphElement parent; + private boolean selected; + private boolean active; + private boolean interactive; + private boolean visible = true; + private boolean filtered; + private WorkflowBean workflowBean; + protected GraphController graphController; + protected float completed; + protected int iteration; + protected int errors; + + protected GraphElement(GraphController graphController) { + this.graphController = graphController; + } + + /** + * Returns the eventManager. + * + * @return the eventManager + */ + public GraphEventManager getEventManager() { + if (graphController != null) { + return graphController.getGraphEventManager(); + } + return null; + } + + /** + * Returns the workflowBean. + * + * @return the workflowBean + */ + public WorkflowBean getWorkflowBean() { + return workflowBean; + } + + /** + * Sets the workflowBean. + * + * @param workflowBean + * the new workflowBean + */ + public void setWorkflowBean(WorkflowBean workflowBean) { + this.workflowBean = workflowBean; + } + + /** + * Returns the parent. + * + * @return the parent + */ + public GraphElement getParent() { + return parent; + } + + /** + * Sets the parent. + * + * @param parent + * the new parent + */ + protected void setParent(GraphElement parent) { + this.parent = parent; + } + + /** + * Returns the label. + * + * @return the label + */ + public String getLabel() { + return label; + } + + /** + * Sets the label. + * + * @param label + * the new label + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * Returns the labelPosition. + * + * @return the labelPosition + */ + public Point getLabelPosition() { + return labelPosition; + } + + /** + * Sets the labelPosition. + * + * @param labelPosition + * the new labelPosition + */ + public void setLabelPosition(Point labelPosition) { + this.labelPosition = labelPosition; + } + + /** + * Returns the id. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Sets the id. + * + * @param id + * the new id + */ + public void setId(String id) { + if (graphController != null) { + graphController.mapElement(id, this); + } + this.id = id; + } + + /** + * Returns the colour. + * + * @return the colour + */ + public Color getColor() { + return color; + } + + /** + * Sets the colour. + * + * @param color + * the new colour + */ + public void setColor(Color color) { + this.color = color; + } + + /** + * Returns the fillColor. + * + * @return the fillColor + */ + public Color getFillColor() { + return fillColor; + } + + /** + * Sets the fillColor. + * + * @param fillColor + * the new fillColor + */ + public void setFillColor(Color fillColor) { + this.fillColor = fillColor; + } + + /** + * Returns the lineStyle. + * + * @return the lineStyle + */ + public LineStyle getLineStyle() { + return lineStyle; + } + + /** + * Sets the lineStyle. + * + * @param lineStyle + * the new lineStyle + */ + public void setLineStyle(LineStyle lineStyle) { + this.lineStyle = lineStyle; + } + + @Override + public String toString() { + return id + "[" + label + "]"; + } + + /** + * Returns the selected. + * + * @return the selected + */ + public boolean isSelected() { + return selected; + } + + /** + * Sets the selected. + * + * @param selected + * the new selected + */ + public void setSelected(boolean selected) { + this.selected = selected; + } + + /** + * Returns the iteration. + * + * @return the value of iteration + */ + public int getIteration() { + return iteration; + } + + /** + * Sets the iteration. + * + * @param iteration + * the new value for iteration + */ + public void setIteration(int iteration) { + this.iteration = iteration; + } + + /** + * Returns the errors. + * + * @return the value of errors + */ + public int getErrors() { + return errors; + } + + /** + * Sets the errors. + * + * @param errors + * the new value for errors + */ + public void setErrors(int errors) { + this.errors = errors; + } + + /** + * Returns the completed. + * + * @return the value of completed + */ + public float getCompleted() { + return completed; + } + + /** + * Sets the completed value. + * + * @param completed + */ + public void setCompleted(float completed) { + this.completed = completed; + } + + /** + * Returns <code>true</code> if the element is active. The default value is + * <code>false</code>. + * + * @return <code>true</code> if the element is active + */ + public boolean isActive() { + return active; + } + + /** + * Sets the value of active. + * + * @param active + * the new active + */ + public void setActive(boolean active) { + this.active = active; + } + + /** + * Returns <code>true</code> if the element is interactive. The default + * value is <code>false</code>. + * + * @return <code>true</code> if the element is interactive + */ + public boolean isInteractive() { + return interactive; + } + + /** + * Sets the value of interactive. + * + * @param interactive + * the new interactive + */ + public void setInteractive(boolean interactive) { + this.interactive = interactive; + } + + /** + * Returns <code>true</code> if the element is visible. The default value is + * <code>true</code>. + * + * @return <code>true</code> if the element is visible + */ + public boolean isVisible() { + return visible; + } + + /** + * Sets whether the element is visible. + * + * @param visible + * the new value for visible + */ + public void setVisible(boolean visible) { + this.visible = visible; + } + + /** + * Returns the opacity value. The default value is 1.0 + * + * @return the opacity value + */ + public float getOpacity() { + return opacity; + } + + /** + * Sets the opacity of the element. Must be a value between 0.0 and 1.0. + * + * @param opacity + * the new opacity value + */ + public void setOpacity(float opacity) { + this.opacity = opacity; + } + + /** + * Returns <code>true</code> if the element is filtered. + * + * @return <code>true</code> if the element is filtered + */ + public boolean isFiltered() { + return filtered; + } + + /** + * Sets the value of filtered. + * + * @param filtered + * the new value for filtered + */ + public void setFiltered(boolean filtered) { + this.filtered = filtered; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + // Equality by id + GraphElement other = (GraphElement) obj; + if (id == null) + return (other.id == null); + return id.equals(other.id); + } + +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEventManager.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEventManager.java new file mode 100644 index 0000000..a6b9b0e --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphEventManager.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +public interface GraphEventManager { + void mouseClicked(GraphElement graphElement, short button, boolean altKey, + boolean ctrlKey, boolean metaKey, int x, int y, int screenX, + int screenY); + + void mouseDown(GraphElement graphElement, short button, boolean altKey, + boolean ctrlKey, boolean metaKey, int x, int y, int screenX, + int screenY); + + void mouseUp(GraphElement graphElement, short button, boolean altKey, + boolean ctrlKey, boolean metaKey, final int x, final int y, + int screenX, int screenY); + + void mouseMoved(GraphElement graphElement, short button, boolean altKey, + boolean ctrlKey, boolean metaKey, int x, int y, int screenX, + int screenY); + + void mouseOver(GraphElement graphElement, short button, boolean altKey, + boolean ctrlKey, boolean metaKey, int x, int y, int screenX, + int screenY); + + void mouseOut(GraphElement graphElement, short button, boolean altKey, + boolean ctrlKey, boolean metaKey, int x, int y, int screenX, + int screenY); +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphNode.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphNode.java new file mode 100644 index 0000000..3f3f85f --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphNode.java
@@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import java.util.ArrayList; +import java.util.List; + +/** + * A node of a graph that can optionally contain other graphs. + * + * @author David Withers + */ +public class GraphNode extends GraphShapeElement { + private List<GraphNode> sourceNodes = new ArrayList<>(); + private List<GraphNode> sinkNodes = new ArrayList<>(); + private Graph graph; + private boolean expanded; + + /** + * Constructs a new instance of Node. + * + */ + public GraphNode(GraphController graphController) { + super(graphController); + } + + /** + * Adds a sink node. + * + * @param sinkNode + * the sink node to add + */ + public void addSinkNode(GraphNode sinkNode) { + sinkNode.setParent(this); + sinkNodes.add(sinkNode); + } + + /** + * Adds a source node. + * + * @param sourceNode + * the source node to add + */ + public void addSourceNode(GraphNode sourceNode) { + sourceNode.setParent(this); + sourceNodes.add(sourceNode); + } + + /** + * Returns the graph that this node contains. + * + * @return the graph that this node contains + */ + public Graph getGraph() { + return graph; + } + + /** + * Returns the sinkNodes. + * + * @return the sinkNodes + */ + public List<GraphNode> getSinkNodes() { + return sinkNodes; + } + + /** + * Returns the sourceNodes. + * + * @return the sourceNodes + */ + public List<GraphNode> getSourceNodes() { + return sourceNodes; + } + + /** + * Returns true if this node is expanded to show the contained graph. + * + * @return true if this node is expanded + */ + public boolean isExpanded() { + return expanded; + } + + /** + * Removes a sink node. + * + * @param sinkNode + * the node to remove + * @return true if the node was removed, false otherwise + */ + public boolean removeSinkNode(GraphNode sinkNode) { + return sinkNodes.remove(sinkNode); + } + + /** + * Removes a source node. + * + * @param sourceNode + * the node to remove + * @return true if the node was removed, false otherwise + */ + public boolean removeSourceNode(GraphNode sourceNode) { + return sourceNodes.remove(sourceNode); + } + + /** + * Sets whether this node is expanded to show the contained graph. + * + * @param expanded + * true if this node is expanded + */ + public void setExpanded(boolean expanded) { + this.expanded = expanded; + } + + /** + * Sets the graph that this node contains. + * + * @param graph + * the new graph + */ + public void setGraph(Graph graph) { + if (graph != null) + graph.setParent(this); + this.graph = graph; + } + + @Override + public void setSelected(boolean selected) { + super.setSelected(selected); + if (isExpanded()) + getGraph().setSelected(selected); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphShapeElement.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphShapeElement.java new file mode 100644 index 0000000..1bb8b6d --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/GraphShapeElement.java
@@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import java.awt.Dimension; +import java.awt.Point; + +/** + * A Graph element that has shape, size and position properties. + * + * @author David Withers + */ +public class GraphShapeElement extends GraphElement { + public enum Shape { + BOX, RECORD, HOUSE, INVHOUSE, DOT, CIRCLE, TRIANGLE, INVTRIANGLE + } + + private Shape shape; + private int x, y, width, height; + + public GraphShapeElement(GraphController graphController) { + super(graphController); + } + + /** + * Returns the height. + * + * @return the height + */ + public int getHeight() { + return height; + } + + /** + * Returns the position. + * + * @return the position + */ + public Point getPosition() { + return new Point(x, y); + } + + /** + * Returns the shape of the element. + * + * @return the shape of the element + */ + public Shape getShape() { + return shape; + } + + /** + * Returns the width. + * + * @return the width + */ + public int getWidth() { + return width; + } + + /** + * Sets the position. + * + * @param position + * the new position + */ + public void setPosition(Point position) { + x = position.x; + y = position.y; + } + + /** + * Sets the shape of the element. + * + * @param shape + * the new shape of the element + */ + public void setShape(Shape shape) { + this.shape = shape; + } + + /** + * Returns the size of the element. + * + * @return the size of the element + */ + public Dimension getSize() { + return new Dimension(width, height); + } + + /** + * Sets the size of the element. + * + * @param size + * the new size of the node + */ + public void setSize(Dimension size) { + width = size.width; + height = size.height; + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/dot/GraphLayout.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/dot/GraphLayout.java new file mode 100644 index 0000000..1205953 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/dot/GraphLayout.java
@@ -0,0 +1,326 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.dot; + +import static java.lang.Float.parseFloat; +import static net.sf.taverna.t2.workbench.models.graph.Graph.Alignment.HORIZONTAL; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import net.sf.taverna.t2.workbench.models.graph.Graph; +import net.sf.taverna.t2.workbench.models.graph.GraphController; +import net.sf.taverna.t2.workbench.models.graph.GraphEdge; +import net.sf.taverna.t2.workbench.models.graph.GraphElement; +import net.sf.taverna.t2.workbench.models.graph.GraphNode; + +import org.apache.log4j.Logger; + +/** + * Lays out a graph from a DOT layout. + * + * @author David Withers + */ +public class GraphLayout implements DOTParserVisitor { + private static final Logger logger = Logger.getLogger(GraphLayout.class); + private static final int BORDER = 10; + + private Rectangle bounds; + private Rectangle requiredBounds; + private GraphController graphController; + private int xOffset; + private int yOffset; + + public Rectangle layoutGraph(GraphController graphController, Graph graph, + String laidOutDot, Rectangle requiredBounds) throws ParseException { + this.graphController = graphController; + this.requiredBounds = requiredBounds; + + bounds = null; + xOffset = 0; + yOffset = 0; + + logger.debug(laidOutDot); + DOTParser parser = new DOTParser(new StringReader(laidOutDot)); + parser.parse().jjtAccept(this, graph); + + // int xOffset = (bounds.width - bounds.width) / 2; + // int yOffset = (bounds.height - bounds.height) / 2; + + return new Rectangle(xOffset, yOffset, bounds.width, bounds.height); + } + + @Override + public Object visit(SimpleNode node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTParse node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTGraph node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTStatementList node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTStatement node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTAttributeStatement node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTNodeStatement node, Object data) { + GraphElement element = graphController.getElement(removeQuotes(node + .getName())); + if (element != null) + return node.childrenAccept(this, element); + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTNodeId node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTPort node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTEdgeStatement node, Object data) { + StringBuilder id = new StringBuilder(); + id.append(removeQuotes(node.getName())); + if (node.getPort() != null) { + id.append(":"); + id.append(removeQuotes(node.getPort())); + } + if (node.children != null) + for (Node child : node.children) + if (child instanceof ASTEdgeRHS) { + NamedNode rhsNode = (NamedNode) child.jjtAccept(this, data); + id.append("->"); + id.append(removeQuotes(rhsNode.getName())); + if (rhsNode.getPort() != null) { + id.append(":"); + id.append(removeQuotes(rhsNode.getPort())); + } + } + GraphElement element = graphController.getElement(id.toString()); + if (element != null) + return node.childrenAccept(this, element); + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTSubgraph node, Object data) { + GraphElement element = graphController.getElement(removeQuotes( + node.getName()).substring("cluster_".length())); + if (element != null) + return node.childrenAccept(this, element); + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTEdgeRHS node, Object data) { + return node; + } + + @Override + public Object visit(ASTAttributeList node, Object data) { + return node.childrenAccept(this, data); + } + + @Override + public Object visit(ASTAList node, Object data) { + if (data instanceof Graph) { + Graph graph = (Graph) data; + if ("bb".equalsIgnoreCase(node.getName())) { + Rectangle rect = getRectangle(node.getValue()); + if (rect.width == 0 && rect.height == 0) { + rect.width = 500; + rect.height = 500; + } + if (bounds == null) { + bounds = calculateBounds(rect); + rect = bounds; + } + graph.setSize(rect.getSize()); + graph.setPosition(rect.getLocation()); + } else if ("lp".equalsIgnoreCase(node.getName())) { + if (bounds != null) + graph.setLabelPosition(getPoint(node.getValue())); + } + } else if (data instanceof GraphNode) { + GraphNode graphNode = (GraphNode) data; + if ("width".equalsIgnoreCase(node.getName())) + graphNode.setSize(new Dimension(getSize(node.getValue()), + graphNode.getHeight())); + else if ("height".equalsIgnoreCase(node.getName())) + graphNode.setSize(new Dimension(graphNode.getWidth(), + getSize(node.getValue()))); + else if ("pos".equalsIgnoreCase(node.getName())) { + Point position = getPoint(node.getValue()); + position.x = position.x - (graphNode.getWidth() / 2); + position.y = position.y - (graphNode.getHeight() / 2); + graphNode.setPosition(position); + } else if ("rects".equalsIgnoreCase(node.getName())) { + List<Rectangle> rectangles = getRectangles(node.getValue()); + List<GraphNode> sinkNodes = graphNode.getSinkNodes(); + if (graphController.getAlignment().equals(HORIZONTAL)) { + Rectangle rect = rectangles.remove(0); + graphNode.setSize(rect.getSize()); + graphNode.setPosition(rect.getLocation()); + } else { + Rectangle rect = rectangles.remove(sinkNodes.size()); + graphNode.setSize(rect.getSize()); + graphNode.setPosition(rect.getLocation()); + } + Point origin = graphNode.getPosition(); + for (GraphNode sinkNode : sinkNodes) { + Rectangle rect = rectangles.remove(0); + rect.setLocation(rect.x - origin.x, rect.y - origin.y); + sinkNode.setSize(rect.getSize()); + sinkNode.setPosition(rect.getLocation()); + } + for (GraphNode sourceNode : graphNode.getSourceNodes()) { + Rectangle rect = rectangles.remove(0); + rect.setLocation(rect.x - origin.x, rect.y - origin.y); + sourceNode.setSize(rect.getSize()); + sourceNode.setPosition(rect.getLocation()); + } + } + } else if (data instanceof GraphEdge) { + GraphEdge graphEdge = (GraphEdge) data; + if ("pos".equalsIgnoreCase(node.getName())) + graphEdge.setPath(getPath(node.getValue())); + } + return node.childrenAccept(this, data); + } + + private Rectangle calculateBounds(Rectangle bounds) { + bounds = new Rectangle(bounds); + bounds.width += BORDER; + bounds.height += BORDER; + Rectangle newBounds = new Rectangle(bounds); + double ratio = bounds.width / (float) bounds.height; + double requiredRatio = requiredBounds.width + / (float) requiredBounds.height; + // adjust the bounds so they match the aspect ration of the required bounds + if (ratio > requiredRatio) + newBounds.height = (int) (ratio / requiredRatio * bounds.height); + else if (ratio < requiredRatio) + newBounds.width = (int) (requiredRatio / ratio * bounds.width); + + xOffset = (newBounds.width - bounds.width) / 2; + yOffset = (newBounds.height - bounds.height) / 2; + // adjust the bounds and so they are not less than the required bounds + if (newBounds.width < requiredBounds.width) { + xOffset += (requiredBounds.width - newBounds.width) / 2; + newBounds.width = requiredBounds.width; + } + if (newBounds.height < requiredBounds.height) { + yOffset += (requiredBounds.height - newBounds.height) / 2; + newBounds.height = requiredBounds.height; + } + // adjust the offset for the border + xOffset += BORDER / 2; + yOffset += BORDER / 2; + return newBounds; + } + + private List<Point> getPath(String value) { + List<Point> path = new ArrayList<>(); + for (String point : removeQuotes(value).split(" ")) { + String[] coords = point.split(","); + if (coords.length == 2) { + int x = (int) parseFloat(coords[0]) + xOffset; + int y = (int) parseFloat(coords[1]) + yOffset; + path.add(new Point(x, flipY(y))); + } + } + return path; + } + + private int flipY(int y) { + return bounds.height - y; + } + + private List<Rectangle> getRectangles(String value) { + List<Rectangle> rectangles = new ArrayList<>(); + String[] rects = value.split(" "); + for (String rectangle : rects) + rectangles.add(getRectangle(rectangle)); + return rectangles; + } + + private Rectangle getRectangle(String value) { + String[] coords = removeQuotes(value).split(","); + Rectangle rectangle = new Rectangle(); + rectangle.x = (int) parseFloat(coords[0]); + rectangle.y = (int) parseFloat(coords[3]); + rectangle.width = (int) parseFloat(coords[2]) - rectangle.x; + rectangle.height = rectangle.y - (int) parseFloat(coords[1]); + rectangle.x += xOffset; + rectangle.y += yOffset; + if (bounds != null) + rectangle.y = flipY(rectangle.y); + else + rectangle.y = rectangle.height - rectangle.y; + return rectangle; + } + + private Point getPoint(String value) { + String[] coords = removeQuotes(value).split(","); + return new Point(xOffset + (int) parseFloat(coords[0]), flipY(yOffset + + (int) parseFloat(coords[1]))); + } + + private int getSize(String value) { + return (int) (parseFloat(removeQuotes(value)) * 72); + } + + private String removeQuotes(String value) { + String result = value.trim(); + if (result.startsWith("\"")) + result = result.substring(1); + if (result.endsWith("\"")) + result = result.substring(0, result.length() - 1); + result = result.replaceAll("\\\\", ""); + return result; + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraph.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraph.java new file mode 100644 index 0000000..be92cdd --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraph.java
@@ -0,0 +1,439 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.COMPLETED_COLOUR; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculatePoints; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement; +import static org.apache.batik.util.CSSConstants.CSS_BLACK_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_NONE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TRANSFORM_TAG; +import static org.apache.batik.util.SVGConstants.SVG_CLICK_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_END_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FONT_SIZE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_MIDDLE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_MOUSEMOVE_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_MOUSEUP_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_NONE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_POINTS_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_TRANSFORM_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.TRANSFORM_TRANSLATE; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; + +import net.sf.taverna.t2.workbench.models.graph.Graph; +import net.sf.taverna.t2.workbench.models.graph.GraphEdge; +import net.sf.taverna.t2.workbench.models.graph.GraphNode; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseClickEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseMovedEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOutEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOverEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseUpEventListener; + +import org.apache.batik.dom.svg.SVGOMAnimationElement; +import org.apache.batik.dom.svg.SVGOMGElement; +import org.apache.batik.dom.svg.SVGOMPolygonElement; +import org.apache.batik.dom.svg.SVGOMTextElement; +import org.w3c.dom.Text; +import org.w3c.dom.events.EventTarget; +import org.w3c.dom.svg.SVGElement; + +/** + * SVG representation of a graph. + * + * @author David Withers + */ +public class SVGGraph extends Graph { + private SVGGraphController graphController; + private SVGGraphElementDelegate delegate; + private SVGMouseClickEventListener mouseClickAction; + private SVGMouseMovedEventListener mouseMovedAction; + private SVGMouseUpEventListener mouseUpAction; + @SuppressWarnings("unused") + private SVGMouseOverEventListener mouseOverAction; + @SuppressWarnings("unused") + private SVGMouseOutEventListener mouseOutAction; + private SVGOMGElement mainGroup, labelGroup; + private SVGOMPolygonElement polygon, completedPolygon; + private SVGOMTextElement label, iteration, error; + private Text labelText, iterationText, errorsText; + private SVGOMAnimationElement animateShape, animatePosition, animateLabel; + + public SVGGraph(SVGGraphController graphController) { + super(graphController); + this.graphController = graphController; + + mouseClickAction = new SVGMouseClickEventListener(this); + mouseMovedAction = new SVGMouseMovedEventListener(this); + mouseUpAction = new SVGMouseUpEventListener(this); + mouseOverAction = new SVGMouseOverEventListener(this); + mouseOutAction = new SVGMouseOutEventListener(this); + + mainGroup = graphController.createGElem(); + mainGroup.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "10"); + mainGroup.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "Helvetica"); + mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, CSS_BLACK_VALUE); + mainGroup.setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, CSS_NONE_VALUE); + mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1"); + mainGroup.setAttribute(SVG_FILL_ATTRIBUTE, CSS_NONE_VALUE); + + EventTarget t = (EventTarget) mainGroup; + t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false); + t.addEventListener(SVG_MOUSEMOVE_EVENT_TYPE, mouseMovedAction, false); + t.addEventListener(SVG_MOUSEUP_EVENT_TYPE, mouseUpAction, false); + // t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false); + // t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false); + + polygon = graphController.createPolygon(); + mainGroup.appendChild(polygon); + + completedPolygon = graphController.createPolygon(); + completedPolygon.setAttribute(SVG_POINTS_ATTRIBUTE, + calculatePoints(getShape(), 0, 0)); + completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE, COMPLETED_COLOUR); + // completedPolygon.setAttribute(SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE, "0.8"); + // completedPolygon.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE); + mainGroup.appendChild(completedPolygon); + + labelText = graphController.createText(""); + label = graphController.createText(labelText); + label.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_MIDDLE_VALUE); + label.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE); + label.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE); + labelGroup = graphController.createGElem(); + labelGroup.appendChild(label); + mainGroup.appendChild(labelGroup); + + iterationText = graphController.createText(""); + iteration = graphController.createText(iterationText); + iteration.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_END_VALUE); + iteration.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "6"); + iteration.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif"); + iteration.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE); + iteration.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE); + polygon.appendChild(iteration); + + errorsText = graphController.createText(""); + error = graphController.createText(errorsText); + error.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_END_VALUE); + error.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "6"); + error.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif"); + error.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE); + error.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE); + polygon.appendChild(error); + + animateShape = createAnimationElement(graphController, SVG_ANIMATE_TAG, + SVG_POINTS_ATTRIBUTE, null); + + animatePosition = createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_TRANSLATE); + + animateLabel = createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_TRANSLATE); + + delegate = new SVGGraphElementDelegate(graphController, this, mainGroup); + } + + public SVGElement getSVGElement() { + return mainGroup; + } + + @Override + public void addEdge(GraphEdge edge) { + if (edge instanceof SVGGraphEdge) { + final SVGGraphEdge svgGraphEdge = (SVGGraphEdge) edge; + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + float opacity = svgGraphEdge.getOpacity(); + svgGraphEdge.setOpacity(0); + mainGroup.appendChild(svgGraphEdge.getSVGElement()); + svgGraphEdge.setOpacity(opacity); + } + }); + } + super.addEdge(edge); + } + + @Override + public void addNode(GraphNode node) { + super.addNode(node); + if (node instanceof SVGGraphNode) { + final SVGGraphNode svgGraphNode = (SVGGraphNode) node; + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + float opacity = svgGraphNode.getOpacity(); + svgGraphNode.setOpacity(0); + mainGroup.appendChild(svgGraphNode.getSVGElement()); + svgGraphNode.setOpacity(opacity); + } + }); + } + } + + @Override + public void addSubgraph(Graph subgraph) { + super.addSubgraph(subgraph); + if (subgraph instanceof SVGGraph) { + final SVGGraph svgGraph = (SVGGraph) subgraph; + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + float opacity = svgGraph.getOpacity(); + svgGraph.setOpacity(0); + mainGroup.appendChild(svgGraph.getSVGElement()); + svgGraph.setOpacity(opacity); + } + }); + } + } + + @Override + public boolean removeEdge(GraphEdge edge) { + if (edge instanceof SVGGraphEdge) { + final SVGGraphEdge svgGraphEdge = (SVGGraphEdge) edge; + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.removeChild(svgGraphEdge.getSVGElement()); + } + }); + } + return super.removeEdge(edge); + } + + @Override + public boolean removeNode(GraphNode node) { + if (node instanceof SVGGraphNode) { + final SVGGraphNode svgGraphNode = (SVGGraphNode) node; + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.removeChild(svgGraphNode.getSVGElement()); + } + }); + } + return super.removeNode(node); + } + + @Override + public boolean removeSubgraph(Graph subgraph) { + if (subgraph instanceof SVGGraph) { + final SVGGraph svgGraph = (SVGGraph) subgraph; + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.removeChild(svgGraph.getSVGElement()); + } + }); + } + return super.removeSubgraph(subgraph); + } + + @Override + public void setPosition(final Point position) { + final Point oldPosition = getPosition(); + if (position != null && !position.equals(oldPosition)) { + super.setPosition(position); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (graphController.isAnimatable()) + animate(animatePosition, polygon, + graphController.getAnimationSpeed(), + oldPosition.x + ", " + oldPosition.y, + position.x + ", " + position.y); + else + polygon.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + position.x + " " + position.y + + ")"); + } + }); + } + } + + @Override + public void setSize(final Dimension size) { + final Dimension oldSize = getSize(); + if (size != null && !size.equals(oldSize)) { + super.setSize(size); + updateShape(oldSize.width, oldSize.height); + } + } + + @Override + public void setShape(Shape shape) { + final Dimension oldSize = getSize(); + final Shape currentShape = getShape(); + if (shape != null && !shape.equals(currentShape)) { + super.setShape(shape); + updateShape(oldSize.width, oldSize.height); + } + } + + @Override + public void setLabel(final String label) { + super.setLabel(label); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + labelText.setData(label); + } + }); + } + + @Override + public void setLabelPosition(final Point labelPosition) { + final Point oldLabelPosition = getLabelPosition(); + if (labelPosition != null && !labelPosition.equals(oldLabelPosition)) { + super.setLabelPosition(labelPosition); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (graphController.isAnimatable() + && oldLabelPosition != null) + animate(animateLabel, labelGroup, + graphController.getAnimationSpeed(), + oldLabelPosition.x + ", " + oldLabelPosition.y, + labelPosition.x + ", " + labelPosition.y); + else + labelGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + labelPosition.x + " " + + labelPosition.y + ")"); + } + }); + } + } + + @Override + public void setIteration(final int iteration) { + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (iteration > 0) + iterationText.setData(String.valueOf(iteration)); + else + iterationText.setData(""); + } + }); + } + + @Override + public void setCompleted(final float complete) { + super.setCompleted(complete); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + Dimension size = getSize(); + Point position = getPosition(); + completedPolygon.setAttribute( + SVG_POINTS_ATTRIBUTE, + calculatePoints(getShape(), + (int) (size.width * complete), size.height)); + completedPolygon.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + position.x + " " + position.y + ")"); + } + }); + } + + private void updateShape(final int oldWidth, final int oldHeight) { + if (getShape() != null && getWidth() > 0f && getHeight() > 0f) { + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (graphController.isAnimatable()) + animate(animateShape, + polygon, + graphController.getAnimationSpeed(), + calculatePoints(getShape(), oldWidth, oldHeight), + calculatePoints(getShape(), getWidth(), + getHeight())); + else { + polygon.setAttribute( + SVG_POINTS_ATTRIBUTE, + calculatePoints(getShape(), getWidth(), + getHeight())); + iteration.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + (getWidth() - 1.5) + " 5.5)"); + error.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + (getWidth() - 1.5) + " " + + (getHeight() - 1) + ")"); + } + } + }); + } + } + + @Override + public void setSelected(final boolean selected) { + delegate.setSelected(selected); + super.setSelected(selected); + } + + @Override + public void setLineStyle(final LineStyle lineStyle) { + delegate.setLineStyle(lineStyle); + super.setLineStyle(lineStyle); + } + + @Override + public void setColor(final Color color) { + delegate.setColor(color); + super.setColor(color); + } + + @Override + public void setFillColor(final Color fillColor) { + delegate.setFillColor(fillColor); + super.setFillColor(fillColor); + } + + @Override + public void setVisible(final boolean visible) { + delegate.setVisible(visible); + super.setVisible(visible); + } + + @Override + public void setFiltered(final boolean filtered) { + delegate.setFiltered(filtered); + super.setFiltered(filtered); + } + + @Override + public void setOpacity(final float opacity) { + delegate.setOpacity(opacity); + super.setOpacity(opacity); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphController.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphController.java new file mode 100644 index 0000000..a4c8e4c --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphController.java
@@ -0,0 +1,555 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +import static java.awt.Color.BLACK; +import static java.awt.Color.GREEN; +import static java.lang.Float.parseFloat; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculateAngle; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createSVGDocument; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getDot; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getHexValue; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.svgNS; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG; +import static org.apache.batik.util.SVGConstants.SVG_ELLIPSE_TAG; +import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FONT_SIZE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_G_TAG; +import static org.apache.batik.util.SVGConstants.SVG_LINE_TAG; +import static org.apache.batik.util.SVGConstants.SVG_PATH_TAG; +import static org.apache.batik.util.SVGConstants.SVG_POINTS_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_POLYGON_TAG; +import static org.apache.batik.util.SVGConstants.SVG_RECT_TAG; +import static org.apache.batik.util.SVGConstants.SVG_STYLE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_TEXT_TAG; +import static org.apache.batik.util.SVGConstants.SVG_TRANSFORM_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_VIEW_BOX_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_X1_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_X2_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_Y1_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_Y2_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_Y_ATTRIBUTE; + +import java.awt.Color; +import java.awt.Point; +import java.awt.Rectangle; +import java.io.IOException; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.models.graph.DotWriter; +import net.sf.taverna.t2.workbench.models.graph.Graph; +import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment; +import net.sf.taverna.t2.workbench.models.graph.GraphController; +import net.sf.taverna.t2.workbench.models.graph.GraphEdge; +import net.sf.taverna.t2.workbench.models.graph.GraphElement; +import net.sf.taverna.t2.workbench.models.graph.GraphNode; +import net.sf.taverna.t2.workbench.models.graph.dot.GraphLayout; +import net.sf.taverna.t2.workbench.models.graph.dot.ParseException; + +import org.apache.batik.bridge.UpdateManager; +import org.apache.batik.dom.svg.SVGOMAnimationElement; +import org.apache.batik.dom.svg.SVGOMEllipseElement; +import org.apache.batik.dom.svg.SVGOMGElement; +import org.apache.batik.dom.svg.SVGOMPathElement; +import org.apache.batik.dom.svg.SVGOMPolygonElement; +import org.apache.batik.dom.svg.SVGOMRectElement; +import org.apache.batik.dom.svg.SVGOMTextElement; +import org.apache.batik.swing.JSVGCanvas; +import org.apache.batik.swing.gvt.GVTTreeRendererAdapter; +import org.apache.batik.swing.gvt.GVTTreeRendererEvent; +import org.apache.log4j.Logger; +import org.w3c.dom.Element; +import org.w3c.dom.Text; +import org.w3c.dom.svg.SVGDocument; +import org.w3c.dom.svg.SVGElement; +import org.w3c.dom.svg.SVGPoint; +import org.w3c.dom.svg.SVGSVGElement; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.profiles.Profile; + +public class SVGGraphController extends GraphController { + private static final Logger logger = Logger.getLogger(SVGGraphController.class); + @SuppressWarnings("unused") + private static final Timer timer = new Timer("SVG Graph controller timer", true); + private static final String dotErrorMessage = "Cannot draw diagram(s)\n" + + "\n" + + "Install dot as described\n" + + "at http://www.taverna.org.uk\n" + + "and specify its location\n" + + "in the workbench preferences"; + + private Map<String, List<SVGGraphEdge>> datalinkMap = new HashMap<>(); + private final JSVGCanvas svgCanvas; + private SVGDocument svgDocument; + private GraphLayout graphLayout = new GraphLayout(); + private EdgeLine edgeLine; + private UpdateManager updateManager; + private ExecutorService executor = Executors.newFixedThreadPool(1); + private boolean drawingDiagram = false; + private int animationSpeed; + private Rectangle bounds, oldBounds; + private SVGOMAnimationElement animateBounds; + private boolean dotMissing = false; + private final WorkbenchConfiguration workbenchConfiguration; + + public SVGGraphController(Workflow dataflow, Profile profile, + boolean interactive, JSVGCanvas svgCanvas, EditManager editManager, + MenuManager menuManager, ColourManager colourManager, + WorkbenchConfiguration workbenchConfiguration) { + super(dataflow, profile, interactive, svgCanvas, editManager, + menuManager, colourManager); + this.svgCanvas = svgCanvas; + this.workbenchConfiguration = workbenchConfiguration; + installUpdateManager(); + layoutSVGDocument(svgCanvas.getBounds()); + svgCanvas.setDocument(getSVGDocument()); + } + + public SVGGraphController(Workflow dataflow, Profile profile, + boolean interactive, JSVGCanvas svgCanvas, Alignment alignment, + PortStyle portStyle, EditManager editManager, + MenuManager menuManager, ColourManager colourManager, + WorkbenchConfiguration workbenchConfiguration) { + super(dataflow, profile, interactive, svgCanvas, alignment, portStyle, + editManager, menuManager, colourManager); + this.svgCanvas = svgCanvas; + this.workbenchConfiguration = workbenchConfiguration; + installUpdateManager(); + layoutSVGDocument(svgCanvas.getBounds()); + svgCanvas.setDocument(getSVGDocument()); + } + + private void installUpdateManager() { + svgCanvas.addGVTTreeRendererListener(new GVTTreeRendererAdapter() { + @Override + public void gvtRenderingCompleted(GVTTreeRendererEvent ev) { + setUpdateManager(svgCanvas.getUpdateManager()); + } + }); + } + + @Override + public GraphEdge createGraphEdge() { + return new SVGGraphEdge(this); + } + + @Override + public Graph createGraph() { + return new SVGGraph(this); + } + + @Override + public GraphNode createGraphNode() { + return new SVGGraphNode(this); + } + + public JSVGCanvas getSVGCanvas() { + return svgCanvas; + } + + public synchronized SVGDocument getSVGDocument() { + if (svgDocument == null) + svgDocument = createSVGDocument(); + return svgDocument; + } + + @Override + public void redraw() { + Graph graph = generateGraph(); + Rectangle actualBounds = layoutGraph(graph, svgCanvas.getBounds()); + setBounds(actualBounds); + transformGraph(getGraph(), graph); + } + + private void layoutSVGDocument(Rectangle bounds) { + animateBounds = createAnimationElement(this, SVG_ANIMATE_TAG, + SVG_VIEW_BOX_ATTRIBUTE, null); + updateManager = null; + datalinkMap.clear(); + + Graph graph = getGraph(); + if (graph instanceof SVGGraph) { + SVGGraph svgGraph = (SVGGraph) graph; + SVGSVGElement svgElement = getSVGDocument().getRootElement(); + SVGElement graphElement = svgGraph.getSVGElement(); + svgElement.appendChild(graphElement); + + setBounds(layoutGraph(graph, bounds)); + + edgeLine = EdgeLine.createAndAdd(getSVGDocument(), this); + } + drawingDiagram = true; + } + + public Rectangle layoutGraph(Graph graph, Rectangle bounds) { + Rectangle actualBounds = null; + bounds = new Rectangle(bounds); + StringWriter stringWriter = new StringWriter(); + DotWriter dotWriter = new DotWriter(stringWriter); + try { + dotWriter.writeGraph(graph); + String layout = getDot(stringWriter.toString(), workbenchConfiguration); + if (layout.isEmpty()) + logger.warn("Invalid dot returned"); + else + actualBounds = graphLayout.layoutGraph(this, graph, layout, bounds); + } catch (IOException e) { + outputMessage(dotErrorMessage); + setDotMissing(true); + logger.warn("Couldn't generate dot"); + } catch (ParseException e) { + logger.warn("Couldn't layout graph", e); + } + return actualBounds; + } + + private void setDotMissing(boolean b) { + this.dotMissing = b; + } + + public boolean isDotMissing() { + return dotMissing; + } + + public void setBounds(final Rectangle bounds) { + oldBounds = this.bounds; + this.bounds = bounds; + updateSVGDocument(new Runnable() { + @Override + public void run() { + SVGSVGElement svgElement = getSVGDocument().getRootElement(); + if (isAnimatable() && oldBounds != null) { + String from = "0 0 " + oldBounds.width + " " + + oldBounds.height; + String to = "0 0 " + bounds.width + " " + bounds.height; + animate(animateBounds, svgElement, getAnimationSpeed(), + from, to); + } else if ((svgElement != null) && (bounds != null)) + svgElement.setAttribute(SVG_VIEW_BOX_ATTRIBUTE, + "0 0 " + String.valueOf(bounds.width) + " " + + String.valueOf(bounds.height)); + } + }); + } + + private void outputMessage(final String message) { + SVGSVGElement svgElement = getSVGDocument().getRootElement(); + String[] parts = message.split("\n"); + int initialPosition = 200; + for (int i = 0; i < parts.length; i++) { + Text errorsText = createText(parts[i]); + SVGOMTextElement error = (SVGOMTextElement) createElement(SVG_TEXT_TAG); + error.setAttribute(SVG_Y_ATTRIBUTE, + Integer.toString(initialPosition + i * 60)); + error.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "20"); + error.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif"); + error.setAttribute(SVG_FILL_ATTRIBUTE, "red"); + error.appendChild(errorsText); + svgElement.appendChild(error); + } + bounds = new Rectangle(300, parts.length * 60 + 200); + svgCanvas.setDocument(getSVGDocument()); + } + + public void setUpdateManager(UpdateManager updateManager) { + this.updateManager = updateManager; + drawingDiagram = false; + resetSelection(); + } + + @Override + public boolean startEdgeCreation(GraphElement graphElement, Point point) { + boolean alreadyStarted = edgeCreationFromSource || edgeCreationFromSink; + boolean started = super.startEdgeCreation(graphElement, point); + if (!alreadyStarted && started) { + if (edgeMoveElement instanceof SVGGraphEdge) { + SVGGraphEdge svgGraphEdge = (SVGGraphEdge) edgeMoveElement; + SVGPoint sourcePoint = svgGraphEdge.getPathElement() + .getPointAtLength(0f); + edgeLine.setSourcePoint(new Point((int) sourcePoint.getX(), + (int) sourcePoint.getY())); + } else + edgeLine.setSourcePoint(point); + edgeLine.setTargetPoint(point); + edgeLine.setColour(Color.BLACK); + // edgeLine.setVisible(true); + } + return started; + } + + @Override + public boolean moveEdgeCreationTarget(GraphElement graphElement, Point point) { + boolean linkValid = super.moveEdgeCreationTarget(graphElement, point); + if (edgeMoveElement instanceof SVGGraphEdge) + ((SVGGraphEdge) edgeMoveElement).setVisible(false); + if (edgeCreationFromSink) { + edgeLine.setSourcePoint(point); + if (linkValid) + edgeLine.setColour(GREEN); + else + edgeLine.setColour(BLACK); + edgeLine.setVisible(true); + } else if (edgeCreationFromSource) { + edgeLine.setTargetPoint(point); + if (linkValid) + edgeLine.setColour(GREEN); + else + edgeLine.setColour(BLACK); + edgeLine.setVisible(true); + } + return linkValid; + } + + @Override + public boolean stopEdgeCreation(GraphElement graphElement, Point point) { + GraphEdge movedEdge = edgeMoveElement; + boolean edgeCreated = super.stopEdgeCreation(graphElement, point); + if (!edgeCreated && movedEdge instanceof SVGGraphEdge) + ((SVGGraphEdge) movedEdge).setVisible(true); + edgeLine.setVisible(false); + return edgeCreated; + } + + @Override + public void setEdgeActive(String edgeId, boolean active) { + if (datalinkMap.containsKey(edgeId)) + for (GraphEdge datalink : datalinkMap.get(edgeId)) + datalink.setActive(active); + } + + public Element createElement(String tag) { + return getSVGDocument().createElementNS(svgNS, tag); + } + + SVGOMGElement createGElem() { + return (SVGOMGElement) createElement(SVG_G_TAG); + } + + SVGOMPolygonElement createPolygon() { + return (SVGOMPolygonElement) createElement(SVG_POLYGON_TAG); + } + + SVGOMEllipseElement createEllipse() { + return (SVGOMEllipseElement) createElement(SVG_ELLIPSE_TAG); + } + + SVGOMPathElement createPath() { + return (SVGOMPathElement) createElement(SVG_PATH_TAG); + } + + SVGOMRectElement createRect() { + return (SVGOMRectElement) createElement(SVG_RECT_TAG); + } + + public Text createText(String text) { + return getSVGDocument().createTextNode(text); + } + + SVGOMTextElement createText(Text text) { + SVGOMTextElement elem = (SVGOMTextElement) createElement(SVG_TEXT_TAG); + elem.appendChild(text); + return elem; + } + + public void updateSVGDocument(final Runnable thread) { + if (updateManager == null && !drawingDiagram) + thread.run(); + else if (!executor.isShutdown()) + executor.execute(new Runnable() { + @Override + public void run() { + waitForUpdateManager(); + try { + updateManager.getUpdateRunnableQueue().invokeLater( + thread); + } catch (IllegalStateException e) { + logger.error("Update of SVG failed", e); + } + } + + private void waitForUpdateManager() { + try { + while (updateManager == null) + Thread.sleep(200); + } catch (InterruptedException e) { + } + } + }); +// if (updateManager == null) +// thread.run(); +// else +// updateManager.getUpdateRunnableQueue().invokeLater(thread); + } + + public boolean isAnimatable() { + return animationSpeed > 0 && updateManager != null && !drawingDiagram; + } + + /** + * Returns the animation speed in milliseconds. + * + * @return the animation speed in milliseconds + */ + public int getAnimationSpeed() { + return animationSpeed; + } + + /** + * Sets the animation speed in milliseconds. A value of 0 turns off animation. + * + * @param animationSpeed the animation speed in milliseconds + */ + public void setAnimationSpeed(int animationSpeed) { + this.animationSpeed = animationSpeed; + } + + @Override + public void shutdown() { + super.shutdown(); + executor.execute(new Runnable() { + @Override + public void run() { + getSVGCanvas().stopProcessing(); + executor.shutdown(); + } + }); + } + +} + +class EdgeLine { + private static final float arrowLength = 10f; + private static final float arrowWidth = 3f; + + private Element line; + private Element pointer; + private SVGGraphController graphController; + + private EdgeLine(SVGGraphController graphController) { + this.graphController = graphController; + } + + public static EdgeLine createAndAdd(SVGDocument svgDocument, SVGGraphController graphController) { + EdgeLine edgeLine = new EdgeLine(graphController); + edgeLine.line = svgDocument.createElementNS(svgNS, SVG_LINE_TAG); + edgeLine.line.setAttribute(SVG_STYLE_ATTRIBUTE, + "fill:none;stroke:black"); + edgeLine.line.setAttribute("pointer-events", "none"); + edgeLine.line.setAttribute("visibility", "hidden"); + edgeLine.line.setAttribute(SVG_X1_ATTRIBUTE, "0"); + edgeLine.line.setAttribute(SVG_Y1_ATTRIBUTE, "0"); + edgeLine.line.setAttribute(SVG_X2_ATTRIBUTE, "0"); + edgeLine.line.setAttribute(SVG_Y2_ATTRIBUTE, "0"); + + edgeLine.pointer = svgDocument.createElementNS(svgNS, SVG_POLYGON_TAG); + edgeLine.pointer.setAttribute(SVG_STYLE_ATTRIBUTE, + "fill:black;stroke:black"); + edgeLine.pointer.setAttribute(SVG_POINTS_ATTRIBUTE, "0,0 " + + -arrowLength + "," + arrowWidth + " " + -arrowLength + "," + + -arrowWidth + " 0,0"); + edgeLine.pointer.setAttribute("pointer-events", "none"); + edgeLine.pointer.setAttribute("visibility", "hidden"); + + Element svgRoot = svgDocument.getDocumentElement(); + svgRoot.insertBefore(edgeLine.line, null); + svgRoot.insertBefore(edgeLine.pointer, null); + + return edgeLine; + } + + public void setSourcePoint(final Point point) { + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + line.setAttribute(SVG_X1_ATTRIBUTE, + String.valueOf(point.getX())); + line.setAttribute(SVG_Y1_ATTRIBUTE, + String.valueOf(point.getY())); + + float x = parseFloat(line.getAttribute(SVG_X2_ATTRIBUTE)); + float y = parseFloat(line.getAttribute(SVG_Y2_ATTRIBUTE)); + double angle = calculateAngle(line); + + pointer.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + x + + " " + y + ") rotate(" + angle + " 0 0) "); + } + }); + } + + public void setTargetPoint(final Point point) { + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + line.setAttribute(SVG_X2_ATTRIBUTE, + String.valueOf(point.getX())); + line.setAttribute(SVG_Y2_ATTRIBUTE, + String.valueOf(point.getY())); + + double angle = calculateAngle(line); + pointer.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + point.x + " " + point.y + ") rotate(" + angle + + " 0 0) "); + } + }); + } + + public void setColour(final Color colour) { + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + String hexColour = getHexValue(colour); + line.setAttribute(SVG_STYLE_ATTRIBUTE, "fill:none;stroke:" + + hexColour + ";"); + pointer.setAttribute(SVG_STYLE_ATTRIBUTE, "fill:" + hexColour + + ";stroke:" + hexColour + ";"); + } + }); + } + + public void setVisible(final boolean visible) { + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (visible) { + line.setAttribute("visibility", "visible"); + pointer.setAttribute("visibility", "visible"); + } else { + line.setAttribute("visibility", "hidden"); + pointer.setAttribute("visibility", "hidden"); + } + } + }); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphEdge.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphEdge.java new file mode 100644 index 0000000..7884d62 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphEdge.java
@@ -0,0 +1,311 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.SELECTED_COLOUR; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.adjustPathLength; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculateAngle; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getHexValue; +import static org.apache.batik.util.CSSConstants.CSS_BLACK_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_DISPLAY_PROPERTY; +import static org.apache.batik.util.CSSConstants.CSS_INLINE_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_NONE_VALUE; +import static org.apache.batik.util.SMILConstants.SMIL_ADDITIVE_ATTRIBUTE; +import static org.apache.batik.util.SMILConstants.SMIL_SUM_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TRANSFORM_TAG; +import static org.apache.batik.util.SVGConstants.SVG_CLICK_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_CX_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_CY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_D_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_MOUSEDOWN_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_NONE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_POINTS_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_RX_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_RY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_TRANSFORM_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_ZERO_VALUE; +import static org.apache.batik.util.SVGConstants.TRANSFORM_ROTATE; +import static org.apache.batik.util.SVGConstants.TRANSFORM_TRANSLATE; + +import java.awt.Color; +import java.awt.Point; +import java.util.List; + +import net.sf.taverna.t2.workbench.models.graph.GraphEdge; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseClickEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseDownEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOutEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOverEventListener; + +import org.apache.batik.dom.svg.SVGGraphicsElement; +import org.apache.batik.dom.svg.SVGOMAnimationElement; +import org.apache.batik.dom.svg.SVGOMEllipseElement; +import org.apache.batik.dom.svg.SVGOMGElement; +import org.apache.batik.dom.svg.SVGOMPathElement; +import org.apache.batik.dom.svg.SVGOMPolygonElement; +import org.w3c.dom.events.EventTarget; +import org.w3c.dom.svg.SVGElement; + +/** + * SVG representation of a graph edge. + * + * @author David Withers + */ +public class SVGGraphEdge extends GraphEdge { + private static final String ARROW_LENGTH = "8.5"; + private static final String ARROW_WIDTH = "3"; + private static final String ELLIPSE_RADIUS = "3.5"; + + private SVGGraphController graphController; + private SVGGraphElementDelegate delegate; + private SVGMouseClickEventListener mouseClickAction; + private SVGMouseDownEventListener mouseDownAction; + @SuppressWarnings("unused") + private SVGMouseOverEventListener mouseOverAction; + @SuppressWarnings("unused") + private SVGMouseOutEventListener mouseOutAction; + private SVGOMGElement mainGroup; + private SVGOMPathElement path, deleteButton; + private SVGOMPolygonElement polygon; + private SVGOMEllipseElement ellipse; + private SVGGraphicsElement arrowHead; + private SVGOMAnimationElement animatePath, animatePosition, animateRotation; + + public SVGGraphEdge(SVGGraphController graphController) { + super(graphController); + this.graphController = graphController; + + mouseClickAction = new SVGMouseClickEventListener(this); + mouseDownAction = new SVGMouseDownEventListener(this); + mouseOverAction = new SVGMouseOverEventListener(this); + mouseOutAction = new SVGMouseOutEventListener(this); + + mainGroup = graphController.createGElem(); + mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, CSS_BLACK_VALUE); + mainGroup.setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, CSS_NONE_VALUE); + mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1"); + + path = graphController.createPath(); + path.setAttribute(SVG_FILL_ATTRIBUTE, SVG_NONE_VALUE); + EventTarget t = (EventTarget) path; + t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false); + // t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false); + // t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false); + mainGroup.appendChild(path); + + polygon = graphController.createPolygon(); + polygon.setAttribute(SVG_POINTS_ATTRIBUTE, ARROW_LENGTH + ", 0" + + " 0, -" + ARROW_WIDTH + " 0," + ARROW_WIDTH); + t = (EventTarget) polygon; + t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false); + t.addEventListener(SVG_MOUSEDOWN_EVENT_TYPE, mouseDownAction, false); + // t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false); + // t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false); + + ellipse = graphController.createEllipse(); + ellipse.setAttribute(SVG_CX_ATTRIBUTE, ELLIPSE_RADIUS); + ellipse.setAttribute(SVG_CY_ATTRIBUTE, SVG_ZERO_VALUE); + ellipse.setAttribute(SVG_RX_ATTRIBUTE, ELLIPSE_RADIUS); + ellipse.setAttribute(SVG_RY_ATTRIBUTE, ELLIPSE_RADIUS); + + arrowHead = polygon; + mainGroup.appendChild(arrowHead); + + deleteButton = graphController.createPath(); + deleteButton.setAttribute(SVG_STROKE_ATTRIBUTE, "red"); + deleteButton.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2"); + deleteButton.setAttribute(SVG_D_ATTRIBUTE, + "M-3.5,-7L3.5,0M-3.5,0L3.5,-7"); + deleteButton.setAttribute(CSS_DISPLAY_PROPERTY, CSS_NONE_VALUE); + mainGroup.appendChild(deleteButton); + + animatePath = createAnimationElement(graphController, SVG_ANIMATE_TAG, + SVG_D_ATTRIBUTE, null); + + animatePosition = createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_TRANSLATE); + + animateRotation = createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_ROTATE); + animateRotation.setAttribute(SMIL_ADDITIVE_ATTRIBUTE, SMIL_SUM_VALUE); + + delegate = new SVGGraphElementDelegate(graphController, this, mainGroup); + } + + public SVGElement getSVGElement() { + return mainGroup; + } + + /** + * Returns the path. + * + * @return the path + */ + public SVGOMPathElement getPathElement() { + return path; + } + + @Override + public void setSelected(boolean selected) { + super.setSelected(selected); + final String color = selected ? SELECTED_COLOUR + : getHexValue(getColor()); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, color); + mainGroup.setAttribute(SVG_FILL_ATTRIBUTE, color); + } + }); + } + + @Override + public void setActive(final boolean active) { + super.setActive(active); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (active) { + path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2"); + deleteButton.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_INLINE_VALUE); + } else { + path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1"); + deleteButton.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_NONE_VALUE); + } + } + }); + } + + @Override + public void setArrowHeadStyle(final ArrowStyle arrowHeadStyle) { + super.setArrowHeadStyle(arrowHeadStyle); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (ArrowStyle.NONE.equals(arrowHeadStyle)) + mainGroup.removeChild(arrowHead); + else if (ArrowStyle.NORMAL.equals(arrowHeadStyle)) { + mainGroup.removeChild(arrowHead); + arrowHead = polygon; + mainGroup.appendChild(arrowHead); + } else if (ArrowStyle.DOT.equals(arrowHeadStyle)) { + mainGroup.removeChild(arrowHead); + arrowHead = ellipse; + mainGroup.appendChild(arrowHead); + } + } + }); + } + + @Override + public void setPath(final List<Point> pointList) { + if (pointList == null) + return; + + final List<Point> oldPointList = getPath(); + super.setPath(pointList); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + Point lastPoint = pointList.get(pointList.size() - 1); + double angle = calculateAngle(pointList); + if (graphController.isAnimatable() && oldPointList != null) { + adjustPathLength(oldPointList, pointList.size()); + Point oldLastPoint = oldPointList.get(oldPointList.size() - 1); + double oldAngle = calculateAngle(oldPointList); + animate(animatePath, path, + graphController.getAnimationSpeed(), + SVGUtil.getPath(oldPointList), + SVGUtil.getPath(pointList)); + + animate(animatePosition, polygon, + graphController.getAnimationSpeed(), oldLastPoint.x + + ", " + oldLastPoint.y, lastPoint.x + ", " + + lastPoint.y); + + animate(animateRotation, polygon, + graphController.getAnimationSpeed(), oldAngle + + " 0 0", angle + " 0 0"); + + ellipse.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + lastPoint.x + " " + lastPoint.y + ") rotate(" + + angle + " 0 0) "); + deleteButton.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + lastPoint.x + " " + lastPoint.y + + ")"); + } else { + path.setAttribute(SVG_D_ATTRIBUTE, + SVGUtil.getPath(pointList)); + polygon.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + lastPoint.x + " " + lastPoint.y + ") rotate(" + + angle + " 0 0) "); + ellipse.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + lastPoint.x + " " + lastPoint.y + ") rotate(" + + angle + " 0 0) "); + deleteButton.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + lastPoint.x + " " + lastPoint.y + + ")"); + } + } + }); + } + + @Override + public void setColor(final Color color) { + delegate.setColor(color); + super.setColor(color); + } + + @Override + public void setFillColor(final Color fillColor) { + delegate.setFillColor(fillColor); + super.setFillColor(fillColor); + } + + @Override + public void setVisible(final boolean visible) { + delegate.setVisible(visible); + super.setVisible(visible); + } + + @Override + public void setFiltered(final boolean filtered) { + delegate.setFiltered(filtered); + super.setFiltered(filtered); + } + + @Override + public void setOpacity(final float opacity) { + delegate.setOpacity(opacity); + super.setOpacity(opacity); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphElementDelegate.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphElementDelegate.java new file mode 100644 index 0000000..cf7f852 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphElementDelegate.java
@@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (C) 2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +import static net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle.NONE; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.SELECTED_COLOUR; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getHexValue; +import static org.apache.batik.util.CSSConstants.CSS_DISPLAY_PROPERTY; +import static org.apache.batik.util.CSSConstants.CSS_INLINE_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_NONE_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_OPACITY_PROPERTY; +import static org.apache.batik.util.CSSConstants.CSS_POINTER_EVENTS_PROPERTY; +import static org.apache.batik.util.CSSConstants.CSS_VISIBLEPAINTED_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG; +import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_NONE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE; + +import java.awt.Color; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement; +import net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle; + +import org.apache.batik.dom.svg.SVGOMAnimationElement; +import org.apache.batik.dom.svg.SVGOMElement; + +/** + * Delegate for GraphElements. Logically a superclass of SVGGraph, SVGGraphNode + * and SVGGraphEdge (if java had multiple inheritance). + * + * @author David Withers + */ +public class SVGGraphElementDelegate { + private SVGGraphController graphController; + private GraphElement graphElement; + private SVGOMElement mainGroup; + private SVGOMAnimationElement animateOpacity; + + public SVGGraphElementDelegate(SVGGraphController graphController, + GraphElement graphElement, SVGOMElement mainGroup) { + this.graphController = graphController; + this.graphElement = graphElement; + this.mainGroup = mainGroup; + + animateOpacity = createAnimationElement(graphController, + SVG_ANIMATE_TAG, CSS_OPACITY_PROPERTY, null); + } + + public void setSelected(final boolean selected) { + boolean currentSelected = graphElement.isSelected(); + if (currentSelected != selected + && !LineStyle.NONE.equals(graphElement.getLineStyle())) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, + selected ? SELECTED_COLOUR + : getHexValue(graphElement.getColor())); + mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, + selected ? "2" : "1"); + } + }); + } + + public void setLineStyle(final LineStyle lineStyle) { + LineStyle currentLineStyle = graphElement.getLineStyle(); + if (!currentLineStyle.equals(lineStyle)) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + String stroke = SVG_NONE_VALUE, dash = SVG_NONE_VALUE; + switch (lineStyle) { + case DOTTED: + stroke = getHexValue(graphElement.getColor()); + dash = "1,5"; + break; + case SOLID: + stroke = getHexValue(graphElement.getColor()); + default: + break; + } + mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, stroke); + mainGroup + .setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, dash); + } + }); + } + + public void setColor(final Color color) { + Color currentColor = graphElement.getColor(); + if (currentColor != color && NONE != graphElement.getLineStyle()) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, + getHexValue(color)); + } + }); + } + + public void setFillColor(final Color fillColor) { + Color currentFillColor = graphElement.getFillColor(); + if (currentFillColor != fillColor) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.setAttribute(SVG_FILL_ATTRIBUTE, + getHexValue(fillColor)); + } + }); + } + + public void setVisible(final boolean visible) { + boolean currentVisible = graphElement.isVisible(); + if (currentVisible != visible) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.setAttribute(CSS_DISPLAY_PROPERTY, + visible ? CSS_INLINE_VALUE : CSS_NONE_VALUE); + } + }); + } + + public void setOpacity(final float opacity) { + final float currentOpacity = graphElement.getOpacity(); + if (currentOpacity != opacity) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (graphController.isAnimatable()) + animate(animateOpacity, mainGroup, + graphController.getAnimationSpeed(), + String.valueOf(currentOpacity), + String.valueOf(opacity)); + else + mainGroup.setAttribute(CSS_OPACITY_PROPERTY, + String.valueOf(opacity)); + } + }); + } + + public void setFiltered(final boolean filtered) { + boolean currentFiltered = graphElement.isFiltered(); + if (currentFiltered != filtered) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.setAttribute(CSS_POINTER_EVENTS_PROPERTY, + filtered ? CSS_NONE_VALUE + : CSS_VISIBLEPAINTED_VALUE); + setOpacity(filtered ? 0.2f : 1f); + } + }); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphNode.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphNode.java new file mode 100644 index 0000000..004b3f6 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphNode.java
@@ -0,0 +1,611 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.COMPLETED_COLOUR; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.ERROR_COLOUR; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculatePoints; +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement; +import static org.apache.batik.util.CSSConstants.CSS_ALL_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_BLACK_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_DISPLAY_PROPERTY; +import static org.apache.batik.util.CSSConstants.CSS_HIDDEN_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_INLINE_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_NONE_VALUE; +import static org.apache.batik.util.CSSConstants.CSS_POINTER_EVENTS_PROPERTY; +import static org.apache.batik.util.CSSConstants.CSS_VISIBILITY_PROPERTY; +import static org.apache.batik.util.CSSConstants.CSS_VISIBLE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG; +import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TRANSFORM_TAG; +import static org.apache.batik.util.SVGConstants.SVG_CLICK_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_CX_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_CY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_D_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_END_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FILL_OPACITY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_FONT_SIZE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_HEIGHT_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_MIDDLE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_MOUSEDOWN_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_MOUSEMOVE_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_MOUSEOUT_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_MOUSEOVER_EVENT_TYPE; +import static org.apache.batik.util.SVGConstants.SVG_NONE_VALUE; +import static org.apache.batik.util.SVGConstants.SVG_POINTS_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_RX_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_RY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_TEXT_ANCHOR_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_TRANSFORM_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_WIDTH_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_X_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_Y_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.TRANSFORM_TRANSLATE; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; + +import net.sf.taverna.t2.workbench.models.graph.Graph; +import net.sf.taverna.t2.workbench.models.graph.GraphNode; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseClickEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseDownEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseMovedEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOutEventListener; +import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOverEventListener; + +import org.apache.batik.dom.svg.SVGOMAnimationElement; +import org.apache.batik.dom.svg.SVGOMEllipseElement; +import org.apache.batik.dom.svg.SVGOMGElement; +import org.apache.batik.dom.svg.SVGOMPathElement; +import org.apache.batik.dom.svg.SVGOMPolygonElement; +import org.apache.batik.dom.svg.SVGOMRectElement; +import org.apache.batik.dom.svg.SVGOMTextElement; +import org.apache.batik.util.CSSConstants; +import org.apache.batik.util.SVGConstants; +import org.w3c.dom.Text; +import org.w3c.dom.events.Event; +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.EventTarget; +import org.w3c.dom.svg.SVGElement; + +/** + * SVG representation of a graph node. + * + * @author David Withers + */ +public class SVGGraphNode extends GraphNode { + private SVGGraphController graphController; + private SVGGraphElementDelegate delegate; + private SVGMouseClickEventListener mouseClickAction; + private SVGMouseMovedEventListener mouseMovedAction; + private SVGMouseDownEventListener mouseDownAction; + @SuppressWarnings("unused") + private SVGMouseOverEventListener mouseOverAction; + @SuppressWarnings("unused") + private SVGMouseOutEventListener mouseOutAction; + private SVGOMGElement mainGroup, labelGroup, portsGroup; + private SVGElement expandedElement, contractedElement; + private SVGOMPolygonElement polygon, completedPolygon; + private SVGOMEllipseElement ellipse; + private SVGOMTextElement label, iteration, error; + private Text labelText, iterationText, errorsText; + private SVGElement deleteButton; + private SVGOMAnimationElement animateShape, animatePosition, animateLabel, animateIteration, + animateErrors; + + public SVGGraphNode(SVGGraphController graphController) { + super(graphController); + this.graphController = graphController; + mouseClickAction = new SVGMouseClickEventListener(this); + mouseDownAction = new SVGMouseDownEventListener(this); + mouseMovedAction = new SVGMouseMovedEventListener(this); + mouseOverAction = new SVGMouseOverEventListener(this); + mouseOutAction = new SVGMouseOutEventListener(this); + + mainGroup = graphController.createGElem(); + mainGroup.setAttribute("alignment-baseline", SVG_MIDDLE_VALUE); + mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, CSS_BLACK_VALUE); + mainGroup.setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, CSS_NONE_VALUE); + mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1"); + mainGroup.setAttribute(SVG_FILL_ATTRIBUTE, CSS_NONE_VALUE); + + EventTarget t = (EventTarget) mainGroup; + t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false); + t.addEventListener(SVG_MOUSEMOVE_EVENT_TYPE, mouseMovedAction, false); + t.addEventListener(SVG_MOUSEDOWN_EVENT_TYPE, mouseDownAction, false); +// t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false); +// t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false); + + expandedElement = graphController.createGElem(); + contractedElement = graphController.createGElem(); + + portsGroup = graphController.createGElem(); + portsGroup.setAttribute(CSS_DISPLAY_PROPERTY, CSS_NONE_VALUE); + contractedElement.appendChild(portsGroup); + + mainGroup.appendChild(contractedElement); + + polygon = graphController.createPolygon(); + contractedElement.appendChild(polygon); + + ellipse = graphController.createEllipse(); + ellipse.setAttribute(CSS_DISPLAY_PROPERTY, CSS_NONE_VALUE); + ellipse.setAttribute(SVG_RX_ATTRIBUTE, String.valueOf(2)); + ellipse.setAttribute(SVG_CX_ATTRIBUTE, String.valueOf(0)); + ellipse.setAttribute(SVG_RY_ATTRIBUTE, String.valueOf(2)); + ellipse.setAttribute(SVG_CY_ATTRIBUTE, String.valueOf(0)); + contractedElement.appendChild(ellipse); + + completedPolygon = graphController.createPolygon(); + completedPolygon.setAttribute(SVG_POINTS_ATTRIBUTE, + calculatePoints(getShape(), 0, 0)); + completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE, COMPLETED_COLOUR); + completedPolygon.setAttribute(SVG_FILL_OPACITY_ATTRIBUTE, "0.8"); + contractedElement.appendChild(completedPolygon); + + labelText = graphController.createText(""); + label = graphController.createText(labelText); + label.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_MIDDLE_VALUE); + label.setAttribute("baseline-shift", "-35%"); + label.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE); + label.setAttribute(SVG_STROKE_ATTRIBUTE, SVGConstants.SVG_NONE_VALUE); + labelGroup = graphController.createGElem(); + labelGroup.appendChild(label); + contractedElement.appendChild(labelGroup); + + iterationText = graphController.createText(""); + iteration = graphController.createText(iterationText); + iteration.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_END_VALUE); + iteration.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "6"); + iteration.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif"); + iteration.setAttribute(SVG_FILL_ATTRIBUTE, CSS_BLACK_VALUE); + iteration.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE); + contractedElement.appendChild(iteration); + + errorsText = graphController.createText(""); + error = graphController.createText(errorsText); + error.setAttribute(SVG_TEXT_ANCHOR_ATTRIBUTE, SVG_END_VALUE); + error.setAttribute(SVG_FONT_SIZE_ATTRIBUTE, "6"); + error.setAttribute(SVG_FONT_FAMILY_ATTRIBUTE, "sans-serif"); + error.setAttribute(SVG_FILL_ATTRIBUTE, CSSConstants.CSS_BLACK_VALUE); + error.setAttribute(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE); + contractedElement.appendChild(error); + + // deleteButton = createDeleteButton(); + // g.appendChild(deleteButton); + + animateShape = createAnimationElement(graphController, SVG_ANIMATE_TAG, + SVG_POINTS_ATTRIBUTE, null); + + animatePosition = createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_TRANSLATE); + + animateLabel = SVGUtil.createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_TRANSLATE); + + animateIteration = createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_TRANSLATE); + + animateErrors = createAnimationElement(graphController, + SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE, + TRANSFORM_TRANSLATE); + + delegate = new SVGGraphElementDelegate(graphController, this, mainGroup); + } + + @SuppressWarnings("unused") + private SVGElement createDeleteButton() { + final SVGOMGElement button = graphController.createGElem(); + button.setAttribute(CSS_VISIBILITY_PROPERTY, CSS_HIDDEN_VALUE); + button.setAttribute(CSS_POINTER_EVENTS_PROPERTY, CSS_ALL_VALUE); + + SVGOMRectElement rect = graphController.createRect(); + rect.setAttribute(SVG_X_ATTRIBUTE, "4"); + rect.setAttribute(SVG_Y_ATTRIBUTE, "4"); + rect.setAttribute(SVG_WIDTH_ATTRIBUTE, "13"); + rect.setAttribute(SVG_HEIGHT_ATTRIBUTE, "13"); + rect.setAttribute(SVG_FILL_ATTRIBUTE, "none"); + button.appendChild(rect); + + final SVGOMPathElement path = graphController.createPath(); + path.setAttribute(SVG_STROKE_ATTRIBUTE, "white"); + path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2"); + path.setAttribute(SVG_D_ATTRIBUTE, "M5,5L12,12M5,12L12,5"); + button.appendChild(path); + + EventTarget t = (EventTarget) button; + t.addEventListener(SVG_MOUSEOVER_EVENT_TYPE, new EventListener() { + @Override + public void handleEvent(Event evt) { + if (isInteractive()) { + deleteButton.setAttribute(CSS_VISIBILITY_PROPERTY, + CSS_VISIBLE_VALUE); + path.setAttribute(SVG_STROKE_ATTRIBUTE, "red"); + evt.stopPropagation(); + } + } + }, false); + t.addEventListener(SVG_MOUSEOUT_EVENT_TYPE, new EventListener() { + @Override + public void handleEvent(Event evt) { + if (isInteractive()) { + path.setAttribute(SVG_STROKE_ATTRIBUTE, "white"); + evt.stopPropagation(); + } + } + }, false); + + return button; + } + + public SVGElement getSVGElement() { + return mainGroup; + } + + @Override + public void setActive(final boolean active) { + super.setActive(active); + if (isInteractive()) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (active) { + deleteButton.setAttribute(CSS_VISIBILITY_PROPERTY, + CSS_VISIBLE_VALUE); + // deleteButton.setAttribute(CSSConstants.CSS_DISPLAY_PROPERTY, + // CSSConstants.CSS_INLINE_VALUE); + } else { + deleteButton.setAttribute(CSS_VISIBILITY_PROPERTY, + CSS_HIDDEN_VALUE); + // button.setAttribute(CSSConstants.CSS_DISPLAY_PROPERTY, + // CSSConstants.CSS_NONE_VALUE); + } + } + }); + } + + @Override + public void setGraph(Graph graph) { + super.setGraph(graph); + if (graph instanceof SVGGraph) { + SVGGraph svgGraph = (SVGGraph) graph; + final SVGElement graphElement = svgGraph.getSVGElement(); + if (isExpanded()) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + mainGroup.replaceChild(expandedElement, graphElement); + } + }); + expandedElement = graphElement; + } + } + + @Override + public void setExpanded(final boolean expanded) { + if (isExpanded() != expanded) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (expanded) + mainGroup.replaceChild(expandedElement, contractedElement); + else + mainGroup.replaceChild(contractedElement, expandedElement); + } + }); + super.setExpanded(expanded); + } + + @Override + public void addSourceNode(final GraphNode sourceNode) { + super.addSourceNode(sourceNode); + if (sourceNode instanceof SVGGraphNode) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + SVGGraphNode svgGraphNode = (SVGGraphNode) sourceNode; + portsGroup.appendChild(svgGraphNode.getSVGElement()); + } + }); + } + + @Override + public boolean removeSourceNode(final GraphNode sourceNode) { + if (sourceNode instanceof SVGGraphNode) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + SVGGraphNode svgGraphNode = (SVGGraphNode) sourceNode; + portsGroup.removeChild(svgGraphNode.getSVGElement()); + } + }); + return super.removeSourceNode(sourceNode); + } + + @Override + public void addSinkNode(final GraphNode sinkNode) { + super.addSinkNode(sinkNode); + if (sinkNode instanceof SVGGraphNode) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + SVGGraphNode svgGraphNode = (SVGGraphNode) sinkNode; + portsGroup.appendChild(svgGraphNode.getSVGElement()); + } + }); + } + + @Override + public boolean removeSinkNode(final GraphNode sinkNode) { + if (sinkNode instanceof SVGGraphNode) + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + SVGGraphNode svgGraphNode = (SVGGraphNode) sinkNode; + portsGroup.removeChild(svgGraphNode.getSVGElement()); + } + }); + return super.removeSinkNode(sinkNode); + } + + @Override + public void setPosition(final Point position) { + final Point oldPosition = getPosition(); + if (position != null && !position.equals(oldPosition)) { + super.setPosition(position); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (graphController.isAnimatable()) + animate(animatePosition, mainGroup, + graphController.getAnimationSpeed(), + oldPosition.x + ", " + oldPosition.y, + position.x + ", " + position.y); + else + mainGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + position.x + " " + position.y + + ")"); + } + }); + } + } + + @Override + public void setSize(final Dimension size) { + final Dimension oldSize = getSize(); + if (size != null && !size.equals(oldSize)) { + super.setSize(size); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + adjustSize(size, oldSize); + } + }); + } + } + + /** core of implementation of {@link #setSize(Dimension)} */ + private void adjustSize(Dimension size, Dimension oldSize) { + int oldWidth = oldSize.width; + int oldHeight = oldSize.height; + if (graphController.isAnimatable()) { + if (Shape.CIRCLE.equals(getShape())) { + ellipse.setAttribute(SVG_RX_ATTRIBUTE, + String.valueOf(size.width / 2f)); + ellipse.setAttribute(SVG_CX_ATTRIBUTE, + String.valueOf(size.width / 2f)); + ellipse.setAttribute(SVG_RY_ATTRIBUTE, + String.valueOf(size.height / 2f)); + ellipse.setAttribute(SVG_CY_ATTRIBUTE, + String.valueOf(size.height / 2f)); + } else + animate(animateShape, polygon, + graphController.getAnimationSpeed(), + calculatePoints(getShape(), oldWidth, oldHeight), + calculatePoints(getShape(), getWidth(), getHeight())); + + if (getLabel() != null && !getLabel().isEmpty()) + animate(animateLabel, labelGroup, + graphController.getAnimationSpeed(), (oldWidth / 2f) + + ", " + (oldHeight / 2f), (getWidth() / 2f) + + ", " + (getHeight() / 2f)); + else + labelGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE, + "translate(" + getWidth() / 2f + " " + getHeight() / 2f + ")"); + + if (getIteration() > 0) + animate(animateIteration, iteration, + graphController.getAnimationSpeed(), (oldWidth - 1.5) + + ", 5.5", (getWidth() - 1.5) + ", 5.5"); + else + iteration.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + (getWidth() - 1.5) + " 5.5)"); + + if (getErrors() > 0) + animate(animateErrors, error, + graphController.getAnimationSpeed(), (oldWidth - 1.5) + + ", " + (oldHeight - 1), (getWidth() - 1.5) + + ", " + (getHeight() - 1)); + else + error.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + (getWidth() - 1.5) + " " + (getHeight() - 1) + ")"); + } else { + if (Shape.CIRCLE.equals(getShape())) { + ellipse.setAttribute(SVG_RX_ATTRIBUTE, + String.valueOf(size.width / 2f)); + ellipse.setAttribute(SVG_CX_ATTRIBUTE, + String.valueOf(size.width / 2f)); + ellipse.setAttribute(SVG_RY_ATTRIBUTE, + String.valueOf(size.height / 2f)); + ellipse.setAttribute(SVG_CY_ATTRIBUTE, + String.valueOf(size.height / 2f)); + } else + polygon.setAttribute(SVG_POINTS_ATTRIBUTE, + calculatePoints(getShape(), getWidth(), getHeight())); + + labelGroup.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + getWidth() / 2f + " " + getHeight() / 2f + ")"); + iteration.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + (getWidth() - 1.5) + " 5.5)"); + error.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate(" + + (getWidth() - 1.5) + " " + (getHeight() - 1) + ")"); + } + } + + @Override + public void setShape(final Shape shape) { + final Shape currentShape = getShape(); + if (shape != null && !shape.equals(currentShape)) { + super.setShape(shape); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (Shape.CIRCLE.equals(shape)) { + ellipse.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_INLINE_VALUE); + polygon.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_NONE_VALUE); + } else if (Shape.CIRCLE.equals(currentShape)) { + ellipse.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_NONE_VALUE); + polygon.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_INLINE_VALUE); + } + if (Shape.RECORD.equals(shape)) + portsGroup.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_INLINE_VALUE); + else if (Shape.RECORD.equals(currentShape)) + portsGroup.setAttribute(CSS_DISPLAY_PROPERTY, + CSS_NONE_VALUE); + } + }); + } + } + + @Override + public void setLabel(final String label) { + super.setLabel(label); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + labelText.setData(label); + } + }); + } + + @Override + public void setIteration(final int iteration) { + super.setIteration(iteration); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (iteration > 0) + iterationText.setData(String.valueOf(iteration)); + else + iterationText.setData(""); + } + }); + } + + @Override + public void setErrors(final int errors) { + super.setErrors(errors); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + if (errors > 0) { + errorsText.setData(String.valueOf(errors)); + completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE, + ERROR_COLOUR); + } else { + errorsText.setData(""); + completedPolygon.setAttribute(SVG_FILL_ATTRIBUTE, + COMPLETED_COLOUR); + } + } + }); + } + + @Override + public void setCompleted(final float complete) { + super.setCompleted(complete); + graphController.updateSVGDocument(new Runnable() { + @Override + public void run() { + completedPolygon.setAttribute( + SVG_POINTS_ATTRIBUTE, + calculatePoints(getShape(), + (int) (getWidth() * complete), getHeight())); + } + }); + } + + @Override + public void setSelected(final boolean selected) { + delegate.setSelected(selected); + super.setSelected(selected); + } + + @Override + public void setLineStyle(final LineStyle lineStyle) { + delegate.setLineStyle(lineStyle); + super.setLineStyle(lineStyle); + } + + @Override + public void setColor(final Color color) { + delegate.setColor(color); + super.setColor(color); + } + + @Override + public void setFillColor(final Color fillColor) { + delegate.setFillColor(fillColor); + super.setFillColor(fillColor); + } + + @Override + public void setVisible(final boolean visible) { + delegate.setVisible(visible); + super.setVisible(visible); + } + + @Override + public void setFiltered(final boolean filtered) { + delegate.setFiltered(filtered); + super.setFiltered(filtered); + } + + @Override + public void setOpacity(final float opacity) { + delegate.setOpacity(opacity); + super.setOpacity(opacity); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphSettings.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphSettings.java new file mode 100644 index 0000000..777102e --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGGraphSettings.java
@@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +public interface SVGGraphSettings { + String COMPLETED_COLOUR = "grey"; + String ERROR_COLOUR = "#dd3131"; + String SELECTED_COLOUR = "#4377d3"; + String NORMAL_COLOUR = "black"; +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGMonitorShape.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGMonitorShape.java new file mode 100644 index 0000000..5aab55f --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGMonitorShape.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +import org.apache.batik.dom.svg.SVGOMPolygonElement; + +public interface SVGMonitorShape extends SVGShape { + /** + * Returns the polygon used to display the completed value. + * + * @return the polygon used to display the completed value + */ + SVGOMPolygonElement getCompletedPolygon(); + + /** + * Sets the polygon used to display the completed value. + * + * @param polygon + * the new polygon used to display the completed value + */ + void setCompletedPolygon(SVGOMPolygonElement polygon); +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGShape.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGShape.java new file mode 100644 index 0000000..8ebc338 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGShape.java
@@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +public interface SVGShape { + public void setIteration(final int iteration); + + // public void setErrors(final int errors); + + // public void setCompleted(final float complete); +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGUtil.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGUtil.java new file mode 100644 index 0000000..f2e4247 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/SVGUtil.java
@@ -0,0 +1,477 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg; + +import static java.lang.Float.parseFloat; +import static java.lang.Math.PI; +import static java.lang.Math.atan2; +import static org.apache.batik.dom.svg.SVGDOMImplementation.getDOMImplementation; +import static org.apache.batik.util.SMILConstants.SMIL_ATTRIBUTE_NAME_ATTRIBUTE; +import static org.apache.batik.util.SMILConstants.SMIL_DUR_ATTRIBUTE; +import static org.apache.batik.util.SMILConstants.SMIL_FILL_ATTRIBUTE; +import static org.apache.batik.util.SMILConstants.SMIL_FREEZE_VALUE; +import static org.apache.batik.util.SMILConstants.SMIL_FROM_ATTRIBUTE; +import static org.apache.batik.util.SMILConstants.SMIL_TO_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_TYPE_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_X1_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_X2_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_Y1_ATTRIBUTE; +import static org.apache.batik.util.SVGConstants.SVG_Y2_ATTRIBUTE; +import static org.apache.batik.util.XMLResourceDescriptor.getXMLParserClassName; + +import java.awt.Color; +import java.awt.Point; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.util.List; + +import net.sf.taverna.t2.lang.io.StreamDevourer; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape; + +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.dom.svg.SVGDOMImplementation; +import org.apache.batik.dom.svg.SVGOMAnimationElement; +import org.apache.batik.dom.svg.SVGOMPoint; +import org.apache.log4j.Logger; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Element; +import org.w3c.dom.svg.SVGDocument; +import org.w3c.dom.svg.SVGElement; +import org.w3c.dom.svg.SVGLocatable; +import org.w3c.dom.svg.SVGMatrix; +//import org.apache.batik.transcoder.TranscoderException; +//import org.apache.batik.transcoder.svg2svg.PrettyPrinter; + +/** + * Utility methods. + * + * @author David Withers + */ +public class SVGUtil { + private static final String C = "C"; + private static final String M = "M"; + private static final String SPACE = " "; + private static final String COMMA = ","; + public static final String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; + private static final String SVG = "svg"; + private static final Logger logger = Logger.getLogger(SVGUtil.class); + + private static SAXSVGDocumentFactory docFactory; + + static { + String parser = getXMLParserClassName(); + logger.info("Using XML parser " + parser); + docFactory = new SAXSVGDocumentFactory(parser); + } + + /** + * Creates a new SVGDocument. + * + * @return a new SVGDocument + */ + public static SVGDocument createSVGDocument() { + DOMImplementation impl = getDOMImplementation(); + return (SVGDocument) impl.createDocument(svgNS, SVG, null); + } + + /** + * Converts a point in screen coordinates to a point in document + * coordinates. + * + * @param locatable + * @param screenPoint + * the point in screen coordinates + * @return the point in document coordinates + */ + public static SVGOMPoint screenToDocument(SVGLocatable locatable, + SVGOMPoint screenPoint) { + SVGMatrix mat = ((SVGLocatable) locatable.getFarthestViewportElement()) + .getScreenCTM().inverse(); + return (SVGOMPoint) screenPoint.matrixTransform(mat); + } + + /** + * Writes SVG to the console. For debugging only. + * + * @param svgDocument + * the document to output + */ +// public static void writeSVG(SVGDocument svgDocument) { +// writeSVG(svgDocument, new OutputStreamWriter(System.out)); +// } + + /** + * Writes SVG to an output stream. + * + * @param svgDocument + * the document to output + * @param writer + * the stream to write the document to + */ +// public static void writeSVG(SVGDocument svgDocument, Writer writer) { +// StringWriter sw = new StringWriter(); +// try { +// Transformer transformer = TransformerFactory.newInstance().newTransformer(); +// Source src = new DOMSource(svgDocument.getDocumentElement()); +// transformer.transform(src, new StreamResult(sw)); +// +// PrettyPrinter pp = new PrettyPrinter(); +// pp.print(new StringReader(sw.toString()), writer); +// } catch (TransformerException | TranscoderException | IOException e) { +// e.printStackTrace(new PrintWriter(writer)); +// } +// } + + /** + * Generates an SVGDocument from DOT text by calling out to GraphViz. + * + * @param dotText + * @return an SVGDocument + * @throws IOException + */ + public static SVGDocument getSVG(String dotText, + WorkbenchConfiguration workbenchConfiguration) throws IOException { + String dotLocation = (String) workbenchConfiguration + .getProperty("taverna.dotlocation"); + if (dotLocation == null) + dotLocation = "dot"; + logger.debug("Invoking dot..."); + Process dotProcess = exec(dotLocation, "-Tsvg"); + StreamDevourer devourer = new StreamDevourer( + dotProcess.getInputStream()); + devourer.start(); + try (PrintWriter out = new PrintWriter(dotProcess.getOutputStream(), + true)) { + out.print(dotText); + out.flush(); + } + + String svgText = devourer.blockOnOutput(); + /* + * Avoid TAV-424, replace buggy SVG outputted by "modern" GraphViz + * versions. http://www.graphviz.org/bugs/b1075.html + * + * Contributed by Marko Ullgren + */ + svgText = svgText.replaceAll("font-weight:regular", + "font-weight:normal"); + logger.info(svgText); + // Fake URI, just used for internal references like #fish + return docFactory.createSVGDocument( + "http://taverna.sf.net/diagram/generated.svg", + new StringReader(svgText)); + } + + /** + * Generates DOT text with layout information from DOT text by calling out + * to GraphViz. + * + * @param dotText + * dot text + * @return dot text with layout information + * @throws IOException + */ + public static String getDot(String dotText, + WorkbenchConfiguration workbenchConfiguration) throws IOException { + String dotLocation = (String) workbenchConfiguration + .getProperty("taverna.dotlocation"); + if (dotLocation == null) + dotLocation = "dot"; + logger.debug("Invoking dot..."); + Process dotProcess = exec(dotLocation, "-Tdot", "-Glp=0,0"); + StreamDevourer devourer = new StreamDevourer( + dotProcess.getInputStream()); + devourer.start(); + try (PrintWriter out = new PrintWriter(dotProcess.getOutputStream(), + true)) { + out.print(dotText); + out.flush(); + } + + String dot = devourer.blockOnOutput(); + // logger.info(dot); + return dot; + } + + private static Process exec(String...args) throws IOException { + Process p = Runtime.getRuntime().exec(args); + /* + * Must create an error devourer otherwise stderr fills up and the + * process stalls! + */ + new StreamDevourer(p.getErrorStream()).start(); + return p; + } + + /** + * Returns the hex value for a <code>Color</code>. If color is null "none" + * is returned. + * + * @param color + * the <code>Color</code> to convert to hex code + * @return the hex value + */ + public static String getHexValue(Color color) { + if (color == null) + return "none"; + + return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), + color.getBlue()); + } + + /** + * Calculates the angle to rotate an arrow head to be placed on the end of a + * line. + * + * @param line + * the line to calculate the arrow head angle from + * @return the angle to rotate an arrow head + */ + public static double calculateAngle(Element line) { + float x1 = parseFloat(line.getAttribute(SVG_X1_ATTRIBUTE)); + float y1 = parseFloat(line.getAttribute(SVG_Y1_ATTRIBUTE)); + float x2 = parseFloat(line.getAttribute(SVG_X2_ATTRIBUTE)); + float y2 = parseFloat(line.getAttribute(SVG_Y2_ATTRIBUTE)); + return calculateAngle(x1, y1, x2, y2); + } + + /** + * Calculates the angle to rotate an arrow head to be placed on the end of a + * line. + * + * @param pointList + * the list of <code>Point</code>s to calculate the arrow head + * angle from + * @return the angle to rotate an arrow head + */ + public static double calculateAngle(List<Point> pointList) { + double angle = 0d; + if (pointList.size() > 1) { + int listSize = pointList.size(); + Point a = pointList.get(listSize - 2); + Point b = pointList.get(listSize - 1); + /* + * dot sometimes generates paths with the same point repeated at the + * end of the path, so move back along the path until two different + * points are found + */ + while (a.equals(b) && listSize > 2) { + b = a; + a = pointList.get(--listSize - 2); + } + angle = calculateAngle(a.x, a.y, b.x, b.y); + } + return angle; + } + + /** + * Calculates the angle to rotate an arrow head to be placed on the end of a + * line. + * + * @param x1 + * the x coordinate of the start of the line + * @param y1 + * the y coordinate of the start of the line + * @param x2 + * the x coordinate of the end of the line + * @param y2 + * the y coordinate of the end of the line + * @return the angle to rotate an arrow head + */ + public static double calculateAngle(float x1, float y1, float x2, float y2) { + return atan2(y2 - y1, x2 - x1) * 180 / PI; + } + + /** + * Calculates the points that make up the polygon for the specified + * {@link Shape}. + * + * @param shape + * the <code>Shape</code> to calculate points for + * @param width + * the width of the <code>Shape</code> + * @param height + * the height of the <code>Shape</code> + * @return the points that make up the polygon for the specified + * <code>Shape</code> + */ + public static String calculatePoints(Shape shape, int width, int height) { + StringBuilder sb = new StringBuilder(); + switch (shape) { + case BOX: + case RECORD: + addPoint(sb, 0, 0); + addPoint(sb, width, 0); + addPoint(sb, width, height); + addPoint(sb, 0, height); + break; + case HOUSE: + addPoint(sb, width / 2f, 0); + addPoint(sb, width, height / 3f); + addPoint(sb, width, height - 3); + addPoint(sb, 0, height - 3); + addPoint(sb, 0, height / 3f); + break; + case INVHOUSE: + addPoint(sb, 0, 3); + addPoint(sb, width, 3); + addPoint(sb, width, height / 3f * 2f); + addPoint(sb, width / 2f, height); + addPoint(sb, 0, height / 3f * 2f); + break; + case TRIANGLE: + addPoint(sb, width / 2f, 0); + addPoint(sb, width, height); + addPoint(sb, 0, height); + break; + case INVTRIANGLE: + addPoint(sb, 0, 0); + addPoint(sb, width, 0); + addPoint(sb, width / 2f, height); + break; + default: + // Nothing to do for the others + break; + } + return sb.toString(); + } + + /** + * Appends x y coordinates to a <code>StringBuilder</code> in the format + * "x,y ". + * + * @param stringBuilder + * the <code>StringBuilder</code> to append the point to + * @param x + * the x coordinate + * @param y + * the y coordinate + */ + public static void addPoint(StringBuilder stringBuilder, float x, float y) { + stringBuilder.append(x).append(COMMA).append(y).append(SPACE); + } + + /** + * Converts a list of points into a string format for a cubic Bezier curve. + * + * For example, "M100,200 C100,100 250,100 250,200". See + * http://www.w3.org/TR/SVG11/paths.html#PathDataCubicBezierCommands. + * + * @param pointList + * a list of points that describes a cubic Bezier curve + * @return a string that describes a cubic Bezier curve + */ + public static String getPath(List<Point> pointList) { + StringBuilder sb = new StringBuilder(); + if (pointList != null && pointList.size() > 1) { + Point firstPoint = pointList.get(0); + sb.append(M).append(firstPoint.x).append(COMMA) + .append(firstPoint.y); + sb.append(SPACE); + Point secontPoint = pointList.get(1); + sb.append(C).append(secontPoint.x).append(COMMA) + .append(secontPoint.y); + for (int i = 2; i < pointList.size(); i++) { + Point point = pointList.get(i); + sb.append(SPACE).append(point.x).append(COMMA).append(point.y); + } + } + return sb.toString(); + } + + /** + * Creates an animation element. + * + * @param graphController + * the SVGGraphController to use to create the animation element + * @param elementType + * the type of animation element to create + * @param attribute + * the attribute that the animation should affect + * @param transformType + * the type of transform - use null not creating a transform + * animation + * @return an new animation element + */ + public static SVGOMAnimationElement createAnimationElement( + SVGGraphController graphController, String elementType, + String attribute, String transformType) { + SVGOMAnimationElement animationElement = (SVGOMAnimationElement) graphController + .createElement(elementType); + animationElement.setAttribute(SMIL_ATTRIBUTE_NAME_ATTRIBUTE, attribute); + if (transformType != null) + animationElement.setAttribute(SVG_TYPE_ATTRIBUTE, transformType); + animationElement.setAttribute(SMIL_FILL_ATTRIBUTE, SMIL_FREEZE_VALUE); + return animationElement; + } + + /** + * Adds an animation to the SVG element and starts the animation. + * + * @param animate + * that animation element + * @param element + * the element to animate + * @param duration + * the duration of the animation in milliseconds + * @param from + * the starting point for the animation, can be null + * @param to + * the end point for the animation, cannot be null + */ + public static void animate(SVGOMAnimationElement animate, SVGElement element, int duration, + String from, String to) { + animate.setAttribute(SMIL_DUR_ATTRIBUTE, duration + "ms"); + if (from != null) + animate.setAttribute(SMIL_FROM_ATTRIBUTE, from); + animate.setAttribute(SMIL_TO_ATTRIBUTE, to); + element.appendChild(animate); + try { + animate.beginElement(); + } catch (NullPointerException e) { + } + } + + /** + * Adjusts the length of <code>pointList</code> by adding or removing points + * to make the length equal to <code>size</code>. If <code>pointList</code> + * is shorter than <code>size</code> the last point is repeated. If + * <code>pointList</code> is longer than <code>size</code> points at the end + * of the list are removed. + * + * @param pointList + * the path to adjust + * @param size + * the required size for <code>pointList</code> + */ + public static void adjustPathLength(List<Point> pointList, int size) { + if (pointList.size() < size) { + Point lastPoint = pointList.get(pointList.size() - 1); + for (int i = pointList.size(); i < size; i++) + pointList.add(lastPoint); + } else if (pointList.size() > size) { + for (int i = pointList.size(); i > size; i--) + pointList.remove(i - 1); + } + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGEventListener.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGEventListener.java new file mode 100644 index 0000000..95b4181 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGEventListener.java
@@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg.event; + +import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.screenToDocument; +import net.sf.taverna.t2.workbench.models.graph.GraphElement; + +import org.apache.batik.dom.svg.SVGOMPoint; +import org.w3c.dom.events.Event; +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.MouseEvent; +import org.w3c.dom.svg.SVGLocatable; + +/** + * Abstract superclass for SVG event listeners. + * + * @author David Withers + */ +public abstract class SVGEventListener implements EventListener { + protected GraphElement graphElement; + + public SVGEventListener(GraphElement graphElement) { + this.graphElement = graphElement; + } + + protected abstract void event(SVGOMPoint point, MouseEvent evt); + + @Override + public final void handleEvent(Event evt) { + if (evt instanceof MouseEvent) { + MouseEvent me = (MouseEvent) evt; + SVGOMPoint point = screenToDocument((SVGLocatable) me.getTarget(), + new SVGOMPoint(me.getClientX(), me.getClientY())); + event(point, me); + evt.stopPropagation(); + } + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseClickEventListener.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseClickEventListener.java new file mode 100644 index 0000000..0c13be3 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseClickEventListener.java
@@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg.event; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement; + +import org.apache.batik.dom.svg.SVGOMPoint; +import org.w3c.dom.events.MouseEvent; + +/** + * SVG event listener for handling mouse click events. + * + * @author David Withers + */ +public class SVGMouseClickEventListener extends SVGEventListener { + public SVGMouseClickEventListener(GraphElement graphElement) { + super(graphElement); + } + + @Override + protected void event(SVGOMPoint point, MouseEvent evt) { + graphElement.getEventManager().mouseClicked(graphElement, + evt.getButton(), evt.getAltKey(), evt.getCtrlKey(), + evt.getMetaKey(), (int) point.getX(), (int) point.getY(), + evt.getScreenX(), evt.getScreenY()); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseDownEventListener.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseDownEventListener.java new file mode 100644 index 0000000..bd69506 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseDownEventListener.java
@@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg.event; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement; + +import org.apache.batik.dom.svg.SVGOMPoint; +import org.w3c.dom.events.MouseEvent; + +/** + * SVG event listener for handling mouse button down events. + * + * @author David Withers + */ +public class SVGMouseDownEventListener extends SVGEventListener { + public SVGMouseDownEventListener(GraphElement graphElement) { + super(graphElement); + } + + @Override + protected void event(SVGOMPoint point, MouseEvent evt) { + graphElement.getEventManager().mouseDown(graphElement, evt.getButton(), + evt.getAltKey(), evt.getCtrlKey(), evt.getMetaKey(), + (int) point.getX(), (int) point.getY(), evt.getScreenX(), + evt.getScreenY()); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseMovedEventListener.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseMovedEventListener.java new file mode 100644 index 0000000..6ae5d50 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseMovedEventListener.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg.event; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement; + +import org.apache.batik.dom.svg.SVGOMPoint; +import org.w3c.dom.events.MouseEvent; + +/** + * SVG event listener for handling mouse movement events. + * + * @author David Withers + */ +public class SVGMouseMovedEventListener extends SVGEventListener { + public SVGMouseMovedEventListener(GraphElement graphElement) { + super(graphElement); + } + + @Override + protected void event(SVGOMPoint point, MouseEvent mouseEvent) { + graphElement.getEventManager().mouseMoved(graphElement, + mouseEvent.getButton(), mouseEvent.getAltKey(), + mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(), + (int) point.getX(), (int) point.getY(), + mouseEvent.getScreenX(), mouseEvent.getScreenY()); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOutEventListener.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOutEventListener.java new file mode 100644 index 0000000..32714a6 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOutEventListener.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg.event; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement; + +import org.apache.batik.dom.svg.SVGOMPoint; +import org.w3c.dom.events.MouseEvent; + +/** + * SVG event listener for handling mouse button up events. + * + * @author David Withers + */ +public class SVGMouseOutEventListener extends SVGEventListener { + public SVGMouseOutEventListener(GraphElement graphElement) { + super(graphElement); + } + + @Override + protected void event(SVGOMPoint point, MouseEvent mouseEvent) { + graphElement.getEventManager().mouseOut(graphElement, + mouseEvent.getButton(), mouseEvent.getAltKey(), + mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(), + (int) point.getX(), (int) point.getY(), + mouseEvent.getScreenX(), mouseEvent.getScreenY()); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOverEventListener.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOverEventListener.java new file mode 100644 index 0000000..1c5f9a4 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseOverEventListener.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg.event; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement; + +import org.apache.batik.dom.svg.SVGOMPoint; +import org.w3c.dom.events.MouseEvent; + +/** + * SVG event listener for handling mouse button up events. + * + * @author David Withers + */ +public class SVGMouseOverEventListener extends SVGEventListener { + public SVGMouseOverEventListener(GraphElement graphElement) { + super(graphElement); + } + + @Override + protected void event(SVGOMPoint point, MouseEvent mouseEvent) { + graphElement.getEventManager().mouseOver(graphElement, + mouseEvent.getButton(), mouseEvent.getAltKey(), + mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(), + (int) point.getX(), (int) point.getY(), + mouseEvent.getScreenX(), mouseEvent.getScreenY()); + } +}
diff --git a/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseUpEventListener.java b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseUpEventListener.java new file mode 100644 index 0000000..492ecc2 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/java/net/sf/taverna/t2/workbench/models/graph/svg/event/SVGMouseUpEventListener.java
@@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph.svg.event; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement; + +import org.apache.batik.dom.svg.SVGOMPoint; +import org.w3c.dom.events.MouseEvent; + +/** + * SVG event listener for handling mouse button up events. + * + * @author David Withers + */ +public class SVGMouseUpEventListener extends SVGEventListener { + public SVGMouseUpEventListener(GraphElement graphElement) { + super(graphElement); + } + + @Override + protected void event(SVGOMPoint point, MouseEvent mouseEvent) { + graphElement.getEventManager().mouseUp(graphElement, + mouseEvent.getButton(), mouseEvent.getAltKey(), + mouseEvent.getCtrlKey(), mouseEvent.getMetaKey(), + (int) point.getX(), (int) point.getY(), + mouseEvent.getScreenX(), mouseEvent.getScreenY()); + } +}
diff --git a/taverna-workbench-graph-model/src/main/jjtree/NamedNode.java b/taverna-workbench-graph-model/src/main/jjtree/NamedNode.java new file mode 100644 index 0000000..da92a97 --- /dev/null +++ b/taverna-workbench-graph-model/src/main/jjtree/NamedNode.java
@@ -0,0 +1,65 @@ +package net.sf.taverna.t2.workbench.models.graph.dot; + +public class NamedNode { + + protected String name, value, port; + + /** + * Returns the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Sets the value of name. + * + * @param name + * the new value for name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the value. + * + * @return the value + */ + public String getValue() { + return value; + } + + /** + * Sets the value of value. + * + * @param value + * the new value for value + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Returns the port. + * + * @return the port + */ + public String getPort() { + return port; + } + + /** + * Sets the value of port. + * + * @param port + * the new value for port + */ + public void setPort(String port) { + this.port = port; + } + +} +
diff --git a/taverna-workbench-graph-model/src/main/jjtree/dotparser.jjt b/taverna-workbench-graph-model/src/main/jjtree/dotparser.jjt new file mode 100644 index 0000000..001450b --- /dev/null +++ b/taverna-workbench-graph-model/src/main/jjtree/dotparser.jjt
@@ -0,0 +1,289 @@ +/** + * JavaCC grammar for parsing Graphviz DOT files. + * Written by Lynn Monson at lm@lmonson.com + * Modified by David Withers + */ +options { + STATIC = false; + UNICODE_INPUT = true; + VISITOR = true; + MULTI=true; + NODE_EXTENDS="NamedNode"; +} + +/*=========================================================================================== + Parser java class + ===========================================================================================*/ +PARSER_BEGIN(DOTParser) +package net.sf.taverna.t2.workbench.models.graph.dot; +import java.io.*; + +class DOTParser { + + public static void parseDot(Reader r) throws IOException, ParseException { + new DOTParser(r).parse().dump(" "); + } + + public static void main(String[] args) throws Exception + { + parseDot(new FileReader(args[0])); + } + +} +PARSER_END(DOTParser) + + +// whitespace +SKIP : { " " | "\t" | "\n" | "\r" | "\f" } + +// comments +MORE : +{ + "//" : IN_SINGLE_LINE_COMMENT +| + "/*" : IN_MULTI_LINE_COMMENT +} + +<IN_SINGLE_LINE_COMMENT> +SPECIAL_TOKEN : +{ + <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : DEFAULT +} + +<IN_MULTI_LINE_COMMENT> +SPECIAL_TOKEN : +{ + <MULTI_LINE_COMMENT: "*/" > : DEFAULT +} + +<IN_SINGLE_LINE_COMMENT,IN_MULTI_LINE_COMMENT> +MORE : { < ~[] > } + +// Case insensitive keywords +TOKEN [IGNORE_CASE] : { + <DIGRAPH: ( "digraph" )> + | <EDGE: ( "edge" )> + | <GRAPH: ( "graph" )> + | <NODE: ( "node" )> + | <STRICT: ( "strict" )> + | <SUBGRAPH: ( "subgraph" )> +} + +// All other tokens +TOKEN: +{ + // ------------------------------------------- + // Various pieces of syntax as lexical tokens + // ------------------------------------------- + <EQ: "="> + | <LBRACE: "{"> + | <RBRACE: "}"> + | <EDGE_UNDIRECTED: "--"> + | <EDGE_DIRECTED: "->"> + | <LBRACKET: "["> + | <RBRACKET: "]"> + | <COMMA: ","> + | <SEMICOLON: ";"> + | <COLON: ":"> + + + // ------------------------------------------- + // Identifiers + // ------------------------------------------- + | <ID: <$HtmlString> | <$Number> | <$UnquotedString> | <$QuotedString> > + | <HTML: <$HtmlString>> + | <#$HtmlString: ( "<" ( "<" (~[">"])* ">" (~["<", ">"])* )* ">" ) > + + // ------------------------------------------- + // Character definitions (taken from xpath) + // ------------------------------------------- + + | <#$BaseChar: + ["\u0041"-"\u005A"] | ["\u0061"-"\u007A"] | ["\u00C0"-"\u00D6"] | ["\u00D8"-"\u00F6"] + | ["\u00F8"-"\u00FF"] | ["\u0100"-"\u0131"] | ["\u0134"-"\u013E"] | ["\u0141"-"\u0148"] + | ["\u014A"-"\u017E"] | ["\u0180"-"\u01C3"] | ["\u01CD"-"\u01F0"] | ["\u01F4"-"\u01F5"] + | ["\u01FA"-"\u0217"] | ["\u0250"-"\u02A8"] | ["\u02BB"-"\u02C1"] | "\u0386" | ["\u0388"-"\u038A"] + | "\u038C" | ["\u038E"-"\u03A1"] | ["\u03A3"-"\u03CE"] | ["\u03D0"-"\u03D6"] | "\u03DA" + | "\u03DC" | "\u03DE" | "\u03E0" | ["\u03E2"-"\u03F3"] | ["\u0401"-"\u040C"] | ["\u040E"-"\u044F"] + | ["\u0451"-"\u045C"] | ["\u045E"-"\u0481"] | ["\u0490"-"\u04C4"] | ["\u04C7"-"\u04C8"] + | ["\u04CB"-"\u04CC"] | ["\u04D0"-"\u04EB"] | ["\u04EE"-"\u04F5"] | ["\u04F8"-"\u04F9"] + | ["\u0531"-"\u0556"] | "\u0559" | ["\u0561"-"\u0586"] | ["\u05D0"-"\u05EA"] | ["\u05F0"-"\u05F2"] + | ["\u0621"-"\u063A"] | ["\u0641"-"\u064A"] | ["\u0671"-"\u06B7"] | ["\u06BA"-"\u06BE"] + | ["\u06C0"-"\u06CE"] | ["\u06D0"-"\u06D3"] | "\u06D5" | ["\u06E5"-"\u06E6"] | ["\u0905"-"\u0939"] + | "\u093D" | ["\u0958"-"\u0961"] | ["\u0985"-"\u098C"] | ["\u098F"-"\u0990"] | ["\u0993"-"\u09A8"] + | ["\u09AA"-"\u09B0"] | "\u09B2" | ["\u09B6"-"\u09B9"] | ["\u09DC"-"\u09DD"] | ["\u09DF"-"\u09E1"] + | ["\u09F0"-"\u09F1"] | ["\u0A05"-"\u0A0A"] | ["\u0A0F"-"\u0A10"] | ["\u0A13"-"\u0A28"] + | ["\u0A2A"-"\u0A30"] | ["\u0A32"-"\u0A33"] | ["\u0A35"-"\u0A36"] | ["\u0A38"-"\u0A39"] + | ["\u0A59"-"\u0A5C"] | "\u0A5E" | ["\u0A72"-"\u0A74"] | ["\u0A85"-"\u0A8B"] | "\u0A8D" + | ["\u0A8F"-"\u0A91"] | ["\u0A93"-"\u0AA8"] | ["\u0AAA"-"\u0AB0"] | ["\u0AB2"-"\u0AB3"] + | ["\u0AB5"-"\u0AB9"] | "\u0ABD" | "\u0AE0" | ["\u0B05"-"\u0B0C"] | ["\u0B0F"-"\u0B10"] + | ["\u0B13"-"\u0B28"] | ["\u0B2A"-"\u0B30"] | ["\u0B32"-"\u0B33"] | ["\u0B36"-"\u0B39"] + | "\u0B3D" | ["\u0B5C"-"\u0B5D"] | ["\u0B5F"-"\u0B61"] | ["\u0B85"-"\u0B8A"] + | ["\u0B8E"-"\u0B90"] | ["\u0B92"-"\u0B95"] | ["\u0B99"-"\u0B9A"] | "\u0B9C" | ["\u0B9E"-"\u0B9F"] + | ["\u0BA3"-"\u0BA4"] | ["\u0BA8"-"\u0BAA"] | ["\u0BAE"-"\u0BB5"] | ["\u0BB7"-"\u0BB9"] + | ["\u0C05"-"\u0C0C"] | ["\u0C0E"-"\u0C10"] | ["\u0C12"-"\u0C28"] | ["\u0C2A"-"\u0C33"] + | ["\u0C35"-"\u0C39"] | ["\u0C60"-"\u0C61"] | ["\u0C85"-"\u0C8C"] | ["\u0C8E"-"\u0C90"] + | ["\u0C92"-"\u0CA8"] | ["\u0CAA"-"\u0CB3"] | ["\u0CB5"-"\u0CB9"] | "\u0CDE" | ["\u0CE0"-"\u0CE1"] + | ["\u0D05"-"\u0D0C"] | ["\u0D0E"-"\u0D10"] | ["\u0D12"-"\u0D28"] | ["\u0D2A"-"\u0D39"] + | ["\u0D60"-"\u0D61"] | ["\u0E01"-"\u0E2E"] | "\u0E30" | ["\u0E32"-"\u0E33"] | ["\u0E40"-"\u0E45"] + | ["\u0E81"-"\u0E82"] | "\u0E84" | ["\u0E87"-"\u0E88"] | "\u0E8A" | "\u0E8D" | ["\u0E94"-"\u0E97"] + | ["\u0E99"-"\u0E9F"] | ["\u0EA1"-"\u0EA3"] | "\u0EA5" | "\u0EA7" | ["\u0EAA"-"\u0EAB"] + | ["\u0EAD"-"\u0EAE"] | "\u0EB0" | ["\u0EB2"-"\u0EB3"] | "\u0EBD" | ["\u0EC0"-"\u0EC4"] + | ["\u0F40"-"\u0F47"] | ["\u0F49"-"\u0F69"] | ["\u10A0"-"\u10C5"] | ["\u10D0"-"\u10F6"] | "\u1100" + | ["\u1102"-"\u1103"] | ["\u1105"-"\u1107"] | "\u1109" | ["\u110B"-"\u110C"] | ["\u110E"-"\u1112"] + | "\u113C" | "\u113E" | "\u1140" | "\u114C" | "\u114E" | "\u1150" | ["\u1154"-"\u1155"] | "\u1159" + | ["\u115F"-"\u1161"] | "\u1163" | "\u1165" | "\u1167" | "\u1169" | ["\u116D"-"\u116E"] + | ["\u1172"-"\u1173"] | "\u1175" | "\u119E" | "\u11A8" | "\u11AB" | ["\u11AE"-"\u11AF"] + | ["\u11B7"-"\u11B8"] | "\u11BA" | ["\u11BC"-"\u11C2"] | "\u11EB" | "\u11F0" | "\u11F9" + | ["\u1E00"-"\u1E9B"] | ["\u1EA0"-"\u1EF9"] | ["\u1F00"-"\u1F15"] | ["\u1F18"-"\u1F1D"] + | ["\u1F20"-"\u1F45"] | ["\u1F48"-"\u1F4D"] | ["\u1F50"-"\u1F57"] | "\u1F59" | "\u1F5B" | "\u1F5D" + | ["\u1F5F"-"\u1F7D"] | ["\u1F80"-"\u1FB4"] | ["\u1FB6"-"\u1FBC"] | "\u1FBE" | ["\u1FC2"-"\u1FC4"] + | ["\u1FC6"-"\u1FCC"] | ["\u1FD0"-"\u1FD3"] | ["\u1FD6"-"\u1FDB"] | ["\u1FE0"-"\u1FEC"] + | ["\u1FF2"-"\u1FF4"] | ["\u1FF6"-"\u1FFC"] | "\u2126" | ["\u212A"-"\u212B"] | "\u212E" + | ["\u2180"-"\u2182"] | ["\u3041"-"\u3094"] | ["\u30A1"-"\u30FA"] | ["\u3105"-"\u312C"] + | ["\uAC00"-"\uD7A3"] > +| <#$Ideographic : ["\u4E00"-"\u9FA5"] | "\u3007" | ["\u3021"-"\u3029"] > +| <#CombiningChar : + ["\u0300"-"\u0345"] | ["\u0360"-"\u0361"] | ["\u0483"-"\u0486"] | ["\u0591"-"\u05A1"] + | ["\u05A3"-"\u05B9"] | ["\u05BB"-"\u05BD"] | "\u05BF" | ["\u05C1"-"\u05C2"] | "\u05C4" + | ["\u064B"-"\u0652"] | "\u0670" | ["\u06D6"-"\u06DC"] | ["\u06DD"-"\u06DF"] + | ["\u06E0"-"\u06E4"] | ["\u06E7"-"\u06E8"] | ["\u06EA"-"\u06ED"] | ["\u0901"-"\u0903"] + | "\u093C" | ["\u093E"-"\u094C"] | "\u094D" | ["\u0951"-"\u0954"] | ["\u0962"-"\u0963"] + | ["\u0981"-"\u0983"] | "\u09BC" | "\u09BE" | "\u09BF" | ["\u09C0"-"\u09C4"] | ["\u09C7"-"\u09C8"] + | ["\u09CB"-"\u09CD"] | "\u09D7" | ["\u09E2"-"\u09E3"] | "\u0A02" | "\u0A3C" | "\u0A3E" + | "\u0A3F" | ["\u0A40"-"\u0A42"] | ["\u0A47"-"\u0A48"] | ["\u0A4B"-"\u0A4D"] | ["\u0A70"-"\u0A71"] + | ["\u0A81"-"\u0A83"] | "\u0ABC" | ["\u0ABE"-"\u0AC5"] | ["\u0AC7"-"\u0AC9"] | ["\u0ACB"-"\u0ACD"] + | ["\u0B01"-"\u0B03"] | "\u0B3C" | ["\u0B3E"-"\u0B43"] | ["\u0B47"-"\u0B48"] | ["\u0B4B"-"\u0B4D"] + | ["\u0B56"-"\u0B57"] | ["\u0B82"-"\u0B83"] | ["\u0BBE"-"\u0BC2"] | ["\u0BC6"-"\u0BC8"] + | ["\u0BCA"-"\u0BCD"] | "\u0BD7" | ["\u0C01"-"\u0C03"] | ["\u0C3E"-"\u0C44"] | ["\u0C46"-"\u0C48"] + | ["\u0C4A"-"\u0C4D"] | ["\u0C55"-"\u0C56"] | ["\u0C82"-"\u0C83"] | ["\u0CBE"-"\u0CC4"] + | ["\u0CC6"-"\u0CC8"] | ["\u0CCA"-"\u0CCD"] | ["\u0CD5"-"\u0CD6"] | ["\u0D02"-"\u0D03"] + | ["\u0D3E"-"\u0D43"] | ["\u0D46"-"\u0D48"] | ["\u0D4A"-"\u0D4D"] | "\u0D57" | "\u0E31" + | ["\u0E34"-"\u0E3A"] | ["\u0E47"-"\u0E4E"] | "\u0EB1" | ["\u0EB4"-"\u0EB9"] + | ["\u0EBB"-"\u0EBC"] | ["\u0EC8"-"\u0ECD"] | ["\u0F18"-"\u0F19"] | "\u0F35" | "\u0F37" | "\u0F39" + | "\u0F3E" | "\u0F3F" | ["\u0F71"-"\u0F84"] | ["\u0F86"-"\u0F8B"] | ["\u0F90"-"\u0F95"] | "\u0F97" + | ["\u0F99"-"\u0FAD"] | ["\u0FB1"-"\u0FB7"] | "\u0FB9" | ["\u20D0"-"\u20DC"] | "\u20E1" + | ["\u302A"-"\u302F"] | "\u3099" | "\u309A" > +| <#$Digit: + ["\u0030"-"\u0039"] | ["\u0660"-"\u0669"] | ["\u06F0"-"\u06F9"] | ["\u0966"-"\u096F"] + | ["\u09E6"-"\u09EF"] | ["\u0A66"-"\u0A6F"] | ["\u0AE6"-"\u0AEF"] | ["\u0B66"-"\u0B6F"] + | ["\u0BE7"-"\u0BEF"] | ["\u0C66"-"\u0C6F"] | ["\u0CE6"-"\u0CEF"] | ["\u0D66"-"\u0D6F"] + | ["\u0E50"-"\u0E59"] | ["\u0ED0"-"\u0ED9"] | ["\u0F20"-"\u0F29"] > +| <#Extender : + "\u00B7" | "\u02D0" | "\u02D1" | "\u0387" | "\u0640" | "\u0E46" | "\u0EC6" | "\u3005" + | ["\u3031"-"\u3035"] | ["\u309D"-"\u309E"] | ["\u30FC"-"\u30FE"] > + | <#$EscapedCharacter: "\\" (["\u0020"-"\u007E", "\n", "\r" ] | "\r\n" ) > + | <#$NotWhitespaceNotQuoteNotEscape:~["\"","\\","\n","\r"]> + | <#$Letter: <$BaseChar> | <$Ideographic> > + | <#$Number: ("-")? ("." (<$Digit>)+) | ((<$Digit>)+ ("." (<$Digit>)*)?)> + | <#$UnquotedString: (<$Letter>|"_"|<$Digit>)+ > + | <#$QuotedString: "\"" (<$NotWhitespaceNotQuoteNotEscape> | <$EscapedCharacter>)* "\"" > +} + +/*=========================================================================================== + DOT Grammar starts here + ===========================================================================================*/ +SimpleNode parse() #Parse : +{} +{ + graph() + { + return jjtThis; + } +} + +void graph() #Graph : +{Token t;} +{ + [<STRICT>] (<GRAPH>|<DIGRAPH>) t=<ID>{jjtThis.setName(t.image);} <LBRACE> stmt_list() <RBRACE> <EOF> +} + +void stmt_list() #StatementList : +{} +{ + stmt() [<SEMICOLON>] [stmt_list()] +} + + +void stmt() #Statement : +{} +{ + ( LOOKAHEAD(edge_stmt()) edge_stmt() | LOOKAHEAD(2) subgraph() | LOOKAHEAD(2) node_stmt() | LOOKAHEAD(2) attr_stmt() | LOOKAHEAD(2) (<ID> <EQ> <ID>) ) + +} + +/* +void ideq_stmt() : +{} +{ + <ID> <EQ> <ID> +} +*/ + +void attr_stmt() #AttributeStatement : +{Token t;} +{ + (t=<GRAPH>{jjtThis.setName(t.image);} | t=<NODE>{jjtThis.setName(t.image);} | t=<EDGE>{jjtThis.setName(t.image);}) attr_list() +} + +void node_stmt() #NodeStatement : +{} +{ + node_id(jjtThis) [attr_list()] +} + +void node_id(NamedNode namedNode) #NodeId : +{Token t;} +{ + t=<ID>{namedNode.setName(t.image);} [port(namedNode)] +} + +void port(NamedNode namedNode) #Port : +{Token t;} +{ + <COLON> (LOOKAHEAD(2) t=<ID>{namedNode.setPort(t.image);} <COLON> <ID> | <ID>) +} + +void edge_stmt() #EdgeStatement : +{} +{ + (node_id(jjtThis)| subgraph()) edgeRHS() [attr_list()] +} + +void subgraph() #Subgraph : +{Token t;} +{ + ( + LOOKAHEAD(2) + ([<SUBGRAPH> [t=<ID>{jjtThis.setName(t.image);}]] <LBRACE> stmt_list() <RBRACE>) + | + (<SUBGRAPH> t=<ID>{jjtThis.setName(t.image);}) + ) + +} + +void edgeRHS() #EdgeRHS : +{} +{ + edgeop() (node_id(jjtThis) | subgraph()) [ edgeRHS() ] +} + +void edgeop() #void : +{} +{ + <EDGE_UNDIRECTED> | <EDGE_DIRECTED> +} + + +void attr_list() #AttributeList : +{} +{ + <LBRACKET> a_list() <RBRACKET> +} + +void a_list() #AList : +{Token t;} +{ + t=<ID>{jjtThis.setName(t.image);} [<EQ> t=<ID>{jjtThis.setValue(t.image);}] [<COMMA>] [a_list()] +} +
diff --git a/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphControllerTest.java b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphControllerTest.java new file mode 100644 index 0000000..cb76b97 --- /dev/null +++ b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphControllerTest.java
@@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import uk.org.taverna.scufl2.api.core.Workflow; + +public class GraphControllerTest { + + Workflow dataflow; + + GraphController graphController; + + @Before + public void setUp() throws Exception { +// System.setProperty("raven.eclipse", "true"); +// setUpRavenRepository(); +// dataflow = WorkflowModelTranslator.doTranslation(loadScufl("nested_iteration.xml")); + graphController = new GraphController(dataflow, null, false, null, null, null, null) { + + @Override + public GraphEdge createGraphEdge() { + return new GraphEdge(this); + } + + @Override + public Graph createGraph() { + return new Graph(this); + } + + @Override + public GraphNode createGraphNode() { + return new GraphNode(this); + } + + @Override + public void redraw() { + + } + + }; + graphController.setPortStyle(PortStyle.NONE); + } + + @Test + @Ignore + public void testGenerateGraph() throws IOException, InterruptedException { + Graph graph = graphController.generateGraph(); + assertEquals(5, graph.getNodes().size()); + assertEquals(9, graph.getEdges().size()); + assertEquals(1, graph.getSubgraphs().size()); + } + +}
diff --git a/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphEdgeTest.java b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphEdgeTest.java new file mode 100644 index 0000000..10a3c20 --- /dev/null +++ b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphEdgeTest.java
@@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import net.sf.taverna.t2.workbench.models.graph.GraphEdge.ArrowStyle; + +import org.junit.Before; +import org.junit.Test; + +public class GraphEdgeTest { + + private GraphEdge edge; + + private GraphNode source; + + private GraphNode sink; + + private ArrowStyle arrowHeadStyle; + + private ArrowStyle arrowTailStyle; + + private GraphController graphController; + + @Before + public void setUp() throws Exception { + source = new GraphNode(graphController); + sink = new GraphNode(graphController); + arrowHeadStyle = ArrowStyle.DOT; + arrowTailStyle = ArrowStyle.NORMAL; + edge = new GraphEdge(graphController); + edge.setArrowHeadStyle(arrowHeadStyle); + edge.setArrowTailStyle(arrowTailStyle); + edge.setSink(sink); + edge.setSource(source); + } + + @Test + public void testEdge() { + edge = new GraphEdge(graphController); + assertNull(edge.getSource()); + assertNull(edge.getSink()); + assertNull(edge.getLabel()); + } + + @Test + public void testEdgeNodeNode() { + edge = new GraphEdge(graphController); + edge.setSource(source); + edge.setSink(sink); + assertEquals(source, edge.getSource()); + assertEquals(sink, edge.getSink()); + assertNull(edge.getLabel()); + } + + @Test + public void testGetSource() { + assertEquals(source, edge.getSource()); + } + + @Test + public void testSetSource() { + GraphNode node = new GraphNode(graphController); + edge.setSource(node); + assertEquals(node, edge.getSource()); + edge.setSource(null); + assertNull(edge.getSource()); + } + + @Test + public void testGetSink() { + assertEquals(sink, edge.getSink()); + } + + @Test + public void testSetSink() { + GraphNode node = new GraphNode(graphController); + edge.setSink(node); + assertEquals(node, edge.getSink()); + edge.setSink(null); + assertNull(edge.getSink()); + } + + @Test + public void testGetArrowHeadStyle() { + assertEquals(arrowHeadStyle, edge.getArrowHeadStyle()); + } + + @Test + public void testSetArrowHeadStyle() { + edge.setArrowHeadStyle(ArrowStyle.DOT); + assertEquals(ArrowStyle.DOT, edge.getArrowHeadStyle()); + edge.setArrowHeadStyle(null); + assertNull(edge.getArrowHeadStyle()); + } + + @Test + public void testGetArrowTailStyle() { + assertEquals(arrowTailStyle, edge.getArrowTailStyle()); + } + + @Test + public void testSetArrowTailStyle() { + edge.setArrowTailStyle(ArrowStyle.NORMAL); + assertEquals(ArrowStyle.NORMAL, edge.getArrowTailStyle()); + edge.setArrowTailStyle(null); + assertNull(edge.getArrowTailStyle()); + } + +}
diff --git a/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphElementTest.java b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphElementTest.java new file mode 100644 index 0000000..8d6b7f8 --- /dev/null +++ b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphElementTest.java
@@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.awt.Color; + +import net.sf.taverna.t2.workbench.models.graph.GraphElement.LineStyle; + +import org.junit.Before; +import org.junit.Test; + +public class GraphElementTest { + + private GraphElement element; + + private String id; + + private String label; + + private LineStyle lineStyle; + + private Color color; + + private Color fillColor; + + private GraphElement parent; + + private GraphController graphController; + + @Before + public void setUp() throws Exception { + element = new GraphElement(graphController) {}; + id = "element-id"; + label = "element-label"; + lineStyle = LineStyle.NONE; + color = Color.BLUE; + fillColor = Color.GREEN; + parent = new GraphNode(graphController); + element.setId(id); + element.setLabel(label); + element.setLineStyle(lineStyle); + element.setColor(color); + element.setFillColor(fillColor); + element.setParent(parent); + } + + @Test + public void testGetParent() { + assertEquals(parent, element.getParent()); + } + + @Test + public void testSetParent() { + GraphNode newParent = new GraphNode(graphController); + element.setParent(newParent); + assertEquals(newParent, element.getParent()); + element.setParent(null); + assertNull(element.getParent()); + } + + @Test + public void testGetLabel() { + assertEquals(label, element.getLabel()); + } + + @Test + public void testSetLabel() { + element.setLabel("new-label"); + assertEquals("new-label", element.getLabel()); + element.setLabel(null); + assertNull(element.getLabel()); + } + + @Test + public void testGetId() { + assertEquals(id, element.getId()); + } + + @Test + public void testSetId() { + element.setId("new-id"); + assertEquals("new-id", element.getId()); + element.setId(null); + assertNull(element.getId()); + } + + @Test + public void testGetColor() { + assertEquals(color, element.getColor()); + } + + @Test + public void testSetColor() { + element.setColor(Color.RED); + assertEquals(Color.RED, element.getColor()); + element.setColor(null); + assertNull(element.getColor()); + } + + @Test + public void testGetFillColor() { + assertEquals(fillColor, element.getFillColor()); + } + + @Test + public void testSetFillColor() { + element.setFillColor(Color.RED); + assertEquals(Color.RED, element.getFillColor()); + element.setFillColor(null); + assertNull(element.getFillColor()); + } + + @Test + public void testGetLineStyle() { + assertEquals(lineStyle, element.getLineStyle()); + } + + @Test + public void testSetLineStyle() { + element.setLineStyle(LineStyle.DOTTED); + assertEquals(LineStyle.DOTTED, element.getLineStyle()); + element.setLineStyle(null); + assertNull(element.getLineStyle()); + } + +}
diff --git a/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphNodeTest.java b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphNodeTest.java new file mode 100644 index 0000000..c5bcd6c --- /dev/null +++ b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphNodeTest.java
@@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.awt.Dimension; + +import net.sf.taverna.t2.workbench.models.graph.GraphShapeElement.Shape; + +import org.junit.Before; +import org.junit.Test; + +public class GraphNodeTest { + + private GraphNode node; + + private Shape shape; + + private Dimension size; + + private Graph graph; + + private boolean expanded; + + private GraphController graphController; + + @Before + public void setUp() throws Exception { + shape = Shape.HOUSE; + size = new Dimension(1, 2); + graph = new Graph(graphController); + expanded = false; + node = new GraphNode(graphController); + node.setShape(shape); + node.setSize(size); + node.setGraph(graph); + node.setExpanded(expanded); + } + + @Test + public void testNode() { + assertNotNull(new GraphNode(graphController)); + } + + @Test + public void testAddSinkNode() { + GraphNode newNode = new GraphNode(graphController); + node.addSinkNode(newNode); + assertEquals(1, node.getSinkNodes().size()); + assertTrue(node.getSinkNodes().contains(newNode)); + assertEquals(node, newNode.getParent()); + } + + @Test + public void testAddSourceNode() { + GraphNode newNode = new GraphNode(graphController); + node.addSourceNode(newNode); + assertEquals(1, node.getSourceNodes().size()); + assertTrue(node.getSourceNodes().contains(newNode)); + assertEquals(node, newNode.getParent()); + } + + @Test + public void testGetGraph() { + assertEquals(graph, node.getGraph()); + } + + @Test + public void testGetHeight() { + assertEquals(size.height, node.getHeight(), 0); + } + + @Test + public void testGetShape() { + assertEquals(shape, node.getShape()); + } + + @Test + public void testGetSinkNodes() { + assertNotNull(node.getSinkNodes()); + assertEquals(0, node.getSinkNodes().size()); + } + + @Test + public void testGetSize() { + assertEquals(size, node.getSize()); + } + + @Test + public void testGetSourceNodes() { + assertNotNull(node.getSourceNodes()); + assertEquals(0, node.getSourceNodes().size()); + } + + @Test + public void testGetWidth() { + assertEquals(size.width, node.getWidth(), 0); + } + + @Test + public void testIsExpanded() { + assertEquals(expanded, node.isExpanded()); + } + + @Test + public void testRemoveSinkNode() { + GraphNode newNode = new GraphNode(graphController); + assertFalse(node.removeSinkNode(newNode)); + node.addSinkNode(newNode); + assertTrue(node.removeSinkNode(newNode)); + assertFalse(node.getSinkNodes().contains(newNode)); + } + + @Test + public void testRemoveSourceNode() { + GraphNode newNode = new GraphNode(graphController); + assertFalse(node.removeSourceNode(newNode)); + node.addSourceNode(newNode); + assertTrue(node.removeSourceNode(newNode)); + assertFalse(node.getSourceNodes().contains(newNode)); + } + + @Test + public void testSetExpanded() { + node.setExpanded(true); + assertEquals(true, node.isExpanded()); + node.setExpanded(false); + assertEquals(false, node.isExpanded()); + } + + @Test + public void testSetGraph() { + Graph newGraph = new Graph(graphController); + node.setGraph(newGraph); + assertEquals(newGraph, node.getGraph()); + node.setGraph(null); + assertNull(node.getGraph()); + } + + @Test + public void testSetShape() { + node.setShape(Shape.INVTRIANGLE); + assertEquals(Shape.INVTRIANGLE, node.getShape()); + node.setShape(Shape.TRIANGLE); + assertEquals(Shape.TRIANGLE, node.getShape()); + } + + @Test + public void testSetSize() { + node.setSize(new Dimension(23, 6)); + assertEquals(new Dimension(23, 6), node.getSize()); + node.setSize(new Dimension(14, 4)); + assertEquals(new Dimension(14, 4), node.getSize()); + } + +}
diff --git a/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphTest.java b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphTest.java new file mode 100644 index 0000000..44a5aaf --- /dev/null +++ b/taverna-workbench-graph-model/src/test/java/net/sf/taverna/t2/workbench/models/graph/GraphTest.java
@@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.models.graph; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import net.sf.taverna.t2.workbench.models.graph.GraphEdge; +import net.sf.taverna.t2.workbench.models.graph.Graph; +import net.sf.taverna.t2.workbench.models.graph.GraphNode; +import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment; + +import org.junit.Before; +import org.junit.Test; + +public class GraphTest { + + private Graph graph; + + private Alignment alignment; + + private GraphController graphController; + + @Before + public void setUp() throws Exception { + alignment = Alignment.VERTICAL; + graph = new Graph(graphController); + } + + @Test + public void testGraph() { + assertNotNull(new Graph(graphController)); + } + + @Test + public void testAddEdge() { + GraphEdge newEdge = new GraphEdge(graphController); + graph.addEdge(newEdge); + assertEquals(1, graph.getEdges().size()); + assertTrue(graph.getEdges().contains(newEdge)); + } + + @Test + public void testAddNode() { + GraphNode newNode = new GraphNode(graphController); + graph.addNode(newNode); + assertEquals(1, graph.getNodes().size()); + assertTrue(graph.getNodes().contains(newNode)); + assertEquals(graph, newNode.getParent()); + } + + @Test + public void testAddSubgraph() { + Graph newGraph = new Graph(graphController); + graph.addSubgraph(newGraph); + assertEquals(1, graph.getSubgraphs().size()); + assertTrue(graph.getSubgraphs().contains(newGraph)); + assertEquals(graph, newGraph.getParent()); + } + + @Test + public void testGetAlignment() { + assertEquals(alignment, graph.getAlignment()); + } + + @Test + public void testGetEdges() { + assertNotNull(graph.getNodes()); + assertEquals(0, graph.getNodes().size()); + } + + @Test + public void testGetNodes() { + assertNotNull(graph.getEdges()); + assertEquals(0, graph.getEdges().size()); + } + + @Test + public void testGetSubgraphs() { + assertNotNull(graph.getSubgraphs()); + assertEquals(0, graph.getSubgraphs().size()); + } + + @Test + public void testRemoveEdge() { + GraphEdge newEdge = new GraphEdge(graphController); + assertFalse(graph.removeEdge(newEdge)); + graph.addEdge(newEdge); + assertTrue(graph.removeEdge(newEdge)); + assertFalse(graph.getNodes().contains(newEdge)); + } + + @Test + public void testRemoveNode() { + GraphNode newNode = new GraphNode(graphController); + assertFalse(graph.removeNode(newNode)); + graph.addNode(newNode); + assertTrue(graph.removeNode(newNode)); + assertFalse(graph.getNodes().contains(newNode)); + } + + @Test + public void testRemoveSubgraph() { + Graph newGraph = new Graph(graphController); + assertFalse(graph.removeSubgraph(newGraph)); + graph.addSubgraph(newGraph); + assertTrue(graph.removeSubgraph(newGraph)); + assertFalse(graph.getSubgraphs().contains(newGraph)); + } + + @Test + public void testSetAlignment() { + graph.setAlignment(Alignment.VERTICAL); + assertEquals(Alignment.VERTICAL, graph.getAlignment()); + graph.setAlignment(Alignment.HORIZONTAL); + assertEquals(Alignment.HORIZONTAL, graph.getAlignment()); + } + +}
diff --git a/taverna-workbench-graph-model/src/test/resources/nested_iteration.xml b/taverna-workbench-graph-model/src/test/resources/nested_iteration.xml new file mode 100644 index 0000000..3675361 --- /dev/null +++ b/taverna-workbench-graph-model/src/test/resources/nested_iteration.xml
@@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<s:scufl xmlns:s="http://org.embl.ebi.escience/xscufl/0.1alpha" version="0.2" log="0"> + <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:c7016fc0-c2f4-4171-b6f1-430f408f4822" author="" title="nested_iteration" /> + <s:processor name="constant" boring="true"> + <s:stringconstant>constant</s:stringconstant> + </s:processor> + <s:processor name="generate_list"> + <s:defaults> + <s:default name="prefix">prefix</s:default> + </s:defaults> + <s:beanshell> + <s:scriptvalue>list = new ArrayList(); +for (int i = 0; i < 20; i++) { + list.add(prefix + i); +}</s:scriptvalue> + <s:beanshellinputlist> + <s:beanshellinput s:syntactictype="'text/plain'">prefix</s:beanshellinput> + </s:beanshellinputlist> + <s:beanshelloutputlist> + <s:beanshelloutput s:syntactictype="l('text/plain')">list</s:beanshelloutput> + </s:beanshelloutputlist> + <s:dependencies s:classloader="iteration" /> + </s:beanshell> + </s:processor> + <s:processor name="merge"> + <s:workflow> + <s:scufl version="0.2" log="0"> + <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:3368fb8d-ecc7-4fcd-b511-6ace84b13c81" author="" title="Untitled workflow #24" /> + <s:processor name="Nested_Workflow"> + <s:workflow> + <s:scufl version="0.2" log="0"> + <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:75b99c76-7a76-4d3c-8d39-8c48df3355ad" author="" title="Untitled workflow #36" /> + <s:processor name="concat"> + <s:beanshell> + <s:scriptvalue>Thread.sleep(200); +out = in1 + in2;</s:scriptvalue> + <s:beanshellinputlist> + <s:beanshellinput s:syntactictype="'text/plain'">in1</s:beanshellinput> + <s:beanshellinput s:syntactictype="'text/plain'">in2</s:beanshellinput> + </s:beanshellinputlist> + <s:beanshelloutputlist> + <s:beanshelloutput s:syntactictype="'text/plain'">out</s:beanshelloutput> + </s:beanshelloutputlist> + <s:dependencies s:classloader="iteration" /> + </s:beanshell> + </s:processor> + <s:link source="in1" sink="concat:in1" /> + <s:link source="in2" sink="concat:in2" /> + <s:link source="concat:out" sink="out" /> + <s:source name="in1" /> + <s:source name="in2" /> + <s:sink name="out" /> + </s:scufl> + </s:workflow> + </s:processor> + <s:link source="in1" sink="Nested_Workflow:in1" /> + <s:link source="in2" sink="Nested_Workflow:in2" /> + <s:link source="Nested_Workflow:out" sink="out" /> + <s:source name="in1" /> + <s:source name="in2" /> + <s:sink name="out" /> + </s:scufl> + </s:workflow> + <s:mergemode input="in2" mode="merge" /> + </s:processor> + <s:link source="constant:value" sink="constant" /> + <s:link source="constant:value" sink="merge:in1" /> + <s:link source="generate_list:list" sink="list" /> + <s:link source="generate_list:list" sink="merge:in2" /> + <s:link source="generate_list:list" sink="merge:in2" /> + <s:link source="merge:out" sink="concat" /> + <s:source name="input" /> + <s:sink name="concat"> + <s:metadata> + <s:mimeTypes> + <s:mimeType>'text/plain'</s:mimeType> + </s:mimeTypes> + </s:metadata> + </s:sink> + <s:sink name="list"> + <s:metadata> + <s:mimeTypes> + <s:mimeType>l('text/plain')</s:mimeType> + </s:mimeTypes> + </s:metadata> + </s:sink> + <s:sink name="constant"> + <s:metadata> + <s:mimeTypes> + <s:mimeType>'text/plain'</s:mimeType> + </s:mimeTypes> + </s:metadata> + </s:sink> + <s:coordination name="constant_BLOCKON_generate_list"> + <s:condition> + <s:state>Completed</s:state> + <s:target>generate_list</s:target> + </s:condition> + <s:action> + <s:target>constant</s:target> + <s:statechange> + <s:from>Scheduled</s:from> + <s:to>Running</s:to> + </s:statechange> + </s:action> + </s:coordination> + <s:coordination name="merge_BLOCKON_generate_list"> + <s:condition> + <s:state>Completed</s:state> + <s:target>generate_list</s:target> + </s:condition> + <s:action> + <s:target>merge</s:target> + <s:statechange> + <s:from>Scheduled</s:from> + <s:to>Running</s:to> + </s:statechange> + </s:action> + </s:coordination> +</s:scufl> +
diff --git a/taverna-workbench-graph-view/pom.xml b/taverna-workbench-graph-view/pom.xml new file mode 100644 index 0000000..df8cdc9 --- /dev/null +++ b/taverna-workbench-graph-view/pom.xml
@@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>graph-view</artifactId> + <packaging>bundle</packaging> + <name>Graph View</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>configuration-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>design-ui</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>workflow-view</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>graph-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>io</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.commons</groupId> + <artifactId>taverna-services-api</artifactId> + </dependency> + <dependency> + <groupId>uk.org.taverna.scufl2</groupId> + <artifactId>scufl2-api</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.batik</groupId> + <artifactId>batik-osgi</artifactId> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/AutoScrollInteractor.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/AutoScrollInteractor.java new file mode 100644 index 0000000..65a4aa5 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/AutoScrollInteractor.java
@@ -0,0 +1,181 @@ +package net.sf.taverna.t2.workbench.views.graph; + +import static java.awt.event.InputEvent.BUTTON1_DOWN_MASK; +import static java.awt.event.InputEvent.BUTTON1_MASK; +import static java.awt.event.MouseEvent.BUTTON1; +import static java.awt.event.MouseEvent.MOUSE_DRAGGED; +import static java.awt.event.MouseEvent.MOUSE_PRESSED; +import static java.lang.System.currentTimeMillis; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.util.Timer; +import java.util.TimerTask; + +import org.apache.batik.swing.JSVGCanvas; +import org.apache.batik.swing.gvt.InteractorAdapter; +import org.apache.batik.swing.gvt.JGVTComponent; + +/** + * An interactor that scrolls the canvas view if the mouse is dragged to the + * edge of the canvas. + * + * @author David Withers + */ +public class AutoScrollInteractor extends InteractorAdapter { + /** + * Defines the border around the canvas in which the auto scroll will become + * active. + */ + private static final int BORDER = 25; + /** + * The interval, in milliseconds, between scroll events. + */ + private static final long SCROLL_INTERVAL = 100; + + private JSVGCanvas svgCanvas; + private Dimension canvasSize; + private int scrollX; + private int scrollY; + private int mouseX; + private int mouseY; + + /** + * Component used to identify mouse events generated by this class + */ + private Component eventIdentifier = new Component() { + private static final long serialVersionUID = -295542754718804222L; + }; + + private static Timer timer = new Timer("GraphAutoScrollTimer", true); + + private TimerTask task; + + /** + * Whether the interactor has finished. + */ + protected boolean finished = true; + + public AutoScrollInteractor(JSVGCanvas svgCanvas) { + this.svgCanvas = svgCanvas; + } + + @Override + public boolean startInteraction(InputEvent ie) { + int mods = ie.getModifiers(); + if (ie.getID() == MOUSE_PRESSED && (mods & BUTTON1_MASK) != 0) { + AffineTransform transform = svgCanvas.getRenderingTransform(); + // check if we're zoomed in + if (transform.getScaleX() > 1d || transform.getScaleY() > 1d) { + canvasSize = svgCanvas.getSize(); + return true; + } + } + return false; + } + + @Override + public boolean endInteraction() { + return finished; + } + + @Override + public void mousePressed(final MouseEvent e) { + if (startInteraction(e)) { + finished = false; + task = new TimerTask() { + @Override + public void run() { + scrollTimerCallback(e); + } + }; + timer.schedule(task, 0, SCROLL_INTERVAL); + } + } + + /** + * Dispatches a mouse drag event that updates the mouse location by the + * amount that the canvas has been scrolled. + * + * @param dragX + * @param dragY + */ + private void dispatchDragEvent(double dragX, double dragY) { + int x = (int) (mouseX + dragX); + int y = (int) (mouseY + dragY); + MouseEvent mouseDragEvent = new MouseEvent(eventIdentifier, + MOUSE_DRAGGED, currentTimeMillis(), BUTTON1_DOWN_MASK, x, y, 1, + false, BUTTON1); + svgCanvas.dispatchEvent(mouseDragEvent); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (!finished) { + finished = true; + scrollX = 0; + scrollY = 0; + if (task != null) + task.cancel(); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + // ignore events generated by this class + if (!finished && e.getSource() != eventIdentifier) { + mouseX = e.getX(); + mouseY = e.getY(); + int minX = BORDER; + int maxX = canvasSize.width - BORDER; + int minY = BORDER; + int maxY = canvasSize.height - BORDER; + + scrollX = (mouseX < minX) ? (minX - mouseX) + : (mouseX > maxX) ? (maxX - mouseX) : 0; + scrollY = (mouseY < minY) ? (minY - mouseY) + : (mouseY > maxY) ? (maxY - mouseY) : 0; + } + } + + private void scrollTimerCallback(MouseEvent e) { + double x = scrollX; + double y = scrollY; + if (x == 0 && y == 0) + return; + + JGVTComponent c = (JGVTComponent) e.getSource(); + AffineTransform rt = (AffineTransform) c.getRenderingTransform() + .clone(); + double currentTranslateX = rt.getTranslateX(); + double currentTranslateY = rt.getTranslateY(); + // the tranlation that will show the east edge + double maxTranslateX = -((canvasSize.width * rt.getScaleX()) - canvasSize.width); + // the translation that will show the south + double maxTranslateY = -((canvasSize.height * rt.getScaleY()) - canvasSize.height); + + if (x > 0 && currentTranslateX + x > 0) + // scroll left && not at west edge + x = -currentTranslateX; + else if (x < 0 && currentTranslateX + x < maxTranslateX) + // scroll right && not at east edge + x = maxTranslateX - currentTranslateX; + + if (y > 0 && currentTranslateY + y > 0) + // scroll up && not at north edge + y = -currentTranslateY; + else if (y < 0 && currentTranslateY + y < maxTranslateY) + // scroll down && not at south edge + y = maxTranslateY - currentTranslateY; + + if (x != 0d || y != 0d) { + AffineTransform at = AffineTransform.getTranslateInstance(x, y); + rt.preConcatenate(at); + c.setRenderingTransform(rt); + dispatchDragEvent(x, y); + } + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponent.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponent.java new file mode 100644 index 0000000..55bcf3f --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponent.java
@@ -0,0 +1,548 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static javax.swing.Action.SHORT_DESCRIPTION; +import static javax.swing.Action.SMALL_ICON; +import static javax.swing.BoxLayout.PAGE_AXIS; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.allportIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.blobIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.expandNestedIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.horizontalIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.noportIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.refreshIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.verticalIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomInIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomOutIcon; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ALIGNMENT; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_ENABLED; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_SPEED; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.PORT_STYLE; +import static org.apache.batik.swing.svg.AbstractJSVGComponent.ALWAYS_DYNAMIC; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.Timer; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.EditManager.AbstractDataflowEditEvent; +import net.sf.taverna.t2.workbench.edits.EditManager.EditManagerEvent; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent; +import net.sf.taverna.t2.workbench.file.events.FileManagerEvent; +import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment; +import net.sf.taverna.t2.workbench.models.graph.GraphController; +import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle; +import net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphController; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; +import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent; +import net.sf.taverna.t2.workbench.selection.events.WorkflowSelectionEvent; +import net.sf.taverna.t2.workbench.ui.dndhandler.ServiceTransferHandler; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; +import net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration; +import net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramAction; +import net.sf.taverna.t2.workbench.views.graph.menu.ZoomInAction; +import net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutAction; + +import org.apache.batik.swing.JSVGCanvas; +import org.apache.batik.swing.JSVGScrollPane; +import org.apache.batik.swing.gvt.GVTTreeRendererAdapter; +import org.apache.batik.swing.gvt.GVTTreeRendererEvent; +import org.apache.log4j.Logger; + +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * @author David Withers + * @author Alex Nenadic + * @author Tom Oinn + */ +public class GraphViewComponent extends JPanel implements UIComponentSPI { + private static final long serialVersionUID = 7404937056378331528L; + private static final Logger logger = Logger.getLogger(GraphViewComponent.class); + + private Workflow workflow; + private SVGGraphController graphController; + private JPanel diagramPanel; + + private Map<WorkflowBundle, Set<Workflow>> workflowsMap = new IdentityHashMap<>(); + + private Map<Workflow, SVGGraphController> graphControllerMap = new IdentityHashMap<>(); + private Map<Workflow, JPanel> diagramPanelMap = new IdentityHashMap<>(); + private Map<Workflow, Action[]> diagramActionsMap = new IdentityHashMap<>(); + + private Timer timer; + + private CardLayout cardLayout; + + private final ColourManager colourManager; + private final EditManager editManager; + private final MenuManager menuManager; + private final GraphViewConfiguration graphViewConfiguration; + private final WorkbenchConfiguration workbenchConfiguration; + private final SelectionManager selectionManager; + private final ServiceRegistry serviceRegistry; + + public GraphViewComponent(ColourManager colourManager, + EditManager editManager, FileManager fileManager, + MenuManager menuManager, + GraphViewConfiguration graphViewConfiguration, + WorkbenchConfiguration workbenchConfiguration, + SelectionManager selectionManager, ServiceRegistry serviceRegistry) { + this.colourManager = colourManager; + this.editManager = editManager; + this.menuManager = menuManager; + this.graphViewConfiguration = graphViewConfiguration; + this.workbenchConfiguration = workbenchConfiguration; + this.selectionManager = selectionManager; + this.serviceRegistry = serviceRegistry; + + cardLayout = new CardLayout(); + setLayout(cardLayout); + + ActionListener taskPerformer = new ActionListener() { + @Override + public void actionPerformed(ActionEvent evt) { + if (graphController != null) + graphController.redraw(); + timer.stop(); + } + }; + timer = new Timer(100, taskPerformer); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + if (timer.isRunning()) + timer.restart(); + else + timer.start(); + } + }); + + editManager.addObserver(new EditManagerObserver()); + selectionManager.addObserver(new SelectionManagerObserver()); + fileManager.addObserver(new FileManagerObserver()); + } + + @Override + protected void finalize() throws Throwable { + if (timer != null) + timer.stop(); + } + + @Override + public String getName() { + return "Graph View Component"; + } + + @Override + public ImageIcon getIcon() { + return null; + } + + @Override + public void onDisplay() { + } + + @Override + public void onDispose() { + if (timer != null) + timer.stop(); + } + + private JPanel createDiagramPanel(Workflow workflow) { + final JPanel diagramPanel = new JPanel(new BorderLayout()); + + // get the default diagram settings + Alignment alignment = Alignment.valueOf(graphViewConfiguration + .getProperty(ALIGNMENT)); + PortStyle portStyle = PortStyle.valueOf(graphViewConfiguration + .getProperty(PORT_STYLE)); + boolean animationEnabled = Boolean.parseBoolean(graphViewConfiguration + .getProperty(ANIMATION_ENABLED)); + int animationSpeed = Integer.parseInt(graphViewConfiguration + .getProperty(ANIMATION_SPEED)); + + // create an SVG canvas + final JSVGCanvas svgCanvas = new JSVGCanvas(null, true, false); + svgCanvas.setEnableZoomInteractor(false); + svgCanvas.setEnableRotateInteractor(false); + svgCanvas.setDocumentState(ALWAYS_DYNAMIC); + svgCanvas.setTransferHandler(new ServiceTransferHandler(editManager, + menuManager, selectionManager, serviceRegistry)); + + AutoScrollInteractor asi = new AutoScrollInteractor(svgCanvas); + svgCanvas.addMouseListener(asi); + svgCanvas.addMouseMotionListener(asi); + + final JSVGScrollPane svgScrollPane = new MySvgScrollPane(svgCanvas); + + GVTTreeRendererAdapter gvtTreeRendererAdapter = new GVTTreeRendererAdapter() { + @Override + public void gvtRenderingCompleted(GVTTreeRendererEvent e) { + logger.info("Rendered svg"); + svgScrollPane.reset(); + diagramPanel.revalidate(); + } + }; + svgCanvas.addGVTTreeRendererListener(gvtTreeRendererAdapter); + + // create a graph controller + SVGGraphController svgGraphController = new SVGGraphController( + workflow, selectionManager.getSelectedProfile(), false, + svgCanvas, alignment, portStyle, editManager, menuManager, + colourManager, workbenchConfiguration); + svgGraphController.setDataflowSelectionModel(selectionManager + .getDataflowSelectionModel(workflow.getParent())); + svgGraphController.setAnimationSpeed(animationEnabled ? animationSpeed + : 0); + + graphControllerMap.put(workflow, svgGraphController); + + // Toolbar with actions related to graph + JToolBar graphActionsToolbar = graphActionsToolbar(workflow, + svgGraphController, svgCanvas, alignment, portStyle); + graphActionsToolbar.setAlignmentX(LEFT_ALIGNMENT); + graphActionsToolbar.setFloatable(false); + + // Panel to hold the toolbars + JPanel toolbarPanel = new JPanel(); + toolbarPanel.setLayout(new BoxLayout(toolbarPanel, PAGE_AXIS)); + toolbarPanel.add(graphActionsToolbar); + + diagramPanel.add(toolbarPanel, NORTH); + diagramPanel.add(svgScrollPane, CENTER); + + // JTextField workflowHierarchy = new JTextField(workflow.getName()); + // diagramPanel.add(workflowHierarchy, BorderLayout.SOUTH); + + return diagramPanel; + } + + @SuppressWarnings("serial") + private JToolBar graphActionsToolbar(Workflow workflow, + final SVGGraphController graphController, JSVGCanvas svgCanvas, + Alignment alignment, PortStyle portStyle) { + JToolBar toolBar = new JToolBar(); + + JButton resetDiagramButton = new JButton(); + resetDiagramButton.setBorder(new EmptyBorder(0, 2, 0, 2)); + JButton zoomInButton = new JButton(); + zoomInButton.setBorder(new EmptyBorder(0, 2, 0, 2)); + JButton zoomOutButton = new JButton(); + zoomOutButton.setBorder(new EmptyBorder(0, 2, 0, 2)); + + Action resetDiagramAction = svgCanvas.new ResetTransformAction(); + ResetDiagramAction.setDesignAction(resetDiagramAction); + resetDiagramAction.putValue(SHORT_DESCRIPTION, "Reset Diagram"); + resetDiagramAction.putValue(SMALL_ICON, refreshIcon); + resetDiagramButton.setAction(resetDiagramAction); + + Action zoomInAction = svgCanvas.new ZoomAction(1.2); + ZoomInAction.setDesignAction(zoomInAction); + zoomInAction.putValue(SHORT_DESCRIPTION, "Zoom In"); + zoomInAction.putValue(SMALL_ICON, zoomInIcon); + zoomInButton.setAction(zoomInAction); + + Action zoomOutAction = svgCanvas.new ZoomAction(1 / 1.2); + ZoomOutAction.setDesignAction(zoomOutAction); + zoomOutAction.putValue(SHORT_DESCRIPTION, "Zoom Out"); + zoomOutAction.putValue(SMALL_ICON, zoomOutIcon); + zoomOutButton.setAction(zoomOutAction); + + diagramActionsMap.put(workflow, new Action[] { resetDiagramAction, + zoomInAction, zoomOutAction }); + + toolBar.add(resetDiagramButton); + toolBar.add(zoomInButton); + toolBar.add(zoomOutButton); + + toolBar.addSeparator(); + + ButtonGroup nodeTypeGroup = new ButtonGroup(); + + JToggleButton noPorts = new JToggleButton(); + JToggleButton allPorts = new JToggleButton(); + JToggleButton blobs = new JToggleButton(); + nodeTypeGroup.add(noPorts); + nodeTypeGroup.add(allPorts); + nodeTypeGroup.add(blobs); + + if (portStyle.equals(PortStyle.NONE)) + noPorts.setSelected(true); + else if (portStyle.equals(PortStyle.ALL)) + allPorts.setSelected(true); + else + blobs.setSelected(true); + + noPorts.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setPortStyle(PortStyle.NONE); + graphController.redraw(); + } + }); + noPorts.getAction().putValue(SHORT_DESCRIPTION, + "Display no service ports"); + noPorts.getAction().putValue(SMALL_ICON, noportIcon); + noPorts.setFocusPainted(false); + + allPorts.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setPortStyle(PortStyle.ALL); + graphController.redraw(); + } + }); + allPorts.getAction().putValue(SHORT_DESCRIPTION, + "Display all service ports"); + allPorts.getAction().putValue(SMALL_ICON, allportIcon); + allPorts.setFocusPainted(false); + + blobs.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setPortStyle(PortStyle.BLOB); + graphController.redraw(); + } + }); + blobs.getAction().putValue(SHORT_DESCRIPTION, + "Display services as circles"); + blobs.getAction().putValue(SMALL_ICON, blobIcon); + blobs.setFocusPainted(false); + + toolBar.add(noPorts); + toolBar.add(allPorts); + toolBar.add(blobs); + + toolBar.addSeparator(); + + ButtonGroup alignmentGroup = new ButtonGroup(); + + JToggleButton vertical = new JToggleButton(); + JToggleButton horizontal = new JToggleButton(); + alignmentGroup.add(vertical); + alignmentGroup.add(horizontal); + + if (alignment.equals(Alignment.VERTICAL)) { + vertical.setSelected(true); + } else { + horizontal.setSelected(true); + } + + vertical.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setAlignment(Alignment.VERTICAL); + graphController.redraw(); + } + }); + vertical.getAction().putValue(SHORT_DESCRIPTION, + "Align services vertically"); + vertical.getAction().putValue(SMALL_ICON, verticalIcon); + vertical.setFocusPainted(false); + + horizontal.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setAlignment(Alignment.HORIZONTAL); + graphController.redraw(); + } + + }); + horizontal.getAction().putValue(SHORT_DESCRIPTION, + "Align services horizontally"); + horizontal.getAction().putValue(SMALL_ICON, horizontalIcon); + horizontal.setFocusPainted(false); + + toolBar.add(vertical); + toolBar.add(horizontal); + + toolBar.addSeparator(); + + JToggleButton expandNested = new JToggleButton(); + expandNested.setSelected(true); + + expandNested.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent arg0) { + graphController.setExpandNestedDataflows(!graphController + .expandNestedDataflows()); + graphController.redraw(); + } + }); + expandNested.getAction().putValue(SHORT_DESCRIPTION, + "Expand Nested Workflows"); + expandNested.getAction().putValue(SMALL_ICON, expandNestedIcon); + expandNested.setFocusPainted(false); + toolBar.add(expandNested); + + return toolBar; + } + + /** + * Sets the Workflow to display in the graph view. + * + * @param workflow + */ + private void setWorkflow(Workflow workflow) { + this.workflow = workflow; + if (!diagramPanelMap.containsKey(workflow)) + addWorkflow(workflow); + graphController = graphControllerMap.get(workflow); + diagramPanel = diagramPanelMap.get(workflow); + Action[] actions = diagramActionsMap.get(workflow); + if (actions != null && actions.length == 3) { + ResetDiagramAction.setDesignAction(actions[0]); + ZoomInAction.setDesignAction(actions[1]); + ZoomOutAction.setDesignAction(actions[2]); + } + cardLayout.show(this, String.valueOf(diagramPanel.hashCode())); + graphController.redraw(); + } + + private void addWorkflow(Workflow workflow) { + JPanel newDiagramPanel = createDiagramPanel(workflow); + add(newDiagramPanel, String.valueOf(newDiagramPanel.hashCode())); + diagramPanelMap.put(workflow, newDiagramPanel); + if (!workflowsMap.containsKey(workflow.getParent())) + workflowsMap.put(workflow.getParent(), new HashSet<Workflow>()); + workflowsMap.get(workflow.getParent()).add(workflow); + } + + private void removeWorkflow(Workflow workflow) { + JPanel panel = diagramPanelMap.remove(workflow); + if (panel != null) + remove(panel); + SVGGraphController removedController = graphControllerMap.remove(workflow); + if (removedController != null) + removedController.shutdown(); + diagramActionsMap.remove(workflow); + Set<Workflow> workflows = workflowsMap.get(workflow.getParent()); + if (workflows != null) + workflows.remove(workflow); + } + + public GraphController getGraphController(Workflow workflow) { + return graphControllerMap.get(workflow); + } + + private class EditManagerObserver extends + SwingAwareObserver<EditManagerEvent> { + @Override + public void notifySwing(Observable<EditManagerEvent> sender, + EditManagerEvent message) { + if (!(message instanceof AbstractDataflowEditEvent)) + return; + AbstractDataflowEditEvent dataflowEditEvent = (AbstractDataflowEditEvent) message; + if (dataflowEditEvent.getDataFlow() != workflow.getParent()) + return; + + boolean animationEnabled = Boolean + .parseBoolean(graphViewConfiguration + .getProperty(ANIMATION_ENABLED)); + int animationSpeed = (animationEnabled ? Integer + .parseInt(graphViewConfiguration + .getProperty(ANIMATION_SPEED)) : 0); + boolean animationSettingChanged = (animationEnabled != (graphController + .getAnimationSpeed() != 0)); + + if (graphController.isDotMissing() || animationSettingChanged) { + removeWorkflow(workflow); + setWorkflow(workflow); + } else { + if (animationSpeed != graphController.getAnimationSpeed()) + graphController.setAnimationSpeed(animationSpeed); + graphController.redraw(); + } + } + } + + private class FileManagerObserver extends SwingAwareObserver<FileManagerEvent> { + @Override + public void notifySwing(Observable<FileManagerEvent> sender, final FileManagerEvent message) { + if (!(message instanceof ClosedDataflowEvent)) + return; + ClosedDataflowEvent closedDataflowEvent = (ClosedDataflowEvent) message; + + WorkflowBundle workflowBundle = closedDataflowEvent.getDataflow(); + if (workflowsMap.containsKey(workflowBundle)) + for (Workflow workflow : workflowsMap.remove(workflowBundle)) + removeWorkflow(workflow); + } + } + + private class SelectionManagerObserver extends + SwingAwareObserver<SelectionManagerEvent> { + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, + SelectionManagerEvent message) { + if (message instanceof WorkflowSelectionEvent) + setWorkflow(selectionManager.getSelectedWorkflow()); + else if (message instanceof WorkflowBundleSelectionEvent) + setWorkflow(selectionManager.getSelectedWorkflow()); + } + } + + private class MySvgScrollPane extends JSVGScrollPane { + private static final long serialVersionUID = -1539947450704269879L; + + public MySvgScrollPane(JSVGCanvas canvas) { + super(canvas); + } + + @Override + public void reset() { + super.resizeScrollBars(); + super.reset(); + } + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponentFactory.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponentFactory.java new file mode 100644 index 0000000..85f2929 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/GraphViewComponentFactory.java
@@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph; + +import javax.swing.ImageIcon; + +import uk.org.taverna.commons.services.ServiceRegistry; + +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; +import net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration; + +/** + * @author David Withers + */ +public class GraphViewComponentFactory implements UIComponentFactorySPI { + private EditManager editManager; + private FileManager fileManager; + private MenuManager menuManager; + private SelectionManager selectionManager; + private ColourManager colourManager; + private WorkbenchConfiguration workbenchConfiguration; + private GraphViewConfiguration graphViewConfiguration; + private ServiceRegistry serviceRegistry; + + @Override + public UIComponentSPI getComponent() { + return new GraphViewComponent(colourManager, editManager, fileManager, + menuManager, graphViewConfiguration, workbenchConfiguration, + selectionManager, serviceRegistry); + } + + @Override + public ImageIcon getIcon() { + return null; + } + + @Override + public String getName() { + return "Graph View"; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + + public void setWorkbenchConfiguration( + WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setGraphViewConfiguration( + GraphViewConfiguration graphViewConfiguration) { + this.graphViewConfiguration = graphViewConfiguration; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFInputAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFInputAction.java new file mode 100644 index 0000000..8c16e4a --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFInputAction.java
@@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.actions; + +import static java.awt.event.InputEvent.ALT_DOWN_MASK; +import static java.awt.event.InputEvent.SHIFT_DOWN_MASK; +import static java.awt.event.KeyEvent.VK_I; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.inputIcon; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.workbench.design.actions.AddDataflowInputAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * An action that adds a workflow input. + * + * @author Alex Nenadic + * @author Alan R Williams + */ +@SuppressWarnings("serial") +public class AddWFInputAction extends AbstractAction implements + DesignOnlyAction { + private final EditManager editManager; + private final SelectionManager selectionManager; + + public AddWFInputAction(EditManager editManager, + SelectionManager selectionManager) { + super(); + this.editManager = editManager; + this.selectionManager = selectionManager; + putValue(SMALL_ICON, inputIcon); + putValue(NAME, "Workflow input port"); + putValue(SHORT_DESCRIPTION, "Workflow input port"); + putValue(ACCELERATOR_KEY, + getKeyStroke(VK_I, SHIFT_DOWN_MASK | ALT_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + Workflow workflow = selectionManager.getSelectedWorkflow(); + new AddDataflowInputAction(workflow, null, editManager, + selectionManager).actionPerformed(e); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFOutputAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFOutputAction.java new file mode 100644 index 0000000..4027773 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/AddWFOutputAction.java
@@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.actions; + +import static java.awt.event.InputEvent.ALT_DOWN_MASK; +import static java.awt.event.InputEvent.SHIFT_DOWN_MASK; +import static java.awt.event.KeyEvent.VK_O; +import static javax.swing.KeyStroke.getKeyStroke; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.workbench.design.actions.AddDataflowOutputAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * An action that adds a workflow output. + * + * @author Alex Nenadic + * @author Alan R Williams + */ +@SuppressWarnings("serial") +public class AddWFOutputAction extends AbstractAction implements + DesignOnlyAction { + private final EditManager editManager; + private final SelectionManager selectionManager; + + public AddWFOutputAction(EditManager editManager, + SelectionManager selectionManager) { + super(); + this.editManager = editManager; + this.selectionManager = selectionManager; + putValue(SMALL_ICON, WorkbenchIcons.outputIcon); + putValue(NAME, "Workflow output port"); + putValue(SHORT_DESCRIPTION, "Workflow output port"); + putValue(ACCELERATOR_KEY, + getKeyStroke(VK_O, SHIFT_DOWN_MASK | ALT_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + Workflow workflow = selectionManager.getSelectedWorkflow(); + new AddDataflowOutputAction(workflow, null, editManager, + selectionManager).actionPerformed(e); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/DeleteGraphComponentAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/DeleteGraphComponentAction.java new file mode 100644 index 0000000..86849b6 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/DeleteGraphComponentAction.java
@@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.actions; + +import static java.awt.event.KeyEvent.VK_DELETE; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.deleteIcon; + +import java.awt.event.ActionEvent; +import java.util.Set; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveConditionAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowInputPortAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowOutputPortAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveDatalinkAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveProcessorAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; +import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.ControlLink; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * An action that deletes the selected graph component. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class DeleteGraphComponentAction extends AbstractAction implements DesignOnlyAction { + /** Current workflow's selection model event observer.*/ + private Observer<DataflowSelectionMessage> workflowSelectionObserver = new DataflowSelectionObserver(); + + private final EditManager editManager; + private final SelectionManager selectionManager; + + public DeleteGraphComponentAction(EditManager editManager, final SelectionManager selectionManager) { + super(); + this.editManager = editManager; + this.selectionManager = selectionManager; + putValue(SMALL_ICON, deleteIcon); + putValue(NAME, "Delete"); + putValue(SHORT_DESCRIPTION, "Delete selected component"); + putValue(ACCELERATOR_KEY, getKeyStroke(VK_DELETE, 0)); + setEnabled(false); + + selectionManager.addObserver(new SelectionManagerObserver()); + } + + @Override + public void actionPerformed(ActionEvent e) { + WorkflowBundle workflowBundle = selectionManager + .getSelectedWorkflowBundle(); + DataflowSelectionModel dataFlowSelectionModel = selectionManager + .getDataflowSelectionModel(workflowBundle); + // Get all selected components + Set<Object> selectedWFComponents = dataFlowSelectionModel.getSelection(); + for (Object selectedWFComponent : selectedWFComponents) + if (selectedWFComponent instanceof Processor) { + Processor processor = (Processor) selectedWFComponent; + new RemoveProcessorAction(processor.getParent(), processor, + null, editManager, selectionManager).actionPerformed(e); + } else if (selectedWFComponent instanceof DataLink) { + DataLink dataLink = (DataLink) selectedWFComponent; + new RemoveDatalinkAction(dataLink.getParent(), dataLink, null, + editManager, selectionManager).actionPerformed(e); + } else if (selectedWFComponent instanceof InputWorkflowPort) { + InputWorkflowPort port = (InputWorkflowPort) selectedWFComponent; + new RemoveDataflowInputPortAction(port.getParent(), port, null, + editManager, selectionManager).actionPerformed(e); + } else if (selectedWFComponent instanceof OutputWorkflowPort) { + OutputWorkflowPort port = (OutputWorkflowPort) selectedWFComponent; + new RemoveDataflowOutputPortAction(port.getParent(), port, + null, editManager, selectionManager).actionPerformed(e); + } else if (selectedWFComponent instanceof ControlLink) { + ControlLink controlLink = (ControlLink) selectedWFComponent; + new RemoveConditionAction(controlLink.getParent(), controlLink, + null, editManager, selectionManager).actionPerformed(e); + } + } + + /** + * Check if action should be enabled or disabled and update its status. + */ + public void updateStatus(WorkflowBundle selectionWorkflowBundle) { + if (selectionWorkflowBundle != null) { + DataflowSelectionModel selectionModel = selectionManager + .getDataflowSelectionModel(selectionWorkflowBundle); + Set<Object> selection = selectionModel.getSelection(); + if (!selection.isEmpty()) { + // Take the first selected item - we only support single selections anyway + Object selected = selection.toArray()[0]; + if ((selected instanceof Processor) + || (selected instanceof InputWorkflowPort) + || (selected instanceof OutputWorkflowPort) + || (selected instanceof DataLink) + || (selected instanceof ControlLink)) { + setEnabled(true); + return; + } + } + } + setEnabled(false); + } + + /** + * Observes events on workflow Selection Manager, i.e. when a workflow node + * is selected in the graph view, and enables/disables this action + * accordingly. + */ + private final class DataflowSelectionObserver extends + SwingAwareObserver<DataflowSelectionMessage> { + @Override + public void notifySwing(Observable<DataflowSelectionMessage> sender, + DataflowSelectionMessage message) { + updateStatus(selectionManager.getSelectedWorkflowBundle()); + } + } + + private final class SelectionManagerObserver extends + SwingAwareObserver<SelectionManagerEvent> { + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, + SelectionManagerEvent message) { + if (!(message instanceof WorkflowBundleSelectionEvent)) + return; + WorkflowBundleSelectionEvent workflowBundleSelectionEvent = (WorkflowBundleSelectionEvent) message; + WorkflowBundle oldFlow = workflowBundleSelectionEvent + .getPreviouslySelectedWorkflowBundle(); + WorkflowBundle newFlow = workflowBundleSelectionEvent + .getSelectedWorkflowBundle(); + + /* + * Remove the workflow selection model listener from the previous + * (if any) and add to the new workflow (if any) + */ + if (oldFlow != null) + selectionManager.getDataflowSelectionModel(oldFlow) + .removeObserver(workflowSelectionObserver); + + // Update the buttons status as current dataflow has changed + updateStatus(newFlow); + + if (newFlow != null) + selectionManager.getDataflowSelectionModel(newFlow) + .addObserver(workflowSelectionObserver); + } + } + +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/RenameWFInputOutputProcessorAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/RenameWFInputOutputProcessorAction.java new file mode 100644 index 0000000..f56a0e0 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/actions/RenameWFInputOutputProcessorAction.java
@@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.actions; + +import static java.awt.event.KeyEvent.VK_F2; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.KeyStroke.getKeyStroke; + +import java.awt.event.ActionEvent; +import java.util.Set; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.workbench.design.actions.EditDataflowInputPortAction; +import net.sf.taverna.t2.workbench.design.actions.EditDataflowOutputPortAction; +import net.sf.taverna.t2.workbench.design.actions.RenameProcessorAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; +import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +/** + * An action that allows user to rename workflow input, output or + * processor, in case one of these is currently selected in the Graph View. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class RenameWFInputOutputProcessorAction extends AbstractAction implements DesignOnlyAction { + /** Current workflow's selection model event observer.*/ + private Observer<DataflowSelectionMessage> workflowSelectionObserver = new DataflowSelectionObserver(); + + private final EditManager editManager; + private final SelectionManager selectionManager; + + public RenameWFInputOutputProcessorAction(EditManager editManager, + final SelectionManager selectionManager) { + super(); + this.editManager = editManager; + this.selectionManager = selectionManager; + putValue(SMALL_ICON, WorkbenchIcons.renameIcon); + putValue(NAME, "Rename"); + putValue(SHORT_DESCRIPTION, "Rename inputs, outputs or services"); + putValue(ACCELERATOR_KEY, getKeyStroke(VK_F2, 0)); + setEnabled(false); + + selectionManager.addObserver(new SelectionManagerObserver()); + } + + @Override + public void actionPerformed(ActionEvent e) { + WorkflowBundle workflowBundle = selectionManager + .getSelectedWorkflowBundle(); + DataflowSelectionModel dataFlowSelectionModel = selectionManager + .getDataflowSelectionModel(workflowBundle); + // Get selected port + Set<Object> selectedWFComponents = dataFlowSelectionModel + .getSelection(); + if (selectedWFComponents.size() > 1) { + showMessageDialog( + null, + "Only one workflow component should be selected for this action.", + "Warning", WARNING_MESSAGE); + } else { + Object selectedWFComponent = selectedWFComponents.toArray()[0]; + if (selectedWFComponent instanceof InputWorkflowPort) { + InputWorkflowPort port = (InputWorkflowPort) selectedWFComponent; + new EditDataflowInputPortAction(port.getParent(), port, null, + editManager, selectionManager).actionPerformed(e); + } else if (selectedWFComponent instanceof OutputWorkflowPort) { + OutputWorkflowPort port = (OutputWorkflowPort) selectedWFComponent; + new EditDataflowOutputPortAction(port.getParent(), port, null, + editManager, selectionManager).actionPerformed(e); + } else if (selectedWFComponent instanceof Processor) { + Processor processor = (Processor) selectedWFComponent; + new RenameProcessorAction(processor.getParent(), processor, + null, editManager, selectionManager).actionPerformed(e); + } else { // should not happen as the button will be disabled otherwise, but ... + showMessageDialog( + null, + "This action does not apply for the selected component.", + "Warning", WARNING_MESSAGE); + } + } + } + + /** + * Check if action should be enabled or disabled and update its status. + */ + public void updateStatus() { + WorkflowBundle workflowBundle = selectionManager + .getSelectedWorkflowBundle(); + DataflowSelectionModel selectionModel = selectionManager + .getDataflowSelectionModel(workflowBundle); + + // List of all selected objects in the graph view + Set<Object> selection = selectionModel.getSelection(); + + if (!selection.isEmpty()) { + // Take the first selected item - we only support single selections anyway + Object selected = selection.toArray()[0]; + if ((selected instanceof Processor) + || (selected instanceof InputWorkflowPort) + || (selected instanceof OutputWorkflowPort)) { + setEnabled(true); + return; + } + } + setEnabled(false); + } + + /** + * Observes events on workflow Selection Manager, i.e. when a workflow node + * is selected in the graph view, and enables/disables this action + * accordingly. + */ + private final class DataflowSelectionObserver extends + SwingAwareObserver<DataflowSelectionMessage> { + @Override + public void notifySwing(Observable<DataflowSelectionMessage> sender, + DataflowSelectionMessage message) { + updateStatus(); + } + } + + private final class SelectionManagerObserver extends + SwingAwareObserver<SelectionManagerEvent> { + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, + SelectionManagerEvent message) { + if (!(message instanceof WorkflowBundleSelectionEvent)) + return; + WorkflowBundleSelectionEvent workflowBundleSelectionEvent = (WorkflowBundleSelectionEvent) message; + WorkflowBundle oldFlow = workflowBundleSelectionEvent + .getPreviouslySelectedWorkflowBundle(); + WorkflowBundle newFlow = workflowBundleSelectionEvent + .getSelectedWorkflowBundle(); + // Update the buttons status as current dataflow has changed + updateStatus(); + + /* + * Remove the workflow selection model listener from the previous + * (if any) and add to the new workflow (if any) + */ + if (oldFlow != null) + selectionManager.getDataflowSelectionModel(oldFlow) + .removeObserver(workflowSelectionObserver); + + if (newFlow != null) + selectionManager.getDataflowSelectionModel(newFlow) + .addObserver(workflowSelectionObserver); + } + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfiguration.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfiguration.java new file mode 100644 index 0000000..b1c3265 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfiguration.java
@@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (C) 2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.config; + +import java.util.HashMap; +import java.util.Map; + +import uk.org.taverna.configuration.AbstractConfigurable; +import uk.org.taverna.configuration.ConfigurationManager; + +import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment; +import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle; + +/** + * Configuration for the GraphViewComponent. + * + * @author David Withers + */ +public class GraphViewConfiguration extends AbstractConfigurable { + public static final String PORT_STYLE = "portStyle"; + public static final String ALIGNMENT = "alignment"; + public static final String ANIMATION_ENABLED = "animationEnabled"; + public static final String ANIMATION_SPEED = "animationSpeed"; + + private Map<String, String> defaultPropertyMap; + + public GraphViewConfiguration(ConfigurationManager configurationManager) { + super(configurationManager); + } + + @Override + public String getCategory() { + return "general"; + } + + @Override + public Map<String, String> getDefaultPropertyMap() { + if (defaultPropertyMap == null) { + defaultPropertyMap = new HashMap<>(); + defaultPropertyMap.put(PORT_STYLE, PortStyle.NONE.toString()); + defaultPropertyMap.put(ALIGNMENT, Alignment.VERTICAL.toString()); + defaultPropertyMap.put(ANIMATION_ENABLED, "false"); + defaultPropertyMap.put(ANIMATION_SPEED, "800"); + } + return defaultPropertyMap; + } + + @Override + public String getDisplayName() { + return "Diagram"; + } + + @Override + public String getFilePrefix() { + return "Diagram"; + } + + @Override + public String getUUID() { + return "3686BA31-449F-4147-A8AC-0C3F63AFC68F"; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationPanel.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationPanel.java new file mode 100644 index 0000000..397ad1b --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationPanel.java
@@ -0,0 +1,360 @@ +/******************************************************************************* + * Copyright (C) 2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.config; + +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NORTHWEST; +import static java.awt.GridBagConstraints.RELATIVE; +import static java.awt.GridBagConstraints.REMAINDER; +import static java.awt.GridBagConstraints.WEST; +import static javax.swing.SwingConstants.LEFT; +import static net.sf.taverna.t2.workbench.helper.Helper.showHelp; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.allportIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.blobIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.horizontalIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.noportIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.verticalIcon; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ALIGNMENT; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_ENABLED; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.ANIMATION_SPEED; +import static net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration.PORT_STYLE; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.Hashtable; + +import javax.swing.AbstractAction; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JSlider; +import javax.swing.JTextArea; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.models.graph.Graph.Alignment; +import net.sf.taverna.t2.workbench.models.graph.GraphController.PortStyle; + +/** + * UI for GraphViewConfiguration. + * + * @author David Withers + */ +public class GraphViewConfigurationPanel extends JPanel { + private static final long serialVersionUID = 3779779432230124131L; + private static final int ANIMATION_SPEED_MIN = 100; + private static final int ANIMATION_SPEED_MAX = 3100; + + private GraphViewConfiguration configuration; + private JRadioButton noPorts; + private JRadioButton allPorts; + private JRadioButton blobs; + private JRadioButton vertical; + private JRadioButton horizontal; + private JCheckBox animation; + private JLabel animationSpeedLabel; + private JSlider animationSpeedSlider; + + public GraphViewConfigurationPanel(GraphViewConfiguration configuration) { + this.configuration = configuration; + GridBagLayout gridbag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + setLayout(gridbag); + + // Title describing what kind of settings we are configuring here + JTextArea descriptionText = new JTextArea( + "Default settings for the workflow diagram"); + descriptionText.setLineWrap(true); + descriptionText.setWrapStyleWord(true); + descriptionText.setEditable(false); + descriptionText.setFocusable(false); + descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10)); + + JLabel defaultLayoutLabel = new JLabel("Service display"); + + noPorts = new JRadioButton(); + allPorts = new JRadioButton(); + blobs = new JRadioButton(); + + JLabel noPortsLabel = new JLabel("Name only", noportIcon, LEFT); + JLabel allPortsLabel = new JLabel("Name and ports", allportIcon, LEFT); + JLabel blobsLabel = new JLabel("No text", blobIcon, LEFT); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(noPorts); + buttonGroup.add(allPorts); + buttonGroup.add(blobs); + + JLabel defaultAlignmentLabel = new JLabel("Diagram alignment"); + + vertical = new JRadioButton(); + horizontal = new JRadioButton(); + + JLabel verticalLabel = new JLabel("Vertical", verticalIcon, LEFT); + JLabel horizontalLabel = new JLabel("Horizontal", horizontalIcon, LEFT); + + ButtonGroup alignmentButtonGroup = new ButtonGroup(); + alignmentButtonGroup.add(horizontal); + alignmentButtonGroup.add(vertical); + + animation = new JCheckBox("Enable animation"); + + animationSpeedLabel = new JLabel("Animation speed"); + + animationSpeedSlider = new JSlider(ANIMATION_SPEED_MIN, + ANIMATION_SPEED_MAX); + animationSpeedSlider.setMajorTickSpacing(500); + animationSpeedSlider.setMinorTickSpacing(100); + animationSpeedSlider.setPaintTicks(true); + animationSpeedSlider.setPaintLabels(true); + animationSpeedSlider.setInverted(true); + animationSpeedSlider.setSnapToTicks(true); + + Hashtable<Integer, JLabel> labelTable = new Hashtable<>(); + labelTable.put(new Integer(ANIMATION_SPEED_MIN), new JLabel("Fast")); + labelTable.put(new Integer( + ((ANIMATION_SPEED_MAX - ANIMATION_SPEED_MIN) / 2) + + ANIMATION_SPEED_MIN), new JLabel("Medium")); + labelTable.put(new Integer(ANIMATION_SPEED_MAX), new JLabel("Slow")); + animationSpeedSlider.setLabelTable(labelTable); + + animation.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + boolean animationEnabled = animation.isSelected(); + animationSpeedLabel.setEnabled(animationEnabled); + animationSpeedSlider.setEnabled(animationEnabled); + } + }); + + // Set current configuration values + setFields(configuration); + + c.anchor = WEST; + c.gridx = 0; + c.gridwidth = REMAINDER; + c.weightx = 1d; + c.weighty = 0d; + c.fill = HORIZONTAL; + + add(descriptionText, c); + + c.insets = new Insets(10, 0, 10, 0); + add(defaultLayoutLabel, c); + + c.insets = new Insets(0, 20, 0, 0); + c.gridwidth = 1; + c.weightx = 0d; + add(noPorts, c); + c.insets = new Insets(0, 5, 0, 0); + c.gridx = RELATIVE; + add(noPortsLabel, c); + + c.insets = new Insets(0, 10, 0, 0); + add(allPorts, c); + c.insets = new Insets(0, 5, 0, 0); + add(allPortsLabel, c); + + c.insets = new Insets(0, 10, 0, 0); + add(blobs, c); + c.insets = new Insets(0, 5, 0, 0); + c.gridwidth = REMAINDER; + c.weightx = 1d; + add(blobsLabel, c); + + // alignment + c.insets = new Insets(20, 0, 10, 0); + c.gridx = 0; + add(defaultAlignmentLabel, c); + + c.insets = new Insets(0, 20, 0, 0); + c.gridx = 0; + c.gridwidth = 1; + c.weightx = 0d; + add(vertical, c); + c.insets = new Insets(0, 5, 0, 0); + c.gridx = RELATIVE; + add(verticalLabel, c); + + c.insets = new Insets(0, 10, 0, 0); + add(horizontal, c); + c.insets = new Insets(0, 5, 0, 0); + c.gridwidth = REMAINDER; + c.weightx = 1d; + add(horizontalLabel, c); + + // animation + c.gridx = 0; + c.gridwidth = REMAINDER; + c.insets = new Insets(20, 0, 10, 0); + add(animation, c); + + c.insets = new Insets(0, 20, 0, 0); + add(animationSpeedLabel, c); + + c.insets = new Insets(0, 20, 10, 30); + c.anchor = NORTHWEST; + c.weighty = 0d; + add(animationSpeedSlider, c); + + // Buttons + c.gridx = 0; + c.insets = new Insets(0, 20, 10, 30); + c.anchor = NORTHWEST; + c.weighty = 1d; + add(createButtonPanel(), c); + } + + /** + * Create the panel with the buttons. + */ + @SuppressWarnings("serial") + private JPanel createButtonPanel() { + final JPanel panel = new JPanel(); + + /** + * The helpButton shows help about the current component + */ + JButton helpButton = new JButton(new AbstractAction("Help") { + @Override + public void actionPerformed(ActionEvent arg0) { + showHelp(panel); + } + }); + panel.add(helpButton); + + /** + * The resetButton changes the property values shown to those + * corresponding to the configuration currently applied. + */ + JButton resetButton = new JButton(new AbstractAction("Reset") { + @Override + public void actionPerformed(ActionEvent arg0) { + setFields(configuration); + } + }); + panel.add(resetButton); + + /** + * The applyButton applies the shown field values to the + * {@link HttpProxyConfiguration} and saves them for future. + */ + JButton applyButton = new JButton(new AbstractAction("Apply") { + @Override + public void actionPerformed(ActionEvent arg0) { + applySettings(); + setFields(configuration); + } + }); + panel.add(applyButton); + + return panel; + } + + /** + * Save the currently set field values to the {@link GraphViewConfiguration} + * . Also apply those values to the currently running Taverna. + */ + private void applySettings() { + // Service display + if (noPorts.isSelected()) { + configuration.setProperty(PORT_STYLE, PortStyle.NONE.toString()); + } else if (allPorts.isSelected()) { + configuration.setProperty(PORT_STYLE, PortStyle.ALL.toString()); + } else if (blobs.isSelected()) { + configuration.setProperty(PORT_STYLE, PortStyle.BLOB.toString()); + } + + // Diagram alignment + if (vertical.isSelected()) { + configuration.setProperty(ALIGNMENT, Alignment.VERTICAL.toString()); + } else if (horizontal.isSelected()) { + configuration.setProperty(ALIGNMENT, + Alignment.HORIZONTAL.toString()); + } + + // Animation and its speed + if (animation.isSelected()) { + configuration.setProperty(ANIMATION_ENABLED, String.valueOf(true)); + } else { + configuration.setProperty(ANIMATION_ENABLED, String.valueOf(false)); + } + int speed = animationSpeedSlider.getValue(); + configuration.setProperty(ANIMATION_SPEED, String.valueOf(speed)); + } + + /** + * Set the shown configuration field values to those currently in use (i.e. + * last saved configuration). + */ + private void setFields(GraphViewConfiguration configurable) { + PortStyle portStyle = PortStyle.valueOf(configurable + .getProperty(PORT_STYLE)); + if (portStyle.equals(PortStyle.NONE)) { + noPorts.setSelected(true); + } else if (portStyle.equals(PortStyle.ALL)) { + allPorts.setSelected(true); + } else { + blobs.setSelected(true); + } + + Alignment alignment = Alignment.valueOf(configurable + .getProperty(ALIGNMENT)); + if (alignment.equals(Alignment.VERTICAL)) { + vertical.setSelected(true); + } else { + horizontal.setSelected(true); + } + + boolean animationEnabled = Boolean.parseBoolean(configurable + .getProperty(ANIMATION_ENABLED)); + animation.setSelected(animationEnabled); + + Integer animationSpeed = Integer.valueOf(configurable + .getProperty(ANIMATION_SPEED)); + if (animationSpeed > ANIMATION_SPEED_MAX) { + animationSpeed = ANIMATION_SPEED_MAX; + } else if (animationSpeed < ANIMATION_SPEED_MIN) { + animationSpeed = ANIMATION_SPEED_MIN; + } + animationSpeedSlider.setValue(animationSpeed); + animationSpeedSlider.setEnabled(animationEnabled); + + animationSpeedLabel.setEnabled(animationEnabled); + } + + // for testing only + public static void main(String[] args) { + JDialog dialog = new JDialog(); + dialog.add(new GraphViewConfigurationPanel(null)); + dialog.setModal(true); + dialog.setSize(500, 400); + dialog.setVisible(true); + System.exit(0); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationUIFactory.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationUIFactory.java new file mode 100644 index 0000000..959b598 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/config/GraphViewConfigurationUIFactory.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (C) 2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.config; + +import javax.swing.JPanel; + +import uk.org.taverna.configuration.Configurable; +import uk.org.taverna.configuration.ConfigurationUIFactory; + +/** + * ConfigurationFactory for the GraphViewConfiguration. + * + * @author David Withers + */ +public class GraphViewConfigurationUIFactory implements ConfigurationUIFactory { + private GraphViewConfiguration graphViewConfiguration; + + @Override + public boolean canHandle(String uuid) { + return uuid.equals(getConfigurable().getUUID()); + } + + @Override + public JPanel getConfigurationPanel() { + return new GraphViewConfigurationPanel(graphViewConfiguration); + } + + @Override + public Configurable getConfigurable() { + return graphViewConfiguration; + } + + public void setGraphViewConfiguration( + GraphViewConfiguration graphViewConfiguration) { + this.graphViewConfiguration = graphViewConfiguration; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFInputMenuAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFInputMenuAction.java new file mode 100644 index 0000000..65448c3 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFInputMenuAction.java
@@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu.INSERT; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.AddWFInputAction; + +/** + * @author Alex Nenadic + */ +public class AddWFInputMenuAction extends AbstractMenuAction { + private static final URI ADD_WF_INPUT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuAddWFInput"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public AddWFInputMenuAction() { + super(INSERT, 10, ADD_WF_INPUT_URI); + } + + @Override + protected Action createAction() { + return new AddWFInputAction(editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFOutputMenuAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFOutputMenuAction.java new file mode 100644 index 0000000..522c841 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/AddWFOutputMenuAction.java
@@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu.INSERT; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.AddWFOutputAction; + +/** + * @author Alex Nenadic + */ +public class AddWFOutputMenuAction extends AbstractMenuAction { + private static final URI ADD_WF_OUTPUT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuAddWFOutput"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public AddWFOutputMenuAction() { + super(INSERT, 20, ADD_WF_OUTPUT_URI); + } + + @Override + protected Action createAction() { + return new AddWFOutputAction(editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DeleteGraphComponentMenuAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DeleteGraphComponentMenuAction.java new file mode 100644 index 0000000..654078f --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DeleteGraphComponentMenuAction.java
@@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.GraphDeleteMenuSection.GRAPH_DELETE_MENU_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.DeleteGraphComponentAction; + +/** + * @author Alex Nenadic + * @author Alan R Williams + */ +public class DeleteGraphComponentMenuAction extends AbstractMenuAction { + private static final URI DELETE_GRAPH_COMPONENT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuDeleteGraphComponent"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public DeleteGraphComponentMenuAction() { + super(GRAPH_DELETE_MENU_SECTION, 10, DELETE_GRAPH_COMPONENT_URI); + } + + @Override + protected Action createAction() { + return new DeleteGraphComponentAction(editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramMenu.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramMenu.java new file mode 100644 index 0000000..02c71d8 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramMenu.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static java.awt.event.KeyEvent.VK_V; +import static javax.swing.Action.MNEMONIC_KEY; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenu; + +public class DiagramMenu extends AbstractMenu { + public static final URI DIAGRAM = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#diagram"); + + public DiagramMenu() { + super(DEFAULT_MENU_BAR, 65, DIAGRAM, "View"); + } + + public static DummyAction makeAction() { + DummyAction action = new DummyAction("View"); + action.putValue(MNEMONIC_KEY, VK_V); + return action; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramSaveMenuSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramSaveMenuSection.java new file mode 100644 index 0000000..5ebb770 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramSaveMenuSection.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu.DIAGRAM; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + */ +public class DiagramSaveMenuSection extends AbstractMenuSection { + public static final URI DIAGRAM_SAVE_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#diagramSaveMenuSection"); + + public DiagramSaveMenuSection() { + super(DIAGRAM, 40, DIAGRAM_SAVE_MENU_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramZoomMenuSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramZoomMenuSection.java new file mode 100644 index 0000000..639deee --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/DiagramZoomMenuSection.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu.DIAGRAM; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + * @author Alan R Williams + */ +public class DiagramZoomMenuSection extends AbstractMenuSection { + public static final URI DIAGRAM_ZOOM_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#diagramZoomMenuSection"); + + public DiagramZoomMenuSection() { + super(DIAGRAM, 20, DIAGRAM_ZOOM_MENU_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphCopyMenuSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphCopyMenuSection.java new file mode 100644 index 0000000..70cc462 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphCopyMenuSection.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * ??? + */ +public class GraphCopyMenuSection extends AbstractMenuSection { + public static final URI GRAPH_COPY_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphCopyMenuSection"); + + public GraphCopyMenuSection() { + super(GRAPH_MENU_SECTION, 15, GRAPH_COPY_MENU_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDeleteMenuSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDeleteMenuSection.java new file mode 100644 index 0000000..28d2144 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDeleteMenuSection.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + */ +public class GraphDeleteMenuSection extends AbstractMenuSection { + public static final URI GRAPH_DELETE_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphDeleteMenuSection"); + + public GraphDeleteMenuSection() { + super(GRAPH_MENU_SECTION, 30, GRAPH_DELETE_MENU_SECTION); + } +} \ No newline at end of file
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDetailsMenuSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDetailsMenuSection.java new file mode 100644 index 0000000..f2b6af1 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphDetailsMenuSection.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + * @author Alan R Williams + */ +public class GraphDetailsMenuSection extends AbstractMenuSection { + public static final URI GRAPH_DETAILS_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphDetailsMenuSection"); + + public GraphDetailsMenuSection() { + super(GRAPH_MENU_SECTION, 25, GRAPH_DETAILS_MENU_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphEditMenuSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphEditMenuSection.java new file mode 100644 index 0000000..1a487b1 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphEditMenuSection.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection.GRAPH_MENU_SECTION; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + */ +public class GraphEditMenuSection extends AbstractMenuSection { + public static final URI GRAPH_EDIT_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphEditMenuSection"); + + public GraphEditMenuSection() { + super(GRAPH_MENU_SECTION, 20, GRAPH_EDIT_MENU_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphMenuSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphMenuSection.java new file mode 100644 index 0000000..4030d34 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/GraphMenuSection.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + */ +public class GraphMenuSection extends AbstractMenuSection { + public static final URI GRAPH_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuSection"); + public static final URI EDIT_MENU_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#edit"); + + public GraphMenuSection() { + super(EDIT_MENU_URI, 20, GRAPH_MENU_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/InsertMenu.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/InsertMenu.java new file mode 100644 index 0000000..9b498e5 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/InsertMenu.java
@@ -0,0 +1,30 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static java.awt.event.KeyEvent.VK_I; +import static javax.swing.Action.MNEMONIC_KEY; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenu; + +/** + * @author alanrw + */ +public class InsertMenu extends AbstractMenu { + public static final URI INSERT = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#insert"); + + public InsertMenu() { + super(DEFAULT_MENU_BAR, 64, INSERT, makeAction()); + } + + public static DummyAction makeAction() { + DummyAction action = new DummyAction("Insert"); + action.putValue(MNEMONIC_KEY, VK_I); + return action; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/RenameWFInputOutputProcessorMenuAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/RenameWFInputOutputProcessorMenuAction.java new file mode 100644 index 0000000..3cf9f66 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/RenameWFInputOutputProcessorMenuAction.java
@@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.GraphDetailsMenuSection.GRAPH_DETAILS_MENU_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.RenameWFInputOutputProcessorAction; + +/** + * @author Alex Nenadic + */ +public class RenameWFInputOutputProcessorMenuAction extends AbstractMenuAction { + private static final URI RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuRenameWFInputOutputProcessor"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public RenameWFInputOutputProcessorMenuAction() { + super(GRAPH_DETAILS_MENU_SECTION, 30, + RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI); + } + + @Override + protected Action createAction() { + return new RenameWFInputOutputProcessorAction(editManager, + selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramAction.java new file mode 100644 index 0000000..9fbd452 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramAction.java
@@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_0; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.refreshIcon; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.DesignOrResultsAction; + +@SuppressWarnings("serial") +public class ResetDiagramAction extends AbstractAction implements + DesignOrResultsAction { + private static Action designAction = null; + @SuppressWarnings("unused") + private static Action resultsAction = null; + + public static void setResultsAction(Action resultsAction) { + ResetDiagramAction.resultsAction = resultsAction; + } + + public static void setDesignAction(Action designAction) { + ResetDiagramAction.designAction = designAction; + } + + public ResetDiagramAction() { + super("Reset diagram", refreshIcon); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_0, getDefaultToolkit().getMenuShortcutKeyMask())); + } + + @Override + public void actionPerformed(ActionEvent e) { +// if (isWorkflowPerspective() && (designAction != null)) + designAction.actionPerformed(e); +// else if (isResultsPerspective() && (resultsAction != null)) +// resultsAction.actionPerformed(e); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramMenuAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramMenuAction.java new file mode 100644 index 0000000..c4b402e --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ResetDiagramMenuAction.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection.DIAGRAM_ZOOM_MENU_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; + +/** + * An action that zooms a diagram image + * + * @author Alex Nenadic + * @author Tom Oinn + * @author Alan R Williams + */ +public class ResetDiagramMenuAction extends AbstractMenuAction { + public static final URI RESET_DIAGRAM_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#diagramMenuResetDiagram"); + + public ResetDiagramMenuAction() { + super(DIAGRAM_ZOOM_MENU_SECTION, 5, RESET_DIAGRAM_URI); + } + + @Override + protected Action createAction() { + return new ResetDiagramAction(); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/SaveGraphImageSubMenu.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/SaveGraphImageSubMenu.java new file mode 100644 index 0000000..49f948a --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/SaveGraphImageSubMenu.java
@@ -0,0 +1,315 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_OPTION; +import static javax.swing.JOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.showMessageDialog; +import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramSaveMenuSection.DIAGRAM_SAVE_MENU_SECTION; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URI; +import java.net.URL; +import java.util.prefs.Preferences; + +import javax.swing.AbstractAction; +import javax.swing.ImageIcon; +import javax.swing.JFileChooser; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; + +import net.sf.taverna.t2.lang.io.StreamCopier; +import net.sf.taverna.t2.lang.io.StreamDevourer; +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.lang.ui.ExtensionFileFilter; +import net.sf.taverna.t2.ui.menu.AbstractMenuCustom; +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.models.graph.DotWriter; +import net.sf.taverna.t2.workbench.models.graph.GraphController; +import net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; +import net.sf.taverna.t2.workbench.views.graph.GraphViewComponent; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * An action that saves graph diagram image. + * + * @author Alex Nenadic + * @author Tom Oinn + */ +public class SaveGraphImageSubMenu extends AbstractMenuCustom { + private static final Logger logger = Logger + .getLogger(SaveGraphImageSubMenu.class); + private static final String[] saveTypes = { "dot", "png", "svg", "ps", + "ps2" }; + private static final String[] saveExtensions = { "dot", "png", "svg", "ps", + "ps" }; + private static final String[] saveTypeNames = { "dot text", "PNG bitmap", + "scalable vector graphics", "postscript", "postscript for PDF" }; + public static final URI SAVE_GRAPH_IMAGE_MENU_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuSaveGraphImage"); + + private JMenu saveDiagramMenu; + private FileManager fileManager; + private SelectionManager selectionManager; + private WorkbenchConfiguration workbenchConfiguration; + private GraphViewComponent graphViewComponent; + + public SaveGraphImageSubMenu() { + super(DIAGRAM_SAVE_MENU_SECTION, 70, SAVE_GRAPH_IMAGE_MENU_URI); + } + + @Override + protected Component createCustomComponent() { + saveDiagramMenu = new JMenu("Export diagram"); + saveDiagramMenu + .setToolTipText("Open this menu to export the diagram in various formats"); + for (int i = 0; i < saveTypes.length; i++) { + String type = saveTypes[i]; + String extension = saveExtensions[i]; + ImageIcon icon = new ImageIcon( + WorkbenchIcons.class.getResource("graph/saveAs" + + type.toUpperCase() + ".png")); + JMenuItem item = new JMenuItem(new DotInvoker("Export as " + + saveTypeNames[i], icon, type, extension)); + saveDiagramMenu.add(item); + } + return saveDiagramMenu; + } + + @SuppressWarnings("serial") + class DotInvoker extends AbstractAction implements DesignOnlyAction { + String type = "dot"; + String extension = "dot"; + + public DotInvoker(String name, ImageIcon icon, String type, + String extension) { + super(name, icon); + this.type = type; + this.extension = extension; + } + + @Override + public void actionPerformed(ActionEvent e) { + Workflow workflow = selectionManager.getSelectedWorkflow(); + if (workflow == null) { + showMessageDialog(null, "Cannot export an empty diagram.", + "Warning", WARNING_MESSAGE); + return; + } + + File file = saveDialogue(null, workflow, extension, + "Export workflow diagram"); + if (file == null) + // User cancelled + return; + + try { + GraphController graphController = graphViewComponent + .getGraphController(workflow); + + if (type.equals("dot")) { + // Just write out the dot text, no processing required + PrintWriter out = new PrintWriter(new FileWriter(file)); + DotWriter dotWriter = new DotWriter(out); + dotWriter.writeGraph(graphController.generateGraph()); + out.flush(); + out.close(); + } else { + String dotLocation = (String) workbenchConfiguration + .getProperty("taverna.dotlocation"); + if (dotLocation == null) + dotLocation = "dot"; + logger.debug("GraphViewComponent: Invoking dot..."); + Process dotProcess = Runtime.getRuntime().exec( + new String[] { dotLocation, "-T" + type }); + + FileOutputStream fos = new FileOutputStream(file); + + StringWriter stringWriter = new StringWriter(); + DotWriter dotWriter = new DotWriter(stringWriter); + dotWriter.writeGraph(graphController.generateGraph()); + + OutputStream dotOut = dotProcess.getOutputStream(); + dotOut.write(SVGUtil.getDot(stringWriter.toString(), + workbenchConfiguration).getBytes()); + dotOut.flush(); + dotOut.close(); + new StreamDevourer(dotProcess.getErrorStream()).start(); + new StreamCopier(dotProcess.getInputStream(), fos).start(); + } + } catch (Exception ex) { + logger.warn("GraphViewComponent: Could not export diagram to " + file, ex); + showMessageDialog(null, + "Problem saving diagram : \n" + ex.getMessage(), + "Error!", ERROR_MESSAGE); + } + } + } + + /** + * Pop up a save dialogue relating to the given workflow. This method can be + * used, for example, for saving the workflow diagram as .png, and will use + * the existing workflow title as a base for suggesting a filename. + * + * @param parentComponent + * Parent component for dialogue window + * @param model + * Workflow to save + * @param extension + * Extension for filename, such as "jpg" + * @param windowTitle + * Title for dialogue box, such as "Save workflow diagram" + * @return File instance for the selected abstract filename, or null if the + * dialogue was cancelled. + */ + private File saveDialogue(Component parentComponent, Workflow workflow, + String extension, String windowTitle) { + JFileChooser fc = new JFileChooser(); + Preferences prefs = Preferences + .userNodeForPackage(SaveGraphImageSubMenu.class); + String curDir = prefs + .get("currentDir", System.getProperty("user.home")); + String suggestedFileName = ""; + // Get the source the workflow was loaded from - can be File, URL, or InputStream + Object source = fileManager.getDataflowSource(workflow.getParent()); + if (source instanceof File) { + suggestedFileName = ((File) source).getName(); + // remove the file extension + suggestedFileName = suggestedFileName.substring(0, + suggestedFileName.lastIndexOf(".")); + } else if (source instanceof URL) { + suggestedFileName = ((URL) source).getPath(); + // remove the file extension + suggestedFileName = suggestedFileName.substring(0, + suggestedFileName.lastIndexOf(".")); + } else { + // We cannot suggest the file name if workflow was read from an InputStream + } + + fc.setDialogTitle(windowTitle); + fc.resetChoosableFileFilters(); + fc.setFileFilter(new ExtensionFileFilter(new String[] { extension })); + if (suggestedFileName.isEmpty()) + // No file suggestion, just the directory + fc.setCurrentDirectory(new File(curDir)); + else + // Suggest a filename from the workflow file name + fc.setSelectedFile(new File(curDir, suggestedFileName + "." + extension)); + + while (true) { + if (fc.showSaveDialog(parentComponent) != APPROVE_OPTION) { + logger.info("GraphViewComponent: Aborting diagram export to " + + suggestedFileName); + return null; + } + + File file = fixExtension(fc.getSelectedFile(), extension); + logger.debug("GraphViewComponent: Selected " + file + " as export target"); + prefs.put("currentDir", fc.getCurrentDirectory().toString()); + + // If file doesn't exist, we may write it! (Well, probably...) + if (!file.exists()) + return file; + + // Ask the user if they want to overwrite the file + String msg = file.getAbsolutePath() + + " already exists. Do you want to overwrite it?"; + if (showConfirmDialog(null, msg, "File already exists", + YES_NO_OPTION) == JOptionPane.YES_OPTION) + return file; + } + } + + /** + * Make sure given File has the given extension. If it has no extension, + * a new File instance will be returned. Otherwise, the passed instance is + * returned unchanged. + * + * @param file + * File which extension is to be checked + * @param extension + * Extension desired, example: "xml" + * @return file parameter if the extension was OK, or a new File instance + * with the correct extension + */ + private File fixExtension(File file, String extension) { + if (file.getName().endsWith("." + extension)) + return file; + // Append the extension (keep the existing one) + String name = file.getName(); + return new File(file.getParent(), name + "." + extension); + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public void setWorkbenchConfiguration( + WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setGraphViewComponent(GraphViewComponent graphViewComponent) { + this.graphViewComponent = graphViewComponent; + } + + private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective"; + + @SuppressWarnings("unused") + private final class SelectionManagerObserver extends + SwingAwareObserver<SelectionManagerEvent> { + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, + SelectionManagerEvent message) { + if (!(message instanceof PerspectiveSelectionEvent)) + return; + PerspectiveSelectionEvent event = (PerspectiveSelectionEvent) message; + + saveDiagramMenu.setEnabled((DESIGN_PERSPECTIVE_ID.equals(event + .getSelectedPerspective().getID()))); + } + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInAction.java new file mode 100644 index 0000000..b8735c9 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInAction.java
@@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_EQUALS; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomInIcon; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.DesignOrResultsAction; + +import org.apache.log4j.Logger; + +@SuppressWarnings("serial") +public class ZoomInAction extends AbstractAction implements + DesignOrResultsAction { + @SuppressWarnings("unused") + private static Logger logger = Logger.getLogger(ZoomInAction.class); + private static Action designAction = null; + @SuppressWarnings("unused") + private static Action resultsAction = null; + + public static void setResultsAction(Action resultsAction) { + ZoomInAction.resultsAction = resultsAction; + } + + public static void setDesignAction(Action designAction) { + ZoomInAction.designAction = designAction; + } + + ZoomInAction() { + super("Zoom in", zoomInIcon); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_EQUALS, getDefaultToolkit() + .getMenuShortcutKeyMask())); + } + + @Override + public void actionPerformed(ActionEvent e) { +// if (isWorkflowPerspective()) { +// if (designAction != null) + designAction.actionPerformed(e); +// else +// logger.error("ZoomInAction.designAction is null"); +// } else if (isResultsPerspective() && (resultsAction != null)) +// resultsAction.actionPerformed(e); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInMenuAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInMenuAction.java new file mode 100644 index 0000000..89eea7d --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomInMenuAction.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection.DIAGRAM_ZOOM_MENU_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; + +/** + * An action that zooms a diagram image + * + * @author Alex Nenadic + * @author Tom Oinn + * @author Alan R Williams + */ +public class ZoomInMenuAction extends AbstractMenuAction { + public static final URI ZOOM_IN_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#diagramMenuZoomIn"); + + public ZoomInMenuAction() { + super(DIAGRAM_ZOOM_MENU_SECTION, 10, ZOOM_IN_URI); + } + + @Override + protected Action createAction() { + return new ZoomInAction(); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutAction.java new file mode 100644 index 0000000..bd2a2b9 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutAction.java
@@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static java.awt.Toolkit.getDefaultToolkit; +import static java.awt.event.KeyEvent.VK_MINUS; +import static javax.swing.KeyStroke.getKeyStroke; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.DesignOrResultsAction; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; + +@SuppressWarnings("serial") +public class ZoomOutAction extends AbstractAction implements + DesignOrResultsAction { + private static Action designAction = null; + @SuppressWarnings("unused") + private static Action resultsAction = null; + + public static void setResultsAction(Action resultsAction) { + ZoomOutAction.resultsAction = resultsAction; + } + + public static void setDesignAction(Action designAction) { + ZoomOutAction.designAction = designAction; + } + + ZoomOutAction() { + super("Zoom out", WorkbenchIcons.zoomOutIcon); + putValue( + ACCELERATOR_KEY, + getKeyStroke(VK_MINUS, getDefaultToolkit() + .getMenuShortcutKeyMask())); + } + + @Override + public void actionPerformed(ActionEvent e) { +// if (isWorkflowPerspective() && (designAction != null)) + designAction.actionPerformed(e); +// else if (isResultsPerspective() && (resultsAction != null)) +// resultsAction.actionPerformed(e); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutMenuAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutMenuAction.java new file mode 100644 index 0000000..bc34252 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/menu/ZoomOutMenuAction.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.menu; + +import static net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection.DIAGRAM_ZOOM_MENU_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; + +/** + * An action that zooms a diagram image + * + * @author Alex Nenadic + * @author Tom Oinn + * @author Alan R Williams + */ +public class ZoomOutMenuAction extends AbstractMenuAction { + public static final URI ZOOM_OUT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#diagramMenuZoomOut"); + + public ZoomOutMenuAction() { + super(DIAGRAM_ZOOM_MENU_SECTION, 20, ZOOM_OUT_URI); + } + + @Override + protected Action createAction() { + return new ZoomOutAction(); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFInputToolbarAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFInputToolbarAction.java new file mode 100644 index 0000000..736ba8d --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFInputToolbarAction.java
@@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.toolbar; + +import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection.GRAPH_EDIT_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.AddWFInputAction; + +/** + * @author Alex Nenadic + */ +public class AddWFInputToolbarAction extends AbstractMenuAction { + private static final URI ADD_WF_INPUT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarAddWFInput"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public AddWFInputToolbarAction() { + super(GRAPH_EDIT_TOOLBAR_SECTION, 10, ADD_WF_INPUT_URI); + } + + @Override + protected Action createAction() { + return new AddWFInputAction(editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFOutputToolbarAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFOutputToolbarAction.java new file mode 100644 index 0000000..ae7d5d0 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/AddWFOutputToolbarAction.java
@@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.toolbar; + +import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection.GRAPH_EDIT_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.AddWFOutputAction; + +/** + * @author Alex Nenadic + */ +public class AddWFOutputToolbarAction extends AbstractMenuAction { + private static final URI ADD_WF_OUTPUT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarAddWFOutput"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public AddWFOutputToolbarAction() { + super(GRAPH_EDIT_TOOLBAR_SECTION, 20, ADD_WF_OUTPUT_URI); + } + + @Override + protected Action createAction() { + return new AddWFOutputAction(editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/DeleteGraphComponentToolbarAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/DeleteGraphComponentToolbarAction.java new file mode 100644 index 0000000..068c530 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/DeleteGraphComponentToolbarAction.java
@@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.toolbar; + +import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphDeleteToolbarSection.GRAPH_DELETE_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.DeleteGraphComponentAction; + +/** + * @author Alex Nenadic + */ +public class DeleteGraphComponentToolbarAction extends AbstractMenuAction { + private static final URI DELETE_GRAPH_COMPONENT_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarDeleteGraphComponent"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public DeleteGraphComponentToolbarAction() { + super(GRAPH_DELETE_TOOLBAR_SECTION, 10, DELETE_GRAPH_COMPONENT_URI); + } + + @Override + protected Action createAction() { + return new DeleteGraphComponentAction(editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphDeleteToolbarSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphDeleteToolbarSection.java new file mode 100644 index 0000000..794cf1f --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphDeleteToolbarSection.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.toolbar; + +import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + */ +public class GraphDeleteToolbarSection extends AbstractMenuSection { + public static final URI GRAPH_DELETE_TOOLBAR_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphDeleteToolbarSection"); + + public GraphDeleteToolbarSection() { + super(DEFAULT_TOOL_BAR, 80, GRAPH_DELETE_TOOLBAR_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphEditToolbarSection.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphEditToolbarSection.java new file mode 100644 index 0000000..a61259a --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/GraphEditToolbarSection.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.toolbar; + +import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + */ +public class GraphEditToolbarSection extends AbstractMenuSection { + public static final URI GRAPH_EDIT_TOOLBAR_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphEditToolbarSection"); + + public GraphEditToolbarSection() { + super(DEFAULT_TOOL_BAR, 30, GRAPH_EDIT_TOOLBAR_SECTION); + } +}
diff --git a/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/RenameWFInputOutputProcessorToolbarAction.java b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/RenameWFInputOutputProcessorToolbarAction.java new file mode 100644 index 0000000..1794e8f --- /dev/null +++ b/taverna-workbench-graph-view/src/main/java/net/sf/taverna/t2/workbench/views/graph/toolbar/RenameWFInputOutputProcessorToolbarAction.java
@@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.graph.toolbar; + +import static net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection.GRAPH_EDIT_TOOLBAR_SECTION; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.views.graph.actions.RenameWFInputOutputProcessorAction; + +/** + * @author Alex Nenadic + */ +public class RenameWFInputOutputProcessorToolbarAction extends + AbstractMenuAction { + private static final URI RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphToolbarRenameWFInputOutputProcessor"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public RenameWFInputOutputProcessorToolbarAction() { + super(GRAPH_EDIT_TOOLBAR_SECTION, 30, + RENAME_WF_INPUT_OUTPUT_PROCESSOR_URI); + } + + @Override + protected Action createAction() { + return new RenameWFInputOutputProcessorAction(editManager, + selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } +}
diff --git a/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..226078d --- /dev/null +++ b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,29 @@ +net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection +net.sf.taverna.t2.workbench.views.graph.toolbar.GraphDeleteToolbarSection +net.sf.taverna.t2.workbench.views.graph.toolbar.GraphSaveToolbarSection +net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFInputToolbarAction +net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFOutputToolbarAction +net.sf.taverna.t2.workbench.views.graph.toolbar.RenameWFInputOutputProcessorToolbarAction +net.sf.taverna.t2.workbench.views.graph.toolbar.DeleteGraphComponentToolbarAction +net.sf.taverna.t2.workbench.views.graph.toolbar.SaveGraphImageToolbarAction + +net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu +net.sf.taverna.t2.workbench.views.graph.menu.DiagramSaveMenuSection +net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection + +net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection +net.sf.taverna.t2.workbench.views.graph.menu.GraphCopyMenuSection +net.sf.taverna.t2.workbench.views.graph.menu.GraphEditMenuSection +net.sf.taverna.t2.workbench.views.graph.menu.GraphDeleteMenuSection +net.sf.taverna.t2.workbench.views.graph.menu.GraphDetailsMenuSection + +net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu + +net.sf.taverna.t2.workbench.views.graph.menu.AddWFInputMenuAction +net.sf.taverna.t2.workbench.views.graph.menu.AddWFOutputMenuAction +net.sf.taverna.t2.workbench.views.graph.menu.RenameWFInputOutputProcessorMenuAction +net.sf.taverna.t2.workbench.views.graph.menu.DeleteGraphComponentMenuAction +net.sf.taverna.t2.workbench.views.graph.menu.SaveGraphImageSubMenu +net.sf.taverna.t2.workbench.views.graph.menu.ZoomInMenuAction +net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutMenuAction +net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramMenuAction \ No newline at end of file
diff --git a/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory new file mode 100644 index 0000000..70830ec --- /dev/null +++ b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfigurationUIFactory
diff --git a/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI new file mode 100644 index 0000000..8086a8d --- /dev/null +++ b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.views.graph.GraphViewComponentFactory \ No newline at end of file
diff --git a/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI new file mode 100644 index 0000000..563c21d --- /dev/null +++ b/taverna-workbench-graph-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.views.graph.GraphViewComponent
diff --git a/taverna-workbench-graph-view/src/main/resources/META-INF/spring/graph-view-context-osgi.xml b/taverna-workbench-graph-view/src/main/resources/META-INF/spring/graph-view-context-osgi.xml new file mode 100644 index 0000000..c7adec0 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/resources/META-INF/spring/graph-view-context-osgi.xml
@@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="GraphViewConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" /> + + <service ref="GraphViewComponentFactory" interface="net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI" /> + + <service ref="GraphEditToolbarSection" auto-export="interfaces" /> + <service ref="GraphDeleteToolbarSection" auto-export="interfaces" /> + <service ref="AddWFInputToolbarAction" auto-export="interfaces" /> + <service ref="AddWFOutputToolbarAction" auto-export="interfaces" /> + <service ref="RenameWFInputOutputProcessorToolbarAction" auto-export="interfaces" /> + <service ref="DeleteGraphComponentToolbarAction" auto-export="interfaces" /> + <service ref="DiagramMenu" auto-export="interfaces" /> + <service ref="DiagramSaveMenuSection" auto-export="interfaces" /> + <service ref="DiagramZoomMenuSection" auto-export="interfaces" /> + <service ref="GraphMenuSection" auto-export="interfaces" /> + <service ref="GraphCopyMenuSection" auto-export="interfaces" /> + <service ref="GraphEditMenuSection" auto-export="interfaces" /> + <service ref="GraphDeleteMenuSection" auto-export="interfaces" /> + <service ref="GraphDetailsMenuSection" auto-export="interfaces" /> + <service ref="InsertMenu" auto-export="interfaces" /> + <service ref="AddWFInputMenuAction" auto-export="interfaces" /> + <service ref="AddWFOutputMenuAction" auto-export="interfaces" /> + <service ref="RenameWFInputOutputProcessorMenuAction" auto-export="interfaces" /> + <service ref="DeleteGraphComponentMenuAction" auto-export="interfaces" /> + <!-- <service ref="SaveGraphImageSubMenu" auto-export="interfaces" /> --> + <service ref="ZoomInMenuAction" auto-export="interfaces" /> + <service ref="ZoomOutMenuAction" auto-export="interfaces" /> + <service ref="ResetDiagramMenuAction" auto-export="interfaces" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + <reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + <reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + <reference id="workbenchConfiguration" interface="net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration" /> + <reference id="configurationManager" interface="uk.org.taverna.configuration.ConfigurationManager" /> + <reference id="serviceRegistry" interface="uk.org.taverna.commons.services.ServiceRegistry" /> + +</beans:beans>
diff --git a/taverna-workbench-graph-view/src/main/resources/META-INF/spring/graph-view-context.xml b/taverna-workbench-graph-view/src/main/resources/META-INF/spring/graph-view-context.xml new file mode 100644 index 0000000..9968805 --- /dev/null +++ b/taverna-workbench-graph-view/src/main/resources/META-INF/spring/graph-view-context.xml
@@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="graphViewConfiguration" + class="net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfiguration"> + <constructor-arg name="configurationManager" ref="configurationManager" /> + </bean> + + <bean id="GraphViewConfigurationUIFactory" + class="net.sf.taverna.t2.workbench.views.graph.config.GraphViewConfigurationUIFactory"> + <property name="graphViewConfiguration"> + <ref local="graphViewConfiguration" /> + </property> + </bean> + + <bean id="GraphViewComponentFactory" class="net.sf.taverna.t2.workbench.views.graph.GraphViewComponentFactory"> + <property name="colourManager" ref="colourManager" /> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="graphViewConfiguration"> + <ref local="graphViewConfiguration" /> + </property> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> + + <bean id="GraphEditToolbarSection" + class="net.sf.taverna.t2.workbench.views.graph.toolbar.GraphEditToolbarSection" /> + <bean id="GraphDeleteToolbarSection" + class="net.sf.taverna.t2.workbench.views.graph.toolbar.GraphDeleteToolbarSection" /> + <bean id="AddWFInputToolbarAction" + class="net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFInputToolbarAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="AddWFOutputToolbarAction" + class="net.sf.taverna.t2.workbench.views.graph.toolbar.AddWFOutputToolbarAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RenameWFInputOutputProcessorToolbarAction" + class="net.sf.taverna.t2.workbench.views.graph.toolbar.RenameWFInputOutputProcessorToolbarAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="DeleteGraphComponentToolbarAction" + class="net.sf.taverna.t2.workbench.views.graph.toolbar.DeleteGraphComponentToolbarAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="DiagramMenu" + class="net.sf.taverna.t2.workbench.views.graph.menu.DiagramMenu" /> + <bean id="DiagramSaveMenuSection" + class="net.sf.taverna.t2.workbench.views.graph.menu.DiagramSaveMenuSection" /> + <bean id="DiagramZoomMenuSection" + class="net.sf.taverna.t2.workbench.views.graph.menu.DiagramZoomMenuSection" /> + <bean id="GraphMenuSection" + class="net.sf.taverna.t2.workbench.views.graph.menu.GraphMenuSection" /> + <bean id="GraphCopyMenuSection" + class="net.sf.taverna.t2.workbench.views.graph.menu.GraphCopyMenuSection" /> + <bean id="GraphEditMenuSection" + class="net.sf.taverna.t2.workbench.views.graph.menu.GraphEditMenuSection" /> + <bean id="GraphDeleteMenuSection" + class="net.sf.taverna.t2.workbench.views.graph.menu.GraphDeleteMenuSection" /> + <bean id="GraphDetailsMenuSection" + class="net.sf.taverna.t2.workbench.views.graph.menu.GraphDetailsMenuSection" /> + <bean id="InsertMenu" class="net.sf.taverna.t2.workbench.views.graph.menu.InsertMenu" /> + <bean id="AddWFInputMenuAction" + class="net.sf.taverna.t2.workbench.views.graph.menu.AddWFInputMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="AddWFOutputMenuAction" + class="net.sf.taverna.t2.workbench.views.graph.menu.AddWFOutputMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RenameWFInputOutputProcessorMenuAction" + class="net.sf.taverna.t2.workbench.views.graph.menu.RenameWFInputOutputProcessorMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="DeleteGraphComponentMenuAction" + class="net.sf.taverna.t2.workbench.views.graph.menu.DeleteGraphComponentMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <!-- <bean id="SaveGraphImageSubMenu" + class="net.sf.taverna.t2.workbench.views.graph.menu.SaveGraphImageSubMenu"> + <property name="fileManager" ref="fileManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="graphViewComponent" ref="GraphViewComponent" /> + </bean> --> + <bean id="ZoomInMenuAction" + class="net.sf.taverna.t2.workbench.views.graph.menu.ZoomInMenuAction" /> + <bean id="ZoomOutMenuAction" + class="net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutMenuAction" /> + <bean id="ResetDiagramMenuAction" + class="net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramMenuAction" /> + +</beans>
diff --git a/taverna-workbench-graph-view/src/test/resources/nested_iteration.t2flow b/taverna-workbench-graph-view/src/test/resources/nested_iteration.t2flow new file mode 100644 index 0000000..9b50c7f --- /dev/null +++ b/taverna-workbench-graph-view/src/test/resources/nested_iteration.t2flow
@@ -0,0 +1,111 @@ +<workflow xmlns="http://taverna.sf.net/2008/xml/t2flow"><dataflow id="23f84bb1-4a04-47fa-8150-7063310db697" role="top"><name>nested_iteration</name><inputPorts /><outputPorts><port><name>concat</name></port><port><name>list</name></port><port><name>constant</name></port></outputPorts><processors><processor><name>constant</name><inputPorts /><outputPorts><port><name>value</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>stringconstant-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.stringconstant.StringConstantActivity</class><inputMap /><outputMap><map from="value" to="value" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean xmlns=""> + <value>constant</value> +</net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>0</initialDelay> + <maxDelay>0</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross /></strategy></iteration></iterationStrategyStack></processor><processor><name>generate_list</name><inputPorts><port><name>prefix</name><depth>0</depth></port></inputPorts><outputPorts><port><name>list</name><depth>1</depth><granularDepth>1</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>beanshell-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.beanshell.BeanshellActivity</class><inputMap><map from="prefix" to="prefix" /></inputMap><outputMap><map from="list" to="list" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean xmlns=""> + <script>list = new ArrayList(); +for (int i = 0; i < 20; i++) { + list.add(prefix + i); +}</script> + <dependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>java.lang.String</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>prefix</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>1</granularDepth> + <name>list</name> + <depth>1</depth> + <mimeTypes> + <string>l('text/plain')</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> +</net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>0</initialDelay> + <maxDelay>0</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><port name="prefix" depth="0" /></strategy></iteration></iterationStrategyStack></processor><processor><name>merge</name><inputPorts><port><name>in1</name><depth>0</depth></port><port><name>in2</name><depth>0</depth></port></inputPorts><outputPorts><port><name>out</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>dataflow-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.dataflow.DataflowActivity</class><inputMap><map from="in2" to="in2" /><map from="in1" to="in1" /></inputMap><outputMap><map from="out" to="out" /></outputMap><configBean encoding="dataflow"><dataflow ref="79ad4092-abcb-42bf-ac98-d66dfac67dff" /></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>0</initialDelay> + <maxDelay>0</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="in1" depth="0" /><port name="in2" depth="0" /></cross></strategy></iteration></iterationStrategyStack></processor><processor><name>generate_list_prefix_defaultValue</name><inputPorts /><outputPorts><port><name>value</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>stringconstant-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.stringconstant.StringConstantActivity</class><inputMap /><outputMap><map from="value" to="value" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean xmlns=""> + <value>prefix</value> +</net.sf.taverna.t2.activities.stringconstant.StringConstantConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>0</initialDelay> + <maxDelay>0</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross /></strategy></iteration></iterationStrategyStack></processor></processors><conditions><condition control="generate_list" target="constant" /></conditions><datalinks><datalink><sink type="processor"><processor>generate_list</processor><port>prefix</port></sink><source type="processor"><processor>generate_list_prefix_defaultValue</processor><port>value</port></source></datalink><datalink><sink type="processor"><processor>merge</processor><port>in1</port></sink><source type="processor"><processor>constant</processor><port>value</port></source></datalink><datalink><sink type="merge"><processor>merge</processor><port>in2</port></sink><source type="processor"><processor>generate_list</processor><port>list</port></source></datalink><datalink><sink type="merge"><processor>merge</processor><port>in2</port></sink><source type="processor"><processor>generate_list</processor><port>list</port></source></datalink><datalink><sink type="dataflow"><port>concat</port></sink><source type="processor"><processor>merge</processor><port>out</port></source></datalink><datalink><sink type="dataflow"><port>list</port></sink><source type="processor"><processor>generate_list</processor><port>list</port></source></datalink><datalink><sink type="dataflow"><port>constant</port></sink><source type="processor"><processor>constant</processor><port>value</port></source></datalink></datalinks></dataflow><dataflow id="79ad4092-abcb-42bf-ac98-d66dfac67dff" role="nested"><name>Untitled workflow #24</name><inputPorts><port><name>in1</name><depth>0</depth><granularDepth>0</granularDepth></port><port><name>in2</name><depth>0</depth><granularDepth>0</granularDepth></port></inputPorts><outputPorts><port><name>out</name></port></outputPorts><processors><processor><name>Nested_Workflow</name><inputPorts><port><name>in2</name><depth>0</depth></port><port><name>in1</name><depth>0</depth></port></inputPorts><outputPorts><port><name>out</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>dataflow-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.dataflow.DataflowActivity</class><inputMap><map from="in2" to="in2" /><map from="in1" to="in1" /></inputMap><outputMap><map from="out" to="out" /></outputMap><configBean encoding="dataflow"><dataflow ref="ebd93027-c046-4a04-befa-c5715e8ba3da" /></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>0</initialDelay> + <maxDelay>0</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="in1" depth="0" /><port name="in2" depth="0" /></cross></strategy></iteration></iterationStrategyStack></processor></processors><conditions /><datalinks><datalink><sink type="processor"><processor>Nested_Workflow</processor><port>in2</port></sink><source type="dataflow"><port>in2</port></source></datalink><datalink><sink type="processor"><processor>Nested_Workflow</processor><port>in1</port></sink><source type="dataflow"><port>in1</port></source></datalink><datalink><sink type="dataflow"><port>out</port></sink><source type="processor"><processor>Nested_Workflow</processor><port>out</port></source></datalink></datalinks></dataflow><dataflow id="ebd93027-c046-4a04-befa-c5715e8ba3da" role="nested"><name>Untitled workflow #36</name><inputPorts><port><name>in1</name><depth>0</depth><granularDepth>0</granularDepth></port><port><name>in2</name><depth>0</depth><granularDepth>0</granularDepth></port></inputPorts><outputPorts><port><name>out</name></port></outputPorts><processors><processor><name>concat</name><inputPorts><port><name>in1</name><depth>0</depth></port><port><name>in2</name><depth>0</depth></port></inputPorts><outputPorts><port><name>out</name><depth>0</depth><granularDepth>0</granularDepth></port></outputPorts><annotations /><activities><activity><raven><group>net.sf.taverna.t2</group><artifact>beanshell-activity</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.activities.beanshell.BeanshellActivity</class><inputMap><map from="in2" to="in2" /><map from="in1" to="in1" /></inputMap><outputMap><map from="out" to="out" /></outputMap><configBean encoding="xstream"><net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean xmlns=""> + <script>Thread.sleep(200); +out = in1 + in2;</script> + <dependencies /> + <inputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>java.lang.String</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>in1</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + <handledReferenceSchemes /> + <translatedElementType>java.lang.String</translatedElementType> + <allowsLiteralValues>true</allowsLiteralValues> + <name>in2</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean> + </inputs> + <outputs> + <net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + <granularDepth>0</granularDepth> + <name>out</name> + <depth>0</depth> + <mimeTypes> + <string>'text/plain'</string> + </mimeTypes> + </net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean> + </outputs> +</net.sf.taverna.t2.activities.beanshell.BeanshellActivityConfigurationBean></configBean></activity></activities><dispatchStack><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig xmlns=""> + <maxJobs>1</maxJobs> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ParallelizeConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry</class><configBean encoding="xstream"><net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig xmlns=""> + <backoffFactor>1.0</backoffFactor> + <initialDelay>0</initialDelay> + <maxDelay>0</maxDelay> + <maxRetries>0</maxRetries> +</net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.RetryConfig></configBean></dispatchLayer><dispatchLayer><raven><group>net.sf.taverna.t2</group><artifact>workflowmodel-impl</artifact><version>0.3-SNAPSHOT</version></raven><class>net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke</class><configBean encoding="xstream"><null xmlns="" /></configBean></dispatchLayer></dispatchStack><iterationStrategyStack><iteration><strategy><cross><port name="in1" depth="0" /><port name="in2" depth="0" /></cross></strategy></iteration></iterationStrategyStack></processor></processors><conditions /><datalinks><datalink><sink type="processor"><processor>concat</processor><port>in1</port></sink><source type="dataflow"><port>in1</port></source></datalink><datalink><sink type="processor"><processor>concat</processor><port>in2</port></sink><source type="dataflow"><port>in2</port></source></datalink><datalink><sink type="dataflow"><port>out</port></sink><source type="processor"><processor>concat</processor><port>out</port></source></datalink></datalinks></dataflow></workflow> \ No newline at end of file
diff --git a/taverna-workbench-graph-view/src/test/resources/nested_iteration.xml b/taverna-workbench-graph-view/src/test/resources/nested_iteration.xml new file mode 100644 index 0000000..3a547bb --- /dev/null +++ b/taverna-workbench-graph-view/src/test/resources/nested_iteration.xml
@@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8"?> +<s:scufl xmlns:s="http://org.embl.ebi.escience/xscufl/0.1alpha" version="0.2" log="0"> + <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:c7016fc0-c2f4-4171-b6f1-430f408f4822" author="" title="nested_iteration" /> + <s:processor name="generate_list"> + <s:defaults> + <s:default name="prefix">prefix</s:default> + </s:defaults> + <s:beanshell> + <s:scriptvalue>list = new ArrayList(); +for (int i = 0; i < 20; i++) { + list.add(prefix + i); +}</s:scriptvalue> + <s:beanshellinputlist> + <s:beanshellinput s:syntactictype="'text/plain'">prefix</s:beanshellinput> + </s:beanshellinputlist> + <s:beanshelloutputlist> + <s:beanshelloutput s:syntactictype="l('text/plain')">list</s:beanshelloutput> + </s:beanshelloutputlist> + <s:dependencies s:classloader="iteration" /> + </s:beanshell> + </s:processor> + <s:processor name="constant" boring="true"> + <s:stringconstant>constant</s:stringconstant> + </s:processor> + <s:processor name="merge"> + <s:workflow> + <s:scufl version="0.2" log="0"> + <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:3368fb8d-ecc7-4fcd-b511-6ace84b13c81" author="" title="Untitled workflow #24" /> + <s:processor name="Nested_Workflow"> + <s:workflow> + <s:scufl version="0.2" log="0"> + <s:workflowdescription lsid="urn:lsid:net.sf.taverna:wfDefinition:75b99c76-7a76-4d3c-8d39-8c48df3355ad" author="" title="Untitled workflow #36" /> + <s:processor name="concat"> + <s:beanshell> + <s:scriptvalue>Thread.sleep(200); +out = in1 + in2;</s:scriptvalue> + <s:beanshellinputlist> + <s:beanshellinput s:syntactictype="'text/plain'">in1</s:beanshellinput> + <s:beanshellinput s:syntactictype="'text/plain'">in2</s:beanshellinput> + </s:beanshellinputlist> + <s:beanshelloutputlist> + <s:beanshelloutput s:syntactictype="'text/plain'">out</s:beanshelloutput> + </s:beanshelloutputlist> + <s:dependencies s:classloader="iteration" /> + </s:beanshell> + </s:processor> + <s:link source="in1" sink="concat:in1" /> + <s:link source="in2" sink="concat:in2" /> + <s:link source="concat:out" sink="out" /> + <s:source name="in1" /> + <s:source name="in2" /> + <s:sink name="out" /> + </s:scufl> + </s:workflow> + </s:processor> + <s:link source="in1" sink="Nested_Workflow:in1" /> + <s:link source="in2" sink="Nested_Workflow:in2" /> + <s:link source="Nested_Workflow:out" sink="out" /> + <s:source name="in1" /> + <s:source name="in2" /> + <s:sink name="out" /> + </s:scufl> + </s:workflow> + <s:mergemode input="in2" mode="merge" /> + </s:processor> + <s:link source="constant:value" sink="merge:in1" /> + <s:link source="generate_list:list" sink="merge:in2" /> + <s:link source="generate_list:list" sink="merge:in2" /> + <s:link source="constant:value" sink="constant" /> + <s:link source="generate_list:list" sink="list" /> + <s:link source="merge:out" sink="concat" /> + <s:sink name="concat"> + <s:metadata> + <s:mimeTypes> + <s:mimeType>'text/plain'</s:mimeType> + </s:mimeTypes> + </s:metadata> + </s:sink> + <s:sink name="list"> + <s:metadata> + <s:mimeTypes> + <s:mimeType>l('text/plain')</s:mimeType> + </s:mimeTypes> + </s:metadata> + </s:sink> + <s:sink name="constant"> + <s:metadata> + <s:mimeTypes> + <s:mimeType>'text/plain'</s:mimeType> + </s:mimeTypes> + </s:metadata> + </s:sink> + <s:coordination name="constant_BLOCKON_generate_list"> + <s:condition> + <s:state>Completed</s:state> + <s:target>generate_list</s:target> + </s:condition> + <s:action> + <s:target>constant</s:target> + <s:statechange> + <s:from>Scheduled</s:from> + <s:to>Running</s:to> + </s:statechange> + </s:action> + </s:coordination> + <s:coordination name="merge_BLOCKON_generate_list"> + <s:condition> + <s:state>Completed</s:state> + <s:target>generate_list</s:target> + </s:condition> + <s:action> + <s:target>merge</s:target> + <s:statechange> + <s:from>Scheduled</s:from> + <s:to>Running</s:to> + </s:statechange> + </s:action> + </s:coordination> +</s:scufl> +
diff --git a/taverna-workbench-helper-api/pom.xml b/taverna-workbench-helper-api/pom.xml new file mode 100644 index 0000000..9f3945f --- /dev/null +++ b/taverna-workbench-helper-api/pom.xml
@@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <packaging>bundle</packaging> + <name>Help System</name> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Embed-Dependency>javahelp</Embed-Dependency> + <Import-Package>org.jdesktop.jdic.browser;resolution:=optional,*</Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>javax.help</groupId> + <artifactId>javahelp</artifactId> + <scope>provided</scope> + </dependency> + <!-- Required by javahelp --> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>com.springsource.javax.servlet</artifactId> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>com.springsource.javax.servlet.jsp</artifactId> + </dependency> + <dependency> + <groupId>javax.el</groupId> + <artifactId>com.springsource.javax.el</artifactId> + </dependency> + <dependency> + <groupId>org.apache.log4j</groupId> + <artifactId>com.springsource.org.apache.log4j</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpCollator.java b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpCollator.java new file mode 100644 index 0000000..8b19b69 --- /dev/null +++ b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpCollator.java
@@ -0,0 +1,307 @@ +package net.sf.taverna.t2.workbench.helper; + +import java.awt.Component; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.help.BadIDException; +import javax.help.HelpSet; +import javax.help.HelpSetException; +import javax.help.Map.ID; +import javax.help.TryMap; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; + +import org.apache.log4j.Logger; + +/** + * This class loads the {@link HelpSet} and also deals with the registration of + * ids and the decoding from a {@link Component} to the corresponding id. These + * two sets of functionality should possibly be separated. + * + * @author alanrw + */ +// TODO Convert to a bean +public final class HelpCollator { + private static Logger logger = Logger.getLogger(HelpCollator.class); + /** + * The HelpSet that is being used. + */ + private static HelpSet hs = null; + /** + * The mapping from components to ids. This is used because of problems with + * CSH throwing exceptions because it tried to use ids that were not in the + * map. + */ + private static Map<Component, String> idMap; + /** + * Indicates whether the HelpCollator has been initialized. + */ + private static boolean initialized = false; + /** + * A Pattern for normalizing the ids. + */ + private static Pattern nonAlphanumeric; + /** + * The emptyHelp is set if the HelpCollator was unable to read the + */ + private static boolean emptyHelp = true; + private static int TIMEOUT = 5000; + + private static String externalHelpSetURL = "http://www.mygrid.org.uk/taverna/helpset/" + + version() + "/helpset.hs"; + + // private static Profile profile = ProfileFactory.getInstance().getProfile(); + private static String version() { + return "NO-VERSION";//profile.getVersion(); + // TODO find a better way to find the version + } + + /** + * Attempt to read the up-to-date HelpSet from the web + */ + private static void readExternalHelpSet() { + try { + URL url = new URL(externalHelpSetURL); + checkConnection(url); + hs = new HelpSet(null, url); + if (hs.getLocalMap() == null) { + hs = null; + logger.error("Helpset from " + externalHelpSetURL + + " local map was null"); + } else + logger.info("Read external help set from " + externalHelpSetURL); + } catch (MissingResourceException e) { + logger.error("No external HelpSet URL specified", e); + } catch (MalformedURLException e) { + logger.error("External HelpSet URL is malformed", e); + } catch (HelpSetException e) { + logger.error("External HelpSet could not be read", e); + } catch (IOException e) { + logger.error("IOException reading External HelpSet", e); + } + } + + private static void checkConnection(URL url) throws IOException { + if (!url.getProtocol().startsWith("http")) + return; + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setReadTimeout(TIMEOUT); + connection.setConnectTimeout(TIMEOUT); + connection.setRequestMethod("HEAD"); + connection.getInputStream().close(); + connection.disconnect(); + } + + /** + * This methods creates a HelpSet based upon, in priority, the external + * HelpSet, then a newly created empty HelpSet. + */ + private static void initialize() { + if (initialized) + return; + readExternalHelpSet(); + if (hs == null) { + hs = new HelpSet(); + hs.setLocalMap(new TryMap()); + } else { + logger.trace("EmptyHelp set to false"); + emptyHelp = false; + } + idMap = new HashMap<>(); + nonAlphanumeric = Pattern.compile("[^a-z0-9\\.]"); + initialized = true; + } + + /** + * Indicates if an empty HelpSet is being used + * + * @return + */ + public static boolean isEmptyHelp() { + return emptyHelp; + } + + public static URL getURLFromID(String id) throws BadIDException, + MalformedURLException { + initialize(); + logger.trace("Looking for id: " + id); + ID theId = ID.create(id, hs); + if (theId == null) + return null; + return hs.getCombinedMap().getURLFromID(theId); + } + + /** + * Register a component under the specified id. The method checks that the + * id is known to the HelpSet's map. + * + * @param component + * @param id + */ + public static void registerComponent(Component component, String id) { + logger.trace("Attempting to register " + id); + initialize(); + String normalizedId = normalizeString(id.toLowerCase()); + if (idMap.containsKey(component)) { + logger.info("Registered " + normalizedId); + return; + } + + /* + * If Workbench is started up while there is no network connection - + * hs.getLocalMap() is null for some reason + */ + if (hs != null && hs.getLocalMap() != null + && hs.getLocalMap().isValidID(normalizedId, hs)) { + idMap.put(component, normalizedId); + logger.info("Registered " + normalizedId); + } else + logger.warn("Refused to register component as " + normalizedId + + " not in map"); + } + + /** + * Register a component. Since no id is specified, the HelpCollator takes + * the canonical name of the component's class. This is useful when an + * explicit hierarchy-based approach has been taken. + * + * @param component + */ + public static void registerComponent(Component component) { + String canonicalName = component.getClass().getCanonicalName(); + if (canonicalName != null) + registerComponent(component, canonicalName); + } + + /** + * Register a component based upon its parent's class and a suffix + * indicating the component's purpose in the parent. + * + * @param component + * @param parent + * @param suffix + */ + public static void registerComponent(Component component, Object parent, + String suffix) { + String canonicalName = parent.getClass().getCanonicalName(); + if (canonicalName != null) + registerComponent(component, canonicalName + "-" + suffix); + } + + /** + * Try to find an id for the Component. This code should be re-written when + * we have more experience in how to couple the UI and HelpSets. + * + * @param c + * @return + */ + static String getHelpID(Component c) { + initialize(); + boolean found = false; + String result = null; + if (c instanceof JTree) { + String idInTree = getHelpIDInTree((JTree) c); + if (idInTree != null) { + found = true; + result = idInTree; + } + } + Component working = c; + if (c != null) + logger.trace("Starting at a " + working.getClass()); + while (!found && (working != null)) { + if (idMap.containsKey(working)) { + result = idMap.get(working); + found = true; + logger.trace("Found component id " + result); + } else { + String className = working.getClass().getCanonicalName(); + if (hs.getLocalMap().isValidID(className, hs)) { + result = className; + found = true; + logger.trace("Found class name " + result); + } + } + if (!found) { + working = working.getParent(); + if (working != null) + logger.trace("Moved up to a " + working.getClass()); + } + } + return result; + } + + /** + * Change the input String into an id that contains only alphanumeric + * characters or hyphens. + * + * @param input + * @return + */ + private static String normalizeString(String input) { + Matcher m = nonAlphanumeric.matcher(input); + return m.replaceAll("-"); + } + + /** + * If help is sought on part of a JTree, then this method attempts to find a + * node of the tree that can be mapped to an id. The possibilities are ad + * hoc and should be re-examined when more experience is gained. + * + * @param c + * @return + */ + private static String getHelpIDInTree(JTree c) { + initialize(); + + TreePath tp = c.getSelectionPath(); + if (tp == null) + return null; + + Object o = tp.getLastPathComponent(); + if (o == null) + return null; + + if (o instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) o; + if (dmtn.getUserObject() != null) + o = dmtn.getUserObject(); + } + + String className = o.getClass().getCanonicalName(); + + logger.trace("Tree node as a string is " + o); + + String possibility = normalizeString(o.toString().toLowerCase()); + + logger.trace("Normalized is " + possibility); + logger.trace("Tree node class name is " + className); + + possibility = className + "-" + possibility; + + logger.trace("Possibility is " + possibility); + + String result; + if (hs.getLocalMap().isValidID(possibility, hs)) { + result = possibility; + logger.trace("Accepted tree node " + result); + } else if (hs.getLocalMap().isValidID(className, hs)) { + result = className; + logger.trace("Found tree node class name " + result); + } else { + result = null; + } + + logger.debug("Tree node is a " + o.getClass()); + return result; + } +}
diff --git a/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpEnabledDialog.java b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpEnabledDialog.java new file mode 100644 index 0000000..ec17171 --- /dev/null +++ b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/HelpEnabledDialog.java
@@ -0,0 +1,101 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.helper; + +import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow; +import static net.sf.taverna.t2.workbench.helper.HelpCollator.registerComponent; +import static net.sf.taverna.t2.workbench.helper.Helper.setKeyCatcher; + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.HeadlessException; + +import javax.swing.JDialog; + +/** + * This class extends JDialog to register the dialog and also attach a key + * catcher so that F1 is interpreted as help + * + * @author alanrw + */ +public class HelpEnabledDialog extends JDialog { + private static final long serialVersionUID = -5068807887477419800L; + + /** + * Create a HelpEnabledDialog, register it (if possible) with the + * HelpCollator and attach a keycatcher. + * + * @param owner + * @param title + * @param modal + * @param id + * @throws HeadlessException + */ + public HelpEnabledDialog(Frame owner, String title, boolean modal, String id) + throws HeadlessException { + super(owner == null ? getMainWindow() : owner, title, modal); + + if (id != null) + registerComponent(this, id); + else if (owner != null) + registerComponent(this, owner.getClass().getCanonicalName() + + "-dialog"); + else if (title != null && !title.isEmpty()) + registerComponent(this, title); + setKeyCatcher(this); + } + + /** + * Create a HelpEnabledDialog, register it (if possible) with the + * HelpCollator and attach a keycatcher. + * + * @param owner + * @param title + * @param modal + * @param id + * @throws HeadlessException + */ + public HelpEnabledDialog(Dialog owner, String title, boolean modal, + String id) throws HeadlessException { + super(owner, title, modal); + if (id != null) + registerComponent(this, id); + else if (owner != null) + registerComponent(this, owner.getClass().getCanonicalName() + + "-dialog"); + setKeyCatcher(this); + } + + /** + * Create a HelpEnabledDialog, register it (if possible) with the + * HelpCollator and attach a keycatcher. + * + * @param owner + * @param title + * @param modal + * @throws HeadlessException + */ + public HelpEnabledDialog(Frame parent, String title, boolean modal) { + this(parent, title, modal, null); + } + + /** + * Create a HelpEnabledDialog, register it (if possible) with the + * HelpCollator and attach a keycatcher. + * + * @param owner + * @param title + * @param modal + * @throws HeadlessException + */ + public HelpEnabledDialog(Dialog parent, String title, boolean modal) { + this(parent, title, modal, null); + } + + @Override + public void setVisible(boolean b) { + setLocationRelativeTo(getParent()); + super.setVisible(b); + } +}
diff --git a/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/Helper.java b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/Helper.java new file mode 100644 index 0000000..21b0f75 --- /dev/null +++ b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/Helper.java
@@ -0,0 +1,187 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.helper; + +import static java.awt.Desktop.getDesktop; +import static java.awt.MouseInfo.getPointerInfo; +import static javax.swing.JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.helper.HelpCollator.getHelpID; +import static net.sf.taverna.t2.workbench.helper.HelpCollator.getURLFromID; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +import javax.help.BadIDException; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.JRootPane; +import javax.swing.RootPaneContainer; + +import org.apache.log4j.Logger; + +/** + * This class creates the dialogs for the presentation of the HelpSet held by + * the HelpCollator. + * + * @author alanrw + */ +public final class Helper { + private static Helper instance; + private static Logger logger = Logger.getLogger(Helper.class); + + /** + * Create a Helper and initialize the static variables. + */ + private Helper() { + } + + /** + * Get the singleton instance of Helper. In theory there could be more than + * one. + * + * @return + */ + private static Helper getInstance() { + if (instance == null) + instance = new Helper(); + return instance; + } + + /** + * Show in the current dialog the entry (if any) corresponding to the + * specified id. + * + * @param id + */ + private static void showID(String id) { + getInstance(); + try { + URL result = getURLFromID(id); + if (result == null) + result = getURLFromID("home"); + getDesktop().browse(result.toURI()); + } catch (BadIDException | IOException | URISyntaxException e) { + logger.error(e); + } + } + + /** + * Show the most suitable help for the specified component. + * + * @param c + */ + public static void showHelp(Component c) { + showID(getHelpID(c)); + } + + /** + * Display the default home page help. + * + * @param e + */ + public static void displayDefaultHelp(AWTEvent e) { + showID("home"); + } + + public static void displayFieldLevelHelp(ActionEvent e) { + // + } + + private static final String HELP_KEY = "F1"; + + /** + * Associated the specified action with key presses in the specified + * component. + * + * @param component + * @param theAction + */ + public static void setKeyCatcher(final JComponent component, + final AbstractAction theAction) { + InputMap oldInputMap = component + .getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + InputMap newInputMap = new InputMap(); + newInputMap.setParent(oldInputMap); + newInputMap.put(getKeyStroke(HELP_KEY), "doSomething"); + component.setInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, newInputMap); + ActionMap oldActionMap = component.getActionMap(); + ActionMap newActionMap = new ActionMap(); + newActionMap.setParent(oldActionMap); + newActionMap.put("doSomething", theAction); + component.setActionMap(newActionMap); + } + + /** + * Set up a key-press catcher for the specified component such that when F1 + * is pressed it should help for the component where the cursor is. + * + * @param rootpanecontainer + */ + public static void setKeyCatcher(final RootPaneContainer rootpanecontainer) { + @SuppressWarnings("serial") + AbstractAction theAction = new AbstractAction() { + @Override + public void actionPerformed(ActionEvent evt) { + Component component = (Component) rootpanecontainer; + Container container = (Container) rootpanecontainer; + logger.info("frame action F1 pressed with source " + + evt.getSource().getClass().getName()); + Point mousePosition = getPointerInfo().getLocation(); + Point framePosition = component.getLocation(); + Point relativePosition = (Point) mousePosition.clone(); + relativePosition.translate(-framePosition.x, -framePosition.y); + Component c = container.findComponentAt(relativePosition); + if (c != null) + logger.info("F1 pressed in a " + c.getClass().getName()); + showHelpWithinContainer(rootpanecontainer, c); + } + }; + + JRootPane pane = rootpanecontainer.getRootPane(); + setKeyCatcher(pane, theAction); + } + + /** + * Show the help most associated with the specific component within the container. + * + * @param root + * @param c + */ + static void showHelpWithinContainer(RootPaneContainer root, Component c) { + getInstance(); + showHelp(c); + } + + /** + * Register a component with the {@link HelpCollator} under the specified + * id. + * + * @param component + * @param id + */ + public static void registerComponent(Component component, final String id) { + HelpCollator.registerComponent(component, id); + } + + /** + * Register a component with the {@link HelpCollator}. + * + * @param component + * @param parent + * @param suffix + */ + public static void registerComponent(Component component, Object parent, + String suffix) { + HelpCollator.registerComponent(component, parent, suffix); + } +}
diff --git a/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/NonBlockedHelpEnabledDialog.java b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/NonBlockedHelpEnabledDialog.java new file mode 100644 index 0000000..67e6bc5 --- /dev/null +++ b/taverna-workbench-helper-api/src/main/java/net/sf/taverna/t2/workbench/helper/NonBlockedHelpEnabledDialog.java
@@ -0,0 +1,40 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.helper; + +import static java.awt.Dialog.ModalExclusionType.APPLICATION_EXCLUDE; + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.HeadlessException; + +/** + * @author alanrw + */ +public class NonBlockedHelpEnabledDialog extends HelpEnabledDialog { + private static final long serialVersionUID = -2455471377333940417L; + + public NonBlockedHelpEnabledDialog(Dialog owner, String title, + boolean modal, String id) throws HeadlessException { + super(owner, title, modal, id); + this.setModalExclusionType(APPLICATION_EXCLUDE); + } + + public NonBlockedHelpEnabledDialog(Frame owner, String title, + boolean modal, String id) throws HeadlessException { + super(owner, title, modal, id); + this.setModalExclusionType(APPLICATION_EXCLUDE); + } + + public NonBlockedHelpEnabledDialog(Frame parent, String title, boolean modal) { + super(parent, title, modal, null); + this.setModalExclusionType(APPLICATION_EXCLUDE); + } + + public NonBlockedHelpEnabledDialog(Dialog parent, String title, + boolean modal) { + super(parent, title, modal, null); + this.setModalExclusionType(APPLICATION_EXCLUDE); + } +}
diff --git a/taverna-workbench-helper/pom.xml b/taverna-workbench-helper/pom.xml new file mode 100644 index 0000000..70c0621 --- /dev/null +++ b/taverna-workbench-helper/pom.xml
@@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>helper</artifactId> + <name>Help System (legacy dependency)</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-httpproxy-config/pom.xml b/taverna-workbench-httpproxy-config/pom.xml new file mode 100644 index 0000000..f1a8328 --- /dev/null +++ b/taverna-workbench-httpproxy-config/pom.xml
@@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>httpproxy-config</artifactId> + <packaging>bundle</packaging> + <name>HTTP Proxy configuration</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationPanel.java b/taverna-workbench-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationPanel.java new file mode 100644 index 0000000..1229d57 --- /dev/null +++ b/taverna-workbench-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationPanel.java
@@ -0,0 +1,582 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.httpproxy.config; + +import static java.awt.GridBagConstraints.BOTH; +import static java.awt.GridBagConstraints.CENTER; +import static java.awt.GridBagConstraints.HORIZONTAL; +import static java.awt.GridBagConstraints.NONE; +import static java.awt.GridBagConstraints.WEST; +import static javax.swing.JOptionPane.showMessageDialog; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; +import static net.sf.taverna.t2.workbench.helper.Helper.showHelp; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.PROXY_USE_OPTION; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_NON_PROXY_HOSTS; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_HOST; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_PASSWORD; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_PORT; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.SYSTEM_PROXY_USER; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_NON_PROXY_HOSTS; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_HOST; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_PASSWORD; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_PORT; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.TAVERNA_PROXY_USER; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.USE_NO_PROXY_OPTION; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.USE_SPECIFIED_VALUES_OPTION; +import static uk.org.taverna.configuration.proxy.HttpProxyConfiguration.USE_SYSTEM_PROPERTIES_OPTION; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.AbstractAction; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.lang.ui.DialogTextArea; +import uk.org.taverna.configuration.proxy.HttpProxyConfiguration; + +/** + * The HttpProxyConfigurationPanel provides the user interface to a + * {@link HttpProxyConfiguration} to determine how HTTP Connections are made by + * Taverna. + * + * @author alanrw + * @author David Withers + */ +public class HttpProxyConfigurationPanel extends JPanel { + static final long serialVersionUID = 3668473431971125038L; + /** + * The size of the field for the JTextFields. + */ + private static int TEXTFIELD_SIZE = 25; + + private final HttpProxyConfiguration httpProxyConfiguration; + /** + * RadioButtons that are in a common ButtonGroup. Selecting one of them + * indicates whether the system http proxy settings, the ad hoc specified + * values or no proxy settings at all should be used. + */ + private JRadioButton useSystemProperties; + private JRadioButton useSpecifiedValues; + private JRadioButton useNoProxy; + /** + * JTextFields and one DialogTextArea to hold the settings for the HTTP + * proxy properties. The values are only editable if the user picks + * useSpecifiedValues. + */ + private JTextField proxyHostField; + private JTextField proxyPortField; + private JTextField proxyUserField; + private JTextField proxyPasswordField; + private DialogTextArea nonProxyHostsArea; + private JScrollPane nonProxyScrollPane; + /** + * A string that indicates which HTTP setting option the user has currently + * picked. This does not necesarily match that which has been applied. + */ + private String shownOption = USE_SYSTEM_PROPERTIES_OPTION; + + /** + * The HttpProxyConfigurationPanel consists of a set of properties where the + * configuration values for HTTP can be specified and a set of buttons where + * the more general apply, help etc. appear. + */ + public HttpProxyConfigurationPanel( + HttpProxyConfiguration httpProxyConfiguration) { + this.httpProxyConfiguration = httpProxyConfiguration; + initComponents(); + } + + /** + * Populates the panel with a representation of the current HTTP proxy + * settings for the specified {@link HttpProxyConfiguration} and also the + * capability to alter them. + */ + private void initComponents() { + shownOption = httpProxyConfiguration.getProperty(PROXY_USE_OPTION); + + this.setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + + // Title describing what kind of settings we are configuring here + JTextArea descriptionText = new JTextArea("HTTP proxy configuration"); + descriptionText.setLineWrap(true); + descriptionText.setWrapStyleWord(true); + descriptionText.setEditable(false); + descriptionText.setFocusable(false); + descriptionText.setBorder(new EmptyBorder(10, 10, 10, 10)); + gbc.anchor = WEST; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.fill = HORIZONTAL; + this.add(descriptionText, gbc); + + /** + * Generate the three radio buttons and put them in a group. Each button + * is bound to an action that alters the shownOption and re-populates + * the shown HTTP property fields. + */ + useNoProxy = new JRadioButton("Do not use a proxy"); + useNoProxy.setAlignmentX(LEFT_ALIGNMENT); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 2; + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.fill = NONE; + gbc.insets = new Insets(10, 0, 0, 0); + this.add(useNoProxy, gbc); + ActionListener useNoProxyListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + shownOption = USE_NO_PROXY_OPTION; + populateFields(); + } + }; + useNoProxy.addActionListener(useNoProxyListener); + + useSystemProperties = new JRadioButton("Use system properties"); + useSystemProperties.setAlignmentX(LEFT_ALIGNMENT); + gbc.gridx = 0; + gbc.gridy = 2; + gbc.insets = new Insets(0, 0, 0, 0); + this.add(useSystemProperties, gbc); + ActionListener systemPropertiesListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + shownOption = USE_SYSTEM_PROPERTIES_OPTION; + populateFields(); + } + }; + useSystemProperties.addActionListener(systemPropertiesListener); + + useSpecifiedValues = new JRadioButton("Use specified values"); + useSpecifiedValues.setAlignmentX(LEFT_ALIGNMENT); + gbc.gridx = 0; + gbc.gridy = 3; + this.add(useSpecifiedValues, gbc); + ActionListener specifiedValuesListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + shownOption = USE_SPECIFIED_VALUES_OPTION; + populateFields(); + } + }; + useSpecifiedValues.addActionListener(specifiedValuesListener); + + ButtonGroup bg = new ButtonGroup(); + bg.add(useSystemProperties); + bg.add(useSpecifiedValues); + bg.add(useNoProxy); + + /** + * Create the fields to show the HTTP proxy property values. These + * become editable if the shown option is to use specified values. + */ + proxyHostField = new JTextField(TEXTFIELD_SIZE); + gbc.gridx = 0; + gbc.gridy = 4; + gbc.gridwidth = 1; + gbc.fill = NONE; + gbc.insets = new Insets(10, 0, 0, 0); + this.add(new JLabel("Proxy host"), gbc); + gbc.gridx = 1; + gbc.gridy = 4; + gbc.gridwidth = 1; + gbc.fill = HORIZONTAL; + this.add(proxyHostField, gbc); + + proxyPortField = new JTextField(TEXTFIELD_SIZE); + gbc.gridx = 0; + gbc.gridy = 5; + gbc.gridwidth = 1; + gbc.fill = NONE; + gbc.insets = new Insets(0, 0, 0, 0); + this.add(new JLabel("Proxy port"), gbc); + gbc.gridx = 1; + gbc.gridy = 5; + gbc.gridwidth = 1; + gbc.fill = HORIZONTAL; + this.add(proxyPortField, gbc); + + proxyUserField = new JTextField(TEXTFIELD_SIZE); + gbc.gridx = 0; + gbc.gridy = 6; + gbc.gridwidth = 1; + gbc.fill = NONE; + this.add(new JLabel("Proxy user"), gbc); + gbc.gridx = 1; + gbc.gridy = 6; + gbc.gridwidth = 1; + gbc.fill = HORIZONTAL; + this.add(proxyUserField, gbc); + + proxyPasswordField = new JTextField(TEXTFIELD_SIZE); + gbc.gridx = 0; + gbc.gridy = 7; + gbc.gridwidth = 1; + gbc.fill = NONE; + this.add(new JLabel("Proxy password"), gbc); + gbc.gridx = 1; + gbc.gridy = 7; + gbc.gridwidth = 1; + gbc.fill = HORIZONTAL; + this.add(proxyPasswordField, gbc); + + nonProxyHostsArea = new DialogTextArea(10, 40); + nonProxyScrollPane = new JScrollPane(nonProxyHostsArea); + nonProxyScrollPane + .setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED); + nonProxyScrollPane + .setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED); + // nonProxyScrollPane.setPreferredSize(new Dimension(300, 500)); + gbc.gridx = 0; + gbc.gridy = 8; + gbc.gridwidth = 2; + gbc.fill = NONE; + gbc.insets = new Insets(10, 0, 0, 0); + this.add(new JLabel("Non-proxy hosts"), gbc); + gbc.gridx = 0; + gbc.gridy = 9; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.gridwidth = 2; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.fill = BOTH; + this.add(nonProxyScrollPane, gbc); + + // Add buttons panel + gbc.gridx = 0; + gbc.gridy = 10; + gbc.weightx = 0.0; + gbc.weighty = 0.0; + gbc.gridwidth = 2; + gbc.fill = HORIZONTAL; + gbc.anchor = CENTER; + gbc.insets = new Insets(10, 0, 0, 0); + this.add(createButtonPanel(), gbc); + + setFields(); + } + + /** + * Populate the fields in the property panel according to which option is + * being shown and the stored values within the + * {@link HttpProxyConfiguration}. + */ + private void populateFields() { + /** + * Editing of the property fields is only available when the option is + * to use the specified values. + */ + boolean editingEnabled = shownOption + .equals(USE_SPECIFIED_VALUES_OPTION); + + if (shownOption.equals(USE_SYSTEM_PROPERTIES_OPTION)) { + proxyHostField.setText(httpProxyConfiguration + .getProperty(SYSTEM_PROXY_HOST)); + proxyPortField.setText(httpProxyConfiguration + .getProperty(SYSTEM_PROXY_PORT)); + proxyUserField.setText(httpProxyConfiguration + .getProperty(SYSTEM_PROXY_USER)); + proxyPasswordField.setText(httpProxyConfiguration + .getProperty(SYSTEM_PROXY_PASSWORD)); + nonProxyHostsArea.setText(httpProxyConfiguration + .getProperty(SYSTEM_NON_PROXY_HOSTS)); + } else if (shownOption.equals(USE_SPECIFIED_VALUES_OPTION)) { + proxyHostField.setText(httpProxyConfiguration + .getProperty(TAVERNA_PROXY_HOST)); + proxyPortField.setText(httpProxyConfiguration + .getProperty(TAVERNA_PROXY_PORT)); + proxyUserField.setText(httpProxyConfiguration + .getProperty(TAVERNA_PROXY_USER)); + proxyPasswordField.setText(httpProxyConfiguration + .getProperty(TAVERNA_PROXY_PASSWORD)); + nonProxyHostsArea.setText(httpProxyConfiguration + .getProperty(TAVERNA_NON_PROXY_HOSTS)); + } else { + proxyHostField.setText(null); + proxyPortField.setText(null); + proxyUserField.setText(null); + proxyPasswordField.setText(null); + nonProxyHostsArea.setText(null); + } + + proxyHostField.setEnabled(editingEnabled); + proxyPortField.setEnabled(editingEnabled); + proxyUserField.setEnabled(editingEnabled); + proxyPasswordField.setEnabled(editingEnabled); + nonProxyHostsArea.setEnabled(editingEnabled); + nonProxyHostsArea.setEditable(editingEnabled); + nonProxyScrollPane.setEnabled(editingEnabled); + } + + /** + * Create the panel to contain the buttons + * + * @return + */ + @SuppressWarnings("serial") + private JPanel createButtonPanel() { + final JPanel panel = new JPanel(); + + /** + * The helpButton shows help about the current component + */ + JButton helpButton = new JButton(new AbstractAction("Help") { + @Override + public void actionPerformed(ActionEvent arg0) { + showHelp(panel); + } + }); + panel.add(helpButton); + + /** + * The resetButton changes the property values shown to those + * corresponding to the configuration currently applied. + */ + JButton resetButton = new JButton(new AbstractAction("Reset") { + @Override + public void actionPerformed(ActionEvent arg0) { + setFields(); + } + }); + panel.add(resetButton); + + /** + * The applyButton applies the shown field values to the + * {@link HttpProxyConfiguration} and saves them for future. + */ + JButton applyButton = new JButton(new AbstractAction("Apply") { + @Override + public void actionPerformed(ActionEvent arg0) { + applySettings(); + setFields(); + } + }); + panel.add(applyButton); + + return panel; + } + + /** + * Checks that the specified values for the HTTP properties are a valid + * combination and, if so, saves them for future use. It does not apply them + * to the currently executing Taverna. + */ + private void saveSettings() { + if (useSystemProperties.isSelected()) { + httpProxyConfiguration.setProperty(PROXY_USE_OPTION, + USE_SYSTEM_PROPERTIES_OPTION); + } else if (useNoProxy.isSelected()) { + httpProxyConfiguration.setProperty(PROXY_USE_OPTION, + USE_NO_PROXY_OPTION); + } else { + if (validateFields()) { + httpProxyConfiguration.setProperty(PROXY_USE_OPTION, + USE_SPECIFIED_VALUES_OPTION); + httpProxyConfiguration.setProperty(TAVERNA_PROXY_HOST, + proxyHostField.getText()); + httpProxyConfiguration.setProperty(TAVERNA_PROXY_PORT, + proxyPortField.getText()); + httpProxyConfiguration.setProperty(TAVERNA_PROXY_USER, + proxyUserField.getText()); + httpProxyConfiguration.setProperty(TAVERNA_PROXY_PASSWORD, + proxyPasswordField.getText()); + httpProxyConfiguration.setProperty(TAVERNA_NON_PROXY_HOSTS, + nonProxyHostsArea.getText()); + } + } + } + + /** + * Validates and, where appropriate formats, the properties values specified + * for HTTP Proxy configuration. + * + * @return + */ + private boolean validateFields() { + boolean result = true; + result = result && validateHostField(); + result = result && validatePortField(); + result = result && validateUserField(); + result = result && validatePasswordField(); + result = result && validateNonProxyHostsArea(); + return result; + } + + /** + * Checks that, if a value is specified for non-proxy hosts then a proxy + * host has also been specified. Formats the non-proxy hosts string so that + * if the user has entered the hosts on separate lines, then the stored + * values are separated by bars. + * + * @return + */ + private boolean validateNonProxyHostsArea() { + boolean result = true; + String value = nonProxyHostsArea.getText(); + if ((value != null) && (!value.equals(""))) { + value = value.replaceAll("\\n", "|"); + nonProxyHostsArea.setText(value); + result = result + && dependsUpon("non-proxy host", "host", + proxyHostField.getText()); + } + return result; + } + + /** + * Checks that, if a password has been specified, then a user has also been + * specified. + * + * @return + */ + private boolean validatePasswordField() { + boolean result = true; + String value = proxyPasswordField.getText(); + if ((value != null) && !value.isEmpty()) + result = result + && dependsUpon("password", "user", proxyHostField.getText()); + return result; + } + + /** + * Checks that if a user has been specified, then a host has also been + * specified. + * + * @return + */ + private boolean validateUserField() { + boolean result = true; + String value = proxyUserField.getText(); + if ((value != null) && !value.isEmpty()) + result = result + && dependsUpon("user", "host", proxyHostField.getText()); + return result; + } + + /** + * Checks that if a port has been specified then a host has also been + * specified. Checks that the port number is a non-negative integer. If the + * port has not been specified, then if a host has been specified, the + * default value 80 is used. + * + * @return + */ + private boolean validatePortField() { + boolean result = true; + String value = proxyPortField.getText(); + if ((value != null) && (!value.equals(""))) { + result = result + && dependsUpon("port", "host", proxyHostField.getText()); + try { + int parsedNumber = Integer.parseInt(value); + if (parsedNumber <= 0) { + showMessageDialog(this, "The port must be non-negative"); + result = false; + } + } catch (NumberFormatException e) { + showMessageDialog(this, "The port must be an integer"); + result = false; + } + } else { + String hostField = proxyHostField.getText(); + if ((hostField != null) && !hostField.isEmpty()) + proxyPortField.setText("80"); + } + return result; + } + + /** + * Checks if the targetValue has been specified. If not then a message is + * displayed indicating that the dependent cannot be specified with the + * target. + * + * @param dependent + * @param target + * @param targetValue + * @return + */ + private boolean dependsUpon(String dependent, String target, + String targetValue) { + boolean result = true; + if ((targetValue == null) || target.equals("")) { + showMessageDialog(this, "A " + dependent + + " cannot be specified without a " + target); + result = false; + } + return result; + } + + /** + * Could validate the host field e.g. by establishing a connection. + * Currently no validation is done. + * + * @return + */ + private boolean validateHostField() { + boolean result = true; + // String value = proxyHostField.getText(); + return result; + } + + /** + * Save the currently set field values (if valid) to the + * {@link HttpProxyConfiguration}. Also applies those values to the + * currently running Taverna. + */ + private void applySettings() { + if (validateFields()) { + saveSettings(); + httpProxyConfiguration.changeProxySettings(); + } + } + + /** + * Set the shown field values to those currently in use (i.e. last saved + * configuration). + */ + private void setFields() { + shownOption = httpProxyConfiguration.getProperty(PROXY_USE_OPTION); + useSystemProperties.setSelected(shownOption + .equals(USE_SYSTEM_PROPERTIES_OPTION)); + useSpecifiedValues.setSelected(shownOption + .equals(USE_SPECIFIED_VALUES_OPTION)); + useNoProxy.setSelected(shownOption.equals(USE_NO_PROXY_OPTION)); + populateFields(); + } +}
diff --git a/taverna-workbench-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationUIFactory.java b/taverna-workbench-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationUIFactory.java new file mode 100644 index 0000000..9f6ac8c --- /dev/null +++ b/taverna-workbench-httpproxy-config/src/main/java/net/sf/taverna/t2/workbench/httpproxy/config/HttpProxyConfigurationUIFactory.java
@@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.httpproxy.config; + +import javax.swing.JPanel; + +import uk.org.taverna.configuration.Configurable; +import uk.org.taverna.configuration.ConfigurationUIFactory; +import uk.org.taverna.configuration.proxy.HttpProxyConfiguration; + +/** + * A Factory to create a HttpProxyConfiguration + * + * @author alanrw + * @author David Withers + */ +public class HttpProxyConfigurationUIFactory implements ConfigurationUIFactory { + private HttpProxyConfiguration httpProxyConfiguration; + + @Override + public boolean canHandle(String uuid) { + return uuid.equals(getConfigurable().getUUID()); + } + + @Override + public JPanel getConfigurationPanel() { + return new HttpProxyConfigurationPanel(httpProxyConfiguration); + } + + @Override + public Configurable getConfigurable() { + return httpProxyConfiguration; + } + + public void setHttpProxyConfiguration(HttpProxyConfiguration httpProxyConfiguration) { + this.httpProxyConfiguration = httpProxyConfiguration; + } +}
diff --git a/taverna-workbench-httpproxy-config/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory b/taverna-workbench-httpproxy-config/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory new file mode 100644 index 0000000..d87772b --- /dev/null +++ b/taverna-workbench-httpproxy-config/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.configuration.ConfigurationUIFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.httpproxy.config.HttpProxyConfigurationUIFactory \ No newline at end of file
diff --git a/taverna-workbench-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context-osgi.xml b/taverna-workbench-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context-osgi.xml new file mode 100644 index 0000000..631bdb4 --- /dev/null +++ b/taverna-workbench-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context-osgi.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="HttpProxyConfigurationUIFactory" interface="uk.org.taverna.configuration.ConfigurationUIFactory" /> + + <reference id="httpProxyConfiguration" interface="uk.org.taverna.configuration.proxy.HttpProxyConfiguration" /> + +</beans:beans>
diff --git a/taverna-workbench-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context.xml b/taverna-workbench-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context.xml new file mode 100644 index 0000000..6d6060f --- /dev/null +++ b/taverna-workbench-httpproxy-config/src/main/resources/META-INF/spring/httpproxy-config-context.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="HttpProxyConfigurationUIFactory" class="net.sf.taverna.t2.workbench.httpproxy.config.HttpProxyConfigurationUIFactory"> + <property name="httpProxyConfiguration" ref="httpProxyConfiguration" /> + </bean> + +</beans>
diff --git a/taverna-workbench-iteration-strategy-ui/pom.xml b/taverna-workbench-iteration-strategy-ui/pom.xml new file mode 100644 index 0000000..0b6ff81 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/pom.xml
@@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>iteration-strategy-ui</artifactId> + <packaging>bundle</packaging> + <name>Menu generation API</name> + <description>An SPI system for building UI menues</description> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.core</groupId> + <artifactId>workflowmodel-api</artifactId> + <version>${t2.core.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.core</groupId> + <artifactId>workflowmodel-impl</artifactId> + <version>${t2.core.version}</version> + <!-- <scope>test</scope> --> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/IterationStrategyIcons.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/IterationStrategyIcons.java new file mode 100644 index 0000000..350c0cc --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/IterationStrategyIcons.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.iterationstrategy; + +import javax.swing.ImageIcon; + +import org.apache.log4j.Logger; + +public class IterationStrategyIcons { + + private static Logger logger = Logger + .getLogger(IterationStrategyIcons.class); + + public static ImageIcon joinIteratorIcon, lockStepIteratorIcon, + leafnodeicon; + + static { + try { + Class<?> c = IterationStrategyIcons.class; + joinIteratorIcon = new ImageIcon(c + .getResource("icons/crossproducticon.png")); + lockStepIteratorIcon = new ImageIcon(c + .getResource("icons/dotproducticon.png")); + leafnodeicon = new ImageIcon(c + .getResource("icons/leafnodeicon.png")); + } catch (Exception ex) { + logger.warn("Could not find icon", ex); + } + } +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyConfigurationDialog.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyConfigurationDialog.java new file mode 100644 index 0000000..1af83cb --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyConfigurationDialog.java
@@ -0,0 +1,148 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.iterationstrategy.contextview; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workbench.iterationstrategy.editor.IterationStrategyEditorControl; +import net.sf.taverna.t2.workflowmodel.Edit; +import net.sf.taverna.t2.workflowmodel.EditException; +import net.sf.taverna.t2.workflowmodel.Edits; +import net.sf.taverna.t2.workflowmodel.Processor; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategyStack; + +import org.apache.log4j.Logger; + +/** + * @author alanrw + * + */ +@SuppressWarnings("serial") +public class IterationStrategyConfigurationDialog extends HelpEnabledDialog { + + private static Logger logger = Logger + .getLogger(IterationStrategyConfigurationDialog.class); + + private final EditManager editManager; + private final FileManager fileManager; + + + private final Frame owner; + private final Processor processor; + private final IterationStrategyStack originalStack; + + private IterationStrategyStack workingStack; + + public IterationStrategyConfigurationDialog(Frame owner, Processor processor, IterationStrategyStack iStack, EditManager editManager, FileManager fileManager) { + super (owner, "List handling for " + processor.getLocalName(), true, null); + this.owner = owner; + this.processor = processor; + this.originalStack = iStack; + this.editManager = editManager; + this.fileManager = fileManager; + this.workingStack = IterationStrategyContextualView.copyIterationStrategyStack(originalStack); + IterationStrategy iterationStrategy = IterationStrategyContextualView.getIterationStrategy(workingStack); + IterationStrategyEditorControl iterationStrategyEditorControl = new IterationStrategyEditorControl( + iterationStrategy); + this.add(iterationStrategyEditorControl, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout()); + + JButton okButton = new JButton(new OKAction(this)); + buttonPanel.add(okButton); + + JButton resetButton = new JButton(new ResetAction( + iterationStrategyEditorControl)); + buttonPanel.add(resetButton); + + JButton cancelButton = new JButton(new CancelAction(this)); + buttonPanel.add(cancelButton); + + this.add(buttonPanel, BorderLayout.SOUTH); + this.pack(); + this.setSize(new Dimension(getPreferredSize().width, getPreferredSize().height > 400 ? 400 : getPreferredSize().height)); + } + + private final class OKAction extends AbstractAction { + private final JDialog dialog; + + private OKAction(JDialog dialog) { + super("OK"); + this.dialog = dialog; + } + + public void actionPerformed(ActionEvent e) { + Edits edits = editManager.getEdits(); + try { + Edit<?> edit = edits.getSetIterationStrategyStackEdit( + processor, + IterationStrategyContextualView.copyIterationStrategyStack(workingStack)); + editManager.doDataflowEdit( + fileManager.getCurrentDataflow(), edit); + dialog.setVisible(false); + } catch (RuntimeException ex) { + logger.warn("Could not set list handling", ex); + JOptionPane.showMessageDialog(owner, + "Can't set list handling", + "An error occured when setting list handling: " + + ex.getMessage(), + JOptionPane.ERROR_MESSAGE); + } catch (EditException ex) { + logger.warn("Could not set list handling", ex); + JOptionPane.showMessageDialog(owner, + "Can't set list handling", + "An error occured when setting list handling: " + + ex.getMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + } + + private final class ResetAction extends AbstractAction { + private final IterationStrategyEditorControl strategyEditorControl; + + private ResetAction( + IterationStrategyEditorControl strategyEditorControl) { + super("Reset"); + this.strategyEditorControl = strategyEditorControl; + } + + public void actionPerformed(ActionEvent e) { + workingStack = IterationStrategyContextualView.copyIterationStrategyStack(originalStack); + strategyEditorControl + .setIterationStrategy(IterationStrategyContextualView.getIterationStrategy(workingStack)); + } + + } + + private final class CancelAction extends AbstractAction { + private final JDialog dialog; + + private CancelAction(JDialog dialog) { + super("Cancel"); + this.dialog = dialog; + } + + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + } + + } + +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualView.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualView.java new file mode 100644 index 0000000..369bea4 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualView.java
@@ -0,0 +1,231 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +/** + * + */ +package net.sf.taverna.t2.workbench.iterationstrategy.contextview; + +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JComponent; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workbench.iterationstrategy.editor.IterationStrategyTree; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workflowmodel.Processor; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategyStack; +import net.sf.taverna.t2.workflowmodel.processor.iteration.impl.IterationStrategyImpl; +import net.sf.taverna.t2.workflowmodel.processor.iteration.impl.IterationStrategyStackImpl; + +import org.apache.log4j.Logger; +import org.jdom.Content; +import org.jdom.Element; +import org.jdom.filter.ElementFilter; + +/** + * Contextual view of an {@link IterationStrategyStack}. + * + * @author Stian Soiland-Reyes + * + */ +public class IterationStrategyContextualView extends ContextualView { + + private static Logger logger = Logger + .getLogger(IterationStrategyContextualView.class); + + private EditManager editManager; + + private FileManager fileManager; + + private IterationStrategyStack iterationStack; + + private final Processor processor; + + private IterationStrategyTree strategyTree = new IterationStrategyTree(); + + static { + +// This should be enabled and modified for T2-822 +/* editManager.addObserver(new Observer<EditManagerEvent> () { + + private void examineEdit(Edit edit) { + if (edit instanceof ConnectDatalinkEdit) { + processConnectDatalinkEdit((ConnectDatalinkEdit) edit); + } + if (edit instanceof CompoundEdit) { + processCompoundEdit((CompoundEdit) edit); + } + } + + private void processConnectDatalinkEdit(ConnectDatalinkEdit edit) { + Datalink d = ((ConnectDatalinkEdit) edit).getSubject(); + EventHandlingInputPort sink = d.getSink(); + if (sink instanceof ProcessorInputPort) { + ProcessorInputPort pip = (ProcessorInputPort) sink; + Processor p = pip.getProcessor(); + final HelpEnabledDialog dialog = new IterationStrategyConfigurationDialog(null, p, copyIterationStrategyStack(p.getIterationStrategy())); + dialog.setVisible(true); + } + } + + private void processCompoundEdit(CompoundEdit edit) { + for (Edit e : edit.getChildEdits()) { + examineEdit(e); + } + } + + @Override + public void notify(Observable<EditManagerEvent> sender, + EditManagerEvent message) throws Exception { + if (!(message instanceof DataflowEditEvent)) { + return; + } + examineEdit(message.getEdit()); + }});*/ + } + + public IterationStrategyContextualView(Processor processor, EditManager editManager, FileManager fileManager) { + if (processor == null || processor.getIterationStrategy() == null) { + throw new NullPointerException( + "Iteration strategy stack can't be null"); + } + this.processor = processor; + this.editManager = editManager; + this.fileManager = fileManager; + refreshIterationStrategyStack(); + initView(); + } + + @Override + public Action getConfigureAction(final Frame owner) { + return new ConfigureIterationStrategyAction(owner); + } + + public Processor getProcessor() { + return processor; + } + + @Override + public void refreshView() { + refreshIterationStrategyStack(); + strategyTree.setIterationStrategy(getIterationStrategy(iterationStack)); + } + + public static IterationStrategyStack copyIterationStrategyStack( + IterationStrategyStack stack) { + Element asXML = ((IterationStrategyStackImpl)stack).asXML(); + stripEmptyElements(asXML); + IterationStrategyStackImpl copyStack = new IterationStrategyStackImpl(); + copyStack.configureFromElement(asXML); + if (copyStack.getStrategies().isEmpty()) { + copyStack.addStrategy(new IterationStrategyImpl()); + } + return copyStack; + } + + private static void stripEmptyElements(Element asXML) { + int childCount = asXML.getContent().size(); + int index = 0; + while (index < childCount) { + Content child = asXML.getContent(index); + if (child instanceof Element) { + Element childElement = (Element) child; + if (childElement.getName().equals("port")) { + index++; + } + else if (childElement.getDescendants(new ElementFilter("port")).hasNext()) { + stripEmptyElements(childElement); + index++; + } else { + asXML.removeContent(childElement); + childCount--; + } + } + } + } + + public static IterationStrategy getIterationStrategy(IterationStrategyStack iStack) { + List<? extends IterationStrategy> strategies = iStack + .getStrategies(); + if (strategies.isEmpty()) { + throw new IllegalStateException("Empty iteration stack"); + } + IterationStrategy strategy = strategies.get(0); + if (!(strategy instanceof IterationStrategyImpl)) { + throw new IllegalStateException( + "Can't edit unknown iteration strategy implementation " + + strategy); + } + return (IterationStrategyImpl) strategy; + } + + private void refreshIterationStrategyStack() { + IterationStrategyStack originalIterationStrategy = processor + .getIterationStrategy(); + if (!(originalIterationStrategy instanceof IterationStrategyStackImpl)) { + throw new IllegalStateException( + "Unknown iteration strategy implementation " + + originalIterationStrategy); + } + this.iterationStack = copyIterationStrategyStack((IterationStrategyStackImpl) originalIterationStrategy); + } + + @Override + public JComponent getMainFrame() { + refreshView(); + return strategyTree; + } + + @Override + public String getViewTitle() { + return "List handling"; + } + + private final class ConfigureIterationStrategyAction extends AbstractAction { + private final Frame owner; + + private ConfigureIterationStrategyAction(Frame owner) { + super("Configure"); + this.owner = owner; + } + + public void actionPerformed(ActionEvent e) { + final HelpEnabledDialog dialog = new IterationStrategyConfigurationDialog(owner, processor, iterationStack, editManager, fileManager); + dialog.setVisible(true); + refreshView(); + } + + + + } + + @Override + public int getPreferredPosition() { + return 200; + } +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualViewFactory.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualViewFactory.java new file mode 100644 index 0000000..a970239 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/contextview/IterationStrategyContextualViewFactory.java
@@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.iterationstrategy.contextview; + +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import net.sf.taverna.t2.workflowmodel.Processor; + +public class IterationStrategyContextualViewFactory implements + ContextualViewFactory<Processor> { + + private EditManager editManager; + private FileManager fileManager; + + public boolean canHandle(Object selection) { + return selection instanceof Processor; + } + + public List<ContextualView> getViews(Processor p) { + return Arrays.asList(new ContextualView[] {new IterationStrategyContextualView(p, editManager, fileManager)}); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyCellRenderer.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyCellRenderer.java new file mode 100644 index 0000000..4c31574 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyCellRenderer.java
@@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +/** + * + */ +package net.sf.taverna.t2.workbench.iterationstrategy.editor; + +import java.awt.Component; + +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; + +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.iterationstrategy.IterationStrategyIcons; +import net.sf.taverna.t2.workflowmodel.processor.iteration.CrossProduct; +import net.sf.taverna.t2.workflowmodel.processor.iteration.DotProduct; +import net.sf.taverna.t2.workflowmodel.processor.iteration.NamedInputPortNode; + +import org.apache.log4j.Logger; + +@SuppressWarnings("serial") +final class IterationStrategyCellRenderer extends DefaultTreeCellRenderer { + + @SuppressWarnings("unused") + private static Logger logger = Logger + .getLogger(IterationStrategyCellRenderer.class); + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + super.getTreeCellRendererComponent(tree, value, selected, expanded, + leaf, row, hasFocus); + if (value instanceof CrossProduct) { + setIcon(IterationStrategyIcons.joinIteratorIcon); + setText("Cross product"); + } else if (value instanceof DotProduct) { + setIcon(IterationStrategyIcons.lockStepIteratorIcon); + setText("Dot product"); + } else if (value instanceof NamedInputPortNode) { + setIcon(IterationStrategyIcons.leafnodeicon); + NamedInputPortNode namedInput = (NamedInputPortNode) value; + setText(namedInput.getPortName()); + } else { + setText("List handling"); + if (!leaf){ + if (expanded) { + setIcon(WorkbenchIcons.folderOpenIcon); + } else { + setIcon(WorkbenchIcons.folderClosedIcon); + } + } + } + return this; + } +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditor.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditor.java new file mode 100644 index 0000000..add5201 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditor.java
@@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.iterationstrategy.editor; + +import java.awt.GraphicsEnvironment; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +//import java.awt.image.BufferedImage; +import java.io.IOException; + +import javax.swing.DropMode; +import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.TransferHandler; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; +import net.sf.taverna.t2.workflowmodel.processor.iteration.AbstractIterationStrategyNode; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy; +import net.sf.taverna.t2.workflowmodel.processor.iteration.NamedInputPortNode; +import net.sf.taverna.t2.workflowmodel.processor.iteration.TerminalNode; + +import org.apache.log4j.Logger; + +@SuppressWarnings("serial") +public class IterationStrategyEditor extends IterationStrategyTree implements + UIComponentSPI { + + private static Logger logger = Logger + .getLogger(IterationStrategyEditor.class); + + //private BufferedImage imgGhost; // The 'drag image' + + // mouse was clicked + + public IterationStrategyEditor() { + super(); + // Make this a drag source + if (!GraphicsEnvironment.isHeadless()) { + this.setDragEnabled(true); + this.setDropMode(DropMode.ON_OR_INSERT); + this.setTransferHandler(new TreeTransferHandler()); + this.getSelectionModel().setSelectionMode( + TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); + expandTree(); + } + + // + } + + public IterationStrategyEditor(IterationStrategy theStrategy) { + this(); + setIterationStrategy(theStrategy); + } + + /** + * + * This code is freely adapted from code derived + * + */ + class TreeTransferHandler extends TransferHandler { + DataFlavor nodesFlavor; + DataFlavor[] flavors = new DataFlavor[1]; + + public TreeTransferHandler() { + getNodesFlavor(); + } + + private DataFlavor getNodesFlavor() { + if (nodesFlavor == null) { + try { + nodesFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + + ";class=" + AbstractIterationStrategyNode.class.getName(), + "AbstractIterationStrategyNode", + this.getClass().getClassLoader()); + flavors[0] = nodesFlavor; + } catch(Exception e) { + logger.error("Problem creating nodesFlavor:" + e); + } + } + return nodesFlavor; + } + + public boolean canImport(TransferHandler.TransferSupport support) { + if(!support.isDrop()) { + logger.error("isDrop not supported"); + return false; + } + + if(!support.isDataFlavorSupported(getNodesFlavor())) { + logger.info("Not correct flavor"); + return false; + } + // Do not allow a drop on the drag source selections. + JTree.DropLocation dl = + (JTree.DropLocation)support.getDropLocation(); + TreePath dest = dl.getPath(); + AbstractIterationStrategyNode destination = + (AbstractIterationStrategyNode)dest.getLastPathComponent(); + Transferable t = support.getTransferable(); + if (destination instanceof TerminalNode) { + return false; + } + try { + AbstractIterationStrategyNode node = (AbstractIterationStrategyNode) t.getTransferData(getNodesFlavor()); + if (node.isNodeDescendant(destination)) { + return false; + } + } catch (UnsupportedFlavorException e) { + return false; + } catch (IOException e) { + return false; + } +// JTree tree = (JTree) support.getComponent(); +// int dropRow = tree.getRowForPath(dl.getPath()); +// int selRow = tree.getLeadSelectionRow(); +// if (selRow == dropRow) { +// logger.info("Dragging to source"); +// return false; +// +// } + support.setShowDropLocation(true); + return true; + } + + protected Transferable createTransferable(JComponent c) { + JTree tree = (JTree)c; + TreePath[] paths = tree.getSelectionPaths(); + if(paths != null) { + AbstractIterationStrategyNode node = + (AbstractIterationStrategyNode)paths[0].getLastPathComponent(); + return new NodeTransferable(node); + } + return null; + } + + protected void exportDone(JComponent source, Transferable data, int action) { + } + + public int getSourceActions(JComponent c) { + return MOVE; + } + + public boolean importData(TransferHandler.TransferSupport support) { + if(!canImport(support)) { + logger.info("Cannot import"); + return false; + } + // Extract transfer data. + AbstractIterationStrategyNode node = null; + try { + Transferable t = support.getTransferable(); + node = (AbstractIterationStrategyNode) t.getTransferData(getNodesFlavor()); + } catch(UnsupportedFlavorException ufe) { + logger.error("UnsupportedFlavor", ufe); + } catch (Exception e) { + logger.error("Problem getting transfer data", e); + } + + // Get drop location info. + JTree.DropLocation dl = + (JTree.DropLocation)support.getDropLocation(); + int childIndex = dl.getChildIndex(); + TreePath dest = dl.getPath(); + AbstractIterationStrategyNode parent = + (AbstractIterationStrategyNode)dest.getLastPathComponent(); + int index = childIndex; + logger.info ("parent is a " + parent.getClass().getName()); + if (parent instanceof NamedInputPortNode) { + AbstractIterationStrategyNode sibling = parent; + parent = (AbstractIterationStrategyNode) sibling.getParent(); + index = parent.getIndex(sibling); + } else if (index == -1) { + index = parent.getChildCount(); + } + if (parent instanceof TerminalNode) { + if (parent.getChildCount() > 0) { + parent = (AbstractIterationStrategyNode) parent.getChildAt(0); + index = parent.getChildCount(); + } + + } + logger.info("parent is a " + parent.getClass().getName()); + + try { + // The parent insert removes from the oldParent + parent.insert(node, index++); + DefaultTreeModel model = IterationStrategyEditor.this + .getModel(); + refreshModel(); + } catch (IllegalStateException e) { + logger.error(e); + } catch (IllegalArgumentException e) { + logger.error(e); + } + return true; + } + + public String toString() { + return getClass().getName(); + } + + public class NodeTransferable implements Transferable { + AbstractIterationStrategyNode node; + + public NodeTransferable(AbstractIterationStrategyNode node) { + this.node = node; + } + + public AbstractIterationStrategyNode getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException { + if(!isDataFlavorSupported(flavor)) + throw new UnsupportedFlavorException(flavor); + return node; + } + + public DataFlavor[] getTransferDataFlavors() { + return flavors; + } + + public boolean isDataFlavorSupported(DataFlavor flavor) { + return getNodesFlavor().equals(flavor); + } + } + } +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditorControl.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditorControl.java new file mode 100644 index 0000000..c745283 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyEditorControl.java
@@ -0,0 +1,439 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +/** + * This file is a component of the Taverna project, + * and is licensed under the GNU LGPL. + * Copyright Tom Oinn, EMBL-EBI + */ +package net.sf.taverna.t2.workbench.iterationstrategy.editor; + +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.iterationstrategy.IterationStrategyIcons; +import net.sf.taverna.t2.workflowmodel.processor.iteration.CrossProduct; +import net.sf.taverna.t2.workflowmodel.processor.iteration.DotProduct; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategyNode; +import net.sf.taverna.t2.workflowmodel.processor.iteration.TerminalNode; + +import org.apache.log4j.Logger; + +/** + * A control panel for the iteration tree editor allowing the user to manipulate + * the tree, removing and adding nodes into the tree based on the context. + * + * @author Tom Oinn + * @author Stian Soiland-Reyes + * + */ +@SuppressWarnings("serial") +public class IterationStrategyEditorControl extends JPanel { + + protected static Set<IterationStrategyNode> descendentsOfNode( + IterationStrategyNode node) { + Set<IterationStrategyNode> descendants = new HashSet<IterationStrategyNode>(); + Set<IterationStrategyNode> nodesToVisit = new HashSet<IterationStrategyNode>(); + Set<IterationStrategyNode> visitedNodes = new HashSet<IterationStrategyNode>(); + + // Note: Not added to descendants + nodesToVisit.add(node); + while (!nodesToVisit.isEmpty()) { + // pick the first one + IterationStrategyNode visiting = nodesToVisit.iterator().next(); + visitedNodes.add(visiting); + nodesToVisit.remove(visiting); + + // Find new and interesting children + List<IterationStrategyNode> children = visiting.getChildren(); + Set<IterationStrategyNode> newNodes = new HashSet<IterationStrategyNode>( + children); + newNodes.removeAll(visitedNodes); + + descendants.addAll(newNodes); + nodesToVisit.addAll(newNodes); + } + return descendants; + } + + private static Logger logger = Logger + .getLogger(IterationStrategyEditorControl.class); + + private IterationStrategyNode selectedNode = null; + + private IterationStrategyTree tree; + + protected AddCrossAction addCross = new AddCrossAction(); + protected AddDotAction addDot = new AddDotAction(); + protected ChangeAction change = new ChangeAction(); + protected NormalizeAction normalize = new NormalizeAction(); + protected RemoveAction remove = new RemoveAction(); + protected MoveUpAction moveUp = new MoveUpAction(); + + //private static final int ICON_SIZE = 15; + + protected ImageIcon arrowUpIcon = WorkbenchIcons.upArrowIcon; + protected ImageIcon arrowDownIcon = WorkbenchIcons.downArrowIcon; + //protected ImageIcon arrowLeft = WorkbenchIcons.leftArrowIcon; + //protected ImageIcon arrowRight = WorkbenchIcons.rightArrowIcon; + protected ImageIcon normalizeIcon = WorkbenchIcons.normalizeIcon; + + private final IterationStrategy strategy; + + /** + * Create a new panel from the supplied iteration strategy + */ + public IterationStrategyEditorControl(IterationStrategy strategy) { + + this.strategy = strategy; + setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + + // Create the components + tree = new IterationStrategyEditor(strategy); + + JButton addCrossButton = new JButton(addCross); + addCrossButton.setHorizontalAlignment(SwingConstants.LEFT); + JButton addDotButton = new JButton(addDot); + addDotButton.setHorizontalAlignment(SwingConstants.LEFT); + JButton normalizeButton = new JButton(normalize); + normalizeButton.setHorizontalAlignment(SwingConstants.LEFT); + normalizeButton.setIcon(normalizeIcon); + JButton removeButton = new JButton(remove); + removeButton.setHorizontalAlignment(SwingConstants.LEFT); + JButton changeButton = new JButton(change); + changeButton.setHorizontalAlignment(SwingConstants.LEFT); + + JButton moveUpButton = new JButton(moveUp); + moveUpButton.setIcon(arrowUpIcon); + moveUpButton.setHorizontalAlignment(SwingConstants.LEFT); + + // Set the default enabled state to off on all buttons other than the + // normalizeButton + // one. + disableButtons(); + + // Create a layout with the tree on the right and the buttons in a grid + // layout on the left + JToolBar toolbar = new JToolBar(); + toolbar.setFloatable(false); + toolbar.setRollover(true); + // toolbar.setLayout(new GridLayout(2,2)); + toolbar.add(normalizeButton); + toolbar.add(addCrossButton); + toolbar.add(addDotButton); + toolbar.add(removeButton); + toolbar.add(changeButton); + toolbar.add(moveUpButton); + + toolbar.setAlignmentX(LEFT_ALIGNMENT); + + // Listen to tree selection events and enable buttons appropriately + tree.addTreeSelectionListener(new ButtonEnabler()); + + // Add components to the control panel + add(toolbar); + JScrollPane treePane = new JScrollPane(tree); + //treePane.setPreferredSize(new Dimension(0, 0)); + add(treePane); + } + + public void setIterationStrategy(IterationStrategy iterationStrategy) { + tree.setIterationStrategy(iterationStrategy); + disableButtons(); + selectNode(null); + } + + private void disableButtons() { + remove.setEnabled(false); + addCross.setEnabled(false); + addDot.setEnabled(false); + change.setEnabled(false); + } + + private IterationStrategyNode findRoot() { + IterationStrategyNode root = (IterationStrategyNode) tree.getModel() + .getRoot(); + if (root.getChildCount() > 0) { + return root.getChildAt(0); + } + return root; + } + + protected void selectNode(TreeNode newNode) { + DefaultTreeModel model = tree.getModel(); + if (newNode == null) { + newNode = (TreeNode) model.getRoot(); + } + TreeNode[] pathToRoot = model.getPathToRoot(newNode); + tree.setSelectionPath(new TreePath(pathToRoot)); + } + + /** + * Add a cross product node as a child of the selected node + */ + protected class AddCrossAction extends AbstractAction { + + public AddCrossAction() { + super("Add Cross", IterationStrategyIcons.joinIteratorIcon); + } + + public void actionPerformed(ActionEvent e) { + CrossProduct newNode = new CrossProduct(); + newNode.setParent(selectedNode); + tree.refreshModel(); + } + } + + /** + * Add a dot product node as a child of the selected node + * + * @author Stian Soiland-Reyes + * + */ + protected class AddDotAction extends AbstractAction { + + public AddDotAction() { + super("Add Dot", IterationStrategyIcons.lockStepIteratorIcon); + } + + public void actionPerformed(ActionEvent e) { + DotProduct newNode = new DotProduct(); + newNode.setParent(selectedNode); + tree.refreshModel(); + } + } + + protected class ButtonEnabler implements TreeSelectionListener { + public void valueChanged(TreeSelectionEvent e) { + TreePath selectedPath = e.getPath(); + IterationStrategyNode selectedObject = (IterationStrategyNode) selectedPath + .getLastPathComponent(); + selectedNode = selectedObject; + if (selectedObject instanceof CrossProduct + || selectedObject instanceof DotProduct) { + if ((selectedObject.getParent() == null) || (selectedObject.getParent() instanceof TerminalNode)) { + remove.setEnabled(false); + } else { + remove.setEnabled(true); + } + if (selectedObject instanceof CrossProduct) { + change.putValue(Action.NAME, "Change to Dot Product"); + change.putValue(Action.SMALL_ICON, + IterationStrategyIcons.lockStepIteratorIcon); + } else { + change.putValue(Action.NAME, "Change to Cross Product"); + change.putValue(Action.SMALL_ICON, + IterationStrategyIcons.joinIteratorIcon); + } + addCross.setEnabled(true); + addDot.setEnabled(true); + change.setEnabled(true); + } else { + // Top- or leaf node + remove.setEnabled(false); + addCross.setEnabled(false); + addDot.setEnabled(false); + change.setEnabled(false); + } + } + } + + /** + * Add a cross product node as a child of the selected node + */ + protected class ChangeAction extends AbstractAction { + + public ChangeAction() { + super("Switch to...", IterationStrategyIcons.joinIteratorIcon); + } + + public void actionPerformed(ActionEvent e) { + IterationStrategyNode newNode; + if (selectedNode instanceof CrossProduct) { + newNode = new DotProduct(); + } else { + newNode = new CrossProduct(); + } + + List<IterationStrategyNode> children = new ArrayList<IterationStrategyNode>( + selectedNode.getChildren()); + for (IterationStrategyNode child : children) { + child.setParent(newNode); + } + + DefaultTreeModel model = tree.getModel(); + if (selectedNode.getParent() == null) { + model.setRoot(newNode); + tree.refreshModel(); + newNode.setParent(null); + } else { + IterationStrategyNode parent = selectedNode.getParent(); + int index = parent.getIndex(selectedNode); + selectedNode.setParent(null); + parent.insert(newNode, index); + tree.refreshModel(); + } + + selectNode(newNode); + } + + } + + /** + * Normalize the tree when the button is pressed + * + */ + protected class NormalizeAction extends AbstractAction { + public NormalizeAction() { + super("Normalize", normalizeIcon); + } + + public void actionPerformed(ActionEvent e) { + strategy.normalize(); + // Expand all the nodes in the tree + //DefaultTreeModel model = tree.getModel(); + tree.refreshModel(); + } + } + + /** + * Remove the selected node, moving any descendant leaf nodes to the parent + * to prevent them getting lost + */ + protected class RemoveAction extends AbstractAction { + public RemoveAction() { + super("Remove node", WorkbenchIcons.deleteIcon); + } + + public void actionPerformed(ActionEvent e) { + IterationStrategyNode nodeToBeRemoved = selectedNode; + + //DefaultTreeModel model = tree.getModel(); + + // Now removeButton the candidate nodes from their parents and + // put them back into the root node + IterationStrategyNode root = findRoot(); + if (root == selectedNode) { + return; + } + IterationStrategyNode oldParent = nodeToBeRemoved.getParent(); + + for (IterationStrategyNode nodeToMove : descendentsOfNode(nodeToBeRemoved)) { + nodeToMove.setParent(oldParent); + } + nodeToBeRemoved.setParent(null); + tree.refreshModel(); + // Disable the various buttons, as the current selection + // is now invalid. + remove.setEnabled(false); + addCross.setEnabled(false); + addDot.setEnabled(false); + change.setEnabled(false); + selectNode(oldParent); + } + } + + protected class MoveUpAction extends AbstractAction { + + public MoveUpAction() { + super("Move up", arrowUpIcon); + } + + public void actionPerformed(ActionEvent e) { + //DefaultTreeModel model = tree.getModel(); + + IterationStrategyNode aboveNode = aboveSelectedNode(); + if ((aboveNode == null) || ((aboveNode instanceof TerminalNode) && (aboveNode.getChildCount() > 0))) { + logger.warn("Can't move above top"); + return; + } + IterationStrategyNode selectedParent = selectedNode.getParent(); + IterationStrategyNode aboveParent = aboveNode.getParent(); + if (selectedParent != null && selectedParent.equals(aboveParent)) { + // Siblings + int aboveChildIndex = selectedParent.getIndex(aboveNode); + selectedParent.insert(selectedNode, aboveChildIndex); + tree.refreshModel(); + selectNode(selectedNode); + } else if (aboveNode.equals(selectedParent)) { + if (aboveParent instanceof TerminalNode + && selectedNode.getAllowsChildren()) { + aboveNode.setParent(selectedNode); + selectedNode.setParent(aboveParent); + tree.refreshModel(); + selectNode(selectedNode); + } else if (!(aboveParent instanceof TerminalNode)){ + int aboveChildIndex = aboveParent.getIndex(aboveNode); + aboveParent.insert(selectedNode, aboveChildIndex); + tree.refreshModel(); + selectNode(selectedNode); + } + } else { + + } + + } + + } + + protected IterationStrategyNode belowSelectedNode() { + return offsetFromSelectedNode(1); + } + + protected IterationStrategyNode offsetFromSelectedNode(int offset) { + int currentRow = tree.getRowForPath(tree.getSelectionPath()); + int offsetRow = currentRow + offset; + TreePath offsetPath = tree.getPathForRow(offsetRow); + if (offsetPath == null) { + return null; + } + IterationStrategyNode offsetNode = (IterationStrategyNode) offsetPath + .getLastPathComponent(); + if (offsetNode == tree.getModel().getRoot()) { + return null; + } + return offsetNode; + } + + protected IterationStrategyNode aboveSelectedNode() { + return offsetFromSelectedNode(-1); + } + +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyTree.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyTree.java new file mode 100644 index 0000000..c4665c6 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/IterationStrategyTree.java
@@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.iterationstrategy.editor; + +import java.util.Enumeration; + +import javax.swing.ImageIcon; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +import net.sf.taverna.t2.workbench.iterationstrategy.IterationStrategyIcons; +import net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI; +import net.sf.taverna.t2.workflowmodel.processor.iteration.IterationStrategy; + +@SuppressWarnings("serial") +public class IterationStrategyTree extends JTree implements UIComponentSPI { + + private IterationStrategy strategy = null; + + public IterationStrategyTree() { + super(); + setCellRenderer(new IterationStrategyCellRenderer()); + } + + public ImageIcon getIcon() { + return IterationStrategyIcons.leafnodeicon; + } + + public void onDisplay() { + // TODO Auto-generated method stub + + } + + public void onDispose() { + this.strategy = null; + setModel(null); + } + + public synchronized void setIterationStrategy( + IterationStrategy theStrategy) { + if (theStrategy != this.strategy) { + this.strategy = theStrategy; + TreeNode terminal = theStrategy.getTerminalNode(); + setModel(new DefaultTreeModel(terminal)); + this.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + expandTree(); + revalidate(); + } + } + + protected synchronized void refreshModel() { + this.getModel().nodeStructureChanged(strategy.getTerminalNode()); + expandTree(); + } + + @Override + public DefaultTreeModel getModel() { + return (DefaultTreeModel) super.getModel(); + } + + @Override + public void setModel(TreeModel newModel) { + if (newModel != null && !(newModel instanceof DefaultTreeModel)) { + throw new IllegalArgumentException( + "Model must be a DefaultTreeModel"); + } + super.setModel(newModel); + } + + protected void expandTree() { + DefaultMutableTreeNode root = + (DefaultMutableTreeNode)this.getModel().getRoot(); + Enumeration e = root.breadthFirstEnumeration(); + while(e.hasMoreElements()) { + DefaultMutableTreeNode node = + (DefaultMutableTreeNode)e.nextElement(); + if(node.isLeaf()) continue; + int row = this.getRowForPath(new TreePath(node.getPath())); + this.expandRow(row); + } + } + +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/menu/IterationStrategyConfigureMenuAction.java b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/menu/IterationStrategyConfigureMenuAction.java new file mode 100644 index 0000000..f8c7f73 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/java/net/sf/taverna/t2/workbench/iterationstrategy/menu/IterationStrategyConfigureMenuAction.java
@@ -0,0 +1,65 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.workbench.iterationstrategy.menu; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyConfigurationDialog; +import net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyContextualView; +import net.sf.taverna.t2.workflowmodel.Processor; + +public class IterationStrategyConfigureMenuAction extends AbstractContextualMenuAction { + + + + public static final URI configureRunningSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configureRunning"); + + private static final URI ITERATION_STRATEGY_CONFIGURE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/iterationStrategyConfigure"); + + public IterationStrategyConfigureMenuAction() { + super(configureRunningSection, 40, ITERATION_STRATEGY_CONFIGURE_URI); + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction("List handling...") { + public void actionPerformed(ActionEvent e) { + Processor p = (Processor) getContextualSelection().getSelection(); + final HelpEnabledDialog dialog = new IterationStrategyConfigurationDialog(null, p, IterationStrategyContextualView.copyIterationStrategyStack(p.getIterationStrategy())); + dialog.setVisible(true); + } + }; + } + + public boolean isEnabled() { + return super.isEnabled() && (getContextualSelection().getSelection() instanceof Processor); + } + +}
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..6f25f4e --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.iterationstrategy.menu.IterationStrategyConfigureMenuAction \ No newline at end of file
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..a6a27b0 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyContextualViewFactory
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context-osgi.xml b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context-osgi.xml new file mode 100644 index 0000000..e3db399 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context-osgi.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="IterationStrategyContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + +</beans:beans>
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context.xml b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context.xml new file mode 100644 index 0000000..f0bc464 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/resources/META-INF/spring/iteration-strategy-ui-context.xml
@@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="IterationStrategyContextualViewFactory" class="net.sf.taverna.t2.workbench.iterationstrategy.contextview.IterationStrategyContextualViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + </bean> + +</beans>
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/crossproducticon.png b/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/crossproducticon.png new file mode 100644 index 0000000..4627fc8 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/crossproducticon.png Binary files differ
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/dotproducticon.png b/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/dotproducticon.png new file mode 100644 index 0000000..c269883 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/dotproducticon.png Binary files differ
diff --git a/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/leafnodeicon.png b/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/leafnodeicon.png new file mode 100644 index 0000000..36808c6 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/main/resources/net/sf/taverna/t2/workbench/iterationstrategy/icons/leafnodeicon.png Binary files differ
diff --git a/taverna-workbench-iteration-strategy-ui/src/test/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/RunIterationStrategyEditor.java b/taverna-workbench-iteration-strategy-ui/src/test/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/RunIterationStrategyEditor.java new file mode 100644 index 0000000..1862233 --- /dev/null +++ b/taverna-workbench-iteration-strategy-ui/src/test/java/net/sf/taverna/t2/workbench/iterationstrategy/editor/RunIterationStrategyEditor.java
@@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.iterationstrategy.editor; + +import javax.swing.JFrame; + +import net.sf.taverna.t2.workflowmodel.processor.iteration.NamedInputPortNode; +import net.sf.taverna.t2.workflowmodel.processor.iteration.impl.IterationStrategyImpl; + +public class RunIterationStrategyEditor { + + /** + * @param args + */ + public static void main(String[] args) { + IterationStrategyImpl iterationStrategyImpl = new IterationStrategyImpl(); + NamedInputPortNode fishPort = new NamedInputPortNode("fish", 2); + NamedInputPortNode otherPort = new NamedInputPortNode("other", 0); + NamedInputPortNode soupPort = new NamedInputPortNode("soup", 1); + iterationStrategyImpl.addInput(fishPort); + iterationStrategyImpl.addInput(soupPort); + iterationStrategyImpl.addInput(otherPort); + + iterationStrategyImpl.connectDefault(otherPort); + iterationStrategyImpl.connectDefault(fishPort); + iterationStrategyImpl.connectDefault(soupPort); + + IterationStrategyEditorControl editorControl = new IterationStrategyEditorControl(iterationStrategyImpl); + + JFrame frame = new JFrame("List handling editor"); + frame.add(editorControl); + frame.setSize(500,400); + frame.setVisible(true); + + + } + +}
diff --git a/taverna-workbench-loop-ui/pom.xml b/taverna-workbench-loop-ui/pom.xml new file mode 100644 index 0000000..f639bea --- /dev/null +++ b/taverna-workbench-loop-ui/pom.xml
@@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-exts</artifactId> + <version>2.0-SNAPSHOT</version> + + </parent> + <groupId>net.sf.taverna.t2.ui-exts</groupId> + <artifactId>loop-ui</artifactId> + <packaging>bundle</packaging> + <name>Loop layer contextual view</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + + + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>beanshell-activity-ui</artifactId> + <version>${t2.ui.activities.version}</version> + </dependency> + + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>edits-impl</artifactId> + <version>${t2.ui.impl.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>file-impl</artifactId> + <version>${t2.ui.impl.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>contextual-views-impl</artifactId> + <version>${t2.ui.impl.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>selection-impl</artifactId> + <version>${t2.ui.impl.version}</version> + <scope>test</scope> + </dependency> + + </dependencies> +</project> \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/ActivityGenerator.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/ActivityGenerator.java new file mode 100644 index 0000000..9bd68e8 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/ActivityGenerator.java
@@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.Map.Entry; + +import net.sf.taverna.t2.workbench.loop.comparisons.Comparison; +import net.sf.taverna.t2.workbench.loop.comparisons.EqualTo; +import net.sf.taverna.t2.workbench.loop.comparisons.IsGreaterThan; +import net.sf.taverna.t2.workbench.loop.comparisons.IsLessThan; +import net.sf.taverna.t2.workbench.loop.comparisons.Matches; +import net.sf.taverna.t2.workbench.loop.comparisons.NotEqualTo; +import net.sf.taverna.t2.workbench.loop.comparisons.NotMatches; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.InputActivityPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class ActivityGenerator { + + private static final String LOOP_PORT = "loop"; + + private static final String SCRIPT = "script"; + + public static URI BEANSHELL_ACTIVITY = URI + .create("http://ns.taverna.org.uk/2010/activity/beanshell"); + + public static URI BEANSHELL_CONFIG = BEANSHELL_ACTIVITY.resolve("#Config"); + + + public static final double DEFAULT_DELAY_S = 0.2; + public static final String COMPARE_PORT = "comparePort"; + public static final String COMPARISON = "comparison"; + public static final String CUSTOM_COMPARISON = "custom"; + public static final String COMPARE_VALUE = "compareValue"; + public static final String IS_FEED_BACK = "isFeedBack"; + public static final String DELAY = "delay"; + + private static Logger logger = Logger.getLogger(ActivityGenerator.class); + private final ObjectNode loopProperties; + private final Processor processorToCompare; + private static Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public ActivityGenerator(ObjectNode configuration, + Processor processorToCompare) { + this.loopProperties = configuration; + this.processorToCompare = processorToCompare; + } + + protected Activity generateActivity() { + Activity beanshell = new Activity(); + beanshell.setType(BEANSHELL_ACTIVITY); + Configuration config = generateBeanshellConfig(beanshell); + // TODO: Where to put the config? + return beanshell; + } + + private Configuration generateBeanshellConfig(Activity beanshell) { + Configuration config = scufl2Tools.createConfigurationFor(beanshell, BEANSHELL_CONFIG); + generateInputPorts(beanshell); + generateOutputPorts(beanshell); + config.getJsonAsObjectNode().put(SCRIPT, generateScript()); + return config; + } + + protected static List<Comparison> comparisons = Arrays.asList( + new EqualTo(), new NotEqualTo(), new Matches(), new NotMatches(), + new IsGreaterThan(), new IsLessThan()); + + protected static Comparison getComparisonById(String id) { + if (id == null || id.isEmpty()) { + return comparisons.get(0); + } + for (Comparison potentialComparison : comparisons) { + if (potentialComparison.getId().equals(id)) { + return potentialComparison; + } + } + return null; + } + + @SuppressWarnings("boxing") + private String generateScript() { + Map<String, String> replacements = new HashMap<String, String>(); + replacements.put("${loopPort}", LOOP_PORT); + replacements.put("${port}", loopProperties.findValue(COMPARE_PORT).asText()); + replacements.put("${value}", beanshellString(loopProperties + .findValue(COMPARE_VALUE).asText())); + + + // as seconds + Double delay = loopProperties.findPath(DELAY).asDouble(DEFAULT_DELAY_S); + // as milliseconds + delay = Math.max(0.0, delay) * 1000; + // as integer (for Thread.sleep) + replacements.put("${delay}", Integer.toString(delay.intValue())); + + String template = getComparisonById( + loopProperties.findValue(COMPARISON).asText()).getScriptTemplate(); + + if (delay > 0.0) { + template += "\nif (\"true\".matches(${loopPort})) {\n"; + template += " Thread.sleep(${delay});\n"; + template += "}"; + } + + String script = template; + for (Entry<String, String> mapping : replacements.entrySet()) { + script = script.replace(mapping.getKey(), mapping.getValue()); + } + return script; + } + + private String beanshellString(String value) { + value = value.replace("\\", "\\\\"); + value = value.replace("\n", "\\n"); + value = value.replace("\"", "\\\""); + return '"' + value + '"'; + } + + private void generateInputPorts(Activity beanshell) { + if (processorToCompare == null) { + return; + } + for (OutputProcessorPort procOut : processorToCompare.getOutputPorts()) { + // Any of the outputs are available to the script, giving + // a custom script that compares multiple outputs a better + // starting point. + String portName = procOut.getName(); + if (portName.equals(loopProperties.findValue(COMPARE_PORT).asText()) || + (loopProperties.findValue(IS_FEED_BACK).asBoolean())) { + InputActivityPort input = new InputActivityPort(beanshell, portName); + input.setDepth(procOut.getDepth()); + input.setParent(beanshell); + } + } + } + + private void generateOutputPorts(Activity beanshell) { + OutputActivityPort loopPort = new OutputActivityPort(beanshell, LOOP_PORT); + loopPort.setDepth(0); + loopPort.setGranularDepth(0); + if (processorToCompare == null) { + return; + } + if (! loopProperties.findValue(IS_FEED_BACK).asBoolean()) { + return; + } + for (InputProcessorPort procIn : processorToCompare.getInputPorts()) { + String portName = procIn.getName(); + if (processorToCompare.getOutputPorts().containsName(portName)) { + OutputActivityPort actOut = new OutputActivityPort(beanshell, portName); + actOut.setDepth(procIn.getDepth()); + actOut.setGranularDepth(procIn.getDepth()); + } + } + } +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/AddLoopFactory.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/AddLoopFactory.java new file mode 100644 index 0000000..f23650c --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/AddLoopFactory.java
@@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.event.ActionEvent; +import java.net.URI; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.workbench.MainWindow; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI; + +import org.apache.log4j.Logger; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Processor; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class AddLoopFactory implements AddLayerFactorySPI { + + private static final URI LOOP_TYPE = URI.create("http://ns.taverna.org.uk/2010/scufl2/taverna/dispatchlayer/Loop"); + + + private static Logger logger = Logger.getLogger(AddLoopFactory.class); + private static final JsonNodeFactory JSON_NODE_FACTORY = JsonNodeFactory.instance; + private static Scufl2Tools scufl2Tools = new Scufl2Tools(); + + private EditManager editManager; + private FileManager fileManager; + private SelectionManager selectionManager; + private ApplicationConfiguration applicationConfig; + + public boolean canAddLayerFor(Processor processor) { + return findLoopLayer(processor) == null; + } + + + public ObjectNode findLoopLayer(Processor processor) { + List<Configuration> configs = scufl2Tools.configurationsFor(processor, selectionManager.getSelectedProfile()); + for (Configuration config : configs) { + if (config.getJson().has("loop")) { + return (ObjectNode) config.getJson().get("loop"); + } + } + return null; + } + + @SuppressWarnings("serial") + public Action getAddLayerActionFor(final Processor processor) { + return new AbstractAction("Add looping") { + + public void actionPerformed(ActionEvent e) { + ObjectNode loopLayer = findLoopLayer(processor); + if (loopLayer == null) { + loopLayer = JSON_NODE_FACTORY.objectNode(); + } + // Pop up the configure loop dialog + LoopConfigureAction loopConfigureAction = new LoopConfigureAction( + MainWindow.getMainWindow(), null, processor, loopLayer, + selectionManager.getSelectedProfile(), editManager, + fileManager, getApplicationConfig()); + loopConfigureAction.actionPerformed(e); + } + }; + } + + @Override + public boolean canCreateLayerClass(URI dispatchLayerType) { + return dispatchLayerType.equals(LOOP_TYPE); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + + public SelectionManager getSelectionManager() { + return selectionManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + + public ApplicationConfiguration getApplicationConfig() { + return applicationConfig; + } + + + public void setApplicationConfig(ApplicationConfiguration applicationConfig) { + this.applicationConfig = applicationConfig; + } + +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopAddMenuAction.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopAddMenuAction.java new file mode 100644 index 0000000..e7b6e87 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopAddMenuAction.java
@@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.core.Processor; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; + +public class LoopAddMenuAction extends AbstractContextualMenuAction { + + public static final URI configureRunningSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configureRunning"); + + private static final URI LOOP_ADD_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/loopAdd"); + + private static final String LOOP_ADD = "Loop add"; + + public LoopAddMenuAction() { + super(configureRunningSection, 20, LOOP_ADD_URI); + } + + private AddLoopFactory addLoopFactory; + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction("Looping...") { + public void actionPerformed(ActionEvent e) { + //Loop loopLayer = null; + Processor p = (Processor) getContextualSelection().getSelection(); + addLoopFactory.getAddLayerActionFor(p).actionPerformed(e); + //LoopConfigureMenuAction.configureLoopLayer(p, e); // Configuration dialog pop up is now done from getAddLayerActionFor() + } + }; + } + + public boolean isEnabled() { + Object selection = getContextualSelection().getSelection(); + return (super.isEnabled() && (selection instanceof Processor) && (LoopConfigureMenuAction.getLoopLayer((Processor)selection) == null)); + } + + public void setAddLoopFactory(AddLoopFactory addLoopFactory) { + this.addLoopFactory = addLoopFactory; + } + + + +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigurationPanel.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigurationPanel.java new file mode 100644 index 0000000..88efb1a --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigurationPanel.java
@@ -0,0 +1,588 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import javax.swing.AbstractAction; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.activities.beanshell.views.BeanshellConfigurationPanel; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workbench.loop.comparisons.Comparison; +import net.sf.taverna.t2.workbench.ui.Utils; + +import org.apache.log4j.Logger; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * UI for {@link LoopConfiguration} + * + * @author Stian Soiland-Reyes + * + */ +@SuppressWarnings("serial") +public class LoopConfigurationPanel extends JPanel { + + private static final String CONDITION_ACTIVITY = "conditionActivity"; + private static final String DEFAULT_DELAY_S = "0.5"; + protected ObjectNode configuration; + + private static final Scufl2Tools scufl2tools = new Scufl2Tools(); + private ApplicationConfiguration applicationConfig; + + + protected final Processor processor; + + protected JPanel headerPanel = new JPanel(); + protected JPanel optionsPanel = new JPanel(); + protected JPanel configPanel = new JPanel(); + protected JPanel customPanel = new JPanel(); + + protected JLabel valueTypeLabel = new JLabel("the string"); + + protected JTextField valueField = new JTextField("", 15); + + protected JLabel delayLabel = new JLabel("adding a delay of "); + protected JTextField delayField = new JTextField( + Double.toString(ActivityGenerator.DEFAULT_DELAY_S), 4); + protected JLabel secondsLabel = new JLabel(" seconds between the loops."); + + private JComboBox<String> portCombo; + private JComboBox<Comparison> comparisonCombo; + private JButton customizeButton; + + protected ObjectNode loopLayer; + private Object Comparison; + private Activity originalCondition = null; + private Profile profile; + + public LoopConfigurationPanel(Processor processor, ObjectNode loopLayer, + Profile profile, ApplicationConfiguration applicationConfig) { + this.processor = processor; + this.loopLayer = loopLayer; + this.profile = profile; + this.applicationConfig = applicationConfig; + this.setBorder(new EmptyBorder(10,10,10,10)); + initialise(); + setConfiguration(loopLayer); + } + + public ObjectNode getConfiguration() { + uiToConfig(); + return loopLayer.deepCopy(); + } + + private static Logger logger = Logger + .getLogger(LoopConfigurationPanel.class); + + protected void uiToConfig() { + String comparisonStr = configuration.path(ActivityGenerator.COMPARISON).asText(); + if (comparisonStr.isEmpty()) { + comparisonStr = ActivityGenerator.CUSTOM_COMPARISON; + } + if (comparisonStr.equals(ActivityGenerator.CUSTOM_COMPARISON) + && ! configuration.path(CONDITION_ACTIVITY).asText().isEmpty()) { + // Ignore values + } else { + configuration.put("runFirst", true); + if (portCombo.getSelectedItem() == null) { + // unconfigured port + configuration.remove(ActivityGenerator.COMPARE_PORT); + configuration.putNull(CONDITION_ACTIVITY); + return; + } else { + configuration.put(ActivityGenerator.COMPARE_PORT, + ((String) portCombo.getSelectedItem())); + } + + Comparison comparison = (Comparison) comparisonCombo + .getSelectedItem(); + if (comparison == null) { + configuration.remove(ActivityGenerator.COMPARISON); + configuration.putNull(CONDITION_ACTIVITY); + return; + } else { + configuration + .put(ActivityGenerator.COMPARISON, comparison.getId()); + } + configuration.put(ActivityGenerator.COMPARE_VALUE, valueField + .getText()); + configuration.put(ActivityGenerator.DELAY, Double.parseDouble(delayField.getText())); + configuration.put(ActivityGenerator.IS_FEED_BACK, feedBackCheck.isSelected()); + + // Generate activity + ActivityGenerator activityGenerator = new ActivityGenerator( + configuration, processor); + configuration.put(CONDITION_ACTIVITY, activityGenerator.generateActivity().getName()); + } + } + + public class ResetAction extends AbstractAction { + public ResetAction() { + super("Clear"); + } + + public void actionPerformed(ActionEvent e) { + configuration.putNull(CONDITION_ACTIVITY); + configToUi(); + } + } + + private final class CustomizeAction implements ActionListener { + + +// public CustomizeAction() { +// super(); +// //putValue(NAME, "Customise loop condition"); +// } + + public void actionPerformed(ActionEvent e) { + uiToConfig(); + + String conditionName = configuration.path(CONDITION_ACTIVITY).asText(); + + Activity condition = profile.getActivities().getByName(conditionName); + if (condition == null) { + condition = new Activity(); + profile.getActivities().add(condition); + configuration.put(CONDITION_ACTIVITY, condition.getName()); + condition.setType(ActivityGenerator.BEANSHELL_ACTIVITY); + Configuration config = scufl2tools.createConfigurationFor(condition, ActivityGenerator.BEANSHELL_CONFIG); + } else if (!(condition.getType().equals(ActivityGenerator.BEANSHELL_ACTIVITY))) { + logger.warn("Can't configure unsupported loop condition of service type " + + condition.getType()); + return; + } + + Frame owner = Utils.getParentFrame(LoopConfigurationPanel.this); + + + final BeanshellConfigurationPanel beanshellConfigView = new BeanshellConfigurationPanel( + condition, applicationConfig); + + final JDialog dialog = new HelpEnabledDialog(owner, "Customize looping", true); + dialog.setLayout(new BorderLayout()); + dialog.add(beanshellConfigView, BorderLayout.NORTH); + dialog.setSize(600, 600); + JPanel buttonPanel = new JPanel(); + + buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + + JButton applyButton = new JButton(new AbstractAction() { + + public void actionPerformed(ActionEvent e) { + if (beanshellConfigView.isConfigurationChanged()) { + beanshellConfigView.noteConfiguration(); +// beanshellActivity.configure(beanshellConfigView +// .getConfiguration()); +// configuration.setCondition(beanshellActivity); + Configuration config = beanshellConfigView.getConfiguration(); + // TODO: Do we need to store this somehow? + configuration.put( + ActivityGenerator.COMPARISON, + ActivityGenerator.CUSTOM_COMPARISON); + } + dialog.setVisible(false); + configToUi(); + } + + }); + applyButton.setText("Apply"); + + buttonPanel.add(applyButton); + JButton closeButton = new JButton(new AbstractAction() { + + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + } + }); + closeButton.setText("Cancel"); + buttonPanel.add(closeButton); + dialog.add(buttonPanel, BorderLayout.SOUTH); + dialog.setLocationRelativeTo(customizeButton); + dialog.setVisible(true); + + } + } + + public void setConfiguration(ObjectNode configuration) { + this.configuration = configuration.deepCopy(); + configToUi(); + } + + protected void configToUi() { + + + String comparisonId; + + if (configuration.has(ActivityGenerator.COMPARISON)) { + comparisonId = configuration.get(ActivityGenerator.COMPARISON) + .asText(); + } else { + comparisonId = ActivityGenerator.CUSTOM_COMPARISON; + } + + if (comparisonId.equals(ActivityGenerator.CUSTOM_COMPARISON) + && configuration.has("conditionalActivity")) { + configPanel.setVisible(false); + customPanel.setVisible(true); + } else { + configPanel.setVisible(true); + customPanel.setVisible(false); + } + + portCombo.setSelectedItem(configuration.get(ActivityGenerator.COMPARE_PORT).asText()); + if (portCombo.getSelectedIndex() == -1 + && portCombo.getModel().getSize() > 0) { + portCombo.setSelectedIndex(0); + } + + Comparison comparison = ActivityGenerator + .getComparisonById(comparisonId); + comparisonCombo.setSelectedItem(comparison); + if (comparisonCombo.getSelectedIndex() == -1 + && comparisonCombo.getModel().getSize() > 0) { + comparisonCombo.setSelectedIndex(0); + } + + valueField.setText(configuration.get(ActivityGenerator.COMPARE_VALUE).asText()); + + if (configuration.has(ActivityGenerator.DELAY)) { + delayField.setText(configuration.get(ActivityGenerator.DELAY).asText()); + } else { + delayField.setText(DEFAULT_DELAY_S); + } + + feedBackCheck.setSelected(configuration.get(ActivityGenerator.IS_FEED_BACK).asBoolean()); + updateFeedbackHelp(); + } + + private void initialise() { + removeAll(); + setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.FIRST_LINE_START; + gbc.gridx = 0; + gbc.weightx = 0.1; + + makeHeader(); + add(headerPanel, gbc); + + makeConfigPanel(); + gbc.weighty = 0.1; + gbc.anchor = GridBagConstraints.CENTER; + gbc.fill = GridBagConstraints.BOTH; + add(configPanel, gbc); + + makeCustomPanel(); + add(customPanel, gbc); + + makeOptions(); + add(optionsPanel, gbc); + } + + protected void makeCustomPanel() { + customPanel.removeAll(); + customPanel.setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.LINE_START; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.HORIZONTAL; + + JLabel helpLabel = new JLabel( + "<html><body>" + + "The service <strong>" + processor.getName() + "</strong> will be " + + "invoked repeatedly as " + + "long as the <em>customized loop condition service</em> returns a string equal " + + "to <strong>\"true\"</strong> on its output port <code>loop</code>." +// + "<br><br>" +// + "Input ports of the condition service will be populated with values from " +// + "the <em>corresponding output ports</em> of the main service invocation " +// + "(as long as they are also " +// + "<strong>connected</strong> in the containing workflow)." +// + "<br><br> " +// +// + "Any <em>matching " +// + "output ports</em> from the condition service will provide the corresponding " +// + "<em>inputs</em> to the main service while looping. You will need to connect " +// + "the <em>initial inputs</em> in the containing workflow." + + "</body></html>"); + customPanel.add(helpLabel, gbc); + + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.NONE; + gbc.gridx = 0; + gbc.gridy++; + gbc.gridwidth = 1; + gbc.anchor = GridBagConstraints.EAST; + JPanel customiseButtonPanel = new JPanel(new FlowLayout()); + customiseButtonPanel.setBorder(new EmptyBorder(10,0,0,0)); + customizeButton = new JButton("Customize loop condition"); + customizeButton.addActionListener(new CustomizeAction()); + customiseButtonPanel.add(customizeButton); + customiseButtonPanel.add(new JButton(new ResetAction())); + customPanel.add(customiseButtonPanel, gbc); + + } + + protected void makeConfigPanel() { + configPanel.removeAll(); + configPanel.setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.LINE_START; + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 4; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.HORIZONTAL; + JLabel invokedRepeatedlyLabel = new JLabel( + + "<html><body>The service <strong>" + processor.getName() + "</strong> " + + "will be invoked repeatedly <em>until</em> its output port</body></html>"); + invokedRepeatedlyLabel.setBorder(new EmptyBorder(10,0,10,0)); // give some top and bottom border to the label + configPanel.add(invokedRepeatedlyLabel, gbc); + gbc.ipadx = 4; + gbc.ipady = 4; + + gbc.weightx = 0.0; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 1; + List<String> activityOutputPorts = getActivityOutputPorts(); + portCombo = new JComboBox(activityOutputPorts.toArray()); + configPanel.add(portCombo, gbc); + + comparisonCombo = new JComboBox(ActivityGenerator.comparisons.toArray()); + comparisonCombo.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Comparison selectedComparison = (Comparison) comparisonCombo + .getSelectedItem(); + if (selectedComparison != null) { + valueTypeLabel.setText("the " + + selectedComparison.getValueType()); + } + } + }); + if (comparisonCombo.getSelectedIndex() == -1) { + comparisonCombo.setSelectedIndex(0); + } + gbc.gridx = 1; + gbc.gridy = 1; + configPanel.add(comparisonCombo, gbc); + + gbc.gridx = 2; + gbc.gridy = 1; + valueTypeLabel.setHorizontalAlignment(SwingConstants.RIGHT); + configPanel.add(valueTypeLabel, gbc); + + gbc.gridx = 3; + gbc.gridy = 1; + gbc.weightx = 0.5; // request all extra space + gbc.fill = GridBagConstraints.HORIZONTAL; + configPanel.add(valueField, gbc); + + gbc.gridx = 0; + gbc.gridy = 2; + gbc.weightx = 0.0; + configPanel.add(delayLabel, gbc); + + gbc.gridx = 1; + gbc.gridx = 1; + gbc.gridy = 2; + gbc.weightx = 0.0; + delayField.setHorizontalAlignment(JTextField.RIGHT); + configPanel.add(delayField, gbc); + + gbc.gridx = 2; + gbc.gridy = 2; + gbc.gridwidth = 2; + gbc.weightx = 0.5; // request all extra space + gbc.fill = GridBagConstraints.HORIZONTAL; + configPanel.add(secondsLabel, gbc); + + if (activityOutputPorts.isEmpty()) { + JLabel warningLabel = new JLabel( + "<html><body><strong>Warning:</strong><br>" + + "<i>No single value output ports detected on the main service, " + + "cannot use built-in comparisons. You may still add a customized " + + "looping script</i></body></html>"); + gbc.gridx = 0; + gbc.gridy++; + gbc.gridwidth = 4; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.BOTH; + gbc.gridy++; + configPanel.add(warningLabel, gbc); + invokedRepeatedlyLabel.setVisible(false); + portCombo.setVisible(false); + comparisonCombo.setVisible(false); + portWarning.setVisible(false); + valueTypeLabel.setVisible(false); + valueField.setVisible(false); + delayField.setVisible(false); + delayLabel.setVisible(false); + secondsLabel.setVisible(false); + } + + gbc.gridy++; + gbc.gridx = 0; + gbc.weightx = 0.1; + gbc.gridwidth = 4; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.BOTH; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(10, 0, 10, 0); + configPanel.add(portWarning, gbc); + + gbc.insets = new Insets(0, 0, 0, 0); + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.NONE; + gbc.gridx = 0; + gbc.gridy++; + gbc.gridwidth = 4; + gbc.anchor = GridBagConstraints.LAST_LINE_END; + JPanel customiseButtonPanel = new JPanel(new FlowLayout()); + customizeButton = new JButton("Customize loop condition"); + customizeButton.addActionListener(new CustomizeAction()); + customiseButtonPanel.add(customizeButton); + configPanel.add(customiseButtonPanel, gbc); + + // filler + gbc.gridy++; + gbc.fill = GridBagConstraints.BOTH; + gbc.gridx = 4; + gbc.weightx = 0.1; + gbc.weighty = 0.1; + gbc.gridwidth = 4; + configPanel.add(Box.createGlue(), gbc); + } + + private List<String> getActivityOutputPorts() { + // Should already be sorted + return new ArrayList<>(processor.getOutputPorts().getNames()); + } + + protected JCheckBox feedBackCheck = new JCheckBox( + "Enable output port to input port feedback"); + private JLabel portWarning = new JLabel( + "<html><body><small>Note that for Taverna to be able to execute this loop, " + + "the output port <strong>must</strong> be connected to an input of another service " + + "or a workflow output port.</small></body></html>"); + + protected void makeOptions() { + optionsPanel.removeAll(); + optionsPanel.setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0.1; + gbc.anchor = GridBagConstraints.FIRST_LINE_START; + gbc.fill = GridBagConstraints.HORIZONTAL; + feedBackCheck.setBorder(new EmptyBorder(0,0,10,0)); + optionsPanel.add(feedBackCheck, gbc); + feedBackCheck.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateFeedbackHelp(); + } + }); + updateFeedbackHelp(); + + gbc.gridy = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; + optionsPanel.add(feedbackHelp, gbc); + } + + protected void updateFeedbackHelp() { + feedbackHelp.setEnabled(feedBackCheck.isSelected()); + Color color; + if (feedBackCheck.isSelected()) { + color = valueTypeLabel.getForeground(); + } else { + // Work around + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4303706 + // and assume gray is the 'disabled' colour in our Look n Feel + color = Color.gray; + } + feedbackHelp.setForeground(color); + + } + + JLabel feedbackHelp = new JLabel( + "<html><small>" + + "<p>When feedback is enabled, the value of the output port is used as input " + + "the next time the loop in invoked. The input and output ports used for feedback " + + "<strong>must</strong> have the same <strong>name</strong> and <strong>depth</strong>." + + "</p><br>" + + + "<p>Feedback can be useful for looping over a nested workflow, " + + "where the nested workflow's output determines its next input value.</p><br>" + + + "<p>In order to use feedback looping, you must provide an initial value to the input port by " + + "connecting it to the output of a previous service or workflow input port." + + "The output port used as feedback also has to be connected to a downstream service " + + "or a workflow output port.</p>" + + + "</small></html>"); + + protected void makeHeader() { + headerPanel.removeAll(); + headerPanel.setLayout(new BorderLayout()); + //headerPanel.add(new ShadedLabel("Looping for service" + // + processor.getLocalName(), ShadedLabel.ORANGE)); + } +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureAction.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureAction.java new file mode 100644 index 0000000..0b7ad8f --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureAction.java
@@ -0,0 +1,262 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import org.apache.log4j.Logger; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; + +/** + * @author Alan R Williams + * @author Stian Soiland-Reyes + * + */ +@SuppressWarnings("serial") +public class LoopConfigureAction extends AbstractAction { + + private static Logger logger = Logger.getLogger(LoopConfigureAction.class); + + private final EditManager editManager; + private final FileManager fileManager; + + private final Frame owner; + private final ObjectNode loopLayer; + private final LoopContextualView contextualView; + private final Processor processor; + private final Profile profile; + + private ApplicationConfiguration applicationConfig; + + + protected LoopConfigureAction(Frame owner, + LoopContextualView contextualView, Processor processor, + ObjectNode loopLayer, Profile profile, EditManager editManager, + FileManager fileManager, ApplicationConfiguration applicationConfig) { + super("Configure"); + this.owner = owner; + this.contextualView = contextualView; + this.loopLayer = loopLayer; + this.profile = profile; + this.editManager = editManager; + this.fileManager = fileManager; + this.processor = processor; + this.applicationConfig = applicationConfig; + } + + public void actionPerformed(ActionEvent e) { + String title = "Looping for service " + processor.getName(); + final JDialog dialog = new HelpEnabledDialog(owner, title, true); + LoopConfigurationPanel loopConfigurationPanel = new LoopConfigurationPanel( + processor, loopLayer, profile, applicationConfig); + dialog.add(loopConfigurationPanel, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout()); + + JButton okButton = new JButton(new OKAction(dialog, loopConfigurationPanel)); + buttonPanel.add(okButton); + + JButton resetButton = new JButton(new ResetAction(loopConfigurationPanel)); + buttonPanel.add(resetButton); + + JButton cancelButton = new JButton(new CancelAction(dialog)); + buttonPanel.add(cancelButton); + + dialog.add(buttonPanel, BorderLayout.SOUTH); + dialog.pack(); + dialog.setSize(650, 430); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + protected class CancelAction extends AbstractAction { + private final JDialog dialog; + + protected CancelAction(JDialog dialog) { + super("Cancel"); + this.dialog = dialog; + } + + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + if (contextualView != null) { + contextualView.refreshView(); + } + } + + } + + protected class OKAction extends AbstractAction { + private final JDialog dialog; + private final LoopConfigurationPanel loopConfigurationPanel; + + protected OKAction(JDialog dialog, LoopConfigurationPanel loopConfigurationPanel) { + super("OK"); + this.dialog = dialog; + this.loopConfigurationPanel = loopConfigurationPanel; + } + + public void actionPerformed(ActionEvent e) { + try { + + List<Edit<?>> compoundEdit = new ArrayList<Edit<?>>(); + LoopConfiguration configuration = loopConfigurationPanel.getConfiguration(); + compoundEdit.add(edits.getConfigureEdit(loopLayer, configuration)); + compoundEdit.addAll(checkPortMappings(configuration.getCondition())); + + editManager.doDataflowEdit(fileManager.getCurrentDataflow(), new CompoundEdit( + compoundEdit)); + dialog.setVisible(false); + if (contextualView != null) { + contextualView.refreshView(); + } + } catch (RuntimeException ex) { + logger.warn("Could not configure looping", ex); + JOptionPane.showMessageDialog(owner, "Could not configure looping", + "An error occured when configuring looping: " + ex.getMessage(), + JOptionPane.ERROR_MESSAGE); + } catch (EditException ex) { + logger.warn("Could not configure looping", ex); + JOptionPane.showMessageDialog(owner, "Could not configure looping", + "An error occured when configuring looping: " + ex.getMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + + protected List<Edit<?>> checkPortMappings(Activity<?> conditionActivity) { + + List<Edit<?>> compoundEdit = new ArrayList<Edit<?>>(); + if (processor.getActivityList().isEmpty()) { + return compoundEdit; + } + Set<String> newInputs = new HashSet<String>(); + Set<String> newOutputs = new HashSet<String>(); + + Activity<?> firstProcessorActivity; + firstProcessorActivity = processor.getActivityList().get(0); + if (conditionActivity != null) { + for (OutputPort condOutPort : conditionActivity.getOutputPorts()) { + String portName = condOutPort.getName(); + Map<String, String> mapping = firstProcessorActivity.getInputPortMapping(); + if (!mapping.containsKey(portName)) { + if (mapping.containsKey(portName)) { + logger.warn("Can't re-map input for " + "conditional output " + + portName); + } + for (InputPort inputPort : firstProcessorActivity.getInputPorts()) { + if (inputPort.equals(portName)) { + Edit<Activity<?>> edit = edits.getAddActivityInputPortMappingEdit( + firstProcessorActivity, portName, portName); + compoundEdit.add(edit); + newInputs.add(portName); + } + } + } + } + for (InputPort condInPort : conditionActivity.getInputPorts()) { + String portName = condInPort.getName(); + Map<String, String> mapping = firstProcessorActivity.getOutputPortMapping(); + if (!mapping.containsValue(portName)) { + for (OutputPort outputPort : firstProcessorActivity.getOutputPorts()) { + if (outputPort.equals(portName)) { + if (mapping.containsKey(portName)) { + logger.warn("Can't re-map output for " + "conditional input " + + portName); + } + Edit<Activity<?>> edit = edits.getAddActivityOutputPortMappingEdit( + firstProcessorActivity, portName, portName); + logger.info("Mapping for conditional non-outgoing activity port binding " + + portName); + compoundEdit.add(edit); + newOutputs.add(portName); + } + } + } + } + } + // Remove any stale bindings that no longer match neither + // conditional activity or the processor output ports + for (String processorIn : firstProcessorActivity.getInputPortMapping().keySet()) { + if (newInputs.contains(processorIn)) { + continue; + } + boolean foundMatch = false; + for (InputPort processorPort : processor.getInputPorts()) { + if (processorPort.getName().equals(processorIn)) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + Edit<Activity<?>> edit = edits.getRemoveActivityInputPortMappingEdit( + firstProcessorActivity, processorIn); + logger.info("Removing stale input port binding " + processorIn); + compoundEdit.add(edit); + } + } + for (String processorOut : firstProcessorActivity.getOutputPortMapping().keySet()) { + if (newInputs.contains(processorOut)) { + continue; + } + boolean foundMatch = false; + for (OutputPort processorPort : processor.getOutputPorts()) { + if (processorPort.getName().equals(processorOut)) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + Edit<Activity<?>> edit = edits.getRemoveActivityOutputPortMappingEdit( + firstProcessorActivity, processorOut); + logger.info("Removing stale output port binding " + processorOut); + compoundEdit.add(edit); + } + } + + return compoundEdit; + } + } + + protected class ResetAction extends AbstractAction { + private LoopConfigurationPanel loopConfigurationPanel; + + protected ResetAction(LoopConfigurationPanel loopConfigurationPanel) { + super("Reset"); + this.loopConfigurationPanel = loopConfigurationPanel; + } + + public void actionPerformed(ActionEvent e) { + if (contextualView != null) { + contextualView.refreshView(); + } + loopConfigurationPanel.setConfiguration(loopLayer.getConfiguration()); + } + + } + +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureMenuAction.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureMenuAction.java new file mode 100644 index 0000000..3d6700d --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopConfigureMenuAction.java
@@ -0,0 +1,97 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +import uk.org.taverna.scufl2.api.core.Processor; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; + +public class LoopConfigureMenuAction extends AbstractContextualMenuAction { + + public static final URI configureRunningSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configureRunning"); + + private static final URI LOOP_CONFIGURE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/loopConfigure"); + + private static final String LOOP_CONFIGURE = "Loop configure"; + + private EditManager editManager; + + private FileManager fileManager; + + public LoopConfigureMenuAction() { + super(configureRunningSection, 20, LOOP_CONFIGURE_URI); + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction("Looping...") { + public void actionPerformed(ActionEvent e) { + Processor p = (Processor) getContextualSelection().getSelection(); + configureLoopLayer(p, e); + } + }; + } + + public void configureLoopLayer(Processor p, ActionEvent e) { + ObjectNode loopLayer = getLoopLayer(p); + if (loopLayer != null) { + LoopConfigureAction loopConfigureAction = new LoopConfigureAction(null, null, loopLayer, editManager, fileManager); + loopConfigureAction.actionPerformed(e); + } + } + + public static ObjectNode getLoopLayer(Processor p) { + for (DispatchLayer dl : p.getDispatchStack().getLayers()) { + if (dl instanceof Loop) { + result = (Loop) dl; + break; + } + } + return result; + } + + public boolean isEnabled() { + Object selection = getContextualSelection().getSelection(); + return (super.isEnabled() && (selection instanceof Processor) && (getLoopLayer((Processor)selection) != null)); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualView.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualView.java new file mode 100644 index 0000000..c22f376 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualView.java
@@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.Properties; + +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.loop.comparisons.Comparison; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Processor; + +/** + * View of a processor, including it's iteration stack, activities, etc. + * + * @author Stian Soiland-Reyes + * + */ +public class LoopContextualView extends ContextualView { + + private static final long serialVersionUID = 1L; + + private static Logger logger = Logger.getLogger(LoopContextualView.class); + + private EditManager editManager; + private FileManager fileManager; + + private Loop loopLayer; + + private JPanel panel; + + private Processor processor; + + public LoopContextualView(Processor processor, EditManager editManager, FileManager fileManager) { + super(); + this.loopLayer = loopLayer; + this.editManager = editManager; + this.fileManager = fileManager; + this.processor = processor; + initialise(); + initView(); + } + + @Override + public Action getConfigureAction(Frame owner) { + return new LoopConfigureAction(owner, this, processor, editManager, fileManager); + } + + @Override + public void refreshView() { + initialise(); + } + + private void initialise() { + if (panel == null) { + panel = new JPanel(); + } else { + panel.removeAll(); + } + panel.setLayout(new GridBagLayout()); + updateUIByConfig(); + } + + @Override + public JComponent getMainFrame() { + return panel; + } + + @Override + public String getViewTitle() { + return "Loop of " + processor.getLocalName(); + } + + protected void updateUIByConfig() { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.HORIZONTAL; + + StringBuilder description = new StringBuilder("<html><body>"); + Properties properties = loopLayer.getConfiguration().getProperties(); + if (properties.getProperty(ActivityGenerator.COMPARISON, + ActivityGenerator.CUSTOM_COMPARISON).equals( + ActivityGenerator.CUSTOM_COMPARISON)) { + Activity<?> condition = loopLayer.getConfiguration().getCondition(); + if (condition != null) { + description.append("Looping using custom conditional "); + if (condition instanceof BeanshellActivity) { + String script = ((BeanshellActivity)condition).getConfiguration().getScript(); + if (script != null) { + if (script.length() <= 100) { + description.append("<pre>\n"); + description.append(script); + description.append("</pre>\n"); + } + } + } + } else { + description.append("<i>Unconfigured, will not loop</i>"); + } + } else { + description.append("The service will be invoked repeatedly "); + description.append("until<br> its output <strong>"); + description.append(properties + .getProperty(ActivityGenerator.COMPARE_PORT)); + description.append("</strong> "); + + Comparison comparison = ActivityGenerator + .getComparisonById(properties + .getProperty(ActivityGenerator.COMPARISON)); + description.append(comparison.getName()); + + description.append(" the " + comparison.getValueType() + ": <pre>"); + description.append(properties + .getProperty(ActivityGenerator.COMPARE_VALUE)); + description.append("</pre>"); + + String delay = properties.getProperty(ActivityGenerator.DELAY, ""); + try { + if (Double.parseDouble(delay) > 0) { + description.append("adding a delay of " + delay + + " seconds between loops."); + } + } catch (NumberFormatException ex) { + } + } + description.append("</body></html>"); + + panel.add(new JLabel(description.toString()), gbc); + gbc.gridy++; + + revalidate(); + } + + + + @Override + public int getPreferredPosition() { + return 400; + } + +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualViewFactory.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualViewFactory.java new file mode 100644 index 0000000..d222849 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopContextualViewFactory.java
@@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.util.Arrays; +import java.util.List; + +import uk.org.taverna.scufl2.api.core.Processor; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; + +public class LoopContextualViewFactory implements ContextualViewFactory<Processor> { + + private EditManager editManager; + private FileManager fileManager; + + public boolean canHandle(Object selection) { + return selection instanceof Processor; + } + + public List<ContextualView> getViews(Processor selection) { + return Arrays.asList(new ContextualView[] {new LoopContextualView(selection, editManager, fileManager)}); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopRemoveMenuAction.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopRemoveMenuAction.java new file mode 100644 index 0000000..12a9592 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/LoopRemoveMenuAction.java
@@ -0,0 +1,92 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.file.FileManager; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.Processor; + +public class LoopRemoveMenuAction extends AbstractContextualMenuAction { + + private static Logger logger = Logger + .getLogger(LoopRemoveMenuAction.class); + + public static final URI configureRunningSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configureRunning"); + + private static final URI LOOP_REMOVE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/loopRemove"); + + private static final String LOOP_REMOVE = "Loop remove"; + + public LoopRemoveMenuAction() { + super(configureRunningSection, 25, LOOP_REMOVE_URI); + } + + private EditManager editManager; + private FileManager fileManager; + + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction("Disable looping") { + public void actionPerformed(ActionEvent e) { + Processor p = (Processor) getContextualSelection().getSelection(); + Loop loopLayer = LoopConfigureMenuAction.getLoopLayer(p); + Edit<DispatchStack> deleteEdit = editManager.getEdits().getDeleteDispatchLayerEdit( + p.getDispatchStack(), loopLayer); + // TODO: Should warn before removing "essential" layers + try { + editManager.doDataflowEdit(fileManager.getCurrentDataflow(), + deleteEdit); + } catch (EditException ex) { + logger.warn("Could not remove layer " + loopLayer, ex); + } + + } + }; + } + + public boolean isEnabled() { + Object selection = getContextualSelection().getSelection(); + return (super.isEnabled() && (selection instanceof Processor) && (LoopConfigureMenuAction.getLoopLayer((Processor)selection) != null)); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setFileManager(FileManager fileManager) { + this.fileManager = fileManager; + } + +}
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Comparison.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Comparison.java new file mode 100644 index 0000000..608030c --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Comparison.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop.comparisons; + +import net.sf.taverna.t2.workbench.loop.LoopConfigurationPanel; + +/** + * A comparison beanshell template for {@link LoopConfigurationPanel}. + * <p> + * A comparison is a template for generating a beanshell that can be used for + * comparisons in say the {@link Loop} layer. + * + * @author Stian Soiland-Reyes + * + */ +public abstract class Comparison { + + public String toString() { + return getName(); + } + + public abstract String getId(); + + public abstract String getName(); + + public abstract String getValueType(); + + public abstract String getScriptTemplate(); +} \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/EqualTo.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/EqualTo.java new file mode 100644 index 0000000..dbfb8f5 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/EqualTo.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop.comparisons; + +public class EqualTo extends Comparison { + + public String getId() { + return "EqualTo"; + } + + public String getName() { + return "is equal to"; + } + + public String getScriptTemplate() { + return "${loopPort} = \"\" + ! ${port}.equals(${value}); "; + } + + public String getValueType() { + return "string"; + } +} \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsGreaterThan.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsGreaterThan.java new file mode 100644 index 0000000..fc6f56b --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsGreaterThan.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop.comparisons; + +public class IsGreaterThan extends Comparison { + + public String getId() { + return "IsGreaterThan"; + } + + public String getName() { + return "is greater than"; + } + + public String getScriptTemplate() { + return "${loopPort} = \"\" + (! (Double.parseDouble(${port}) > Double.parseDouble(${value})));"; + } + + public String getValueType() { + return "number"; + } +} \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsLessThan.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsLessThan.java new file mode 100644 index 0000000..d5fe38c --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/IsLessThan.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop.comparisons; + +public class IsLessThan extends Comparison { + + public String getId() { + return "IsLessThan"; + } + + public String getName() { + return "is less than"; + } + + public String getScriptTemplate() { + return "${loopPort} = \"\" + (! (Double.parseDouble(${port}) < Double.parseDouble(${value})));"; + } + + public String getValueType() { + return "number"; + } +} \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Matches.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Matches.java new file mode 100644 index 0000000..fa84aeb --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/Matches.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop.comparisons; + +public class Matches extends Comparison { + + public String getId() { + return "Matches"; + } + + public String getName() { + return "matches"; + } + + public String getScriptTemplate() { + return "${loopPort} = \"\" + ! ${port}.matches(${value});"; + } + + public String getValueType() { + return "regular expression"; + } +} \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotEqualTo.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotEqualTo.java new file mode 100644 index 0000000..9c73835 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotEqualTo.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop.comparisons; + +public class NotEqualTo extends Comparison { + + public String getId() { + return "NotEqualTo"; + } + + public String getName() { + return "is not equal to"; + } + + public String getScriptTemplate() { + return "${loopPort} = \"\" + ${port}.equals(${value});"; + } + + public String getValueType() { + return "string"; + } +} \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotMatches.java b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotMatches.java new file mode 100644 index 0000000..803d5d7 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/java/net/sf/taverna/t2/workbench/loop/comparisons/NotMatches.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop.comparisons; + +public class NotMatches extends Comparison { + + public String getId() { + return "NotMatches"; + } + + public String getName() { + return "does not match"; + } + + public String getScriptTemplate() { + return "${loopPort} = \"\" + ${port}.matches(${value});"; + } + + public String getValueType() { + return "regular expression"; + } +} \ No newline at end of file
diff --git a/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..1956a3f --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,3 @@ +net.sf.taverna.t2.workbench.loop.LoopConfigureMenuAction +net.sf.taverna.t2.workbench.loop.LoopAddMenuAction +net.sf.taverna.t2.workbench.loop.LoopRemoveMenuAction
diff --git a/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI b/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI new file mode 100644 index 0000000..52eafc4 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.loop.AddLoopFactory
diff --git a/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..9150066 --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.loop.LoopContextualViewFactory
diff --git a/taverna-workbench-loop-ui/src/main/resources/META-INF/spring/loop-ui-context-osgi.xml b/taverna-workbench-loop-ui/src/main/resources/META-INF/spring/loop-ui-context-osgi.xml new file mode 100644 index 0000000..4abb75f --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/resources/META-INF/spring/loop-ui-context-osgi.xml
@@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="AddLoopFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.AddLayerFactorySPI" /> + + <service ref="LoopConfigureMenuAction" auto-export="interfaces" /> + <service ref="LoopAddMenuAction" auto-export="interfaces" /> + <service ref="LoopRemoveMenuAction" auto-export="interfaces" /> + + <service ref="LoopContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + <reference id="applicationConfig" interface="uk.org.taverna.configuration.app.ApplicationConfiguration"/> +</beans:beans>
diff --git a/taverna-workbench-loop-ui/src/main/resources/META-INF/spring/loop-ui-context.xml b/taverna-workbench-loop-ui/src/main/resources/META-INF/spring/loop-ui-context.xml new file mode 100644 index 0000000..4c2133c --- /dev/null +++ b/taverna-workbench-loop-ui/src/main/resources/META-INF/spring/loop-ui-context.xml
@@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="AddLoopFactory" class="net.sf.taverna.t2.workbench.loop.AddLoopFactory"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="applicationConfig" ref="applicationConfig" /> + </bean> + + <bean id="LoopConfigureMenuAction" class="net.sf.taverna.t2.workbench.loop.LoopConfigureMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + </bean> + <bean id="LoopAddMenuAction" class="net.sf.taverna.t2.workbench.loop.LoopAddMenuAction"> + <property name="addLoopFactory"> + <ref local="AddLoopFactory"/> + </property> + </bean> + <bean id="LoopRemoveMenuAction" class="net.sf.taverna.t2.workbench.loop.LoopRemoveMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + </bean> + + <bean id="LoopContextualViewFactory" class="net.sf.taverna.t2.workbench.loop.LoopContextualViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="fileManager" ref="fileManager" /> + </bean> + +</beans>
diff --git a/taverna-workbench-loop-ui/src/test/java/net/sf/taverna/t2/workbench/loop/ShowContextualView.java b/taverna-workbench-loop-ui/src/test/java/net/sf/taverna/t2/workbench/loop/ShowContextualView.java new file mode 100644 index 0000000..3e3d121 --- /dev/null +++ b/taverna-workbench-loop-ui/src/test/java/net/sf/taverna/t2/workbench/loop/ShowContextualView.java
@@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.loop; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Processor; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.edits.impl.EditManagerImpl; +import net.sf.taverna.t2.workbench.file.FileManager; +import net.sf.taverna.t2.workbench.file.impl.FileManagerImpl; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.impl.SelectionManagerImpl; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.impl.ContextualViewComponent; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactoryRegistry; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.impl.ContextualViewFactoryRegistryImpl; + +/** + * A standalone application to show contextual views + * <p> + * The application shows a JFrame containing a contextual view, together with + * buttons which will select items in the {@link SelectionManager} for a + * (rather) empty current dataflow. + * + * @author Stian Soiland-Reyes. + * + */ +public class ShowContextualView { + + public static void main(String[] args) throws Exception { + EditManager editManager = new EditManagerImpl(); + FileManager fileManager = new FileManagerImpl(editManager); + ContextualViewFactoryRegistry contextualViewFactoryRegistry = new ContextualViewFactoryRegistryImpl(); + SelectionManagerImpl selectionMan = new SelectionManagerImpl(); + selectionMan.setFileManager(fileManager); + selectionMan.setEditManager(editManager); + new ShowContextualView(editManager, fileManager,selectionMan, contextualViewFactoryRegistry).showFrame(); + } + + private SelectionManager selectionManager; + private FileManager fileManager; + private EditManager editManager; + private ContextualViewFactoryRegistry contextualViewFactoryRegistry; + + private uk.org.taverna.scufl2.api.core.Processor processor; + + private WorkflowBundle currentDataflow; + + public ShowContextualView(EditManager editManager, FileManager fileManager, final SelectionManager selectionManager, ContextualViewFactoryRegistry contextualViewFactoryRegistry) { + this.editManager = editManager; + this.fileManager = fileManager; + this.selectionManager = selectionManager; + this.contextualViewFactoryRegistry = contextualViewFactoryRegistry; + currentDataflow = fileManager.newDataflow(); + makeProcessor(); + + } + + private void makeProcessor() { + processor = new Processor(currentDataflow.getMainWorkflow(), "Hello"); + } + + private List getSelections() { + return Arrays.asList(processor, currentDataflow); + } + + private Component makeSelectionButtons() { + JPanel buttons = new JPanel(); + for (final Object selection : getSelections()) { + buttons.add(new JButton(new AbstractAction("" + selection) { + public void actionPerformed(ActionEvent e) { + selectionManager.getDataflowSelectionModel( + currentDataflow).setSelection( + Collections.<Object> singleton(selection)); + } + })); + } + return buttons; + } + + protected void showFrame() { + JFrame frame = new JFrame(getClass().getName()); + ContextualViewComponent contextualViewComponent = new ContextualViewComponent(editManager, selectionManager, contextualViewFactoryRegistry); + frame.add(contextualViewComponent, BorderLayout.CENTER); + + frame.add(makeSelectionButtons(), BorderLayout.NORTH); + frame.setSize(400, 400); + frame.setVisible(true); + } + +}
diff --git a/taverna-workbench-loop-ui/src/test/resources/log4j.properties b/taverna-workbench-loop-ui/src/test/resources/log4j.properties new file mode 100644 index 0000000..850ede3 --- /dev/null +++ b/taverna-workbench-loop-ui/src/test/resources/log4j.properties
@@ -0,0 +1,10 @@ +log4j.rootLogger=WARN, CONSOLE +log4j.logger.net.sf.taverna.t2=INFO +#log4j.logger.net.sf.taverna.t2.ui=DEBUG + +#log4j.logger.org.apache.commons.httpclient=ERROR + +# Default output to console +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=%-5p %d{ISO8601} (%c:%L) - %m%n \ No newline at end of file
diff --git a/taverna-workbench-menu-api/pom.xml b/taverna-workbench-menu-api/pom.xml new file mode 100644 index 0000000..247cd41 --- /dev/null +++ b/taverna-workbench-menu-api/pom.xml
@@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-api</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <packaging>bundle</packaging> + <name>Menu generation API</name> + <description>An SPI system for building UI menus</description> + + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractContextualMenuAction.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractContextualMenuAction.java new file mode 100644 index 0000000..7209cae --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractContextualMenuAction.java
@@ -0,0 +1,64 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.net.URI; + +/** + * An {@link AbstractMenuAction} that is {@link ContextualMenuComponent} aware. + * The contextual selection can be retrieved from + * {@link #getContextualSelection()}. + * <p> + * The cached action will be flushed everytime the contextual selection changes, + * forcing a new call to {@link #createAction()} - given that + * {@link #isEnabled()} returns true. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractContextualMenuAction extends AbstractMenuAction + implements ContextualMenuComponent { + + private ContextualSelection contextualSelection; + + public AbstractContextualMenuAction(URI parentId, int positionHint) { + super(parentId, positionHint); + } + + public AbstractContextualMenuAction(URI parentId, int positionHint, URI id) { + super(parentId, positionHint, id); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return contextualSelection != null; + } + + @Override + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + // Force new createAction() call + action = null; + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenu.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenu.java new file mode 100644 index 0000000..07eb8d2 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenu.java
@@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import static net.sf.taverna.t2.ui.menu.MenuComponent.MenuType.menu; + +import java.net.URI; + +import javax.swing.Action; + +/** + * A {@link MenuComponent} of the type {@link MenuType#menu menu}. + * <p> + * Subclass to create an SPI implementation for the {@link MenuManager} of a + * menu. The definition of "menu" includes both the menu bar and sub menus. A + * menu can contain {@linkplain AbstractMenuAction actions}, + * {@linkplain AbstractMenuToggle toggles} or {@linkplain AbstractMenuCustom + * custom components}, or any of the above grouped in a + * {@linkplain AbstractMenuSection section}, + * {@linkplain AbstractMenuOptionGroup option group} or a + * {@linkplain AbstractMenu submenu}. + * <p> + * Menu components are linked together using URIs, avoiding the need for compile + * time dependencies between SPI implementations. To add components to a menu, + * use the {@link URI} identifying this menu as their parent id. + * <p> + * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique + * URI root that is related to the Java package name, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash + * identifiers for each menu item, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run" + * item. Use flat URI namespaces, don't base a child's URI on the parent's URI, + * as this might make it difficult to relocate the parent menu. + * <p> + * You need to list the {@linkplain Class#getName() fully qualified class name} + * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the menu + * implementation in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so + * that it can be discovered by the {@link MenuManager}. This requirement also + * applies to parent menu components (except {@link DefaultToolBar} and + * {@link DefaultMenuBar}, but ensure they are only listed once. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractMenu extends AbstractMenuItem { + /** + * Construct a menu bar (does not have a parent). This menu bar can be built + * and used through {@link MenuManager#createMenuBar(URI)}. There is a + * default menu bar implementation in {@link DefaultMenuBar} that can be + * built using {@link MenuManager#createMenuBar()}, but in case you need + * several menu bars for different windows or modes, use this constructor. + * + * @param id + * The {@link URI} to identify this menu bar. Use this as the + * parent ID for menu components to appear in this menu. + */ + public AbstractMenu(URI id) { + super(menu, (URI) null, id); + } + + /** + * Construct a submenu. + * + * @param parentId + * The {@link URI} of the parent menu. The parent should be of + * type + * {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#menu}. + * @param positionHint + * The position hint to determine the position of this submenu + * among its siblings in the parent menu. For extensibility, use + * BASIC style numbering such as 10, 20, etc. + * @param id + * The {@link URI} to identify this menu bar. Use this as the + * parent ID for menu components to appear in this submenu. + * @param label + * The label for presenting this sub-menu in the parent menu. + */ + public AbstractMenu(URI parentId, int positionHint, URI id, String label) { + this(parentId, positionHint, id, new DummyAction(label)); + } + + /** + * Construct a submenu. + * + * @param parentId + * The {@link URI} of the parent menu. The parent should be of + * type + * {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#menu}. + * @param positionHint + * The position hint to determine the position of this submenu + * among its siblings in the parent menu. For extensibility, use + * BASIC style numbering such as 10, 20, etc. + * @param id + * The {@link URI} to identify this menu bar. Use this as the + * parent ID for menu components to appear in this submenu. + * @param action + * The action containing a label and icon for presenting this + * sub-menu in the parent menu. + */ + public AbstractMenu(URI parentId, int positionHint, URI id, Action action) { + super(menu, parentId, id); + this.action = action; + this.positionHint = positionHint; + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuAction.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuAction.java new file mode 100644 index 0000000..446a2ec --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuAction.java
@@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +/** + * A {@link MenuComponent} of the type {@link MenuType#action action}. + * <p> + * Subclass to create an SPI implementation for the {@link MenuManager} of an + * action. An action is an item within a menu or toolbar that can be + * clicked/selected to invoke some action. + * <p> + * This action can have as an parent a {@linkplain AbstractMenu menu} or + * {@linkplain AbstractToolBar toolbar}, or grouped within an + * {@linkplain AbstractMenuSection section} or + * {@linkplain AbstractMenuOptionGroup option group}. + * <p> + * To define the {@link Action}, implement {@link #createAction()}. The action + * should provide both the label/icon (representation) and + * {@link ActionListener#actionPerformed(ActionEvent)}. + * <p> + * You need to list the {@linkplain Class#getName() fully qualified class name} + * (for example <code>com.example.t2plugin.menu.MyMenuAction</code>) of the menu + * action implementation in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so + * that it can be discovered by the {@link MenuManager}. This requirement also + * applies to parent menu components (except {@link DefaultToolBar} and + * {@link DefaultMenuBar}, but ensure they are only listed once. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractMenuAction extends AbstractMenuItem { + /** + * Construct a menu action to appear within the specified menu component. + * + * @param parentId + * The {@link URI} of the parent menu component. The component + * should be a {@linkplain MenuComponent.MenuType#isParentType() + * parent type} and must have been registered separately as an + * SPI. + * @param positionHint + * The position hint to determine the position of this action + * among its siblings in the parent menu, section or toolbar. For + * extensibility, use BASIC style numbering such as 10, 20, etc. + * (Note that position hints are local to each parent, so each + * {@linkplain AbstractMenuSection section} have their own + * position hint scheme.) + */ + public AbstractMenuAction(URI parentId, int positionHint) { + this(parentId, positionHint, null); + } + + /** + * Construct a menu action to appear within the specified menu component. + * + * @param parentId + * The {@link URI} of the parent menu component. The component + * should be a {@linkplain MenuComponent.MenuType#isParentType() + * parent type} and must have been registered separately as an + * SPI. + * @param positionHint + * The position hint to determine the position of this action + * among its siblings in the parent menu, section or toolbar. For + * extensibility, use BASIC style numbering such as 10, 20, etc. + * (Note that position hints are local to each parent, so each + * {@linkplain AbstractMenuSection section} have their own + * position hint scheme.) + * @param id + * The {@link URI} to identify this action. Although no + * components can have an action as their parent, this URI can be + * used to retrieve the realisation of this component using + * {@link MenuManager#getComponentByURI(URI)}. This ID might also + * be registered as a help identifier with the help system. + */ + public AbstractMenuAction(URI parentId, int positionHint, URI id) { + super(MenuType.action, parentId, id); + this.positionHint = positionHint; + } + + /** + * Call {@link #createAction()} on first call, after that return cached + * action. + * + * @see #createAction() + */ + @Override + public synchronized Action getAction() { + if (action == null) + action = createAction(); + return action; + } + + /** + * Create the {@link Action} that labels this menu action, in addition to + * performing the desired action on + * {@link ActionListener#actionPerformed(ActionEvent)}. + * <p> + * This method will be called by {@link #getAction()} on the first call. + * Concurrent calls to <tt>getAction()</tt> will return the same action. + * <p> + * Implementations might use {@link AbstractAction} as a superclass for menu + * actions. It is recommended to make the action a top level class so that + * it can be used both within an {@link AbstractMenuAction} of a menu bar + * and within an {@link AbstractMenuAction} of a tool bar. + * + * @see #getAction() + * @return A configured {@link Action} that should at least have a label or + * icon. + */ + protected abstract Action createAction(); +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuCustom.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuCustom.java new file mode 100644 index 0000000..9a64130 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuCustom.java
@@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.awt.Component; +import java.net.URI; + +import javax.swing.JButton; +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +/** + * A {@link MenuComponent} of the type {@link MenuType#custom}. + * <p> + * Subclass to create an SPI implementation for the {@link MenuManager} of a + * custom menu or toolbar {@link Component}, for instance a {@link JMenu}, + * {@link JMenuItem} or {@link JButton}. + * <p> + * This type of component can be useful for adding third party components that + * are built using other menu systems, or to provide dynamic menus such as a + * list of open files. This is the recommended way to customise the menus, + * although it is also possible to modify the components returned using + * {@link MenuManager#getComponentByURI(URI)}, but as the components built by + * the menu manager might be refreshed by various actions forcing an update to + * the SPI registry, such as installing a plugin. By using a custom menu + * component it is possible to avoid these problems and to provide the + * {@link Component} to be inserted into the menu/toolbar as built by the + * {@link MenuManager}. + * <p> + * This component can have as an parent any menu component that + * {@linkplain MenuType#isParentType() is a parent type}. Note that although you + * can specify an {@link URI} to identify the custom component (to be used with + * {@link MenuManager#getComponentByURI(URI)} a custom component can't have + * children. Such children would have to be created manually and added to the + * component. + * <p> + * You need to list the {@linkplain Class#getName() fully qualified class name} + * (for example <code>com.example.t2plugin.menu.MyMenuAction</code>) of the menu + * action implementation in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so + * that it can be discovered by the {@link MenuManager}. This requirement also + * applies to parent menu components (except {@link DefaultToolBar} and + * {@link DefaultMenuBar}, but ensure they are only listed once. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractMenuCustom extends AbstractMenuItem { + /** + * Construct a menu action to appear within the specified menu component. + * + * @param parentId + * The {@link URI} of the parent menu component. The component + * should be a {@link MenuType#isParentType() parent type} and + * must have been registered separately as an SPI. + * @param positionHint + * The position hint to determine the position of this action + * among its siblings in the parent menu, section or toolbar. For + * extensibility, use BASIC style numbering such as 10, 20, etc. + * (Note that position hints are local to each parent, so each + * {@linkplain AbstractMenuSection section} have their own + * position hint scheme.) + */ + public AbstractMenuCustom(URI parentId, int positionHint) { + this(parentId, positionHint, null); + } + + /** + * Construct a menu action to appear within the specified menu component. + * + * @param parentId + * The {@link URI} of the parent menu component. The component + * should be a {@linkplain MenuType#isParentType() parent type} + * and must have been registered separately as an SPI. + * @param positionHint + * The position hint to determine the position of this action + * among its siblings in the parent menu, section or toolbar. For + * extensibility, use BASIC style numbering such as 10, 20, etc. + * (Note that position hints are local to each parent, so each + * {@linkplain AbstractMenuSection section} have their own + * position hint scheme.) + * @param id + * The {@link URI} to identify this action. Although no + * components can have an action as their parent, this URI can be + * used to retrieve the realisation of this component using + * {@link MenuManager#getComponentByURI(URI)}. This ID might also + * be registered as a help identifier with the help system. + */ + public AbstractMenuCustom(URI parentId, int positionHint, URI id) { + super(MenuType.custom, parentId, id); + this.positionHint = positionHint; + } + + /** + * Create the {@link Component} that is to be added to the parent. + * <p> + * The component must be compatible with the parent realisation from the + * {@link MenuManager}, for instance you can't add {@link JMenuItem}s to a + * toolbar. + * </p> + * <p> + * Note that the component might get assigned new parents if the + * menues/toolbars are rebuilt by the {@link MenuManager} is refreshed, + * although the menu manager will try to avoid a second call to + * {@link #createCustomComponent()}. + * </p> + * + * @return A custom {@link Component} such as {@link JMenu}, + * {@link JMenuItem} or {@link JButton} to be added to the parent + * menu component. + */ + protected abstract Component createCustomComponent(); + + /** + * Return the custom component created using + * {@link #createCustomComponent()} on first call, return cached instance on + * later calls. + * + * {@inheritDoc} + */ + @Override + public final synchronized Component getCustomComponent() { + if (customComponent == null) + customComponent = createCustomComponent(); + return customComponent; + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuItem.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuItem.java new file mode 100644 index 0000000..63b6c78 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuItem.java
@@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Icon; + +/** + * An abstract implementation of {@link MenuComponent} that can be used by + * convenience to create menu component SPIs for the {@link MenuManager}. + * <p> + * Abstract subclasses of this class are specialised by their + * {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType}. To create a menu, + * toolbar, section, action etc, create an SPI implementation by subclassing + * depending on the required type: + * </p> + * <dl> + * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#menu}</dt> + * <dd>Subclass {@link AbstractMenu}</dd> + * + * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#toolBar}</dt> + * <dd>Subclass {@link AbstractToolBar}</dd> + * + * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#section}</dt> + * <dd>Subclass {@link AbstractMenuSection}</dd> + * + * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#action}</dt> + * <dd>Subclass {@link AbstractMenuAction}</dd> + * + * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#toggle}</dt> + * <dd>Subclass {@link AbstractMenuToggle}</dd> + * + * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#custom}</dt> + * <dd>Subclass {@link AbstractMenuCustom}</dd> + * + * <dt> {@link net.sf.taverna.t2.ui.menu.MenuComponent.MenuType#optionGroup}</dt> + * <dd>Subclass {@link AbstractMenuOptionGroup}</dd> + * + * </dl> + * <p> + * Note that you are not required to subclass any of these as long as your SPI + * implementations implement the {@link MenuComponent} interface. In all cases + * you are still required to list all your implementations, including + * intermediate menus and sections, in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> + * </p> + * + * @author Stian Soiland-Reyes + * + */ +public abstract class AbstractMenuItem implements MenuComponent { + /** + * An {@link Action} that does not perform any action, but only contains a + * name and icon. Used by {@link AbstractMenu} and others. + * + * @author Stian Soiland-Reyes + * + */ + @SuppressWarnings("serial") + public static class DummyAction extends AbstractAction { + public DummyAction(String name) { + super(name); + } + + public DummyAction(String name, Icon icon) { + super(name, icon); + } + + @Override + public void actionPerformed(ActionEvent e) { + } + } + + public AbstractMenuItem(MenuType type, URI parentId, URI id) { + this.type = type; + this.parentId = parentId; + this.id = id; + } + + private final MenuType type; + private final URI parentId; + private final URI id; + protected int positionHint = 100; + protected Action action; + protected Component customComponent; + + @Override + public Action getAction() { + return action; + } + + @Override + public Component getCustomComponent() { + return customComponent; + } + + @Override + public URI getId() { + return id; + } + + @Override + public URI getParentId() { + return parentId; + } + + @Override + public int getPositionHint() { + return positionHint; + } + + @Override + public MenuType getType() { + return type; + } + + @Override + public boolean isEnabled() { + return true; + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuOptionGroup.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuOptionGroup.java new file mode 100644 index 0000000..091cc23 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuOptionGroup.java
@@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.net.URI; + +/** + * A {@link MenuComponent} of the type {@link MenuType#optionGroup}. + * <p> + * Subclass to create an SPI implementation for the {@link MenuManager} of an + * option group. An option group is similar to a + * {@linkplain AbstractMenuSection section}, but enforces that only one of the + * children are selected at any time. + * <p> + * Menu components are linked together using URIs, avoiding the need for compile + * time dependencies between SPI implementations. To add actions or toggles to + * an option group, use the {@link URI} identifying this section as their parent + * id. + * <p> + * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique + * URI root that is related to the Java package name, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash + * identifiers for each menu item, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run" + * item. Use flat URI namespaces, don't base a child's URI on the parent's URI, + * as this might make it difficult to relocate the parent menu. + * <p> + * You need to list the {@linkplain Class#getName() fully qualified class name} + * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the option + * group implementation in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so + * that it can be discovered by the {@link MenuManager}. This requirement also + * applies to parent menu components (except {@link DefaultToolBar} and + * {@link DefaultMenuBar}, but ensure they are only listed once. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractMenuOptionGroup extends AbstractMenuItem { + /** + * Construct an option group. + * + * @param parentId + * The {@link URI} of the parent menu component. The parent + * should be of type {@link MenuType#menu} or + * {@link MenuType#toolBar}. + * @param positionHint + * The position hint to determine the position of this option + * group among its siblings in the parent menu. For + * extensibility, use BASIC style numbering such as 10, 20, etc. + * (Note that position hints are local to each parent, so each + * option group have their own position hint scheme for their + * children.) + * @param id + * The {@link URI} to identify this option group. Use this as the + * parent ID for menu components to appear in this option group. + */ + public AbstractMenuOptionGroup(URI parentId, int positionHint, URI id) { + super(MenuType.optionGroup, parentId, id); + this.positionHint = positionHint; + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuSection.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuSection.java new file mode 100644 index 0000000..2e649e0 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuSection.java
@@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.awt.Color; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +/** + * A {@link MenuComponent} of the type {@link MenuType#section}. + * <p> + * Subclass to create an SPI implementation for the {@link MenuManager} of a + * section. A section is a part of a {@linkplain AbstractMenu menu} or + * {@linkplain AbstractToolBar toolbar} that group together + * {@linkplain AbstractMenuAction actions} or {@linkplain AbstractMenuToggle + * toggles}, and separates them from siblings using separators if needed. + * <p> + * Menu components are linked together using URIs, avoiding the need for compile + * time dependencies between SPI implementations. To add actions to a section, + * use the {@link URI} identifying this section as their parent id. + * <p> + * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique + * URI root that is related to the Java package name, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash + * identifiers for each menu item, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run" + * item. Use flat URI namespaces, don't base a child's URI on the parent's URI, + * as this might make it difficult to relocate the parent menu. + * <p> + * You need to list the {@linkplain Class#getName() fully qualified class name} + * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the section + * implementation in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so + * that it can be discovered by the {@link MenuManager}. This requirement also + * applies to parent menu components (except {@link DefaultToolBar} and + * {@link DefaultMenuBar}, but ensure they are only listed once. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractMenuSection extends AbstractMenuItem { + public static final String SECTION_COLOR = "sectionColor"; + + /** + * Construct a menu section. + * + * @param parentId + * The {@link URI} of the parent menu component. The parent + * should be of type {@link MenuType#menu} or + * {@link MenuType#toolBar}. + * @param positionHint + * The position hint to determine the position of this section + * among its siblings in the parent menu. For extensibility, use + * BASIC style numbering such as 10, 20, etc. (Note that position + * hints are local to each parent, so each section have their own + * position hint scheme for their children.) + * @param id + * The {@link URI} to identify this menu section. Use this as the + * parent ID for menu components to appear in this section. + */ + public AbstractMenuSection(URI parentId, int positionHint, URI id) { + super(MenuType.section, parentId, id); + this.positionHint = positionHint; + } + + @Override + public synchronized Action getAction() { + if (action == null) + action = createAction(); + return action; + } + + /** + * (Optionally) Create the {@link Action} that labels this section. + * <p> + * The actual action will be ignored, but the label and/or icon will be used + * as a section header in the menu. If the property {@link #SECTION_COLOR} + * has been defined in the action, that {@link Color} will be used to make + * the section background. + * <p> + * The default implementation of this method returns <code>null</code>, + * meaning that no section header will be created - instead a simple line + * will separate this section from the items above (if needed). + * <p> + * Implementations might use {@link AbstractAction} as a superclass for menu + * actions. + * + * @return A configured {@link Action} that should at least have a label or + * icon. + */ + protected Action createAction() { + return null; + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuToggle.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuToggle.java new file mode 100644 index 0000000..97e977d --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractMenuToggle.java
@@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +/** + * A {@link MenuComponent} of the type {@link MenuType#toggle}. + * <p> + * Subclass to create an SPI implementation for the {@link MenuManager} of an + * toggle action. A toggle is a menu item that can be turned on/off and are + * typically represented with a check box when they are enabled. + * <p> + * This action can have as an parent a {@linkplain AbstractMenu menu} or + * {@linkplain AbstractToolBar toolbar}, or grouped within a + * {@linkplain AbstractMenuSection section} or + * {@linkplain AbstractMenuOptionGroup option group}. + * <p> + * To define the {@link Action}, implement {@link #createAction()}. The action + * should provide both the label/icon (representation) and + * {@link ActionListener#actionPerformed(ActionEvent)}. + * <p> + * You need to list the {@linkplain Class#getName() fully qualified class name} + * (for example <code>com.example.t2plugin.menu.MyMenuAction</code>) of the menu + * action implementation in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so + * that it can be discovered by the {@link MenuManager}. This requirement also + * applies to parent menu components (except {@link DefaultToolBar} and + * {@link DefaultMenuBar}, but ensure they are only listed once. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractMenuToggle extends AbstractMenuItem { + /** + * Construct a toggle action to appear within the specified menu component. + * + * @param parentId + * The {@link URI} of the parent menu component. The component + * should be a {@linkplain MenuType#isParentType() parent type} + * and must have been registered separately as an SPI. + * @param positionHint + * The position hint to determine the position of this toggle + * action among its siblings in the parent menu, section or + * toolbar. For extensibility, use BASIC style numbering such as + * 10, 20, etc. (Note that position hints are local to each + * parent, so each {@linkplain AbstractMenuSection section} have + * their own position hint scheme.) + */ + public AbstractMenuToggle(URI parentId, int positionHint) { + this(parentId, null, positionHint); + } + + /** + * Construct a toggle action to appear within the specified menu component. + * + * @param parentId + * The {@link URI} of the parent menu component. The component + * should be a {@link MenuType#isParentType() parent type} and + * must have been registered separately as an SPI. + * @param id + * The {@link URI} to identify this toggle action. Although no + * components can have an action as their parent, this URI can be + * used to retrieve the realisation of this component using + * {@link MenuManager#getComponentByURI(URI)}. This ID might also + * be registered as a help identifier with the help system. + * @param positionHint + * The position hint to determine the position of this action + * among its siblings in the parent menu, section or toolbar. For + * extensibility, use BASIC style numbering such as 10, 20, etc. + * (Note that position hints are local to each parent, so each + * {@linkplain AbstractMenuSection section} have their own + * position hint scheme.) + */ + public AbstractMenuToggle(URI parentId, URI id, int positionHint) { + super(MenuType.toggle, parentId, id); + this.positionHint = positionHint; + } + + /** + * Call {@link #createAction()} on first call, after that return cached + * action. + * + * @see #createAction() + * + * {@inheritDoc} + */ + @Override + public synchronized Action getAction() { + if (action == null) + action = createAction(); + return action; + } + + /** + * Create the {@link Action} that labels this toggle action, in addition to + * performing the desired action on + * {@link ActionListener#actionPerformed(ActionEvent)}. + * <p> + * Implementations might use {@link AbstractAction} as a superclass for menu + * actions. It is recommended to make the action a top level class so that + * it can be used both within an {@link AbstractMenuAction} of a menu bar + * and within an {@link AbstractMenuAction} of a tool bar. + * </p> + * + * @return A configured {@link Action} that should at least have a label or + * icon. + */ + protected abstract Action createAction(); +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractToolBar.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractToolBar.java new file mode 100644 index 0000000..234ea75 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/AbstractToolBar.java
@@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.net.URI; + +/** + * A {@link MenuComponent} of the type {@link MenuType#toolBar}. + * <p> + * Subclass to create an SPI implementation for the {@link MenuManager} of a + * toolbar. A toolbar can contain {@linkplain AbstractMenuAction actions}, + * {@linkplain AbstractMenuToggle toggles} or {@linkplain AbstractMenuCustom + * custom components}, or any of the above grouped in a + * {@linkplain AbstractMenuSection section} or an + * {@linkplain AbstractMenuOptionGroup option group}. + * <p> + * The {@link DefaultToolBar default toolbar} can be used as a parent for items + * that are to be returned in the toolbar {@link MenuManager#createToolBar()}, + * while toolbars from other instances of AbstractToolBar can be created using + * {@link MenuManager#createToolBar(URI)} specifying the URI of the toolbar's + * identifier. + * <p> + * Menu components are linked together using URIs, avoiding the need for compile + * time dependencies between SPI implementations. To add components to a + * toolbar, use the {@link URI} identifying this toolbar as their parent id. + * <p> + * <strong>Note:</strong> To avoid conflicts with other plugins, use a unique + * URI root that is related to the Java package name, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash + * identifiers for each menu item, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a "Run" + * item. Use flat URI namespaces, don't base a child's URI on the parent's URI, + * as this might make it difficult to relocate the parent menu. + * <p> + * You need to list the {@linkplain Class#getName() fully qualified class name} + * (for example <code>com.example.t2plugin.menu.MyMenu</code>) of the toolbar + * implementation in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> so + * that it can be discovered by the {@link MenuManager}. This requirement also + * applies to parent menu components (except {@link DefaultToolBar} and + * {@link DefaultMenuBar}, but ensure they are only listed once. + * + * @author Stian Soiland-Reyes + */ +public abstract class AbstractToolBar extends AbstractMenuItem { + /** + * Construct a toolbar with the given {@link URI} as identifier. + * + * @param id + * The {@link URI} to identify this toolbar. Use this as the + * parent ID for menu components to appear in this toolbar. + */ + public AbstractToolBar(URI id) { + super(MenuType.toolBar, null, id); + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualMenuComponent.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualMenuComponent.java new file mode 100644 index 0000000..080beb1 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualMenuComponent.java
@@ -0,0 +1,35 @@ +package net.sf.taverna.t2.ui.menu; + +import java.awt.Component; + +/** + * A contextual menu component. + * <p> + * A {@link MenuComponent} that also implements ContextualMenuComponent, when + * included in a menu tree rooted in the {@link DefaultContextualMenu} and + * retrieved using + * {@link MenuManager#createContextMenu(Object, Object, Component)}, will be + * {@linkplain #setContextualSelection(ContextualSelection) informed} before + * calls to {@link #isEnabled()} or {@link #getAction()}. + * <p> + * In this way the contextual menu item can be visible for only certain + * selections, and its action can be bound to the current selection. + * <p> + * Contextual menu components can be grouped by {@linkplain AbstractMenuSection + * sections} and {@linkplain AbstractMenu sub-menus}, or directly have the + * {@link DefaultContextualMenu} as the parent. + * + * @see ContextualSelection + * @see DefaultContextualMenu + * @author Stian Soiland-Reyes + */ +public interface ContextualMenuComponent extends MenuComponent { + /** + * Set the contextual selection, or <code>null</code> if there is no current + * selection (if the menu item was not included in a contextual menu). + * + * @param contextualSelection + * The contextual selection + */ + void setContextualSelection(ContextualSelection contextualSelection); +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualSelection.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualSelection.java new file mode 100644 index 0000000..e94307e --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/ContextualSelection.java
@@ -0,0 +1,48 @@ +package net.sf.taverna.t2.ui.menu; + +import java.awt.Component; + +import javax.swing.JPopupMenu; + +import uk.org.taverna.scufl2.api.core.Workflow; + +/** + * A contextual selection as passed to a {@link ContextualMenuComponent}. + * + * @author Stian Soiland-Reyes + */ +public class ContextualSelection { + private final Object parent; + private final Object selection; + private final Component relativeToComponent; + + public ContextualSelection(Object parent, Object selection, + Component relativeToComponent) { + this.parent = parent; + this.selection = selection; + this.relativeToComponent = relativeToComponent; + } + + /** + * The parent object of the selected object, for instance a {@link Workflow}. + */ + public Object getParent() { + return parent; + } + + /** + * The selected object which actions in the contextual menu relate to, for + * instance a Processor. + */ + public Object getSelection() { + return selection; + } + + /** + * A UI component which the returned {@link JPopupMenu} (and it's actions) + * is to be relative to, for instance as a parent of pop-up dialogues. + */ + public Component getRelativeToComponent() { + return relativeToComponent; + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultContextualMenu.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultContextualMenu.java new file mode 100644 index 0000000..0db13a7 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultContextualMenu.java
@@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.net.URI; + +/** + * The default contextual menu, created using + * {@link MenuManager#createContextMenu(Object, Object, java.awt.Component)()}. + * <p> + * Items that are part of a contextual menu should also implement + * {@link ContextualMenuComponent}, the menu manager will then be able to tell + * the items what is the current selection for the contextual menu, so that the + * items can update their {@link MenuComponent#isEnabled()} (only visible for + * some selections) and {@link MenuComponent#getAction()} (the action needs the + * selected object). + * + * @author Stian Soiland-Reyes + */ +public class DefaultContextualMenu extends AbstractMenu { + /** + * The URI of a menu item representing the default menu bar. Menu items who + * has this URI as their {@link #getParentId()} will be shown in the top + * menu of the main application window. + */ + public static final URI DEFAULT_CONTEXT_MENU = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#defaultContextMenu"); + + /** + * Construct the default menu bar + */ + public DefaultContextualMenu() { + super(DEFAULT_CONTEXT_MENU); + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultMenuBar.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultMenuBar.java new file mode 100644 index 0000000..8c5eab6 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultMenuBar.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.net.URI; + +/** + * The default {@link AbstractMenu menu bar} that appears in the main + * application window, created using {@link MenuManager#createMenuBar()}. + * Alternative menu bars can be created using + * {@link MenuManager#createMenuBar(URI)} - referring to the URI of another + * instance of {@link AbstractMenu}. + * + * @author Stian Soiland-Reyes + */ +public class DefaultMenuBar extends AbstractMenu { + /** + * The URI of a menu item representing the default menu bar. Menu items who + * has this URI as their {@link #getParentId()} will be shown in the top + * menu of the main application window. + */ + public static final URI DEFAULT_MENU_BAR = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#defaultMenuBar"); + + /** + * Construct the default menu bar + * + */ + public DefaultMenuBar() { + super(DEFAULT_MENU_BAR); + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultToolBar.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultToolBar.java new file mode 100644 index 0000000..302c37a --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DefaultToolBar.java
@@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.net.URI; + +/** + * The default tool bar that will be shown by the main application window. Use + * {@link #DEFAULT_TOOL_BAR} as the {@link #getParentId()} for items that should + * appear in this toolbar. This toolbar can be created using + * {@link MenuManager#createToolBar()} + * <p> + * Separate toolbars can be made by subclassing {@link AbstractToolBar} and + * created by using {@link MenuManager#createToolBar(URI)}. + * + * @author Stian Soiland-Reyes + */ +public class DefaultToolBar extends AbstractToolBar { + /** + * The URI of a tool bar item representing the default tool bar. Items who + * has this URI as their {@link #getParentId()} will be shown in the default + * toolbar of the main application window. + */ + public static final URI DEFAULT_TOOL_BAR = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#defaultToolBar"); + + /** + * Construct the default toolbar. + */ + public DefaultToolBar() { + super(DEFAULT_TOOL_BAR); + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOnlyAction.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOnlyAction.java new file mode 100644 index 0000000..7260fdf --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOnlyAction.java
@@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +/** + * Marker interface for actions that are valid only when the design perspective + * is selected. + * + * @author alanrw + * @author David Withers + */ +public interface DesignOnlyAction { + +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOrResultsAction.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOrResultsAction.java new file mode 100644 index 0000000..26e6b62 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/DesignOrResultsAction.java
@@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +/** + * Marker interface for actions that are valid the design or result perspectives + * are selected. + * + * @author alanrw + * @author David Withers + */ +public interface DesignOrResultsAction { + +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java new file mode 100644 index 0000000..5bc91c4 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuComponent.java
@@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; + +import javax.swing.Action; +import javax.swing.ButtonGroup; +import javax.swing.JCheckBox; +import javax.swing.JMenu; +import javax.swing.JToolBar; +import javax.swing.MenuElement; + +/** + * A menu component, including sub menus, toolbars, and menu items. + * <p> + * This is an {@link net.sf.taverna.t2.spi.SPIRegistry SPI}, and implementations + * should list their fully qualified classnames in + * <tt>META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</tt> to be + * discovered by the {@link MenuManager}. + * + * @author Stian Soiland-Reyes + * @author David Withers + */ +public interface MenuComponent { + /** + * The {@link Action} describing this menu item, used for creating the UI + * representation of this item. + * <p> + * As a minimum the action should contain a name, and optionally an icon, a + * description and a keyboard shortcut. For {@linkplain MenuType#action + * actions} and {@linkplain MenuType#toggle toggles} the {@link Action}'s + * {@link ActionListener#actionPerformed(ActionEvent)} method is called when + * the item is clicked/selected. + * <p> + * This action is ignored and should be <code>null</code> for items of type + * {@link MenuType#optionGroup} and {@link MenuType#custom}. The action is + * optional for {@linkplain MenuType#toolBar toolbars} and + * {@linkplain MenuType#section sections}, where the action's name would be + * used as a label. + * + * @return The {@link Action} describing this menu item, or + * <code>null</code> if the {@link #getType()} is + * {@link MenuType#section}, {@link MenuType#optionGroup} or + * {@link MenuType#custom}. + */ + public Action getAction(); + + /** + * Get a custom {@link Component} to be inserted into the parent + * menu/toolbar. + * <p> + * Used instead of creating menu elements from the {@link #getAction()} if + * the {@link #getType()} is {@link MenuType#custom}. This can be used to + * include dynamic menus. + * <p> + * This value is ignored and should be <code>null</code> for all other types + * except {@link MenuType#custom}. + * + * @return A {@link Component} to be inserted into the parent menu/toolbar. + */ + public Component getCustomComponent(); + + /** + * The {@link URI} to identify this menu item. + * <p> + * This identifier can be used with other menu item's {@link #getParentId()} + * if this item has a {@link #getType()} of {@link MenuType#menu}, + * {@link MenuType#toolBar}, {@link MenuType#section} or + * {@link MenuType#optionGroup}. + * <p> + * Leaf menu items of {@link #getType()} {@link MenuType#toggle}, + * {@link MenuType#custom} and {@link MenuType#action} don't need an + * identifier as they can't have children, and may return <code>null</code> + * instead. However, a valid identifier might be used to look up the + * MenuItem with {@link MenuManager#getComponentByURI(URI)} + * <p> + * <strong>Note:</strong> To avoid conflicts with other plugins, use a + * unique URI root that is related to the Java package name, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu</code>, and use hash + * identifiers for each menu item, for instance + * <code>http://cs.university.ac.uk/myplugin/2008/menu#run</code> for a + * "Run" item. Use flat URI namespaces, don't base a child's URI on the + * parent's URI, as this might make it difficult to relocate the parent + * menu. + * + * @return The {@link URI} to identify this menu item. + */ + public URI getId(); + + /** + * The {@link URI} of the parent menu item, as returned by the parent's + * {@link #getId()}. + * <p> + * If this is the {@link DefaultMenuBar#DEFAULT_MENU_BAR}, then this menu + * item will be one of the top level menus of the main application window, + * like "File" or "Edit", and must have {@link #getType()} + * {@link MenuType#menu}. + * <p> + * This value should be <code>null</code> if this item is of + * {@link #getType()} {@link MenuType#toolBar}, and could be + * <code>null</code> if this is an independent root menu of type + * {@link MenuType#menu} (to be used outside the main window). + * <p> + * <strong>Note:</strong> To avoid compile time and runtime dependency on + * the parent menu item, always construct this URI directly using + * {@link URI#create(String)}. + * + * @return The {@link URI} of the parent menu item. + */ + public URI getParentId(); + + /** + * A hint on how to position this item below the parent. + * <p> + * Menu items within the same parent menu/group/toolBar are ordered + * according to this position hint. If several items have the same position + * hint, their internal order is undefined, although generally it will be + * the order in which they were loaded. + * <p> + * <strong>Tip:</strong> Number the position hints in BASIC style, such as + * 10, 20, etc. so that plugins can use position hint such as 19 or 21 to be + * immediately before or after your item. + * + * @return A position hint + */ + public int getPositionHint(); + + /** + * The {@link MenuType type} of menu item. + * <p> + * In the simple case of a "File -> New" menu structure, the "File" menu + * item has a type of {@link MenuType#menu}, while the "New" has a type of + * {@link MenuType#action}. + * <p> + * The menu item can only have children (i.e., items with + * {@link #getParentId()} equalling to this item's {@link #getId()}) if the + * type is not a leaf type, i.e., not {@link MenuType#toggle} or + * {@link MenuType#action}. + * + * @return A {@link MenuType} to specify the role of this menu item. + */ + public MenuType getType(); + + /** + * True if this menu component is to be included in the menu/toolbar. + * + * @return True is this menu component is to be included + */ + public boolean isEnabled(); + + /** + * The type of menu item, such as {@link #action}, {@link #menu} or + * {@link #toolBar}. + * <p> + * Some types are {@linkplain #isParentType() parent types} - that means + * URIs to menu components of that type can be used as a + * {@linkplain MenuComponent#getParentId() parent id}. + * + * @author Stian Soiland-Reyes + * + */ + public static enum MenuType { + /** + * A normal {@link Action} as part of a {@link #menu}, {@link #toolBar}, + * {@link #section} or {@link #optionGroup}. Such menu items are leaf + * nodes, which no {@link MenuComponent}s can have this as it's + * {@link MenuComponent#getParentId()}. The action's + * {@link ActionListener#actionPerformed(ActionEvent)} will be called + * when choosing/clicking the menu item from the menu or toolBar. + */ + action, + /** + * Provide a customised {@link MenuElement} from + * {@link MenuComponent#getCustomComponent()} that is to be used instead + * of creating an element from {@link MenuComponent#getAction()}. + */ + custom, + /** + * A group containing mutually exclusive choices (as {@link #action}s), + * to be grouped in a {@link ButtonGroup}, separated using + * {@link JMenu#addSeparator()} or {@link JToolBar#addSeparator()} when + * needed. The {@link MenuComponent#getAction()} is ignored and should + * be <code>null</code>. + */ + optionGroup, + /** + * A section of menu items within {@link #menu} or {@link #toolBar}. + * Sections are separated using {@link JMenu#addSeparator()} or + * {@link JToolBar#addSeparator()} when needed. The + * {@link MenuComponent#getAction()} is ignored and should be + * <code>null</code>. + */ + section, + /** + * A (sub)menu that contain other menu items, including deeper + * {@link #menu}s. The {@link Action} from + * {@link MenuComponent#getAction()} is used to find the name, icon, + * etc., for the sub-menu, while its + * {@link ActionListener#actionPerformed(ActionEvent)} method is + * ignored. The {@link DefaultMenuBar} is the default top level menu, + * although others can be created with <code>null</code> as their + * parent. + */ + menu, + /** + * A boolean toggle action, the action will be shown as a + * {@link JCheckBox} on a menu or toolBar. Such menu items are leaf + * nodes, which no {@link MenuComponent}s can have this as it's + * {@link MenuComponent#getParentId()}. The action's + * {@link ActionListener#actionPerformed(ActionEvent)} will be called + * when toggling the action. + */ + toggle, + /** + * A toolBar containing {@link #optionGroup}s, {@link #toggle}s or + * {@link #action}s. The toolBar can be shown as a {@link JToolBar}. The + * {@link MenuComponent#getAction()} and + * {@link MenuComponent#getParentId()} are ignored and should be + * <code>null</code>. + */ + toolBar; + + private static final Set<MenuType> parentTypes = defineParentTypes(); + + /** + * True if the menu type is a parent type such as {@link #optionGroup}, + * {@link #section}, {@link #menu} or {@link #toolBar}. If the type of a + * menu component is a a parent type it can (should) have children, + * i.e., the children has a {@link MenuComponent#getParentId()} that + * equals the parent's {@link MenuComponent#getId()}. + * + * @return True if the menu type is a parent type. + */ + public boolean isParentType() { + return parentTypes.contains(this); + } + + /** + * Create the set of {@link MenuType}s that {@link #isParentType()} + * would return <code>true</code> for. + * + * @return A {@link Set} of {@link MenuType}s. + */ + private static Set<MenuType> defineParentTypes() { + HashSet<MenuType> types = new HashSet<>(); + types.add(optionGroup); + types.add(section); + types.add(menu); + types.add(toolBar); + return types; + } + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java new file mode 100644 index 0000000..cca1bf0 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/MenuManager.java
@@ -0,0 +1,339 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu; + +import java.awt.Component; +import java.lang.ref.WeakReference; +import java.net.URI; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JToolBar; + +import uk.org.taverna.scufl2.api.core.Workflow; +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.ui.menu.MenuComponent.MenuType; +import net.sf.taverna.t2.ui.menu.MenuManager.MenuManagerEvent; + +/** + * Create {@link JMenuBar}s and {@link JToolBar}s based on SPI instances of + * {@link MenuComponent}. + * <p> + * Elements of menus are discovered automatically using an {@link SPIRegistry}. + * The elements specify their internal relationship through + * {@link MenuComponent#getParentId()} and + * {@link MenuComponent#getPositionHint()}. {@link MenuComponent#getType()} + * specifies how the component is to be rendered or grouped. + * <p> + * The menu manager is {@link Observable}, you can + * {@linkplain #addObserver(Observer) add an observer} to be notified when the + * menus have changed, i.e. when {@link #update()} has been called, for instance + * when the {@link SPIRegistry} (which the menu manager observes) has been + * updated due to a plugin installation. + * <p> + * {@link #createMenuBar()} creates the default menu bar, ie. the menu bar + * containing all the items with {@link DefaultMenuBar#DEFAULT_MENU_BAR} as + * their parent. Alternate menu bars can be created using + * {@link #createMenuBar(URI)}. + * <p> + * Similary {@link #createToolBar()} creates the default tool bar, containing + * the items that has {@link DefaultToolBar#DEFAULT_TOOL_BAR} as their parent. + * Alternate toolbars can be created using {@link #createToolBar(URI)}. + * <p> + * The menu manager keeps weak references to the created (published) menu bars + * and tool bars, and will attempt to update them when {@link #update()} is + * called. + * <p> + * See the package level documentation for more information about how to specify + * menu elements. + * + * @author Stian Soiland-Reyes + */ +public interface MenuManager extends Observable<MenuManagerEvent> { + /** + * Add the items from the list of menu items to the parent menu with + * expansion sub-menus if needed. + * <p> + * If the list contains more than <tt>maxItemsInMenu</tt> items, a series of + * sub-menus will be created and added to the parentMenu instead, each + * containing a maximum of <tt>maxItemsInMenu</tt> items. (Note that if + * menuItems contains more than <tt>maxItemsInMenu*maxItemsInMenu</tt> + * items, there might be more than <tt>maxItemsInMenu</tt> sub-menus added + * to the parent). + * <p> + * The sub-menus are titled according to the {@link JMenuItem#getText()} of + * the first and last menu item it contains - assuming that they are already + * sorted. + * <p> + * The optional {@link ComponentFactory} headerItemFactory, if not + * <code>null</code>, will be invoked to create a header item that will be + * inserted on top of the sub-menus. This item does not count towards + * <tt>maxItemsInMenu</tt>. + * <p> + * Note that this is a utility method that does not mandate the use of the + * {@link MenuManager} structure for the menu. + * + * @param menuItems + * {@link JMenuItem}s to be inserted + * @param parentMenu + * Menu to insert items to + * @param maxItemsInMenu + * Maximum number of items in parent menu or created sub-menus + * @param headerItemFactory + * If not <code>null</code>, a {@link ComponentFactory} to create + * a header item to insert at top of created sub-menus + */ + abstract void addMenuItemsWithExpansion(List<JMenuItem> menuItems, + JMenu parentMenu, int maxItemsInMenu, + ComponentFactory headerItemFactory); + + /** + * Create a contextual menu for a selected object. + * <p> + * Items for the contextual menues are discovered in a similar to fashion as + * with {@link #createMenuBar()}, but using {@link DefaultContextualMenu} as + * the root. + * <p> + * Additionally, items implementing {@link ContextualMenuComponent} will be + * {@linkplain ContextualMenuComponent#setContextualSelection(Object, Object, Component) + * informed} about what is the current selection, as passed to this method. + * <p> + * Thus, the items can choose if they want to be + * {@link MenuComponent#isEnabled() visible} or not for a given selection, + * and return an action that is bound it to the selection. + * + * @param parent + * The parent object of the selected object, for instance a + * {@link Workflow}. + * @param selection + * The selected object which actions in the contextual menu + * relate to, for instance a {@link Processor} + * @param relativeToComponent + * A UI component which the returned {@link JPopupMenu} (and it's + * actions) is to be relative to, for instance as a parent of + * pop-up dialogues. + * @return An empty or populated {@link JPopupMenu} depending on the + * selected objects. + */ + abstract JPopupMenu createContextMenu(Object parent, Object selection, + Component relativeToComponent); + + /** + * Create the {@link JMenuBar} containing menu elements defining + * {@link DefaultMenuBar#DEFAULT_MENU_BAR} as their + * {@linkplain MenuComponent#getParentId() parent}. + * <p> + * A {@linkplain WeakReference weak reference} is kept in the menu manager + * to update the menubar if {@link #update()} is called (manually or + * automatically when the SPI is updated). + * + * @return A {@link JMenuBar} populated with the items belonging to the + * default menu bar + */ + abstract JMenuBar createMenuBar(); + + /** + * Create the {@link JMenuBar} containing menu elements defining the given + * <code>id</code> as their {@linkplain MenuComponent#getParentId() parent}. + * <p> + * Note that the parent itself also needs to exist as a registered SPI + * instance og {@link MenuComponent#getType()} equal to + * {@link MenuType#menu}, for instance by subclassing {@link AbstractMenu}. + * <p> + * A {@linkplain WeakReference weak reference} is kept in the menu manager + * to update the menubar if {@link #update()} is called (manually or + * automatically when the SPI is updated). + * + * @param id + * The {@link URI} identifying the menu bar + * @return A {@link JMenuBar} populated with the items belonging to the + * given parent id. + */ + abstract JMenuBar createMenuBar(URI id); + + /** + * Create the {@link JToolBar} containing elements defining + * {@link DefaultToolBar#DEFAULT_TOOL_BAR} as their + * {@linkplain MenuComponent#getParentId() parent}. + * <p> + * A {@linkplain WeakReference weak reference} is kept in the menu manager + * to update the toolbar if {@link #update()} is called (manually or + * automatically when the SPI is updated). + * + * @return A {@link JToolBar} populated with the items belonging to the + * default tool bar + */ + abstract JToolBar createToolBar(); + + /** + * Create the {@link JToolBar} containing menu elements defining the given + * <code>id</code> as their {@linkplain MenuComponent#getParentId() parent}. + * <p> + * Note that the parent itself also needs to exist as a registered SPI + * instance of {@link MenuComponent#getType()} equal to + * {@link MenuType#toolBar}, for instance by subclassing + * {@link AbstractToolBar}. + * <p> + * A {@linkplain WeakReference weak reference} is kept in the menu manager + * to update the toolbar if {@link #update()} is called (manually or + * automatically when the SPI is updated). + * + * @param id + * The {@link URI} identifying the tool bar + * @return A {@link JToolBar} populated with the items belonging to the + * given parent id. + */ + abstract JToolBar createToolBar(URI id); + + /** + * Get a menu item identified by the given URI. + * <p> + * Return the UI {@link Component} last created for a {@link MenuComponent}, + * through {@link #createMenuBar()}, {@link #createMenuBar(URI)}, + * {@link #createToolBar()} or {@link #createToolBar(URI)}. + * <p> + * For instance, if {@link #createMenuBar()} created a menu bar containing a + * "File" menu with {@link MenuComponent#getId() getId()} == + * <code>http://example.com/menu#file</code>, calling: + * + * <pre> + * Component fileMenu = getComponentByURI(URI + * .create("http://example.com/menu#file")); + * </pre> + * + * would return the {@link JMenu} last created for "File". Note that "last + * created" could mean both the last call to {@link #createMenuBar()} and + * last call to {@link #update()} - which could have happened because the + * SPI registry was updated. To be notified when + * {@link #getComponentByURI(URI)} might return a new Component because the + * menues have been reconstructed, {@linkplain #addObserver(Observer) add an + * observer} to the MenuManager. + * <p> + * If the URI is unknown, has not yet been rendered as a {@link Component}, + * or the Component is no longer in use outside the menu manager's + * {@linkplain WeakReference weak references}, <code>null</code> is returned + * instead. + * + * @see #getURIByComponent(Component) + * @param id + * {@link URI} of menu item as returned by + * {@link MenuComponent#getId()} + * @return {@link Component} as previously generated by + * {@link #createMenuBar()}/{@link #createToolBar()}, or + * <code>null</code> if the URI is unknown, or if the + * {@link Component} no longer exists. + */ + public abstract Component getComponentByURI(URI id); + + /** + * Get the URI of the {@link MenuComponent} this menu/toolbar + * {@link Component} was created from. + * <p> + * If the component was created by the MenuManager, through + * {@link #createMenuBar()}, {@link #createMenuBar(URI)}, + * {@link #createToolBar()} or {@link #createToolBar(URI)}, the URI + * identifying the defining {@link MenuComponent} is returned. This will be + * the same URI as returned by {@link MenuComponent#getId()}. + * <p> + * Note that if {@link #update()} has been invoked, the {@link MenuManager} + * might have rebuilt the menu structure and replaced the components since + * the given <code>component</code> was created. The newest + * {@link Component} for the given URI can be retrieved using + * {@link #getComponentByURI(URI)}. + * <p> + * If the component is unknown, <code>null</code> is returned instead. + * + * @see #getComponentByURI(URI) + * @param component + * {@link Component} that was previously created by the + * {@link MenuManager} + * @return {@link URI} identifying the menu component, as returned by + * {@link MenuComponent#getId()}, or <code>null</code> if the + * component is unknown. + */ + abstract URI getURIByComponent(Component component); + + /** + * Update and rebuild the menu structure. + * <p> + * Rebuild menu structure as defined by the {@link MenuComponent}s retrieved + * from the MenuComponent {@link SPIRegistry}. + * <p> + * Rebuilds previously published menubars and toolbars created with + * {@link #createMenuBar()}, {@link #createMenuBar(URI)}, + * {@link #createToolBar()} and {@link #createToolBar(URI)}. Note that the + * rebuild will do a removeAll() on the menubar/toolbar, so all components + * will be reconstructed. You can use {@link #getComponentByURI(URI)} to + * look up individual components within the menu and toolbars. + * <p> + * Note that the menu manager is observing the {@link SPIRegistry}, so if a + * plugin gets installed and the SPI registry is updated, this update method + * will be called by the SPI registry observer. + * <p> + * If there are several concurrent calls to {@link #update()}, the calls + * from the other thread will return immediately, while the first thread to + * get the synchronization lock on the menu manager will do the actual + * update. If you want to ensure that {@link #update()} does not return + * before the update has been performed fully, synchronize on the menu + * manager: + * + * <pre> + * MenuManager menuManager = MenuManager.getInstance(); + * synchronized (menuManager) { + * menuManager.update(); + * } + * doSomethingAfterUpdateFinished(); + * </pre> + */ + abstract void update(); + + /** + * Abstract class for events sent to {@linkplain Observer observers} of the + * menu manager. + * + * @see UpdatedMenuManagerEvent + * @author Stian Soiland-Reyes + */ + static abstract class MenuManagerEvent { + } + + /** + * Event sent to observers registered by + * {@link MenuManager#addObserver(Observer)} when the menus have been + * updated, i.e. when {@link MenuManager#update()} has been called. + */ + static class UpdatedMenuManagerEvent extends MenuManagerEvent { + } + + /** + * A factory for making {@link Component}s, in particular for making headers + * (like {@link JLabel}s) for + * {@link MenuManager#addMenuItemsWithExpansion(List, JMenu, int, ComponentFactory)} + */ + interface ComponentFactory { + public Component makeComponent(); + } +}
diff --git a/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java new file mode 100644 index 0000000..4c86db5 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/java/net/sf/taverna/t2/ui/menu/package-info.java
@@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +/** + * An {@link net.sf.taverna.t2.spi.SPIRegistry SPI} based system for creating + * {@link javax.swing.JMenuBar menues} and {@link javax.swing.JToolBar toolbars}. + * <p> + * Each element of a menu and/or toolbar is created by making an SPI + * implementation class of {@link net.sf.taverna.t2.ui.menu.MenuComponent} and listing the fully qualified + * class name in the SPI description resource file + * <code>/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent</code> + * </p> + * <p> + * The {@link net.sf.taverna.t2.ui.menu.MenuManager} discovers all menu components using an SPI registry, + * and builds the {@link javax.swing.JMenuBar menu bar} using + * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar()} or the + * {@link javax.swing.JToolBar toolbar} using + * {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar()}. + * </p> + * <p> + * This allows plugins to provide actions (menu items) and submenues that can be + * inserted at any points in the generated menu. All parts of the menues are + * described through a parent/child relationship using + * {@link net.sf.taverna.t2.ui.menu.MenuComponent#getId()} and {@link net.sf.taverna.t2.ui.menu.MenuComponent#getParentId()}. The + * components are identified using {@link java.net.URI}s to avoid compile time + * dependencies, so a plugin can for instance add something to the existing + * "Edit" menu without depending on the actual implementation of the + * {@link net.sf.taverna.t2.ui.menu.MenuComponent} describing "Edit", as long as it refers to the same + * URI. The use of URIs instead of pure strings is to encourage the use of + * unique identifiers, for instance plugins should use an URI base that is + * derived from their package name to avoid collision with other plugins. + * </p> + * <p> + * A set of abstract classes, with a common parent {@link net.sf.taverna.t2.ui.menu.AbstractMenuItem}, + * make it more convenient to create simple SPI implementations. Two default top + * level implementations {@link net.sf.taverna.t2.ui.menu.DefaultMenuBar} and {@link net.sf.taverna.t2.ui.menu.DefaultToolBar} can + * be used as parents for items that are to be included in + * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar()} and {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar()}, + * but it's possible to have other parents - such menu trees would have to be + * created by providing the URI of the top level parent to + * {@link net.sf.taverna.t2.ui.menu.MenuManager#createMenuBar(URI)} or + * {@link net.sf.taverna.t2.ui.menu.MenuManager#createToolBar(URI)}. + * </p> + * <p> + * In the simplest form a menu structure can be built by subclassing + * {@link net.sf.taverna.t2.ui.menu.AbstractMenu} and {@link net.sf.taverna.t2.ui.menu.AbstractMenuAction}, but more complex menus + * can be built by including submenus (AbstractMenu with an AbstractMenu as a + * parent), grouping similar actions in a {@link net.sf.taverna.t2.ui.menu.AbstractMenuSection section}, + * or making {@link net.sf.taverna.t2.ui.menu.AbstractMenuToggle toggle actions} and + * {@link net.sf.taverna.t2.ui.menu.AbstractMenuOptionGroup option groups}. You can add arbitrary "real" + * {@link javax.swing.JMenuBar} / {@link javax.swing.JToolBar} compatible items + * (such as {@link javax.swing.JMenu}s, {@link javax.swing.JMenuItem}s and + * {@link javax.swing.JButton}s) using + * {@link net.sf.taverna.t2.ui.menu.AbstractMenuCustom custom menu items}. + * </p> + * + * <p> + * Example showing how <code>File->Open</code> could be implemented using + * two SPI implementations net.sf.taverna.t2.ui.perspectives.hello.FileMenu and + * net.sf.taverna.t2.ui.perspectives.hello.FileOpenAction: + * </p> + * + * <pre> + * package net.sf.taverna.t2.ui.perspectives.hello; + * + * import java.net.URI; + * + * import net.sf.taverna.t2.ui.menu.AbstractMenu; + * import net.sf.taverna.t2.ui.menu.DefaultMenuBar; + * + * public class FileMenu extends AbstractMenu { + * + * private static final URI FILE_URI = URI + * .create("http://taverna.sf.net/2008/t2workbench/test#file"); + * + * public FileMenu() { + * super(DefaultMenuBar.DEFAULT_MENU_BAR, 10, FILE_URI, "File"); + * } + * + * } + * </pre> + * <pre> + * package net.sf.taverna.t2.ui.perspectives.hello; + * + * import java.awt.event.ActionEvent; + * import java.net.URI; + * + * import javax.swing.AbstractAction; + * import javax.swing.Action; + * import javax.swing.JOptionPane; + * + * import net.sf.taverna.t2.ui.menu.AbstractMenuAction; + * + * public class FileOpenAction extends AbstractMenuAction { + * public FileOpenAction() { + * super(URI.create("http://taverna.sf.net/2008/t2workbench/test#file"), + * 20); + * } + * + * @Override + * public Action createAction() { + * return new AbstractAction("Open") { + * public void actionPerformed(ActionEvent arg0) { + * JOptionPane.showMessageDialog(null, "Open"); + * } + * }; + * } + * } + * </pre> + * + * <p> + * The implementation of the {@link net.sf.taverna.t2.ui.menu.MenuManager} itself is discovered by an + * internal SPI registry through {@link net.sf.taverna.t2.ui.menu.MenuManager#getInstance()}. The menu + * manager is observing the SPI registry, so that any updates to the registry + * from installing plugins etc. are reflected in an automatic rebuild of the + * menus. This update can also be triggered manually by calling + * {@link net.sf.taverna.t2.ui.menu.MenuManager#update()}. + * </p> + * + * @author Stian Soiland-Reyes + * + */ +package net.sf.taverna.t2.ui.menu; +
diff --git a/taverna-workbench-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..c137386 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,4 @@ +net.sf.taverna.t2.ui.menu.DefaultMenuBar +net.sf.taverna.t2.ui.menu.DefaultToolBar +net.sf.taverna.t2.ui.menu.DefaultContextualMenu +
diff --git a/taverna-workbench-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml b/taverna-workbench-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml new file mode 100644 index 0000000..b27e860 --- /dev/null +++ b/taverna-workbench-menu-api/src/main/resources/META-INF/spring/menu-api-context-osgi.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="DefaultMenuBar" auto-export="interfaces" /> + <service ref="DefaultToolBar" auto-export="interfaces" /> + <service ref="DefaultContextualMenu" auto-export="interfaces" /> + +</beans:beans>
diff --git a/taverna-workbench-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml b/taverna-workbench-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml new file mode 100644 index 0000000..734dbbe --- /dev/null +++ b/taverna-workbench-menu-api/src/main/resources/META-INF/spring/menu-api-context.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="DefaultMenuBar" class="net.sf.taverna.t2.ui.menu.DefaultMenuBar" /> + <bean id="DefaultToolBar" class="net.sf.taverna.t2.ui.menu.DefaultToolBar" /> + <bean id="DefaultContextualMenu" class="net.sf.taverna.t2.ui.menu.DefaultContextualMenu" /> + +</beans>
diff --git a/taverna-workbench-menu-impl/pom.xml b/taverna-workbench-menu-impl/pom.xml new file mode 100644 index 0000000..d95bf49 --- /dev/null +++ b/taverna-workbench-menu-impl/pom.xml
@@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-impl</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>menu-impl</artifactId> + <packaging>bundle</packaging> + <name>Menu generation implementation</name> + <description>The main workbench ui</description> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Embed-Dependency>javahelp</Embed-Dependency> + <Import-Package>org.jdesktop.jdic.browser;resolution:=optional,*</Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>helper-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + + <dependency> + <groupId>javax.help</groupId> + <artifactId>javahelp</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java new file mode 100644 index 0000000..ee1fd3d --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/ui/menu/impl/MenuManagerImpl.java
@@ -0,0 +1,880 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu.impl; + +import static java.lang.Math.min; +import static javax.help.CSH.setHelpIDString; +import static javax.swing.Action.NAME; +import static javax.swing.Action.SHORT_DESCRIPTION; +import static net.sf.taverna.t2.lang.ui.ShadedLabel.GREEN; +import static net.sf.taverna.t2.ui.menu.AbstractMenuSection.SECTION_COLOR; +import static net.sf.taverna.t2.ui.menu.DefaultContextualMenu.DEFAULT_CONTEXT_MENU; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; +import static net.sf.taverna.t2.ui.menu.DefaultToolBar.DEFAULT_TOOL_BAR; + +import java.awt.Color; +import java.awt.Component; +import java.lang.ref.WeakReference; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.WeakHashMap; + +import javax.swing.AbstractButton; +import javax.swing.Action; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.lang.observer.MultiCaster; +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.lang.ui.ShadedLabel; +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.ui.menu.AbstractMenuOptionGroup; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.ui.menu.DesignOrResultsAction; +import net.sf.taverna.t2.ui.menu.MenuComponent; +import net.sf.taverna.t2.ui.menu.MenuComponent.MenuType; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; + +import org.apache.log4j.Logger; + +/** + * Implementation of {@link MenuManager}. + * + * @author Stian Soiland-Reyes + */ +public class MenuManagerImpl implements MenuManager { + private static Logger logger = Logger.getLogger(MenuManagerImpl.class); + + private boolean needsUpdate; + /** + * Cache used by {@link #getURIByComponent(Component)} + */ + private WeakHashMap<Component, URI> componentToUri; + /** + * {@link MenuElementComparator} used for sorting menu components from the + * SPI registry. + */ + private MenuElementComparator menuElementComparator = new MenuElementComparator(); + /** + * Map of {@link URI} to it's discovered children. Populated by + * {@link #findChildren()}. + */ + private HashMap<URI, List<MenuComponent>> menuElementTree; + /** + * Multicaster to distribute messages to {@link Observer}s of this menu + * manager. + */ + private MultiCaster<MenuManagerEvent> multiCaster; + /** + * Lock for {@link #update()} + */ + private final Object updateLock = new Object(); + /** + * True if {@link #doUpdate()} is running, subsequents call to + * {@link #update()} will return immediately. + */ + private boolean updating; + /** + * Cache used by {@link #getComponentByURI(URI)} + */ + private Map<URI, WeakReference<Component>> uriToComponent; + /** + * Map from {@link URI} to defining {@link MenuComponent}. Children are in + * {@link #menuElementTree}. + */ + private Map<URI, MenuComponent> uriToMenuElement; + // Note: Not reset by #resetCollections() + private Map<URI, List<WeakReference<Component>>> uriToPublishedComponents = new HashMap<>(); + private List<MenuComponent> menuComponents = new ArrayList<>(); + + /** + * Construct the MenuManagerImpl. Observes the SPI registry and does an + * initial {@link #update()}. + */ + public MenuManagerImpl() { + multiCaster = new MultiCaster<>(this); + needsUpdate = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void addMenuItemsWithExpansion(List<JMenuItem> menuItems, + JMenu parentMenu, int maxItemsInMenu, + ComponentFactory headerItemFactory) { + if (menuItems.size() <= maxItemsInMenu) { + // Just add them directly + for (JMenuItem menuItem : menuItems) + parentMenu.add(menuItem); + return; + } + int index = 0; + while (index < menuItems.size()) { + int toIndex = min(menuItems.size(), index + maxItemsInMenu); + if (toIndex == menuItems.size() - 1) + // Don't leave a single item left for the last subMenu + toIndex--; + List<JMenuItem> subList = menuItems.subList(index, toIndex); + JMenuItem firstItem = subList.get(0); + JMenuItem lastItem = subList.get(subList.size() - 1); + JMenu subMenu = new JMenu(firstItem.getText() + " ... " + + lastItem.getText()); + if (headerItemFactory != null) + subMenu.add(headerItemFactory.makeComponent()); + for (JMenuItem menuItem : subList) + subMenu.add(menuItem); + parentMenu.add(subMenu); + index = toIndex; + } + } + + @Override + public void addObserver(Observer<MenuManagerEvent> observer) { + multiCaster.addObserver(observer); + } + + @Override + public JPopupMenu createContextMenu(Object parent, Object selection, + Component relativeToComponent) { + ContextualSelection contextualSelection = new ContextualSelection( + parent, selection, relativeToComponent); + JPopupMenu popupMenu = new JPopupMenu(); + populateContextMenu(popupMenu, DEFAULT_CONTEXT_MENU, + contextualSelection); + registerComponent(DEFAULT_CONTEXT_MENU, popupMenu, true); + return popupMenu; + } + + @Override + public JMenuBar createMenuBar() { + return createMenuBar(DEFAULT_MENU_BAR); + } + + @Override + public JMenuBar createMenuBar(URI id) { + JMenuBar menuBar = new JMenuBar(); + if (needsUpdate) + update(); + populateMenuBar(menuBar, id); + registerComponent(id, menuBar, true); + return menuBar; + } + + @Override + public JToolBar createToolBar() { + return createToolBar(DEFAULT_TOOL_BAR); + } + + @Override + public JToolBar createToolBar(URI id) { + JToolBar toolbar = new JToolBar(); + if (needsUpdate) + update(); + populateToolBar(toolbar, id); + registerComponent(id, toolbar, true); + return toolbar; + } + + @Override + public synchronized Component getComponentByURI(URI id) { + WeakReference<Component> componentRef = uriToComponent.get(id); + if (componentRef == null) + return null; + // Might also be null it reference has gone dead + return componentRef.get(); + } + + @Override + public List<Observer<MenuManagerEvent>> getObservers() { + return multiCaster.getObservers(); + } + + @Override + public synchronized URI getURIByComponent(Component component) { + return componentToUri.get(component); + } + + @Override + public void removeObserver(Observer<MenuManagerEvent> observer) { + multiCaster.removeObserver(observer); + } + + @Override + public void update() { + synchronized (updateLock) { + if (updating && !needsUpdate) + return; + updating = true; + } + try { + doUpdate(); + } finally { + synchronized (updateLock) { + updating = false; + needsUpdate = false; + } + } + } + + public void update(Object service, Map<?, ?> properties) { + needsUpdate = true; + update(); + } + + /** + * Add a {@link JMenu} to the list of components as described by the menu + * component. If there are no children, the menu is not added. + * + * @param components + * List of components where to add the created {@link JMenu} + * @param menuComponent + * The {@link MenuComponent} definition for this menu + * @param isToolbar + * True if the list of components is to be added to a toolbar + */ + private void addMenu(List<Component> components, + MenuComponent menuComponent, MenuOptions menuOptions) { + URI menuId = menuComponent.getId(); + if (menuOptions.isToolbar()) { + logger.warn("Can't have menu " + menuComponent + + " within toolBar element"); + return; + } + MenuOptions childOptions = new MenuOptions(menuOptions); + List<Component> subComponents = makeComponents(menuId, childOptions); + if (subComponents.isEmpty()) { + logger.warn("No sub components found for menu " + menuId); + return; + } + + JMenu menu = new JMenu(menuComponent.getAction()); + for (Component menuItem : subComponents) + if (menuItem == null) + menu.addSeparator(); + else + menu.add(menuItem); + registerComponent(menuId, menu); + components.add(menu); + } + + /** + * Add <code>null</code> to the list of components, meaning that a separator + * is to be created. Subsequent separators are ignored, and if there are no + * components on the list already no separator will be added. + * + * @param components + * List of components + */ + private void addNullSeparator(List<Component> components) { + if (components.isEmpty()) + // Don't start with a separator + return; + if (components.get(components.size() - 1) == null) + // Already a separator in last position + return; + components.add(null); + } + + /** + * Add an {@link AbstractMenuOptionGroup option group} to the list of + * components + * + * @param components + * List of components where to add the created {@link JMenu} + * @param optionGroupId + * The {@link URI} identifying the option group + * @param isToolbar + * True if the option group is to be added to a toolbar + */ + private void addOptionGroup(List<Component> components, URI optionGroupId, + MenuOptions menuOptions) { + MenuOptions childOptions = new MenuOptions(menuOptions); + childOptions.setOptionGroup(true); + + List<Component> buttons = makeComponents(optionGroupId, childOptions); + addNullSeparator(components); + if (buttons.isEmpty()) { + logger.warn("No sub components found for option group " + + optionGroupId); + return; + } + ButtonGroup buttonGroup = new ButtonGroup(); + + for (Component button : buttons) { + if (button instanceof AbstractButton) + buttonGroup.add((AbstractButton) button); + else + logger.warn("Component of button group " + optionGroupId + + " is not an AbstractButton: " + button); + if (button == null) { + logger.warn("Separator found within button group"); + addNullSeparator(components); + } else + components.add(button); + } + addNullSeparator(components); + } + + /** + * Add a section to a list of components. + * + * @param components + * List of components + * @param sectionId + * The {@link URI} identifying the section + * @param menuOptions + * {@link MenuOptions options} for creating the menu + */ + private void addSection(List<Component> components, URI sectionId, + MenuOptions menuOptions) { + List<Component> childComponents = makeComponents(sectionId, menuOptions); + + MenuComponent sectionDef = uriToMenuElement.get(sectionId); + addNullSeparator(components); + if (childComponents.isEmpty()) { + logger.warn("No sub components found for section " + sectionId); + return; + } + Action sectionAction = sectionDef.getAction(); + if (sectionAction != null) { + String sectionLabel = (String) sectionAction.getValue(NAME); + if (sectionLabel != null) { + // No separators before the label + stripTrailingNullSeparator(components); + Color labelColor = (Color) sectionAction.getValue(SECTION_COLOR); + if (labelColor == null) + labelColor = GREEN; + ShadedLabel label = new ShadedLabel(sectionLabel, labelColor); + components.add(label); + } + } + for (Component childComponent : childComponents) + if (childComponent == null) { + logger.warn("Separator found within section " + sectionId); + addNullSeparator(components); + } else + components.add(childComponent); + addNullSeparator(components); + } + + /** + * Remove the last <code>null</code> separator from the list of components + * if it's present. + * + * @param components + * List of components + */ + private void stripTrailingNullSeparator(List<Component> components) { + if (!components.isEmpty()) { + int lastIndex = components.size() - 1; + if (components.get(lastIndex) == null) + components.remove(lastIndex); + } + } + + /** + * Perform the actual update, called by {@link #update()}. Reset all the + * collections, refresh from SPI, modify any previously published components + * and notify any observers. + */ + protected synchronized void doUpdate() { + resetCollections(); + findChildren(); + updatePublishedComponents(); + multiCaster.notify(new UpdatedMenuManagerEvent()); + } + + /** + * Find all children for all known menu components. Populates + * {@link #uriToMenuElement}. + * + */ + protected void findChildren() { + for (MenuComponent menuElement : menuComponents) { + uriToMenuElement.put(menuElement.getId(), menuElement); + logger.debug("Found menu element " + menuElement.getId() + " " + + menuElement); + if (menuElement.getParentId() == null) + continue; + List<MenuComponent> siblings = menuElementTree.get(menuElement + .getParentId()); + if (siblings == null) { + siblings = new ArrayList<>(); + synchronized (menuElementTree) { + menuElementTree.put(menuElement.getParentId(), siblings); + } + } + siblings.add(menuElement); + } +// if (uriToMenuElement.isEmpty()) { +// logger.error("No menu elements found, check classpath/Raven/SPI"); +// } + } + + /** + * Get the children which have the given URI specified as their parent, or + * an empty list if no children exist. + * + * @param id + * The {@link URI} of the parent + * @return The {@link List} of {@link MenuComponent} which have the given + * parent + */ + protected List<MenuComponent> getChildren(URI id) { + List<MenuComponent> children = null; + synchronized (menuElementTree) { + children = menuElementTree.get(id); + if (children != null) + children = new ArrayList<>(children); + } + if (children == null) + children = Collections.<MenuComponent> emptyList(); + else + Collections.sort(children, menuElementComparator); + return children; + } + + /** + * Make the list of Swing {@link Component}s that are the children of the + * given {@link URI}. + * + * @param id + * The {@link URI} of the parent which children are to be made + * @param menuOptions + * Options of the created menu, for instance + * {@link MenuOptions#isToolbar()}. + * @return A {@link List} of {@link Component}s that can be added to a + * {@link JMenuBar}, {@link JMenu} or {@link JToolBar}. + */ + protected List<Component> makeComponents(URI id, MenuOptions menuOptions) { + List<Component> components = new ArrayList<>(); + for (MenuComponent childElement : getChildren(id)) { + if (childElement instanceof ContextualMenuComponent) + ((ContextualMenuComponent) childElement) + .setContextualSelection(menuOptions + .getContextualSelection()); + /* + * Important - check this AFTER setContextualSelection so the item + * can change it's enabled-state if needed. + */ + if (!childElement.isEnabled()) + continue; + MenuType type = childElement.getType(); + Action action = childElement.getAction(); + URI childId = childElement.getId(); + if (type.equals(MenuType.action)) { + if (action == null) { + logger.warn("Skipping invalid action " + childId + " for " + + id); + continue; + } + + Component actionComponent; + if (menuOptions.isOptionGroup()) { + if (menuOptions.isToolbar()) { + actionComponent = new JToggleButton(action); + toolbarizeButton((AbstractButton) actionComponent); + } else + actionComponent = new JRadioButtonMenuItem(action); + } else { + if (menuOptions.isToolbar()) { + actionComponent = new JButton(action); + toolbarizeButton((AbstractButton) actionComponent); + } else + actionComponent = new JMenuItem(action); + } + registerComponent(childId, actionComponent); + components.add(actionComponent); + } else if (type.equals(MenuType.toggle)) { + if (action == null) { + logger.warn("Skipping invalid toggle " + childId + " for " + + id); + continue; + } + Component toggleComponent; + if (menuOptions.isToolbar()) + toggleComponent = new JToggleButton(action); + else + toggleComponent = new JCheckBoxMenuItem(action); + registerComponent(childId, toggleComponent); + components.add(toggleComponent); + } else if (type.equals(MenuType.custom)) { + Component customComponent = childElement.getCustomComponent(); + if (customComponent == null) { + logger.warn("Skipping null custom component " + childId + + " for " + id); + continue; + } + registerComponent(childId, customComponent); + components.add(customComponent); + } else if (type.equals(MenuType.optionGroup)) + addOptionGroup(components, childId, menuOptions); + else if (type.equals(MenuType.section)) + addSection(components, childId, menuOptions); + else if (type.equals(MenuType.menu)) + addMenu(components, childElement, menuOptions); + else { + logger.warn("Skipping invalid/unknown type " + type + " for " + + id); + continue; + } + } + stripTrailingNullSeparator(components); + return components; + } + + /** + * Fill the specified menu bar with the menu elements that have the given + * URI as their parent. + * <p> + * Existing elements on the menu bar will be removed. + * + * @param menuBar + * The {@link JMenuBar} to update + * @param id + * The {@link URI} of the menu bar + */ + protected void populateMenuBar(JMenuBar menuBar, URI id) { + menuBar.removeAll(); + MenuComponent menuDef = uriToMenuElement.get(id); + if (menuDef == null) + throw new IllegalArgumentException("Unknown menuBar " + id); + if (!menuDef.getType().equals(MenuType.menu)) + throw new IllegalArgumentException("Element " + id + + " is not a menu, but a " + menuDef.getType()); + MenuOptions menuOptions = new MenuOptions(); + for (Component component : makeComponents(id, menuOptions)) + if (component == null) + logger.warn("Ignoring separator in menu bar " + id); + else + menuBar.add(component); + } + + /** + * Fill the specified menu bar with the menu elements that have the given + * URI as their parent. + * <p> + * Existing elements on the menu bar will be removed. + * + * @param popupMenu + * The {@link JPopupMenu} to update + * @param id + * The {@link URI} of the menu bar + * @param contextualSelection + * The current selection for the context menu + */ + protected void populateContextMenu(JPopupMenu popupMenu, URI id, + ContextualSelection contextualSelection) { + popupMenu.removeAll(); + MenuComponent menuDef = uriToMenuElement.get(id); + if (menuDef == null) + throw new IllegalArgumentException("Unknown menuBar " + id); + if (!menuDef.getType().equals(MenuType.menu)) + throw new IllegalArgumentException("Element " + id + + " is not a menu, but a " + menuDef.getType()); + MenuOptions menuOptions = new MenuOptions(); + menuOptions.setContextualSelection(contextualSelection); + for (Component component : makeComponents(id, menuOptions)) + if (component == null) + popupMenu.addSeparator(); + else + popupMenu.add(component); + } + + /** + * Fill the specified tool bar with the elements that have the given URI as + * their parent. + * <p> + * Existing elements on the tool bar will be removed. + * + * @param toolbar + * The {@link JToolBar} to update + * @param id + * The {@link URI} of the tool bar + */ + protected void populateToolBar(JToolBar toolbar, URI id) { + toolbar.removeAll(); + MenuComponent toolbarDef = uriToMenuElement.get(id); + if (toolbarDef == null) + throw new IllegalArgumentException("Unknown toolBar " + id); + if (!toolbarDef.getType().equals(MenuType.toolBar)) + throw new IllegalArgumentException("Element " + id + + " is not a toolBar, but a " + toolbarDef.getType()); + if (toolbarDef.getAction() != null) { + String name = (String) toolbarDef.getAction().getValue(Action.NAME); + toolbar.setName(name); + } else + toolbar.setName(""); + MenuOptions menuOptions = new MenuOptions(); + menuOptions.setToolbar(true); + for (Component component : makeComponents(id, menuOptions)) { + if (component == null) { + toolbar.addSeparator(); + continue; + } + if (component instanceof JButton) { + JButton toolbarButton = (JButton) component; + toolbarButton.putClientProperty("hideActionText", true); + } + toolbar.add(component); + } + } + + /** + * Register a component that has been created. Such a component can be + * resolved through {@link #getComponentByURI(URI)}. + * + * @param id + * The {@link URI} that defined the component + * @param component + * The {@link Component} that was created. + */ + protected synchronized void registerComponent(URI id, Component component) { + registerComponent(id, component, false); + } + + /** + * Register a component that has been created. Such a component can be + * resolved through {@link #getComponentByURI(URI)}. + * + * @param id + * The {@link URI} that defined the component + * @param component + * The {@link Component} that was created. + * @param published + * <code>true</code> if the component has been published through + * {@link #createMenuBar()} or similar, and is to be + * automatically updated by later calls to {@link #update()}. + */ + protected synchronized void registerComponent(URI id, Component component, + boolean published) { + uriToComponent.put(id, new WeakReference<>(component)); + componentToUri.put(component, id); + if (published) { + List<WeakReference<Component>> publishedComponents = uriToPublishedComponents + .get(id); + if (publishedComponents == null) { + publishedComponents = new ArrayList<>(); + uriToPublishedComponents.put(id, publishedComponents); + } + publishedComponents.add(new WeakReference<>(component)); + } + setHelpStringForComponent(component, id); + } + + /** + * Reset all collections + * + */ + protected synchronized void resetCollections() { + menuElementTree = new HashMap<>(); + componentToUri = new WeakHashMap<>(); + uriToMenuElement = new HashMap<>(); + uriToComponent = new HashMap<>(); + } + + /** + * Set javax.help string to identify the component for later references to + * the help document. Note that the component (ie. the + * {@link AbstractMenuAction} must have an ID for an registration to take + * place. + * + * @param component + * The {@link Component} to set help string for + * @param componentId + * The {@link URI} to be used as identifier + */ + protected void setHelpStringForComponent(Component component, + URI componentId) { + if (componentId != null) { + String helpId = componentId.toASCIIString(); + setHelpIDString(component, helpId); + } + } + + /** + * Make an {@link AbstractButton} be configured in a "toolbar-like" way, for + * instance showing only the icon. + * + * @param actionButton + * Button to toolbarise + */ + protected void toolbarizeButton(AbstractButton actionButton) { + Action action = actionButton.getAction(); + if (action.getValue(SHORT_DESCRIPTION) == null) + action.putValue(SHORT_DESCRIPTION, action.getValue(NAME)); + actionButton.setBorder(new EmptyBorder(0, 2, 0, 2)); + // actionButton.setHorizontalTextPosition(JButton.CENTER); + // actionButton.setVerticalTextPosition(JButton.BOTTOM); + if (action.getValue(Action.SMALL_ICON) != null) { + // Don't show the text + actionButton.putClientProperty("hideActionText", true); + // Since hideActionText seems to be broken in Java 5 and/or OS X + actionButton.setText(null); + } + } + + /** + * Update all components that have been published using + * {@link #createMenuBar()} and similar. Content of such components will be + * removed and replaced by fresh components. + */ + protected void updatePublishedComponents() { + for (Entry<URI, List<WeakReference<Component>>> entry : uriToPublishedComponents + .entrySet()) + for (WeakReference<Component> reference : entry.getValue()) { + URI id = entry.getKey(); + Component component = reference.get(); + if (component == null) + continue; + if (component instanceof JToolBar) + populateToolBar((JToolBar) component, id); + else if (component instanceof JMenuBar) + populateMenuBar((JMenuBar) component, id); + else + logger.warn("Could not update published component " + id + + ": " + component.getClass()); + } + } + + public void setMenuComponents(List<MenuComponent> menuComponents) { + this.menuComponents = menuComponents; + } + + public void setSelectionManager(SelectionManager selectionManager) { + selectionManager.addObserver(new SelectionManagerObserver()); + } + + /** + * {@link Comparator} that can order {@link MenuComponent}s by their + * {@link MenuComponent#getPositionHint()}. + */ + protected static class MenuElementComparator implements + Comparator<MenuComponent> { + @Override + public int compare(MenuComponent a, MenuComponent b) { + return a.getPositionHint() - b.getPositionHint(); + } + } + + /** + * Various options for + * {@link MenuManagerImpl#makeComponents(URI, MenuOptions)} and friends. + * + * @author Stian Soiland-Reyes + */ + public static class MenuOptions { + private boolean isToolbar = false; + private boolean isOptionGroup = false; + private ContextualSelection contextualSelection = null; + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + public void setContextualSelection( + ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + } + + public MenuOptions(MenuOptions original) { + this.isOptionGroup = original.isOptionGroup(); + this.isToolbar = original.isToolbar(); + this.contextualSelection = original.getContextualSelection(); + } + + public MenuOptions() { + } + + @Override + protected MenuOptions clone() { + return new MenuOptions(this); + } + + public boolean isToolbar() { + return isToolbar; + } + + public void setToolbar(boolean isToolbar) { + this.isToolbar = isToolbar; + } + + public boolean isOptionGroup() { + return isOptionGroup; + } + + public void setOptionGroup(boolean isOptionGroup) { + this.isOptionGroup = isOptionGroup; + } + } + + private final class SelectionManagerObserver extends + SwingAwareObserver<SelectionManagerEvent> { + private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective"; + private static final String RESULTS_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.results.ResultsPerspective"; + + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, + SelectionManagerEvent message) { + if (!(message instanceof PerspectiveSelectionEvent)) + return; + handlePerspectiveSelect((PerspectiveSelectionEvent) message); + } + + private void handlePerspectiveSelect(PerspectiveSelectionEvent event) { + String perspectiveID = event.getSelectedPerspective().getID(); + boolean isDesign = DESIGN_PERSPECTIVE_ID.equals(perspectiveID); + boolean isResults = RESULTS_PERSPECTIVE_ID.equals(perspectiveID); + + for (MenuComponent menuComponent : menuComponents) + if (!(menuComponent instanceof ContextualMenuComponent)) { + Action action = menuComponent.getAction(); + if (action instanceof DesignOnlyAction) + action.setEnabled(isDesign); + else if (action instanceof DesignOrResultsAction) + action.setEnabled(isDesign || isResults); + } + } + } +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java new file mode 100644 index 0000000..9a2f37b --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/AdvancedMenu.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import static java.awt.event.KeyEvent.VK_A; +import static javax.swing.Action.MNEMONIC_KEY; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenu; + +public class AdvancedMenu extends AbstractMenu { + public static final URI ADVANCED_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#advanced"); + + public AdvancedMenu() { + super(DEFAULT_MENU_BAR, 1000, ADVANCED_URI, makeAction()); + } + + public static DummyAction makeAction() { + DummyAction action = new DummyAction("Advanced"); + action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_A)); + return action; + } +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java new file mode 100644 index 0000000..a15237c --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/EditMenu.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import static java.awt.event.KeyEvent.VK_E; +import static javax.swing.Action.MNEMONIC_KEY; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenu; + +public class EditMenu extends AbstractMenu { + public EditMenu() { + super(DEFAULT_MENU_BAR, 20, URI + .create("http://taverna.sf.net/2008/t2workbench/menu#edit"), + makeAction()); + } + + public static DummyAction makeAction() { + DummyAction action = new DummyAction("Edit"); + action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_E)); + return action; + } +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java new file mode 100644 index 0000000..6b6eb7c --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FeedbackMenuAction.java
@@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import static java.awt.Desktop.getDesktop; +import static net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu.HELP_URI; + +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; + +import org.apache.log4j.Logger; + +/** + * MenuItem for feedback + * + * @author alanrw + */ +public class FeedbackMenuAction extends AbstractMenuAction { + private static Logger logger = Logger.getLogger(FeedbackMenuAction.class); + + private static String FEEDBACK_URL = "http://www.taverna.org.uk/about/contact-us/feedback/"; + + public FeedbackMenuAction() { + super(HELP_URI, 20); + } + + @Override + protected Action createAction() { + return new FeedbackAction(); + } + + @SuppressWarnings("serial") + private final class FeedbackAction extends AbstractAction { + private FeedbackAction() { + super("Contact us"); + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + getDesktop().browse(new URI(FEEDBACK_URL)); + } catch (IOException e1) { + logger.error("Unable to open URL", e1); + } catch (URISyntaxException e1) { + logger.error("Invalid URL syntax", e1); + } + } + } + +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java new file mode 100644 index 0000000..61f963b --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/FileMenu.java
@@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import static java.awt.event.KeyEvent.VK_F; +import static javax.swing.Action.MNEMONIC_KEY; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenu; + +/** + * File menu + * + * @author Stian Soiland-Reyes + */ +public class FileMenu extends AbstractMenu { + public FileMenu() { + super(DEFAULT_MENU_BAR, 10, URI + .create("http://taverna.sf.net/2008/t2workbench/menu#file"), + makeAction()); + } + + public static DummyAction makeAction() { + DummyAction action = new DummyAction("File"); + action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_F)); + return action; + } +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java new file mode 100644 index 0000000..c4169cc --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/HelpMenu.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import static java.awt.event.KeyEvent.VK_H; +import static javax.swing.Action.MNEMONIC_KEY; +import static net.sf.taverna.t2.ui.menu.DefaultMenuBar.DEFAULT_MENU_BAR; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenu; + +public class HelpMenu extends AbstractMenu { + public static final URI HELP_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#help"); + + public HelpMenu() { + super(DEFAULT_MENU_BAR, 1024, HELP_URI, makeAction()); + } + + public static DummyAction makeAction() { + DummyAction action = new DummyAction("Help"); + action.putValue(MNEMONIC_KEY, Integer.valueOf(VK_H)); + return action; + } +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java new file mode 100644 index 0000000..d091c8e --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/OnlineHelpMenuAction.java
@@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import static java.awt.event.KeyEvent.VK_F1; +import static javax.swing.KeyStroke.getKeyStroke; +import static net.sf.taverna.t2.workbench.helper.Helper.displayDefaultHelp; +import static net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu.HELP_URI; + +import java.awt.AWTEvent; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; + +/** + * MenuItem for help + * + * @author alanrw + */ +public class OnlineHelpMenuAction extends AbstractMenuAction { + public OnlineHelpMenuAction() { + super(HELP_URI, 10); + } + + @Override + protected Action createAction() { + return new OnlineHelpAction(); + } + + @SuppressWarnings("serial") + private final class OnlineHelpAction extends AbstractAction { + private OnlineHelpAction() { + super("Online help"); + putValue(ACCELERATOR_KEY, getKeyStroke(VK_F1, 0)); + + } + + /** + * When selected, use the Helper to display the default help. + */ + @Override + public void actionPerformed(ActionEvent e) { + displayDefaultHelp((AWTEvent) e); + // TODO change helper to bean? + } + } +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java new file mode 100644 index 0000000..308d51d --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ShowLogsAndDataMenuAction.java
@@ -0,0 +1,89 @@ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import static java.lang.Runtime.getRuntime; +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.showInputDialog; +import static net.sf.taverna.t2.workbench.MainWindow.getMainWindow; +import static net.sf.taverna.t2.workbench.ui.impl.menu.AdvancedMenu.ADVANCED_URI; + +import java.awt.event.ActionEvent; +import java.io.File; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; + +import org.apache.log4j.Logger; + +import uk.org.taverna.configuration.app.ApplicationConfiguration; + +public class ShowLogsAndDataMenuAction extends AbstractMenuAction { + private static final String OPEN = "open"; + private static final String EXPLORER = "explorer"; + // TODO Consider using xdg-open instead of gnome-open + private static final String GNOME_OPEN = "gnome-open"; + private static final String WINDOWS = "Windows"; + private static final String MAC_OS_X = "Mac OS X"; + + private ApplicationConfiguration applicationConfiguration; + + public ShowLogsAndDataMenuAction() { + super(ADVANCED_URI, 200); + } + + private static Logger logger = Logger.getLogger(ShowLogsAndDataMenuAction.class); + + @Override + protected Action createAction() { + return new AbstractAction("Show logs and data folder") { + private static final long serialVersionUID = 1L; + + @Override + public void actionPerformed(ActionEvent e) { + File logsAndDataDir = applicationConfiguration.getApplicationHomeDir(); + showDirectory(logsAndDataDir, "Taverna logs and data folder"); + } + }; + } + + public static void showDirectory(File dir, String title) { + String path = dir.getAbsolutePath(); + String os = System.getProperty("os.name"); + String cmd; + boolean isWindows = false; + if (os.equals(MAC_OS_X)) + cmd = OPEN; + else if (os.startsWith(WINDOWS)) { + cmd = EXPLORER; + isWindows = true; + } else + // Assume Unix - best option is gnome-open + cmd = GNOME_OPEN; + + String[] cmdArray = new String[2]; + cmdArray[0] = cmd; + cmdArray[1] = path; + try { + Process exec = getRuntime().exec(cmdArray); + Thread.sleep(300); + exec.getErrorStream().close(); + exec.getInputStream().close(); + exec.getOutputStream().close(); + exec.waitFor(); + if (exec.exitValue() == 0 || isWindows && exec.exitValue() == 1) + // explorer.exe thinks 1 means success + return; + logger.warn("Exit value from " + cmd + " " + path + ": " + exec.exitValue()); + } catch (Exception ex) { + logger.warn("Could not call " + cmd + " " + path, ex); + } + // Fall-back - just show a dialogue with the path + showInputDialog(getMainWindow(), "Copy path from below:", title, + INFORMATION_MESSAGE, null, null, path); + } + + public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + } +}
diff --git a/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ViewShowMenuSection.java b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ViewShowMenuSection.java new file mode 100644 index 0000000..2df05e5 --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/java/net/sf/taverna/t2/workbench/ui/impl/menu/ViewShowMenuSection.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.ui.impl.menu; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; + +/** + * @author Alex Nenadic + * @author Alan R Williams + */ +public class ViewShowMenuSection extends AbstractMenuSection { + public static final URI DIAGRAM_MENU = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#diagram"); + public static final URI VIEW_SHOW_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#viewShowMenuSection"); + + public ViewShowMenuSection() { + super(DIAGRAM_MENU, 10, VIEW_SHOW_MENU_SECTION); + } +}
diff --git a/taverna-workbench-menu-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuManager b/taverna-workbench-menu-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuManager new file mode 100644 index 0000000..3b06fd9 --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuManager
@@ -0,0 +1 @@ +net.sf.taverna.t2.ui.menu.impl.MenuManagerImpl \ No newline at end of file
diff --git a/taverna-workbench-menu-impl/src/main/resources/META-INF/spring/menu-impl-context-osgi.xml b/taverna-workbench-menu-impl/src/main/resources/META-INF/spring/menu-impl-context-osgi.xml new file mode 100644 index 0000000..3a1eadf --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/resources/META-INF/spring/menu-impl-context-osgi.xml
@@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="MenuManagerImpl" interface="net.sf.taverna.t2.ui.menu.MenuManager" /> + + <service ref="FileMenu" auto-export="interfaces" /> + <service ref="EditMenu" auto-export="interfaces" /> + <service ref="AdvancedMenu" auto-export="interfaces" /> + <service ref="HelpMenu" auto-export="interfaces" /> + <service ref="OnlineHelpMenuAction" auto-export="interfaces" /> + <service ref="FeedbackMenuAction" auto-export="interfaces" /> + <service ref="ShowLogsAndDataMenuAction" auto-export="interfaces" /> + + <reference id="applicationConfiguration" interface="uk.org.taverna.configuration.app.ApplicationConfiguration" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + + <list id="menuComponents" interface="net.sf.taverna.t2.ui.menu.MenuComponent" cardinality="0..N" greedy-proxying="true"> + <listener ref="MenuManagerImpl" bind-method="update" unbind-method="update" /> + </list> + +</beans:beans>
diff --git a/taverna-workbench-menu-impl/src/main/resources/META-INF/spring/menu-impl-context.xml b/taverna-workbench-menu-impl/src/main/resources/META-INF/spring/menu-impl-context.xml new file mode 100644 index 0000000..62fd24e --- /dev/null +++ b/taverna-workbench-menu-impl/src/main/resources/META-INF/spring/menu-impl-context.xml
@@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="MenuManagerImpl" class="net.sf.taverna.t2.ui.menu.impl.MenuManagerImpl"> + <property name="menuComponents" ref="menuComponents" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + + <bean id="FileMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.FileMenu" /> + <bean id="EditMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.EditMenu" /> + <bean id="AdvancedMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.AdvancedMenu" /> + <bean id="HelpMenu" class="net.sf.taverna.t2.workbench.ui.impl.menu.HelpMenu" /> + <bean id="OnlineHelpMenuAction" + class="net.sf.taverna.t2.workbench.ui.impl.menu.OnlineHelpMenuAction" /> + <bean id="FeedbackMenuAction" + class="net.sf.taverna.t2.workbench.ui.impl.menu.FeedbackMenuAction" /> + <bean id="ShowLogsAndDataMenuAction" + class="net.sf.taverna.t2.workbench.ui.impl.menu.ShowLogsAndDataMenuAction"> + <property name="applicationConfiguration" ref="applicationConfiguration" /> + </bean> + +</beans>
diff --git a/taverna-workbench-menu-items/pom.xml b/taverna-workbench-menu-items/pom.xml new file mode 100644 index 0000000..c7007e3 --- /dev/null +++ b/taverna-workbench-menu-items/pom.xml
@@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-exts</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-exts</groupId> + <artifactId>menu-items</artifactId> + <packaging>bundle</packaging> + <name>Additional menu items</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>report-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>design-ui</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>graph-view</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>workflow-view</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.commons</groupId> + <artifactId>taverna-services-api</artifactId> + <version>0.1.0-SNAPSHOT</version> + </dependency> + + <dependency> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>stringconstant-activity-ui</artifactId> + <version>${t2.ui.activities.version}</version> + </dependency> + <!-- TODO remove dependencies on implementations --> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>contextual-views-impl</artifactId> + <version>${t2.ui.impl.version}</version> + </dependency> + </dependencies> +</project> \ No newline at end of file
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AbstractConnectPortMenuActions.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AbstractConnectPortMenuActions.java new file mode 100644 index 0000000..2bbed5b --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AbstractConnectPortMenuActions.java
@@ -0,0 +1,268 @@ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import java.awt.Color; +import java.awt.Component; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.Action; +import javax.swing.Icon; +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.common.NamedSet; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputPort; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import uk.org.taverna.scufl2.api.port.Port; +import uk.org.taverna.scufl2.api.port.ProcessorPort; +import uk.org.taverna.scufl2.api.port.ReceiverPort; +import uk.org.taverna.scufl2.api.port.SenderPort; +import uk.org.taverna.scufl2.api.port.WorkflowPort; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import net.sf.taverna.t2.lang.ui.ShadedLabel; +import net.sf.taverna.t2.ui.menu.AbstractMenuCustom; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.ui.menu.MenuManager.ComponentFactory; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; + +public abstract class AbstractConnectPortMenuActions extends AbstractMenuCustom + implements ContextualMenuComponent { + + protected ActivityIconManager activityIconManager; + protected ContextualSelection contextualSelection; + protected MenuManager menuManager; + protected WorkbenchConfiguration workbenchConfiguration; + protected ColourManager colourManager; + private EditManager editManager; + + public static final String CONNECT_AS_INPUT_TO = "Connect as input to..."; + public static final String CONNECT_WITH_OUTPUT_FROM = "Connect with output from..."; + + public static final String SERVICE_INPUT_PORTS = "Service input ports"; + public static final String SERVICE_OUTPUT_PORTS = "Service output ports"; + + public static final String NEW_WORKFLOW_INPUT_PORT = "New workflow input port..."; + public static final String NEW_WORKFLOW_OUTPUT_PORT = "New workflow output port..."; + + public static final String WORKFLOW_INPUT_PORTS = "Workflow input ports"; + public static final String WORKFLOW_OUTPUT_PORTS = "Workflow output ports"; + + public static final String SERVICES = "Services"; + + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public AbstractConnectPortMenuActions(URI parentId, int positionHint) { + super(parentId, positionHint); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.customComponent = null; + } + + @Override + protected Component createCustomComponent() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + Profile profile = workflow.getParent().getMainProfile(); + Port port = getSelectedPort(); + // Component component = + // getContextualSelection().getRelativeToComponent(); + + String label; + if (port instanceof ReceiverPort) { + label = CONNECT_WITH_OUTPUT_FROM; + } else { + label = CONNECT_AS_INPUT_TO; + } + JMenu connectMenu = new JMenu(new DummyAction(label, + WorkbenchIcons.datalinkIcon)); + addPortMenuItems(workflow, port, connectMenu); + addProcessorMenuItems(workflow, profile, port, connectMenu); + return connectMenu; + } + + private Port getSelectedPort() { + Port port = (Port) getContextualSelection().getSelection(); + return port; + } + + protected void addPortMenuItems(Workflow workflow, Port port, JMenu connectMenu) { + Color workflowPortColour = colourManager.getPreferredColour(WorkflowPort.class.getCanonicalName()); + + boolean addedPorts = false; + if (port instanceof SenderPort) { + connectMenu.add(new ShadedLabel(WORKFLOW_OUTPUT_PORTS, workflowPortColour)); + for (OutputWorkflowPort outputWorkflowPort : workflow.getOutputPorts()) { + ConnectPortsAction connectPortsAction = + new ConnectPortsAction(workflow, (SenderPort) port, outputWorkflowPort, editManager); + connectPortsAction.putValue(Action.SMALL_ICON, WorkbenchIcons.outputIcon); + connectPortsAction.putValue(Action.NAME, outputWorkflowPort.getName()); + connectMenu.add(new JMenuItem(connectPortsAction)); + addedPorts = true; + } + } else if (port instanceof ReceiverPort) { + connectMenu.add(new ShadedLabel(WORKFLOW_INPUT_PORTS, workflowPortColour)); + for (InputWorkflowPort inputWorkflowPort : workflow.getInputPorts()) { + ConnectPortsAction connectPortsAction = + new ConnectPortsAction(workflow, inputWorkflowPort, (ReceiverPort) port, editManager); + connectPortsAction.putValue(Action.SMALL_ICON, WorkbenchIcons.inputIcon); + connectPortsAction.putValue(Action.NAME, inputWorkflowPort.getName()); + connectMenu.add(new JMenuItem(connectPortsAction)); + addedPorts = true; + } + } + if (addedPorts) { + connectMenu.addSeparator(); + } + CreateAndConnectDataflowPortAction newDataflowPortAction = new CreateAndConnectDataflowPortAction( + workflow, port, getSuggestedName(port), contextualSelection.getRelativeToComponent(), editManager); + + if (port instanceof ReceiverPort) { + newDataflowPortAction.putValue(Action.NAME, NEW_WORKFLOW_INPUT_PORT); + } else if (port instanceof SenderPort) { + newDataflowPortAction.putValue(Action.NAME, NEW_WORKFLOW_OUTPUT_PORT); + } + newDataflowPortAction.putValue(Action.SMALL_ICON, WorkbenchIcons.newIcon); + connectMenu.add(new JMenuItem(newDataflowPortAction)); + } + + /** + * @param port + * @return + */ + private String getSuggestedName(Port port) { + String suggestedName; + if (port instanceof ProcessorPort) { + suggestedName = ((ProcessorPort) port).getParent().getName() + "_" + port.getName(); + } else { + suggestedName = port.getName(); + } + return suggestedName; + } + + protected void addProcessorMenuItems(Workflow dataflow, Profile profile, + final Port targetPort, JMenu connectMenu) { + final Set<Processor> processors = findProcessors(dataflow, targetPort); + if (processors.isEmpty()) { + return; + } + connectMenu.add(new ShadedLabel(SERVICES, colourManager.getPreferredColour(Processor.class.getCanonicalName()))); + + List<JMenuItem> menuItems = new ArrayList<JMenuItem>(); + for (Processor processor : processors) { + Activity activity = scufl2Tools.processorBindingForProcessor(processor, profile).getBoundActivity(); + Icon icon = activityIconManager.iconForActivity(activity); + final Color processorPortColour = colourManager.getPreferredColour(ProcessorPort.class.getCanonicalName()); + + JMenu processorMenu = new JMenu(new DummyAction(processor.getName(), icon)); + List<JMenuItem> processorMenuItems = new ArrayList<JMenuItem>(); + if (targetPort instanceof ReceiverPort) { + processorMenu.add(new ShadedLabel(SERVICE_OUTPUT_PORTS, + processorPortColour)); + menuItems.add(processorMenu); + for (OutputProcessorPort outputProcessorPort : processor.getOutputPorts()) { + ConnectPortsAction connectPortsAction = new ConnectPortsAction(dataflow, + outputProcessorPort, (ReceiverPort) targetPort, editManager); + connectPortsAction.putValue(Action.SMALL_ICON, + WorkbenchIcons.outputPortIcon); + connectPortsAction.putValue(Action.NAME, outputProcessorPort.getName()); + processorMenuItems.add(new JMenuItem(connectPortsAction)); + } + } else if (targetPort instanceof SenderPort) { + processorMenu.add(new ShadedLabel(SERVICE_INPUT_PORTS, + processorPortColour)); + menuItems.add(processorMenu); + for (InputProcessorPort inputProcessorPort : processor.getInputPorts()) { + ConnectPortsAction connectPortsAction = new ConnectPortsAction(dataflow, + (SenderPort) targetPort, inputProcessorPort, editManager); + connectPortsAction.putValue(Action.SMALL_ICON, + WorkbenchIcons.inputPortIcon); + connectPortsAction.putValue(Action.NAME, inputProcessorPort.getName()); + processorMenuItems.add(new JMenuItem(connectPortsAction)); + } + } + + menuManager.addMenuItemsWithExpansion(processorMenuItems, + processorMenu, workbenchConfiguration.getMaxMenuItems(), + new ComponentFactory() { + public Component makeComponent() { + if (targetPort instanceof InputPort) { + return new ShadedLabel(SERVICE_OUTPUT_PORTS, processorPortColour); + } else { + return new ShadedLabel(SERVICE_INPUT_PORTS, processorPortColour); + } + } + }); + } + menuManager.addMenuItemsWithExpansion(menuItems, connectMenu, + workbenchConfiguration.getMaxMenuItems(), + new ComponentFactory() { + public Component makeComponent() { + return new ShadedLabel(SERVICES, colourManager + .getPreferredColour(Processor.class + .getCanonicalName())); + } + }); + } + + protected Set<Processor> findProcessors(Workflow dataflow, Port targetPort) { + Set<Processor> possibleProcessors = new HashSet<Processor>(); + if (targetPort instanceof InputProcessorPort) { + InputProcessorPort inputProcessorPort = (InputProcessorPort) targetPort; + possibleProcessors = scufl2Tools.possibleUpStreamProcessors(dataflow, inputProcessorPort.getParent()); + } else if (targetPort instanceof OutputProcessorPort) { + OutputProcessorPort outputProcessorPort = (OutputProcessorPort) targetPort; + possibleProcessors = scufl2Tools.possibleDownStreamProcessors(dataflow, outputProcessorPort.getParent()); + } else { + // Probably a dataflow port, everything is allowed + possibleProcessors = dataflow.getProcessors(); + } + return possibleProcessors; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + public void setWorkbenchConfiguration(WorkbenchConfiguration workbenchConfiguration) { + this.workbenchConfiguration = workbenchConfiguration; + } + + public void setColourManager(ColourManager colourManager) { + this.colourManager = colourManager; + } + +} \ No newline at end of file
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityInputPortSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityInputPortSection.java new file mode 100644 index 0000000..89414d6 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityInputPortSection.java
@@ -0,0 +1,67 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; + +public class ActivityInputPortSection extends AbstractMenuSection implements + ContextualMenuComponent { + + private static final String ACTIVITY_INPUT_PORT = "Service input port: "; + public static final URI activityInputPortSection = URI + .create("http://taverna.sf.net/2009/contextMenu/activityInputPort"); + private ContextualSelection contextualSelection; + + public ActivityInputPortSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, + activityInputPortSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return getContextualSelection().getSelection() instanceof InputProcessorPort; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.action = null; + } + + @Override + protected Action createAction() { + InputProcessorPort port = (InputProcessorPort) getContextualSelection().getSelection(); + String name = ACTIVITY_INPUT_PORT + port.getName(); + return new DummyAction(name); + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityOutputPortSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityOutputPortSection.java new file mode 100644 index 0000000..b26cff9 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ActivityOutputPortSection.java
@@ -0,0 +1,67 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; + +public class ActivityOutputPortSection extends AbstractMenuSection implements + ContextualMenuComponent { + + private static final String ACTIVITY_OUTPUT_PORT = "Service output port: "; + public static final URI activityOutputPortSection = URI + .create("http://taverna.sf.net/2009/contextMenu/activityOutputPort"); + private ContextualSelection contextualSelection; + + public ActivityOutputPortSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, + activityOutputPortSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return getContextualSelection().getSelection() instanceof OutputProcessorPort; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.action = null; + } + + @Override + protected Action createAction() { + OutputProcessorPort port = (OutputProcessorPort) getContextualSelection().getSelection(); + String name = ACTIVITY_OUTPUT_PORT + port.getName(); + return new DummyAction(name); + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AddInputPortDefaultValueAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AddInputPortDefaultValueAction.java new file mode 100644 index 0000000..414ffac --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/AddInputPortDefaultValueAction.java
@@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JOptionPane; + +import net.sf.taverna.t2.activities.stringconstant.views.StringConstantConfigView; +import net.sf.taverna.t2.workbench.design.actions.DataflowEditAction; +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.AddChildEdit; +import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit; +import net.sf.taverna.t2.workflow.edits.AddProcessorEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.activity.Activity; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.iterationstrategy.CrossProduct; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; +import uk.org.taverna.scufl2.api.port.OutputActivityPort; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; +import uk.org.taverna.scufl2.api.profiles.ProcessorBinding; +import uk.org.taverna.scufl2.api.profiles.ProcessorOutputPortBinding; +import uk.org.taverna.scufl2.api.profiles.Profile; + +/** + * Action for adding a default value to an input port of a processor. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class AddInputPortDefaultValueAction extends DataflowEditAction { + + private static Logger logger = Logger.getLogger(AddInputPortDefaultValueAction.class); + + private static final URI STRING_CONSTANT = URI + .create("http://ns.taverna.org.uk/2010/activity/constant"); + + private InputProcessorPort inputPort; + + private final ServiceRegistry serviceRegistry; + + public AddInputPortDefaultValueAction(Workflow workflow, InputProcessorPort inputPort, + Component component, EditManager editManager, SelectionManager selectionManager, + ServiceRegistry serviceRegistry) { + super(workflow, component, editManager, selectionManager); + this.inputPort = inputPort; + this.serviceRegistry = serviceRegistry; + putValue(SMALL_ICON, WorkbenchIcons.inputValueIcon); + putValue(NAME, "Set constant value"); + } + + public void actionPerformed(ActionEvent e) { + try { + Activity activity = new Activity(); + activity.setType(STRING_CONSTANT); + Configuration configuration = new Configuration(); + configuration.setType(STRING_CONSTANT.resolve("#Config")); + configuration.getJsonAsObjectNode().put("string", ""); + configuration.setConfigures(activity); + + StringConstantConfigView configView = new StringConstantConfigView(activity, + configuration, serviceRegistry); + + int answer = JOptionPane.showConfirmDialog(component, configView, + "Text constant value", JOptionPane.OK_CANCEL_OPTION); + if (answer != JOptionPane.CANCEL_OPTION) { + + configView.noteConfiguration(); + configuration.setJson(configView.getJson()); + + Profile profile = selectionManager.getSelectedProfile(); + + Processor processor = new Processor(); + processor.setName(inputPort.getName() + "_value"); + + CrossProduct crossProduct = new CrossProduct(); + crossProduct.setParent(processor.getIterationStrategyStack()); + + ProcessorBinding processorBinding = new ProcessorBinding(); + processorBinding.setBoundProcessor(processor); + processorBinding.setBoundActivity(activity); + + // create activity port + OutputActivityPort activityPort = new OutputActivityPort(activity, "value"); + activityPort.setDepth(0); + activityPort.setGranularDepth(0); + // create processor port + OutputProcessorPort processorPort = new OutputProcessorPort(processor, + activityPort.getName()); + processorPort.setDepth(0); + processorPort.setGranularDepth(0); + // add a new port binding + new ProcessorOutputPortBinding(processorBinding, activityPort, processorPort); + + // Add a data link between the string constant processor's output port + // and the processor containing the passed inputPort. + DataLink datalink = new DataLink(); + datalink.setReceivesFrom(processorPort); + datalink.setSendsTo(inputPort); + + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + editList.add(new AddChildEdit<Profile>(profile, activity)); + editList.add(new AddChildEdit<Profile>(profile, configuration)); + editList.add(new AddChildEdit<Profile>(profile, processorBinding)); + editList.add(new AddProcessorEdit(dataflow, processor)); + editList.add(new AddDataLinkEdit(dataflow, datalink)); + + editManager.doDataflowEdit(dataflow.getParent(), new CompoundEdit(editList)); + + } + } catch (EditException ex) { + logger.error("Adding default value for input port failed", ex); + } + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectInputPortMenuActions.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectInputPortMenuActions.java new file mode 100644 index 0000000..f22ebbc --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectInputPortMenuActions.java
@@ -0,0 +1,41 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; + +public class ConnectInputPortMenuActions extends AbstractConnectPortMenuActions + implements ContextualMenuComponent { + + public ConnectInputPortMenuActions() { + super(ActivityInputPortSection.activityInputPortSection, 20); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof InputProcessorPort + && getContextualSelection().getParent() instanceof Workflow; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectOutputPortMenuActions.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectOutputPortMenuActions.java new file mode 100644 index 0000000..2bb3ec0 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectOutputPortMenuActions.java
@@ -0,0 +1,41 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputProcessorPort; + + +public class ConnectOutputPortMenuActions extends AbstractConnectPortMenuActions { + + public ConnectOutputPortMenuActions() { + super(ActivityOutputPortSection.activityOutputPortSection, 20); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof OutputProcessorPort + && getContextualSelection().getParent() instanceof Workflow; + } + + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectPortsAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectPortsAction.java new file mode 100644 index 0000000..f10fedf --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/ConnectPortsAction.java
@@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.ReceiverPort; +import uk.org.taverna.scufl2.api.port.SenderPort; + +@SuppressWarnings("serial") +public class ConnectPortsAction extends AbstractAction { + private static Logger logger = Logger.getLogger(ConnectPortsAction.class); + private final Workflow workflow; + private final ReceiverPort receiverPort; + private final SenderPort senderPort; + private final EditManager editManager; + + public ConnectPortsAction(Workflow workflow, + SenderPort senderPort, ReceiverPort receiverPort, EditManager editManager) { + super("Connect " + senderPort.getName() + " to " + receiverPort.getName()); + this.workflow = workflow; + this.receiverPort = receiverPort; + this.senderPort = senderPort; + this.editManager = editManager; + } + + public void actionPerformed(ActionEvent e) { + DataLink dataLink = new DataLink(); + dataLink.setReceivesFrom(senderPort); + dataLink.setSendsTo(receiverPort); + Edit<Workflow> edit = new AddDataLinkEdit(workflow, dataLink); + try { + editManager.doDataflowEdit(workflow.getParent(), edit); + } catch (EditException ex) { + logger.warn("Can't create connection between " + senderPort + + " and " + receiverPort, ex); + } + } +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/CreateAndConnectDataflowPortAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/CreateAndConnectDataflowPortAction.java new file mode 100644 index 0000000..534f4ca --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/CreateAndConnectDataflowPortAction.java
@@ -0,0 +1,226 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractAction; + +import net.sf.taverna.t2.lang.ui.ValidatingUserInputDialog; +import net.sf.taverna.t2.workbench.design.ui.DataflowInputPortPanel; +import net.sf.taverna.t2.workbench.design.ui.DataflowOutputPortPanel; +import net.sf.taverna.t2.workbench.edits.CompoundEdit; +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workflow.edits.AddChildEdit; +import net.sf.taverna.t2.workflow.edits.AddDataLinkEdit; +import net.sf.taverna.t2.workflow.edits.AddWorkflowInputPortEdit; +import net.sf.taverna.t2.workflow.edits.AddWorkflowOutputPortEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.DepthPort; +import uk.org.taverna.scufl2.api.port.InputPort; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import uk.org.taverna.scufl2.api.port.Port; +import uk.org.taverna.scufl2.api.port.ReceiverPort; +import uk.org.taverna.scufl2.api.port.SenderPort; + +/** + * Action to create a dataflow input/output port and connect it to the specified + * processor/activity output/input port. + * <p> + * The created dataflow port name will be taken from the name of the provided + * port. + * + * @author Stian Soiland-Reyes + * + */ +@SuppressWarnings("serial") +public class CreateAndConnectDataflowPortAction extends AbstractAction { + + private static final String VALID_PORT_NAME_REGEX = "[\\p{L}\\p{Digit}_.]+"; + private static final Dimension INPUT_PORT_DIALOGUE_SIZE = new Dimension(400, 250); + private static final Dimension OUTPUT_PORT_DIALOGUE_SIZE = new Dimension(400, 200); + + private static final String INVALID_WORKFLOW_OUTPUT_PORT_NAME = "Invalid workflow output port name."; + private static final String DUPLICATE_WORKFLOW_OUTPUT_PORT_NAME = "Duplicate workflow output port name."; + private static final String SET_THE_WORKFLOW_OUTPUT_PORT_NAME = "Set the workflow output port name."; + private static final String ADD_WORKFLOW_OUTPUT_PORT = "Add workflow output port"; + private static final String SET_THE_INPUT_PORT_LIST_DEPTH = "Set the input port list depth."; + private static final String SET_THE_INPUT_PORT_TYPE = "Set the input port type."; + private static final String INVALID_WORKFLOW_INPUT_PORT_NAME = "Invalid workflow input port name."; + private static final String DUPLICATE_WORKFLOW_INPUT_PORT_NAME = "Duplicate workflow input port name."; + private static final String SET_THE_WORKFLOW_INPUT_PORT_NAME = "Set the workflow input port name."; + private static final String ADD_WORKFLOW_INPUT_PORT = "Add workflow input port"; + private static Logger logger = Logger.getLogger(CreateAndConnectDataflowPortAction.class); + private final Workflow workflow; + + private final Port port; + private final String suggestedName; + private final Component parentComponent; + private final EditManager editManager; + + /** + * Action for creating a Workflow input/output port and linking it to the + * specified port. + * <p> + * If the provided port is an InputPort then a + * Workflow OutputPort will be created and linked. Vice versa, if the + * provided port is an OutputPort, a Workflow InputPort will be created. + * + * @param workflow + * Workflow where to create the Workflow input/output port + * @param port + * Existing Processor port to connect to + * @param suggestedName + * suggested port name + * @param parentComponent + * Component to be parent of any pop-ups + */ + public CreateAndConnectDataflowPortAction(Workflow workflow, Port port, + String suggestedName, Component parentComponent, EditManager editManager) { + super("Connect to new workflow port"); + this.workflow = workflow; + this.port = port; + this.suggestedName = suggestedName; + this.parentComponent = parentComponent; + this.editManager = editManager; + if (!(port instanceof InputPort || port instanceof OutputPort)) { + throw new IllegalArgumentException("Port " + port + + " must be either an InputPort or OutputPort"); + } + } + + public void actionPerformed(ActionEvent e) { + if (port instanceof ReceiverPort) { + InputWorkflowPort inputWorkflowPort = new InputWorkflowPort(); + inputWorkflowPort.setName(suggestedName); + workflow.getInputPorts().addWithUniqueName(inputWorkflowPort); + workflow.getInputPorts().remove(inputWorkflowPort); + if (port instanceof DepthPort) { + inputWorkflowPort.setDepth(((DepthPort) port).getDepth()); + } else { + inputWorkflowPort.setDepth(0); + } + showDialogue(inputWorkflowPort); + + } else if (port instanceof SenderPort) { + OutputWorkflowPort outputWorkflowPort = new OutputWorkflowPort(); + outputWorkflowPort.setName(suggestedName); + workflow.getOutputPorts().addWithUniqueName(outputWorkflowPort); + workflow.getOutputPorts().remove(outputWorkflowPort); + showDialogue(outputWorkflowPort); + } else { + throw new IllegalStateException("Port " + port + + " must be either an InputPort or OutputPort"); + } + + } + + protected void showDialogue(InputWorkflowPort portTemplate) { + Set<String> usedInputPorts = new HashSet<String>(); + for (InputWorkflowPort usedInputPort : workflow.getInputPorts()) { + usedInputPorts.add(usedInputPort.getName()); + } + DataflowInputPortPanel inputPanel = new DataflowInputPortPanel(); + + ValidatingUserInputDialog vuid = new ValidatingUserInputDialog( + ADD_WORKFLOW_INPUT_PORT, inputPanel); + vuid.addTextComponentValidation(inputPanel.getPortNameField(), + SET_THE_WORKFLOW_INPUT_PORT_NAME, usedInputPorts, + DUPLICATE_WORKFLOW_INPUT_PORT_NAME, VALID_PORT_NAME_REGEX, + INVALID_WORKFLOW_INPUT_PORT_NAME); + vuid.addMessageComponent(inputPanel.getSingleValueButton(), + SET_THE_INPUT_PORT_TYPE); + vuid.addMessageComponent(inputPanel.getListValueButton(), + SET_THE_INPUT_PORT_LIST_DEPTH); + vuid.setSize(INPUT_PORT_DIALOGUE_SIZE); + + inputPanel.setPortName(portTemplate.getName()); + inputPanel.setPortDepth(portTemplate.getDepth()); + + if (vuid.show(parentComponent)) { + InputWorkflowPort inputWorkflowPort = new InputWorkflowPort(); + inputWorkflowPort.setName(inputPanel.getPortName()); + inputWorkflowPort.setDepth(inputPanel.getPortDepth()); + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + editList.add(new AddWorkflowInputPortEdit(workflow, inputWorkflowPort)); + DataLink dataLink = new DataLink(); + dataLink.setReceivesFrom(inputWorkflowPort); + dataLink.setSendsTo((ReceiverPort) port); + editList.add(new AddDataLinkEdit(workflow, dataLink)); + try { + CompoundEdit compoundEdit = new CompoundEdit(editList); + editManager.doDataflowEdit(workflow.getParent(), compoundEdit); + } catch (EditException ex) { + logger.warn("Can't create or connect new input port", ex); + } + + } + } + + protected void showDialogue(OutputWorkflowPort portTemplate) { + Set<String> usedOutputPorts = new HashSet<String>(); + for (OutputWorkflowPort usedInputPort : workflow.getOutputPorts()) { + usedOutputPorts.add(usedInputPort.getName()); + } + DataflowOutputPortPanel outputPanel = new DataflowOutputPortPanel(); + + ValidatingUserInputDialog vuid = new ValidatingUserInputDialog( + ADD_WORKFLOW_OUTPUT_PORT, outputPanel); + vuid.addTextComponentValidation(outputPanel.getPortNameField(), + SET_THE_WORKFLOW_OUTPUT_PORT_NAME, usedOutputPorts, + DUPLICATE_WORKFLOW_OUTPUT_PORT_NAME, + VALID_PORT_NAME_REGEX, INVALID_WORKFLOW_OUTPUT_PORT_NAME); + vuid.setSize(OUTPUT_PORT_DIALOGUE_SIZE); + outputPanel.setPortName(portTemplate.getName()); + + if (vuid.show(parentComponent)) { + OutputWorkflowPort outputWorkflowPort = new OutputWorkflowPort(); + outputWorkflowPort.setName(outputPanel.getPortName()); + List<Edit<?>> editList = new ArrayList<Edit<?>>(); + editList.add(new AddWorkflowOutputPortEdit(workflow, outputWorkflowPort)); + DataLink dataLink = new DataLink(); + dataLink.setReceivesFrom((SenderPort) port); + dataLink.setSendsTo(outputWorkflowPort); + editList.add(new AddDataLinkEdit(workflow, dataLink)); + try { + CompoundEdit compoundEdit = new CompoundEdit(editList); + editManager.doDataflowEdit(workflow.getParent(), compoundEdit); + } catch (EditException ex) { + logger.warn("Can't create or connect new workflow output port", ex); + } + } + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetConstantInputPortValueMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetConstantInputPortValueMenuAction.java new file mode 100644 index 0000000..30a91c6 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetConstantInputPortValueMenuAction.java
@@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; + +public class SetConstantInputPortValueMenuAction extends AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + private ServiceRegistry serviceRegistry; + + public SetConstantInputPortValueMenuAction() { + super(ActivityInputPortSection.activityInputPortSection, 10); + } + + @Override + public synchronized Action getAction() { + SetDefaultInputPortValueAction action = (SetDefaultInputPortValueAction) super.getAction(); + action.updateStatus(); + return action; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof InputProcessorPort + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + return new SetDefaultInputPortValueAction(editManager, selectionManager, serviceRegistry); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetDefaultInputPortValueAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetDefaultInputPortValueAction.java new file mode 100644 index 0000000..302aaf6 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/activityport/SetDefaultInputPortValueAction.java
@@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.ui.menu.items.activityport; + +import java.awt.event.ActionEvent; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.Observer; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage; +import net.sf.taverna.t2.workbench.selection.events.PerspectiveSelectionEvent; +import net.sf.taverna.t2.workbench.selection.events.SelectionManagerEvent; +import net.sf.taverna.t2.workbench.selection.events.WorkflowBundleSelectionEvent; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.port.InputProcessorPort; + +/** + * An action that sets a default value to a processor's input port, in case + * the input port is selected on the Graph View. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class SetDefaultInputPortValueAction extends AbstractAction { + + /* Current workflow's selection model event observer. */ + private Observer<DataflowSelectionMessage> workflowSelectionObserver = new DataflowSelectionObserver(); + + private final EditManager editManager; + private final SelectionManager selectionManager; + private final ServiceRegistry serviceRegistry; + + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public SetDefaultInputPortValueAction(EditManager editManager, + SelectionManager selectionManager, ServiceRegistry serviceRegistry) { + super(); + this.editManager = editManager; + this.selectionManager = selectionManager; + this.serviceRegistry = serviceRegistry; + putValue(SMALL_ICON, WorkbenchIcons.inputValueIcon); + putValue(NAME, "Constant value"); + putValue(SHORT_DESCRIPTION, "Add a constant value for an input port"); + setEnabled(false); + + selectionManager.addObserver(new SelectionManagerObserver()); + + } + + public void actionPerformed(ActionEvent e) { + WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle(); + DataflowSelectionModel dataFlowSelectionModel = selectionManager + .getDataflowSelectionModel(workflowBundle); + // Get selected port + Set<Object> selectedWFComponents = dataFlowSelectionModel.getSelection(); + if (selectedWFComponents.size() > 1) { + JOptionPane.showMessageDialog(null, + "Only one workflow component should be selected for this action.", "Warning", + JOptionPane.WARNING_MESSAGE); + } else { + Object selectedWFComponent = selectedWFComponents.toArray()[0]; + if (selectedWFComponent instanceof InputProcessorPort) { + new AddInputPortDefaultValueAction(workflowBundle.getMainWorkflow(), + (InputProcessorPort) selectedWFComponent, null, editManager, + selectionManager, serviceRegistry).actionPerformed(e); + } + } + } + + /** + * Check if action should be enabled or disabled and update its status. + */ + public void updateStatus() { + WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle(); + DataflowSelectionModel selectionModel = selectionManager + .getDataflowSelectionModel(workflowBundle); + + // List of all selected objects in the graph view + Set<Object> selection = selectionModel.getSelection(); + + if (selection.isEmpty()) { + setEnabled(false); + } else { + // Take the first selected item - we only support single selections anyway + Object selected = selection.toArray()[0]; + if (selected instanceof InputProcessorPort) { + // If this input port is not already connected to something - enable the button + setEnabled(scufl2Tools.datalinksTo((InputProcessorPort) selected).isEmpty()); + } + } + } + + /** + * Observes events on workflow Selection Manager, i.e. when a workflow + * node is selected in the graph view, and enables/disables this action accordingly. + */ + private final class DataflowSelectionObserver implements Observer<DataflowSelectionMessage> { + + public void notify(Observable<DataflowSelectionMessage> sender, + DataflowSelectionMessage message) throws Exception { + updateStatus(); + } + } + + private final class SelectionManagerObserver extends SwingAwareObserver<SelectionManagerEvent> { + + private static final String DESIGN_PERSPECTIVE_ID = "net.sf.taverna.t2.ui.perspectives.design.DesignPerspective"; + + @Override + public void notifySwing(Observable<SelectionManagerEvent> sender, + SelectionManagerEvent message) { + if (message instanceof WorkflowBundleSelectionEvent) { + WorkflowBundleSelectionEvent workflowBundleSelectionEvent = (WorkflowBundleSelectionEvent) message; + WorkflowBundle oldFlow = workflowBundleSelectionEvent + .getPreviouslySelectedWorkflowBundle(); + WorkflowBundle newFlow = workflowBundleSelectionEvent.getSelectedWorkflowBundle(); + // Update the buttons status as current dataflow has changed + updateStatus(); + + // Remove the workflow selection model listener from the previous (if any) + // and add to the new workflow (if any) + if (oldFlow != null) { + selectionManager.getDataflowSelectionModel(oldFlow).removeObserver( + workflowSelectionObserver); + } + + if (newFlow != null) { + selectionManager.getDataflowSelectionModel(newFlow).addObserver( + workflowSelectionObserver); + } + } else if (message instanceof PerspectiveSelectionEvent) { + PerspectiveSelectionEvent perspectiveSelectionEvent = (PerspectiveSelectionEvent) message; + if (DESIGN_PERSPECTIVE_ID.equals(perspectiveSelectionEvent.getSelectedPerspective().getID())) { + updateStatus(); + } else { + setEnabled(false); + } + } + } + + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/annotated/AnnotatedConfigureMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/annotated/AnnotatedConfigureMenuAction.java new file mode 100644 index 0000000..7933940 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/annotated/AnnotatedConfigureMenuAction.java
@@ -0,0 +1,77 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.annotated; + +import java.awt.event.ActionEvent; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JOptionPane; + +import net.sf.taverna.t2.annotation.Annotated; +import net.sf.taverna.t2.annotation.AnnotationBeanSPI; +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated.AnnotatedContextualView; + +public class AnnotatedConfigureMenuAction extends AbstractContextualMenuAction { + private static final String ANNOTATE = "Annotate..."; + private EditManager editManager; + private SelectionManager selectionManager; + private List<AnnotationBeanSPI> annotationBeans; + + public AnnotatedConfigureMenuAction() { + super(ConfigureSection.configureSection, 40); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() && (getContextualSelection().getSelection() instanceof Annotated); + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction(ANNOTATE) { + public void actionPerformed(ActionEvent e) { + AnnotatedContextualView view = new AnnotatedContextualView((Annotated) getContextualSelection().getSelection(), + editManager, selectionManager, annotationBeans); + JOptionPane.showMessageDialog(null, view.getMainFrame(), "Annotation", JOptionPane.PLAIN_MESSAGE); + } + }; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setAnnotationBeans(List<AnnotationBeanSPI> annotationBeans) { + this.annotationBeans = annotationBeans; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureRunningContextualMenuSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureRunningContextualMenuSection.java new file mode 100644 index 0000000..9c459e2 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureRunningContextualMenuSection.java
@@ -0,0 +1,50 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenu; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; + +public class ConfigureRunningContextualMenuSection extends AbstractMenu +implements ContextualMenuComponent { + public static final String CONFIGURE_RUNNING = "Configure running"; + public static final URI configureRunningSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configureRunning"); + private ContextualSelection contextualSelection; + + public ConfigureRunningContextualMenuSection() { + super(ConfigureSection.configureSection, 45, configureRunningSection, CONFIGURE_RUNNING); + } + + @Override + public boolean isEnabled() { + return true; +// return super.isEnabled() && contextualSelection instanceof Processor; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureSection.java new file mode 100644 index 0000000..0f1a17f --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ConfigureSection.java
@@ -0,0 +1,61 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.net.URI; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; + +/** + * Menu section containing the actions to add service templates, i.e. activities + * than are not readily runnable but need to be configured first. The actual actions that + * go into this menu can be found in the ui modules for the activities. + * + * @author Alex Nenadic + * + */ +public class ConfigureSection extends AbstractMenuSection + implements ContextualMenuComponent { + + public static final URI configureSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configure"); + private ContextualSelection contextualSelection; + + public ConfigureSection() { + super(EditSection.editSection, 100, configureSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + Object selection = getContextualSelection().getSelection(); + return super.isEnabled(); + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + } +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/EditSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/EditSection.java new file mode 100644 index 0000000..d23ec6e --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/EditSection.java
@@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.lang.ui.ShadedLabel; +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; + +/** + * Menu section containing the actions to add service templates, i.e. activities + * than are not readily runnable but need to be configured first. The actual actions that + * go into this menu can be found in the ui modules for the activities. + * + * @author Alex Nenadic + * + */ +public class EditSection extends AbstractMenuSection + implements ContextualMenuComponent { + + private static final String EDIT = "Edit"; + public static final URI editSection = URI + .create("http://taverna.sf.net/2009/contextMenu/edit"); + private ContextualSelection contextualSelection; + + public EditSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, editSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled(); + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + } + + @Override + protected Action createAction() { + DummyAction action = new DummyAction(EDIT); + // Set the colour for the section + action.putValue(AbstractMenuSection.SECTION_COLOR, ShadedLabel.ORANGE); + return action; + } +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/InsertSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/InsertSection.java new file mode 100644 index 0000000..c1e21f6 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/InsertSection.java
@@ -0,0 +1,63 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class InsertSection extends AbstractMenuSection implements + ContextualMenuComponent { + + private static final String INSERT = "Insert"; + public static final URI insertSection = URI + .create("http://taverna.sf.net/2009/contextMenu/insert"); + private ContextualSelection contextualSelection; + + public InsertSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 20, insertSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Workflow; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + } + + @Override + protected Action createAction() { + return new DummyAction(INSERT); + } +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/PasteMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/PasteMenuAction.java new file mode 100644 index 0000000..8f9b1f9 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/PasteMenuAction.java
@@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.net.URI; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.actions.PasteGraphComponentAction; +import uk.org.taverna.commons.services.ServiceRegistry; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class PasteMenuAction extends AbstractContextualMenuAction { + + private static final URI PASTE_SERVICE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/paste#pasteServiceComponent"); + + private EditManager editManager; + private MenuManager menuManager; + private SelectionManager selectionManager; + private ServiceRegistry serviceRegistry; + + public PasteMenuAction() { + super(EditSection.editSection, 20, PASTE_SERVICE_URI); + } + + @Override + protected Action createAction() { + return PasteGraphComponentAction.getInstance(editManager, menuManager, selectionManager, serviceRegistry); + } + + public boolean isEnabled() { + return super.isEnabled() && (getContextualSelection().getSelection() instanceof Workflow); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) { + this.serviceRegistry = serviceRegistry; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowConfigureMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowConfigureMenuAction.java new file mode 100644 index 0000000..49c07a2 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowConfigureMenuAction.java
@@ -0,0 +1,165 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.awt.KeyboardFocusManager; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.net.URI; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.KeyStroke; +import javax.swing.text.JTextComponent; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.ui.menu.MenuManager; +import net.sf.taverna.t2.workbench.design.actions.EditDataflowInputPortAction; +import net.sf.taverna.t2.workbench.design.actions.EditDataflowOutputPortAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.merge.MergeConfigurationView; +import net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +public class ShowConfigureMenuAction extends AbstractMenuAction { + + private static Logger logger = Logger.getLogger(ShowConfigureMenuAction.class); + + public static final URI GRAPH_DETAILS_MENU_SECTION = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphDetailsMenuSection"); + + private static final URI SHOW_CONFIGURE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuShowConfigureComponent"); + + private EditManager editManager; + + private SelectionManager selectionManager; + + private MenuManager menuManager; + + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public ShowConfigureMenuAction() { + super(GRAPH_DETAILS_MENU_SECTION, 20, SHOW_CONFIGURE_URI); + } + + @Override + protected Action createAction() { + return new ShowConfigureAction(); + } + + @SuppressWarnings("serial") + protected class ShowConfigureAction extends AbstractAction implements DesignOnlyAction { + + private boolean enabled; + + ShowConfigureAction() { + super(); + putValue(NAME, "Configure"); + putValue(SHORT_DESCRIPTION, "Configure selected component"); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false)); + + KeyboardFocusManager focusManager = KeyboardFocusManager + .getCurrentKeyboardFocusManager(); + focusManager.addPropertyChangeListener(new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + String prop = e.getPropertyName(); + if ("focusOwner".equals(prop)) { + if (e.getNewValue() instanceof JTextComponent) { + ShowConfigureAction.super.setEnabled(false); + } else { + ShowConfigureAction.this.setEnabled(enabled); + } + } + } + }); + } + + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; + super.setEnabled(enabled); + } + + public void actionPerformed(ActionEvent e) { + WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle(); + DataflowSelectionModel dataFlowSelectionModel = selectionManager + .getDataflowSelectionModel(workflowBundle); + // Get selected port + Set<Object> selectedWFComponents = dataFlowSelectionModel.getSelection(); + if (selectedWFComponents.size() > 0) { + Object component = selectedWFComponents.iterator().next(); + if (component instanceof Processor) { + Action action = WorkflowView.getConfigureAction((Processor) component, + menuManager); + if (action != null) { + action.actionPerformed(e); + } + } else if (component instanceof DataLink) { + DataLink dataLink = (DataLink) component; + if (dataLink.getMergePosition() != null) { + List<DataLink> datalinks = scufl2Tools.datalinksTo(dataLink.getSendsTo()); + MergeConfigurationView mergeConfigurationView = new MergeConfigurationView( + datalinks, editManager, selectionManager); + mergeConfigurationView.setLocationRelativeTo(null); + mergeConfigurationView.setVisible(true); + } + } else if (component instanceof InputWorkflowPort) { + InputWorkflowPort port = (InputWorkflowPort) component; + new EditDataflowInputPortAction(port.getParent(), port, null, editManager, + selectionManager).actionPerformed(e); + } else if (component instanceof OutputWorkflowPort) { + OutputWorkflowPort port = (OutputWorkflowPort) component; + new EditDataflowOutputPortAction(port.getParent(), port, null, editManager, + selectionManager).actionPerformed(e); + } + } + } + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setMenuManager(MenuManager menuManager) { + this.menuManager = menuManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsContextualMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsContextualMenuAction.java new file mode 100644 index 0000000..27a47de --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsContextualMenuAction.java
@@ -0,0 +1,65 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.ui.Workbench; + +import org.apache.log4j.Logger; + +public class ShowDetailsContextualMenuAction extends AbstractContextualMenuAction { + private static final String SHOW_DETAILS = "Show details"; + private String namedComponent = "contextualView"; + + private static Logger logger = Logger.getLogger(ShowDetailsContextualMenuAction.class); + private Workbench workbench; + + public ShowDetailsContextualMenuAction() { + super(ConfigureSection.configureSection, 40); + } + + @Override + public boolean isEnabled() { + return super.isEnabled(); + // FIXME: Should we list all the applicable types here? + // && getContextualSelection().getSelection() instanceof Processor; + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction(SHOW_DETAILS) { + public void actionPerformed(ActionEvent e) { + workbench.makeNamedComponentVisible(namedComponent); + } + }; + } + + public void setWorkbench(Workbench workbench) { + this.workbench = workbench; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsMenuAction.java new file mode 100644 index 0000000..ae9fee3 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowDetailsMenuAction.java
@@ -0,0 +1,81 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.KeyStroke; + +import net.sf.taverna.t2.ui.menu.AbstractMenuAction; +import net.sf.taverna.t2.ui.menu.DesignOnlyAction; +import net.sf.taverna.t2.workbench.ui.Workbench; + +public class ShowDetailsMenuAction extends AbstractMenuAction { + private static final URI SHOW_DETAILS_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/menu#graphMenuShowDetailsComponent"); + + private static final String SHOW_DETAILS = "Details"; + private String namedComponent = "contextualView"; + + private Workbench workbench; + + public ShowDetailsMenuAction() { + super(ShowConfigureMenuAction.GRAPH_DETAILS_MENU_SECTION, 20, SHOW_DETAILS_URI); + } + + @Override + public boolean isEnabled() { + return super.isEnabled(); + // FIXME: Should we list all the applicable types here? + // && getContextualSelection().getSelection() instanceof Processor; + } + + @Override + protected Action createAction() { + return new ShowDetailsAction(); + } + + protected class ShowDetailsAction extends AbstractAction implements DesignOnlyAction { + + ShowDetailsAction() { + super(); + putValue(NAME, "Show details"); + putValue(SHORT_DESCRIPTION, "Show details of selected component"); + putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK)); + } + + public void actionPerformed(ActionEvent e) { + workbench.makeNamedComponentVisible(namedComponent); + } + + } + + public void setWorkbench(Workbench workbench) { + this.workbench = workbench; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowReportsContextualMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowReportsContextualMenuAction.java new file mode 100644 index 0000000..c9d7279 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/contextualviews/ShowReportsContextualMenuAction.java
@@ -0,0 +1,103 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.contextualviews; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Icon; + +import net.sf.taverna.t2.lang.ui.icons.Icons; +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.report.ReportManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Workbench; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.container.WorkflowBundle; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.validation.Status; + +public class ShowReportsContextualMenuAction extends AbstractContextualMenuAction { + + private static final String SHOW_REPORTS = "Show validation report"; + private String namedComponent = "reportView"; + private ReportManager reportManager; + private Workbench workbench; + private SelectionManager selectionManager; + + @SuppressWarnings("unused") + private static Logger logger = Logger.getLogger(ShowReportsContextualMenuAction.class); + + public ShowReportsContextualMenuAction() { + /** Right below ShowDetailsContextualMenuAction + */ + super(ConfigureSection.configureSection, 41); + } + + @Override + public boolean isEnabled() { + return super.isEnabled(); + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + WorkflowBundle parent; + if (getContextualSelection().getParent() instanceof Workflow) { + parent = ((Workflow)getContextualSelection().getParent()).getParent(); + } else { + parent = selectionManager.getSelectedWorkflowBundle(); + } + Status status = Status.OK; + if (reportManager != null) { +// status = reportManager.getStatus(parent.getMainProfile(), (WorkflowBean) getContextualSelection().getSelection()); + } + + Icon icon = null; + if (status == Status.WARNING) { + icon = Icons.warningIcon; + } else if (status == Status.SEVERE) { + icon = Icons.severeIcon; + } + + return new AbstractAction(SHOW_REPORTS, icon) { + public void actionPerformed(ActionEvent e) { + workbench.makeNamedComponentVisible(namedComponent); + } + }; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setReportManager(ReportManager reportManager) { + this.reportManager = reportManager; + } + + public void setWorkbench(Workbench workbench) { + this.workbench = workbench; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/ConditionSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/ConditionSection.java new file mode 100644 index 0000000..3f67def --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/ConditionSection.java
@@ -0,0 +1,71 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.controllink; + +import java.net.URI; + +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.core.BlockingControlLink; +import uk.org.taverna.scufl2.api.core.ControlLink; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; + +public class ConditionSection extends AbstractMenuSection implements + ContextualMenuComponent { + + private static final String CONTROL_LINK = "Control link: "; + public static final URI conditionSection = URI + .create("http://taverna.sf.net/2009/contextMenu/condition"); + private ContextualSelection contextualSelection; + + public ConditionSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, conditionSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof BlockingControlLink; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.action = null; + } + + @Override + protected Action createAction() { + BlockingControlLink controllink = (BlockingControlLink) getContextualSelection() + .getSelection(); + String name = CONTROL_LINK + controllink.getBlock().getName() + + " RUNS_AFTER " + controllink.getUntilFinished().getName(); + return new DummyAction(name); + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/RemoveConditionMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/RemoveConditionMenuAction.java new file mode 100644 index 0000000..68e32e2 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/controllink/RemoveConditionMenuAction.java
@@ -0,0 +1,67 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.controllink; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveConditionAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.ControlLink; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class RemoveConditionMenuAction extends AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public RemoveConditionMenuAction() { + super(ConditionSection.conditionSection, 10); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof ControlLink + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow dataflow = (Workflow) getContextualSelection().getParent(); + ControlLink controlLink = (ControlLink) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new RemoveConditionAction(dataflow, controlLink, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/LinkSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/LinkSection.java new file mode 100644 index 0000000..aee08ea --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/LinkSection.java
@@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.datalink; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.core.DataLink; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; + +public class LinkSection extends AbstractMenuSection implements + ContextualMenuComponent { + + public static final URI linkSection = URI + .create("http://taverna.sf.net/2009/contextMenu/link"); + private ContextualSelection contextualSelection; + + public LinkSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, linkSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof DataLink; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.action = null; + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + DataLink link = (DataLink) getContextualSelection().getSelection(); + String name = "Data link: " + link.getReceivesFrom().getName() + " -> " + link.getSendsTo().getName(); + return new AbstractAction(name) { + public void actionPerformed(ActionEvent e) { + } + }; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/RemoveLinkMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/RemoveLinkMenuAction.java new file mode 100644 index 0000000..c672f99 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/datalink/RemoveLinkMenuAction.java
@@ -0,0 +1,66 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.datalink; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveDatalinkAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.DataLink; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class RemoveLinkMenuAction extends AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public RemoveLinkMenuAction() { + super(LinkSection.linkSection, 10); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof DataLink + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + DataLink datalink = (DataLink) getContextualSelection().getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new RemoveDatalinkAction(workflow, datalink, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowInputPortMenuActions.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowInputPortMenuActions.java new file mode 100644 index 0000000..15e8424 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowInputPortMenuActions.java
@@ -0,0 +1,42 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.items.activityport.AbstractConnectPortMenuActions; + +public class ConnectDataflowInputPortMenuActions extends + AbstractConnectPortMenuActions implements ContextualMenuComponent { + + public ConnectDataflowInputPortMenuActions() { + super(WorkflowInputPortSection.inputPort, 20); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof InputWorkflowPort + && getContextualSelection().getParent() instanceof Workflow; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowOutputPortMenuActions.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowOutputPortMenuActions.java new file mode 100644 index 0000000..d99a361 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/ConnectDataflowOutputPortMenuActions.java
@@ -0,0 +1,42 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.items.activityport.AbstractConnectPortMenuActions; + +public class ConnectDataflowOutputPortMenuActions extends + AbstractConnectPortMenuActions implements ContextualMenuComponent { + + public ConnectDataflowOutputPortMenuActions() { + super(WorkflowOutputPortSection.outputPort, 20); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof OutputWorkflowPort + && getContextualSelection().getParent() instanceof Workflow; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowInputPortMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowInputPortMenuAction.java new file mode 100644 index 0000000..77c25f5 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowInputPortMenuAction.java
@@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.design.actions.EditDataflowInputPortAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; + +public class EditDataflowInputPortMenuAction extends + AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public EditDataflowInputPortMenuAction() { + super(WorkflowInputPortSection.inputPort, 10); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof InputWorkflowPort + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + InputWorkflowPort inport = (InputWorkflowPort) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new EditDataflowInputPortAction(workflow, inport, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowOutputPortMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowOutputPortMenuAction.java new file mode 100644 index 0000000..0f406dd --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/EditDataflowOutputPortMenuAction.java
@@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.design.actions.EditDataflowOutputPortAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +public class EditDataflowOutputPortMenuAction extends + AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public EditDataflowOutputPortMenuAction() { + super(WorkflowOutputPortSection.outputPort, 10); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof OutputWorkflowPort + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + OutputWorkflowPort outport = (OutputWorkflowPort) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new EditDataflowOutputPortAction(workflow, outport, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowInputPortMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowInputPortMenuAction.java new file mode 100644 index 0000000..f5e2fc1 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowInputPortMenuAction.java
@@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowInputPortAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; + +public class RemoveDataflowInputPortMenuAction extends + AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public RemoveDataflowInputPortMenuAction() { + super(WorkflowInputPortSection.inputPort, 10); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof InputWorkflowPort + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + InputWorkflowPort inport = (InputWorkflowPort) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new RemoveDataflowInputPortAction(workflow, inport, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowOutputPortMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowOutputPortMenuAction.java new file mode 100644 index 0000000..da775a5 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/RemoveDataflowOutputPortMenuAction.java
@@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveDataflowOutputPortAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +public class RemoveDataflowOutputPortMenuAction extends + AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public RemoveDataflowOutputPortMenuAction() { + super(WorkflowOutputPortSection.outputPort, 10); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof OutputWorkflowPort + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + OutputWorkflowPort outport = (OutputWorkflowPort) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new RemoveDataflowOutputPortAction(workflow, outport, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowInputPortSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowInputPortSection.java new file mode 100644 index 0000000..1a0d8ef --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowInputPortSection.java
@@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.port.InputWorkflowPort; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; + +public class WorkflowInputPortSection extends AbstractMenuSection implements + ContextualMenuComponent { + + public static final URI inputPort = URI + .create("http://taverna.sf.net/2009/contextMenu/inputPort"); + private ContextualSelection contextualSelection; + + public WorkflowInputPortSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, inputPort); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof InputWorkflowPort; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.action = null; + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + InputWorkflowPort proc = (InputWorkflowPort) getContextualSelection().getSelection(); + String name = "Workflow input port: " + proc.getName(); + return new AbstractAction(name) { + public void actionPerformed(ActionEvent e) { + } + }; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowOutputPortSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowOutputPortSection.java new file mode 100644 index 0000000..e387f9e --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/ports/WorkflowOutputPortSection.java
@@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.ports; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.port.OutputWorkflowPort; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; + +public class WorkflowOutputPortSection extends AbstractMenuSection implements + ContextualMenuComponent { + + public static final URI outputPort = URI + .create("http://taverna.sf.net/2009/contextMenu/outputPort"); + private ContextualSelection contextualSelection; + + public WorkflowOutputPortSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 10, outputPort); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof OutputWorkflowPort; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.action = null; + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + OutputWorkflowPort proc = (OutputWorkflowPort) getContextualSelection().getSelection(); + String name = "Workflow output port: " + proc.getName(); + return new AbstractAction(name) { + public void actionPerformed(ActionEvent e) { + } + }; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ConditionMenuActions.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ConditionMenuActions.java new file mode 100644 index 0000000..a3e569b --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ConditionMenuActions.java
@@ -0,0 +1,118 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.processor; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +import net.sf.taverna.t2.lang.ui.ShadedLabel; +import net.sf.taverna.t2.ui.menu.AbstractMenuCustom; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.design.actions.AddConditionAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.icons.WorkbenchIcons; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class ConditionMenuActions extends AbstractMenuCustom implements + ContextualMenuComponent { + + private ContextualSelection contextualSelection; + private EditManager editManager; + private SelectionManager selectionManager; + private ActivityIconManager activityIconManager; + private Scufl2Tools scufl2Tools = new Scufl2Tools(); + + public ConditionMenuActions() { + super(ConfigureSection.configureSection, 80 ); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Processor + && getContextualSelection().getParent() instanceof Workflow; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.customComponent = null; + } + + @Override + protected Component createCustomComponent() { + + Workflow workflow = (Workflow) getContextualSelection().getParent(); + Processor processor = (Processor) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + + List<AddConditionAction> conditions = getAddConditionActions(workflow, + processor, component); + if (conditions.isEmpty()) { + return null; + } + JMenu conditionMenu = new JMenu("Run after"); + conditionMenu.setIcon(WorkbenchIcons.controlLinkIcon); + conditionMenu.add(new ShadedLabel("Services:", ShadedLabel.ORANGE)); + conditionMenu.addSeparator(); + for (AddConditionAction addConditionAction : conditions) { + conditionMenu.add(new JMenuItem(addConditionAction)); + } + return conditionMenu; + } + + protected List<AddConditionAction> getAddConditionActions( + Workflow workflow, Processor targetProcessor, Component component) { + List<AddConditionAction> actions = new ArrayList<AddConditionAction>(); + for (Processor processor : scufl2Tools.possibleUpStreamProcessors(workflow, targetProcessor)) { + actions.add(new AddConditionAction(workflow, processor, + targetProcessor, component, editManager, selectionManager, activityIconManager)); + } + return actions; + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + + public void setActivityIconManager(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ProcessorSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ProcessorSection.java new file mode 100644 index 0000000..b2bde61 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/ProcessorSection.java
@@ -0,0 +1,58 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.processor; + +import java.net.URI; + +import uk.org.taverna.scufl2.api.core.Processor; + +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.items.contextualviews.EditSection; + +public class ProcessorSection extends AbstractMenuSection implements + ContextualMenuComponent { + + public static final URI processorSection = URI + .create("http://taverna.sf.net/2009/contextMenu/processor"); + private ContextualSelection contextualSelection; + + public ProcessorSection() { + super(EditSection.editSection, 200, processorSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Processor; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + this.action = null; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RemoveProcessorMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RemoveProcessorMenuAction.java new file mode 100644 index 0000000..e9c8fb4 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RemoveProcessorMenuAction.java
@@ -0,0 +1,67 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.processor; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.design.actions.RemoveProcessorAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class RemoveProcessorMenuAction extends AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public RemoveProcessorMenuAction() { + super(ProcessorSection.processorSection, 100); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Processor + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + Processor processor = (Processor) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new RemoveProcessorAction(workflow, processor, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RenameProcessorMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RenameProcessorMenuAction.java new file mode 100644 index 0000000..a077726 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/processor/RenameProcessorMenuAction.java
@@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.processor; + +import java.awt.Component; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection; +import net.sf.taverna.t2.workbench.design.actions.RenameProcessorAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class RenameProcessorMenuAction extends AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public RenameProcessorMenuAction() { + super(ConfigureSection.configureSection, 60); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Processor + && getContextualSelection().getParent() instanceof Workflow; + } + + @Override + protected Action createAction() { + Workflow workflow = (Workflow) getContextualSelection().getParent(); + Processor processor = (Processor) getContextualSelection() + .getSelection(); + Component component = getContextualSelection().getRelativeToComponent(); + return new RenameProcessorAction(workflow, processor, component, editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateInputMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateInputMenuAction.java new file mode 100644 index 0000000..6a3b880 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateInputMenuAction.java
@@ -0,0 +1,62 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.workflow; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection; +import net.sf.taverna.t2.workbench.design.actions.AddDataflowInputAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class CreateInputMenuAction extends AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public CreateInputMenuAction() { + super(InsertSection.insertSection, 10); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Workflow; + } + + @Override + protected Action createAction() { + return new AddDataflowInputAction((Workflow) getContextualSelection() + .getSelection(), getContextualSelection() + .getRelativeToComponent(), editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateOutputMenuAction.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateOutputMenuAction.java new file mode 100644 index 0000000..226258c --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/CreateOutputMenuAction.java
@@ -0,0 +1,62 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.workflow; + +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection; +import net.sf.taverna.t2.workbench.design.actions.AddDataflowOutputAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Workflow; + +public class CreateOutputMenuAction extends AbstractContextualMenuAction { + + private EditManager editManager; + private SelectionManager selectionManager; + + public CreateOutputMenuAction() { + super(InsertSection.insertSection, 20); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Workflow; + } + + @Override + protected Action createAction() { + return new AddDataflowOutputAction((Workflow) getContextualSelection() + .getSelection(), getContextualSelection() + .getRelativeToComponent(), editManager, selectionManager); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/WorkflowServiceTemplatesSection.java b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/WorkflowServiceTemplatesSection.java new file mode 100644 index 0000000..d461b5e --- /dev/null +++ b/taverna-workbench-menu-items/src/main/java/net/sf/taverna/t2/ui/menu/items/workflow/WorkflowServiceTemplatesSection.java
@@ -0,0 +1,76 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.ui.menu.items.workflow; + +import java.net.URI; + +import javax.swing.Action; + +import uk.org.taverna.scufl2.api.core.Workflow; + +import net.sf.taverna.t2.lang.ui.ShadedLabel; +import net.sf.taverna.t2.ui.menu.AbstractMenuSection; +import net.sf.taverna.t2.ui.menu.ContextualMenuComponent; +import net.sf.taverna.t2.ui.menu.ContextualSelection; +import net.sf.taverna.t2.ui.menu.DefaultContextualMenu; + +/** + * Menu section containing the actions to add service templates, i.e. activities + * than are not readily runnable but need to be configured first. The actual actions that + * go into this menu can be found in the ui modules for the activities. + * + * @author Alex Nenadic + * + */ +public class WorkflowServiceTemplatesSection extends AbstractMenuSection + implements ContextualMenuComponent { + + private static final String SERVICE_TEMPLATES = "Service templates"; + public static final URI serviceTemplatesSection = URI + .create("http://taverna.sf.net/2009/contextMenu/serviceTemplates"); + private ContextualSelection contextualSelection; + + public WorkflowServiceTemplatesSection() { + super(DefaultContextualMenu.DEFAULT_CONTEXT_MENU, 30, serviceTemplatesSection); + } + + public ContextualSelection getContextualSelection() { + return contextualSelection; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() + && getContextualSelection().getSelection() instanceof Workflow; + } + + public void setContextualSelection(ContextualSelection contextualSelection) { + this.contextualSelection = contextualSelection; + } + + @Override + protected Action createAction() { + DummyAction action = new DummyAction(SERVICE_TEMPLATES); + // Set the colour for the section + action.putValue(AbstractMenuSection.SECTION_COLOR, ShadedLabel.ORANGE); + return action; + } +}
diff --git a/taverna-workbench-menu-items/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-menu-items/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..47f3e0e --- /dev/null +++ b/taverna-workbench-menu-items/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1,46 @@ +net.sf.taverna.t2.ui.menu.items.activityport.ActivityInputPortSection +net.sf.taverna.t2.ui.menu.items.activityport.SetConstantInputPortValueMenuAction +net.sf.taverna.t2.ui.menu.items.activityport.ConnectInputPortMenuActions + +net.sf.taverna.t2.ui.menu.items.activityport.ActivityOutputPortSection +net.sf.taverna.t2.ui.menu.items.activityport.ConnectOutputPortMenuActions + +net.sf.taverna.t2.ui.menu.items.controllink.ConditionSection +net.sf.taverna.t2.ui.menu.items.controllink.RemoveConditionMenuAction + +net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureRunningContextualMenuSection +net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsMenuAction +net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsContextualMenuAction +net.sf.taverna.t2.ui.menu.items.contextualviews.ShowConfigureMenuAction +net.sf.taverna.t2.ui.menu.items.contextualviews.ShowReportsContextualMenuAction + +net.sf.taverna.t2.ui.menu.items.workflow.CreateInputMenuAction +net.sf.taverna.t2.ui.menu.items.workflow.CreateOutputMenuAction +net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection +net.sf.taverna.t2.ui.menu.items.workflow.WorkflowServiceTemplatesSection +net.sf.taverna.t2.ui.menu.items.contextualviews.EditSection +net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection +net.sf.taverna.t2.ui.menu.items.contextualviews.PasteMenuAction + +net.sf.taverna.t2.ui.menu.items.datalink.LinkSection +net.sf.taverna.t2.ui.menu.items.datalink.RemoveLinkMenuAction + +net.sf.taverna.t2.ui.menu.items.merge.MergeSection +net.sf.taverna.t2.ui.menu.items.merge.RemoveMergeMenuAction + +net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowInputPortMenuActions +net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowOutputPortMenuActions +net.sf.taverna.t2.ui.menu.items.ports.EditDataflowInputPortMenuAction +net.sf.taverna.t2.ui.menu.items.ports.EditDataflowOutputPortMenuAction +net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowInputPortMenuAction +net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowOutputPortMenuAction +net.sf.taverna.t2.ui.menu.items.ports.WorkflowInputPortSection +net.sf.taverna.t2.ui.menu.items.ports.WorkflowOutputPortSection + +net.sf.taverna.t2.ui.menu.items.processor.ConditionMenuActions +net.sf.taverna.t2.ui.menu.items.processor.ProcessorSection +net.sf.taverna.t2.ui.menu.items.processor.RenameProcessorMenuAction +net.sf.taverna.t2.ui.menu.items.processor.RemoveProcessorMenuAction + +net.sf.taverna.t2.ui.menu.items.annotated.AnnotatedConfigureMenuAction +
diff --git a/taverna-workbench-menu-items/src/main/resources/META-INF/spring/menu-items-context-osgi.xml b/taverna-workbench-menu-items/src/main/resources/META-INF/spring/menu-items-context-osgi.xml new file mode 100644 index 0000000..b7ba9e4 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/resources/META-INF/spring/menu-items-context-osgi.xml
@@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="ActivityInputPortSection" auto-export="interfaces" /> + <service ref="ActivityOutputPortSection" auto-export="interfaces" /> + <service ref="AnnotatedConfigureMenuAction" auto-export="interfaces" /> + <service ref="ConditionMenuActions" auto-export="interfaces" /> + <service ref="ConditionSection" auto-export="interfaces" /> + <service ref="ConfigureRunningContextualMenuSection" auto-export="interfaces" /> + <service ref="ConfigureSection" auto-export="interfaces" /> + <service ref="ConnectDataflowInputPortMenuActions" auto-export="interfaces" /> + <service ref="ConnectDataflowOutputPortMenuActions" auto-export="interfaces" /> + <service ref="ConnectInputPortMenuActions" auto-export="interfaces" /> + <service ref="ConnectOutputPortMenuActions" auto-export="interfaces" /> + <service ref="CreateInputMenuAction" auto-export="interfaces" /> + <service ref="CreateOutputMenuAction" auto-export="interfaces" /> + <service ref="EditDataflowInputPortMenuAction" auto-export="interfaces" /> + <service ref="EditDataflowOutputPortMenuAction" auto-export="interfaces" /> + <service ref="EditSection" auto-export="interfaces" /> + <service ref="InsertSection" auto-export="interfaces" /> + <service ref="LinkSection" auto-export="interfaces" /> + <service ref="PasteMenuAction" auto-export="interfaces" /> + <service ref="ProcessorSection" auto-export="interfaces" /> + <service ref="RemoveConditionMenuAction" auto-export="interfaces" /> + <service ref="RemoveDataflowInputPortMenuAction" auto-export="interfaces" /> + <service ref="RemoveDataflowOutputPortMenuAction" auto-export="interfaces" /> + <service ref="RemoveLinkMenuAction" auto-export="interfaces" /> + <service ref="RemoveProcessorMenuAction" auto-export="interfaces" /> + <service ref="RenameProcessorMenuAction" auto-export="interfaces" /> + <service ref="SetConstantInputPortValueMenuAction" auto-export="interfaces" /> + <service ref="ShowConfigureMenuAction" auto-export="interfaces" /> + <service ref="ShowDetailsContextualMenuAction" auto-export="interfaces" /> + <service ref="ShowDetailsMenuAction" auto-export="interfaces" /> + <service ref="ShowReportsContextualMenuAction" auto-export="interfaces" /> + <service ref="WorkflowInputPortSection" auto-export="interfaces" /> + <service ref="WorkflowOutputPortSection" auto-export="interfaces" /> + <service ref="WorkflowServiceTemplatesSection" auto-export="interfaces" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="fileManager" interface="net.sf.taverna.t2.workbench.file.FileManager" /> + <reference id="menuManager" interface="net.sf.taverna.t2.ui.menu.MenuManager" /> + <reference id="reportManager" interface="net.sf.taverna.t2.workbench.report.ReportManager" cardinality="0..1" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + <reference id="workbench" interface="net.sf.taverna.t2.workbench.ui.Workbench" cardinality="0..1" /> + <reference id="workbenchConfiguration" interface="net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration" /> + <reference id="activityIconManager" interface="net.sf.taverna.t2.workbench.activityicons.ActivityIconManager" /> + <reference id="colourManager" interface="net.sf.taverna.t2.workbench.configuration.colour.ColourManager" /> + <reference id="serviceRegistry" interface="uk.org.taverna.commons.services.ServiceRegistry" /> + + <list id="annotationBeans" interface="net.sf.taverna.t2.annotation.AnnotationBeanSPI"/> + +</beans:beans>
diff --git a/taverna-workbench-menu-items/src/main/resources/META-INF/spring/menu-items-context.xml b/taverna-workbench-menu-items/src/main/resources/META-INF/spring/menu-items-context.xml new file mode 100644 index 0000000..6208a72 --- /dev/null +++ b/taverna-workbench-menu-items/src/main/resources/META-INF/spring/menu-items-context.xml
@@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="ActivityInputPortSection" class="net.sf.taverna.t2.ui.menu.items.activityport.ActivityInputPortSection" /> + <bean id="ActivityOutputPortSection" class="net.sf.taverna.t2.ui.menu.items.activityport.ActivityOutputPortSection" /> + <bean id="ConditionMenuActions" class="net.sf.taverna.t2.ui.menu.items.processor.ConditionMenuActions"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="activityIconManager" ref="activityIconManager" /> + </bean> + <bean id="AnnotatedConfigureMenuAction" class="net.sf.taverna.t2.ui.menu.items.annotated.AnnotatedConfigureMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="annotationBeans" ref="annotationBeans" /> + </bean> + <bean id="ConditionSection" class="net.sf.taverna.t2.ui.menu.items.controllink.ConditionSection" /> + <bean id="ConfigureRunningContextualMenuSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureRunningContextualMenuSection" /> + <bean id="ConfigureSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ConfigureSection" /> + <bean id="ConnectDataflowInputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowInputPortMenuActions"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + </bean> + <bean id="ConnectDataflowOutputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.ports.ConnectDataflowOutputPortMenuActions"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + </bean> + <bean id="ConnectInputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.activityport.ConnectInputPortMenuActions"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + </bean> + <bean id="ConnectOutputPortMenuActions" class="net.sf.taverna.t2.ui.menu.items.activityport.ConnectOutputPortMenuActions"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="workbenchConfiguration" ref="workbenchConfiguration" /> + <property name="activityIconManager" ref="activityIconManager" /> + <property name="colourManager" ref="colourManager" /> + </bean> + <bean id="CreateInputMenuAction" class="net.sf.taverna.t2.ui.menu.items.workflow.CreateInputMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="CreateOutputMenuAction" class="net.sf.taverna.t2.ui.menu.items.workflow.CreateOutputMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="EditDataflowInputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.EditDataflowInputPortMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="EditDataflowOutputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.EditDataflowOutputPortMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="EditSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.EditSection" /> + <bean id="InsertSection" class="net.sf.taverna.t2.ui.menu.items.contextualviews.InsertSection" /> + <bean id="LinkSection" class="net.sf.taverna.t2.ui.menu.items.datalink.LinkSection" /> + <bean id="PasteMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.PasteMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> + <bean id="ProcessorSection" class="net.sf.taverna.t2.ui.menu.items.processor.ProcessorSection" /> + <bean id="RemoveConditionMenuAction" class="net.sf.taverna.t2.ui.menu.items.controllink.RemoveConditionMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RemoveDataflowInputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowInputPortMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RemoveDataflowOutputPortMenuAction" class="net.sf.taverna.t2.ui.menu.items.ports.RemoveDataflowOutputPortMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RemoveLinkMenuAction" class="net.sf.taverna.t2.ui.menu.items.datalink.RemoveLinkMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RemoveProcessorMenuAction" class="net.sf.taverna.t2.ui.menu.items.processor.RemoveProcessorMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="RenameProcessorMenuAction" class="net.sf.taverna.t2.ui.menu.items.processor.RenameProcessorMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="SetConstantInputPortValueMenuAction" class="net.sf.taverna.t2.ui.menu.items.activityport.SetConstantInputPortValueMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + <property name="serviceRegistry" ref="serviceRegistry" /> + </bean> + <bean id="ShowConfigureMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowConfigureMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="menuManager" ref="menuManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + <bean id="ShowDetailsContextualMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsContextualMenuAction"> + <property name="workbench" ref="workbench" /> + </bean> + <bean id="ShowDetailsMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowDetailsMenuAction"> + <property name="workbench" ref="workbench" /> + </bean> + <bean id="ShowReportsContextualMenuAction" class="net.sf.taverna.t2.ui.menu.items.contextualviews.ShowReportsContextualMenuAction"> + <property name="selectionManager" ref="selectionManager" /> + <property name="reportManager" ref="reportManager" /> + <property name="workbench" ref="workbench" /> + </bean> + <bean id="WorkflowInputPortSection" class="net.sf.taverna.t2.ui.menu.items.ports.WorkflowInputPortSection" /> + <bean id="WorkflowOutputPortSection" class="net.sf.taverna.t2.ui.menu.items.ports.WorkflowOutputPortSection" /> + <bean id="WorkflowServiceTemplatesSection" class="net.sf.taverna.t2.ui.menu.items.workflow.WorkflowServiceTemplatesSection" /> + +</beans>
diff --git a/taverna-workbench-monitor-view/pom.xml b/taverna-workbench-monitor-view/pom.xml new file mode 100644 index 0000000..b4e102f --- /dev/null +++ b/taverna-workbench-monitor-view/pom.xml
@@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-components</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>monitor-view</artifactId> + <packaging>bundle</packaging> + <name>Monitor View</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>workbench-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>graph-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>results-view</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>graph-view</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.platform</groupId> + <artifactId>taverna-report-api</artifactId> + <version>${platform.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.platform</groupId> + <artifactId>taverna-run-api</artifactId> + <version>${platform.version}</version> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>${commons.beanutils.version}</version> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/MonitorViewComponent.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/MonitorViewComponent.java new file mode 100644 index 0000000..7ebd21f --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/MonitorViewComponent.java
@@ -0,0 +1,168 @@ + +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.monitor; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.SOUTH; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; +import static org.apache.batik.ext.swing.GridBagConstants.EAST; +import static org.apache.batik.ext.swing.GridBagConstants.NONE; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; + +import net.sf.taverna.t2.workbench.ui.Updatable; +import net.sf.taverna.t2.workbench.views.monitor.graph.MonitorGraphComponent; +import net.sf.taverna.t2.workbench.views.monitor.progressreport.TableMonitorComponent; + +/** + * Component that shows the progress of a workflow run, either through a graph or + * a table shown in separate tabs. For previous runs, it pulls processor and workflow + * statuses from provenance. + * + * Graph and table are interactive, where clicking on them triggers displaying of + * workflow results or intermediate results in a separate component. + * + * It also contains buttons to pause/resume and stop a workflow run. + * + */ +@SuppressWarnings({"serial","unused"}) +public class MonitorViewComponent extends JPanel implements Updatable { + private MonitorGraphComponent monitorGraph; + private TableMonitorComponent tableMonitorComponent; + + private JTabbedPane tabbedPane; + private JPanel buttonsPanel; + + public MonitorViewComponent() { + super(new BorderLayout()); + tabbedPane = new JTabbedPane(); + buttonsPanel = new JPanel(new GridBagLayout()); + +// buttonsPanel.add(new JLabel("Workflow status")); +// +// buttonsPanel.add(new JButton("Pause")); +// buttonsPanel.add(new JButton("Cancel")); +// buttonsPanel.add(new JButton("Show results")); + + add(tabbedPane, CENTER); + add(buttonsPanel, SOUTH); + } + + public void setMonitorGraph(MonitorGraphComponent monitorGraph) { + this.monitorGraph = monitorGraph; + tabbedPane.add("Graph", monitorGraph); + } + + public void setTableMonitorComponent(TableMonitorComponent tableMonitorComponent) { + this.tableMonitorComponent = tableMonitorComponent; + + JScrollPane scrollPane = new JScrollPane(tableMonitorComponent, + VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + + tabbedPane.add("Progress report", scrollPane); + } + + public void addWorkflowRunStatusLabel(JLabel statusLabel){ + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 0; + gbc.gridy = 0; + + gbc.fill = NONE; + buttonsPanel.add(statusLabel, gbc); + } + + public void addWorkflowPauseButton(JButton workflowRunPauseButton) { + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 1; + gbc.gridy = 0; + + gbc.fill = NONE; + gbc.weightx = 0.0; + buttonsPanel.add(workflowRunPauseButton, gbc); + } + + public void addWorkflowCancelButton(JButton workflowRunCancelButton) { + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 2; + gbc.gridy = 0; + + gbc.fill = NONE; + gbc.weightx = 0.0; + buttonsPanel.add(workflowRunCancelButton, gbc); + } + + public void addReloadWorkflowButton(JButton reloadWorkflowButton) { + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 3; + gbc.gridy = 0; + + gbc.fill = NONE; + gbc.weightx = 1.0; + gbc.anchor = EAST; + buttonsPanel.add(reloadWorkflowButton, gbc); + } + + public void addIntermediateValuesButton(JButton intermediateValuesButton) { + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 4; + gbc.gridy = 0; + + gbc.fill = NONE; + gbc.weightx = 1.0; + gbc.anchor = EAST; + buttonsPanel.add(intermediateValuesButton, gbc); + } + + public void addWorkflowResultsButton(JButton workflowResultsButton) { + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 5; + gbc.gridy = 0; + + gbc.fill = NONE; + gbc.weightx = 0.0; + gbc.anchor = EAST; + buttonsPanel.add(workflowResultsButton, gbc); + } + + @Override + public void update() { + Component selectedComponent = tabbedPane.getSelectedComponent(); + if (selectedComponent instanceof Updatable) + ((Updatable) selectedComponent).update(); + } +}
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitor.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitor.java new file mode 100644 index 0000000..ecaff3e --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitor.java
@@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.monitor.graph; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.closeIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.tickIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.workingIcon; + +import java.util.HashSet; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JLabel; + +import net.sf.taverna.t2.workbench.models.graph.GraphController; +import net.sf.taverna.t2.workbench.ui.Updatable; +import uk.org.taverna.platform.report.ActivityReport; +import uk.org.taverna.platform.report.ProcessorReport; +import uk.org.taverna.platform.report.State; +import uk.org.taverna.platform.report.WorkflowReport; + +/** + * An implementation of the Updatable interface that updates a Graph. + * + * @author David Withers + */ +public class GraphMonitor implements Updatable { + private static final String STATUS_RUNNING = "Running"; + private static final String STATUS_FINISHED = "Finished"; + private static final String STATUS_CANCELLED = "Cancelled"; + + /** + * Workflow run status label - we can only tell of workflow is running or is + * finished from inside this monitor. If workfow run is stopped or paused - + * this will be updated form the run-ui. + */ + private JLabel workflowRunStatusLabel; + /** + * Similarly to {@link #workflowRunStatusLabel} - we disable the pause anc + * cancel buttons when workflow runs is finished + */ + private JButton workflowRunPauseButton; + private JButton workflowRunCancelButton; + private GraphController graphController; + private Set<GraphMonitorNode> processors = new HashSet<>(); + private final WorkflowReport workflowReport; + + public GraphMonitor(GraphController graphController, + WorkflowReport workflowReport) { + this.graphController = graphController; + this.workflowReport = workflowReport; + createMonitorNodes(workflowReport.getSubject().getName(), + workflowReport); + redraw(); + } + + private void createMonitorNodes(String id, WorkflowReport workflowReport) { + for (ProcessorReport processorReport : workflowReport + .getProcessorReports()) { + String processorId = id + processorReport.getSubject().getName(); + processors.add(new GraphMonitorNode(processorId, processorReport, + graphController)); + for (ActivityReport activityReport : processorReport + .getActivityReports()) { + WorkflowReport nestedWorkflowReport = activityReport + .getNestedWorkflowReport(); + if (nestedWorkflowReport != null) + createMonitorNodes(processorId, nestedWorkflowReport); + } + } + } + + public void redraw() { + for (GraphMonitorNode node : processors) + node.redraw(); + } + + @Override + public void update() { + for (GraphMonitorNode node : processors) + node.update(); + // updateState(); + } + + @SuppressWarnings("unused") + private void updateState() { + State state = workflowReport.getState(); + switch (state) { + case COMPLETED: + case FAILED: + workflowRunStatusLabel.setText(STATUS_FINISHED); + workflowRunStatusLabel.setIcon(tickIcon); + workflowRunPauseButton.setEnabled(false); + workflowRunCancelButton.setEnabled(false); + break; + case CANCELLED: + workflowRunStatusLabel.setText(STATUS_CANCELLED); + workflowRunStatusLabel.setIcon(closeIcon); + workflowRunPauseButton.setEnabled(false); + workflowRunCancelButton.setEnabled(false); + break; + case RUNNING: + workflowRunStatusLabel.setText(STATUS_RUNNING); + workflowRunStatusLabel.setIcon(workingIcon); + default: + break; + } + } + + // Set the status label that will be updated from this monitor + public void setWorkflowRunStatusLabel(JLabel workflowRunStatusLabel) { + this.workflowRunStatusLabel = workflowRunStatusLabel; + } + + public void setWorkflowRunPauseButton(JButton workflowRunPauseButton) { + this.workflowRunPauseButton = workflowRunPauseButton; + } + + public void setWorkflowRunCancelButton(JButton workflowRunCancelButton) { + this.workflowRunCancelButton = workflowRunCancelButton; + } +}
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitorNode.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitorNode.java new file mode 100644 index 0000000..8e5c441 --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/GraphMonitorNode.java
@@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.monitor.graph; + +import static java.lang.Math.max; +import net.sf.taverna.t2.workbench.models.graph.GraphController; +import uk.org.taverna.platform.report.ProcessorReport; + +/** + * A <code>MonitorNode</code> that updates a <code>Graph</code> when + * <code>ProcessorReport</code> property changes. + * + * @author David Withers + */ +public class GraphMonitorNode { + private ProcessorReport processorReport; + private GraphController graphController; + private String processorId; + private int queueSize = 0; + private int sentJobs = 0; + private int completedJobs = 0; + private int errors = 0; + + public GraphMonitorNode(String id, ProcessorReport processorReport, + GraphController graphController) { + this.processorReport = processorReport; + this.graphController = graphController; + processorId = id; + } + + /** + * Updates the <code>Graph</code> when changes to properties are detected. + */ + public void update() { + synchronized (graphController) { + boolean queueSizeChanged = false; + boolean sentJobsChanged = false; + boolean completedJobsChanged = false; + boolean errorsChanged = false; + + int newQueueSize = processorReport.getJobsQueued(); + newQueueSize = newQueueSize == -1 ? 0 : newQueueSize; + if (queueSize != newQueueSize) { + queueSize = newQueueSize; + queueSizeChanged = true; + } + + int newSentJobs = processorReport.getJobsStarted(); + if (sentJobs != newSentJobs) { + sentJobs = newSentJobs; + sentJobsChanged = true; + } + + int newCompletedJobs = processorReport.getJobsCompleted(); + if (completedJobs != newCompletedJobs) { + completedJobs = newCompletedJobs; + completedJobsChanged = true; + } + + int newErrors = processorReport.getJobsCompletedWithErrors(); + if (errors != newErrors) { + errors = newErrors; + errorsChanged = true; + } + + if (queueSizeChanged || sentJobsChanged || completedJobsChanged + || errorsChanged) { + if (completedJobsChanged) + graphController.setIteration(processorId, completedJobs); + if (completedJobs > 0) + graphController.setNodeCompleted(processorId, + (completedJobs / (float) (sentJobs + queueSize))); + if (sentJobsChanged) { + // graphController.setEdgeActive(processorId, true); + } + if (errorsChanged && errors > 0) + graphController.setErrors(processorId, errors); + } + } + } + + public void redraw() { + synchronized (graphController) { + queueSize = max(processorReport.getJobsQueued(), 0); + sentJobs = processorReport.getJobsStarted(); + completedJobs = processorReport.getJobsCompleted(); + errors = processorReport.getJobsCompletedWithErrors(); + + graphController.setIteration(processorId, completedJobs); + if (completedJobs > 0) + graphController.setNodeCompleted(processorId, + (completedJobs / (float) (sentJobs + queueSize))); + if (errors > 0) + graphController.setErrors(processorId, errors); + } + } +}
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/MonitorGraphComponent.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/MonitorGraphComponent.java new file mode 100644 index 0000000..0be66ff --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/graph/MonitorGraphComponent.java
@@ -0,0 +1,378 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.monitor.graph; + +import static java.awt.BorderLayout.CENTER; +import static java.awt.BorderLayout.NORTH; +import static javax.swing.Action.SHORT_DESCRIPTION; +import static javax.swing.Action.SMALL_ICON; +import static javax.swing.BoxLayout.PAGE_AXIS; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.refreshIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomInIcon; +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.zoomOutIcon; +import static org.apache.batik.swing.svg.AbstractJSVGComponent.ALWAYS_DYNAMIC; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.Action; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JToolBar; +import javax.swing.Timer; +import javax.swing.border.EmptyBorder; + +import net.sf.taverna.t2.workbench.configuration.colour.ColourManager; +import net.sf.taverna.t2.workbench.configuration.workbench.WorkbenchConfiguration; +import net.sf.taverna.t2.workbench.models.graph.GraphElement; +import net.sf.taverna.t2.workbench.models.graph.GraphEventManager; +import net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphController; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Updatable; +import net.sf.taverna.t2.workbench.views.graph.AutoScrollInteractor; +import net.sf.taverna.t2.workbench.views.graph.menu.ResetDiagramAction; +import net.sf.taverna.t2.workbench.views.graph.menu.ZoomInAction; +import net.sf.taverna.t2.workbench.views.graph.menu.ZoomOutAction; + +import org.apache.batik.swing.JSVGCanvas; +import org.apache.batik.swing.JSVGScrollPane; +import org.apache.batik.swing.gvt.GVTTreeRendererAdapter; +import org.apache.batik.swing.gvt.GVTTreeRendererEvent; +import org.apache.log4j.Logger; + +import uk.org.taverna.platform.run.api.InvalidRunIdException; +import uk.org.taverna.platform.run.api.RunService; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.WorkflowPort; +import uk.org.taverna.scufl2.api.profiles.Profile; + +/** + * Use to display the graph for fresh workflow runs and allow the user to click + * on processors to see the intermediate results for processors pulled from + * provenance. + */ +@SuppressWarnings("serial") +public class MonitorGraphComponent extends JPanel implements Updatable { + private static Logger logger = Logger.getLogger(MonitorGraphComponent.class); + + private SVGGraphController graphController; + private JPanel diagramPanel; + private GraphMonitor graphMonitor; + + private Map<String, SVGGraphController> graphControllerMap = new HashMap<>(); + private Map<String, GraphMonitor> graphMonitorMap = new HashMap<>(); + private Map<String, JPanel> diagramPanelMap = new HashMap<>(); + private Map<String, Action[]> diagramActionsMap = new HashMap<>(); + + @SuppressWarnings("unused") + private Timer timer; + private CardLayout cardLayout; + @SuppressWarnings("unused") + private JLabel statusLabel; + + private final RunService runService; + private final ColourManager colourManager; + private final WorkbenchConfiguration workbenchConfiguration; + private final SelectionManager selectionManager; + + public MonitorGraphComponent(RunService runService, ColourManager colourManager, + WorkbenchConfiguration workbenchConfiguration, SelectionManager selectionManager) { + this.runService = runService; + this.colourManager = colourManager; + this.workbenchConfiguration = workbenchConfiguration; + this.selectionManager = selectionManager; + + cardLayout = new CardLayout(); + setLayout(cardLayout); + +// ActionListener taskPerformer = new ActionListener() { +// public void actionPerformed(ActionEvent evt) { +// if (graphController != null) { +// graphController.redraw(); +// graphMonitor.redraw(); +// } +// timer.stop(); +// } +// }; +// timer = new Timer(100, taskPerformer); +// +// addComponentListener(new ComponentAdapter() { +// public void componentResized(ComponentEvent e) { +// if (timer.isRunning()) { +// timer.restart(); +// } else { +// timer.start(); +// } +// } +// }); + + } + + @Override + protected void finalize() throws Throwable { + if (graphController != null) + graphController.shutdown(); + } + + @Override + public void update() { + if (graphMonitor != null) + graphMonitor.update(); + } + + private JPanel createDiagramPanel(String workflowRun) { + final JPanel diagramPanel = new JPanel(new BorderLayout()); + + try { + Workflow workflow = runService.getWorkflow(workflowRun); + Profile profile = runService.getProfile(workflowRun); + + // get the default diagram settings + // Alignment alignment = Alignment.valueOf(graphViewConfiguration + // .getProperty(GraphViewConfiguration.ALIGNMENT)); + // PortStyle portStyle = PortStyle.valueOf(graphViewConfiguration + // .getProperty(GraphViewConfiguration.PORT_STYLE)); + + // create an SVG canvas + final JSVGCanvas svgCanvas = new JSVGCanvas(null, true, false); + svgCanvas.setEnableZoomInteractor(false); + svgCanvas.setEnableRotateInteractor(false); + svgCanvas.setDocumentState(ALWAYS_DYNAMIC); + + AutoScrollInteractor asi = new AutoScrollInteractor(svgCanvas); + svgCanvas.addMouseListener(asi); + svgCanvas.addMouseMotionListener(asi); + + final JSVGScrollPane svgScrollPane = new MySvgScrollPane(svgCanvas); + + GVTTreeRendererAdapter gvtTreeRendererAdapter = new GVTTreeRendererAdapter() { + @Override + public void gvtRenderingCompleted(GVTTreeRendererEvent e) { + logger.info("Rendered svg"); +// svgScrollPane.reset(); +// diagramPanel.revalidate(); + } + }; + svgCanvas.addGVTTreeRendererListener(gvtTreeRendererAdapter); + + // create a graph controller + SVGGraphController svgGraphController = new SVGGraphController( + workflow, profile, true, svgCanvas, null, null, + colourManager, workbenchConfiguration); + DataflowSelectionModel selectionModel = selectionManager + .getWorkflowRunSelectionModel(workflowRun); + svgGraphController.setDataflowSelectionModel(selectionModel); + svgGraphController + .setGraphEventManager(new MonitorGraphEventManager( + selectionModel)); + + graphControllerMap.put(workflowRun, svgGraphController); + + // Toolbar with actions related to graph + JToolBar graphActionsToolbar = graphActionsToolbar(workflowRun, svgCanvas); + graphActionsToolbar.setAlignmentX(LEFT_ALIGNMENT); + graphActionsToolbar.setFloatable(false); + + // Panel to hold the toolbars + JPanel toolbarPanel = new JPanel(); + toolbarPanel.setLayout(new BoxLayout(toolbarPanel, PAGE_AXIS)); + toolbarPanel.add(graphActionsToolbar); + + diagramPanel.add(toolbarPanel, NORTH); + diagramPanel.add(svgScrollPane, CENTER); + + // JTextField workflowHierarchy = new JTextField(workflow.getName()); + // diagramPanel.add(workflowHierarchy, BorderLayout.SOUTH); + } catch (InvalidRunIdException e) { + diagramPanel.add(new JLabel("Workflow run ID invalid", JLabel.CENTER), + CENTER); + } + return diagramPanel; + } + + protected JToolBar graphActionsToolbar(String workflowRun, JSVGCanvas svgCanvas) { + JToolBar toolBar = new JToolBar(); + toolBar.setAlignmentX(LEFT_ALIGNMENT); + toolBar.setFloatable(false); + + JButton resetDiagramButton = new JButton(); + resetDiagramButton.setBorder(new EmptyBorder(0, 2, 0, 2)); + JButton zoomInButton = new JButton(); + zoomInButton.setBorder(new EmptyBorder(0, 2, 0, 2)); + JButton zoomOutButton = new JButton(); + zoomOutButton.setBorder(new EmptyBorder(0, 2, 0, 2)); + + Action resetDiagramAction = svgCanvas.new ResetTransformAction(); + ResetDiagramAction.setResultsAction(resetDiagramAction); + resetDiagramAction.putValue(SHORT_DESCRIPTION, "Reset Diagram"); + resetDiagramAction.putValue(SMALL_ICON, refreshIcon); + resetDiagramButton.setAction(resetDiagramAction); + + Action zoomInAction = svgCanvas.new ZoomAction(1.2); + ZoomInAction.setResultsAction(zoomInAction); + zoomInAction.putValue(SHORT_DESCRIPTION, "Zoom In"); + zoomInAction.putValue(SMALL_ICON, zoomInIcon); + zoomInButton.setAction(zoomInAction); + + Action zoomOutAction = svgCanvas.new ZoomAction(1 / 1.2); + ZoomOutAction.setResultsAction(zoomOutAction); + zoomOutAction.putValue(SHORT_DESCRIPTION, "Zoom Out"); + zoomOutAction.putValue(SMALL_ICON, zoomOutIcon); + zoomOutButton.setAction(zoomOutAction); + + // diagramActionsMap.put(workflowRun, new Action[] { resetDiagramAction, zoomInAction, + // zoomOutAction }); + + toolBar.add(resetDiagramButton); + toolBar.add(zoomInButton); + toolBar.add(zoomOutButton); + + return toolBar; + } + + // public void setStatus(Status status) { + // switch (status) { + // case RUNNING : + // statusLabel.setText("Workflow running"); + // statusLabel.setIcon(WorkbenchIcons.workingIcon); + // if (workflow != null){ // should not be null really, workflow should be set before this + // method is called + // workflow.setIsRunning(true); + // } + // break; + // case FINISHED : + // statusLabel.setText("Workflow finished"); + // statusLabel.setIcon(WorkbenchIcons.tickIcon); + // if (workflow != null){// should not be null really, workflow should be set before this method + // is called + // workflow.setIsRunning(false); + // } + // break; + // } + // } + + public void setWorkflowRun(String workflowRun) throws InvalidRunIdException { + if (workflowRun != null) { + if (!diagramPanelMap.containsKey(workflowRun)) + addWorkflowRun(workflowRun); + graphController = graphControllerMap.get(workflowRun); + diagramPanel = diagramPanelMap.get(workflowRun); + graphMonitor = graphMonitorMap.get(workflowRun); + Action[] actions = diagramActionsMap.get(workflowRun); + if (actions != null && actions.length == 3) { + ResetDiagramAction.setDesignAction(actions[0]); + ZoomInAction.setDesignAction(actions[1]); + ZoomOutAction.setDesignAction(actions[2]); + } + cardLayout.show(this, String.valueOf(diagramPanel.hashCode())); + // graphController.redraw(); + } + } + + public void addWorkflowRun(String workflowRun) throws InvalidRunIdException { + JPanel newDiagramPanel = createDiagramPanel(workflowRun); + add(newDiagramPanel, String.valueOf(newDiagramPanel.hashCode())); + diagramPanelMap.put(workflowRun, newDiagramPanel); + graphMonitorMap.put(workflowRun, + new GraphMonitor(graphControllerMap.get(workflowRun), + runService.getWorkflowReport(workflowRun))); + } + + public void removeWorkflowRun(String workflowRun) { + JPanel removedDiagramPanel = diagramPanelMap.remove(workflowRun); + if (removedDiagramPanel != null) + remove(removedDiagramPanel); + SVGGraphController removedController = graphControllerMap + .remove(workflowRun); + if (removedController != null) + removedController.shutdown(); + graphMonitorMap.remove(workflowRun); + diagramActionsMap.remove(workflowRun); + } + + private class MonitorGraphEventManager implements GraphEventManager { + private final DataflowSelectionModel selectionModel; + + public MonitorGraphEventManager(DataflowSelectionModel selectionModel) { + this.selectionModel = selectionModel; + } + + @Override + public void mouseClicked(final GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + Object workflowObject = graphElement.getWorkflowBean(); + if (workflowObject instanceof Processor + || workflowObject instanceof WorkflowPort) + selectionModel.addSelection(workflowObject); + } + + @Override + public void mouseDown(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + } + + @Override + public void mouseMoved(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + } + + @Override + public void mouseUp(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + } + + @Override + public void mouseOut(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + } + + @Override + public void mouseOver(GraphElement graphElement, short button, + boolean altKey, boolean ctrlKey, boolean metaKey, int x, int y, + int screenX, int screenY) { + } + } + + private class MySvgScrollPane extends JSVGScrollPane { + private static final long serialVersionUID = 6890422410714378543L; + + public MySvgScrollPane(JSVGCanvas canvas) { + super(canvas); + } + + @Override + public void reset() { + super.resizeScrollBars(); + super.reset(); + } + } +}
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/TableMonitorComponent.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/TableMonitorComponent.java new file mode 100644 index 0000000..bb82421 --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/TableMonitorComponent.java
@@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (C) 2013 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.monitor.progressreport; + +import java.awt.CardLayout; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.Updatable; +import uk.org.taverna.platform.report.WorkflowReport; +import uk.org.taverna.platform.run.api.InvalidRunIdException; +import uk.org.taverna.platform.run.api.RunService; + +/** + * @author David Withers + */ +@SuppressWarnings("serial") +public class TableMonitorComponent extends JPanel implements Updatable { + private Map<String, WorkflowRunProgressTreeTable> tableMap = new HashMap<>(); + private Map<String, WorkflowRunProgressTreeTableModel> tableModelMap = new HashMap<>(); + private WorkflowRunProgressTreeTable table; + private WorkflowRunProgressTreeTableModel tableModel; + private CardLayout cardLayout; + + private final RunService runService; + private final SelectionManager selectionManager; + private final ActivityIconManager activityIconManager; + + public TableMonitorComponent(RunService runService, + SelectionManager selectionManager, + ActivityIconManager activityIconManager) { + this.runService = runService; + this.selectionManager = selectionManager; + this.activityIconManager = activityIconManager; + + cardLayout = new CardLayout(); + setLayout(cardLayout); + } + + public void setWorkflowRun(String workflowRun) throws InvalidRunIdException { + if (workflowRun != null) { + if (!tableMap.containsKey(workflowRun)) + addWorkflowRun(workflowRun); + table = tableMap.get(workflowRun); + tableModel = tableModelMap.get(workflowRun); + cardLayout.show(this, String.valueOf(table.hashCode())); + } + } + + public void addWorkflowRun(String workflowRun) throws InvalidRunIdException { + WorkflowReport workflowReport = runService + .getWorkflowReport(workflowRun); + WorkflowRunProgressTreeTableModel newTableModel = new WorkflowRunProgressTreeTableModel( + workflowReport); + WorkflowRunProgressTreeTable newTable = new WorkflowRunProgressTreeTable( + newTableModel, activityIconManager, + selectionManager.getWorkflowRunSelectionModel(workflowRun)); + + add(new JScrollPane(newTable), String.valueOf(newTable.hashCode())); + tableMap.put(workflowRun, newTable); + tableModelMap.put(workflowRun, newTableModel); + } + + public void removeWorkflowRun(String workflowRun) { + WorkflowRunProgressTreeTable removedTable = tableMap + .remove(workflowRun); + if (removedTable != null) + remove(removedTable); + tableModelMap.remove(workflowRun); + } + + @Override + public void update() { + if (tableModel != null) + tableModel.update(); + } +}
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeCellRenderer.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeCellRenderer.java new file mode 100644 index 0000000..4c4d6ad --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeCellRenderer.java
@@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.monitor.progressreport; + +import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.workflowExplorerIcon; + +import java.awt.Component; +import java.util.Set; + +import javax.swing.Icon; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; + +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import uk.org.taverna.platform.report.ActivityReport; +import uk.org.taverna.platform.report.ProcessorReport; +import uk.org.taverna.platform.report.WorkflowReport; + +/** + * Cell renderer for Workflow Explorer tree. + * + * @author Alex Nenadic + */ +@SuppressWarnings("serial") +public class WorkflowRunProgressTreeCellRenderer extends DefaultTreeCellRenderer { + private ActivityIconManager activityIconManager; + + public WorkflowRunProgressTreeCellRenderer(ActivityIconManager activityIconManager) { + this.activityIconManager = activityIconManager; + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean sel, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + Component result = super.getTreeCellRendererComponent(tree, value, sel, + expanded, leaf, row, hasFocus); + + Object userObject = ((DefaultMutableTreeNode) value).getUserObject(); + + WorkflowRunProgressTreeCellRenderer renderer = (WorkflowRunProgressTreeCellRenderer) result; + + if (userObject instanceof WorkflowReport) // the root node + renderWorkflowReport(renderer, (WorkflowReport) userObject); + else if (userObject instanceof ProcessorReport) + renderProcessorReport(renderer, (ProcessorReport) userObject); + + return result; + } + + private void renderWorkflowReport( + WorkflowRunProgressTreeCellRenderer renderer, + WorkflowReport workflowReport) { + renderer.setIcon(workflowExplorerIcon); + renderer.setText(workflowReport.getSubject().getName()); + } + + private void renderProcessorReport(WorkflowRunProgressTreeCellRenderer renderer, + ProcessorReport processorReport) { + /* + * Get the activity associated with the processor - currently only + * one gets displayed + */ + Set<ActivityReport> activityReports = processorReport + .getActivityReports(); + String text = processorReport.getSubject().getName(); + if (!activityReports.isEmpty()) { + ActivityReport activityReport = activityReports.iterator() + .next(); + Icon icon = activityIconManager.iconForActivity(activityReport + .getSubject()); + if (icon != null) + renderer.setIcon(icon); + } + renderer.setText(text); + } +}
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTable.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTable.java new file mode 100644 index 0000000..f1f031c --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTable.java
@@ -0,0 +1,112 @@ +package net.sf.taverna.t2.workbench.views.monitor.progressreport; + +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import net.sf.taverna.t2.lang.observer.Observable; +import net.sf.taverna.t2.lang.observer.SwingAwareObserver; +import net.sf.taverna.t2.lang.ui.treetable.JTreeTable; +import net.sf.taverna.t2.workbench.activityicons.ActivityIconManager; +import net.sf.taverna.t2.workbench.selection.DataflowSelectionModel; +import net.sf.taverna.t2.workbench.selection.events.DataflowSelectionMessage; +import uk.org.taverna.platform.report.StatusReport; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.core.Workflow; +import uk.org.taverna.scufl2.api.port.WorkflowPort; + +@SuppressWarnings("serial") +public class WorkflowRunProgressTreeTable extends JTreeTable { + private final WorkflowRunProgressTreeTableModel treeTableModel; + private final DataflowSelectionModel selectionModel; + private final DataflowSelectionObserver dataflowSelectionObserver; + + public WorkflowRunProgressTreeTable( + WorkflowRunProgressTreeTableModel treeTableModel, + ActivityIconManager activityIconManager, + DataflowSelectionModel selectionModel) { + super(treeTableModel); + + this.treeTableModel = treeTableModel; + this.selectionModel = selectionModel; + + this.tree.setCellRenderer(new WorkflowRunProgressTreeCellRenderer( + activityIconManager)); + this.tree.setEditable(false); + this.tree.setExpandsSelectedPaths(true); + this.tree.setDragEnabled(false); + this.tree.setScrollsOnExpand(false); + + getTableHeader().setReorderingAllowed(false); + getSelectionModel().setSelectionMode( + ListSelectionModel.SINGLE_SELECTION); + getSelectionModel().addListSelectionListener( + new TableSelectionListener()); + + dataflowSelectionObserver = new DataflowSelectionObserver(); + selectionModel.addObserver(dataflowSelectionObserver); + } + + @Override + protected void finalize() throws Throwable { + selectionModel.removeObserver(dataflowSelectionObserver); + } + + /** + * Return object in the tree part of this JTreeTable that corresponds to + * this row. It will either be a workflow (tree root) or a processor. + */ + public Object getTreeObjectForRow(int row) { + TreePath path = tree.getPathForRow(row); + if (path == null) + return null; + return ((DefaultMutableTreeNode) path.getLastPathComponent()) + .getUserObject(); + } + + public void setSelectedRowForObject(Object workflowObject) { + // Find the row for the object in the tree + DefaultMutableTreeNode node = treeTableModel.getNodeForObject(workflowObject); + if (node != null) { + TreeNode[] path = node.getPath(); + tree.scrollPathToVisible(new TreePath(path)); + int row = tree.getRowForPath(new TreePath(path)); + if (row >= 0) + // Set selected row on the table + setRowSelectionInterval(row, row); + } + } + + private class DataflowSelectionObserver extends SwingAwareObserver<DataflowSelectionMessage> { + @Override + public void notifySwing(Observable<DataflowSelectionMessage> sender, + DataflowSelectionMessage message) { + for (Object selection : selectionModel.getSelection()) { + if (selection instanceof Processor + || selection instanceof Workflow) + setSelectedRowForObject(selection); + else if (selection instanceof WorkflowPort) + setSelectedRowForObject(((WorkflowPort) selection) + .getParent()); + } + } + } + + private class TableSelectionListener implements ListSelectionListener { + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) + return; + int selectedRow = getSelectedRow(); + if (selectedRow < 0) + return; + Object selection = getTreeObjectForRow(selectedRow); + if (selection instanceof StatusReport) + selectionModel.addSelection(((StatusReport<?, ?>) selection) + .getSubject()); + } + } +}
diff --git a/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTableModel.java b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTableModel.java new file mode 100644 index 0000000..1e2e6e9 --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/java/net/sf/taverna/t2/workbench/views/monitor/progressreport/WorkflowRunProgressTreeTableModel.java
@@ -0,0 +1,279 @@ +/******************************************************************************* + * Copyright (C) 2007 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.views.monitor.progressreport; + +import static java.util.Collections.nCopies; +import static net.sf.taverna.t2.workbench.views.monitor.progressreport.WorkflowRunProgressTreeTableModel.Column.values; +import static net.sf.taverna.t2.workbench.views.results.processor.ProcessorResultsComponent.formatMilliseconds; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; + +import net.sf.taverna.t2.lang.ui.treetable.AbstractTreeTableModel; +import net.sf.taverna.t2.lang.ui.treetable.TreeTableModel; +import uk.org.taverna.platform.report.ActivityReport; +import uk.org.taverna.platform.report.Invocation; +import uk.org.taverna.platform.report.ProcessorReport; +import uk.org.taverna.platform.report.State; +import uk.org.taverna.platform.report.StatusReport; +import uk.org.taverna.platform.report.WorkflowReport; + +/** + * A TreeTableModel used to display the progress of a workfow run. The workflow + * and its processors (some of which may be nested) are represented as a tree, + * where their properties, such as status, start and finish times, number of + * iterations, etc. are represented as table columns. + * + * @author Alex Nenadic + * @author Stian Soiland-Reyes + * @author David Withers + */ +public class WorkflowRunProgressTreeTableModel extends AbstractTreeTableModel { + public static final String NAME = "Name"; + public static final String STATUS = "Status"; + public static final String AVERAGE_ITERATION_TIME = "Average time per iteration"; + public static final String ITERATIONS = "Queued iterations"; + public static final String ITERATIONS_DONE = "Completed iterations"; + public static final String ITERATIONS_FAILED = "Iterations with errors"; + + public enum Column { + NAME("Name", TreeTableModel.class), STATUS("Status"), ITERATIONS_QUEUED( + "Queued iterations"), ITERATIONS_DONE("Iterations done"), ITERATIONS_FAILED( + "Iterations w/errors"), AVERAGE_ITERATION_TIME( + "Average time/iteration"), START_TIME( + "First iteration started", Date.class), FINISH_TIME( + "Last iteration ended", Date.class); + + private final String label; + private final Class<?> columnClass; + + Column(String label) { + this(label, String.class); + } + + Column(String label, Class<?> columnClass) { + this.label = label; + this.columnClass = columnClass; + } + + public Class<?> getColumnClass() { + return columnClass; + } + + public String getLabel() { + return label; + } + + @Override + public String toString() { + return label; + } + } + + // Table data (maps workflow element nodes to column data associated with them) + private final Map<DefaultMutableTreeNode, List<Object>> data = new HashMap<>(); + private final Map<Object, DefaultMutableTreeNode> nodeForObject = new HashMap<>(); + private final DefaultMutableTreeNode rootNode; + + public WorkflowRunProgressTreeTableModel(WorkflowReport workflowReport) { + super(new DefaultMutableTreeNode(workflowReport)); + rootNode = (DefaultMutableTreeNode) this.getRoot(); + createTree(workflowReport, rootNode); + } + + private void createTree(WorkflowReport workflowReport, DefaultMutableTreeNode root) { + // If this is the root of the tree rather than a root of the nested sub-tree + if (root.equals(rootNode)) { + List<Object> columnData = new ArrayList<>(nCopies(values().length, + null)); + setColumnValues(workflowReport, columnData); + nodeForObject.put(workflowReport, root); + nodeForObject.put(workflowReport.getSubject(), root); + data.put(root, columnData); + } + // One row for each processor + for (ProcessorReport processorReport : workflowReport.getProcessorReports()) { + List<Object> columnData = new ArrayList<>(nCopies(values().length, + null)); + DefaultMutableTreeNode processorNode = new DefaultMutableTreeNode( + processorReport); + setColumnValues(processorReport, columnData); + nodeForObject.put(processorReport, processorNode); + nodeForObject.put(processorReport.getSubject(), processorNode); + data.put(processorNode, columnData); + root.add(processorNode); + + Set<ActivityReport> activityReports = processorReport.getActivityReports(); + if (activityReports.size() == 1) { + WorkflowReport nestedWorkflowReport = activityReports.iterator().next() + .getNestedWorkflowReport(); + if (nestedWorkflowReport != null) + // create sub-tree + createTree(nestedWorkflowReport, processorNode); + } + } + } + + public DefaultMutableTreeNode getNodeForObject(Object workflowObject) { + return nodeForObject.get(workflowObject); + } + + public void setColumnValues(StatusReport<?, ?> report, List<Object> columns) { + if (report instanceof WorkflowReport) { + WorkflowReport workflowReport = (WorkflowReport) report; + + State state = workflowReport.getState(); + Date startTime = workflowReport.getStartedDate(); + Date finishTime = workflowReport.getCompletedDate(); + + columns.set(Column.NAME.ordinal(), workflowReport.getSubject().getName()); + columns.set(Column.STATUS.ordinal(), state); + columns.set(Column.ITERATIONS_DONE.ordinal(), "-"); + columns.set(Column.ITERATIONS_FAILED.ordinal(), "-"); + columns.set(Column.ITERATIONS_QUEUED.ordinal(), "-"); + columns.set(Column.START_TIME.ordinal(), startTime); + columns.set(Column.FINISH_TIME.ordinal(), finishTime); + if (startTime != null && finishTime != null) + columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), + formatMilliseconds(finishTime.getTime() - finishTime.getTime())); + else + columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), "-"); + } else if (report instanceof ProcessorReport) { + ProcessorReport processorReport = (ProcessorReport) report; + + State state = processorReport.getState(); + SortedSet<Invocation> invocations = processorReport + .getInvocations(); + + columns.set(Column.NAME.ordinal(), processorReport.getSubject() + .getName()); + columns.set(Column.STATUS.ordinal(), state); + columns.set(Column.ITERATIONS_QUEUED.ordinal(), + processorReport.getJobsQueued()); + columns.set(Column.ITERATIONS_DONE.ordinal(), + processorReport.getJobsCompleted()); + columns.set(Column.ITERATIONS_FAILED.ordinal(), + processorReport.getJobsCompletedWithErrors()); + + if (invocations.isEmpty()) { + columns.set(Column.START_TIME.ordinal(), null); + columns.set(Column.FINISH_TIME.ordinal(), null); + columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), null); // iteration + } else { + Date earliestStartTime = invocations.first().getStartedDate(); + Date latestFinishTime = invocations.first().getCompletedDate(); + long totalInvocationTime = 0; + int finishedInvocations = 0; + + for (Invocation invocation : invocations) { + // Get the earliest start time of all invocations + Date startTime = invocation.getStartedDate(); + if (startTime != null) { + if (startTime.before(earliestStartTime)) + earliestStartTime = startTime; + // Get the latest finish time of all invocations + Date finishTime = invocation.getCompletedDate(); + if (finishTime != null) { + if (finishTime.after(latestFinishTime)) { + latestFinishTime = finishTime; + totalInvocationTime += finishTime.getTime() - startTime.getTime(); + } + finishedInvocations++; + } + } + } + + columns.set(Column.START_TIME.ordinal(), earliestStartTime); + columns.set(Column.FINISH_TIME.ordinal(), latestFinishTime); + if (finishedInvocations > 0) { + long averageTime = totalInvocationTime / finishedInvocations; + columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), + formatMilliseconds(averageTime)); + } else + columns.set(Column.AVERAGE_ITERATION_TIME.ordinal(), "-"); + } + } + } + + public void update() { + update(rootNode); + fireTreeNodesChanged(rootNode, rootNode.getPath(), null, null); + } + + private void update(DefaultMutableTreeNode node) { + setColumnValues((StatusReport<?, ?>) node.getUserObject(), data.get(node)); + for (int i = 0; i < node.getChildCount(); i++) + update((DefaultMutableTreeNode) node.getChildAt(i)); + } + + // + // The TreeModel interface + // + + @Override + public int getChildCount(Object node) { + return ((TreeNode) node).getChildCount(); + } + + @Override + public Object getChild(Object node, int i) { + return ((TreeNode) node).getChildAt(i); + } + + // + // The TreeTableNode interface. + // + + @Override + public int getColumnCount() { + return values().length; + } + + @Override + public String getColumnName(int column) { + return values()[column].getLabel(); + } + + @Override + public Class<?> getColumnClass(int column) { + return values()[column].getColumnClass(); + } + + public Object getValueAt(Object node, Column column) { + return getValueAt(node, column.ordinal()); + } + + @Override + public Object getValueAt(Object node, int column) { + List<Object> columnValues = data.get(node); + if (columnValues == null) + return null; + return columnValues.get(column); + } +} \ No newline at end of file
diff --git a/taverna-workbench-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI b/taverna-workbench-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI new file mode 100644 index 0000000..cf13a46 --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentFactorySPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.views.monitor.graph.MonitorGraphComponentFactory \ No newline at end of file
diff --git a/taverna-workbench-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI b/taverna-workbench-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI new file mode 100644 index 0000000..30f1743 --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.zaria.UIComponentSPI
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.views.monitor.graph.MonitorGraphComponent \ No newline at end of file
diff --git a/taverna-workbench-monitor-view/src/main/resources/META-INF/spring/monitor-view-context-osgi.xml b/taverna-workbench-monitor-view/src/main/resources/META-INF/spring/monitor-view-context-osgi.xml new file mode 100644 index 0000000..ab22b97 --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/resources/META-INF/spring/monitor-view-context-osgi.xml
@@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + +</beans:beans>
diff --git a/taverna-workbench-monitor-view/src/main/resources/META-INF/spring/monitor-view-context.xml b/taverna-workbench-monitor-view/src/main/resources/META-INF/spring/monitor-view-context.xml new file mode 100644 index 0000000..d662d87 --- /dev/null +++ b/taverna-workbench-monitor-view/src/main/resources/META-INF/spring/monitor-view-context.xml
@@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + +</beans>
diff --git a/taverna-workbench-parallelize-ui/pom.xml b/taverna-workbench-parallelize-ui/pom.xml new file mode 100644 index 0000000..6fba337 --- /dev/null +++ b/taverna-workbench-parallelize-ui/pom.xml
@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-exts</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-exts</groupId> + <artifactId>parallelize-ui</artifactId> + <packaging>bundle</packaging> + <name>Parallelize layer contextual view</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>edits-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>selection-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>${jackson-databind.version}</version> + </dependency> + </dependencies> +</project>
diff --git a/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigurationPanel.java b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigurationPanel.java new file mode 100644 index 0000000..f951da6 --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigurationPanel.java
@@ -0,0 +1,99 @@ +package net.sf.taverna.t2.workbench.parallelize; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; + +import uk.org.taverna.scufl2.api.configurations.Configuration; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +@SuppressWarnings("serial") +public class ParallelizeConfigurationPanel extends JPanel { + + private ObjectNode json; + private JTextField maxJobsField = new JTextField(10); + private final String processorName; + + public ParallelizeConfigurationPanel(Configuration configuration, String processorName) { + if (configuration.getJson().has("parallelize")) { + json = (ObjectNode) configuration.getJson().get("parallelize").deepCopy(); + } else { + json = configuration.getJsonAsObjectNode().objectNode(); + } + this.processorName = processorName; + this.setLayout(new GridBagLayout()); + this.setBorder(new EmptyBorder(10,10,10,10)); + populate(); + } + + public void populate() { + this.removeAll(); + GridBagConstraints gbc = new GridBagConstraints(); + JLabel jobs = new JLabel("<html><body>Maximum numbers of items to process at the same time</body></html>"); + + jobs.setBorder(new EmptyBorder(0,0,0,10)); + gbc.weightx = 0.8; + gbc.fill = GridBagConstraints.HORIZONTAL; + this.add(jobs, gbc); + if (json.has("maximumJobs")) { + maxJobsField.setText(json.get("maximumJobs").asText()); + } else { + maxJobsField.setText("1"); + } + gbc.weightx = 0.2; + gbc.fill = GridBagConstraints.HORIZONTAL; + this.add(maxJobsField, gbc); + gbc.weightx = 0.1; + this.add(new JPanel(), gbc); + + gbc.gridy=1; + gbc.gridx=0; + gbc.gridwidth=3; + gbc.weightx=0; + gbc.anchor = GridBagConstraints.SOUTH; + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 1.0; + JLabel explanationLabel = new JLabel("<html><body><small>" + + "The service <b>" + processorName + "</b> will be invoked as soon as the required inputs " + + "for an iteration are available, but no more than the maximum number of items " + + "will be invoked at the same time." + + "</small></body></html>"); + this.add(explanationLabel, gbc); + + this.setPreferredSize(new Dimension(350, 170)); + } + + public boolean validateConfig() { + String errorText = ""; + int maxJobs = -1; + try { + maxJobs = Integer.parseInt(maxJobsField.getText()); + if (maxJobs < 1) { + errorText += "The maximum number of items must be a positive integer.\n"; + } + } + catch (NumberFormatException e) { + errorText += "The maximum number of items must be an integer.\n"; + } + + if (errorText.length() > 0) { + JOptionPane.showMessageDialog(this, errorText, "", JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + public JsonNode getJson() { + json.put("maximumJobs", maxJobsField.getText()); + return json; + } + +}
diff --git a/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureAction.java b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureAction.java new file mode 100644 index 0000000..6727c59 --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureAction.java
@@ -0,0 +1,185 @@ +/** + * + */ +package net.sf.taverna.t2.workbench.parallelize; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.util.Iterator; +import java.util.Map.Entry; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import net.sf.taverna.t2.workbench.edits.Edit; +import net.sf.taverna.t2.workbench.edits.EditException; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.helper.HelpEnabledDialog; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workflow.edits.AddChildEdit; +import net.sf.taverna.t2.workflow.edits.ChangeJsonEdit; + +import org.apache.log4j.Logger; + +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Processor; +import uk.org.taverna.scufl2.api.profiles.Profile; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * @author alanrw + * @author David Withers + */ +@SuppressWarnings("serial") +public class ParallelizeConfigureAction extends AbstractAction { + + private Frame owner; + private final Processor processor; + private final ParallelizeContextualView parallelizeContextualView; + + private EditManager editManager; + + private static Logger logger = Logger.getLogger(ParallelizeConfigureAction.class); + + private final Scufl2Tools scufl2Tools = new Scufl2Tools(); + private final SelectionManager selectionManager; + + public ParallelizeConfigureAction(Frame owner, + ParallelizeContextualView parallelizeContextualView, + Processor processor, EditManager editManager, SelectionManager selectionManager) { + super("Configure"); + this.owner = owner; + this.parallelizeContextualView = parallelizeContextualView; + this.processor = processor; + this.editManager = editManager; + this.selectionManager = selectionManager; + } + + public void actionPerformed(ActionEvent e) { + String processorName = processor.getName(); + String title = "Parallel jobs for service " + processorName; + final JDialog dialog = new HelpEnabledDialog(owner, title, true); + Configuration configuration; + try { + configuration = scufl2Tools.configurationFor(processor, selectionManager.getSelectedProfile()); + } catch (IndexOutOfBoundsException ex) { + configuration = new Configuration(); + } + ParallelizeConfigurationPanel parallelizeConfigurationPanel = new ParallelizeConfigurationPanel(configuration, processorName); + dialog.add(parallelizeConfigurationPanel, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout()); + + JButton okButton = new JButton(new OKAction(dialog, + parallelizeConfigurationPanel)); + buttonPanel.add(okButton); + + JButton resetButton = new JButton(new ResetAction( + parallelizeConfigurationPanel)); + buttonPanel.add(resetButton); + + JButton cancelButton = new JButton(new CancelAction(dialog)); + buttonPanel.add(cancelButton); + + dialog.add(buttonPanel, BorderLayout.SOUTH); + dialog.pack(); + dialog.setLocationRelativeTo(null); + dialog.setVisible(true); + } + + public class ResetAction extends AbstractAction { + + private final ParallelizeConfigurationPanel parallelizeConfigurationPanel; + + public ResetAction(ParallelizeConfigurationPanel parallelizeConfigurationPanel) { + super("Reset"); + this.parallelizeConfigurationPanel = parallelizeConfigurationPanel; + } + + public void actionPerformed(ActionEvent e) { + parallelizeConfigurationPanel.populate(); + } + + } + + public class OKAction extends AbstractAction { + + private final ParallelizeConfigurationPanel parallelizeConfigurationPanel; + private final JDialog dialog; + + public OKAction(JDialog dialog, ParallelizeConfigurationPanel parallelizeConfigurationPanel) { + super("OK"); + this.dialog = dialog; + this.parallelizeConfigurationPanel = parallelizeConfigurationPanel; + } + + public void actionPerformed(ActionEvent e) { + if (parallelizeConfigurationPanel.validateConfig()) { + try { + try { + Configuration configuration = scufl2Tools.configurationFor(processor, selectionManager.getSelectedProfile()); + ObjectNode json = configuration.getJsonAsObjectNode().deepCopy(); + ObjectNode parallelizeNode = null; + if (json.has("parallelize")) { + parallelizeNode = (ObjectNode) json.get("parallelize"); + } else { + parallelizeNode = json.objectNode(); + json.put("parallelize", parallelizeNode); + } + JsonNode newParallelizeNode = parallelizeConfigurationPanel.getJson(); + Iterator<Entry<String, JsonNode>> fields = newParallelizeNode.fields(); + while (fields.hasNext()) { + Entry<String, JsonNode> entry = fields.next(); + parallelizeNode.set(entry.getKey(), entry.getValue()); + } + Edit<Configuration> edit = new ChangeJsonEdit(configuration, json); + editManager.doDataflowEdit(selectionManager.getSelectedWorkflowBundle(), edit); + } catch (IndexOutOfBoundsException ex) { + Configuration configuration = new Configuration(); + configuration.setConfigures(processor); + ObjectNode json = configuration.getJsonAsObjectNode(); + json.put("parallelize", parallelizeConfigurationPanel.getJson()); + Edit<Profile> edit = new AddChildEdit<Profile>(selectionManager.getSelectedProfile(), configuration); + editManager.doDataflowEdit(selectionManager.getSelectedWorkflowBundle(), edit); + } + dialog.setVisible(false); + if (parallelizeContextualView != null) { + parallelizeContextualView.refreshView(); + } + } catch (EditException e1) { + logger.warn("Could not configure jobs", e1); + JOptionPane.showMessageDialog(owner, "Could not configure jobs", + "An error occured when configuring jobs: " + e1.getMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + } + + } + + public class CancelAction extends AbstractAction { + + private final JDialog dialog; + + public CancelAction(JDialog dialog) { + super("Cancel"); + this.dialog = dialog; + + } + + public void actionPerformed(ActionEvent e) { + dialog.setVisible(false); + } + + } + +}
diff --git a/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureMenuAction.java b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureMenuAction.java new file mode 100644 index 0000000..194d81e --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeConfigureMenuAction.java
@@ -0,0 +1,77 @@ +/********************************************************************** + * Copyright (C) 2007-2009 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + **********************************************************************/ +package net.sf.taverna.t2.workbench.parallelize; + +import java.awt.event.ActionEvent; +import java.net.URI; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import net.sf.taverna.t2.ui.menu.AbstractContextualMenuAction; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import uk.org.taverna.scufl2.api.core.Processor; + +public class ParallelizeConfigureMenuAction extends AbstractContextualMenuAction { + + public static final URI configureRunningSection = URI + .create("http://taverna.sf.net/2009/contextMenu/configureRunning"); + + private static final URI PARALLELIZE_CONFIGURE_URI = URI + .create("http://taverna.sf.net/2008/t2workbench/parallelizeConfigure"); + + public static URI TYPE = URI.create("http://ns.taverna.org.uk/2010/scufl2/taverna/dispatchlayer/Parallelize"); + + private EditManager editManager; + + private SelectionManager selectionManager; + + public ParallelizeConfigureMenuAction() { + super(configureRunningSection, 10, PARALLELIZE_CONFIGURE_URI); + } + + @SuppressWarnings("serial") + @Override + protected Action createAction() { + return new AbstractAction("Parallel jobs...") { + public void actionPerformed(ActionEvent e) { + Processor processor = (Processor) getContextualSelection().getSelection(); + ParallelizeConfigureAction parallelizeConfigureAction = new ParallelizeConfigureAction( + null, null, processor, editManager, selectionManager); + parallelizeConfigureAction.actionPerformed(e); + } + }; + } + + public boolean isEnabled() { + return super.isEnabled() && (getContextualSelection().getSelection() instanceof Processor); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualView.java b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualView.java new file mode 100644 index 0000000..22d2da1 --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualView.java
@@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.parallelize; + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.util.List; + +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JTextArea; + +import net.sf.taverna.t2.lang.ui.ReadOnlyTextArea; +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import uk.org.taverna.scufl2.api.common.Scufl2Tools; +import uk.org.taverna.scufl2.api.configurations.Configuration; +import uk.org.taverna.scufl2.api.core.Processor; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * View of a processor, including it's iteration stack, activities, etc. + * + * @author Alan R Williams + * + */ +@SuppressWarnings("serial") +public class ParallelizeContextualView extends ContextualView { + + private final Scufl2Tools scufl2Tools = new Scufl2Tools(); + + private Processor processor; + + private JPanel panel; + + private final EditManager editManager; + + private final SelectionManager selectionManager; + + public ParallelizeContextualView(Processor processor, EditManager editManager, SelectionManager selectionManager) { + super(); + this.processor = processor; + this.editManager = editManager; + this.selectionManager = selectionManager; + initialise(); + initView(); + } + + @Override + public void refreshView() { + initialise(); + } + + private void initialise() { + if (panel == null) { + panel = createPanel(); + } else { + panel.removeAll(); + } + + JTextArea textArea = new ReadOnlyTextArea(); + textArea.setEditable(false); + String maxJobs = "1"; + for (Configuration configuration : scufl2Tools.configurationsFor(processor, selectionManager.getSelectedProfile())) { + JsonNode processorConfig = configuration.getJson(); + if (processorConfig.has("parallelize")) { + JsonNode parallelizeConfig = processorConfig.get("parallelize"); + if (parallelizeConfig.has("maximumJobs")) { + maxJobs = parallelizeConfig.get("maximumJobs").asText(); + } + } + } + textArea.setText("The maximum number of jobs is " + maxJobs); + textArea.setBackground(panel.getBackground()); + panel.add(textArea, BorderLayout.CENTER); + revalidate(); + } + + + @Override + public JComponent getMainFrame() { + return panel; + } + + @Override + public String getViewTitle() { + return "Parallel jobs"; + } + + protected JPanel createPanel() { + JPanel result = new JPanel(); + result.setLayout(new BorderLayout()); + + + return result; + } + + @Override + public int getPreferredPosition() { + return 400; + } + + @Override + public Action getConfigureAction(Frame owner) { + return new ParallelizeConfigureAction(owner, this, processor, editManager, selectionManager); + } + + +}
diff --git a/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualViewFactory.java b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualViewFactory.java new file mode 100644 index 0000000..f9ff7e7 --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/java/net/sf/taverna/t2/workbench/parallelize/ParallelizeContextualViewFactory.java
@@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (C) 2008 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package net.sf.taverna.t2.workbench.parallelize; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import net.sf.taverna.t2.workbench.edits.EditManager; +import net.sf.taverna.t2.workbench.selection.SelectionManager; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.ContextualView; +import net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory; +import uk.org.taverna.scufl2.api.core.Processor; + +public class ParallelizeContextualViewFactory implements ContextualViewFactory<Processor> { + + public static URI TYPE = URI.create("http://ns.taverna.org.uk/2010/scufl2/taverna/dispatchlayer/Parallelize"); + + private EditManager editManager; + private SelectionManager selectionManager; + + public boolean canHandle(Object selection) { + return selection instanceof Processor; + } + + public List<ContextualView> getViews(Processor selection) { + return Arrays.asList(new ContextualView[] {new ParallelizeContextualView(selection, editManager, selectionManager)}); + } + + public void setEditManager(EditManager editManager) { + this.editManager = editManager; + } + + public void setSelectionManager(SelectionManager selectionManager) { + this.selectionManager = selectionManager; + } + +}
diff --git a/taverna-workbench-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent new file mode 100644 index 0000000..edd7b2d --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.ui.menu.MenuComponent
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.parallelize.ParallelizeConfigureMenuAction
diff --git a/taverna-workbench-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory new file mode 100644 index 0000000..8b94f86 --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory
@@ -0,0 +1 @@ +net.sf.taverna.t2.workbench.parallelize.ParallelizeContextualViewFactory
diff --git a/taverna-workbench-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context-osgi.xml b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context-osgi.xml new file mode 100644 index 0000000..1859672 --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context-osgi.xml
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans:beans xmlns="http://www.springframework.org/schema/osgi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd"> + + <service ref="ParallelizeConfigureMenuAction" auto-export="interfaces" /> + + <service ref="ParallelizeContextualViewFactory" interface="net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory" /> + + <reference id="editManager" interface="net.sf.taverna.t2.workbench.edits.EditManager" /> + <reference id="selectionManager" interface="net.sf.taverna.t2.workbench.selection.SelectionManager" /> + +</beans:beans>
diff --git a/taverna-workbench-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context.xml b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context.xml new file mode 100644 index 0000000..1d1fdd4 --- /dev/null +++ b/taverna-workbench-parallelize-ui/src/main/resources/META-INF/spring/parallelize-ui-context.xml
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean id="ParallelizeConfigureMenuAction" class="net.sf.taverna.t2.workbench.parallelize.ParallelizeConfigureMenuAction"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + + <bean id="ParallelizeContextualViewFactory" class="net.sf.taverna.t2.workbench.parallelize.ParallelizeContextualViewFactory"> + <property name="editManager" ref="editManager" /> + <property name="selectionManager" ref="selectionManager" /> + </bean> + +</beans>
diff --git a/taverna-workbench-perspective-biocatalogue/LocalTestLauncher.bat b/taverna-workbench-perspective-biocatalogue/LocalTestLauncher.bat new file mode 100644 index 0000000..1c7f299 --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/LocalTestLauncher.bat
@@ -0,0 +1,14 @@ +@echo off +echo. +echo. +del biocatalogue-perspective-local-launch.jar +echo Deleted old JAR file if it was there. +cd target\classes +jar cfM ..\..\biocatalogue-perspective-local-launch.jar *.* +cd ..\.. +echo JAR assembly done, launching app... + +java -cp .;.\biocatalogue-perspective-local-launch.jar;c:/Users/Sergey/.m2/repository/net/sf/taverna/t2/workbench/ui-api/0.2/ui-api-0.2.jar;c:\Users\Sergey\.m2\repository\net\sf\taverna\t2\lang\ui\1.0\ui-1.0.jar;c:\Users\Sergey\.m2\repository\net\sf\taverna\t2\ui-components\workflow-view\1.0\workflow-view-1.0.jar;c:\Users\Sergey\.m2\repository\net\sf\taverna\t2\ui-activities\wsdl-activity-ui\0.7\wsdl-activity-ui-0.7.jar;C:\Users\Sergey\.m2\repository\log4j\log4j\1.2.13\log4j-1.2.13.jar;C:\Users\Sergey\.m2\repository\net\sf\taverna\t2\workbench\commons-icons\0.2\commons-icons-0.2.jar;c:\Users\Sergey\.m2\repository\BrowserLauncher2\BrowserLauncher2\1.3\BrowserLauncher2-1.3.jar;C:\Users\Sergey\.m2\repository\jdom\jdom\1.0\jdom-1.0.jar;"c:\Program Files\Java\xmlbeans-2.4.0\lib\xbean.jar";"c:\Program Files\Java\xmlbeans-2.4.0\lib\jsr173_1.0_api.jar";.\lib\lablib-checkboxtree-3.1.jar;.\lib\core-renderer.jar;.\lib\commons-lang-2.4.jar net.sf.taverna.t2.ui.perspectives.biocatalogue.TestJFrameForLocalLaunch + +del biocatalogue-perspective-local-launch.jar +echo Cleanup done - deleted old the JAR file that was used for the current launch. \ No newline at end of file
diff --git a/taverna-workbench-perspective-biocatalogue/lib/core-renderer.jar b/taverna-workbench-perspective-biocatalogue/lib/core-renderer.jar new file mode 100644 index 0000000..871fabf --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/lib/core-renderer.jar Binary files differ
diff --git a/taverna-workbench-perspective-biocatalogue/log4j.properties b/taverna-workbench-perspective-biocatalogue/log4j.properties new file mode 100644 index 0000000..fcd1d0c --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/log4j.properties
@@ -0,0 +1,4 @@ +log4j.rootLogger=DEBUG, A1 +log4j.appender.A1=org.apache.log4j.ConsoleAppender +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
diff --git a/taverna-workbench-perspective-biocatalogue/pom.xml b/taverna-workbench-perspective-biocatalogue/pom.xml new file mode 100644 index 0000000..3f5457a --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/pom.xml
@@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>ui-exts</artifactId> + <version>2.0-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.ui-exts</groupId> + <artifactId>perspective-biocatalogue</artifactId> + <name>BioCatalogue Perspective</name> + <repositories> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository</url> + </repository> + <repository> + <releases /> + <snapshots> + <enabled>false</enabled> + </snapshots> + <id>fuse</id> + <name>fuseRepository</name> + <url>http://repo.fusesource.com/maven2-all/</url> + </repository> + <repository> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + <id>mygrid-snapshot-repository</id> + <name>myGrid Snapshot Repository</name> + <url> + http://www.mygrid.org.uk/maven/snapshot-repository + </url> + </repository> + </repositories> + + <dependencies> + <!-- <dependency> <groupId>net.sf.taverna.t2.ui-api</groupId> <artifactId>perspective-core</artifactId> + <version>${t2.ui.api.version}</version> </dependency> --> + <dependency> + <groupId>net.sf.taverna.t2.core</groupId> + <artifactId>workflowmodel-impl</artifactId> + <version>${t2.core.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>menu-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>file-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <version>${t2.lang.version}</version> + </dependency> + <!-- required for providing contextual views in the bottom-left area of + Design perspective --> + <dependency> + <groupId>net.sf.taverna.t2.ui-api</groupId> + <artifactId>contextual-views-api</artifactId> + <version>${t2.ui.api.version}</version> + </dependency> + <!-- required for inserting a SOAP processor into the current workflow --> + <dependency> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>wsdl-activity-ui</artifactId> + <version>${t2.ui.activities.version}</version> + </dependency> + <!-- required for importing REST processors into the current workflow --> + <dependency> + <groupId>net.sf.taverna.t2.ui-activities</groupId> + <artifactId>rest-activity-ui</artifactId> + <version>${t2.ui.activities.version}</version> + </dependency> + <!-- required for inserting a processor into the current workflow --> + <dependency> + <groupId>net.sf.taverna.t2.ui-components</groupId> + <artifactId>workflow-view</artifactId> + <version>${t2.ui.components.version}</version> + </dependency> + <!-- required registering with and opening help window --> + <dependency> + <groupId>net.sf.taverna.t2.ui-impl</groupId> + <artifactId>helper</artifactId> + <version>${t2.ui.impl.version}</version> + </dependency> + <dependency> + <groupId>uk.org.taverna.configuration</groupId> + <artifactId>taverna-app-configuration-api</artifactId> + <version>${taverna.configuration.version}</version> + </dependency> + + <dependency> + <groupId>org.jdom</groupId> + <artifactId>com.springsource.org.jdom</artifactId> + <version>${jdom.version}</version> + </dependency> + <dependency> + <groupId>org.apache.log4j</groupId> + <artifactId>com.springsource.org.apache.log4j</artifactId> + </dependency> + <dependency> + <groupId>org.apache.xmlbeans</groupId> + <artifactId>xmlbeans</artifactId> + <version>2.5.0</version> + </dependency> + + <!-- FlyingSaucer XHTML Renderer --> + <!-- (it is critical to use version R8, not any earlier ones - e.g. R8pre2, + etc.) --> + <dependency> + <groupId>org.xhtmlrenderer</groupId> + <artifactId>core-renderer</artifactId> + <version>${org.xhtmlrenderer.core-renderer.version}</version> + <exclusions> + <exclusion> + <groupId>bouncycastle</groupId> + <artifactId>bcprov-jdk14</artifactId> + </exclusion> + <exclusion> + <groupId>bouncycastle</groupId> + <artifactId>bcmail-jdk14</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- At least StringEscapeUtils class is used from this library --> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>com.springsource.org.apache.commons.lang</artifactId> + <version>${commons.lang.version}</version> + </dependency> + + <!-- Gson: Java to Json conversion --> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>${gson.version}</version> + </dependency> + </dependencies> + + <build> + <!-- Adds "xmlbeans:xmlbeans" Maven2 goal to compile the API binding classes + from XSD schema. --> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>xmlbeans-maven-plugin</artifactId> + <version>2.3.3</version> + <executions> + <execution> + <goals> + <goal>xmlbeans</goal> + </goals> + </execution> + </executions> + <inherited>true</inherited> + <configuration> + <!-- "javaSource=1.5" is required to make use of generics and have getXXXList() + methods available, not just getXXXArrray() --> + <javaSource>1.5</javaSource> + <download>true</download> + <schemaDirectory>src/main/xsd</schemaDirectory> + <!-- Default is target/generated-sources/xmlbeans - which the Maven + plugin should be able to add to the Project classpath --> + <!-- <sourceGenerationDirectory>src/main/java</sourceGenerationDirectory> --> + </configuration> + </plugin> + </plugins> + </build> +</project>
diff --git a/taverna-workbench-perspective-biocatalogue/schema_compilation/move_scomp_results_into_project.bat b/taverna-workbench-perspective-biocatalogue/schema_compilation/move_scomp_results_into_project.bat new file mode 100644 index 0000000..3786d7c --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/schema_compilation/move_scomp_results_into_project.bat
@@ -0,0 +1,29 @@ +@echo off + +echo This will replace the JAR file with compiled API classes +pause +del ..\lib\biocatalogue_api_classes.jar +move biocatalogue_api_classes.jar ..\lib\ +echo JAR file replaced +echo. + +REM replace the sources of API classes +echo This will delete *ALL* files in \src\main\java\org\biocatalogue +echo \src\main\java\org\purl +echo \src\main\java\org\w3 +pause + +rd /S /Q ..\src\main\java\org\biocatalogue +rd /S /Q ..\src\main\java\org\purl +rd /S /Q ..\src\main\java\org\w3 + +move /Y org\biocatalogue ..\src\main\java\org +move /Y org\purl ..\src\main\java\org +move /Y org\w3 ..\src\main\java\org +rd org + +echo Sources of API classes replaced +echo. + +echo Done! +pause \ No newline at end of file
diff --git a/taverna-workbench-perspective-biocatalogue/schema_compilation/scomp_compile_from_web.bat b/taverna-workbench-perspective-biocatalogue/schema_compilation/scomp_compile_from_web.bat new file mode 100644 index 0000000..ee10a50 --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/schema_compilation/scomp_compile_from_web.bat
@@ -0,0 +1,9 @@ +@echo off + +REM -src . -- put source files here +REM -srconly -- only sources, no compiling of java classes, no jar bundling +REM -compiler -- where to find javac +REM -javasource -- which JAVA version to aim for (1.5 uses generics) +REM -dl -- allows download of referenced schemas + +scomp -src . -srconly -compiler "%JAVA_HOME%\bin\javac.exe" -javasource 1.5 -dl http://www.biocatalogue.org/2009/xml/rest/schema-v1.xsd \ No newline at end of file
diff --git a/taverna-workbench-perspective-biocatalogue/schema_compilation/scomp_compile_from_web_to_jar.bat b/taverna-workbench-perspective-biocatalogue/schema_compilation/scomp_compile_from_web_to_jar.bat new file mode 100644 index 0000000..dd66206 --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/schema_compilation/scomp_compile_from_web_to_jar.bat
@@ -0,0 +1,9 @@ +@echo off + +REM -src . -- put source files here +REM -compiler -- where to find javac +REM -javasource -- which JAVA version to aim for (1.5 uses generics) +REM -out -- specifies the name of the target JAR file +REM -dl -- allows download of referenced schemas + +scomp -src . -compiler "%JAVA_HOME%\bin\javac.exe" -javasource 1.5 -out biocatalogue_api_classes.jar -dl http://www.biocatalogue.org/2009/xml/rest/schema-v1.xsd \ No newline at end of file
diff --git a/taverna-workbench-perspective-biocatalogue/src/main/doc/BioCatalogue Plugin Documentation.odt b/taverna-workbench-perspective-biocatalogue/src/main/doc/BioCatalogue Plugin Documentation.odt new file mode 100644 index 0000000..85056d7 --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/src/main/doc/BioCatalogue Plugin Documentation.odt Binary files differ
diff --git a/taverna-workbench-perspective-biocatalogue/src/main/help/Index-TOC-Map-Additions.txt b/taverna-workbench-perspective-biocatalogue/src/main/help/Index-TOC-Map-Additions.txt new file mode 100644 index 0000000..8d6c953 --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/src/main/help/Index-TOC-Map-Additions.txt
@@ -0,0 +1,20 @@ +/* http://www.mygrid.org.uk/taverna2_1/helpset/toc.xml */ + +<tocitem text="BioCatalogue Plugin" target="biocatalogue-plugin" expand="false"> + <tocitem text="Feature Overview" target="biocatalogue-plugin-features"/> + <tocitem text="Feedback" target="biocatalogue-plugin-feedback"/> +</tocitem> + + + +/* http://www.mygrid.org.uk/taverna2_1/helpset/index.xml */ + +<indexitem text="BioCatalogue Plugin" target="biocatalogue-plugin"/> + + + +/* http://www.mygrid.org.uk/taverna2_1/helpset/map.xml */ + +<mapID target="biocatalogue-plugin" url="html/biocatalogue-plugin.html"/> +<mapID target="biocatalogue-plugin-features" url="html/biocatalogue-plugin-features.html"/> +<mapID target="biocatalogue-plugin-feedback" url="html/biocatalogue-plugin-feedback.html"/> \ No newline at end of file
diff --git a/taverna-workbench-perspective-biocatalogue/src/main/help/biocatalogue-plugin-features.html b/taverna-workbench-perspective-biocatalogue/src/main/help/biocatalogue-plugin-features.html new file mode 100644 index 0000000..ece2949 --- /dev/null +++ b/taverna-workbench-perspective-biocatalogue/src/main/help/biocatalogue-plugin-features.html
@@ -0,0 +1,113 @@ +<html> + <head> + <meta content="text/html; charset=MacRoman" http-equiv="Content-Type"> + <link rel="stylesheet" href="./basic.css" type="text/css"> + <meta name="generator" content="Helen"> + <title>BioCatalogue Plugin - Features</title> + </head> + <body> + <h2> + BioCatalogue Plugin - Features + </h2> + + <h3> + BioCatalogue Perspective + </h3> + <p> + This perspective is designed for searching and browsing the BioCatalogue + data. It aids with discovery of Processors that may be used in a workflow. + </p> + + <p> + In the BioCatalogue perspective you may: + <ul> + <li>Search for Web Services by free text queries of by tags;</li> + <li>Filter search results by the same set of criteria as available on the BioCatalogue website;</li> + <li>Save favourite filters and search queries for re-use at a later point;</li> + <li>View search history;</li> + <li>Immediately see the types and monitoring statuses of all found Web + Services directly in the results listings;</li> + <li>Preview Web Services.</li> + </ul> + </p> + + <p> + Web Service previews display a similar set of information to + that shown on the BioCatalogue website: service description, + type, location, provider and other details are shown. Listing + of all operations and their descriptions within that Web Service + is shown. Any operation may be added directly into the current + workflow or into the Service Panel in the Design Perspective + for later use. + </p> + + <h3> + Integration into Design Perspective + </h3> + + <p> + <ul> + <li>Any Web Service operations added to the Service Panel from the BioCatalogue + perspective can be dragged into the Workflow Diagram like any other Processors. + They are saved by the plugin, so that when Taverna is restarted, those services + can still be found in the Service Panel. + </li> + <li>Right mouse click on a Processor in the Workflow Explorer or Workflow Diagram + will display options provided by the plugin - for all WSDL Processors it is + possible to check their monitoring status or launch the Processor Preview. + </li> + <li>Right mouse click on an empty space in the Workflow Diagram will let to launch + the workflow "health check" - currently this feature will identify a list of all + WSDL activities in a workflow and will fetch the latest monitoring data about + each of the from BioCatalogue. + </li> + <li>"Details" tab in the contextual view area (bottom-left corner of the Design + Perspective) will display information about the WSDL Processors and their + input or output ports (if they are registered in BioCatalogue). These contextual + views are only shown if BioCatalogue knows how to handle the selected type of