All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.cockpit.modeler.figures.process.ParamConnection Maven / Gradle / Ivy

The newest version!
/*
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package org.openbp.cockpit.modeler.figures.process;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.util.Iterator;
import java.util.StringTokenizer;

import org.openbp.cockpit.modeler.ViewModeMgr;
import org.openbp.cockpit.modeler.drawing.ProcessDrawing;
import org.openbp.cockpit.modeler.figures.generic.CircleConstants;
import org.openbp.cockpit.modeler.figures.generic.GeometryException;
import org.openbp.cockpit.modeler.figures.generic.GeometryUtil;
import org.openbp.cockpit.modeler.figures.generic.Orientation;
import org.openbp.cockpit.modeler.figures.generic.XArrowTip;
import org.openbp.cockpit.modeler.figures.spline.PolySplineConnection;
import org.openbp.cockpit.modeler.figures.tag.TagConnector;
import org.openbp.cockpit.modeler.skins.LinkDescriptor;
import org.openbp.common.CollectionUtil;
import org.openbp.common.util.ToStringHelper;
import org.openbp.core.model.ModelObject;
import org.openbp.core.model.item.process.ControlLink;
import org.openbp.core.model.item.process.DataLink;
import org.openbp.core.model.item.process.DataLinkImpl;
import org.openbp.core.model.item.process.NodeSocket;
import org.openbp.core.model.item.process.Param;
import org.openbp.core.model.item.process.ProcessItem;
import org.openbp.core.model.item.process.ProcessObject;

import CH.ifa.draw.figures.LineDecoration;
import CH.ifa.draw.framework.ConnectionFigure;
import CH.ifa.draw.framework.Connector;
import CH.ifa.draw.framework.Figure;

/**
 * Spline figure representing a data link.
 *
 * @author Stephan Moritz
 */
