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