class ExceptionDestComparator<T> implements Comparator<ExceptionDest<T>> {
    private final DotNamer<T> namer;

    ExceptionDestComparator(DotNamer<T> namer) {
      this.namer = namer;
    }

    private int getValue(ExceptionDest<T> o) {
      T handler = o.getHandlerNode();
      if (handler == null) {
        return Integer.MAX_VALUE;
      } else {
        return namer.getNumber(handler);
      }
    }

    @Override
    public int compare(ExceptionDest<T> o1, ExceptionDest<T> o2) {
      return (getValue(o1) - getValue(o2));
    }

    public boolean equal(ExceptionDest<T> o1, ExceptionDest<T> o2) {
      return (getValue(o1) == getValue(o2));
    }
  }

  /**
   * Create a <code>DotGraph</code> whose nodes and edges depict a control flow graph without distinguished exceptional
   * edges.
   *
   * @param graph
   *          a <code>DirectedGraph</code> representing a CFG (probably an instance of {@link UnitGraph}, {@link BlockGraph},
   *          or one of their subclasses).
   *
   * @param body
   *          the <code>Body</code> represented by <code>graph</code> (used to format the text within nodes). If no body is
   *          available, pass <code>null</code>.
   *
   * @return a visualization of <code>graph</code>.
   */
  public <N> DotGraph drawCFG(DirectedGraph<N> graph, Body body) {
    DotGraph canvas = initDotGraph(body);
    DotNamer<N> namer = new DotNamer<N>((int) (graph.size() / 0.7f), 0.7f);
    NodeComparator<N> comparator = new NodeComparator<N>(namer);

    // To facilitate comparisons between different graphs of the same
    // method, prelabel the nodes in the order they appear
    // in the iterator, rather than the order that they appear in the
    // graph traversal (so that corresponding nodes are more likely
    // to have the same label in different graphs of a given method).
    for (N node : graph) {
      namer.getName(node);
    }
    for (N node : graph) {
      canvas.drawNode(namer.getName(node));
      for (Iterator<N> succsIt = sortedIterator(graph.getSuccsOf(node), comparator); succsIt.hasNext();) {
        N succ = succsIt.next();
        canvas.drawEdge(namer.getName(node), namer.getName(succ));
      }
    }
    setStyle(graph.getHeads(), canvas, namer, DotGraphConstants.NODE_STYLE_FILLED, headAttr);
    setStyle(graph.getTails(), canvas, namer, DotGraphConstants.NODE_STYLE_FILLED, tailAttr);
    if (!isBrief) {
      formatNodeText(body, canvas, namer);
    }

    return canvas;
  }

  /**
   * Create a <code>DotGraph</code> whose nodes and edges depict the control flow in a <code>ExceptionalGraph</code>, with
   * distinguished edges for exceptional control flow.
   *
   * @param graph
   *          the control flow graph
   *
   * @return a visualization of <code>graph</code>.
   */
  public <N> DotGraph drawCFG(ExceptionalGraph<N> graph) {<FILL_FUNCTION_BODY>

    Body body = graph.getBody();
    DotGraph canvas = initDotGraph(body);
    DotNamer<Object> namer = new DotNamer<Object>((int) (graph.size() / 0.7f), 0.7f);
    @SuppressWarnings("unchecked")
    NodeComparator<N> nodeComparator = new NodeComparator<N>((DotNamer<N>) namer);

    // Prelabel nodes in iterator order, to facilitate
    // comparisons between graphs of a given method.
    for (N node : graph) {
      namer.getName(node);
    }

    for (N node : graph) {
      canvas.drawNode(namer.getName(node));

      for (Iterator<N> succsIt = sortedIterator(graph.getUnexceptionalSuccsOf(node), nodeComparator); succsIt.hasNext();) {
        N succ = succsIt.next();
        DotGraphEdge edge = canvas.drawEdge(namer.getName(node), namer.getName(succ));
        edge.setAttribute(unexceptionalControlFlowAttr);
      }

      for (Iterator<N> succsIt = sortedIterator(graph.getExceptionalSuccsOf(node), nodeComparator); succsIt.hasNext();) {
        N succ = succsIt.next();
        DotGraphEdge edge = canvas.drawEdge(namer.getName(node), namer.getName(succ));
        edge.setAttribute(exceptionalControlFlowAttr);
      }

      if (showExceptions) {
        Collection<? extends ExceptionDest<N>> exDests = graph.getExceptionDests(node);
        if (exDests != null) {
          @SuppressWarnings("unchecked")
          ExceptionDestComparator<N> comp = new ExceptionDestComparator<>((DotNamer<N>) namer);
          for (Iterator<? extends ExceptionDest<N>> destsIt = sortedIterator(exDests, comp); destsIt.hasNext();) {
            ExceptionDest<N> dest = destsIt.next();
            Object handlerStart = dest.getHandlerNode();
            if (handlerStart == null) {
              // Giving each escaping exception its own, invisible
              // exceptional exit node produces a less cluttered
              // graph.
              handlerStart = new Object() {
                @Override
                public String toString() {
                  return "Esc";
                }
              };
              DotGraphNode escapeNode = canvas.drawNode(namer.getName(handlerStart));
              escapeNode.setStyle(DotGraphConstants.NODE_STYLE_INVISIBLE);
            }
            DotGraphEdge edge = canvas.drawEdge(namer.getName(node), namer.getName(handlerStart));
            edge.setAttribute(exceptionEdgeAttr);
            edge.setLabel(formatThrowableSet(dest.getThrowables()));
          }
        }
      }
    }
    setStyle(graph.getHeads(), canvas, namer, DotGraphConstants.NODE_STYLE_FILLED, headAttr);
    setStyle(graph.getTails(), canvas, namer, DotGraphConstants.NODE_STYLE_FILLED, tailAttr);
    if (!isBrief) {
      formatNodeText(graph.getBody(), canvas, namer);
    }
    return canvas;
  
<no_super_class>