All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.
dev.the_fireplace.annotateddi.impl.di.AnnotatedDIModule Maven / Gradle / Ivy
package dev.the_fireplace.annotateddi.impl.di;
import ImplementationContainer;
import com.google.gson.*;
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
import dev.the_fireplace.annotateddi.impl.AnnotatedDI;
import dev.the_fireplace.annotateddi.impl.UrlUtil;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.impl.util.FileSystemUtil;
import record;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.zip.ZipError;
@SuppressWarnings({"rawtypes", "unchecked"})
public final class AnnotatedDIModule extends AbstractModule {
public static final String DI_CONFIG_FILE_NAME = "annotated-di.json";
@Override
protected void configure() {
Set implementations = findImplementations();
bindImplementations(implementations);
}
private void bindImplementations(Iterable modImplementations) {
for (ImplementationContainer modImplementationData : modImplementations) {
for (ImplementationData implementationData : modImplementationData.implementations) {
bindImplementationToInterface(
implementationData.implementation,
implementationData.interfaces.toArray(new Class[0]),
implementationData.name
);
}
}
}
private Set findImplementations() {
try {
Enumeration diConfigFileUrls = this.getClass().getClassLoader().getResources(DI_CONFIG_FILE_NAME);
Set validConfigFilePaths = getConfigFilePaths(diConfigFileUrls);
return getImplementationContainersFromConfigs(validConfigFilePaths);
} catch (IOException e) {
AnnotatedDI.getLogger().error("Exception when scanning for implementations!", e);
}
return Set.of();
}
private Set getConfigFilePaths(Enumeration mods) {
Set modsList = new HashSet<>();
while (mods.hasMoreElements()) {
URL url;
try {
url = UrlUtil.getSource(DI_CONFIG_FILE_NAME, mods.nextElement());
} catch (Exception e) {
AnnotatedDI.getLogger().error("Error getting DI config's source!", e);
continue;
}
Path normalizedPath, configJsonPath;
try {
normalizedPath = UrlUtil.asPath(url).normalize();
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to convert URL " + url + "!", e);
}
if (Files.isDirectory(normalizedPath)) {
configJsonPath = normalizedPath.resolve(DI_CONFIG_FILE_NAME);
} else {
// JAR file
try {
FileSystemUtil.FileSystemDelegate jarFs = FileSystemUtil.getJarFileSystem(normalizedPath, false);
configJsonPath = jarFs.get().getPath(DI_CONFIG_FILE_NAME);
} catch (IOException e) {
AnnotatedDI.getLogger().error("Failed to open JAR at " + normalizedPath + "!", e);
continue;
} catch (ZipError e) {
AnnotatedDI.getLogger().error("Jar at " + normalizedPath + " is corrupted!", e);
continue;
}
}
modsList.add(configJsonPath);
}
return modsList;
}
private Set getImplementationContainersFromConfigs(Set configFilePaths) {
Set implementationContainers = new HashSet<>();
for (Path path : configFilePaths) {
ImplementationContainer implementationContainer = readImplementationContainerFromPath(path);
if (implementationContainer != null) {
implementationContainers.add(implementationContainer);
}
}
return implementationContainers;
}
@Nullable
private ImplementationContainer readImplementationContainerFromPath(Path path) {
ImplementationContainer implementationContainer = null;
JsonParser jsonParser = new JsonParser();
try (BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8))) {
JsonElement jsonElement = jsonParser.parse(br);
if (jsonElement instanceof JsonObject jsonObject) {
implementationContainer = readImplementationContainerJson(jsonObject);
}
} catch (IOException | JsonParseException | ClassNotFoundException e) {
AnnotatedDI.getLogger().error("Exception when reading implementation file!", e);
}
return implementationContainer;
}
private ImplementationContainer readImplementationContainerJson(JsonObject jsonObject) throws ClassNotFoundException {
JsonArray modImplementations = jsonObject.getAsJsonArray("implementations");
List implementationDatas = new ArrayList<>();
for (JsonElement element : modImplementations) {
JsonObject implementationObj = (JsonObject) element;
if (isOnWrongEnvironment(implementationObj)) {
continue;
}
JsonArray interfaceNames = implementationObj.getAsJsonArray("interfaces");
List interfaces = new ArrayList<>();
for (JsonElement interfaceName : interfaceNames) {
interfaces.add(stringToClass(interfaceName.getAsString()));
}
implementationDatas.add(new ImplementationData(
stringToClass(implementationObj.get("class").getAsString()),
interfaces,
implementationObj.has("namedImplementation")
? implementationObj.get("namedImplementation").getAsString()
: ""
));
}
return new ImplementationContainer(jsonObject.get("version").getAsString(), implementationDatas);
}
private boolean isOnWrongEnvironment(JsonObject implementationObj) {
return implementationObj.has("environment")
&& !FabricLoader.getInstance().getEnvironmentType().equals(EnvType.valueOf(implementationObj.get("environment").getAsString()));
}
private void bindImplementationToInterface(Class implementation, Class[] injectableInterfaces, String name) {
if (!Arrays.equals(injectableInterfaces, new Class[]{Object.class})) {
for (Class injectableInterface : injectableInterfaces) {
bindWithOptionalName(injectableInterface, implementation, name);
}
} else {
Class[] interfaces = implementation.getInterfaces();
if (interfaces.length == 1) {
bindWithOptionalName(interfaces[0], implementation, name);
} else if (interfaces.length > 1) {
throw new ImplementationException(String.format("Multiple interfaces found for @Implementation annotated class %s, please set the value(s) to pick the correct one(s).", implementation.getCanonicalName()));
} else {
throw new ImplementationException(String.format("No interfaces found for @Implementation annotated class %s, please set the value(s) to pick the correct one(s).", implementation.getCanonicalName()));
}
}
}
private void bindWithOptionalName(Class injectableInterface, Class implementation, String name) {
if (name.isBlank()) {
bind(injectableInterface).to(implementation);
} else {
bind(injectableInterface).annotatedWith(Names.named(name)).to(implementation);
}
}
private Class stringToClass(String className) throws ClassNotFoundException {
return Class.forName(className);
}
private record ImplementationContainer(String version, List implementations) {}
private record ImplementationData(Class implementation, List interfaces, String name) {}
}