class CycleDetector {

  static void detect(List<Component<?>> components) {
    Set<ComponentNode> graph = toGraph(components);
    Set<ComponentNode> roots = getRoots(graph);

    int numVisited = 0;
    while (!roots.isEmpty()) {
      ComponentNode node = roots.iterator().next();
      roots.remove(node);
      numVisited++;

      for (ComponentNode dependent : node.getDependencies()) {
        dependent.removeDependent(node);
        if (dependent.isRoot()) {
          roots.add(dependent);
        }
      }
    }

    // If there is no dependency cycle in the graph, the number of visited nodes will be equal to
    // the original list.
    if (numVisited == components.size()) {
      return;
    }

    // Otherwise there is a cycle.
    List<Component<?>> componentsInCycle = new ArrayList<>();
    for (ComponentNode node : graph) {
      if (!node.isRoot() && !node.isLeaf()) {
        componentsInCycle.add(node.getComponent());
      }
    }

    throw new DependencyCycleException(componentsInCycle);
  }

}