public class ParamConnection extends PolySplineConnection
	implements ProcessElementContainer
{
	/** The data link this connection represents */
	private DataLink dataLink;

	/** The process this link belongs to */
	private ProcessItem process;

	/** Color for data links with path specification */
	private Color memberPathColor = Color.BLACK;

	/** Decoration for the end of the connection (arrow) */
	private static final LineDecoration endDecoration = new XArrowTip(0.4, 12, 9);

	/**
	 * Constructor.
	 *
	 * @param dataLink Link represented by this figure
	 * @param startConnector Start connector the connection should attach to
	 * @param endConnector End connector the connection should attach to
	 * @param drawing Process drawing that owns the figure
	 */
	public ParamConnection(DataLink dataLink, Connector startConnector, Connector endConnector, ProcessDrawing drawing)
	{
		super(drawing);

		// Connection between the data link and the spline
		this.dataLink = dataLink;
		dataLink.setRepresentation(this);

		process = dataLink.getProcess();

		// The label serves as name holder of the data link name
		getLabel().setClient(dataLink);

		// The decodeGeometry method may need the connectors in order to set the connector orientation, so provide it up front
		setStartConnector(startConnector);
		setEndConnector(endConnector);

		decodeGeometry();

		initializeFigureAttributes();

		// Now do the actual connection after the geometry data has been decoded
		connectStart(startConnector);
		connectEnd(endConnector);
	}

	/**
	 * Constructor for a virgin figure.
	 *
	 * @param drawing Process drawing that owns the figure
	 */
	public ParamConnection(ProcessDrawing drawing)
	{
		super(drawing);

		this.process = drawing.getProcess();

		// We retrieve a new data link from the process
		dataLink = process.createDataLink();
		dataLink.setRepresentation(this);
		getLabel().setClient(dataLink);

		drawDecorations = false;

		initializeFigureAttributes();
	}

	/**
	 * Initializes the figure's drawing attributes.
	 */
	protected void initializeFigureAttributes()
	{
		setEndDecoration(endDecoration);

		LinkDescriptor desc = getDrawing().getProcessSkin().getLinkDescriptor(FigureTypes.LINKTYPE_DATA);
		if (desc != null)
		{
			setStroke(desc.getStroke());
			setFrameColor(desc.getColor());
			memberPathColor = desc.getColor2();
		}
	}

	/**
	 * Returns the data link associated with this parameter connection.
	 */
	public DataLink getDataLink()
	{
		return dataLink;
	}

	/**
	 * Returns a string representation of this object.
	 */
	public String toString()
	{
		return ToStringHelper.toString(this, "dataLink");
	}

	//////////////////////////////////////////////////
	// @@ PolySplineFigure overrides
	//////////////////////////////////////////////////

	public void draw(Graphics g)
	{
		Graphics2D g2 = (Graphics2D) g;

		Stroke oldStroke = g2.getStroke();
		g2.setStroke(getStroke());

		Color oldColor = g2.getColor();

		Color c;
		if (dataLink.getSourceMemberPath() != null || dataLink.getTargetMemberPath() != null)
		{
			c = memberPathColor;
		}
		else
		{
			c = getFrameColor();
		}
		g2.setColor(c);

		drawSpline(g2);

		if (drawDecorations)
		{
			g2.setColor(c);
			drawDecorations(g);
		}

		g2.setColor(oldColor);
		g2.setStroke(oldStroke);
	}

	//////////////////////////////////////////////////
	// @@ PolySplineConnection overrides
	//////////////////////////////////////////////////

	/**
	 * Delegates the check to the underlying control link.
	 * (true if data types match and one is an entry, the other one an exit parameter)
	 */
	public boolean canConnectFigures(Figure startFigure, Figure endFigure, int flags)
	{
		if (!((startFigure instanceof ParamFigure) && (endFigure instanceof ParamFigure)))
			return false;

		SocketFigure startSocketFigure = (SocketFigure) ((ParamFigure) startFigure).getParent();
		SocketFigure endSocketFigure = (SocketFigure) ((ParamFigure) endFigure).getParent();

		if (startSocketFigure.isEntrySocket() == endSocketFigure.isEntrySocket())
		{
			// Never can connect an input param to another input param or output params accordingly
			return false;
		}

		// Check if there is also a control link going from the source socket to the target socket
		boolean haveControlLink = false;
		NodeSocket startSocket = startSocketFigure.getNodeSocket();
		NodeSocket endSocket = endSocketFigure.getNodeSocket();
		for (Iterator it = startSocket.getControlLinks(); it.hasNext();)
		{
			ControlLink link = (ControlLink) it.next();
			if (link.getTargetSocket() == endSocket)
			{
				haveControlLink = true;
				break;
			}
		}
		for (Iterator it = endSocket.getControlLinks(); it.hasNext();)
		{
			ControlLink link = (ControlLink) it.next();
			if (link.getTargetSocket() == startSocket)
			{
				haveControlLink = true;
				break;
			}
		}
		if (!haveControlLink)
			return false;

		return (DataLinkImpl.canLink(((ParamFigure) startFigure).getNodeParam(), dataLink.getSourceMemberPath(), (((ParamFigure) endFigure).getNodeParam()), dataLink.getTargetMemberPath(), flags) != DataLink.CANNOT_LINK);
	}

	protected boolean shouldReverse(Figure startFigure, Figure endFigure)
	{
		SocketFigure socketFigure = (SocketFigure) ((ParamFigure) startFigure()).getParent();
		return socketFigure.isEntrySocket();
	}

	protected void handleConnect(Figure startFigure, Figure endFigure)
	{
		ParamFigure startParamFigure = (ParamFigure) startFigure;
		ParamFigure endParamFigure = (ParamFigure) endFigure;

		Param startParam = startParamFigure.getNodeParam();
		Param endParam = endParamFigure.getNodeParam();

		// Link the parameters
		dataLink.link(startParam, endParam);

		startParamFigure.addParamConnection(this);
		endParamFigure.addParamConnection(this);

		// Add the link to the process if it has not already been added
		if (CollectionUtil.containsReference(process.getDataLinkList(), dataLink))
		{
			// Already present
			return;
		}

		// Add it to the process
		process.addDataLink(dataLink);
		dataLink.maintainReferences(ModelObject.SYNC_GLOBAL_REFNAMES | ModelObject.SYNC_LOCAL_REFNAMES);
	}

	protected void handleDisconnect(Figure startFigure, Figure endFigure)
	{
		process.removeDataLink(dataLink);

		ParamFigure startParamFigure = (ParamFigure) startFigure;
		ParamFigure endParamFigure = (ParamFigure) endFigure;

		if (startParamFigure != null)
			startParamFigure.removeParamConnection(this);
		if (endParamFigure != null)
			endParamFigure.removeParamConnection(this);
	}

	public boolean connectsSame(ConnectionFigure other)
	{
		if (!(other instanceof ParamConnection))
		{
			return false;
		}

		DataLink otherLink = ((ParamConnection) other).getDataLink();

		return dataLink.getSourceParamName().equals(otherLink.getSourceParamName()) && dataLink.getTargetParamName().equals(otherLink.getTargetParamName());
	}

	/////////////////////////////////////////////////////////////////////////
	// @@ Change listeners
	/////////////////////////////////////////////////////////////////////////

	protected void addStartConnectorChangeListener()
	{
		if (getStartConnector() != null)
		{
			((TagConnector) getStartConnector()).getSocketFigure().addFigureChangeListener(this);
		}
	}

	/**
	 * Removes ourself as figure change listener from the start socket we are connected to.
	 */
	protected void removeStartConnectorChangeListener()
	{
		if (getStartConnector() != null)
		{
			((TagConnector) getStartConnector()).getSocketFigure().removeFigureChangeListener(this);
		}
	}

	/**
	 * Adds ourself as figure change listener to the end socket we are connected to.
	 */
	protected void addEndConnectorChangeListener()
	{
		if (getEndConnector() != null)
		{
			((TagConnector) getEndConnector()).getSocketFigure().addFigureChangeListener(this);
		}
	}

	/**
	 * Removes ourself as figure change listener from the end socket we are connected to.
	 */
	protected void removeEndConnectorChangeListener()
	{
		if (getEndConnector() != null)
		{
			((TagConnector) getEndConnector()).getSocketFigure().removeFigureChangeListener(this);
		}
	}

	/**
	 * Checks if the connection is minimized.
	 */
	public boolean isMinimized()
	{
		return !ViewModeMgr.getInstance().isDataLinkVisible(this);
	}

	//////////////////////////////////////////////////
	// @@ Figure overrides
	//////////////////////////////////////////////////

	public void release()
	{
		super.release();

		process.removeDataLink(dataLink);
	}

	//////////////////////////////////////////////////
	// @@ Connection orientation
	//////////////////////////////////////////////////

	/**
	 * Checks if the direction is locked.
	 */
	public boolean isOrientationLocked()
	{
		TagConnector startConnector = (TagConnector) getStartConnector();
		TagConnector endConnector = (TagConnector) getEndConnector();

		return startConnector.isOrientationLocked() || endConnector.isOrientationLocked();
	}

	/**
	 * Toggles the orientation lock.
	 */
	public void toggleOrientationLock()
	{
		TagConnector startConnector = (TagConnector) getStartConnector();
		TagConnector endConnector = (TagConnector) getEndConnector();

		boolean startLocked = startConnector.isOrientationLocked();
		boolean endLocked = endConnector.isOrientationLocked();

		if (startLocked && endLocked)
		{
			// Unlock the orientation of the associated connectors
			if (startLocked)
			{
				startConnector.setLockedOrientation(Orientation.UNDETERMINED);
			}
			if (endLocked)
			{
				endConnector.setLockedOrientation(Orientation.UNDETERMINED);
			}
		}
		else
		{
			// Lock the orientation of the associated connectors
			if (!startLocked)
			{
				startConnector.toggleOrientationLock();
			}
			if (!endLocked)
			{
				endConnector.toggleOrientationLock();
			}
		}
	}

	/**
	 * Flips (toggles) the locked orientation of the associated parameter figures.
	 */
	public void flipOrientation()
	{
		TagConnector startConnector = (TagConnector) getStartConnector();
		TagConnector endConnector = (TagConnector) getEndConnector();

		// First, lock the orientation of the associated figures
		if (!startConnector.isOrientationLocked())
		{
			startConnector.toggleOrientationLock();
		}
		if (!endConnector.isOrientationLocked())
		{
			endConnector.toggleOrientationLock();
		}

		Orientation startOrientation = startConnector.getLockedOrientation();
		Orientation endOrientation = endConnector.getLockedOrientation();

		// Order of orientations to toggle:
		// ^ ^
		// v ^
		// ^ v
		// v v

		if ((startOrientation == Orientation.TOP || startOrientation == Orientation.LEFT) && (endOrientation == Orientation.TOP || endOrientation == Orientation.LEFT))
		{
			startConnector.flipOrientation();
		}
		else if ((startOrientation == Orientation.BOTTOM || startOrientation == Orientation.RIGHT) && (endOrientation == Orientation.TOP || endOrientation == Orientation.LEFT))
		{
			startConnector.flipOrientation();
			endConnector.flipOrientation();
		}
		else if ((startOrientation == Orientation.TOP || startOrientation == Orientation.LEFT) && (endOrientation == Orientation.BOTTOM || endOrientation == Orientation.RIGHT))
		{
			startConnector.flipOrientation();
		}
		else if ((startOrientation == Orientation.BOTTOM || startOrientation == Orientation.RIGHT) && (endOrientation == Orientation.BOTTOM || endOrientation == Orientation.RIGHT))
		{
			startConnector.flipOrientation();
			endConnector.flipOrientation();
		}
	}

	public void layoutAndAdjustConnection()
	{
		TagConnector startConnector = (TagConnector) getStartConnector();
		TagConnector endConnector = (TagConnector) getEndConnector();

		Orientation startOrientation = startConnector.getOrientation();
		Orientation endOrientation = endConnector.getOrientation();

		SocketFigure startSocketFigure = (SocketFigure) ((ParamFigure) startFigure()).getParent();
		SocketFigure endSocketFigure = (SocketFigure) ((ParamFigure) endFigure()).getParent();
		Figure startNodeFigure = startSocketFigure.getParent();
		Figure endNodeFigure = endSocketFigure.getParent();

		Point startCenter = startSocketFigure.center();
		Point endCenter = endSocketFigure.center();
		Orientation startSocketOrientation = CircleConstants.determineOrientation(startSocketFigure.getAngle(), startNodeFigure.displayBox());
		Orientation endSocketOrientation = CircleConstants.determineOrientation(endSocketFigure.getAngle(), endNodeFigure.displayBox());
		boolean startSocketIsVertical = startSocketOrientation == Orientation.TOP || startSocketOrientation == Orientation.BOTTOM;
		boolean endSocketIsVertical = endSocketOrientation == Orientation.TOP || endSocketOrientation == Orientation.BOTTOM;
		boolean socketsInSync = startSocketIsVertical == endSocketIsVertical;

		// TODO Fix 4: Param autoconnector orientation doesn't work optimal, test...
		if (socketsInSync)
		{
			endOrientation = startOrientation;
		}
		else
		{
			if (startSocketOrientation == Orientation.LEFT || startSocketOrientation == Orientation.RIGHT)
			{
				if (startCenter.y <= endCenter.y)
					startOrientation = Orientation.BOTTOM;
				else
					startOrientation = Orientation.TOP;
			}

			if (startSocketOrientation == Orientation.TOP || startSocketOrientation == Orientation.BOTTOM)
			{
				if (startCenter.x <= endCenter.x)
					startOrientation = Orientation.RIGHT;
				else
					startOrientation = Orientation.LEFT;
			}

			if (endSocketOrientation == Orientation.LEFT || endSocketOrientation == Orientation.RIGHT)
			{
				if (startCenter.y <= endCenter.y)
					endOrientation = Orientation.TOP;
				else
					endOrientation = Orientation.BOTTOM;
			}

			if (endSocketOrientation == Orientation.TOP || endSocketOrientation == Orientation.BOTTOM)
			{
				if (startCenter.x <= endCenter.x)
					endOrientation = Orientation.LEFT;
				else
					endOrientation = Orientation.RIGHT;
			}

			/*
			if (verticalRelationship)
			{
				// Vertical line, connectors can be LEFT and RIGHT for param connections
				if (startCenter.x < endCenter.x)
				{
					startOrientation = Orientation.RIGHT;
					endOrientation = Orientation.LEFT;
				}
				else
				{
					startOrientation = Orientation.LEFT;
					endOrientation = Orientation.RIGHT;
				}
			}
			else
			{
				// Horizontal line, connectors can be TOP and BOTTOM for param connections
				if (startCenter.y < endCenter.y)
				{
					startOrientation = Orientation.BOTTOM;
					endOrientation = Orientation.TOP;
				}
				else
				{
					startOrientation = Orientation.TOP;
					endOrientation = Orientation.BOTTOM;
				}
				startConnector.setLockedOrientation(startOrientation);
				endConnector.setLockedOrientation(endOrientation);
			}
			 */
		}

		startConnector.setLockedOrientation(startOrientation);
		endConnector.setLockedOrientation(endOrientation);

		// Perform a simple connection layout
		layoutConnection();
	}

	//////////////////////////////////////////////////
	// @@ Geometry serialization support
	//////////////////////////////////////////////////

	public void decodeGeometry()
	{
		String errIdent = dataLink.getQualifier().toUntypedString();
		decode(dataLink.getGeometry(), errIdent);
	}

	public void encodeGeometry()
	{
		dataLink.setGeometry(encode());
	}

	protected boolean decodeParameter(String parameter, String errName)
	{
		if (super.decodeParameter(parameter, errName))
			return true;

		StringTokenizer st = new StringTokenizer(parameter, ":");

		String paramName = st.nextToken();

		if (paramName.equals("orientation"))
		{
			Orientation startOrientation = Orientation.fromInt(GeometryUtil.parseInt(st, paramName, errName));
			Orientation endOrientation = Orientation.fromInt(GeometryUtil.parseInt(st, paramName, errName));
			((TagConnector) getStartConnector()).setLockedOrientation(startOrientation);
			((TagConnector) getEndConnector()).setLockedOrientation(endOrientation);
		}
		else
		{
			throw new GeometryException("Unknown paramter '" + paramName + "'.", errName);
		}
		return true;
	}

	protected String encode()
	{
		String result = super.encode();

		TagConnector startConnector = (TagConnector) getStartConnector();
		TagConnector endConnector = (TagConnector) getEndConnector();
		if ((startConnector != null && startConnector.getLockedOrientation() != Orientation.UNDETERMINED) && (endConnector != null && endConnector.getLockedOrientation() != Orientation.UNDETERMINED))
		{
			result = result + "|orientation:" + startConnector.getLockedOrientation() + ":" + endConnector.getLockedOrientation();
		}

		return result;
	}

	//////////////////////////////////////////////////
	// @@ ProcessElementContainer implementation
	//////////////////////////////////////////////////

	public ProcessObject getProcessElement()
	{
		return dataLink;
	}

	public ProcessObject getReferredProcessElement()
	{
		return getProcessElement();
	}

	/**
	 * Start socket should be selected on deletion.
	 */
	public Figure selectionOnDelete()
	{
		return startFigure();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy