org.molgenis.data.util.GenericDependencyResolver Maven / Gradle / Ivy
package org.molgenis.data.util;
import static com.google.common.collect.ImmutableSet.copyOf;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.Collections.singleton;
import static java.util.stream.Collectors.toSet;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.molgenis.data.MolgenisDataException;
import org.springframework.stereotype.Component;
@Component
public class GenericDependencyResolver {
public List resolve(Collection items, Function> getDependencies) {
List result = newArrayList();
Set alreadyResolved = newHashSet();
Set stillToResolve = newHashSet(items);
while (!stillToResolve.isEmpty()) {
List newlyResolved =
stillToResolve.stream()
.filter(item -> alreadyResolved.containsAll(getDependencies.apply(item)))
.collect(Collectors.toList());
if (newlyResolved.isEmpty()) {
throw new MolgenisDataException(
"Could not resolve dependencies of items "
+ stillToResolve
+ ". Are there circular dependencies?");
}
alreadyResolved.addAll(newlyResolved);
stillToResolve.removeAll(newlyResolved);
result.addAll(newlyResolved);
}
return result;
}
/**
* Retrieves all items that depend on a given item.
*
* @param item the item that the other items depend on
* @param getDepth function that returns the depth up to which a specific item's dependencies are
* resolved
* @param getDependants function that returns the items that depend on a specific item
* @param the type of the item
* @return Set of items that directly or indirectly depend on the given item
*/
public Set getAllDependants(
A item, Function getDepth, Function> getDependants) {
Set currentGeneration = singleton(item);
Set result = newHashSet();
Set visited = newHashSet();
for (int depth = 0; !currentGeneration.isEmpty(); depth++) {
currentGeneration =
copyOf(difference(getDirectDependants(currentGeneration, getDependants), visited));
result.addAll(
currentGeneration.stream().filter(getDepthFilter(depth, getDepth)).collect(toSet()));
visited.addAll(currentGeneration);
}
return result;
}
private Set getDirectDependants(Set items, Function> getDependants) {
return items.stream().flatMap(item -> getDependants.apply(item).stream()).collect(toSet());
}
private Predicate getDepthFilter(int depth, Function getDepth) {
return item -> getDepth.apply(item) > depth;
}
}