/**
   * {@inheritDoc}
   *
   * @see org.eclipse.draw2d.AbstractRouter#invalidate(org.eclipse.draw2d.Connection)
   */
  @Override
  public void invalidate(final Connection conn) {
    if (conn.getSourceAnchor() == null
        || conn.getSourceAnchor().getOwner() == null
        || conn.getTargetAnchor() == null
        || conn.getTargetAnchor().getOwner() == null) {
      return;
    }

    final ListIterator li = Lists.newArrayList(connectionList).listIterator();
    while (li.hasNext()) {
      final Connection connNext = (Connection) li.next();

      if (!trunkVertexEqual(connNext, conn)) {
        updateConstraint(connNext);
      }
    }
  }
  /** @param conn */
  private void internalRoute(final Connection conn) {
    if (conn.getSourceAnchor() == null
        || conn.getSourceAnchor().getOwner() == null
        || conn.getTargetAnchor() == null
        || conn.getTargetAnchor().getOwner() == null) {
      super.route(conn);
      return;
    }

    if (!connectionList.contains(conn)) {
      connectionList.add(conn);
    }

    checkTrunkVertex(conn);

    getBranchRouter().route(conn);
    invalidate(conn);
  }
  /**
   * UpdateConstraint Updates the constraint value for the connection based on the tree vertex.
   *
   * @param conn Connection whose constraint is to be updated.
   */
  protected void updateConstraint(final Connection conn) {
    boolean update =
        conn != null && conn.getSourceAnchor() != null && conn.getTargetAnchor() != null;
    update =
        update
            && conn.getSourceAnchor().getOwner() != null
            && conn.getTargetAnchor().getOwner() != null;
    if (update) {
      if (isUpdatingPeers()) {
        return;
      }

      List bendpoints = (List) conn.getRoutingConstraint();
      if (bendpoints == null) {
        bendpoints = new ArrayList(conn.getPoints().size());
      }

      final Point sourceRefPoint = conn.getSourceAnchor().getReferencePoint();
      conn.translateToRelative(sourceRefPoint);

      final Point targetRefPoint = conn.getTargetAnchor().getReferencePoint();
      conn.translateToRelative(targetRefPoint);

      final Point ptTrunk = getTrunkLocation(conn);
      final Point ptSource = getBranchRouter().getSourceLocation(conn, ptTrunk);

      bendpoints.clear();
      final PointList pts = getBranchRouter().recreateBranch(conn, ptSource, ptTrunk);
      for (int i = 0; i < pts.size(); i++) {
        final Bendpoint bp = new AbsoluteBendpoint(pts.getPoint(i));
        bendpoints.add(bp);
      }

      setUpdatingPeers(true);

      try {
        setConstraint(conn, bendpoints);
        conn.invalidate();
        conn.validate();
      } finally {
        setUpdatingPeers(false);
      }
    }
  }
  /**
   * {@inheritDoc}
   *
   * @see org.eclipse.draw2d.BendpointConnectionRouter#remove(org.eclipse.draw2d.Connection)
   */
  @Override
  public void remove(final Connection conn) {
    if (conn.getSourceAnchor() == null || conn.getTargetAnchor() == null) {
      return;
    }

    final int index = connectionList.indexOf(conn);
    connectionList.remove(conn);
    for (int i = index + 1; i < connectionList.size(); i++) {
      ((Connection) connectionList.get(i)).revalidate();
    }

    getBranchRouter().remove(conn);
  }
  private void processStaleConnections() {
    Iterator<Connection> iter = staleConnections.iterator();
    if (iter.hasNext() && connectionToPaths == null) {
      connectionToPaths = new HashMap<Connection, Path>();
      hookAll();
    }

    while (iter.hasNext()) {
      Connection conn = (Connection) iter.next();

      Path path = (Path) connectionToPaths.get(conn);
      if (path == null) {
        path = new Path(conn);
        connectionToPaths.put(conn, path);
        algorithm.addPath(path);
      }

      List<?> constraint = (List<?>) getConstraint(conn);
      if (constraint == null) {
        constraint = Collections.EMPTY_LIST;
      }

      Point start = conn.getSourceAnchor().getReferencePoint().getCopy();
      Point end = conn.getTargetAnchor().getReferencePoint().getCopy();

      container.translateToRelative(start);
      container.translateToRelative(end);

      path.setStartPoint(start);
      path.setEndPoint(end);

      if (!constraint.isEmpty()) {
        PointList bends = new PointList(constraint.size());
        for (int i = 0; i < constraint.size(); i++) {
          Bendpoint bp = (Bendpoint) constraint.get(i);
          bends.addPoint(bp.getLocation());
        }
        path.setBendPoints(bends);
      } else {
        path.setBendPoints(null);
      }

      isDirty |= path.isDirty;
    }
    staleConnections.clear();
  }
  @Override
  public void route(Connection conn) {
    if (isDirty) {
      ignoreInvalidate = true;
      processStaleConnections();
      isDirty = false;
      List<?> updated = algorithm.solve();
      Connection current;
      for (int i = 0; i < updated.size(); i++) {
        Path path = (Path) updated.get(i);
        current = (Connection) path.data;
        current.revalidate();

        PointList points = path.getPoints().getCopy();
        Point ref1;
        Point ref2;
        Point start;
        Point end;
        ref1 = new PrecisionPoint(points.getPoint(1));
        ref2 = new PrecisionPoint(points.getPoint(points.size() - 2));
        current.translateToAbsolute(ref1);
        current.translateToAbsolute(ref2);

        start = current.getSourceAnchor().getLocation(ref1).getCopy();
        end = current.getTargetAnchor().getLocation(ref2).getCopy();

        current.translateToRelative(start);
        current.translateToRelative(end);
        points.setPoint(start, 0);
        points.setPoint(end, points.size() - 1);

        current.setPoints(points);
      }
      ignoreInvalidate = false;
    }
  }
 private Rectangle getSourceAnchorRelativeBounds(final Connection conn) {
   final Rectangle bounds = conn.getSourceAnchor().getOwner().getBounds().getCopy();
   conn.getSourceAnchor().getOwner().translateToAbsolute(bounds);
   conn.translateToRelative(bounds);
   return bounds;
 }
  /**
   * Computes clipping rectangle(s) for a given connection. Will consider all enclosing viewports,
   * excluding the root viewport.
   */
  protected Rectangle[] getEdgeClippingRectangle(Connection connection) {
    // start with clipping the connection at its original bounds
    Rectangle clipRect = getAbsoluteBoundsAsCopy(connection);

    // in case we cannot infer source and target of the connection (e.g.
    // if XYAnchors are used), returning the bounds is all we can do
    ConnectionAnchor sourceAnchor = connection.getSourceAnchor();
    ConnectionAnchor targetAnchor = connection.getTargetAnchor();
    if (sourceAnchor == null
        || sourceAnchor.getOwner() == null
        || targetAnchor == null
        || targetAnchor.getOwner() == null) {
      return new Rectangle[] {clipRect};
    }

    // source and target figure are known, see if there is common
    // viewport
    // the connection has to be clipped at.
    IFigure sourceFigure = sourceAnchor.getOwner();
    IFigure targetFigure = targetAnchor.getOwner();
    Viewport nearestEnclosingCommonViewport =
        ViewportUtilities.getNearestCommonViewport(sourceFigure, targetFigure);
    if (nearestEnclosingCommonViewport == null) {
      return new Rectangle[] {clipRect};
    }

    // if the nearest common viewport is not the root viewport, we may
    // start with clipping the connection at this viewport.
    if (nearestEnclosingCommonViewport != getRootViewport()) {
      clipRect.intersect(getNodeClippingRectangle(nearestEnclosingCommonViewport));
    }

    // if the nearest common viewport of source and target is not
    // simultaneously
    // the nearest enclosing viewport of source and target respectively, the
    // connection has to be further clipped (the connection may even not be
    // visible at all)
    Viewport nearestEnclosingSourceViewport =
        ViewportUtilities.getNearestEnclosingViewport(sourceFigure);
    Viewport nearestEnclosingTargetViewport =
        ViewportUtilities.getNearestEnclosingViewport(targetFigure);
    if (nearestEnclosingSourceViewport != nearestEnclosingTargetViewport) {
      // compute if source and target anchor are visible
      // within the nearest common enclosing viewport (which may
      // itself be nested in other viewports).
      Rectangle sourceClipRect = getAbsoluteBoundsAsCopy(connection);
      if (nearestEnclosingSourceViewport != nearestEnclosingCommonViewport) {
        clipAtViewports(
            sourceClipRect,
            ViewportUtilities.getViewportsPath(
                nearestEnclosingSourceViewport, nearestEnclosingCommonViewport, false));
      }
      Rectangle targetClipRect = getAbsoluteBoundsAsCopy(connection);
      if (nearestEnclosingTargetViewport != nearestEnclosingCommonViewport) {
        clipAtViewports(
            targetClipRect,
            ViewportUtilities.getViewportsPath(
                nearestEnclosingTargetViewport, nearestEnclosingCommonViewport, false));
      }
      PointList absolutePointsAsCopy = getAbsolutePointsAsCopy(connection);
      boolean sourceAnchorVisible =
          sourceClipRect.getExpanded(PRIVATE_INSETS).contains(absolutePointsAsCopy.getFirstPoint());
      boolean targetAnchorVisible =
          targetClipRect.getExpanded(PRIVATE_INSETS).contains(absolutePointsAsCopy.getLastPoint());

      if (!sourceAnchorVisible || !targetAnchorVisible) {
        // one (or both) of source or target anchor is invisible
        // within the nearest common viewport, so up to now
        // we regard the edge as invisible.
        return new Rectangle[] {};
        // TODO: We could come up with a more decent strategy here,
        // which also computes clipping fragments in those cases
        // where source/target are not visible but the edge
        // intersects with the enclosing source/target viewport's
        // parents bounds.

      } else {
        // both ends are visible, so just return what we have
        // computed before
        // (clipping at nearest enclosing viewport)
        return new Rectangle[] {clipRect};
      }
    } else {
      // source and target share the same enclosing viewport, so just
      // return what we have computed before (clipping at nearest
      // enclosing viewport)
      return new Rectangle[] {clipRect};
    }
  }