Skip to content

Commit

Permalink
Global and Local Inheritance (#11)
Browse files Browse the repository at this point in the history
* feat: local property inheritance

* feat: global inheritance

* fix: inheritance with no reference

* fix: inheritance with private constructor

* fix: additional changes required for local inheritance

* fix: make use of kotlin's instance variable
  • Loading branch information
ShindouMihou authored Jan 20, 2023
1 parent 4a55978 commit 0db39d1
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 3 deletions.
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();
}

0 comments on commit 0db39d1

Please sign in to comment.