Skip to content

Commit

Permalink
Add ConfigName annotation to specify the path to a value in config
Browse files Browse the repository at this point in the history
  • Loading branch information
boxbeam committed Dec 22, 2021
1 parent a0a53ac commit 3f28065
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 141 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: 2021-12-21 20:47
version: 2021-12-22 04:41
author: Redempt
api-version: 1.13
load: STARTUP
103 changes: 103 additions & 0 deletions src/redempt/redlib/config/ConfigField.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package redempt.redlib.config;

import redempt.redlib.config.annotations.ConfigName;

import java.lang.reflect.Field;

/**
* Wraps a Field and stores the name which should be used to store its value in config
* @author Redempt
*/
public class ConfigField {

private Field field;
private String name;

/**
* Constructs a ConfigField from a field
* @param field The Field
*/
public ConfigField(Field field) {
this.field = field;
ConfigName annotation = field.getAnnotation(ConfigName.class);
name = annotation == null ? field.getName() : annotation.value();
}

/**
* @return The wrapped Field
*/
public Field getField() {
return field;
}

/**
* Attemps to set the value of the field for the target object to the value
* @param target The target object
* @param value The value
*/
public void set(Object target, Object value) {
try {
field.set(target, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

/**
* Attemps to set the field in a static context to the given value
* @param value The value
*/
public void set(Object value) {
set(null, value);
}

/**
* Attempts to get the field's value for a given object
* @param target The target object to get the value from
* @return The value
*/
public Object get(Object target) {
try {
return field.get(target);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}

/**
* Attemps to get the value of the field in a static context
* @return The value
*/
public Object get() {
return get(null);
}

/**
* @return The name for the field that should be used to store config values
*/
public String getName() {
return name;
}

/**
* Sets the name of this ConfigField
* @param name The name to set
*/
public void setName(String name) {
this.name = name;
}

public int hashCode() {
return field.hashCode();
}

public boolean equals(Object o) {
if (!(o instanceof ConfigField)) {
return false;
}
ConfigField cf = (ConfigField) o;
return cf.field.equals(field);
}

}
3 changes: 2 additions & 1 deletion src/redempt/redlib/config/ConfigManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import redempt.redlib.config.conversion.TypeConverter;
import redempt.redlib.config.data.ConfigurationSectionDataHolder;
import redempt.redlib.config.data.DataHolder;
import redempt.redlib.config.instantiation.Instantiator;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -254,7 +255,7 @@ private <T> TypeConverter<?> createConverter(ConfigType<?> type) {
if (Map.class.isAssignableFrom(type.getType())) {
return MapConverter.create(this, type);
}
if (type.getType().isAnnotationPresent(ConfigMappable.class) || type.getType().getSuperclass().getName().equals("java.lang.Record")) {
if (type.getType().isAnnotationPresent(ConfigMappable.class) || Instantiator.isRecord(type.getType())) {
return ObjectConverter.create(this, type);
}
return NativeConverter.create();
Expand Down
2 changes: 2 additions & 0 deletions src/redempt/redlib/config/annotations/ConfigMappable.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package redempt.redlib.config.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand All @@ -11,5 +12,6 @@
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigMappable {
}
16 changes: 16 additions & 0 deletions src/redempt/redlib/config/annotations/ConfigName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package redempt.redlib.config.annotations;

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

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigName {

String value();

}
2 changes: 2 additions & 0 deletions src/redempt/redlib/config/annotations/ConfigPostInit.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package redempt.redlib.config.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand All @@ -11,5 +12,6 @@
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigPostInit {
}
60 changes: 8 additions & 52 deletions src/redempt/redlib/config/conversion/ObjectConverter.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
package redempt.redlib.config.conversion;

import org.bukkit.configuration.ConfigurationSection;
import redempt.redlib.config.ConfigField;
import redempt.redlib.config.ConfigManager;
import redempt.redlib.config.ConfigType;
import redempt.redlib.config.annotations.ConfigPath;
import redempt.redlib.config.annotations.ConfigPostInit;
import redempt.redlib.config.data.DataHolder;
import redempt.redlib.config.instantiation.InstantiationInfo;
import redempt.redlib.config.instantiation.FieldSummary;
import redempt.redlib.config.instantiation.Instantiator;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* A converter which builds objects from configuration sections
Expand All @@ -34,52 +28,18 @@ public static <T> TypeConverter<T> create(ConfigManager manager, ConfigType<?> t
if (type.getType().isInterface() || Modifier.isAbstract(type.getType().getModifiers())) {
throw new IllegalStateException("Cannot automatically convert abstract classe or interface " + type.getType());
}
List<Field> fields = new ArrayList<>();
Map<Field, TypeConverter<?>> converters = new HashMap<>();
Instantiator instantiator = Instantiator.getInstantiator(type.getType());
Field configPath = null;
StringConverter<?> configPathConverter = null;
for (Field field : type.getType().getDeclaredFields()) {
int mod = field.getModifiers();
if (Modifier.isTransient(mod) || Modifier.isStatic(mod)) {
continue;
}
field.setAccessible(true);
if (field.isAnnotationPresent(ConfigPath.class)) {
configPath = field;
configPathConverter = manager.getStringConverter(ConfigType.get(configPath));
continue;
}
fields.add(field);
ConfigType<?> fieldType = ConfigType.get(field);
converters.put(field, manager.getConverter(fieldType));
}
Method postInit = null;
for (Method method : type.getType().getDeclaredMethods()) {
int mod = method.getModifiers();
if (Modifier.isStatic(mod)) {
continue;
}
if (method.isAnnotationPresent(ConfigPostInit.class)) {
if (method.getParameterCount() != 0) {
throw new IllegalStateException("Post-init method must have no parameters: " + method);
}
method.setAccessible(true);
postInit = method;
break;
}
}
InstantiationInfo info = new InstantiationInfo(postInit, configPath, configPathConverter);
FieldSummary summary = FieldSummary.getFieldSummary(manager, type.getType(), false);
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<>();
for (Field field : fields) {
Object value = converters.get(field).loadFrom(newSection, field.getName(), null);
for (ConfigField field : summary.getFields()) {
Object value = summary.getConverters().get(field).loadFrom(newSection, field.getName(), null);
objs.add(value);
}
return (T) instantiator.instantiate(manager, currentValue, type.getType(), fields, objs, path, info);
return (T) instantiator.instantiate(manager, currentValue, type.getType(), objs, path, summary);
}

@Override
Expand All @@ -93,12 +53,8 @@ public void saveTo(T t, DataHolder section, String path, boolean overwrite) {
return;
}
DataHolder newSection = path == null ? section : section.createSubsection(path);
try {
for (Field field : fields) {
saveWith(converters.get(field), field.get(t), newSection, field.getName(), overwrite);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
for (ConfigField field : summary.getFields()) {
saveWith(summary.getConverters().get(field), field.get(t), newSection, field.getName(), overwrite);
}
}
};
Expand Down
34 changes: 9 additions & 25 deletions src/redempt/redlib/config/conversion/StaticRootConverter.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package redempt.redlib.config.conversion;

import org.bukkit.configuration.ConfigurationSection;
import redempt.redlib.config.ConfigField;
import redempt.redlib.config.ConfigManager;
import redempt.redlib.config.ConfigType;
import redempt.redlib.config.data.DataHolder;
import redempt.redlib.config.instantiation.FieldSummary;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
Expand All @@ -26,27 +28,13 @@ public class StaticRootConverter {
* @return A static root converter
*/
public static <T> TypeConverter<T> create(ConfigManager manager, Class<?> root) {
List<Field> fields = new ArrayList<>();
Map<Field, TypeConverter<?>> converters = new HashMap<>();
for (Field field : root.getDeclaredFields()) {
int mod = field.getModifiers();
if (Modifier.isTransient(mod) || !Modifier.isStatic(mod)) {
continue;
}
field.setAccessible(true);
fields.add(field);
converters.put(field, manager.getConverter(ConfigType.get(field)));
}
FieldSummary summary = FieldSummary.getFieldSummary(manager, root, true);
return new TypeConverter<T>() {
@Override
public T loadFrom(DataHolder section, String path, T currentValue) {
try {
for (Field field : fields) {
Object val = converters.get(field).loadFrom(section, field.getName(), null);
field.set(null, val);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
for (ConfigField field : summary.getFields()) {
Object val = summary.getConverters().get(field).loadFrom(section, field.getName(), null);
field.set(val);
}
return null;
}
Expand All @@ -58,13 +46,9 @@ public void saveTo(T t, DataHolder section, String path) {

@Override
public void saveTo(T t, DataHolder section, String path, boolean overwrite) {
try {
for (Field field : fields) {
Object obj = field.get(null);
saveWith(converters.get(field), obj, section, field.getName(), overwrite);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
for (ConfigField field : summary.getFields()) {
Object obj = field.get();
saveWith(summary.getConverters().get(field), obj, section, field.getName(), overwrite);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package redempt.redlib.config.instantiation;

import redempt.redlib.config.ConfigField;
import redempt.redlib.config.ConfigManager;
import redempt.redlib.config.annotations.ConfigPath;

Expand Down Expand Up @@ -47,15 +48,14 @@ private ConstructorInstantiator(Constructor<?> constructor) {
* @param manager The ConfigManager handling config data
* @param target The target object, always ignored by this type of Instantiator
* @param clazz The class whose fields are being used
* @param fields The fields being worked with
* @param values The values for the fields
* @param path The path in config
* @param info Extra info about the instantiation
* @param <T> The type
* @return The constructed object
*/
@Override
public <T> T instantiate(ConfigManager manager, Object target, Class<T> clazz, List<Field> fields, List<Object> values, String path, InstantiationInfo info) {
public <T> T instantiate(ConfigManager manager, Object target, Class<T> clazz, List<Object> values, String path, FieldSummary info) {
Object[] objs = new Object[params.length];
int valuePos = 0;
for (int i = 0; i < params.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package redempt.redlib.config.instantiation;

import redempt.redlib.config.ConfigField;
import redempt.redlib.config.ConfigManager;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

Expand Down Expand Up @@ -37,22 +37,21 @@ public static <T> T instantiate(Class<T> clazz) {
* @param manager The ConfigManager handling the config data
* @param target The object to load to, or null if creating a new one
* @param clazz The class whose fields are being worked with
* @param fields The fields being worked with
* @param values The values for the fields
* @param path The path in config
* @param info Extra info used for post-instantiation steps
* @param <T> The type
* @return The instantiated object, or the input object with its fields modified
*/
@Override
public <T> T instantiate(ConfigManager manager, Object target, Class<T> clazz, List<Field> fields, List<Object> values, String path, InstantiationInfo info) {
public <T> T instantiate(ConfigManager manager, Object target, Class<T> clazz, List<Object> values, String path, FieldSummary info) {
try {
T t = target == null ? instantiate(clazz) : (T) target;
for (int i = 0; i < fields.size(); i++) {
for (int i = 0; i < info.getFields().size(); i++) {
if (values.get(i) == null) {
continue;
}
fields.get(i).set(t, values.get(i));
info.getFields().get(i).set(t, values.get(i));
}
if (info.getConfigPath() != null) {
Object pathValue = info.getConfigPathConverter().fromString(path);
Expand Down
Loading

0 comments on commit 3f28065

Please sign in to comment.