Skip to content

Commit

Permalink
Expose annotation scanning as API
Browse files Browse the repository at this point in the history
Signed-off-by: TheSilkMiner <thesilkminer@outlook.com>
  • Loading branch information
TheSilkMiner authored and jaredlll08 committed Oct 11, 2022
1 parent f593ac7 commit a315370
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 62 deletions.
@@ -1,7 +1,6 @@
package com.blamejared.crafttweaker.gametest;

import com.blamejared.crafttweaker.api.CraftTweakerAPI;
import com.blamejared.crafttweaker.api.CraftTweakerConstants;
import com.blamejared.crafttweaker.api.util.ClassUtil;
import com.blamejared.crafttweaker.gametest.framework.FutureJUnitTestFunction;
import com.blamejared.crafttweaker.gametest.framework.Modifier;
import com.blamejared.crafttweaker.gametest.framework.ModifingConsumer;
Expand All @@ -10,7 +9,6 @@
import com.blamejared.crafttweaker.gametest.framework.annotation.ScriptTestHolder;
import com.blamejared.crafttweaker.gametest.util.CraftTweakerGameTester;
import com.blamejared.crafttweaker.gametest.util.ICraftTweakerGameTester;
import com.blamejared.crafttweaker.platform.Services;
import net.minecraft.gametest.framework.GameTest;
import net.minecraft.gametest.framework.GlobalTestReporter;
import net.minecraft.gametest.framework.StructureUtils;
Expand All @@ -36,7 +34,7 @@ public List<TestFunction> collectTests() {
GlobalTestReporter.replaceWith(new SpecialCaseTestReporter("GameTests", new File("game-test-results.xml")));

List<TestFunction> functions = new ArrayList<>();
Services.PLATFORM.findClassesWithAnnotation(ScriptTestHolder.class).forEach(aClass -> {
ClassUtil.findClassesWithAnnotation(ScriptTestHolder.class).forEach(aClass -> {

for(Method method : aClass.getDeclaredMethods()) {
if(method.isAnnotationPresent(Test.class)) {
Expand All @@ -46,7 +44,7 @@ public List<TestFunction> collectTests() {
}
});

Services.PLATFORM.findClassesWithAnnotation(CraftTweakerGameTestHolder.class).forEach(aClass -> {
ClassUtil.findClassesWithAnnotation(CraftTweakerGameTestHolder.class).forEach(aClass -> {

for(Method method : aClass.getDeclaredMethods()) {
if(!method.isAnnotationPresent(GameTest.class)) {
Expand Down
@@ -1,7 +1,8 @@
package com.blamejared.crafttweaker.gametest.framework;

import com.blamejared.crafttweaker.api.util.ClassUtil;
import com.blamejared.crafttweaker.gametest.framework.annotation.ScriptTestHolder;
import com.blamejared.crafttweaker.platform.Services;
import com.mojang.datafixers.util.Either;
import org.junit.jupiter.engine.JupiterTestEngine;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.Launcher;
Expand All @@ -12,6 +13,7 @@
import org.junit.platform.launcher.core.LauncherFactory;

import java.util.List;
import java.util.Map;

public class TestRunner {

Expand All @@ -28,8 +30,12 @@ public TestRunner(String loader) {

private List<Class<?>> gatherTests() {

return (List<Class<?>>) Services.PLATFORM.findClassesWithAnnotation(ScriptTestHolder.class, mod -> {}, scriptTestHolderMapEither -> scriptTestHolderMapEither.map(ScriptTestHolder::loader, map -> map.get("loader"))
.equals(this.loader)).toList();
return (List<Class<?>>) ClassUtil.findClassesWithAnnotation(ScriptTestHolder.class, this::isSuitable).toList();
}

private boolean isSuitable(final Either<ScriptTestHolder, Map<String, Object>> data) {

return this.loader.equals(data.map(ScriptTestHolder::loader, map -> map.get("loader")));
}


Expand Down
@@ -0,0 +1,45 @@
package com.blamejared.crafttweaker.api.util;

import com.blamejared.crafttweaker.api.mod.Mod;
import com.blamejared.crafttweaker.platform.Services;
import com.mojang.datafixers.util.Either;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

public final class ClassUtil {

public static <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(final Class<T> annotationClass) {

return findClassesWithAnnotation(annotationClass, it -> {});
}

public static <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(
final Class<T> annotationClass,
final Consumer<Mod> classProviderConsumer
) {

return findClassesWithAnnotation(annotationClass, classProviderConsumer, annotationData -> true);
}

public static <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(
final Class<T> annotationCLass,
final Predicate<Either<T, Map<String, Object>>> annotationFilter
) {

return findClassesWithAnnotation(annotationCLass, it -> {}, annotationFilter);
}

public static <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(
final Class<T> annotationClass,
final Consumer<Mod> classProviderConsumer,
final Predicate<Either<T, Map<String, Object>>> annotationFilter
) {

return Services.PLATFORM.findClassesWithAnnotation(annotationClass, classProviderConsumer, annotationFilter);
}

}
@@ -1,9 +1,9 @@
package com.blamejared.crafttweaker.gametest;

import com.blamejared.crafttweaker.api.util.ClassUtil;
import com.blamejared.crafttweaker.api.util.InstantiationUtil;
import com.blamejared.crafttweaker.gametest.util.CraftTweakerGameTester;
import com.blamejared.crafttweaker.gametest.util.ICraftTweakerGameTester;
import com.blamejared.crafttweaker.platform.Services;
import net.minecraft.gametest.framework.GameTestGenerator;
import net.minecraft.gametest.framework.TestFunction;

Expand All @@ -16,7 +16,7 @@ public class CraftTweakerGameTests {
public static List<TestFunction> getTests() {

List<TestFunction> functions = new ArrayList<>();
Services.PLATFORM.findClassesWithAnnotation(CraftTweakerGameTester.class).forEach(aClass -> {
ClassUtil.findClassesWithAnnotation(CraftTweakerGameTester.class).forEach(aClass -> {
if(ICraftTweakerGameTester.class.isAssignableFrom(aClass)) {
ICraftTweakerGameTester runner = (ICraftTweakerGameTester) InstantiationUtil.getOrCreateInstance(aClass);
if(runner != null) {
Expand Down
Expand Up @@ -3,11 +3,11 @@
import com.blamejared.crafttweaker.api.CraftTweakerAPI;
import com.blamejared.crafttweaker.api.plugin.CraftTweakerPlugin;
import com.blamejared.crafttweaker.api.plugin.ICraftTweakerPlugin;
import com.blamejared.crafttweaker.api.util.ClassUtil;
import com.blamejared.crafttweaker.api.util.GenericUtil;
import com.blamejared.crafttweaker.api.zencode.IScriptLoader;
import com.blamejared.crafttweaker.api.zencode.scriptrun.ScriptRunConfiguration;
import com.blamejared.crafttweaker.impl.registry.CraftTweakerRegistry;
import com.blamejared.crafttweaker.platform.Services;
import com.mojang.datafixers.util.Pair;
import net.minecraft.ResourceLocationException;
import net.minecraft.resources.ResourceLocation;
Expand Down Expand Up @@ -71,7 +71,7 @@ public static PluginManager of() {

private static List<DecoratedCraftTweakerPlugin> discoverPlugins() {

return Services.PLATFORM.findClassesWithAnnotation(CraftTweakerPlugin.class)
return ClassUtil.findClassesWithAnnotation(CraftTweakerPlugin.class)
.map(PluginManager::checkAndCast)
.map(PluginManager::initPlugin)
.filter(Objects::nonNull)
Expand Down
Expand Up @@ -2,9 +2,9 @@

import com.blamejared.crafttweaker.api.plugin.IRecipeHandlerRegistrationHandler;
import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
import com.blamejared.crafttweaker.api.util.ClassUtil;
import com.blamejared.crafttweaker.api.util.GenericUtil;
import com.blamejared.crafttweaker.api.util.InstantiationUtil;
import com.blamejared.crafttweaker.platform.Services;
import net.minecraft.world.item.crafting.Recipe;

import java.lang.annotation.Annotation;
Expand All @@ -23,7 +23,7 @@ void gatherAndRegisterHandlers(final IRecipeHandlerRegistrationHandler handler)

private Stream<? extends Class<?>> findWithAnnotation(final Class<? extends Annotation> annotation) {

return Services.PLATFORM.findClassesWithAnnotation(annotation);
return ClassUtil.findClassesWithAnnotation(annotation);
}

private void tryRegister(final Class<?> clazz, final IRecipeHandlerRegistrationHandler handler) {
Expand Down
@@ -1,12 +1,12 @@
package com.blamejared.crafttweaker.impl.plugin.crafttweaker;

import com.blamejared.crafttweaker.api.annotation.ZenRegister;
import com.blamejared.crafttweaker.api.util.ClassUtil;
import com.blamejared.crafttweaker.platform.Services;
import com.google.common.base.Suppliers;
import com.mojang.datafixers.util.Either;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -26,11 +26,11 @@ private record ZenCandidates(List<ZenClassData> classCandidates, Runnable modsPr

this.zenCandidates = Suppliers.memoize(() -> {
final CraftTweakerModList modList = new CraftTweakerModList();
final List<ZenClassData> classes = Services.PLATFORM.findClassesWithAnnotation(ZenRegister.class, modList::add, this::checkModDependencies)
final List<ZenClassData> classes = ClassUtil.findClassesWithAnnotation(ZenRegister.class, modList::add, this::checkModDependencies)
.filter(Objects::nonNull)
.flatMap(this::makeForClass)
.toList();
return new ZenCandidates(Collections.unmodifiableList(classes), modList::printToLog);
return new ZenCandidates(classes, modList::printToLog);
});
}

Expand Down
Expand Up @@ -90,32 +90,6 @@ default Path getRelativePathFromGameDirectory(final String path) {
return this.getRelativePathFromGameDirectory(Path.of(path));
}

/**
* Finds classes with the given annotation
*
* @param annotationCls The annotation class to look for.
*
* @return A stream of classes with the annotation
*/
default <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(Class<T> annotationCls) {

return findClassesWithAnnotation(annotationCls, mod -> {}, tMapEither -> true);
}

/**
* Finds classes with the given annotation
*
* @param annotationCls The annotation class to look for.
* @param consumer Consumer to collect the given mod that added the class if available.
*
* @return A stream of classes with the annotation
*/
default <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(Class<T> annotationCls, Consumer<Mod> consumer) {

return findClassesWithAnnotation(annotationCls, consumer, tMapEither -> true);
}


/**
* Finds classes with the given annotation and applies a filter.
*
Expand All @@ -125,7 +99,11 @@ default <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotat
*
* @return A stream of classes with the annotation
*/
<T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(Class<T> annotationCls, Consumer<Mod> consumer, Predicate<Either<T, Map<String, Object>>> annotationFilter);
<T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(
final Class<T> annotationClass,
final Consumer<Mod> classProviderConsumer,
final Predicate<Either<T, Map<String, Object>>> annotationFilter
);

String findMappedMethodName(final Class<?> clazz, final String methodName, final Class<?> returnType, final Class<?>... parameterTypes);

Expand Down
Expand Up @@ -158,13 +158,17 @@ public Path getGameDirectory() {
}

@Override
public <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(Class<T> annotationCls, Consumer<Mod> consumer, Predicate<Either<T, Map<String, Object>>> annotationFilter) {
public <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(
final Class<T> annotationClass,
final Consumer<Mod> classProviderConsumer,
final Predicate<Either<T, Map<String, Object>>> annotationFilter
) {

Set<Class<?>> typesAnnotatedWith = REFLECTIONS.get().getTypesAnnotatedWith(annotationCls);
typesAnnotatedWith.stream().map(this::getModsForClass).flatMap(Collection::stream).forEach(consumer);
final Set<Class<?>> typesAnnotatedWith = REFLECTIONS.get().getTypesAnnotatedWith(annotationClass);
return typesAnnotatedWith.stream()
.filter(it -> it.isAnnotationPresent(annotationCls)) // Thank you reflections for giving classes without the annotation, very cool
.filter(it -> annotationFilter.test(Either.left(it.getAnnotation(annotationCls))));
.filter(it -> it.isAnnotationPresent(annotationClass)) // Thank you reflections for giving classes without the annotation, very cool
.filter(it -> annotationFilter.test(Either.left(it.getAnnotation(annotationClass))))
.peek(it -> this.getModsForClass(it).forEach(classProviderConsumer));
}

private List<Mod> getModsForClass(Class<?> clazz) {
Expand Down
Expand Up @@ -178,23 +178,17 @@ public Path getGameDirectory() {
}

@Override
public <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(Class<T> annotationCls, Consumer<Mod> consumer, Predicate<Either<T, Map<String, Object>>> annotationFilter) {
public <T extends Annotation> Stream<? extends Class<?>> findClassesWithAnnotation(
final Class<T> annotationClass,
final Consumer<Mod> classProviderConsumer,
final Predicate<Either<T, Map<String, Object>>> annotationFilter
) {

final Type annotationType = Type.getType(annotationCls);
final Type annotationType = Type.getType(annotationClass);
return ModList.get()
.getAllScanData()
.stream()
.flatMap(scanData -> scanData.getAnnotations()
.stream()
.filter(a -> annotationType.equals(a.annotationType()))
.filter(annotationData -> annotationFilter.test(Either.right(annotationData.annotationData())))
.peek(annotationData -> scanData.getIModInfoData()
.stream()
.flatMap(iModFileInfo -> iModFileInfo.getMods()
.stream())
.map(iModInfo -> new Mod(iModInfo.getModId(), iModInfo.getDisplayName(), iModInfo.getVersion()
.toString())).forEach(consumer))
.map(ModFileScanData.AnnotationData::clazz))
.flatMap(it -> this.fromScanData(annotationType, classProviderConsumer, annotationFilter, it))
.map(ForgePlatformHelper::getClassFromType)
.filter(Objects::nonNull);
}
Expand Down Expand Up @@ -290,6 +284,26 @@ public Set<MutableComponent> getFluidsForDump(ItemStack stack, Player player, In
return components;
}

private <T extends Annotation> Stream<Type> fromScanData(
final Type annotationType,
final Consumer<Mod> classProviderConsumer,
final Predicate<Either<T, Map<String, Object>>> annotationFilter,
final ModFileScanData data
) {

return data.getAnnotations()
.stream()
.filter(it -> annotationType.equals(it.annotationType()))
.filter(it -> annotationFilter.test(Either.right(it.annotationData())))
.peek(ignored -> data.getIModInfoData()
.stream()
.flatMap(it -> it.getMods().stream())
.map(it -> new Mod(it.getModId(), it.getDisplayName(), it.getVersion().toString()))
.forEach(classProviderConsumer)
)
.map(ModFileScanData.AnnotationData::clazz);
}

private static Class<?> getClassFromType(Type type) {

try {
Expand Down

0 comments on commit a315370

Please sign in to comment.