/**
   * Adds the specified JGraphT edge to be reflected by this graph model. To be called only for
   * edges that already exist in the JGraphT graph.
   *
   * @param jtEdge a JGraphT edge to be reflected by this graph model.
   */
  void handleJGraphTAddedEdge(E jtEdge) {
    DefaultEdge edgeCell = cellFactory.createEdgeCell(jtEdge);
    edgeToCell.put(jtEdge, edgeCell);
    cellToEdge.put(edgeCell, jtEdge);

    ConnectionSet cs = new ConnectionSet();
    cs.connect(
        edgeCell,
        getVertexPort(jtGraph.getEdgeSource(jtEdge)),
        getVertexPort(jtGraph.getEdgeTarget(jtEdge)));

    internalInsertCell(edgeCell, createEdgeAttributeMap(edgeCell), cs);
  }
  /**
   * Adds to the underlying JGraphT graph a vertex corresponding to the specified JGraph vertex. In
   * JGraph, two vertices with the same user object are in principle allowed; in JGraphT, this would
   * lead to duplicate vertices, which is not allowed. So if such vertex already exists, the
   * specified vertex is REMOVED from the JGraph graph and a a warning is printed.
   *
   * <p>This method is to be called only for vertices that have already been added to the JGraph
   * graph.
   *
   * @param jVertex the JGraph vertex that has been added.
   */
  @SuppressWarnings("unchecked")
  void handleJGraphInsertedVertex(GraphCell jVertex) {
    V jtVertex;

    if (jVertex instanceof DefaultGraphCell) {
      // FIXME hb 28-nov-05: waiting for jgraph to go generic
      jtVertex = (V) ((DefaultGraphCell) jVertex).getUserObject();
    } else {
      // FIXME: Why toString? Explain if for a good reason otherwise fix.
      jtVertex = (V) jVertex.toString();
    }

    if (vertexToCell.containsKey(jtVertex)) {
      // We have to remove the new vertex, because it would lead to
      // duplicate vertices. We can't use ShieldedGraph.removeVertex for
      // that, because it would remove the wrong (existing) vertex.
      System.err.println(
          "Warning: detected two JGraph vertices with "
              + "the same JGraphT vertex as user object. It is an "
              + "indication for a faulty situation that should NOT happen."
              + "Removing vertex: "
              + jVertex);
      internalRemoveCell(jVertex);
    } else {
      jtGraph.addVertex(jtVertex);

      cellToVertex.put(jVertex, jtVertex);
      vertexToCell.put(jtVertex, jVertex);
    }
  }
  /**
   * Adds to the underlying JGraphT graph an edge that corresponds to the specified JGraph edge. If
   * the specified JGraph edge is a dangling edge, it is NOT added to the underlying JGraphT graph.
   *
   * <p>This method is to be called only for edges that have already been added to the JGraph graph.
   *
   * @param jEdge the JGraph edge that has been added.
   */
  void handleJGraphInsertedEdge(org.jgraph.graph.Edge jEdge) {
    if (isDangling(jEdge)) {
      // JGraphT forbid dangling edges so we cannot add the edge yet. If
      // later the edge becomes connected, we will add it.
    } else {
      // FIXME hb 28-nov-05: waiting for jgraph to go generic
      Object jSource = getSourceVertex(this, jEdge);
      Object jTarget = getTargetVertex(this, jEdge);

      V jtSource = cellToVertex.get(jSource);
      V jtTarget = cellToVertex.get(jTarget);

      E jtEdge = jtGraph.addEdge(jtSource, jtTarget);

      if (jtEdge != null) {
        cellToEdge.put(jEdge, jtEdge);
        edgeToCell.put(jtEdge, jEdge);
      } else {
        // Adding failed because user is using a JGraphT graph the
        // forbids parallel edges.
        // For consistency, we remove the edge from the JGraph too.
        internalRemoveCell(jEdge);
        System.err.println(
            "Warning: an edge was deleted because the underlying "
                + "JGraphT graph refused to create it. "
                + "This situation can happen when a constraint of the "
                + "underlying graph is violated, e.g., an attempt to add "
                + "a parallel edge or a self-loop to a graph that forbids "
                + "them. To avoid this message, make sure to use a "
                + "suitable underlying JGraphT graph.");
      }
    }
  }
  /**
   * Removes the edge corresponding to the specified JGraph edge from the JGraphT graph. If the
   * specified edge is not contained in {@link #cellToEdge}, it is silently ignored.
   *
   * <p>This method is to be called only for edges that have already been removed from the JGraph
   * graph.
   *
   * @param jEdge the JGraph edge that has been removed.
   */
  void handleJGraphRemovedEdge(org.jgraph.graph.Edge jEdge) {
    if (cellToEdge.containsKey(jEdge)) {
      E jtEdge = cellToEdge.get(jEdge);

      jtGraph.removeEdge(jtEdge);

      cellToEdge.remove(jEdge);
      edgeToCell.remove(jtEdge);
    }
  }
  /**
   * Adds/removes an edge to/from the underlying JGraphT graph according to the change in the
   * specified JGraph edge. If both vertices are connected, we ensure to have a corresponding
   * JGraphT edge. Otherwise, we ensure NOT to have a corresponding JGraphT edge.
   *
   * <p>This method is to be called only for edges that have already been changed in the JGraph
   * graph.
   *
   * @param jEdge the JGraph edge that has changed.
   */
  void handleJGraphChangedEdge(org.jgraph.graph.Edge jEdge) {
    if (isDangling(jEdge)) {
      if (cellToEdge.containsKey(jEdge)) {
        // a non-dangling edge became dangling -- remove the JGraphT
        // edge by faking as if the edge is removed from the JGraph.
        // TODO: Consider keeping the JGraphT edges outside the graph
        // to avoid loosing user data, such as weights.
        handleJGraphRemovedEdge(jEdge);
      } else {
        // a dangling edge is still dangling -- just ignore.
      }
    } else {
      // edge is not dangling
      if (cellToEdge.containsKey(jEdge)) {
        // edge already has a corresponding JGraphT edge.
        // check if any change to its endpoints.
        E jtEdge = cellToEdge.get(jEdge);

        Object jSource = getSourceVertex(this, jEdge);
        Object jTarget = getTargetVertex(this, jEdge);

        Object jtSource = cellToVertex.get(jSource);
        Object jtTarget = cellToVertex.get(jTarget);

        if ((jtGraph.getEdgeSource(jtEdge) == jtSource)
            && (jtGraph.getEdgeTarget(jtEdge) == jtTarget)) {
          // no change in edge's endpoints -- nothing to do.
        } else {
          // edge's end-points have changed -- need to refresh the
          // JGraphT edge. Refresh by faking as if the edge has been
          // removed from JGraph and then added again.
          // ALSO HERE: consider an alternative that maintains user
          // data
          handleJGraphRemovedEdge(jEdge);
          handleJGraphInsertedEdge(jEdge);
        }
      } else {
        // a new edge
        handleJGraphInsertedEdge(jEdge);
      }
    }
  }
  /**
   * Removes the vertex corresponding to the specified JGraph vertex from the JGraphT graph. If the
   * specified vertex is not contained in {@link #cellToVertex}, it is silently ignored.
   *
   * <p>If any edges are incident with this vertex, we first remove them from the both graphs,
   * because otherwise the JGraph graph would leave them intact and the JGraphT graph would throw
   * them out. TODO: Revise this behavior now that we gracefully tolerate dangling edges. It might
   * be possible to remove just the JGraphT edges. The JGraph edges will be left dangling, as a
   * result.
   *
   * <p>This method is to be called only for vertices that have already been removed from the JGraph
   * graph.
   *
   * @param jVertex the JGraph vertex that has been removed.
   */
  void handleJGraphRemovedVertex(GraphCell jVertex) {
    if (cellToVertex.containsKey(jVertex)) {
      V jtVertex = cellToVertex.get(jVertex);
      Set<E> jtIncidentEdges = jtGraph.edgesOf(jtVertex);

      if (!jtIncidentEdges.isEmpty()) {
        // We can't just call removeAllEdges with this list: that
        // would throw a ConcurrentModificationException. So we create
        // a shallow copy.
        // This also triggers removal of the corresponding JGraph
        // edges.
        jtGraph.removeAllEdges(new ArrayList<E>(jtIncidentEdges));
      }

      jtGraph.removeVertex(jtVertex);

      cellToVertex.remove(jVertex);
      vertexToCell.remove(jtVertex);
    }
  }