Skip to content

Commit

Permalink
Add @ConfigConstructor
Browse files Browse the repository at this point in the history
  • Loading branch information
boxbeam committed Feb 13, 2024
1 parent 0d06195 commit 7a6dd40
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 16 deletions.
2 changes: 1 addition & 1 deletion res/plugin.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: RedLib
main: redempt.redlib.RedLib
version: 2023-12-22 19:59
version: 2024-02-13 00:41
author: Redempt
api-version: 1.13
load: STARTUP
14 changes: 14 additions & 0 deletions src/redempt/redlib/config/annotations/ConfigConstructor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package redempt.redlib.config.annotations;

import java.lang.annotation.*;

/**
* Denotes a constructor which can be used to initialize the object from config.
* Parameter names in the constructor must match field names in the class.
*/
@Inherited
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigConstructor {
}
4 changes: 2 additions & 2 deletions src/redempt/redlib/config/conversion/ObjectConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ public static <T> TypeConverter<T> create(ConversionManager manager, ConfigType<
if (type.getType().isInterface() || Modifier.isAbstract(type.getType().getModifiers())) {
throw new IllegalStateException("Cannot automatically convert abstract classe or interface " + type.getType());
}
Instantiator instantiator = Instantiator.getInstantiator(type.getType());
FieldSummary summary = FieldSummary.getFieldSummary(manager, type.getType(), false);
Instantiator instantiator = Instantiator.getInstantiator(type.getType());
return new TypeConverter<T>() {
@Override
public T loadFrom(DataHolder section, String path, T currentValue) {
DataHolder newSection = path == null ? section : section.getSubsection(path);
List<Object> objs = new ArrayList<>();
List<Object> objs = new ArrayList<>(summary.getFields().size());
for (ConfigField field : summary.getFields()) {
Object value = summary.getConverters().get(field).loadFrom(newSection, field.getName(), null);
objs.add(value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
package redempt.redlib.config.instantiation;

import redempt.redlib.config.ConfigManager;
import redempt.redlib.config.ConfigField;
import redempt.redlib.config.ConversionManager;
import redempt.redlib.config.annotations.ConfigPath;
import redempt.redlib.config.data.DataHolder;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.stream.IntStream;

/**
* An instantiator used for record types which passes in all necessary fields
* @author Redempt
*/
public class ConstructorInstantiator implements Instantiator {

/**
* Attempts to create an Instantiator for a record type, or a class which has a constructor taking all its fields
* in the same order they appear in the class
* @param clazz The class to create an Instantiator for
* @param <T> The type
* @return An Instantiator
*/
public static <T> Instantiator create(Class<?> clazz) {
public static <T> Instantiator createDefault(Class<?> clazz) {
try {
Field[] fields = clazz.getDeclaredFields();
Constructor<?> constructor = clazz.getDeclaredConstructor(Arrays.stream(fields).map(Field::getType).toArray(Class<?>[]::new));
return new ConstructorInstantiator(constructor);
} catch (NoSuchMethodException e) {
e.printStackTrace();
return null;
throw new IllegalStateException("Class '" + clazz.getName() + "' does not have a constructor that takes all of its fields in order");
}
}

Expand All @@ -43,6 +41,11 @@ private ConstructorInstantiator(Constructor<?> constructor) {
this.constructor = constructor;
params = constructor.getParameters();
}

private ConstructorInstantiator(Constructor<?> constructor, int[] indices) {
this.constructor = constructor;
params = constructor.getParameters();
}

/**
* Instantiates a new object using its constructor
Expand Down
13 changes: 8 additions & 5 deletions src/redempt/redlib/config/instantiation/Instantiator.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package redempt.redlib.config.instantiation;

import redempt.redlib.config.ConfigManager;
import redempt.redlib.config.ConversionManager;
import redempt.redlib.config.annotations.ConfigConstructor;
import redempt.redlib.config.annotations.ConfigMappable;
import redempt.redlib.config.data.DataHolder;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
* A utility to instantiate objects from values loaded from config
Expand All @@ -25,11 +27,12 @@ public static boolean isRecord(Class<?> clazz) {
*/
public static Instantiator getInstantiator(Class<?> clazz) {
if (isRecord(clazz)) {
return ConstructorInstantiator.create(clazz);
return ConstructorInstantiator.createDefault(clazz);
}
if (clazz.isAnnotationPresent(ConfigMappable.class)) {
return new EmptyInstantiator();
}
Optional<Constructor<?>> constructor = Arrays.stream(clazz.getConstructors()).filter(c -> c.isAnnotationPresent(ConfigConstructor.class)).findFirst();
return constructor.map(value -> ConstructorInstantiator.createDefault(clazz)).orElseGet(EmptyInstantiator::new);
}
throw new IllegalArgumentException("Cannot create instantiator for class which is not a record type and not annotated with ConfigMappable (" + clazz + ")");
}

Expand Down

0 comments on commit 7a6dd40

Please sign in to comment.