diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnexpectedCheckEndException.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnexpectedCheckEndException.java
new file mode 100644
index 0000000..1a51e97
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/UnexpectedCheckEndException.java
@@ -0,0 +1,48 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.exceptions;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Thrown when a sequence of if checks or switch cases fail unexpectedly.
+ *
+ * @since v1-alpha2
+ */
+public class UnexpectedCheckEndException extends RuntimeException {
+ /**
+ * Constructs this exception.
+ *
+ * @param checkOccurrence the sequence of checks that failed
+ * @since v1-alpha2
+ */
+ public UnexpectedCheckEndException(@NotNull String checkOccurrence) {
+ super("A sequence of if checks or switch cases failed unexpectedly while " + checkOccurrence);
+ }
+
+ /**
+ * Constructs this exception.
+ *
+ * @since v1-alpha2
+ */
+ public UnexpectedCheckEndException() {
+ super("A sequence of if checks or switch cases failed unexpectedly");
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/IncompatibleTypeReflection.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/IncompatibleTypeReflection.java
new file mode 100644
index 0000000..52e1840
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/IncompatibleTypeReflection.java
@@ -0,0 +1,43 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.exceptions.reflection;
+
+import de.staropensource.sosengine.base.types.reflection.ClassType;
+import de.staropensource.sosengine.base.utility.ListFormatter;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Thrown when the method called does not apply to the class type.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public class IncompatibleTypeReflection extends RuntimeException {
+ /**
+ * Constructs this exception.
+ *
+ * @param methodName name of the method that failed
+ * @param requiredClassType class type received by the method
+ * @param compatibleTypes class types the method is compatible with
+ */
+ public IncompatibleTypeReflection(@NotNull String methodName, @NotNull ClassType requiredClassType, @NotNull ClassType[] compatibleTypes) {
+ super("The method ReflectionClass#" + methodName + " only applies to type(s) " + ListFormatter.formatArray(compatibleTypes) + ", not " + requiredClassType.name());
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/InvalidFieldException.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/InvalidFieldException.java
new file mode 100644
index 0000000..ac6c4fb
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/InvalidFieldException.java
@@ -0,0 +1,41 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.exceptions.reflection;
+
+import de.staropensource.sosengine.base.reflection.ReflectionClass;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Thrown when a field could not be found.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public class InvalidFieldException extends Exception {
+ /**
+ * Constructs this exception.
+ *
+ * @param clazz caller {@link ReflectionClass}
+ * @param fieldName name of the invalid field
+ */
+ public InvalidFieldException(@NotNull ReflectionClass clazz, @NotNull String fieldName) {
+ super("Invalid field name \"" + fieldName + "\" in class " + clazz.getPath());
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/InvalidMethodException.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/InvalidMethodException.java
new file mode 100644
index 0000000..e30620b
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/InvalidMethodException.java
@@ -0,0 +1,41 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.exceptions.reflection;
+
+import de.staropensource.sosengine.base.reflection.ReflectionClass;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Thrown when a method could not be found.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public class InvalidMethodException extends Exception {
+ /**
+ * Constructs this exception.
+ *
+ * @param clazz caller {@link ReflectionClass}
+ * @param fieldName name of the invalid method
+ */
+ public InvalidMethodException(@NotNull ReflectionClass clazz, @NotNull String fieldName) {
+ super("Invalid method name \"" + fieldName + "\" in class " + clazz.getPath());
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/NoAccessException.java b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/NoAccessException.java
new file mode 100644
index 0000000..5277c65
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/exceptions/reflection/NoAccessException.java
@@ -0,0 +1,34 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.exceptions.reflection;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Thrown if access to some class, method or field is denied.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public class NoAccessException extends Exception {
+ public NoAccessException(@NotNull String type, @NotNull String name) {
+ super("Access to " + type + " " + name + " has been denied");
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/internal/reflection/ReflectionAccessWidener.java b/base/src/main/java/de/staropensource/sosengine/base/internal/reflection/ReflectionAccessWidener.java
new file mode 100644
index 0000000..87cd50c
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/internal/reflection/ReflectionAccessWidener.java
@@ -0,0 +1,193 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.internal.reflection;
+
+import de.staropensource.sosengine.base.exceptions.UnexpectedThrowableException;
+import de.staropensource.sosengine.base.exceptions.reflection.NoAccessException;
+import de.staropensource.sosengine.base.reflection.ReflectionField;
+import de.staropensource.sosengine.base.reflection.ReflectionMethod;
+
+import java.lang.reflect.*;
+
+/**
+ * Allows access to fields and methods to be widened.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public class ReflectionAccessWidener {
+ /**
+ * Allows access to an {@link AccessibleObject}.
+ *
+ * Stolen from the jOOR library.
+ * All credits to them.
+ *
+ * @param accessible object to allow access to
+ */
+ public static void allowAccess(T accessible) {
+ if (accessible == null)
+ return;
+
+ if (accessible instanceof Member member)
+ if (Modifier.isPublic(member.getModifiers()) && Modifier.isPublic(member.getDeclaringClass().getModifiers()))
+ return;
+
+ //noinspection deprecation // no, it's what we want
+ if (!accessible.isAccessible())
+ accessible.setAccessible(true);
+ }
+
+ /**
+ * Unlocks modifications to some field.
+ *
+ * @param reflectionField {@link ReflectionField} to unlock
+ * @return updated modifiers. pass those to {@code lockModifications} after modifying the field
+ * @see ReflectionAccessWidener#lockModifications(ReflectionField, int)
+ * @since v1-alpha2
+ */
+ public static int unlockModifications(ReflectionField reflectionField) throws UnexpectedThrowableException, NoAccessException {
+ int updatedModifiers = 0;
+ Field field = reflectionField.getField();
+
+ Field modifiersField;
+ try {
+ modifiersField = field.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside field " + field.getName());
+ }
+
+ modifiersField.setAccessible(true);
+
+ if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL)
+ updatedModifiers = ~Modifier.FINAL;
+ if ((field.getModifiers() & Modifier.STATIC) == Modifier.STATIC)
+ updatedModifiers = updatedModifiers & ~Modifier.STATIC;
+
+ try {
+ modifiersField.setInt(field, field.getModifiers() & ~updatedModifiers);
+ } catch (IllegalAccessException exception) {
+ throw new NoAccessException("field", field.getName());
+ }
+
+ return updatedModifiers;
+ }
+
+ /**
+ * Unlocks modifications to some method.
+ *
+ * @param reflectionMethod {@link ReflectionMethod} to unlock
+ * @return updated modifiers. pass those to {@code lockModifications} after modifying the method
+ * @see ReflectionAccessWidener#lockModifications(ReflectionMethod, int)
+ * @since v1-alpha2
+ */
+ public static int unlockModifications(ReflectionMethod reflectionMethod) throws UnexpectedThrowableException, NoAccessException {
+ Method method = reflectionMethod.getMethod();
+ int updatedModifiers = method.getModifiers();
+
+ Field modifiersField;
+ try {
+ modifiersField = method.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside method " + method.getName());
+ }
+
+ modifiersField.setAccessible(true);
+
+ if (Modifier.isFinal(method.getModifiers()))
+ updatedModifiers = ~Modifier.FINAL;
+ if (Modifier.isStatic(method.getModifiers()))
+ updatedModifiers = updatedModifiers & ~Modifier.STATIC;
+ if (!Modifier.isPublic(method.getModifiers())) {
+ if (Modifier.isProtected(method.getModifiers()))
+ updatedModifiers = updatedModifiers & ~Modifier.PROTECTED;
+ if (Modifier.isPrivate(method.getModifiers()))
+ updatedModifiers = updatedModifiers & ~Modifier.PRIVATE;
+
+ updatedModifiers = updatedModifiers & ~Modifier.PUBLIC;
+ }
+ try {
+ modifiersField.setInt(method, method.getModifiers() & ~updatedModifiers);
+ } catch (IllegalAccessException exception) {
+ throw new NoAccessException("method", method.getName());
+ }
+
+ return updatedModifiers;
+ }
+
+ /**
+ * Locks modifications to some field.
+ *
+ * @param reflectionField {@link ReflectionField} to lock
+ * @param updatedModifiers original modifiers
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is not present inside the field
+ * @see ReflectionAccessWidener#unlockModifications(ReflectionField)
+ * @since v1-alpha2
+ */
+ public static void lockModifications(ReflectionField reflectionField, int updatedModifiers) throws UnexpectedThrowableException, NoAccessException {
+ Field field = reflectionField.getField();
+
+ Field modifiersField;
+ try {
+ modifiersField = field.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside field " + field.getName());
+ }
+
+ modifiersField.setAccessible(true);
+
+ try {
+ modifiersField.setInt(field, field.getModifiers() & ~updatedModifiers);
+ } catch (IllegalAccessException exception) {
+ throw new NoAccessException("field", field.getName());
+ }
+
+ modifiersField.setAccessible(false);
+ }
+
+ /**
+ * Locks modifications to some method.
+ *
+ * @param reflectionMethod {@link ReflectionMethod} to lock
+ * @param updatedModifiers original modifiers
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is not present inside the method
+ * @see ReflectionAccessWidener#unlockModifications(ReflectionMethod)
+ * @since v1-alpha2
+ */
+ public static void lockModifications(ReflectionMethod reflectionMethod, int updatedModifiers) throws UnexpectedThrowableException, NoAccessException {
+ Method method = reflectionMethod.getMethod();
+
+ Field modifiersField;
+ try {
+ modifiersField = method.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside method " + method.getName());
+ }
+
+ modifiersField.setAccessible(true);
+
+ try {
+ modifiersField.setInt(method, method.getModifiers() & ~updatedModifiers);
+ } catch (IllegalAccessException exception) {
+ throw new NoAccessException("method", method.getName());
+ }
+
+ modifiersField.setAccessible(false);
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/internal/reflection/ReflectionScanningHelper.java b/base/src/main/java/de/staropensource/sosengine/base/internal/reflection/ReflectionScanningHelper.java
new file mode 100644
index 0000000..ca90b2c
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/internal/reflection/ReflectionScanningHelper.java
@@ -0,0 +1,84 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.internal.reflection;
+
+import org.jetbrains.annotations.NotNull;
+import org.reflections.Reflections;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Helps in scanning the class path.
+ *
+ * This entire class has been stolen from
+ * the Reflections library.
+ * All credits to them.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public class ReflectionScanningHelper {
+ /**
+ * Returns the classpath as URLs.
+ *
+ * @return collection of classpath urls
+ * @since v1-alpha2
+ */
+ @NotNull
+ public static Collection getClasspathURLs() {
+ Collection urls = new ArrayList<>();
+ String javaClassPath = System.getProperty("java.class.path");
+ if (javaClassPath != null) {
+ for (String path : javaClassPath.split(File.pathSeparator)) {
+ try {
+ urls.add(new File(path).toURI().toURL());
+ } catch (Exception e) {
+ if (Reflections.log != null) {
+ Reflections.log.warn("Could not get URL", e);
+ }
+ }
+ }
+ }
+ return fixURLs(urls);
+ }
+
+ /**
+ * Fixes slowdowns which {@link URL}s may cause.
+ *
+ * Visit this blog post for more information.
+ *
+ * @param urls unfixed urls
+ * @return fixed urls
+ * @since v1-alpha2
+ */
+ @NotNull
+ public static Collection fixURLs(@NotNull Collection urls) {
+ Map distinct = new LinkedHashMap<>(urls.size());
+ for (URL url : urls) {
+ distinct.put(url.toExternalForm(), url);
+ }
+ return distinct.values();
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/reflection/Reflect.java b/base/src/main/java/de/staropensource/sosengine/base/reflection/Reflect.java
new file mode 100644
index 0000000..f43f7a3
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/reflection/Reflect.java
@@ -0,0 +1,69 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.reflection;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * The class you'd likely want to use for reflection.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public final class Reflect {
+ /**
+ * Allows reflecting on some class.
+ *
+ * @param clazz class to reflect on
+ * @return new {@link ReflectionClass}
+ * @since v1-alpha2
+ */
+ @NotNull
+ public static ReflectionClass reflectOn(@NotNull Class> clazz) {
+ return new ReflectionClass(clazz);
+ }
+
+ /**
+ * Allows reflecting on some method.
+ *
+ * @param method method to reflect on
+ * @return new {@link ReflectionMethod}
+ * @since v1-alpha2
+ */
+ @NotNull
+ public static ReflectionMethod reflectOn(@NotNull Method method) {
+ return new ReflectionMethod(method);
+ }
+
+ /**
+ * Allows reflecting on some field.
+ *
+ * @param field field to reflect on
+ * @return new {@link ReflectionField}
+ * @since v1-alpha2
+ */
+ @NotNull
+ public static ReflectionField reflectOn(@NotNull Field field) {
+ return new ReflectionField(field);
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionClass.java b/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionClass.java
new file mode 100644
index 0000000..e23268c
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionClass.java
@@ -0,0 +1,214 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.reflection;
+
+import de.staropensource.sosengine.base.exceptions.UnexpectedCheckEndException;
+import de.staropensource.sosengine.base.exceptions.reflection.IncompatibleTypeReflection;
+import de.staropensource.sosengine.base.exceptions.reflection.InvalidFieldException;
+import de.staropensource.sosengine.base.exceptions.reflection.InvalidMethodException;
+import de.staropensource.sosengine.base.types.reflection.ClassType;
+import de.staropensource.sosengine.base.types.reflection.VisibilityModifier;
+import lombok.Getter;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+
+/**
+ * Allows reflection on classes.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" })
+@Getter
+public final class ReflectionClass {
+ /**
+ * The class to reflect on.
+ *
+ * @since v1-alpha2
+ *
+ * -- GETTER --
+ * Returns the class to reflect on.
+ *
+ * @return class reference
+ * @since v1-alpha2
+ */
+ private final Class> clazz;
+
+ /**
+ * Constructs this class.
+ *
+ * @param clazz class to reflect on
+ * @since v1-alpha2
+ */
+ public ReflectionClass(@NotNull Class> clazz) {
+ this.clazz = clazz;
+ }
+
+ /**
+ * Returns the class name.
+ *
+ * @return class name
+ * @since v1-alpha2
+ */
+ public String getName() {
+ return clazz.getName().replace(getPackage() + ".", "");
+ }
+
+ /**
+ * Returns the package path.
+ *
+ * @return package path
+ * @since v1-alpha2
+ */
+ public String getPackage() {
+ return clazz.getPackage().getName();
+ }
+
+ /**
+ * Returns the full path, including the package and class name.
+ *
+ * @return full path
+ * @since v1-alpha2
+ */
+ public String getPath() {
+ return clazz.getName();
+ }
+
+ /**
+ * Returns the type of this class.
+ *
+ * @return class type
+ * @since v1-alpha2
+ */
+ public ClassType getType() {
+ if (clazz.isLocalClass() || clazz.isMemberClass() || clazz.isAnonymousClass())
+ return ClassType.CLASS;
+ else if (clazz.isInterface())
+ return ClassType.INTERFACE;
+ else if (clazz.isEnum())
+ return ClassType.ENUM;
+ else if (clazz.isRecord())
+ return ClassType.RECORD;
+ else {
+ // Class#isUnnamedClass is in preview, use reflection to invoke method
+ try {
+ if ((boolean) clazz.getMethod("isUnnamedClass").invoke(clazz))
+ return ClassType.CLASS;
+ } catch (Exception ignored) {}
+ }
+ return ClassType.UNKNOWN;
+ }
+
+ /**
+ * Returns the class' visibility.
+ *
+ * @return class visibility
+ * @throws UnexpectedCheckEndException if the class is neither public, protected or private (should be impossible)
+ * @since v1-alpha2
+ */
+ public VisibilityModifier getVisibility() throws UnexpectedCheckEndException {
+ if (Modifier.isPublic(clazz.getModifiers()))
+ return VisibilityModifier.PUBLIC;
+ else if (Modifier.isProtected(clazz.getModifiers()))
+ return VisibilityModifier.PROTECTED;
+ else if (Modifier.isPrivate(clazz.getModifiers()))
+ return VisibilityModifier.PRIVATE;
+ else
+ throw new UnexpectedCheckEndException("checking the visibility of a class with modifiers " + clazz.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code final} modifier is present.
+ *
+ * @return presence of the {@code final} modifier
+ * @since v1-alpha2
+ */
+ public boolean isFinal() throws IncompatibleTypeReflection {
+ if (getType() != ClassType.CLASS)
+ throw new IncompatibleTypeReflection("isFinal", getType(), new ClassType[]{ ClassType.CLASS });
+ return Modifier.isFinal(clazz.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code abstract} modifier is present.
+ *
+ * @return presence of the {@code abstract} modifier
+ * @since v1-alpha2
+ */
+ public boolean isAbstract() throws IncompatibleTypeReflection {
+ if (getType() != ClassType.CLASS)
+ throw new IncompatibleTypeReflection("isAbstract", getType(), new ClassType[]{ClassType.CLASS});
+ return Modifier.isAbstract(clazz.getModifiers());
+ }
+
+ /**
+ * Returns all annotations this class has.
+ *
+ * @return array of all annotations
+ * @since v1-alpha2
+ */
+ public Annotation[] getAnnotations() {
+ return clazz.getAnnotations();
+ }
+
+ /**
+ * Returns the specified annotation or {@code null} if not found.
+ *
+ * @param annotation class
+ * @return annotation or {@code null}
+ * @since v1-alpha2
+ */
+ public Annotation getAnnotation(@NotNull Class annotation) {
+ return clazz.getAnnotation(annotation);
+ }
+
+ /**
+ * Returns the specified method.
+ *
+ * @param methodName name of the method
+ * @return new {@link ReflectionMethod} instance
+ * @throws InvalidMethodException if the method does not exist
+ * @since v1-alpha2
+ */
+ public ReflectionMethod getMethod(@NotNull String methodName, Class>... methodArguments) throws InvalidMethodException {
+ try {
+ return new ReflectionMethod(clazz.getDeclaredMethod(methodName, methodArguments));
+ } catch (NoSuchMethodException exception) {
+ throw new InvalidMethodException(this, methodName);
+ }
+ }
+
+ /**
+ * Returns the specified field.
+ *
+ * @param fieldName name of the field
+ * @return new {@link ReflectionField} instance
+ * @throws InvalidFieldException if the field does not exist
+ * @since v1-alpha2
+ */
+ public ReflectionField getField(@NotNull String fieldName) throws InvalidFieldException {
+ try {
+ return new ReflectionField(clazz.getField(fieldName));
+ } catch (NoSuchFieldException exception) {
+ throw new InvalidFieldException(this, fieldName);
+ }
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionField.java b/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionField.java
new file mode 100644
index 0000000..2978aae
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionField.java
@@ -0,0 +1,368 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.reflection;
+
+import de.staropensource.sosengine.base.exceptions.UnexpectedCheckEndException;
+import de.staropensource.sosengine.base.exceptions.UnexpectedThrowableException;
+import de.staropensource.sosengine.base.exceptions.reflection.NoAccessException;
+import de.staropensource.sosengine.base.internal.reflection.ReflectionAccessWidener;
+import de.staropensource.sosengine.base.types.reflection.VisibilityModifier;
+import lombok.Getter;
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * Allows reflection on methods.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" })
+@Getter
+public final class ReflectionField {
+
+ /**
+ * The class the method is contained in.
+ *
+ * @since v1-alpha2
+ *
+ * -- GETTER --
+ * Returns the class the method is contained in.
+ *
+ * @return parent class
+ * @since v1-alpha2
+ */
+ private final Class> parentClass;
+
+ /**
+ * The field to reflect on.
+ *
+ * @since v1-alpha2
+ *
+ * -- GETTER --
+ * Returns the field to reflect on.
+ *
+ * @return field reference
+ * @since v1-alpha2
+ */
+ private final Field field;
+
+ /**
+ * Constructs this class.
+ *
+ * @param field field to reflect on
+ */
+ public ReflectionField(@NotNull Field field) {
+ parentClass = null;
+ this.field = field;
+ }
+
+ /**
+ * Constructs this class.
+ *
+ * @param parentClass clazz this field is contained in
+ * @param field field to reflect on
+ */
+ public ReflectionField(@NotNull Class> parentClass, @NotNull Field field) {
+ this.parentClass = parentClass;
+ this.field = field;
+ }
+
+ /**
+ * Returns the name of the field.
+ *
+ * @return field name
+ * @since v1-alpha2
+ */
+ public String getName() {
+ return field.getName();
+ }
+
+ /**
+ * Returns the field's visibility.
+ *
+ * @return field visibility
+ * @throws UnexpectedCheckEndException if the field is neither public, protected or private (should be impossible)
+ * @since v1-alpha2
+ */
+ public VisibilityModifier getVisibility() throws UnexpectedCheckEndException {
+ if (Modifier.isPublic(field.getModifiers()))
+ return VisibilityModifier.PUBLIC;
+ else if (Modifier.isProtected(field.getModifiers()))
+ return VisibilityModifier.PROTECTED;
+ else if (Modifier.isPrivate(field.getModifiers()))
+ return VisibilityModifier.PRIVATE;
+ else
+ throw new UnexpectedCheckEndException("checking the visibility of a field with modifiers " + field.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code final} modifier is present.
+ *
+ * @return presence of the {@code final} modifier
+ * @since v1-alpha2
+ */
+ public boolean isFinal() {
+ return Modifier.isFinal(field.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code static} modifier is present.
+ *
+ * @return presence of the {@code static} modifier
+ * @since v1-alpha2
+ */
+ public boolean isStatic() {
+ return Modifier.isStatic(field.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code transient} modifier is present.
+ *
+ * @return presence of the {@code transient} modifier
+ * @since v1-alpha2
+ */
+ public boolean isTransient() {
+ return Modifier.isTransient(field.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code volatile} modifier is present.
+ *
+ * @return presence of the {@code volatile} modifier
+ * @since v1-alpha2
+ */
+ public boolean isVolatile() {
+ return Modifier.isVolatile(field.getModifiers());
+ }
+
+ /**
+ * Updates the presence of the {@code final} modifier.
+ *
+ * @param newValue new presence of the {@code final} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setFinal(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isFinal() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = field.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside field " + field.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(field, modifiersField.getInt(field) & ~Modifier.FINAL);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Updates the presence of the {@code static} modifier.
+ *
+ * @param newValue new presence of the {@code static} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setStatic(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isStatic() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = field.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside field " + field.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(field, modifiersField.getInt(field) & ~Modifier.STATIC);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Updates the presence of the {@code transient} modifier.
+ *
+ * @param newValue new presence of the {@code transient} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setTransient(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isTransient() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = field.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside field " + field.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(field, modifiersField.getInt(field) & ~Modifier.TRANSIENT);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Updates the presence of the {@code volatile} modifier.
+ *
+ * @param newValue new presence of the {@code volatile} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setVolatile(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isVolatile() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = field.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside field " + field.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(field, modifiersField.getInt(field) & ~Modifier.VOLATILE);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Returns all annotations this field has.
+ *
+ * @return array of all annotations
+ * @since v1-alpha2
+ */
+ public Annotation[] getAnnotations() {
+ return field.getAnnotations();
+ }
+
+ /**
+ * Returns the specified annotation or {@code null} if not found.
+ *
+ * @param annotation class
+ * @return annotation or {@code null}
+ * @since v1-alpha2
+ */
+ public Annotation getAnnotation(@NotNull Class annotation) {
+ return field.getAnnotation(annotation);
+ }
+
+ /**
+ * Returns the type this field has.
+ *
+ * @return field type
+ * @since v1-alpha2
+ */
+ public Object getType() {
+ return field.getType();
+ }
+
+ /**
+ * Returns the generic type this field has.
+ *
+ * You can read the between {@code getType} and {@code getGenericType} here.
+ *
+ * @return field type
+ * @since v1-alpha2
+ */
+ public Object getGenericType() {
+ return field.getGenericType();
+ }
+
+ /**
+ * Updates the field with a new value.
+ *
+ * @param newValue new value
+ * @since v1-alpha2
+ */
+ public void setValue(Object newValue) throws NoAccessException {
+ try {
+ field.set(parentClass, newValue);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", getName());
+ }
+ }
+
+ /**
+ * Updates the field with a new value.
+ *
+ * @return field's value
+ * @since v1-alpha2
+ */
+ public Object getValue() throws NoAccessException {
+ try {
+ return field.get(parentClass);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", getName());
+ }
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionMethod.java b/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionMethod.java
new file mode 100644
index 0000000..83ac2ea
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/reflection/ReflectionMethod.java
@@ -0,0 +1,385 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.reflection;
+
+import de.staropensource.sosengine.base.exceptions.UnexpectedCheckEndException;
+import de.staropensource.sosengine.base.exceptions.UnexpectedThrowableException;
+import de.staropensource.sosengine.base.exceptions.reflection.NoAccessException;
+import de.staropensource.sosengine.base.internal.reflection.ReflectionAccessWidener;
+import de.staropensource.sosengine.base.types.reflection.VisibilityModifier;
+import lombok.Getter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Allows reflection on methods.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" })
+@Getter
+public final class ReflectionMethod {
+ /**
+ * The class the method is contained in.
+ *
+ * @since v1-alpha2
+ *
+ * -- GETTER --
+ * Returns the class the method is contained in.
+ *
+ * @return parent class
+ * @since v1-alpha2
+ */
+ private final Class> parentClass;
+
+ /**
+ * The method to reflect on.
+ *
+ * @since v1-alpha2
+ *
+ * -- GETTER --
+ * Returns the method to reflect on.
+ *
+ * @return method reference
+ * @since v1-alpha2
+ */
+ private final Method method;
+
+ /**
+ * Constructs this class.
+ *
+ * @param method method to reflect on
+ */
+ public ReflectionMethod(@NotNull Method method) {
+ parentClass = null;
+ this.method = method;
+ }
+
+ /**
+ * Constructs this class.
+ *
+ * @param parentClass clazz this method is contained in
+ * @param method method to reflect on
+ */
+ public ReflectionMethod(@NotNull Class> parentClass, @NotNull Method method) {
+ this.parentClass = parentClass;
+ this.method = method;
+ }
+
+ /**
+ * Returns the name of the method.
+ *
+ * @return method name
+ * @since v1-alpha2
+ */
+ public String getName() {
+ return method.getName();
+ }
+
+ /**
+ * Returns the method's visibility.
+ *
+ * @return method visibility
+ * @throws UnexpectedCheckEndException if the method is neither public, protected or private (should be impossible)
+ * @since v1-alpha2
+ */
+ public VisibilityModifier getVisibility() throws UnexpectedCheckEndException {
+ if (Modifier.isPublic(method.getModifiers()))
+ return VisibilityModifier.PUBLIC;
+ else if (Modifier.isProtected(method.getModifiers()))
+ return VisibilityModifier.PROTECTED;
+ else if (Modifier.isPrivate(method.getModifiers()))
+ return VisibilityModifier.PRIVATE;
+ else
+ throw new UnexpectedCheckEndException("checking the visibility of a method with modifiers " + method.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code final} modifier is present.
+ *
+ * @return presence of the {@code final} modifier
+ * @since v1-alpha2
+ */
+ public boolean isFinal() {
+ return Modifier.isFinal(method.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code static} modifier is present.
+ *
+ * @return presence of the {@code static} modifier
+ * @since v1-alpha2
+ */
+ public boolean isStatic() {
+ return Modifier.isStatic(method.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code abstract} modifier is present.
+ *
+ * @return presence of the {@code abstract} modifier
+ * @since v1-alpha2
+ */
+ public boolean isAbstract() {
+ return Modifier.isAbstract(method.getModifiers());
+ }
+
+ /**
+ * Checks if the {@code synchronized} modifier is present.
+ *
+ * @return presence of the {@code synchronized} modifier
+ * @since v1-alpha2
+ */
+ public boolean isSynchronized() {
+ return Modifier.isSynchronized(method.getModifiers());
+ }
+
+ /**
+ * Updates the presence of the {@code final} modifier.
+ *
+ * @param newValue new presence of the {@code final} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setFinal(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isFinal() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = method.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside method " + method.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(method, modifiersField.getInt(method) & ~Modifier.FINAL);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Updates the presence of the {@code static} modifier.
+ *
+ * @param newValue new presence of the {@code static} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setStatic(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isStatic() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = method.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside method " + method.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(method, modifiersField.getInt(method) & ~Modifier.STATIC);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Updates the presence of the {@code abstract} modifier.
+ *
+ * @param newValue new presence of the {@code abstract} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setAbstract(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isAbstract() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = method.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside method " + method.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(method, modifiersField.getInt(method) & ~Modifier.ABSTRACT);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Updates the presence of the {@code synchronized} modifier.
+ *
+ * @param newValue new presence of the {@code synchronized} modifier
+ * @throws UnexpectedThrowableException if the {@code modifiers} field is missing
+ * @throws NoAccessException if access to the {@code modifiers} field has been denied
+ * @since v1-alpha2
+ */
+ public void setSynchronized(boolean newValue) throws UnexpectedThrowableException, NoAccessException {
+ // Don't do anything if the new value already matches the current value
+ if (isSynchronized() == newValue)
+ return;
+
+ // Unlock modifications
+ int modifiedModifiers = ReflectionAccessWidener.unlockModifications(this);
+
+ // Get 'modifiers' field
+ Field modifiersField;
+ try {
+ modifiersField = method.getClass().getDeclaredField("modifiers");
+ } catch (NoSuchFieldException exception) {
+ throw new UnexpectedThrowableException(exception, "Field \"modifiers\" not present inside method " + method.getName());
+ }
+
+ // Update 'modifiers' field
+ try {
+ modifiersField.setInt(method, modifiersField.getInt(method) & ~Modifier.SYNCHRONIZED);
+ } catch (IllegalAccessException e) {
+ throw new NoAccessException("field", "modifiers");
+ }
+
+ // Lock modifications
+ ReflectionAccessWidener.lockModifications(this, modifiedModifiers);
+ }
+
+ /**
+ * Returns all annotations this method has.
+ *
+ * @return array of all annotations
+ * @since v1-alpha2
+ */
+ public Annotation[] getAnnotations() {
+ return method.getAnnotations();
+ }
+
+ /**
+ * Returns the specified annotation or {@code null} if not found.
+ *
+ * @param annotation class
+ * @return annotation or {@code null}
+ * @since v1-alpha2
+ */
+ public Annotation getAnnotation(@NotNull Class annotation) {
+ return method.getAnnotation(annotation);
+ }
+
+ /**
+ * Returns the method's return type.
+ *
+ * @return method return type
+ * @since v1-alpha2
+ */
+ @NotNull
+ public Class> getReturnType() {
+ return method.getReturnType();
+ }
+
+ /**
+ * Returns the method's generic return type.
+ *
+ * You can read the between {@code getReturnType} and {@code getGenericReturnType} here.
+ *
+ * @return field type
+ * @since v1-alpha2
+ */
+ public Object getGenericReturnType() {
+ return method.getGenericReturnType();
+ }
+
+ /**
+ * Invokes the method.
+ *
+ * @return method return value
+ * @throws NoAccessException if access to the method has been denied
+ * @throws InvocationTargetException covers exceptions thrown by the method
+ * @throws UnexpectedThrowableException if the {@code modifiers} field could not be found
+ * @since v1-alpha2
+ */
+ @Nullable
+ public Object invoke() throws NoAccessException, InvocationTargetException, UnexpectedThrowableException {
+ return invoke(new Object[0]);
+ }
+
+ /**
+ * Invokes the method.
+ *
+ * @return method return value
+ * @throws NoAccessException if access to the method has been denied
+ * @throws InvocationTargetException covers exceptions thrown by the method
+ * @throws UnexpectedThrowableException if the {@code modifiers} field could not be found
+ * @since v1-alpha2
+ */
+ @Nullable
+ public Object invoke(Object... args) throws NoAccessException, InvocationTargetException, UnexpectedThrowableException {
+ Object returnValue;
+
+ // Allow access to method
+ ReflectionAccessWidener.allowAccess(method);
+
+ // Invoke method
+ try {
+ returnValue = method.invoke(parentClass, args);
+ } catch (IllegalAccessException exception) {
+ //ReflectionAccessWidener.lockModifications(this, updatedModifiers); // Lock method before throwing exception
+ throw new NoAccessException("method", getName());
+ }
+
+ // Return return value from method
+ return returnValue;
+ }
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/types/reflection/ClassType.java b/base/src/main/java/de/staropensource/sosengine/base/types/reflection/ClassType.java
new file mode 100644
index 0000000..264cdc8
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/types/reflection/ClassType.java
@@ -0,0 +1,63 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.types.reflection;
+
+/**
+ * Identifies a class' type.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public enum ClassType {
+ /**
+ * Identifies the class as a {@code class} class.
+ *
+ * @since v1-alpha2
+ */
+ CLASS,
+
+ /**
+ * Identifies the class as an {@code interface} class.
+ *
+ * @since v1-alpha2
+ */
+ INTERFACE,
+
+ /**
+ * Identifies the class as an {@code enum} class.
+ *
+ * @since v1-alpha2
+ */
+ ENUM,
+
+ /**
+ * Identifies the class as a {@code record} class.
+ *
+ * @since v1-alpha2
+ */
+ RECORD,
+
+ /**
+ * Identifies the class as some unknown class the engine does not yet know of.
+ *
+ * @since v1-alpha2
+ */
+ UNKNOWN,
+}
diff --git a/base/src/main/java/de/staropensource/sosengine/base/types/reflection/VisibilityModifier.java b/base/src/main/java/de/staropensource/sosengine/base/types/reflection/VisibilityModifier.java
new file mode 100644
index 0000000..603d853
--- /dev/null
+++ b/base/src/main/java/de/staropensource/sosengine/base/types/reflection/VisibilityModifier.java
@@ -0,0 +1,76 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.base.types.reflection;
+
+import de.staropensource.sosengine.base.exceptions.UnexpectedCheckEndException;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Determines which visibility a class, method or field has.
+ *
+ * @since v1-alpha2
+ */
+@SuppressWarnings({ "unused" })
+public enum VisibilityModifier {
+ /**
+ * Marks the class, method or field as public.
+ *
+ * @since v1-alpha2
+ */
+ PUBLIC,
+
+ /**
+ * Marks the class, method or field as protected.
+ *
+ * @since v1-alpha2
+ */
+ PROTECTED,
+
+ /**
+ * Marks the class, method or field as private.
+ *
+ * @since v1-alpha2
+ */
+ PRIVATE;
+
+ /**
+ * Converts the visibility into a usable modifier bit.
+ *
+ * @return int
+ * @since v1-alpha2
+ */
+ public int getModifier() {
+ switch (this) {
+ case PUBLIC -> {
+ return Modifier.PUBLIC;
+ }
+ case PROTECTED -> {
+ return Modifier.PROTECTED;
+ }
+ case PRIVATE -> {
+ return Modifier.PRIVATE;
+ }
+ case null, default -> {
+ throw new UnexpectedCheckEndException("converting the visibility into a modifier");
+ }
+ }
+ }
+}
diff --git a/base/src/main/java/module-info.java b/base/src/main/java/module-info.java
index fd2b073..5de2afa 100644
--- a/base/src/main/java/module-info.java
+++ b/base/src/main/java/module-info.java
@@ -16,15 +16,18 @@ module sosengine.base {
exports de.staropensource.sosengine.base.annotations;
exports de.staropensource.sosengine.base.classes;
exports de.staropensource.sosengine.base.classes.helpers;
- exports de.staropensource.sosengine.base.types.logging;
exports de.staropensource.sosengine.base.data.info;
exports de.staropensource.sosengine.base.data.versioning;
exports de.staropensource.sosengine.base.events;
exports de.staropensource.sosengine.base.exceptions;
+ exports de.staropensource.sosengine.base.exceptions.reflection;
exports de.staropensource.sosengine.base.logging;
exports de.staropensource.sosengine.base.logging.implementation;
+ exports de.staropensource.sosengine.base.reflection;
exports de.staropensource.sosengine.base.types;
exports de.staropensource.sosengine.base.types.immutable;
+ exports de.staropensource.sosengine.base.types.logging;
+ exports de.staropensource.sosengine.base.types.reflection;
exports de.staropensource.sosengine.base.types.vectors;
exports de.staropensource.sosengine.base.utility;
exports de.staropensource.sosengine.base.utility.converter;
@@ -39,15 +42,18 @@ module sosengine.base {
opens de.staropensource.sosengine.base.annotations;
opens de.staropensource.sosengine.base.classes;
opens de.staropensource.sosengine.base.classes.helpers;
- opens de.staropensource.sosengine.base.types.logging;
opens de.staropensource.sosengine.base.data.info;
opens de.staropensource.sosengine.base.data.versioning;
opens de.staropensource.sosengine.base.events;
opens de.staropensource.sosengine.base.exceptions;
+ opens de.staropensource.sosengine.base.exceptions.reflection;
opens de.staropensource.sosengine.base.logging;
opens de.staropensource.sosengine.base.logging.implementation;
+ opens de.staropensource.sosengine.base.reflection;
opens de.staropensource.sosengine.base.types;
opens de.staropensource.sosengine.base.types.immutable;
+ opens de.staropensource.sosengine.base.types.logging;
+ opens de.staropensource.sosengine.base.types.reflection;
opens de.staropensource.sosengine.base.types.vectors;
opens de.staropensource.sosengine.base.utility;
opens de.staropensource.sosengine.base.utility.converter;