/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.gui.workflow.editor.connections;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDescription;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDescriptionsManager;
import de.rcenvironment.core.component.workflow.model.api.Connection;
import de.rcenvironment.core.component.workflow.model.api.Location;
import de.rcenvironment.core.component.workflow.model.api.WorkflowDescription;
import de.rcenvironment.core.component.workflow.model.api.WorkflowNode;
import de.rcenvironment.core.datamodel.api.DataType;
import de.rcenvironment.core.datamodel.api.EndpointType;
import de.rcenvironment.core.datamodel.api.TypedDatumConverter;
import de.rcenvironment.core.datamodel.api.TypedDatumService;
import de.rcenvironment.core.gui.workflow.EndpointContentProvider;
import de.rcenvironment.core.gui.workflow.EndpointContentProvider.Endpoint;
import de.rcenvironment.core.gui.workflow.EndpointHandlingHelper;
import de.rcenvironment.core.gui.workflow.EndpointLabelProvider;
import de.rcenvironment.core.gui.workflow.editor.commands.ConnectionAddCommand;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.incubator.ServiceRegistry;
import de.rcenvironment.core.utils.incubator.ServiceRegistryAccess;
/**
* Common composite for ConnectionDialog and ConnectionView.
*
* @author Oliver Seebach
*
*/
public class ConnectionDialogComposite extends Composite {
private static final Log LOGGER = LogFactory.getLog(ConnectionDialogComposite.class);
private static final String SLASH = "/"; //$NON-NLS-1$
private static final String SEMICOLON = ";"; //$NON-NLS-1$
private static final String ENDPOINT_SEPARATOR = "###";
private String selectionData;
private EndpointTreeViewer sourceTreeViewer;
private EndpointTreeViewer targetTreeViewer;
private Tree sourceTree;
private Tree targetTree;
private ConnectionCanvas canvas;
private ComponentViewerFilter sourceFilter;
private ComponentViewerFilter targetFilter;
private Text sourceFilterText;
private Text targetFilterText;
private WorkflowDescription workflowDescription;
private Group targetGroup;
private Group sourceGroup;
private String sourceFilterString = "";
private String targetFilterString = "";
private FilterMode defaultFilterMode = FilterMode.ISEXACTLY;
private FilterMode sourceFilterMode = defaultFilterMode;
private FilterMode targetFilterMode = defaultFilterMode;
private boolean wasDoubleClicked;
private Cursor targetTreeDefaultCursor;
private Cursor crossCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_CROSS);
private TypedDatumConverter datumConverter;
private CommandStack editorsCommandStack;
private boolean initializedSection;
/**
* Filter modes.
*
* @author Oliver Seebach
*/
public enum FilterMode {
/** Node name contains filter string. */
CONTAINS,
/** Node name starts with filter string. */
STARTSWITH, /**
* /** Node name is exactly filter string.
*/
ISEXACTLY,
/** Node name is exactly either source or target filter string. */
DOUBLECLICK
}
public ConnectionDialogComposite(Composite parent, int style) {
super(parent, style);
// Source Group
sourceGroup = new Group(this, SWT.NONE);
sourceGroup.setText(Messages.source);
GridData gridDataSourceGroup = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
gridDataSourceGroup.widthHint = 1;
sourceGroup.setLayoutData(gridDataSourceGroup);
GridLayout gridLayoutSourceGroup = new GridLayout(1, false);
gridLayoutSourceGroup.marginTop = 5;
gridLayoutSourceGroup.marginWidth = 0;
gridLayoutSourceGroup.verticalSpacing = 0;
gridLayoutSourceGroup.marginHeight = 0;
gridLayoutSourceGroup.horizontalSpacing = 0;
sourceGroup.setLayout(gridLayoutSourceGroup);
sourceTreeViewer = new EndpointTreeViewer(sourceGroup, SWT.NONE);
sourceTree = sourceTreeViewer.getTree();
sourceTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
sourceTree.setLinesVisible(true);
sourceFilter = new ComponentViewerFilter();
sourceTreeViewer.addFilter(sourceFilter);
// Connection Group
Group connectionGroup = new Group(this, SWT.NONE);
connectionGroup.setText(Messages.connections);
GridData gridDataConnectionGroup = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
gridDataConnectionGroup.widthHint = 1;
connectionGroup.setLayoutData(gridDataConnectionGroup);
GridLayout gridLayoutConnectionGroup = new GridLayout(1, false);
gridLayoutConnectionGroup.marginTop = 5;
gridLayoutConnectionGroup.verticalSpacing = 0;
gridLayoutConnectionGroup.marginWidth = 0;
gridLayoutConnectionGroup.marginHeight = 0;
gridLayoutConnectionGroup.horizontalSpacing = 0;
connectionGroup.setLayout(gridLayoutConnectionGroup);
canvas = new ConnectionCanvas(connectionGroup, SWT.NONE);
canvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
canvas.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
// Target Group
targetGroup = new Group(this, SWT.NONE);
targetGroup.setText(Messages.target);
GridData gridDataTargetGroup = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
gridDataTargetGroup.widthHint = 1;
targetGroup.setLayoutData(gridDataTargetGroup);
GridLayout gridLayoutTargetGroup = new GridLayout(1, false);
gridLayoutTargetGroup.marginTop = 5;
gridLayoutTargetGroup.verticalSpacing = 0;
gridLayoutTargetGroup.marginWidth = 0;
gridLayoutTargetGroup.marginHeight = 0;
gridLayoutTargetGroup.horizontalSpacing = 0;
targetGroup.setLayout(gridLayoutTargetGroup);
targetTreeViewer = new EndpointTreeViewer(targetGroup, SWT.NONE);
targetTree = targetTreeViewer.getTree();
targetTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
targetTree.setLinesVisible(true);
targetFilter = new ComponentViewerFilter();
targetTreeViewer.addFilter(targetFilter);
}
public void setWasDoubleClicked(boolean wasDoubleClicked) {
this.wasDoubleClicked = wasDoubleClicked;
}
public void setSourceFilterMode(FilterMode sourceFilterMode) {
this.sourceFilterMode = sourceFilterMode;
}
public void setTargetFilterMode(FilterMode targetFilterMode) {
this.targetFilterMode = targetFilterMode;
}
public Group getTargetGroup() {
return targetGroup;
}
public Group getSourceGroup() {
return sourceGroup;
}
/**
* Applies the target filter and refreshes both trees.
*/
public void applyTargetFilter() {
targetFilter.setExact(false);
targetFilter.setFilterMode(targetFilterMode);
targetFilter.setFilterString(targetFilterString);
if (wasDoubleClicked) {
targetFilter.setFilterMode(FilterMode.DOUBLECLICK);
targetFilter.setFilterString(sourceFilterString + ENDPOINT_SEPARATOR + targetFilterString);
}
sourceTreeViewer.refresh();
canvas.redraw();
targetTreeViewer.refresh();
targetTreeViewer.expandAll();
sourceTreeViewer.expandAll();
}
/**
* Applies the source filter and refreshes both trees.
*/
public void applySourceFilter() {
sourceFilter.setExact(false);
sourceFilter.setFilterMode(sourceFilterMode);
sourceFilter.setFilterString(sourceFilterString);
if (wasDoubleClicked) {
sourceFilter.setFilterMode(FilterMode.DOUBLECLICK);
sourceFilter.setFilterString(sourceFilterString + ENDPOINT_SEPARATOR + targetFilterString);
}
sourceTreeViewer.refresh();
canvas.redraw();
targetTreeViewer.refresh();
targetTreeViewer.expandAll();
sourceTreeViewer.expandAll();
}
public void setSourceFilterString(String sourceFilterString) {
this.sourceFilterString = sourceFilterString;
}
public void setTargetFilterString(String targetFilterString) {
this.targetFilterString = targetFilterString;
}
public Text getSourceFilterText() {
return sourceFilterText;
}
public Text getTargetFilterText() {
return targetFilterText;
}
public void setWorkflowDescription(WorkflowDescription workflowDescription) {
this.workflowDescription = workflowDescription;
}
/**
* Marks whether the section has just been initialized.
*/
public void markSectionAsInitialized() {
initializedSection = true;
}
private void selectOutputSource() {
Object selectedElement = ((ITreeSelection) sourceTreeViewer.getSelection()).getFirstElement();
selectionData = null;
if (selectedElement instanceof Endpoint) {
selectionData = getDataString((Endpoint) selectedElement);
} else if (selectedElement instanceof WorkflowNode) {
selectionData = getDataString((WorkflowNode) selectedElement);
}
}
private static String getDataString(String nodeId, String endpointName) {
return nodeId + SLASH + endpointName;
}
private static String getDataString(Endpoint endpoint) {
return getDataString(endpoint.getWorkflowNode().getIdentifier(), endpoint.getEndpointDescription().getName());
}
private static String getDataString(WorkflowNode workflowNode) {
StringBuffer data = new StringBuffer();
for (EndpointDescription endpointDesc : workflowNode.getComponentDescription().getOutputDescriptionsManager()
.getEndpointDescriptions()) {
data.append(getDataString(workflowNode.getIdentifier(), endpointDesc.getName()) + SEMICOLON);
}
return new String(data);
}
private boolean performEndpointDrop(String sourceNodeId, String sourceEndpointName, Object targetObject) {
WorkflowNode sourceNode = null;
for (final WorkflowNode node : workflowDescription.getWorkflowNodes()) {
if (node.getIdentifier().equals(sourceNodeId)) {
sourceNode = node;
break;
}
}
// if no node with the respective id was found, perform no drop and log warning
if (sourceNode == null) {
LOGGER.warn("Connection could not be created because workflow node with id " + sourceNodeId + " was not found.");
return false;
}
// if the section has been initialized and no source node is selected, perform no drop
if (initializedSection) {
return false;
}
Endpoint targetEndpoint = null;
Object currentTarget = targetObject;
if (currentTarget instanceof Endpoint) {
targetEndpoint = (Endpoint) currentTarget;
} else if (currentTarget instanceof WorkflowNode) {
targetEndpoint = findEndpoint((WorkflowNode) currentTarget, sourceEndpointName);
}
// if no matching target endpoint was found
if (targetEndpoint == null) {
selectionData = "";
return false;
}
EndpointDescriptionsManager endpointManager = sourceNode.getComponentDescription().getOutputDescriptionsManager();
DataType sourceDataType = endpointManager.getEndpointDescription(sourceEndpointName).getDataType();
if (sourceDataType != targetEndpoint.getEndpointDescription().getDataType()
&& !datumConverter.isConvertibleTo(sourceDataType, targetEndpoint.getEndpointDescription().getDataType())) {
MessageDialog.openError(Display.getCurrent().getActiveShell(), Messages.error, StringUtils.format(Messages.incompatibleTypes,
sourceDataType.getDisplayName(), targetEndpoint.getEndpointDescription().getDataType().getDisplayName()));
return false;
}
// if connection is already connected, show warning and don't perform drop
Connection connectedTo = null;
for (Connection c : workflowDescription.getConnections()) {
if (c.getTargetNode().equals(targetEndpoint.getWorkflowNode())
&& targetEndpoint.getEndpointDescription().getIdentifier().equals(c.getInput().getIdentifier())) {
connectedTo = c;
MessageDialog.openError(
Display.getCurrent().getActiveShell(),
Messages.error,
StringUtils.format(Messages.alreadyConnected, targetEndpoint.getName(),
connectedTo.getOutput().getName(), connectedTo.getSourceNode().getName()));
return false;
}
}
// check connections for already existent bendpoints between the two nodes
List<Location> alreadyExistentBendpoints = new ArrayList<>();
for (Connection connection : workflowDescription.getConnections()) {
if ((connection.getSourceNode().getIdentifier().equals(sourceNode.getIdentifier())
&& connection.getTargetNode().getIdentifier().equals(targetEndpoint.getWorkflowNode().getIdentifier()))) {
alreadyExistentBendpoints = connection.getBendpoints();
break;
} else if (connection.getSourceNode().getIdentifier().equals(targetEndpoint.getWorkflowNode().getIdentifier())
&& connection.getTargetNode().getIdentifier().equals(sourceNode.getIdentifier())) {
// invert order
for (Location l : connection.getBendpoints()) {
alreadyExistentBendpoints.add(0, l);
}
break;
}
}
Connection connection = new Connection(sourceNode, endpointManager.getEndpointDescription(sourceEndpointName),
targetEndpoint.getWorkflowNode(), targetEndpoint.getEndpointDescription(), alreadyExistentBendpoints);
ConnectionAddCommand command = new ConnectionAddCommand(workflowDescription, connection);
// in section, execute on editors command stack, otherwise without command stack.
// TODO add command stack for connection dialog.
if (editorsCommandStack != null) {
editorsCommandStack.execute(command);
} else {
command.execute();
}
return true;
}
private Endpoint findEndpoint(WorkflowNode workflowNode, String name) {
// if two endpoints have the same name, connect them
for (Endpoint e : EndpointHandlingHelper.getEndpoints(workflowNode, EndpointType.INPUT)) {
if (e.getEndpointDescription().getName().equals(name)) {
return e;
}
}
// if two component just have 1 endpoint each and the types match, connect them
for (Endpoint e : EndpointHandlingHelper.getEndpoints(workflowNode, EndpointType.INPUT)) {
int targetInputs = workflowNode.getInputDescriptionsManager().getEndpointDescriptions().size();
WorkflowNode sourceNode = null;
if (sourceTreeViewer.getSelection() instanceof TreeSelection) {
TreeSelection selection = (TreeSelection) sourceTreeViewer.getSelection();
if (selection.getFirstElement() instanceof WorkflowNode) {
sourceNode = ((WorkflowNode) selection.getFirstElement());
}
}
if (sourceNode != null) {
int sourceOutputs = sourceNode.getOutputDescriptionsManager().getEndpointDescriptions().size();
if (targetInputs == 1 && sourceOutputs == 1) {
DataType targetInputDataType =
((EndpointDescription) workflowNode.getInputDescriptionsManager().getEndpointDescriptions().toArray()[0])
.getDataType();
DataType sourceOutputDataType =
((EndpointDescription) sourceNode.getOutputDescriptionsManager().getEndpointDescriptions().toArray()[0])
.getDataType();
if (targetInputDataType.equals(sourceOutputDataType)) {
return e;
}
}
}
}
return null;
}
private void expandSelectedSourceNode() {
if (sourceTreeViewer.getSelection() instanceof TreeSelection) {
TreeSelection selection = (TreeSelection) sourceTreeViewer.getSelection();
if (selection.getFirstElement() instanceof WorkflowNode) {
WorkflowNode node = (WorkflowNode) selection.getFirstElement();
for (TreeItem item : sourceTree.getItems()) {
if (item.getText().equals(node.getName())) {
item.setExpanded(true);
sourceTreeViewer.refresh();
}
}
}
}
}
private void expandTargetNode(Object target) {
// Object target = getCurrentTarget();
if (target instanceof WorkflowNode) {
WorkflowNode node = (WorkflowNode) target;
for (TreeItem item : targetTree.getItems()) {
if (item.getText().equals(node.getName())) {
item.setExpanded(true);
targetTreeViewer.refresh();
}
}
}
}
/**
* Updates the connection trees and the canvas according to the new model.
*
* @param newModel The new model for the connection trees and canvas.
*/
public void updateConnectionViewer(WorkflowDescription newModel) {
if (!sourceTree.isDisposed() && !targetTree.isDisposed() && !canvas.isDisposed()) {
targetTreeViewer.refresh(true);
sourceTreeViewer.refresh(true);
canvas.repaint();
}
}
/**
* Initializes the common connection composite.
*
* @param model The model to be represented in the composite.
* @param sourceWorkflowNode The preselected source node.
* @param targetWorkflowNode The preselected target node
*/
public void initialize(WorkflowDescription model, WorkflowNode sourceWorkflowNode, WorkflowNode targetWorkflowNode) {
workflowDescription = model;
sourceTreeViewer.setLabelProvider(new EndpointLabelProvider(EndpointType.OUTPUT));
targetTreeViewer.setLabelProvider(new EndpointLabelProvider(EndpointType.INPUT));
sourceTreeViewer.setContentProvider(new EndpointContentProvider(EndpointType.OUTPUT));
targetTreeViewer.setContentProvider(new EndpointContentProvider(EndpointType.INPUT));
sourceTreeViewer.setInput(workflowDescription);
targetTreeViewer.setInput(workflowDescription);
if (sourceWorkflowNode != null) {
sourceFilterString = sourceWorkflowNode.getName();
sourceTreeViewer.expandToLevel(sourceWorkflowNode, 2);
sourceTreeViewer.setSelection(new StructuredSelection(sourceWorkflowNode));
}
if (targetWorkflowNode != null) {
targetFilterString = targetWorkflowNode.getName();
targetTreeViewer.expandToLevel(targetWorkflowNode, 2);
targetTreeViewer.setSelection(new StructuredSelection(targetWorkflowNode));
}
canvas.initialize(workflowDescription, sourceTreeViewer, targetTreeViewer);
// Repaint
sourceTree.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent event) {
canvas.repaint();
}
});
targetTree.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent event) {
canvas.repaint();
}
});
canvas.repaint();
ServiceRegistryAccess serviceRegistryAccess = ServiceRegistry.createAccessFor(this);
TypedDatumService typedDatumService = serviceRegistryAccess.getService(TypedDatumService.class);
datumConverter = typedDatumService.getConverter();
// DND
int operations = DND.DROP_COPY | DND.DROP_MOVE;
Transfer[] transfer = new Transfer[] { TextTransfer.getInstance() };
sourceTreeViewer.addDragSupport(operations, transfer, new OutputDragSourceListener());
ViewerDropAdapter dropAdapter = new InputViewerDropAdapter();
dropAdapter.setFeedbackEnabled(false);
Transfer[] dropTransfer = new Transfer[] { TextTransfer.getInstance() };
targetTreeViewer.addDropSupport(operations, dropTransfer, dropAdapter);
targetTreeViewer.addSelectionChangedListener(new InputTargetSelectionListener());
sourceTreeViewer.addSelectionChangedListener(new OutputSourceSelectionListener());
// initialize old cursor
targetTreeDefaultCursor = targetTree.getCursor();
targetTree.addMouseMoveListener(new TargetTreeMouseMoveListener());
// Per default, expand all
sourceTreeViewer.expandAll();
targetTreeViewer.expandAll();
}
public WorkflowDescription getWorkflowDescription() {
return workflowDescription;
}
public ConnectionCanvas getCanvas() {
return canvas;
}
public EndpointTreeViewer getSourceTreeViewer() {
return sourceTreeViewer;
}
public EndpointTreeViewer getTargetTreeViewer() {
return targetTreeViewer;
}
private void setTargetTreeCursorToDefault() {
targetTree.setCursor(targetTreeDefaultCursor);
}
/**
* Filter to remove unwanted components in connection dialog.
*
* @author Sascha Zur
* @author Oliver Seebach
*/
public class ComponentViewerFilter extends ViewerFilter {
private String filterString = "";
private boolean exact = false;
private FilterMode filterMode = defaultFilterMode;
public ComponentViewerFilter() {}
public void setFilterMode(FilterMode filterMode) {
this.filterMode = filterMode;
}
@Override
public boolean select(Viewer arg0, Object arg1, Object arg2) {
if (arg2 instanceof WorkflowNode) {
WorkflowNode item = ((WorkflowNode) arg2);
// empty filter show all
if (filterString.isEmpty()) {
return true;
}
if (filterMode.equals(FilterMode.CONTAINS)) {
if (!(item.getName().toLowerCase().contains(filterString.toLowerCase()))) {
return false;
}
} else if (filterMode.equals(FilterMode.STARTSWITH)) {
if (!(item.getName().toLowerCase().startsWith(filterString.toLowerCase()))) {
return false;
}
} else if (filterMode.equals(FilterMode.ISEXACTLY)) {
if (!(item.getName().toLowerCase().equals(filterString.toLowerCase()))) {
return false;
}
} else if (filterMode.equals(FilterMode.DOUBLECLICK)) {
// by convention, the source and target connection names are handed over separated by "###"
if (filterString.split(ENDPOINT_SEPARATOR).length == 2) {
String endpoint1 = filterString.split(ENDPOINT_SEPARATOR)[0];
String endpoint2 = filterString.split(ENDPOINT_SEPARATOR)[1];
if (!(item.getName().toLowerCase().equals(endpoint1.toLowerCase())
|| item.getName().toLowerCase().equals(endpoint2.toLowerCase()))) {
return false;
}
}
}
}
return true;
}
public String getFilterString() {
return filterString;
}
public void setFilterString(String filterString) {
this.filterString = filterString;
}
public boolean isExact() {
return exact;
}
public void setExact(boolean exact) {
this.exact = exact;
}
/**
* Configures the component filter.
*
* @param filter the text to be used as filter.
* @param filterModus the filter mode to be applied.
*/
public void configureFilter(String filter, FilterMode filterModus) {
this.filterString = filter;
this.filterMode = filterModus;
}
}
/**
* Reacts on changes in source tree.
*
* @author Oliver Seebach
*/
public class OutputSourceSelectionListener implements ISelectionChangedListener {
@Override
public void selectionChanged(SelectionChangedEvent e) {
if (initializedSection) {
initializedSection = false;
} else {
selectOutputSource();
}
}
}
/**
* Reacts on changes in target tree and initiates connection pulling.
*
* @author Oliver Seebach
*/
public class InputTargetSelectionListener implements ISelectionChangedListener {
@Override
public void selectionChanged(SelectionChangedEvent event) {
if (!event.getSelection().isEmpty() && selectionData != null) {
Object currentTarget = ((ITreeSelection) event.getSelection()).getFirstElement();
Object currentSource = ((ITreeSelection) sourceTreeViewer.getSelection()).getFirstElement();
boolean selectionTypesEqual = false;
if (currentSource != null) {
if (currentSource.getClass() == currentTarget.getClass()) {
selectionTypesEqual = true;
}
if (selectionTypesEqual && (currentTarget instanceof Endpoint || currentTarget instanceof WorkflowNode)) {
connectEqualTypes(currentTarget);
}
}
canvas.repaint();
targetTreeViewer.refresh();
sourceTreeViewer.refresh();
}
}
// Tries to connect two endpoints with identical types
private void connectEqualTypes(Object currentTarget) {
for (String sourceString : selectionData.split(Pattern.quote(SEMICOLON))) {
String[] splittedSourceString = sourceString.split(Pattern.quote(SLASH), 2);
if (splittedSourceString.length > 1) {
if (performEndpointDrop(splittedSourceString[0], splittedSourceString[1], currentTarget)) {
setTargetTreeCursorToDefault();
}
}
}
}
}
/**
* Listener for handling dragging outputs.
*
* @author Doreen Seider
*/
public class OutputDragSourceListener implements DragSourceListener {
@Override
public void dragFinished(DragSourceEvent event) {}
@Override
public void dragSetData(DragSourceEvent event) {
Object selectedElement = ((ITreeSelection) sourceTreeViewer.getSelection()).getFirstElement();
if (selectedElement instanceof Endpoint) {
selectionData = getDataString((Endpoint) selectedElement);
} else if (selectedElement instanceof WorkflowNode) {
selectionData = getDataString((WorkflowNode) selectedElement);
}
event.data = selectionData;
}
@Override
public void dragStart(DragSourceEvent event) {
Object item = ((ITreeSelection) sourceTreeViewer.getSelection()).getFirstElement();
event.doit = item instanceof Endpoint || item instanceof WorkflowNode;
}
}
/**
* Handling dropping to inputs.
*
* @author Doreen Seider
*/
public class InputViewerDropAdapter extends ViewerDropAdapter {
public InputViewerDropAdapter() {
super(targetTreeViewer);
}
@Override
public boolean performDrop(Object element) {
boolean performed = false;
boolean successful = false;
Object currentTarget = getCurrentTarget();
if (currentTarget instanceof Endpoint || currentTarget instanceof WorkflowNode) {
for (String sourceString : ((String) element).split(Pattern.quote(SEMICOLON))) {
String[] splittedSourceString = sourceString.split(Pattern.quote(SLASH), 2);
successful = performEndpointDrop(splittedSourceString[0], splittedSourceString[1], currentTarget);
}
performed = true;
}
if (!successful) {
expandTargetNode(getCurrentTarget());
expandSelectedSourceNode();
}
canvas.repaint();
targetTreeViewer.refresh();
sourceTreeViewer.refresh();
if (selectionData != null) {
if (selectionData.isEmpty()) {
setTargetTreeCursorToDefault();
}
} else if (selectionData == null) {
setTargetTreeCursorToDefault();
}
return performed;
}
@Override
public boolean validateDrop(Object dest, int operation, TransferData transferType) {
int endpoints = 0;
if (dest instanceof WorkflowNode) {
endpoints = ((WorkflowNode) dest).getComponentDescription().getInputDescriptionsManager().getEndpointDescriptions().size();
}
boolean selectionValid = false;
if (selectionData != null) {
if (!selectionData.isEmpty()) {
selectionValid = true;
}
}
Object currentSource = ((ITreeSelection) sourceTreeViewer.getSelection()).getFirstElement();
boolean selectionTypesEqual = false;
if (currentSource != null && dest != null) {
if (currentSource.getClass() == dest.getClass()) {
selectionTypesEqual = true;
}
}
boolean valid = TextTransfer.getInstance().isSupportedType(transferType)
&& ((dest instanceof Endpoint || (dest instanceof WorkflowNode && endpoints > 0)))
&& selectionValid && selectionTypesEqual;
return valid;
}
}
public void setCommandStack(CommandStack editorsCommandStack2) {
this.editorsCommandStack = editorsCommandStack2;
}
/**
* Mouse movement listener that handles cursor.
*
* @author Oliver Seebach
*
*/
private final class TargetTreeMouseMoveListener implements MouseMoveListener {
@Override
public void mouseMove(MouseEvent event) {
// if nothing is selected as source, no action required
if (selectionData != null) {
Object endpointClass = Endpoint.class;
Object workflowNodeClass = WorkflowNode.class;
Object currentSource = ((ITreeSelection) sourceTreeViewer.getSelection()).getFirstElement();
Object targetType = null;
boolean selectionTypesEqual = false;
// find tree item the mouse is moving over
String hoveredComponentName = "";
String hoveredEndpointName = "";
Point pt = new Point(event.x, event.y);
// First level item = node; Second level item = endpoint
for (TreeItem firstLevelItem : targetTree.getItems()) {
if (firstLevelItem.getBounds().contains(pt)) {
targetType = workflowNodeClass;
hoveredComponentName = firstLevelItem.getText();
break;
}
for (TreeItem secondLevelItem : firstLevelItem.getItems()) {
if (secondLevelItem.getBounds().contains(pt)) {
targetType = endpointClass;
hoveredEndpointName = secondLevelItem.getText();
hoveredComponentName = secondLevelItem.getParentItem().getText();
break;
}
}
}
if (currentSource != null && targetType != null) {
if (currentSource.getClass() == targetType) {
selectionTypesEqual = true;
}
}
// when mouse is not over endpoint or component item.
if (hoveredComponentName.isEmpty() || hoveredEndpointName.isEmpty()) {
setTargetTreeCursorToDefault();
}
if (selectionTypesEqual) {
for (WorkflowNode node : workflowDescription.getWorkflowNodes()) {
if (node.getName().equals(hoveredComponentName) && (targetType == endpointClass)) {
EndpointDescription targetEndpoint =
node.getInputDescriptionsManager().getEndpointDescription(hoveredEndpointName);
String[] sourceCandidates = selectionData.split(SEMICOLON);
for (String candidate : sourceCandidates) {
checkCandidateForCursorSetting(targetEndpoint, candidate);
}
} else if (node.getName().equals(hoveredComponentName) && (targetType == workflowNodeClass)
&& !initializedSection) {
targetTreeDefaultCursor = targetTree.getParent().getCursor();
targetTree.setCursor(crossCursor);
break;
}
}
} else {
setTargetTreeCursorToDefault();
}
}
}
private void checkCandidateForCursorSetting(EndpointDescription targetEndpoint, String candidate) {
boolean isConnected = targetEndpoint.isConnected();
if (candidate.split(SLASH).length > 1) {
String sourceComponent = candidate.split(SLASH)[0];
String sourceEndpoint = candidate.split(SLASH)[1];
DataType sourceDataType =
workflowDescription.getWorkflowNode(sourceComponent).getOutputDescriptionsManager()
.getEndpointDescription(sourceEndpoint).getDataType();
DataType targetDataType = targetEndpoint.getDataType();
boolean typeCompatible = (datumConverter.isConvertibleTo(sourceDataType, targetDataType))
|| (sourceDataType.equals(targetDataType));
if (!isConnected && typeCompatible && !initializedSection) {
targetTreeDefaultCursor = targetTree.getParent().getCursor();
targetTree.setCursor(crossCursor);
} else {
setTargetTreeCursorToDefault();
}
}
}
}
}