| /******************************************************************************* |
| * Copyright (c) 2000, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.draw2d; |
| |
| import java.util.ArrayList; |
| |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.PointList; |
| import org.eclipse.draw2d.internal.MultiValueMap; |
| |
| /** |
| * An abstract router implementation which detects when multiple connections are |
| * overlapping. Two connections overlap if the combination of source and target |
| * anchors are equal. Subclasses must implement {@link #handleCollision(PointList, int)} |
| * to determine how to avoid the overlap. |
| * <p> |
| * This router can delegate to another connection router. The wrappered router will route |
| * the connections first, after which overlapping will be determined. |
| */ |
| public abstract class AutomaticRouter |
| extends AbstractRouter |
| { |
| |
| private ConnectionRouter nextRouter; |
| private MultiValueMap connections = new MultiValueMap(); |
| |
| private class HashKey { |
| |
| private ConnectionAnchor anchor1, anchor2; |
| |
| HashKey(Connection conn) { |
| anchor1 = conn.getSourceAnchor(); |
| anchor2 = conn.getTargetAnchor(); |
| } |
| |
| public boolean equals(Object object) { |
| boolean isEqual = false; |
| HashKey hashKey; |
| |
| if (object instanceof HashKey) { |
| hashKey = (HashKey)object; |
| ConnectionAnchor hkA1 = hashKey.getFirstAnchor(); |
| ConnectionAnchor hkA2 = hashKey.getSecondAnchor(); |
| |
| isEqual = (hkA1.equals(anchor1) && hkA2.equals(anchor2)) |
| || (hkA1.equals(anchor2) && hkA2.equals(anchor1)); |
| } |
| return isEqual; |
| } |
| |
| public ConnectionAnchor getFirstAnchor() { |
| return anchor1; |
| } |
| |
| public ConnectionAnchor getSecondAnchor() { |
| return anchor2; |
| } |
| |
| public int hashCode() { |
| return anchor1.hashCode() ^ anchor2.hashCode(); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.draw2d.ConnectionRouter#getConstraint(Connection) |
| */ |
| public Object getConstraint(Connection connection) { |
| if (next() != null) |
| return next().getConstraint(connection); |
| return null; |
| } |
| |
| /** |
| * Handles collisions between 2 or more Connections. Collisions are currently defined as 2 |
| * connections with no bendpoints and whose start and end points coincide. In other |
| * words, the 2 connections are the exact same line. |
| * |
| * @param list The PointList of a connection that collides with another connection |
| * @param index The index of the current connection in the list of colliding connections |
| */ |
| protected abstract void handleCollision(PointList list, int index); |
| |
| /** |
| * @see org.eclipse.draw2d.ConnectionRouter#invalidate(Connection) |
| */ |
| public void invalidate(Connection conn) { |
| if (next() != null) |
| next().invalidate(conn); |
| if (conn.getSourceAnchor() == null || conn.getTargetAnchor() == null) |
| return; |
| HashKey connectionKey = new HashKey(conn); |
| ArrayList connectionList = connections.get(connectionKey); |
| int affected = connections.remove(connectionKey, conn); |
| if (affected != -1) { |
| for (int i = affected; i < connectionList.size(); i++) |
| ((Connection)connectionList.get(i)).revalidate(); |
| } else |
| connections.removeValue(conn); |
| |
| } |
| |
| /** |
| * Returns the next router in the chain. |
| * @return The next router |
| * @since 2.0 |
| */ |
| protected ConnectionRouter next() { |
| return nextRouter; |
| } |
| |
| |
| |
| /** |
| * @see org.eclipse.draw2d.ConnectionRouter#remove(Connection) |
| */ |
| public void remove(Connection conn) { |
| if (conn.getSourceAnchor() == null || conn.getTargetAnchor() == null) |
| return; |
| HashKey connectionKey = new HashKey(conn); |
| ArrayList connectionList = connections.get(connectionKey); |
| if (connectionList != null) { |
| int index = connections.remove(connectionKey, conn); |
| for (int i = index + 1; i < connectionList.size(); i++) |
| ((Connection)connectionList.get(i)).revalidate(); |
| } |
| if (next() != null) |
| next().remove(conn); |
| } |
| |
| /** |
| * Routes the given connection. Calls the 'next' router first (if one exists) and if no |
| * bendpoints were added by the next router, collisions are dealt with by calling |
| * {@link #handleCollision(PointList, int)}. |
| * @param conn The connection to route |
| */ |
| public void route(Connection conn) { |
| if (next() != null) |
| next().route(conn); |
| else { |
| conn.getPoints().removeAllPoints(); |
| setEndPoints(conn); |
| } |
| |
| if (conn.getPoints().size() == 2) { |
| PointList points = conn.getPoints(); |
| HashKey connectionKey = new HashKey(conn); |
| ArrayList connectionList = connections.get(connectionKey); |
| |
| if (connectionList != null) { |
| |
| int index; |
| |
| if (connectionList.contains(conn)) { |
| index = connectionList.indexOf(conn) + 1; |
| } else { |
| index = connectionList.size() + 1; |
| connections.put(connectionKey, conn); |
| } |
| |
| handleCollision(points, index); |
| conn.setPoints(points); |
| } else { |
| connections.put(connectionKey, conn); |
| } |
| } |
| } |
| |
| /** |
| * An AutomaticRouter needs no constraints for the connections it routes. This method |
| * invalidates the connections and calls {@link #setConstraint(Connection, Object)} on the |
| * {@link #next()} router. |
| * @see org.eclipse.draw2d.ConnectionRouter#setConstraint(Connection, Object) |
| */ |
| public void setConstraint(Connection connection, Object constraint) { |
| invalidate(connection); |
| if (next() != null) |
| next().setConstraint(connection, constraint); |
| } |
| |
| /** |
| * Sets the start and end points for the given connection. |
| * @param conn The connection |
| */ |
| protected void setEndPoints(Connection conn) { |
| PointList points = conn.getPoints(); |
| points.removeAllPoints(); |
| Point start = getStartPoint(conn); |
| Point end = getEndPoint(conn); |
| conn.translateToRelative(start); |
| conn.translateToRelative(end); |
| points.addPoint(start); |
| points.addPoint(end); |
| conn.setPoints(points); |
| } |
| |
| /** |
| * Sets the next router. |
| * @param router The ConnectionRouter |
| * @since 2.0 |
| */ |
| public void setNextRouter(ConnectionRouter router) { |
| nextRouter = router; |
| } |
| |
| } |