DIKit.java
package space.sunqian.fs.di;
import space.sunqian.annotation.Immutable;
import space.sunqian.annotation.Nonnull;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Dependency injection utilities.
*
* @author sunqian
*/
public class DIKit {
/**
* Checks whether the cycle dependencies exist in the given component.
*
* @param component the component to check
* @throws DIException if the cycle dependencies exist
*/
public static <T> void checkCycleDependencies(
@Nonnull T component,
@Nonnull DependenciesFunction<@Nonnull T> dependenciesFunction
) throws DIException {
if (dependenciesFunction.dependencies(component).isEmpty()) {
return;
}
Set<T> stack = new LinkedHashSet<>();
stack.add(component);
checkCycleDependencies(component, stack, dependenciesFunction);
}
private static <T> void checkCycleDependencies(
@Nonnull T component,
@Nonnull Set<@Nonnull T> stack,
@Nonnull DependenciesFunction<@Nonnull T> dependenciesFunction
) throws DIException {
if (dependenciesFunction.dependencies(component).isEmpty()) {
return;
}
for (T dependency : dependenciesFunction.dependencies(component)) {
if (dependency.equals(component)) {
continue;
}
if (stack.contains(dependency)) {
throw new DIException("Cycle dependency: " + toCycleString(dependency, stack) + ".");
}
stack.add(dependency);
checkCycleDependencies(dependency, stack, dependenciesFunction);
stack.remove(dependency);
}
}
private static @Nonnull String toCycleString(
@Nonnull Object component,
@Nonnull Set<?> stack
) {
return stack.stream()
.map(Object::toString)
.collect(Collectors.joining(" -> "))
+ " -> " + component;
}
/**
* Represents function to get dependencies of a component.
*
* @param <T> the type of the component
*/
public interface DependenciesFunction<T> {
/**
* Returns the dependencies of the given component.
*
* @param component the component to get dependencies
* @return the dependencies of the given component
*/
@Nonnull
@Immutable
List<? extends @Nonnull T> dependencies(@Nonnull T component);
}
private DIKit() {
}
}