Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
top.hendrixshen.magiclib.dependency.impl.Dependencies Maven / Gradle / Ivy
package top.hendrixshen.magiclib.dependency.impl;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.spongepowered.asm.service.MixinService;
import org.spongepowered.asm.util.Annotations;
import top.hendrixshen.magiclib.MagicLibReference;
import top.hendrixshen.magiclib.dependency.api.DepCheckException;
import top.hendrixshen.magiclib.dependency.api.DepCheckFailureCallback;
import top.hendrixshen.magiclib.dependency.api.Predicate;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Dependencies {
public static final String SATISFIED = "Satisfied!";
public final List andRequirements;
public final List conflicts;
public final List orRequirements;
public final boolean andRequirementsSatisfied;
public final boolean orRequirementsSatisfied;
public final boolean noConflicts;
public Predicate> predicate;
private Dependencies(List andRequirements, List orRequirements, List conflicts, Predicate> predicate) {
this.andRequirements = andRequirements;
this.orRequirements = orRequirements;
this.conflicts = conflicts;
this.andRequirementsSatisfied = this.andRequirements.isEmpty() || this.andRequirements.stream().allMatch(modPredicate -> modPredicate.satisfied);
this.orRequirementsSatisfied = this.orRequirements.isEmpty() || this.orRequirements.stream().anyMatch(modPredicate -> modPredicate.satisfied);
this.noConflicts = this.conflicts.isEmpty() || this.conflicts.stream().noneMatch(modPredicate -> modPredicate.satisfied);
this.predicate = predicate;
}
private Dependencies(top.hendrixshen.magiclib.dependency.api.annotation.@NotNull Dependencies dependencies, Class clazz) {
this(generateRequirement(dependencies.and()), generateRequirement(dependencies.or()), generateRequirement(dependencies.not()),
new top.hendrixshen.magiclib.dependency.api.annotation.Dependencies.DefaultPredicate());
try {
this.predicate = dependencies.predicate().getDeclaredConstructor().newInstance();
// Ensure that the input type is correct.
if (!(this.predicate instanceof top.hendrixshen.magiclib.dependency.api.annotation.Dependencies.DefaultPredicate)) {
this.predicate.getClass().getMethod("isSatisfied", clazz);
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
MagicLibReference.getLogger().throwing(e);
}
}
@SuppressWarnings("unchecked")
private static boolean testHelper(@NotNull Predicate l, Object obj) {
return l.isSatisfied((T) obj);
}
@Contract("_, _ -> new")
public static @NotNull Dependencies of(top.hendrixshen.magiclib.dependency.api.annotation.Dependencies dependencies, Class clazz) {
return new Dependencies<>(dependencies, clazz);
}
@Nullable
public static Dependencies getFabricEntrypointDependencies(String entrypointClassName, String methodName) {
@Nullable
AnnotationNode dependencyAnnotation = null;
try {
ClassNode classNode = MixinService.getService().getBytecodeProvider().getClassNode(entrypointClassName);
for (MethodNode methodNode : classNode.methods) {
if (methodNode.name.equals(methodName) && methodNode.desc.equals("()V")) {
dependencyAnnotation = Annotations.getVisible(methodNode, top.hendrixshen.magiclib.dependency.api.annotation.Dependencies.class);
break;
}
}
} catch (ClassNotFoundException | IOException e) {
return null;
}
if (dependencyAnnotation == null) {
return null;
}
return of(dependencyAnnotation, Object.class);
}
@Nullable
public static Dependencies getMixinClassDependencies(String mixinClassName) {
AnnotationNode dependencyAnnotation;
try {
ClassNode classNode = MixinService.getService().getBytecodeProvider().getClassNode(mixinClassName);
dependencyAnnotation = Annotations.getVisible(classNode, top.hendrixshen.magiclib.dependency.api.annotation.Dependencies.class);
} catch (ClassNotFoundException | IOException e) {
throw new RuntimeException(e);
}
if (dependencyAnnotation == null) {
return null;
}
return of(dependencyAnnotation, ClassNode.class);
}
@Contract("_, _ -> new")
public static @NotNull Dependencies of(AnnotationNode dependencyAnnotation, Class clazz) {
List andDependencies = getDependencyList(dependencyAnnotation, "and");
List orDependencies = getDependencyList(dependencyAnnotation, "or");
List notDependencies = getDependencyList(dependencyAnnotation, "not");
Type predicateType = Annotations.getValue(dependencyAnnotation, "predicate");
Predicate> predicate;
if (predicateType == null) {
predicate = new top.hendrixshen.magiclib.dependency.api.annotation.Dependencies.DefaultPredicate();
} else {
try {
predicate = (Predicate>) Class.forName(predicateType.getClassName()).getDeclaredConstructor().newInstance();
// Ensure that the input type is correct.
predicate.getClass().getMethod("isSatisfied", clazz);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
return new Dependencies<>(andDependencies, orDependencies, notDependencies, predicate);
}
private static List generateRequirement(top.hendrixshen.magiclib.dependency.api.annotation.Dependency[] dependencies) {
return Arrays.stream(dependencies).map(Dependency::of).collect(Collectors.toList());
}
private static ClassNode loadClassNode(String className) throws IOException, ClassNotFoundException {
return MixinService.getService().getBytecodeProvider().getClassNode(className);
}
private static @NotNull List getDependencyList(AnnotationNode annotationNode, String key) {
ArrayList ret = new ArrayList<>();
List dependenciesNode = Annotations.getValue(annotationNode, key, true);
dependenciesNode.forEach(node -> ret.add(Dependency.of(node)));
return ret;
}
public static boolean checkDependency(String targetClassName, String mixinClassName, DepCheckFailureCallback depCheckFailureCallback) {
ClassNode targetClassNode = null;
try {
targetClassNode = loadClassNode(targetClassName);
} catch (IOException | ClassNotFoundException ignored) {
}
Dependencies dependencies = getMixinClassDependencies(mixinClassName);
if (dependencies == null) {
return true;
}
String result = dependencies.getCheckResult(targetClassNode);
if (!result.equals(Dependencies.SATISFIED)) {
depCheckFailureCallback.callback(targetClassName, mixinClassName, new DepCheckException(result));
return false;
}
return true;
}
public String getCheckResult(@Nullable T obj) {
StringBuilder result = new StringBuilder();
if (!this.andRequirementsSatisfied) {
result.append("Requirements:");
this.andRequirements.forEach(dependency -> result.append(dependency.satisfied ? "" : "\n\t" + dependency.getCheckResult()));
}
if (!this.orRequirementsSatisfied) {
if (result.length() != 0) {
result.append("\n");
}
result.append("Optional Requirements:");
this.orRequirements.forEach(dependency -> result.append(dependency.satisfied ? "" : "\n\t" + dependency.getCheckResult()));
}
if (!this.noConflicts) {
if (result.length() != 0) {
result.append("\n");
}
result.append("Conflicts:");
this.conflicts.forEach(dependency -> result.append(dependency.satisfied ? String.format("\n\tMod %s [%s] detected.", dependency.modId, dependency.versionPredicate) : ""));
}
if (!testHelper(this.predicate, obj)) {
if (result.length() != 0) {
result.append("\n");
}
result.append(String.format("Predicate %s check {%s} failed.", this.predicate, obj));
}
String ret = result.toString();
if (ret.isEmpty()) {
ret = SATISFIED;
}
return ret;
}
public boolean satisfied(@Nullable T obj) {
return this.andRequirementsSatisfied && this.orRequirementsSatisfied && this.noConflicts && testHelper(this.predicate, obj);
}
}