/** @see GraphListener#edgeRemoved(GraphEdgeChangeEvent) */
    public void edgeRemoved(GraphEdgeChangeEvent<V, E> e) {
      E jtEdge = e.getEdge();

      if (!jtElementsBeingRemoved.remove(jtEdge)) {
        handleJGraphTRemovedEdge(jtEdge);
      }
    }
 /** @see GraphListener#edgeRemoved(GraphEdgeChangeEvent) */
 public void edgeRemoved(GraphEdgeChangeEvent<V, E> e) {
   E edge = e.getEdge();
   V source = graph.getEdgeSource(edge);
   V target = graph.getEdgeTarget(edge);
   if (successorMap.containsKey(source)) {
     successorMap.get(source).removeNeighbor(target);
   }
   if (predecessorMap.containsKey(target)) {
     predecessorMap.get(target).removeNeighbor(source);
   }
 }
  /** @see GraphListener#edgeAdded(GraphEdgeChangeEvent) */
  public void edgeAdded(GraphEdgeChangeEvent<V, E> e) {
    E edge = e.getEdge();
    V source = graph.getEdgeSource(edge);
    V target = graph.getEdgeTarget(edge);

    // if a map does not already contain an entry,
    // then skip addNeighbor, since instantiating the map
    // will take care of processing the edge (which has already
    // been added)

    if (successorMap.containsKey(source)) {
      getSuccessors(source).addNeighbor(target);
    } else {
      getSuccessors(source);
    }
    if (predecessorMap.containsKey(target)) {
      getPredecessors(target).addNeighbor(source);
    } else {
      getPredecessors(target);
    }
  }
    @Override
    public void edgeRemoved(final GraphEdgeChangeEvent<Spot, DefaultWeightedEdge> event) {
      // To signal to ModelChangeListeners
      edgesRemoved.add(event.getEdge());

      // To maintain connected sets coherence

      final DefaultWeightedEdge e = event.getEdge();
      final Integer id = edgeToID.get(e);
      if (null == id) {
        throw new RuntimeException("Edge is unkown to this model: " + e);
      }
      final Set<DefaultWeightedEdge> set = connectedEdgeSets.get(id);
      if (null == set) {
        throw new RuntimeException("Unknown set ID: " + id);
      }

      // Remove edge from set.
      final boolean removed = set.remove(e);
      if (!removed) {
        throw new RuntimeException("Could not removed edge " + e + " from set with ID: " + id);
      }
      // Forget about edge.
      edgeToID.remove(e);

      /*
       * Ok the trouble is that now we might be left with 2 sets if the
       * edge "was in the middle". Or 1 if it was in the end. Or 0 if it
       * was the last edge of the set.
       */

      if (set.size() == 0) {
        // The set is empty, remove it from the map.
        connectedEdgeSets.remove(id);
        names.remove(id);
        visibility.remove(id);
        /* We need to remove also the vertices */
        final Set<Spot> vertexSet = connectedVertexSets.get(id);
        // Forget the vertices were in a set
        for (final Spot spot : vertexSet) {
          vertexToID.remove(spot);
        }
        // Forget the vertex set
        connectedVertexSets.remove(id);
        /*
         * We do not mark it as a track to update, for it disappeared.
         * On the other hand, it might *have been* marked as a track to
         * update. Since it just joined oblivion, we remove it from the
         * list of tracks to update.
         */
        tracksUpdated.remove(id);

      } else {
        // So there are some edges remaining in the set.
        // Look at the connected component of its source and target.
        // Source
        final HashSet<Spot> sourceVCS = new HashSet<Spot>();
        final HashSet<DefaultWeightedEdge> sourceECS = new HashSet<DefaultWeightedEdge>();
        {
          final Spot source = graph.getEdgeSource(e);
          // Get its connected set
          final BreadthFirstIterator<Spot, DefaultWeightedEdge> i =
              new BreadthFirstIterator<Spot, DefaultWeightedEdge>(graph, source);
          while (i.hasNext()) {
            final Spot sv = i.next();
            sourceVCS.add(sv);
            sourceECS.addAll(graph.edgesOf(sv));
          }
        }
        // Target
        final HashSet<Spot> targetVCS = new HashSet<Spot>();
        final HashSet<DefaultWeightedEdge> targetECS = new HashSet<DefaultWeightedEdge>();
        {
          final Spot target = graph.getEdgeTarget(e);
          // Get its connected set
          final BreadthFirstIterator<Spot, DefaultWeightedEdge> i =
              new BreadthFirstIterator<Spot, DefaultWeightedEdge>(graph, target);
          while (i.hasNext()) {
            final Spot sv = i.next();
            targetVCS.add(sv);
            targetECS.addAll(graph.edgesOf(sv));
          }
        }

        /*
         * If the two connected components are the same, it means that
         * the edge was an "internal" edge: Because there is another
         * path that connect its source and target, removing it did NOT
         * split the track in 2. We therefore need not to re-attribute
         * it.
         */
        if (targetVCS.equals(sourceVCS)) {
          tracksUpdated.add(id);
          connectedEdgeSets.get(id).remove(e);
          return;
        }

        /*
         * Re-attribute the found connected sets to the model. The
         * largest one (in vertices) gets the original id, the other
         * gets a new id. As for names: the largest one keeps its name,
         * the small one gets a new name.
         */

        if (targetVCS.size() > sourceVCS.size()) {

          connectedEdgeSets.put(id, targetECS);
          connectedVertexSets.put(id, targetVCS); // they already
          // have the
          // right id in
          // #vertexToId
          tracksUpdated.add(id); // old track has changed

          if (sourceECS.size() > 0) {
            // the smaller part is still a track
            final int newid = IDcounter++;
            connectedEdgeSets.put(newid, sourceECS); // otherwise
            // forget it
            for (final DefaultWeightedEdge te : sourceECS) {
              edgeToID.put(te, newid);
            }
            connectedVertexSets.put(newid, sourceVCS);
            for (final Spot tv : sourceVCS) {
              vertexToID.put(tv, newid);
            }
            final Boolean targetVisibility = visibility.get(id);
            visibility.put(newid, targetVisibility);
            names.put(newid, nameGenerator.next());
            // Transaction: both children tracks are marked for
            // update.
            tracksUpdated.add(newid);

          } else {
            /*
             * Nothing remains from the smallest part. The remaining
             * solitary vertex has no right to be called a track.
             */
            final Spot solitary = sourceVCS.iterator().next();
            vertexToID.remove(solitary);
          }

        } else {

          if (sourceECS.size() > 0) {
            // There are still some pieces left. It is worth noting
            // it.
            connectedEdgeSets.put(id, sourceECS);
            connectedVertexSets.put(id, sourceVCS);
            tracksUpdated.add(id);

            if (targetECS.size() > 0) {
              // the small part is still a track
              final int newid = IDcounter++;
              connectedEdgeSets.put(newid, targetECS);
              for (final DefaultWeightedEdge te : targetECS) {
                edgeToID.put(te, newid);
              }
              connectedVertexSets.put(newid, targetVCS);
              for (final Spot v : targetVCS) {
                vertexToID.put(v, newid);
              }
              final Boolean targetVisibility = visibility.get(id);
              visibility.put(newid, targetVisibility);
              names.put(newid, nameGenerator.next());
              // Transaction: both children tracks are marked for
              // update.
              tracksUpdated.add(newid);
            } else {
              /*
               * Nothing remains from the smallest part. The
               * remaining solitary vertex has no right to be
               * called a track.
               */
              final Spot solitary = targetVCS.iterator().next();
              vertexToID.remove(solitary);
            }

          } else {
            // Nothing remains (maybe a solitary vertex) -> forget
            // about it all.
            connectedEdgeSets.remove(id);
            connectedVertexSets.remove(id);
            names.remove(id);
            visibility.remove(id);
            tracksUpdated.remove(id);
          }
        }
      }
    }
    @Override
    public void edgeAdded(final GraphEdgeChangeEvent<Spot, DefaultWeightedEdge> event) {
      // To signal to ModelChangeListener
      edgesAdded.add(event.getEdge());

      // To maintain connected sets coherence:
      /*
       * This is the tricky part: when we add an edge to our set model,
       * first we need to find to what existing set it has been added.
       * Then a new edge sometime come with 1 or 2 vertices that might be
       * new or belonging to an existing set.
       */
      final DefaultWeightedEdge e = event.getEdge();

      // Was it added to known tracks?
      final Spot sv = graph.getEdgeSource(e);
      final Integer sid = vertexToID.get(sv);
      final Spot tv = graph.getEdgeTarget(e);
      final Integer tid = vertexToID.get(tv);

      if (null != tid && null != sid) {
        // Case 1: it was added between two existing sets. We connect
        // them, therefore
        // and take the id of the largest one. The other id, disappear,
        // unless they
        // belonged to the same set.

        // Did they come from the same set?
        if (tid.equals(sid)) {
          // They come from the same set (equals ID). Not much to do.
          final Set<DefaultWeightedEdge> ses = connectedEdgeSets.get(sid);
          ses.add(e);
          edgeToID.put(e, sid);

        } else {
          // They come from different sets.

          // Edges:
          final Set<DefaultWeightedEdge> ses = connectedEdgeSets.get(sid);
          final Set<DefaultWeightedEdge> tes = connectedEdgeSets.get(tid);
          final HashSet<DefaultWeightedEdge> nes =
              new HashSet<DefaultWeightedEdge>(ses.size() + tes.size() + 1);
          nes.addAll(ses);
          nes.addAll(tes);
          nes.add(e);

          // Vertices:
          final Set<Spot> svs = connectedVertexSets.get(sid);
          final Set<Spot> tvs = connectedVertexSets.get(tid);
          final HashSet<Spot> nvs = new HashSet<Spot>(ses.size() + tes.size());
          nvs.addAll(svs);
          nvs.addAll(tvs);

          Integer nid, rid;
          if (nvs.size() > tvs.size()) {
            nid = sid;
            rid = tid;
            for (final Spot v : tvs) {
              // Vertices of target set change id
              vertexToID.put(v, nid);
            }
            for (final DefaultWeightedEdge te : tes) {
              edgeToID.put(te, nid);
            }
          } else {
            nid = tid;
            rid = sid;
            for (final Spot v : svs) {
              // Vertices of source set change id
              vertexToID.put(v, nid);
            }
            for (final DefaultWeightedEdge se : ses) {
              edgeToID.put(se, nid);
            }
          }
          edgeToID.put(e, nid);
          connectedVertexSets.put(nid, nvs);
          connectedVertexSets.remove(rid);
          connectedEdgeSets.put(nid, nes);
          connectedEdgeSets.remove(rid);

          // Transaction: we signal that the large id is to be
          // updated, and forget about the small one
          tracksUpdated.add(nid);
          tracksUpdated.remove(rid);

          // Visibility: if at least one is visible, the new set is
          // made visible.
          final Boolean targetVisibility = visibility.get(sid) || visibility.get(tid);
          visibility.put(nid, targetVisibility);
          visibility.remove(rid);

          // Name: the new set gets the name of the largest one.
          names.remove(rid); // 'nid' already has the right name.
        }

      } else if (null == sid && null == tid) {
        // Case 4: the edge was added between two lonely vertices.
        // Create a new set id from this
        final HashSet<Spot> nvs = new HashSet<Spot>(2);
        nvs.add(graph.getEdgeSource(e));
        nvs.add(graph.getEdgeTarget(e));

        final HashSet<DefaultWeightedEdge> nes = new HashSet<DefaultWeightedEdge>(1);
        nes.add(e);

        final int nid = IDcounter++;
        connectedEdgeSets.put(nid, nes);
        connectedVertexSets.put(nid, nvs);
        vertexToID.put(sv, nid);
        vertexToID.put(tv, nid);
        edgeToID.put(e, nid);

        // Give it visibility
        visibility.put(nid, Boolean.TRUE);
        // and a default name.
        names.put(nid, nameGenerator.next());
        // Transaction: we mark the new track as updated
        tracksUpdated.add(nid);

      } else if (null == sid) {
        // Case 2: the edge was added to the target set. No source set,
        // but there is a source vertex.
        // Add it, with the source vertex, to the target id.
        connectedEdgeSets.get(tid).add(e);
        edgeToID.put(e, tid);
        connectedVertexSets.get(tid).add(sv);
        vertexToID.put(sv, tid);
        // We do not change the visibility, nor the name.
        // Transaction: we mark the mother track as updated
        tracksUpdated.add(tid);

      } else if (null == tid) {
        // Case 3: the edge was added to the source set. No target set,
        // but there is a target vertex.
        // Add it, with the target vertex, to the source id.
        connectedEdgeSets.get(sid).add(e);
        edgeToID.put(e, sid);
        connectedVertexSets.get(sid).add(tv);
        vertexToID.put(tv, sid);
        // We do not change the visibility, nor the name.
        // Transaction: we mark the mother track as updated
        tracksUpdated.add(sid);
      }
    }