Add basic reflection support

This commit is contained in:
JeremyStar™ 2024-07-15 13:13:35 +02:00
parent dd7bc714b9
commit 97a0218bf6
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
14 changed files with 1667 additions and 2 deletions

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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");
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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());
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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());
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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());
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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");
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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}.
* <p>
* Stolen from <a href="https://github.com/jOOQ/jOOR">the jOOR library</a>.
* All credits to them.
*
* @param accessible object to allow access to
*/
public static <T extends AccessibleObject> 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);
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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.
* <p>
* This entire class has been stolen from
* <a href="https://github.com/ronmamo/reflections">the Reflections library</a>.
* 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<URL> getClasspathURLs() {
Collection<URL> 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.
* <p>
* Visit <a href="http://michaelscharf.blogspot.co.il/2006/11/javaneturlequals-and-hashcode-make.html">this blog post</a> for more information.
*
* @param urls unfixed urls
* @return fixed urls
* @since v1-alpha2
*/
@NotNull
public static Collection<URL> fixURLs(@NotNull Collection<URL> urls) {
Map<String, URL> distinct = new LinkedHashMap<>(urls.size());
for (URL url : urls) {
distinct.put(url.toExternalForm(), url);
}
return distinct.values();
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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> 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);
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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> 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.
* <p>
* <a href="https://docs.oracle.com/javase/tutorial/reflect/member/fieldTypes.html">You can read the between {@code getType} and {@code getGenericType} here.</a>
*
* @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());
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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> 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.
* <p>
* <a href="https://docs.oracle.com/javase/tutorial/reflect/member/fieldTypes.html">You can read the between {@code getReturnType} and {@code getGenericReturnType} here.</a>
*
* @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;
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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,
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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");
}
}
}
}

View file

@ -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;