package nl.fontys.sofa.limo.view.chain; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.beans.IntrospectionException; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.JComponent; import javax.swing.event.UndoableEditEvent; import javax.swing.undo.UndoManager; import nl.fontys.sofa.limo.api.service.status.StatusBarService; import nl.fontys.sofa.limo.domain.component.Node; import nl.fontys.sofa.limo.domain.component.SupplyChain; import nl.fontys.sofa.limo.domain.component.event.Event; import nl.fontys.sofa.limo.domain.component.hub.Hub; import nl.fontys.sofa.limo.domain.component.leg.Leg; import nl.fontys.sofa.limo.domain.component.leg.MultiModeLeg; import nl.fontys.sofa.limo.domain.component.leg.ScheduledLeg; import nl.fontys.sofa.limo.domain.component.procedure.Procedure; import nl.fontys.sofa.limo.domain.component.type.LegType; import nl.fontys.sofa.limo.view.custom.panel.SelectLegTypePanel; import nl.fontys.sofa.limo.view.node.WidgetableNode; import nl.fontys.sofa.limo.view.node.bean.AbstractBeanNode; import nl.fontys.sofa.limo.view.node.bean.EventNode; import nl.fontys.sofa.limo.view.node.bean.HubNode; import nl.fontys.sofa.limo.view.node.bean.LegNode; import nl.fontys.sofa.limo.view.node.bean.LegTypeNode; import nl.fontys.sofa.limo.view.node.bean.MultiModeLegNode; import nl.fontys.sofa.limo.view.node.bean.ProcedureNode; import nl.fontys.sofa.limo.view.node.bean.ScheduledLegNode; import nl.fontys.sofa.limo.view.node.factory.LegTypeChildFactory; import nl.fontys.sofa.limo.view.topcomponent.DynamicExplorerManagerProvider; import nl.fontys.sofa.limo.view.util.LIMOResourceBundle; import nl.fontys.sofa.limo.view.util.undoable.widget.hub.AddHubWidgetUndoableEdit; import nl.fontys.sofa.limo.view.util.undoable.widget.leg.AddLegWidgetUndoableEdit; import nl.fontys.sofa.limo.view.widget.BasicWidget; import nl.fontys.sofa.limo.view.widget.HubWidget; import nl.fontys.sofa.limo.view.widget.LegWidget; import nl.fontys.sofa.limo.view.widget.StartWidget; import nl.fontys.sofa.limo.view.wizard.leg.multimode.MultimodeLegWizardAction; import nl.fontys.sofa.limo.view.wizard.leg.scheduled.ScheduledLegWizardAction; import org.netbeans.api.visual.action.AcceptProvider; import org.netbeans.api.visual.action.ActionFactory; import org.netbeans.api.visual.action.ConnectProvider; import org.netbeans.api.visual.action.ConnectorState; import org.netbeans.api.visual.action.SelectProvider; import org.netbeans.api.visual.action.WidgetAction; import org.netbeans.api.visual.anchor.AnchorFactory; import org.netbeans.api.visual.layout.LayoutFactory; import org.netbeans.api.visual.widget.ConnectionWidget; import org.netbeans.api.visual.widget.LayerWidget; import org.netbeans.api.visual.widget.Scene; import org.netbeans.api.visual.widget.Widget; import org.netbeans.spi.palette.PaletteController; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; import org.openide.nodes.NodeTransfer; import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.lookup.AbstractLookup; import org.openide.util.lookup.InstanceContent; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; import org.openide.windows.TopComponent; /** * Implementation of the {@link nl.fontys.sofa.limo.view.chain.ChainGraphScene} * interface. * <p> * This scene has 3 different {@link org.netbeans.api.visual.widget.LayerWidget} * to draw things on and provides several * {@link org.netbeans.api.visual.action.WidgetAction}. It also uses a * {@link nl.fontys.sofa.limo.view.chain.ChainBuilder} which manages the correct * building of the {@link nl.fontys.sofa.limo.domain.component.SupplyChain}. * <p> * Some actions need a provider implementation which is implemented in several * private classes within this class. * <p> * The {@link nl.fontys.sofa.limo.view.chain.ChainGraphSceneImpl} creates * {@link javax.swing.undo.UndoableEdit} for add and deleting a * {@link nl.fontys.sofa.limo.view.widget.LegWidget} or * {@link nl.fontys.sofa.limo.view.widget.HubWidget}. When a HubWidget is * deleted a undoableEdit is created and added to the UndoManager which is hand * in the constructor. The same belongs to add HubWidget and delete and add * LegWidget. * * @author Sebastiaan Heijmann */ public class ChainGraphSceneImpl extends ChainGraphScene implements PropertyChangeListener { private final InstanceContent ic; private final DynamicExplorerManagerProvider parent; private final ChainBuilder chainBuilder; private final SupplyChain loadedChain; // The layers to draw things on. private final LayerWidget mainLayer; private final LayerWidget connectionLayer; private final LayerWidget interactionLayer; // The scene actions available. private final WidgetAction moveAlignAction; private final WidgetAction zoomAction; private final WidgetAction panAction; private final WidgetAction acceptAction; private final WidgetAction selectAction; private final WidgetAction connectAction; //@TODO implement reconnect action private final WidgetAction reconnectAction; private HubWidget startHubWidget; private final StartWidget startFlagWidget; private UndoManager undoManager; private ProxyLookup lookup; private PaletteController paletteController; /** * Constructor which sets the parent and creates the chain builder, the * layers and the available actions. * * @param parent the parent of this scene. * @param chain * @throws IOException can occur when certain resources like images cannot * @throws IntrospectionException be found. */ public ChainGraphSceneImpl(DynamicExplorerManagerProvider parent, SupplyChain chain, PaletteController paletteController) throws IOException, IntrospectionException { this(parent, chain, null, paletteController); } /** * Constructor which sets the parent and creates the chain builder, the * layers, the available actions and using a undoManager. * * @param parent the parent of this scene. * @param chain * @param undoManager undo manager which should be used to undo changes * @throws IOException can occur when certain resources like images cannot * @throws IntrospectionException be found. */ public ChainGraphSceneImpl(DynamicExplorerManagerProvider parent, SupplyChain chain, UndoManager undoManager, PaletteController paletteController) throws IOException, IntrospectionException { this.ic = new InstanceContent(); this.parent = parent; chainBuilder = new ChainBuilderImpl(chain); chainBuilder.getSupplyChain().setName(chain.getName()); //sets the name of //the supplyChain so that when you load an existing supplychain and //then save it at another location dont get a file named null.lsc chainBuilder.getSupplyChain().setFilepath(chain.getFilepath()); loadedChain = chain; this.mainLayer = new LayerWidget(this); this.connectionLayer = new LayerWidget(this); this.interactionLayer = new LayerWidget(this); this.paletteController = paletteController; addChild(mainLayer); addChild(connectionLayer); addChild(interactionLayer); moveAlignAction = ActionFactory.createAlignWithMoveAction( mainLayer, interactionLayer, null); zoomAction = ActionFactory.createZoomAction(); panAction = ActionFactory.createPanAction(); acceptAction = ActionFactory.createAcceptAction(new SceneAcceptProvider(this)); selectAction = ActionFactory.createSelectAction(new SceneSelectProvider()); connectAction = ActionFactory.createExtendedConnectAction(interactionLayer, new SceneConnectProvider()); reconnectAction = null; getActions().addAction(acceptAction); getActions().addAction(zoomAction); getActions().addAction(panAction); startFlagWidget = new StartWidget(this); this.undoManager = undoManager; Lookup dL = new AbstractLookup(ic); if (undoManager != null) { Lookup undoRedo = Lookups.singleton(undoManager); lookup = new ProxyLookup(dL, undoRedo, super.getLookup()); } else { lookup = new ProxyLookup(dL, super.getLookup()); } } @Override public Lookup getLookup() { return lookup; } @Override public JComponent createView() { JComponent component = super.createView(); if (loadedChain.getStartHub() != null) { try { drawExistingSupplyChain(loadedChain); } catch (IntrospectionException ex) { Exceptions.printStackTrace(ex); } } return component; } /** * Draw an existing supplychain onto the scene. * * @param supplyChain the supplychain to draw. * @throws IntrospectionException */ void drawExistingSupplyChain(SupplyChain supplyChain) throws IntrospectionException { Point point = new Point(100, 100); mainLayer.setLayout(LayoutFactory.createAbsoluteLayout()); Node currentNode = supplyChain.getStartHub(); HubWidget previousWidget = null; ConnectionWidget connectionWidget = null; HubWidget nextWidget = null; while (currentNode != null) { if (currentNode instanceof Hub) { HubWidget w = (HubWidget) addNode(new HubNode((Hub) currentNode)); addHubWidget(w); if (currentNode == supplyChain.getStartHub()) { setStartWidget(w); } w.setPreferredLocation(point); nextWidget = w; point = new Point(((int) point.getX() + 300), (int) point.getY()); if (connectionWidget != null) { connectHubWidgets(previousWidget, connectionWidget, nextWidget); } } else if (currentNode instanceof Leg) { if (currentNode instanceof ScheduledLeg) { connectionWidget = (ConnectionWidget) addEdge(new ScheduledLegNode((ScheduledLeg) currentNode)); } else if (currentNode instanceof MultiModeLeg) { connectionWidget = (ConnectionWidget) addEdge(new MultiModeLegNode((MultiModeLeg) currentNode)); } else if (currentNode instanceof Leg) { connectionWidget = (ConnectionWidget) addEdge(new LegNode((Leg) currentNode)); } previousWidget = nextWidget; } currentNode = currentNode.getNext(); } } @Override public ChainBuilder getChainBuilder() { return chainBuilder; } @Override public WidgetAction getSelectAction() { return selectAction; } @Override public WidgetAction getConnectAction() { return connectAction; } @Override public WidgetAction getMoveAlignAction() { return moveAlignAction; } @Override public SupplyChain getSupplyChain() { return chainBuilder.getSupplyChain(); } @Override public DynamicExplorerManagerProvider getParent() { return parent; } @Override public Widget getStartWidget() { return startHubWidget; } @Override public void setStartWidget(Widget widget) { if (startHubWidget != null) { startHubWidget.setStartFlag(false); } HubWidget hubWidget = (HubWidget) widget; startHubWidget = hubWidget; startHubWidget.setStartFlag(true); chainBuilder.setStartHub(hubWidget.getHub()); this.validate(); } @Override public void addHubWidget(HubWidget hubWidget) { Lookup.getDefault().lookup(StatusBarService.class).setMessage(hubWidget.getHub().getName(), StatusBarService.ACTION_ADD, StatusBarService.STATE_SUCCESS, null); if (hubWidget.getHub().getPrevious() == null) { hubWidget.setStartFlag(true); } mainLayer.addChild(hubWidget); chainBuilder.addHub(hubWidget.getHub()); checkChainHubs(); getScene().repaint(); ic.add((BasicWidget) hubWidget); hubWidget.addPropertyChangeListener(this); } @Override public void connectHubWidgets(HubWidget source, ConnectionWidget legWidget, HubWidget target) { Node node = target.getHub(); while (node != null) { node = node.getNext(); if (node != null) { if (node.equals(source.getHub())) { NotifyDescriptor d = new NotifyDescriptor.Message("Circles in supply chain aren't allowed!", NotifyDescriptor.WARNING_MESSAGE); DialogDisplayer.getDefault().notify(d); return; } } } Lookup.getDefault().lookup(StatusBarService.class).setMessage(source.getHub().getName() + " " + LIMOResourceBundle.getString("AND") + " " + target.getHub().getName(), StatusBarService.ACTION_CONNECT, StatusBarService.STATE_SUCCESS, null); AbstractBeanNode sourceNode = (AbstractBeanNode) findObject(source); AbstractBeanNode legNode = (AbstractBeanNode) findObject(legWidget); AbstractBeanNode targetNode = (AbstractBeanNode) findObject(target); setEdgeSource(legNode, sourceNode); setEdgeTarget(legNode, targetNode); Hub hubSource = sourceNode.getLookup().lookup(Hub.class); Leg leg = legNode.getLookup().lookup(Leg.class); Hub hubTarget = targetNode.getLookup().lookup(Hub.class); chainBuilder.connectHubsByLeg( hubSource, leg, hubTarget); target.setStartFlag(false); this.checkChainHubs(); ic.add((BasicWidget) legWidget); ((LegWidget) legWidget).addPropertyChangeListener(this); } /** * Checks the hubs and hubWidgets if the previous Node is null and sets the * starthub. */ public void checkChainHubs() { int checkIfMoreThanOne = 0; Hub startHub = new Hub(); List<Hub> hubList = chainBuilder.getHubList(); for (Hub hub : hubList) { if (hub.getPrevious() == null) { checkIfMoreThanOne++; startHub = hub; } } if (checkIfMoreThanOne == 1) { chainBuilder.setStartHub(startHub); } List<Widget> hubWidgetList = mainLayer.getChildren(); hubWidgetList.stream().map((hubWidget) -> (HubWidget) hubWidget).forEach((hw) -> { hw.setStartFlag(hw.getHub().getPrevious() == null); }); } @Override public void removeHubWidget(HubWidget hubWidget) { Hub hub = hubWidget.getHub(); chainBuilder.removeHub(hubWidget.getHub()); if (chainBuilder.getStartHub() == hub) { chainBuilder.setStartHub(null); } ic.remove((BasicWidget) hubWidget); } @Override public void disconnectLegWidget(LegWidget legWidget) { Lookup.getDefault().lookup(StatusBarService.class).setMessage(legWidget.getLeg().getName(), StatusBarService.ACTION_DELETE, StatusBarService.STATE_SUCCESS, null); chainBuilder.disconnectLeg(legWidget.getLeg()); this.checkChainHubs(); ic.remove((BasicWidget) legWidget); } @Override public int getNumberOfHubs() { return chainBuilder.getNumberOfHubs(); } @Override protected Widget attachNodeWidget(AbstractBeanNode node) { WidgetableNode wn = (WidgetableNode) node; BasicWidget widget = (BasicWidget) wn.getWidget(this); widget.addActions(this); return (Widget) widget; } @Override protected Widget attachEdgeWidget(AbstractBeanNode edge) { BasicWidget connectionWidget = new LegWidget(this, edge); connectionWidget.addActions(this); connectionLayer.addChild((Widget) connectionWidget); return (Widget) connectionWidget; } @Override protected void attachEdgeSourceAnchor(AbstractBeanNode edge, AbstractBeanNode oldSourceNode, AbstractBeanNode sourceNode) { Widget sourceWidget = findWidget(sourceNode); if (sourceWidget == null) { sourceWidget = findWidget(oldSourceNode); } ConnectionWidget connectionWidget = (ConnectionWidget) findWidget(edge); connectionWidget.setSourceAnchor(AnchorFactory.createRectangularAnchor(sourceWidget, false)); } @Override protected void attachEdgeTargetAnchor(AbstractBeanNode edge, AbstractBeanNode oldTargetNode, AbstractBeanNode targetNode) { Widget targetWidget = findWidget(targetNode); if (targetWidget == null) { targetWidget = (HubWidget) findWidget(oldTargetNode); } ConnectionWidget connectionWidget = (ConnectionWidget) findWidget(edge); connectionWidget.setTargetAnchor(AnchorFactory.createRectangularAnchor(targetWidget, false)); } @Override public void propertyChange(PropertyChangeEvent evt) { chainBuilder.modify(); validate(); } /** * This provider is responsible for accepting objects into the scene. */ private class SceneAcceptProvider implements AcceptProvider { private final ChainGraphScene scene; /** * Constructor sets the scene. * * @param scene the main scene. */ public SceneAcceptProvider(ChainGraphScene scene) { this.scene = scene; } private boolean showLegNotification = false; @Override public ConnectorState isAcceptable(Widget widget, Point point, Transferable transferable) { org.openide.nodes.Node node = NodeTransfer.node(transferable, NodeTransfer.DND_COPY_OR_MOVE); if (node instanceof WidgetableNode) { WidgetableNode widgetNode = (WidgetableNode) node; if (widgetNode.isAcceptable(widget, point)) { return ConnectorState.ACCEPT; } } else if (node instanceof EventNode) { return ConnectorState.ACCEPT; } else if (node instanceof ProcedureNode) { return ConnectorState.ACCEPT; } if (node instanceof LegTypeNode) { if (!showLegNotification) { showLegNotification = true; DialogDescriptor nd = new DialogDescriptor("Just select the leg type from the palette and draw the line with ctrl key between the two hubs.", "Information", false, new String[]{"OK"}, null, DialogDescriptor.DEFAULT_ALIGN, null, null); nd.setButtonListener((ActionEvent e) -> { showLegNotification = false; }); nd.addPropertyChangeListener((PropertyChangeEvent evt) -> { if (evt.getNewValue() == DialogDescriptor.CANCEL_OPTION || evt.getNewValue() == DialogDescriptor.CLOSED_OPTION) { showLegNotification = false; } }); DialogDisplayer.getDefault().notifyLater(nd); } return ConnectorState.REJECT_AND_STOP; } return ConnectorState.REJECT; } @Override public void accept(Widget widget, Point point, Transferable transferable) { AbstractBeanNode node = (AbstractBeanNode) NodeTransfer.node(transferable, NodeTransfer.DND_COPY_OR_MOVE); if (node instanceof EventNode) { acceptEvent(node, point); } else if (node instanceof ProcedureNode) { acceptProcedure(node, point); } else { acceptHub(node, widget, point); } TopComponent comp = (TopComponent) parent; comp.requestActive(); } private void acceptHub(AbstractBeanNode node, Widget widget, Point point) { AbstractBeanNode detachedNode = node.getDetachedNodeCopy(); BasicWidget w = (BasicWidget) scene.addNode(detachedNode); detachedNode.addPropertyChangeListener(w); w.drop(scene, widget, point); if (undoManager != null) { UndoableEditEvent event = new UndoableEditEvent(w, new AddHubWidgetUndoableEdit(scene, (HubWidget) w)); undoManager.undoableEditHappened(event); } TopComponent comp = (TopComponent) parent; comp.requestActive(); } private void acceptEvent(AbstractBeanNode node, Point point) { Event event = node.getLookup().lookup(Event.class); List<Widget> hitlist = new ArrayList<>(); mainLayer.getChildren().forEach((w) -> { Point p = w.convertSceneToLocal(point); if (w.isHitAt(p)) { hitlist.add(w); } }); connectionLayer.getChildren().forEach((Widget c) -> { Point p = c.convertSceneToLocal(point); Rectangle r = new Rectangle(p); r.width = 25; r.height = 25; r.x -= r.width / 2; r.y -= r.height / 2; if (c.getBounds().contains(r)) { hitlist.add(c); } }); hitlist.forEach((w) -> { if (w instanceof HubWidget) { HubWidget hubWidget = (HubWidget) w; List<Event> events = hubWidget.getHub().getEvents(); events.add(event); hubWidget.getHub().setEvents(events); hubWidget.updateLabels(); } else if (w instanceof LegWidget) { LegWidget legWidget = (LegWidget) w; List<Event> events = legWidget.getLeg().getEvents(); events.add(event); legWidget.getLeg().setEvents(events); legWidget.updateLabels(); } }); if (hitlist.size() > 0) { mainLayer.revalidate(); connectionLayer.revalidate(); chainBuilder.modify(); } } private void acceptProcedure(AbstractBeanNode node, Point point) { Procedure procedure = node.getLookup().lookup(Procedure.class); List<Widget> hitlist = new ArrayList<>(); mainLayer.getChildren().forEach((w) -> { Point p = w.convertSceneToLocal(point); if (w.isHitAt(p)) { hitlist.add(w); } }); connectionLayer.getChildren().forEach((c) -> { Point p = c.convertSceneToLocal(point); Rectangle r = new Rectangle(p); r.width = 25; r.height = 25; r.x -= r.width / 2; r.y -= r.height / 2; if (c.getBounds().contains(r)) { hitlist.add(c); } }); hitlist.forEach((Widget w) -> { if (w instanceof HubWidget) { HubWidget hubWidget = (HubWidget) w; List<Procedure> procedures = hubWidget.getHub().getProcedures(); procedures.add(procedure); hubWidget.getHub().setProcedures(procedures); hubWidget.updateLabels(); } else if (w instanceof LegWidget) { LegWidget legWidget = (LegWidget) w; List<Procedure> procedures = legWidget.getLeg().getProcedures(); procedures.add(procedure); legWidget.getLeg().setProcedures(procedures); legWidget.updateLabels(); } }); if (hitlist.size() > 0) { mainLayer.revalidate(); connectionLayer.revalidate(); chainBuilder.modify(); } } } /** * This provider is responsible for selecting objects in a scene. */ private class SceneSelectProvider implements SelectProvider { @Override public boolean isAimingAllowed(Widget widget, Point localLocation, boolean invertSelection) { return false; } @Override public boolean isSelectionAllowed(Widget widget, Point localLocation, boolean invertSelection) { return findObject(widget) != null; } @Override public void select(Widget widget, Point localLocation, boolean invertSelection) { Object object = findObject(widget); AbstractBeanNode container = (AbstractBeanNode) object; parent.setRootContext(container); setFocusedObject(object); if (object != null) { if (!invertSelection && getSelectedObjects().contains(object)) { return; } userSelectionSuggested(Collections.singleton(object), invertSelection); } else { userSelectionSuggested(Collections.emptySet(), invertSelection); } } } /** * This provider is responsible for connecting objects in a scene. */ private class SceneConnectProvider implements ConnectProvider { private AbstractBeanNode source = null; private AbstractBeanNode target = null; @Override public boolean isSourceWidget(Widget sourceWidget) { AbstractBeanNode container = (AbstractBeanNode) findObject(sourceWidget); source = isNode(container) ? container : null; return source != null; } @Override public ConnectorState isTargetWidget(Widget sourceWidget, Widget targetWidget) { AbstractBeanNode container = (AbstractBeanNode) findObject(targetWidget); target = isNode(container) ? container : null; if (target != null) { return !source.equals(target) ? ConnectorState.ACCEPT : ConnectorState.REJECT_AND_STOP; } return container != null ? ConnectorState.REJECT_AND_STOP : ConnectorState.REJECT; } @Override public boolean hasCustomTargetWidgetResolver(Scene scene) { return false; } @Override public Widget resolveTargetWidget(Scene scene, Point sceneLocation) { return null; } private Leg leg; @Override public void createConnection(Widget sourceWidget, Widget targetWidget) { leg = null; if (validateConnection(sourceWidget, targetWidget)) { LegType legType = paletteController.getSelectedItem().lookup(LegType.class); if (legType == null) { SelectLegTypePanel inputPane = new SelectLegTypePanel(); leg = inputPane.getLeg(); } else if (legType == LegTypeChildFactory.MULTIMODE_LEGTYPE) { MultimodeLegWizardAction multimodeLegWizardAction; multimodeLegWizardAction = new MultimodeLegWizardAction((MultiModeLeg newLeg) -> { leg = newLeg; }); multimodeLegWizardAction.actionPerformed(null); } else if (legType == LegTypeChildFactory.SCHEDULED_LEGTYPE) { ScheduledLegWizardAction scheduledLegWizardAction; scheduledLegWizardAction = new ScheduledLegWizardAction((ScheduledLeg newLeg) -> { leg = newLeg; }); scheduledLegWizardAction.actionPerformed(null); } else { leg = new Leg(legType); } if (leg != null) { try { HubWidget hubSourceWidget = (HubWidget) findWidget(source); HubWidget hubTargetWidget = (HubWidget) findWidget(target); ConnectionWidget connectionWidget; if (leg instanceof ScheduledLeg) { connectionWidget = (ConnectionWidget) addEdge(new ScheduledLegNode((ScheduledLeg) leg)); } else if (leg instanceof MultiModeLeg) { connectionWidget = (ConnectionWidget) addEdge(new MultiModeLegNode((MultiModeLeg) leg)); } else { connectionWidget = (ConnectionWidget) addEdge(new LegNode((Leg) leg)); } connectHubWidgets( hubSourceWidget, connectionWidget, hubTargetWidget); if (undoManager != null) { undoManager.undoableEditHappened(new UndoableEditEvent(connectionWidget, new AddLegWidgetUndoableEdit((LegWidget) connectionWidget, (HubNode) source, (HubNode) target, ChainGraphSceneImpl.this))); } } catch (IntrospectionException ex) { Exceptions.printStackTrace(ex); } } } } /** * Validate a connection between two widgets. * * @param sourceWidget the connection source. * @param targetWidget the connection target. * @return */ private boolean validateConnection( Widget sourceWidget, Widget targetWidget) { AbstractBeanNode sourceNode = (AbstractBeanNode) findObject(sourceWidget); AbstractBeanNode targetNode = (AbstractBeanNode) findObject(targetWidget); return findNodeEdges(sourceNode, true, false).isEmpty() && findNodeEdges(targetNode, false, true).isEmpty(); } } }