All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.graph.CycleDetector Maven / Gradle / Ivy

/*
 * Sonar, open source software quality management tool.
 * Copyright (C) 2009 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * Sonar is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * Sonar is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sonar; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.graph;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class CycleDetector {

  private Set vertices;
  private DirectedGraphAccessor graph;
  private Set analyzedVertices;
  private Set cycles = new HashSet();
  private Set edgesToExclude;
  private long searchCyclesCalls = 0;
  private int maxSearchDepth = -1;
  private boolean maxSearchDepthActivated = false;
  private int maxCyclesToFound = Integer.MAX_VALUE;

  public CycleDetector(DirectedGraphAccessor graph, Collection vertices) {
    init(graph, vertices, new HashSet());
  }

  public CycleDetector(DirectedGraphAccessor graph, Collection vertices, Set edgesToExclude) {
    init(graph, vertices, edgesToExclude);
  }

  public CycleDetector(DirectedGraphAccessor graph) {
    init(graph, graph.getVertices(), new HashSet());
  }

  public CycleDetector(DirectedGraphAccessor graph, Set edgesToExclude) {
    init(graph, graph.getVertices(), edgesToExclude);
  }

  private void init(DirectedGraphAccessor graph, Collection vertices, Set edgesToExclude) {
    this.graph = graph;
    this.vertices = new HashSet(vertices);
    this.analyzedVertices = new HashSet();
    this.edgesToExclude = edgesToExclude;
  }

  public Set detectCycles() {
    run();
    return getCycles();
  }

  public Set detectCyclesWithUpperLimit(int maxCyclesToFound) {
    this.maxCyclesToFound = maxCyclesToFound;
    run();
    return getCycles();
  }

  public Set detectCyclesWithMaxSearchDepth(int maxSearchDepth) {
    if (maxSearchDepth > 1) {
      maxSearchDepthActivated = true;
      this.maxSearchDepth = maxSearchDepth;
    }
    run();
    return getCycles();
  }

  private void run() {
    if (!cycles.isEmpty()) {
      throw new IllegalStateException("Cycle detection can't be executed twice on the same CycleDetector object.");
    }
    try {
      for (V vertex : vertices) {
        if (maxSearchDepthActivated || !analyzedVertices.contains(vertex)) {
          Set tmpAnalyzedVertices = new HashSet();
          searchCycles(vertex, new ArrayList(), tmpAnalyzedVertices);
          analyzedVertices.addAll(tmpAnalyzedVertices);
        }
      }
    } catch (MaximumCyclesToFoundException e) {
    }
  }

  private void searchCycles(V fromVertex, List path, Set tmpAnalyzedVertices) {
    searchCyclesCalls++;
    path.add(fromVertex);
    tmpAnalyzedVertices.add(fromVertex);
    for (Edge edge : graph.getOutgoingEdges(fromVertex)) {
      V toVertex = edge.getTo();
      if (!edgesToExclude.contains(edge) && vertices.contains(toVertex)
          && (maxSearchDepthActivated || !analyzedVertices.contains(toVertex))) {
        if (path.contains(toVertex)) {
          path.add(toVertex);
          List cyclePath = path.subList(path.indexOf(toVertex), path.size());
          Cycle cycle = convertListOfVerticesToCycle(cyclePath);
          cycles.add(cycle);

          if (cycles.size() >= maxCyclesToFound) {
            throw new MaximumCyclesToFoundException();
          }
          path.remove(path.size() - 1);
        } else if (!maxSearchDepthActivated || (maxSearchDepthActivated && path.size() < maxSearchDepth)) {
          searchCycles(toVertex, path, tmpAnalyzedVertices);
        }
      }
    }
    path.remove(path.size() - 1);
  }

  private Cycle convertListOfVerticesToCycle(List vertices) {
    List edges = new ArrayList();
    V firstVertex = vertices.get(0);
    V from = firstVertex;
    for (int index = 1; index < vertices.size(); index++) {
      V to = vertices.get(index);
      edges.add(graph.getEdge(from, to));
      from = to;
    }
    return new Cycle(edges);
  }

  public Set getCycles() {
    return cycles;
  }

  public boolean isAcyclicGraph() {
    return cycles.isEmpty();
  }

  public long getSearchCyclesCalls() {
    return searchCyclesCalls;
  }

}

class MaximumCyclesToFoundException extends RuntimeException {
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy