From d45dff1490b87d86c71847badc4c14bf544eba44 Mon Sep 17 00:00:00 2001 From: KrLite <68179735+KrLite@users.noreply.github.com> Date: Thu, 2 May 2024 15:51:41 +0800 Subject: [PATCH] specs! --- .../annotation/SpecInRangeByte.java | 16 ++ .../annotation/SpecInRangeDouble.java | 3 + .../annotation/SpecInRangeInt.java | 3 + .../annotation/SpecInRangeLong.java | 3 + .../annotation/SpecInRangeShort.java | 16 ++ .../converter/FloatToDoubleConverter.java | 1 - .../nightautoconfig/spec/SpecBuilder.java | 145 ++++++++++++++---- 7 files changed, 158 insertions(+), 29 deletions(-) create mode 100644 src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeByte.java create mode 100644 src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeShort.java diff --git a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeByte.java b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeByte.java new file mode 100644 index 0000000..5c4511e --- /dev/null +++ b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeByte.java @@ -0,0 +1,16 @@ +package band.kessokuteatime.nightautoconfig.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SpecInRangeByte { + byte min() default 0; + byte max() default Byte.MAX_VALUE; + + List> associatedTypes = List.of(Byte.class, byte.class); +} diff --git a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeDouble.java b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeDouble.java index e707cff..d7710dc 100644 --- a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeDouble.java +++ b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeDouble.java @@ -4,10 +4,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.List; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface SpecInRangeDouble { double min() default 0; double max() default Double.MAX_VALUE; + + List> associatedTypes = List.of(Double.class, double.class, Float.class, float.class); } diff --git a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeInt.java b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeInt.java index 7f0a88a..0bca4bc 100644 --- a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeInt.java +++ b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeInt.java @@ -4,10 +4,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.List; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface SpecInRangeInt { int min() default 0; int max() default Integer.MAX_VALUE; + + List> associatedTypes = List.of(Integer.class, int.class); } diff --git a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeLong.java b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeLong.java index d6f5bba..2f8bcc7 100644 --- a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeLong.java +++ b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeLong.java @@ -4,10 +4,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.List; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface SpecInRangeLong { long min() default 0; long max() default Integer.MAX_VALUE; + + List> associatedTypes = List.of(Long.class, long.class); } diff --git a/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeShort.java b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeShort.java new file mode 100644 index 0000000..2067d4b --- /dev/null +++ b/src/main/java/band/kessokuteatime/nightautoconfig/annotation/SpecInRangeShort.java @@ -0,0 +1,16 @@ +package band.kessokuteatime.nightautoconfig.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SpecInRangeShort { + short min() default 0; + short max() default Short.MAX_VALUE; + + List> associatedTypes = List.of(Short.class, short.class); +} diff --git a/src/main/java/band/kessokuteatime/nightautoconfig/converter/FloatToDoubleConverter.java b/src/main/java/band/kessokuteatime/nightautoconfig/converter/FloatToDoubleConverter.java index b1fbbc6..33c6a74 100644 --- a/src/main/java/band/kessokuteatime/nightautoconfig/converter/FloatToDoubleConverter.java +++ b/src/main/java/band/kessokuteatime/nightautoconfig/converter/FloatToDoubleConverter.java @@ -2,7 +2,6 @@ import com.electronwill.nightconfig.core.conversion.Converter; -@Deprecated public class FloatToDoubleConverter implements Converter { @Override public Float convertToField(Double value) { diff --git a/src/main/java/band/kessokuteatime/nightautoconfig/spec/SpecBuilder.java b/src/main/java/band/kessokuteatime/nightautoconfig/spec/SpecBuilder.java index 01b0aa3..65b5cb3 100644 --- a/src/main/java/band/kessokuteatime/nightautoconfig/spec/SpecBuilder.java +++ b/src/main/java/band/kessokuteatime/nightautoconfig/spec/SpecBuilder.java @@ -2,11 +2,18 @@ import band.kessokuteatime.nightautoconfig.annotation.*; import com.electronwill.nightconfig.core.ConfigSpec; +import com.electronwill.nightconfig.core.conversion.AdvancedPath; +import com.electronwill.nightconfig.core.conversion.Path; +import com.electronwill.nightconfig.core.utils.StringUtils; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.function.Function; import java.util.function.Predicate; public record SpecBuilder(T t) { @@ -19,10 +26,47 @@ public ConfigSpec build() { return spec; } - public void appendBasicSpecs(ConfigSpec spec) { - Field[] fields = t.getClass().getDeclaredFields(); + /** + * Gets the path of a field: returns the annotated path, or the field's name if there is no + * annotated path. + * + * @return the annotated path, if any, or the field name + */ + static List getPath(Field field) { + List annotatedPath = getPath((AnnotatedElement)field); + return (annotatedPath == null) ? Collections.singletonList(field.getName()) : annotatedPath; + } + /** + * Gets the annotated path (specified with @Path or @AdvancedPath) of an annotated element. + * + * @return the annotated path, or {@code null} if there is none. + */ + static List getPath(AnnotatedElement annotatedElement) { + Path path = annotatedElement.getDeclaredAnnotation(Path.class); + if (path != null) { + return StringUtils.split(path.value(), '.'); + } + AdvancedPath advancedPath = annotatedElement.getDeclaredAnnotation(AdvancedPath.class); + if (advancedPath != null) { + return Arrays.asList(advancedPath.value()); + } + return null; + } - Arrays.stream(fields) + static Double safeDouble(Object floatObject) { + return ((Float) floatObject).doubleValue(); + } + + static Predicate typeChecker(List> availableTypes) { + return field -> availableTypes.contains(field.getType()); + } + + private Field[] fields() { + return t.getClass().getDeclaredFields(); + } + + private void appendBasicSpecs(ConfigSpec spec) { + Arrays.stream(fields()) .forEach(field -> { try { field.setAccessible(true); @@ -35,17 +79,17 @@ public void appendBasicSpecs(ConfigSpec spec) { Predicate validator = annotation.value().getDeclaredConstructor().newInstance(); if (isFloat) { - spec.define(field.getName(), ((Float) value).doubleValue(), validator); + // Store float as double + spec.define(getPath(field), safeDouble(value), validator); } else { - spec.define(field.getName(), value, validator); + spec.define(getPath(field), value, validator); } } else { - System.out.println(field.getName() + ", " + value); - if (isFloat) { - spec.define(field.getName(), ((Float) value).doubleValue()); + // Store float as double + spec.define(getPath(field), safeDouble(value)); } else { - spec.define(field.getName(), value); + spec.define(getPath(field), value); } } } catch (IllegalAccessException | InvocationTargetException | InstantiationException | @@ -55,18 +99,58 @@ public void appendBasicSpecs(ConfigSpec spec) { }); } - public void appendInRangeSpecs(ConfigSpec spec) { + private > void appendInRangeSpec( + ConfigSpec spec, Field field, + V min, V max + ) throws IllegalAccessException { + field.setAccessible(true); + appendInRangeSpec(spec, field, (V) field.get(t), min, max); + } + + private > void appendInRangeSpec( + ConfigSpec spec, Field field, + V value, V min, V max + ) { + spec.defineInRange(getPath(field), value, min, max); + } + + private void appendInRangeSpecs(ConfigSpec spec) { Field[] fields = t.getClass().getDeclaredFields(); + // Byte + Arrays.stream(fields) + .filter(typeChecker(SpecInRangeByte.associatedTypes)) + .filter(field -> field.isAnnotationPresent(SpecInRangeByte.class)) + .forEach(field -> { + SpecInRangeByte annotation = field.getAnnotation(SpecInRangeByte.class); + try { + appendInRangeSpec(spec, field, annotation.min(), annotation.max()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + + // Short + Arrays.stream(fields) + .filter(typeChecker(SpecInRangeShort.associatedTypes)) + .filter(field -> field.isAnnotationPresent(SpecInRangeShort.class)) + .forEach(field -> { + SpecInRangeShort annotation = field.getAnnotation(SpecInRangeShort.class); + try { + appendInRangeSpec(spec, field, annotation.min(), annotation.max()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + // Int Arrays.stream(fields) - .filter(field -> List.of(int.class, Integer.class).contains(field.getType())) + .filter(typeChecker(SpecInRangeInt.associatedTypes)) .filter(field -> field.isAnnotationPresent(SpecInRangeInt.class)) .forEach(field -> { + SpecInRangeInt annotation = field.getAnnotation(SpecInRangeInt.class); try { - field.setAccessible(true); - SpecInRangeInt annotation = field.getAnnotation(SpecInRangeInt.class); - spec.defineInRange(field.getName(), (int) field.get(t), annotation.min(), annotation.max()); + appendInRangeSpec(spec, field, annotation.min(), annotation.max()); } catch (IllegalAccessException e) { throw new RuntimeException(e); } @@ -74,33 +158,38 @@ public void appendInRangeSpecs(ConfigSpec spec) { // Long Arrays.stream(fields) - .filter(field -> List.of(long.class, Long.class).contains(field.getType())) + .filter(typeChecker(SpecInRangeLong.associatedTypes)) .filter(field -> field.isAnnotationPresent(SpecInRangeLong.class)) .forEach(field -> { + SpecInRangeLong annotation = field.getAnnotation(SpecInRangeLong.class); try { - field.setAccessible(true); - SpecInRangeLong annotation = field.getAnnotation(SpecInRangeLong.class); - spec.defineInRange(field.getName(), (long) field.get(t), annotation.min(), annotation.max()); + appendInRangeSpec(spec, field, annotation.min(), annotation.max()); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }); - // Floating points + // Float Arrays.stream(fields) - .filter(field -> List.of(double.class, Double.class, float.class, Float.class).contains(field.getType())) + .filter(typeChecker(List.of(Float.class, float.class))) .filter(field -> field.isAnnotationPresent(SpecInRangeDouble.class)) .forEach(field -> { + SpecInRangeDouble annotation = field.getAnnotation(SpecInRangeDouble.class); try { - field.setAccessible(true); - SpecInRangeDouble annotation = field.getAnnotation(SpecInRangeDouble.class); - Object obj = field.get(t); + appendInRangeSpec(spec, field, safeDouble(field.get(t)), annotation.min(), annotation.max()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); - if (List.of(double.class, Double.class).contains(field.getType())) { - spec.defineInRange(field.getName(), (double) obj, annotation.min(), annotation.max()); - } else if (List.of(float.class, Float.class).contains(field.getType())) { - spec.defineInRange(field.getName(), ((Float) obj).doubleValue(), annotation.min(), annotation.max()); - } + // Double + Arrays.stream(fields) + .filter(typeChecker(List.of(Double.class, double.class))) + .filter(field -> field.isAnnotationPresent(SpecInRangeDouble.class)) + .forEach(field -> { + SpecInRangeDouble annotation = field.getAnnotation(SpecInRangeDouble.class); + try { + appendInRangeSpec(spec, field, (double) field.get(t), annotation.min(), annotation.max()); } catch (IllegalAccessException e) { throw new RuntimeException(e); }