de.spricom.dessert.util.Dag Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dessert-core Show documentation
Show all versions of dessert-core Show documentation
A library for unit-tests to check the dependencies between classes.
The newest version!
package de.spricom.dessert.util;
/*-
* #%L
* Dessert Dependency Assertion Library for Java
* %%
* Copyright (C) 2017 - 2023 Hans Jörg Heßmann
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.*;
/**
* This class implements the Depth-first search algorithm (see
* https://en.wikipedia.org/wiki/Topological_sorting)
* to detect illegal cycles on a directed acyclic graph.
*
* @param The node type.
*/
public final class Dag {
enum Mark {
NONE, TEMPORARY, PERMANENT
}
static final class Node {
final T value;
final Set> edges = new HashSet>();
Mark mark = Mark.NONE;
public Node(T value) {
assert value != null : "value == null";
this.value = value;
}
@Override
public int hashCode() {
return value.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("unchecked")
Node other = (Node) obj;
return value.equals(other.value);
}
}
private final Map> nodes = new HashMap>();
private LinkedList> sorted;
private LinkedList> cycle;
public void addEdge(T from, T to) {
sorted = null;
getNode(from).edges.add(getNode(to));
}
private Node getNode(T value) {
Node n = nodes.get(value);
if (n == null) {
n = new Node(value);
nodes.put(value, n);
}
return n;
}
public boolean isCycleFree() {
if (sorted == null) {
sort();
}
return cycle == null;
}
public List cycle() {
return values(cycle);
}
private List values(List> nodes) {
List list = new ArrayList(nodes.size());
for (Node node : nodes) {
list.add(node.value);
}
return list;
}
private void sort() {
sorted = new LinkedList>();
for (Node n : nodes.values()) {
if (visit(n)) {
return;
}
}
}
private boolean visit(Node n) {
if (n.mark == Mark.PERMANENT) {
return false;
} else if (n.mark == Mark.TEMPORARY) {
cycle = new LinkedList>();
cycle.add(n);
return true;
} else {
n.mark = Mark.TEMPORARY;
for (Node m : n.edges) {
if (visit(m)) {
cycle.addFirst(n);
return true;
}
}
n.mark = Mark.PERMANENT;
sorted.addFirst(n);
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy