diff --git a/build.properties b/build.properties index e84593f..08c2e78 100644 --- a/build.properties +++ b/build.properties @@ -1,3 +1,3 @@ -VERSION=3.0.3 +VERSION=4.0.0-SNAPSHOT GROUP=org.infernalstudios ARTIFACT=config diff --git a/src/main/java/org/infernalstudios/config/Config.java b/src/main/java/org/infernalstudios/config/Config.java index 4cc97a9..a23b154 100644 --- a/src/main/java/org/infernalstudios/config/Config.java +++ b/src/main/java/org/infernalstudios/config/Config.java @@ -101,7 +101,7 @@ public void reload() { Object obj = this.config.get(element.getName()); IConfigElementHandler handler = (IConfigElementHandler) element.getTypeHandler(); if (obj != null && handler.canHandle(obj.getClass())) { - handler.update(((IConfigElement) element), handler.deserialize(obj)); + handler.update(((IConfigElement) element), handler.deserialize((IConfigElement) element, obj)); } else { this.config.set(element.getName(), handler.serialize((IConfigElement) element)); shouldSave = true; diff --git a/src/main/java/org/infernalstudios/config/annotation/ListValue.java b/src/main/java/org/infernalstudios/config/annotation/ListValue.java new file mode 100644 index 0000000..fb1ce74 --- /dev/null +++ b/src/main/java/org/infernalstudios/config/annotation/ListValue.java @@ -0,0 +1,41 @@ +/* + * Copyright 2022 Infernal Studios + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.infernalstudios.config.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies that the field is a {@link java.util.List} field. + * Must specify `deserialize` and `serialize` handlers. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ListValue { + /** + * The handler for deserializing the list. + * Handler must have a return type of the element in list, must accept a {@link Config} parameter, and must be static. + */ + String deserialize(); + + /** + * The handler for serializing the list. + * Handler must have a return type of {@link Config}, must accept the element in list as a parameter, and must be static. + */ + String serialize(); +} diff --git a/src/main/java/org/infernalstudios/config/element/ListConfigElement.java b/src/main/java/org/infernalstudios/config/element/ListConfigElement.java new file mode 100644 index 0000000..cc1ca25 --- /dev/null +++ b/src/main/java/org/infernalstudios/config/element/ListConfigElement.java @@ -0,0 +1,81 @@ +/* + * Copyright 2022 Infernal Studios + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.infernalstudios.config.element; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; + +import org.infernalstudios.config.annotation.ListValue; +import org.infernalstudios.config.element.handler.IConfigElementHandler; +import org.infernalstudios.config.util.annotation.Nullable; + +import com.electronwill.nightconfig.core.Config; + +@SuppressWarnings("rawtypes") +public class ListConfigElement extends ConfigElement { + @Nullable + public final Method deserializeHandler; + @Nullable + public final Method serializeHandler; + + public ListConfigElement(Field field, IConfigElementHandler handler) { + super(field, handler); + ListValue valueAnnotation = field.getAnnotation(ListValue.class); + + if (valueAnnotation != null) { + String deserializeHandlerPath = valueAnnotation.deserialize(); + String serializeHandlerPath = valueAnnotation.serialize(); + + String deserializeClassPath = deserializeHandlerPath.substring(0, deserializeHandlerPath.lastIndexOf("::")); + String deserializeMethod = deserializeHandlerPath.substring(deserializeHandlerPath.lastIndexOf("::") + 2); + + String serializeClassPath = serializeHandlerPath.substring(0, serializeHandlerPath.lastIndexOf("::")); + String serializeMethod = serializeHandlerPath.substring(serializeHandlerPath.lastIndexOf("::") + 2); + + Class deserializeClass; + try { + deserializeClass = Class.forName(deserializeClassPath); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Could not find class for deserialization handler", e); + } + Class serializeClass; + try { + serializeClass = Class.forName(serializeClassPath); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Could not find class for serialization handler", e); + } + + try { + this.deserializeHandler = deserializeClass.getDeclaredMethod(deserializeMethod, Config.class); + } catch (NoSuchMethodException e) { + throw new IllegalStateException("Could not find method for deserialization handler", e); + } catch (SecurityException e) { + throw new IllegalStateException("Could not access method for deserialization handler", e); + } + try { + this.serializeHandler = serializeClass.getDeclaredMethod(serializeMethod); + } catch (NoSuchMethodException e) { + throw new IllegalStateException("Could not find method for serialization handler", e); + } catch (SecurityException e) { + throw new IllegalStateException("Could not access method for serialization handler", e); + } + } else { + this.deserializeHandler = null; + this.serializeHandler = null; + } + } +} diff --git a/src/main/java/org/infernalstudios/config/element/handler/BooleanElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/BooleanElementHandler.java index 32a86e8..702fb3f 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/BooleanElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/BooleanElementHandler.java @@ -46,7 +46,7 @@ public Boolean serialize(IConfigElement element) { } @Override - public Boolean deserialize(Boolean obj) { + public Boolean deserialize(IConfigElement element, Boolean obj) { return obj; } diff --git a/src/main/java/org/infernalstudios/config/element/handler/DoubleElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/DoubleElementHandler.java index e2ef5ab..c9970c3 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/DoubleElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/DoubleElementHandler.java @@ -65,7 +65,7 @@ public Number serialize(IConfigElement element) { } @Override - public Double deserialize(Number obj) { + public Double deserialize(IConfigElement element, Number obj) { return obj.doubleValue(); } diff --git a/src/main/java/org/infernalstudios/config/element/handler/FloatElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/FloatElementHandler.java index 24939db..cb082de 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/FloatElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/FloatElementHandler.java @@ -65,7 +65,7 @@ public Number serialize(IConfigElement element) { } @Override - public Float deserialize(Number obj) { + public Float deserialize(IConfigElement element, Number obj) { return obj.floatValue(); } diff --git a/src/main/java/org/infernalstudios/config/element/handler/IConfigElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/IConfigElementHandler.java index e65ae02..058bded 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/IConfigElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/IConfigElementHandler.java @@ -45,7 +45,7 @@ public interface IConfigElementHandler { * @param obj The value to deserialize. */ @Nullable - T deserialize(S obj); + T deserialize(IConfigElement element, S obj); /** * Determines whether the provided type can be handled by this handler. diff --git a/src/main/java/org/infernalstudios/config/element/handler/IntegerElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/IntegerElementHandler.java index 6ac4490..48454da 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/IntegerElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/IntegerElementHandler.java @@ -65,7 +65,7 @@ public Number serialize(IConfigElement element) { } @Override - public Integer deserialize(Number obj) { + public Integer deserialize(IConfigElement element, Number obj) { return obj.intValue(); } diff --git a/src/main/java/org/infernalstudios/config/element/handler/ListElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/ListElementHandler.java index 1592640..61fd462 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/ListElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/ListElementHandler.java @@ -16,10 +16,11 @@ package org.infernalstudios.config.element.handler; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.util.List; -import org.infernalstudios.config.element.ConfigElement; import org.infernalstudios.config.element.IConfigElement; +import org.infernalstudios.config.element.ListConfigElement; import org.infernalstudios.config.util.annotation.Nullable; @SuppressWarnings("rawtypes") @@ -29,7 +30,7 @@ private ListElementHandler() {} @Override public IConfigElement create(Field field) { - return new ConfigElement<>(field, this); + return new ListConfigElement(field, this); } @Override @@ -40,14 +41,44 @@ public IConfigElement update(IConfigElement element, @Nullable List return element; } + @SuppressWarnings("unchecked") @Override public List serialize(IConfigElement element) { List value = element.getFromField(); - return value == null ? element.getDefault() : value; + if (value == null) { + value = element.getDefault(); + } + + if (element instanceof ListConfigElement listConfigElement && listConfigElement.serializeHandler != null) { + value = value.stream().map(listElement -> { + try { + return listConfigElement.serializeHandler.invoke(null, listElement); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new IllegalStateException("Could not serialize list", e); + } + }).toList(); + } + + return value; } + @SuppressWarnings("unchecked") @Override - public List deserialize(List obj) { + public List deserialize(IConfigElement element, List obj) { + if (obj == null) { + obj = element.getDefault(); + } + + if (element instanceof ListConfigElement listConfigElement && listConfigElement.deserializeHandler != null) { + obj = obj.stream().map(listElement -> { + try { + return listConfigElement.deserializeHandler.invoke(null, listElement); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new IllegalStateException("Could not deserialize list", e); + } + }).toList(); + } + return obj; } diff --git a/src/main/java/org/infernalstudios/config/element/handler/NumberElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/NumberElementHandler.java index 0dabaa7..8582003 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/NumberElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/NumberElementHandler.java @@ -32,7 +32,7 @@ public Number serialize(IConfigElement element) { } @Override - public Number deserialize(Number obj) { + public Number deserialize(IConfigElement element, Number obj) { return obj; } diff --git a/src/main/java/org/infernalstudios/config/element/handler/StringElementHandler.java b/src/main/java/org/infernalstudios/config/element/handler/StringElementHandler.java index 8f77489..3f42a67 100644 --- a/src/main/java/org/infernalstudios/config/element/handler/StringElementHandler.java +++ b/src/main/java/org/infernalstudios/config/element/handler/StringElementHandler.java @@ -45,7 +45,7 @@ public String serialize(IConfigElement element) { } @Override - public String deserialize(String obj) { + public String deserialize(IConfigElement element, String obj) { return obj; }