Javadocs for prosperity, hopefully

This commit is contained in:
Altiami
2024-05-05 09:37:09 -07:00
parent e5370f7d61
commit 267c92446b
6 changed files with 100 additions and 0 deletions

View File

@@ -25,16 +25,35 @@ import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier;
/**
* Base class for plugin compatibility. Defines common functions for evaluating plugin compatibility conditions.
* Any given instance of a subclass should always be a singleton. See {@link net.gensokyoreimagined.nitori.compatibility.PluginCompatibilityRegistry}
*/
public abstract class BasePluginCompatibility {
/**
* List of plugin names required by this compatibility class. The names in this list are to be the same used in
* the plugin.yml files in their respective plugins.
*/
private final String[] pluginNames;
/**
* Whether the compatibility class has attempted to resolve the reflection references yet or not.
*/
private final AtomicBoolean reflectionResolutionAttempted = new AtomicBoolean(false);
/**
* Define a new compatibility class.
* @param pluginNames See {@link net.gensokyoreimagined.nitori.compatibility.BasePluginCompatibility#pluginNames}
*/
protected BasePluginCompatibility(String[] pluginNames) {
this.pluginNames = pluginNames;
}
/**
* Finds the class loaders for the plugins needed for this compatibility class.
* @return The class loaders for the plugins needed for this compatibility class.
*/
@Nullable
private Map<String, ClassLoader> collectPluginClassLoaders() {
var pluginManager = Bukkit.getPluginManager();
@@ -49,21 +68,48 @@ public abstract class BasePluginCompatibility {
return pluginClassLoaders;
}
/**
* Logs a message for when checking a compatibility condition fails.
* @param problemFormat The format String to be used for explaining the problem. Requires one `%s` for the class name.
* @param e The exception thrown by the check failure.
*/
private void logCompatibilityCheckFailureMessage(@Nonnull String problemFormat, @Nonnull Exception e) {
NitoriUtil.getPreferredLogger().error(NitoriUtil.makeLogMessage(problemFormat.formatted("`" + this.getClass().getName() + "`") + "! I'll assume the related plugin(s) will otherwise hang and always run on main. Please update my intel! Exception follows:\n" +
e));
}
/**
* What to do when the plugin classes are not found.
* @param e The exception thrown by the plugin classes not being found.
*/
private void onPluginClassesNotFound(ReflectiveOperationException e) {
logCompatibilityCheckFailureMessage("FAILED TO LOCATE CLASSES FOR %s", e);
}
/**
* What to do when attempting to use the plugin reflection references fails.
* @param e The exception thrown by an attempt to use a plugin reflection reference fails.
*/
private void onReferenceUseFailure(CompletionException e) {
logCompatibilityCheckFailureMessage("FAILED TO USE REFLECTED REFERENCES FOR %s!", e);
}
/**
* Resolves and caches plugin reflection references.
* @param pluginClassLoaders The plugin class loaders used to resolve the reflection references.
* @throws ReflectiveOperationException If something went wrong finding the resolutions for reflection references.
*/
protected abstract void collectReferences(@Nonnull Map<String, ClassLoader> pluginClassLoaders) throws ReflectiveOperationException;
/**
* Fullfills a plugin compatibility boolean condition.
* @param conditionCallback The function used to ultimately determine if the condition passes or fails.
* The condition callback may throw an {@link IllegalStateException} if the reflection references have not yet been resolved.
* @return If the necessary plugins aren't loaded, false.
* Otherwise, if the reflection references failed to be resolved, or an unexpected exception is thrown by the condition callback, true.
* Otherwise, the result of the condition callback.
* @throws IllegalStateException If the reflection references weren't resolved before calling the condition callback.
*/
boolean completePluginCompatibilityCondition(BooleanSupplier conditionCallback) {
synchronized (this) { // compatibility classes are singletons so syncing on itself is safe
if (!this.reflectionResolutionAttempted.getAcquire()) {

View File

@@ -14,9 +14,18 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package net.gensokyoreimagined.nitori.compatibility;
/**
* Stores and provides access to all plugin compatibility instances.
*/
public class PluginCompatibilityRegistry {
/**
* Instance for Citizens compatibility.
*/
public static final PluginCompatibilityCitizens CITIZENS = new PluginCompatibilityCitizens();
/**
* Instance for Train Carts compatibility.
*/
public static final PluginCompatibilityTrainCarts TRAIN_CARTS = new PluginCompatibilityTrainCarts();
}

View File

@@ -17,17 +17,40 @@ package net.gensokyoreimagined.nitori.compatibility.reflection;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* A resolver for a reflection reference to be invoked at runtime when the resolution should be valid.
* @param <T> The data type to be returned.
*/
public abstract class BaseReflectionReferenceResolver<T> {
/**
* The resolution of this reflection reference. Is null until resolved.
*/
@Nullable
private T resolution;
/**
* Resolves this reflection reference.
* @param classLoader The class loader to use for resolving this reflection reference
* @return The resolution of this reflection reference.
* @throws ReflectiveOperationException If something went wrong finding the resolution for this reflection reference
*/
@Nonnull
protected abstract T resolve(ClassLoader classLoader) throws ReflectiveOperationException;
/**
* Accept a classloader and resolve this reflection reference.
* @param classLoader The class loader to use for resolving this reflection reference
* @throws ReflectiveOperationException If something went wrong finding the resolution for this reflection reference
*/
public void accept(ClassLoader classLoader) throws ReflectiveOperationException {
this.resolution = resolve(classLoader);
}
/**
* Get the resolution of this reflection reference.
* @return The resolution of this reflection reference.
* @throws IllegalStateException If this reflection reference has not yet been resolved.
*/
@Nonnull
public T getResolution() {
if (this.resolution == null) {
@@ -36,6 +59,13 @@ public abstract class BaseReflectionReferenceResolver<T> {
return this.resolution;
}
/**
* Common function for resolving a class.
* @param classLoader The class loader to use for finding the class.
* @param classPath The path of the class.
* @return The class
* @throws ClassNotFoundException If the class could not be found with the given class loader
*/
@Nonnull
protected static Class<?> resolveClass(ClassLoader classLoader, String classPath) throws ClassNotFoundException {
return Class.forName(classPath, false, classLoader);

View File

@@ -20,6 +20,10 @@ public class ClassReflectionReferenceResolver extends BaseReflectionReferenceRes
private final String classPath;
/**
* Creates a new class resolver.
* @param classPath The path of the class.
*/
public ClassReflectionReferenceResolver(String classPath) {
this.classPath = classPath;
}

View File

@@ -24,6 +24,11 @@ public class FieldReflectionReferenceResolver extends BaseReflectionReferenceRes
private final String fieldName;
/**
* Creates a new field resolver.
* @param classPath The path of the class containing the field.
* @param fieldName the name of the field.
*/
public FieldReflectionReferenceResolver(String classPath, String fieldName) {
this.classPath = classPath;
this.fieldName = fieldName;

View File

@@ -26,6 +26,12 @@ public class MethodReflectionReferenceResolver extends BaseReflectionReferenceRe
private final Class<?>[] methodParameterClasses;
/**
* Creates a new method resolver.
* @param classPath The path of the class containing the method.
* @param methodName The name of the method.
* @param methodParameterClasses The classes of the parameter types of the method, per parameter, in order.
*/
public MethodReflectionReferenceResolver(String classPath, String methodName, Class<?>... methodParameterClasses) {
this.classPath = classPath;
this.methodName = methodName;