Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global and Local Inheritance #11

Merged
merged 6 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,12 @@ class NexusGlobalConfiguration internal constructor() {
* if you are using a Log4j logger but with a SLF4J bridge as the default logging adapter uses SLF4J.
*/
@Volatile var logger: NexusLoggingAdapter = NexusDefaultLoggingAdapter()

/**
* To enable global inheritance, wherein all the commands inherits properties from the class provided below, you can
* specify which class you want to be inherited and Nexus will take care of inheriting them. Including a global inheritance
* class would mean the properties of the global inheritance class will be inherited by children commands regardless of
* whether they have a local inheritance class.
*/
@JvmField @Volatile var inheritance: Any? = null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package pw.mihou.nexus.core.exceptions

import java.lang.RuntimeException

class NotInheritableException(clazz: Class<*>):
RuntimeException("${clazz.name} is not an inheritable class, ensure that there is at least one empty constructor, or no constructors at all.")
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package pw.mihou.nexus.core.reflective.core;

import pw.mihou.nexus.Nexus;
import pw.mihou.nexus.core.exceptions.NotInheritableException;
import pw.mihou.nexus.core.reflective.annotations.Required;
import pw.mihou.nexus.core.reflective.annotations.Share;
import pw.mihou.nexus.core.reflective.annotations.WithDefault;
import pw.mihou.nexus.core.reflective.facade.NexusReflectiveVariableFacade;
import pw.mihou.nexus.features.command.core.NexusCommandCore;
import pw.mihou.nexus.features.inheritance.Inherits;

import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class NexusReflectiveVariableCore implements NexusReflectiveVariableFacade {
Expand All @@ -15,9 +21,9 @@ public class NexusReflectiveVariableCore implements NexusReflectiveVariableFacad
private final HashMap<String, Object> sharedFields = new HashMap<>();

public NexusReflectiveVariableCore(Object object, NexusCommandCore core) {
// We'll collect all the fields with the WithDefault annotation from the reference class first.
// then utilize those fields when we need a default value. Please ensure that the field always
// has a value beforehand.
// We'll collect all the fields with the WithDefault annotation from the reference class first.
// then utilize those fields when we need a default value. Please ensure that the field always
// has a value beforehand.

Arrays.stream(NexusCommandCore.class.getDeclaredFields())
.filter(field -> field.isAnnotationPresent(WithDefault.class))
Expand All @@ -33,6 +39,52 @@ public NexusReflectiveVariableCore(Object object, NexusCommandCore core) {
}
});

Object globalParent = Nexus.getConfiguration().global.inheritance;
if (globalParent != null) {
Class<?> parent = globalParent.getClass();
prepareInheritance(parent, object, globalParent);
}

if (object.getClass().isAnnotationPresent(Inherits.class)) {
Class<?> parent = object.getClass().getAnnotation(Inherits.class).value();
try {
Object inheritanceReference;

Field localInstance = null;

try {
localInstance = parent.getField("INSTANCE");
} catch (NoSuchFieldException ignored) {}

if (localInstance != null) {
inheritanceReference = localInstance.get(null);
} else {
Constructor<?> constructor;
if (parent.getConstructors().length != 0) {
constructor = Arrays.stream(parent.getConstructors())
.filter(construct -> construct.getParameterCount() == 0)
.findFirst()
.orElse(null);

if (constructor == null) {
throw new NotInheritableException(parent);
}

} else {
constructor = parent.getDeclaredConstructor();
}

constructor.setAccessible(true);
inheritanceReference = constructor.newInstance();
}

prepareInheritance(parent, object, inheritanceReference);
} catch (InvocationTargetException | InstantiationException | IllegalAccessException |
NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

// After collecting all the defaults, we can start bootstrapping the fields HashMap.
Arrays.stream(object.getClass().getDeclaredFields()).forEach(field -> {
field.setAccessible(true);
Expand Down Expand Up @@ -74,6 +126,29 @@ public NexusReflectiveVariableCore(Object object, NexusCommandCore core) {
});
}

private void prepareInheritance(Class<?> parent, Object command, Object inheritanceReference) {
Arrays.stream(parent.getDeclaredFields())
.forEach(field -> {
field.setAccessible(true);
try {
Object obj = field.get(inheritanceReference);
if (obj == null) {
return;
}

if (field.isAnnotationPresent(Share.class)) {
sharedFields.put(field.getName().toLowerCase(), obj);
return;
}

fields.put(field.getName().toLowerCase(), field.get(inheritanceReference));
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new IllegalStateException("Nexus was unable to complete variable inheritance stage for class: " + command.getClass().getName());
}
});
}

@Override
@SuppressWarnings("unchecked")
public <R> Optional<R> get(String field) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/pw/mihou/nexus/features/inheritance/Inherits.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package pw.mihou.nexus.features.inheritance;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inherits {
Class<?> value();
}