Przeglądaj źródła

Remove listeners (setter as alternative)

Lortseam 4 lat temu
rodzic
commit
f6e9d43d69

+ 0 - 10
lib/src/main/java/me/lortseam/completeconfig/api/ConfigEntry.java

@@ -46,16 +46,6 @@ public @interface ConfigEntry {
      */
     String[] tooltipTranslationKeys() default {};
 
-    /**
-     * Specifies whether the field should always get updated, regardless of the number of listeners.
-     *
-     * <p>If at least one listener exists in the field's class, by default, the field will not get modified when the
-     * config is saved. Instead, all listeners will be called. Set this to true to disable that behaviour.
-     *
-     * @return true if the field should always get updated, else false
-     */
-    boolean forceUpdate() default false;
-
     /**
      * Specifies whether the game needs to be restarted after modifying the entry.
      *

+ 0 - 32
lib/src/main/java/me/lortseam/completeconfig/api/ConfigEntryListener.java

@@ -1,32 +0,0 @@
-package me.lortseam.completeconfig.api;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Applied to declare that a method listens to a config entry's value changes.
- *
- * <p>The annotated method requires a parameter of the same type as the entry's field and a {@code void} return type.
- */
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface ConfigEntryListener {
-
-    /**
-     * Specifies the name of the entry's field.
-     *
-     * @return the entry's field name
-     */
-    String value() default "";
-
-    /**
-     * Specifies the class in which the entry's field is declared in. Only required if the listener is not declared
-     * in the same class.
-     *
-     * @return the entry's parent class
-     */
-    Class<? extends ConfigContainer> container() default ConfigContainer.class;
-
-}

+ 36 - 78
lib/src/main/java/me/lortseam/completeconfig/data/Entry.java

@@ -1,9 +1,8 @@
 package me.lortseam.completeconfig.data;
 
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
 import com.google.common.collect.Lists;
-import lombok.*;
+import lombok.Getter;
+import lombok.NonNull;
 import lombok.extern.log4j.Log4j2;
 import me.lortseam.completeconfig.CompleteConfig;
 import me.lortseam.completeconfig.api.ConfigContainer;
@@ -22,14 +21,18 @@ import org.apache.commons.lang3.StringUtils;
 import org.spongepowered.configurate.CommentedConfigurationNode;
 import org.spongepowered.configurate.serialize.SerializationException;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.*;
-import java.util.function.Consumer;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.*;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.function.UnaryOperator;
 
 @Log4j2
-public class Entry<T> extends EntryBase<T> implements DataPart {
+public class Entry<T> implements DataPart {
 
     private static final Transformer DEFAULT_TRANSFORMER = Entry::new;
     private static final List<Transformation> transformations = Lists.newArrayList(
@@ -62,7 +65,6 @@ public class Entry<T> extends EntryBase<T> implements DataPart {
             Transformation.builder().byAnnotation(ConfigEntry.Color.class).transforms(ColorEntry::new),
             Transformation.builder().byType(TextColor.class).transforms(origin -> new ColorEntry<>(origin, false))
     );
-    private static final BiMap<Key, EntryBase> entries = HashBiMap.create();
 
     static {
         CompleteConfig.getExtensions().stream().filter(extension -> {
@@ -72,10 +74,17 @@ public class Entry<T> extends EntryBase<T> implements DataPart {
         }).filter(Objects::nonNull).forEach(transformations::addAll);
     }
 
-    static EntryBase<?> of(Field field, Class<? extends ConfigContainer> parentClass) {
-        return entries.computeIfAbsent(new Key(field, parentClass), absentField -> new Draft<>(field));
+    static Entry<?> of(Field field, ConfigContainer parentObject, TranslationIdentifier parentTranslation) {
+        EntryOrigin origin = new EntryOrigin(field, parentObject, parentTranslation);
+        return transformations.stream().filter(transformation -> transformation.test(origin)).findFirst().map(Transformation::getTransformer).orElse(DEFAULT_TRANSFORMER).transform(origin);
     }
 
+    @Getter
+    private final Field field;
+    @Getter
+    private final Type type;
+    @Getter
+    private final Class<T> typeClass;
     private final ConfigContainer parentObject;
     private String customID;
     @Getter
@@ -83,20 +92,20 @@ public class Entry<T> extends EntryBase<T> implements DataPart {
     private final TranslationIdentifier parentTranslation;
     private TranslationIdentifier customTranslation;
     private TranslationIdentifier[] customTooltipTranslation;
-    private boolean forceUpdate;
     private boolean requiresRestart;
     private String comment;
-    private final UnaryOperator<T> modifier;
-    private final List<Listener<T>> listeners = new ArrayList<>();
+    private final UnaryOperator<T> valueModifier;
 
-    protected Entry(EntryOrigin origin, UnaryOperator<T> modifier) {
-        super(origin.getField());
+    protected Entry(EntryOrigin origin, UnaryOperator<T> valueModifier) {
+        field = origin.getField();
         if (!field.isAccessible()) {
             field.setAccessible(true);
         }
+        type = TypeUtils.getFieldType(origin.getField());
+        typeClass = (Class<T>) TypeUtils.getTypeClass(type);
         parentObject = origin.getParentObject();
         parentTranslation = origin.getParentTranslation();
-        this.modifier = modifier;
+        this.valueModifier = valueModifier;
         defaultValue = getValue();
     }
 
@@ -128,8 +137,8 @@ public class Entry<T> extends EntryBase<T> implements DataPart {
     }
 
     private boolean update(T value) {
-        if (modifier != null) {
-            value = modifier.apply(value);
+        if (valueModifier != null) {
+            value = valueModifier.apply(value);
         }
         if (value.equals(getFieldValue())) {
             return false;
@@ -139,20 +148,16 @@ public class Entry<T> extends EntryBase<T> implements DataPart {
     }
 
     private void set(T value) {
-        if (listeners.stream().noneMatch(listener -> listener.getParentObject() == parentObject) || forceUpdate) {
-            try {
-                field.set(parentObject, value);
-            } catch (IllegalAccessException e) {
-                throw new RuntimeException(e);
+        try {
+            Method writeMethod = new PropertyDescriptor(field.getName(), field.getDeclaringClass()).getWriteMethod();
+            if (writeMethod != null) {
+                writeMethod.invoke(Modifier.isStatic(writeMethod.getModifiers()) ? null : parentObject, value);
+            } else {
+                field.set(Modifier.isStatic(field.getModifiers()) ? null : parentObject, value);
             }
+        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException("Failed to set entry value", e);
         }
-        for (Listener<T> listener : listeners) {
-            listener.invoke(value);
-        }
-    }
-
-    void addListener(Method method, ConfigContainer parentObject) {
-        listeners.add(new Listener<>(method, parentObject));
     }
 
     @Override
@@ -196,7 +201,6 @@ public class Entry<T> extends EntryBase<T> implements DataPart {
                 }
                 customTooltipTranslation = Arrays.stream(customTooltipTranslationKeys).map(key -> parentTranslation.root().append(key)).toArray(TranslationIdentifier[]::new);
             }
-            forceUpdate = annotation.forceUpdate();
             requiresRestart = annotation.requiresRestart();
             String comment = annotation.comment();
             if (!StringUtils.isBlank(comment)) {
@@ -230,50 +234,4 @@ public class Entry<T> extends EntryBase<T> implements DataPart {
         }
     }
 
-    @Override
-    void interact(Consumer<Entry<T>> interaction) {
-        interaction.accept(this);
-    }
-
-    @AllArgsConstructor(access = AccessLevel.PRIVATE)
-    @EqualsAndHashCode
-    private static class Key {
-
-        private final Field field;
-        private final Class<? extends ConfigContainer> parentClass;
-
-    }
-
-    public static class Draft<T> extends EntryBase<T> {
-
-        static <T> Draft<T> of(Field field, Class<? extends ConfigContainer> parentClass) {
-            EntryBase<T> accessor = (EntryBase<T>) Entry.of(field, parentClass);
-            if (!(accessor instanceof Draft)) {
-                throw new UnsupportedOperationException("Entry draft of field " + field + " was already built");
-            }
-            return (Draft<T>) accessor;
-        }
-
-        private final List<Consumer<Entry<T>>> interactions = new ArrayList<>();
-
-        private Draft(Field field) {
-            super(field);
-        }
-
-        @Override
-        void interact(Consumer<Entry<T>> interaction) {
-            interactions.add(interaction);
-        }
-
-        Entry<T> build(ConfigContainer parentObject, TranslationIdentifier parentTranslation) {
-            Entry<T> entry = (Entry<T>) transformations.stream().filter(transformation -> transformation.test(this)).findFirst().map(Transformation::getTransformer).orElse(DEFAULT_TRANSFORMER).transform(new EntryOrigin(this, parentObject, parentTranslation));
-            for (Consumer<Entry<T>> interaction : interactions) {
-                interaction.accept(entry);
-            }
-            entries.put(entries.inverse().get(this), entry);
-            return entry;
-        }
-
-    }
-
 }

+ 0 - 27
lib/src/main/java/me/lortseam/completeconfig/data/EntryBase.java

@@ -1,27 +0,0 @@
-package me.lortseam.completeconfig.data;
-
-import lombok.Getter;
-import me.lortseam.completeconfig.util.TypeUtils;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Type;
-import java.util.function.Consumer;
-
-public abstract class EntryBase<T> {
-
-    @Getter
-    protected final Field field;
-    @Getter
-    protected final Type type;
-    @Getter
-    protected final Class<T> typeClass;
-
-    protected EntryBase(Field field) {
-        this.field = field;
-        type = TypeUtils.getFieldType(field);
-        typeClass = (Class<T>) TypeUtils.getTypeClass(type);
-    }
-
-    abstract void interact(Consumer<Entry<T>> interaction);
-
-}

+ 1 - 53
lib/src/main/java/me/lortseam/completeconfig/data/EntrySet.java

@@ -1,19 +1,13 @@
 package me.lortseam.completeconfig.data;
 
-import com.google.common.base.CaseFormat;
 import me.lortseam.completeconfig.api.ConfigContainer;
 import me.lortseam.completeconfig.api.ConfigEntries;
 import me.lortseam.completeconfig.api.ConfigEntry;
-import me.lortseam.completeconfig.api.ConfigEntryListener;
 import me.lortseam.completeconfig.data.text.TranslationIdentifier;
-import me.lortseam.completeconfig.exception.IllegalAnnotationParameterException;
 import me.lortseam.completeconfig.exception.IllegalModifierException;
-import me.lortseam.completeconfig.exception.IllegalReturnTypeException;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
-import java.util.regex.Pattern;
 
 public class EntrySet extends DataSet<Entry> {
 
@@ -35,56 +29,10 @@ public class EntrySet extends DataSet<Entry> {
                 if (Modifier.isFinal(field.getModifiers())) {
                     throw new IllegalModifierException("Entry field " + field + " must not be final");
                 }
-                Entry<?> entry = Entry.Draft.of(field, container.getClass()).build(Modifier.isStatic(field.getModifiers()) ? null : container, translation);
+                Entry<?> entry = Entry.of(field, container, translation);
                 entry.resolve(field);
                 return entry;
             }).forEach(this::add);
-            Arrays.stream(clazz.getDeclaredMethods()).filter(method -> {
-                if (clazz != container.getClass() && Modifier.isStatic(method.getModifiers())) {
-                    return false;
-                }
-                return method.isAnnotationPresent(ConfigEntryListener.class);
-            }).forEach(method -> {
-                Field field = null;
-                Class<? extends ConfigContainer> fieldClass = clazz;
-                Class<? extends ConfigContainer> parentClass = container.getClass();
-                if (method.isAnnotationPresent(ConfigEntryListener.class)) {
-                    ConfigEntryListener listener = method.getDeclaredAnnotation(ConfigEntryListener.class);
-                    if (listener.container() != ConfigContainer.class) {
-                        fieldClass = listener.container();
-                        parentClass = fieldClass;
-                    }
-                    if (!listener.value().equals("")) {
-                        try {
-                            field = fieldClass.getDeclaredField(listener.value());
-                        } catch (NoSuchFieldException e) {
-                            throw new IllegalAnnotationParameterException(e);
-                        }
-                    }
-                }
-                if (field == null && fieldClass == clazz && method.getName().startsWith("set")) {
-                    try {
-                        field = fieldClass.getDeclaredField(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, method.getName().replaceFirst(Pattern.quote("set"), "")));
-                    } catch (NoSuchFieldException ignore) {}
-                }
-                if (field == null) {
-                    throw new IllegalAnnotationParameterException("Could not detect field name for listener method " + method);
-                }
-                if (method.getParameterCount() != 1) {
-                    throw new IllegalArgumentException("Listener method " + method + " has wrong number of parameters");
-                }
-                EntryBase<?> entry = Entry.of(field, parentClass);
-                if (!method.getParameterTypes()[0].equals(entry.getType())) {
-                    throw new IllegalArgumentException("Listener method " + method + " has wrong argument type");
-                }
-                if (method.getReturnType() != Void.TYPE) {
-                    throw new IllegalReturnTypeException("Listener method " + method + " may not return a type other than void");
-                }
-                if (!method.isAccessible()) {
-                    method.setAccessible(true);
-                }
-                entry.interact(e -> e.addListener(method, Modifier.isStatic(method.getModifiers()) ? null : container));
-            });
         }
     }
 

+ 0 - 26
lib/src/main/java/me/lortseam/completeconfig/data/Listener.java

@@ -1,26 +0,0 @@
-package me.lortseam.completeconfig.data;
-
-import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import me.lortseam.completeconfig.api.ConfigContainer;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-@AllArgsConstructor(access = AccessLevel.PACKAGE)
-public class Listener<T> {
-
-    private final Method method;
-    @Getter(AccessLevel.PACKAGE)
-    private final ConfigContainer parentObject;
-
-    void invoke(T value) {
-        try {
-            method.invoke(parentObject, value);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-}

+ 6 - 5
lib/src/main/java/me/lortseam/completeconfig/data/entry/EntryOrigin.java

@@ -1,15 +1,18 @@
 package me.lortseam.completeconfig.data.entry;
 
 import lombok.Getter;
+import lombok.RequiredArgsConstructor;
 import me.lortseam.completeconfig.api.ConfigContainer;
-import me.lortseam.completeconfig.data.Entry;
 import me.lortseam.completeconfig.data.text.TranslationIdentifier;
+import me.lortseam.completeconfig.util.TypeUtils;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
+import java.lang.reflect.Type;
 import java.util.Objects;
 import java.util.Optional;
 
+@RequiredArgsConstructor
 public final class EntryOrigin {
 
     @Getter
@@ -19,10 +22,8 @@ public final class EntryOrigin {
     @Getter
     private final TranslationIdentifier parentTranslation;
 
-    public EntryOrigin(Entry.Draft<?> draft, ConfigContainer parentObject, TranslationIdentifier parentTranslation) {
-        field = draft.getField();
-        this.parentObject = parentObject;
-        this.parentTranslation = parentTranslation;
+    public Type getType() {
+        return TypeUtils.getFieldType(field);
     }
 
     public <A extends Annotation> A getAnnotation(Class<A> annotationType) {

+ 11 - 10
lib/src/main/java/me/lortseam/completeconfig/data/entry/Transformation.java

@@ -4,12 +4,13 @@ import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.RequiredArgsConstructor;
-import me.lortseam.completeconfig.data.Entry;
 import org.apache.commons.lang3.ArrayUtils;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
-import java.util.*;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -22,22 +23,22 @@ public final class Transformation {
         return new Transformation.Builder();
     }
 
-    private final Predicate<Entry.Draft<?>> predicate;
+    private final Predicate<EntryOrigin> predicate;
     @Getter
     private final Transformer transformer;
 
-    public boolean test(Entry.Draft<?> draft) {
-        return predicate.test(draft);
+    public boolean test(EntryOrigin origin) {
+        return predicate.test(origin);
     }
 
     @NoArgsConstructor(access = AccessLevel.PRIVATE)
     public static class Builder {
 
-        private Predicate<Entry.Draft<?>> predicate;
+        private Predicate<EntryOrigin> predicate;
         private final Set<Class<? extends Annotation>> requiredAnnotations = new HashSet<>();
         private final Set<Class<? extends Annotation>> optionalAnnotations = new HashSet<>();
 
-        private Builder by(Predicate<Entry.Draft<?>> predicate) {
+        private Builder by(Predicate<EntryOrigin> predicate) {
             if (this.predicate == null) {
                 this.predicate = predicate;
             } else {
@@ -51,7 +52,7 @@ public final class Transformation {
         }
 
         public Builder byType(Predicate<Type> typePredicate) {
-            return by(draft -> typePredicate.test(draft.getType()));
+            return by(origin -> typePredicate.test(origin.getType()));
         }
 
         public Builder byAnnotation(Class<? extends Annotation> annotation, boolean optional) {
@@ -73,8 +74,8 @@ public final class Transformation {
 
         public Transformation transforms(Transformer transformer) {
             if (!requiredAnnotations.isEmpty() || !optionalAnnotations.isEmpty()) {
-                by(draft -> {
-                    Set<Class<? extends Annotation>> declaredAnnotations = Arrays.stream(draft.getField().getDeclaredAnnotations()).map(Annotation::annotationType).filter(registeredAnnotations::contains).collect(Collectors.toSet());
+                by(origin -> {
+                    Set<Class<? extends Annotation>> declaredAnnotations = Arrays.stream(origin.getField().getDeclaredAnnotations()).map(Annotation::annotationType).filter(registeredAnnotations::contains).collect(Collectors.toSet());
                     for (Class<? extends Annotation> requiredAnnotation : requiredAnnotations) {
                         if (!declaredAnnotations.remove(requiredAnnotation)) return false;
                     }

+ 0 - 1
lib/src/test/java/me/lortseam/completeconfig/ConfigTest.java

@@ -4,7 +4,6 @@ import com.google.common.collect.Iterables;
 import me.lortseam.completeconfig.api.ConfigContainer;
 import me.lortseam.completeconfig.data.Config;
 import me.lortseam.completeconfig.data.Entry;
-import me.lortseam.completeconfig.data.EntryBase;
 import me.lortseam.completeconfig.data.containers.*;
 import me.lortseam.completeconfig.data.groups.EmptyGroup;
 import me.lortseam.completeconfig.data.listeners.*;

+ 0 - 2
lib/src/test/java/me/lortseam/completeconfig/data/listeners/CustomListener.java

@@ -2,14 +2,12 @@ package me.lortseam.completeconfig.data.listeners;
 
 import me.lortseam.completeconfig.api.ConfigEntry;
 import me.lortseam.completeconfig.api.ConfigContainer;
-import me.lortseam.completeconfig.api.ConfigEntryListener;
 
 public class CustomListener implements ConfigContainer {
 
     @ConfigEntry
     private boolean value;
 
-    @ConfigEntryListener("value")
     public void update(boolean value) {
         this.value = value;
     }

+ 0 - 2
lib/src/test/java/me/lortseam/completeconfig/data/listeners/EmptyListener.java

@@ -2,14 +2,12 @@ package me.lortseam.completeconfig.data.listeners;
 
 import me.lortseam.completeconfig.api.ConfigEntry;
 import me.lortseam.completeconfig.api.ConfigContainer;
-import me.lortseam.completeconfig.api.ConfigEntryListener;
 
 public class EmptyListener implements ConfigContainer {
 
     @ConfigEntry
     private boolean value;
 
-    @ConfigEntryListener("value")
     public void onUpdate(boolean value) {}
 
     public boolean getValue() {

+ 0 - 2
lib/src/test/java/me/lortseam/completeconfig/data/listeners/ForceUpdateListener.java

@@ -2,14 +2,12 @@ package me.lortseam.completeconfig.data.listeners;
 
 import me.lortseam.completeconfig.api.ConfigEntry;
 import me.lortseam.completeconfig.api.ConfigContainer;
-import me.lortseam.completeconfig.api.ConfigEntryListener;
 
 public class ForceUpdateListener implements ConfigContainer {
 
     @ConfigEntry(forceUpdate = true)
     private boolean value;
 
-    @ConfigEntryListener("value")
     public void onUpdate(boolean value) {}
 
     public boolean getValue() {

+ 0 - 2
lib/src/test/java/me/lortseam/completeconfig/data/listeners/OutsideListener.java

@@ -1,7 +1,6 @@
 package me.lortseam.completeconfig.data.listeners;
 
 import me.lortseam.completeconfig.api.ConfigContainer;
-import me.lortseam.completeconfig.api.ConfigEntryListener;
 import me.lortseam.completeconfig.data.containers.ContainerWithEntry;
 
 public class OutsideListener implements ConfigContainer {
@@ -10,7 +9,6 @@ public class OutsideListener implements ConfigContainer {
     private final ContainerWithEntry container = new ContainerWithEntry();
     private boolean value = container.getValue();
 
-    @ConfigEntryListener(container = ContainerWithEntry.class, value = "entry")
     public void onUpdateValue(boolean value) {
         this.value = value;
     }

+ 0 - 2
lib/src/test/java/me/lortseam/completeconfig/data/listeners/SetterListener.java

@@ -2,14 +2,12 @@ package me.lortseam.completeconfig.data.listeners;
 
 import me.lortseam.completeconfig.api.ConfigEntry;
 import me.lortseam.completeconfig.api.ConfigContainer;
-import me.lortseam.completeconfig.api.ConfigEntryListener;
 
 public class SetterListener implements ConfigContainer {
 
     @ConfigEntry
     private boolean value;
 
-    @ConfigEntryListener
     public void setValue(boolean value) {
         this.value = value;
     }