From 38b1a1aca4d37779404f18b5e976b316fbbb3d6a Mon Sep 17 00:00:00 2001 From: Exlll Date: Mon, 10 Sep 2018 20:11:16 +0200 Subject: [PATCH] Added @Format annotation This annotation can be used to apply a FieldNameFormatter to a Configuration without having to instantiate a Properties object. The FieldNameFormatter returned by the annotation takes precedence over the value returned by the Properties object. Added FieldNameFormatters.UPPER_UNDERSCORE --- .../src/main/resources/plugin.yml | 2 +- .../src/main/resources/plugin.yml | 2 +- .../de/exlll/configlib/Configuration.java | 12 ++- .../java/de/exlll/configlib/Converter.java | 42 ++++++--- .../java/de/exlll/configlib/Converters.java | 6 +- .../java/de/exlll/configlib/FieldMapper.java | 68 +++++++++++--- .../main/java/de/exlll/configlib/Reflect.java | 25 ++--- .../configlib/annotation/ElementType.java | 10 ++ .../de/exlll/configlib/annotation/Format.java | 38 ++++++++ .../configlib/format/FieldNameFormatters.java | 20 ++++ .../exlll/configlib/FieldMapperHelpers.java | 7 +- .../de/exlll/configlib/FieldMapperTest.java | 91 ++++++++++++++++++- .../configlib/FieldNameFormattersTest.java | 7 ++ README.md | 18 +++- build.gradle | 2 +- 15 files changed, 293 insertions(+), 57 deletions(-) create mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java diff --git a/ConfigLib-Bukkit/src/main/resources/plugin.yml b/ConfigLib-Bukkit/src/main/resources/plugin.yml index 688abcc..5b27cae 100644 --- a/ConfigLib-Bukkit/src/main/resources/plugin.yml +++ b/ConfigLib-Bukkit/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ConfigLib author: Exlll -version: 2.1.0 +version: 2.2.0 main: de.exlll.configlib.ConfigLib \ No newline at end of file diff --git a/ConfigLib-Bungee/src/main/resources/plugin.yml b/ConfigLib-Bungee/src/main/resources/plugin.yml index 688abcc..5b27cae 100644 --- a/ConfigLib-Bungee/src/main/resources/plugin.yml +++ b/ConfigLib-Bungee/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ConfigLib author: Exlll -version: 2.1.0 +version: 2.2.0 main: de.exlll.configlib.ConfigLib \ No newline at end of file diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java index 0689998..501ae1d 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java @@ -1,5 +1,6 @@ package de.exlll.configlib; +import de.exlll.configlib.FieldMapper.MappingInfo; import de.exlll.configlib.filter.FieldFilter; import de.exlll.configlib.filter.FieldFilters; import de.exlll.configlib.format.FieldNameFormatter; @@ -45,7 +46,9 @@ public abstract class Configuration> { public final void save() { try { preSave(); - Map map = FieldMapper.instanceToMap(this, props); + MappingInfo mappingInfo = MappingInfo.from(this); + Map map = FieldMapper + .instanceToMap(this, mappingInfo); getSource().saveConfiguration(getThis(), map); } catch (IOException e) { throw new ConfigurationStoreException(e); @@ -63,7 +66,8 @@ public abstract class Configuration> { public final void load() { try { Map map = getSource().loadConfiguration(getThis()); - FieldMapper.instanceFromMap(this, map, props); + MappingInfo mappingInfo = MappingInfo.from(this); + FieldMapper.instanceFromMap(this, map, mappingInfo); postLoad(); } catch (IOException e) { throw new ConfigurationStoreException(e); @@ -100,6 +104,10 @@ public abstract class Configuration> { */ protected void postLoad() {} + Properties getProperties() { + return props; + } + /** * Instances of a {@code Properties} class are used to configure different * aspects of a configuration. diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java index d7ad86b..e6fa70f 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java @@ -1,5 +1,6 @@ package de.exlll.configlib; +import de.exlll.configlib.FieldMapper.MappingInfo; import de.exlll.configlib.annotation.ElementType; import java.lang.reflect.Field; @@ -10,21 +11,21 @@ import java.lang.reflect.Field; *

* Implementations must have a no-args constructor. * - * @param the type of the field value - * @param the type of the converted value + * @param the source type + * @param the target type */ -public interface Converter { +public interface Converter { /** * Converts a field value to an object that can be stored by a * {@code ConfigurationSource}. *

- * If this method returns null, a {@code ConfigurationException} will be thrown. + * If this method returns null, a {@code ConfigurationException} is thrown. * * @param element field value that is converted * @param info information about the current conversion step * @return converted field value */ - T convertTo(F element, ConversionInfo info); + T convertTo(S element, ConversionInfo info); /** * Executes some action before the field value is converted. @@ -43,7 +44,7 @@ public interface Converter { * @param info information about the current conversion step * @return the element's original representation */ - F convertFrom(T element, ConversionInfo info); + S convertFrom(T element, ConversionInfo info); /** * Executes some action before the converted field value is converted back @@ -58,6 +59,7 @@ public interface Converter { * configuration, configuration element, and the conversion step. */ final class ConversionInfo { + private final MappingInfo mappingInfo; private final Field field; private final Object instance; private final Object value; @@ -70,8 +72,11 @@ public interface Converter { private final int nestingLevel; private int currentNestingLevel; - private ConversionInfo(Field field, Object instance, Object mapValue, - Configuration.Properties props) { + private ConversionInfo( + Field field, Object instance, Object mapValue, + MappingInfo mappingInfo + ) { + this.mappingInfo = mappingInfo; this.field = field; this.instance = instance; this.value = Reflect.getValue(field, instance); @@ -79,7 +84,7 @@ public interface Converter { this.fieldType = field.getType(); this.valueType = value.getClass(); this.fieldName = field.getName(); - this.props = props; + this.props = mappingInfo.getProperties(); this.elementType = elementType(field); this.nestingLevel = nestingLevel(field); } @@ -100,14 +105,17 @@ public interface Converter { return -1; } - static ConversionInfo of(Field field, Object instance, - Configuration.Properties props) { - return new ConversionInfo(field, instance, null, props); + static ConversionInfo from( + Field field, Object instance, MappingInfo mappingInfo + ) { + return new ConversionInfo(field, instance, null, mappingInfo); } - static ConversionInfo of(Field field, Object instance, Object mapValue, - Configuration.Properties props) { - return new ConversionInfo(field, instance, mapValue, props); + static ConversionInfo from( + Field field, Object instance, Object mapValue, + MappingInfo mappingInfo + ) { + return new ConversionInfo(field, instance, mapValue, mappingInfo); } /** @@ -215,5 +223,9 @@ public interface Converter { void incCurrentNestingLevel() { currentNestingLevel++; } + + MappingInfo getMappingInfo() { + return this.mappingInfo; + } } } diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java index 95404f6..fcb1fbc 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java @@ -366,7 +366,7 @@ final class Converters { return o -> { Map map = toTypeMap(o, null); Object inst = Reflect.newInstance(info.getElementType()); - FieldMapper.instanceFromMap(inst, map, info.getProperties()); + FieldMapper.instanceFromMap(inst, map, info.getMappingInfo()); return inst; }; } else if ((element instanceof String) && currentLevelSameAsExpected) { @@ -524,7 +524,7 @@ final class Converters { @Override public Object convertTo(Object element, ConversionInfo info) { - return FieldMapper.instanceToMap(element, info.getProperties()); + return FieldMapper.instanceToMap(element, info.getMappingInfo()); } @Override @@ -538,7 +538,7 @@ final class Converters { checkElementIsConvertibleToConfigurationElement(element, info); Object newInstance = Reflect.newInstance(info.getValueType()); Map typeMap = toTypeMap(element, info.getFieldName()); - FieldMapper.instanceFromMap(newInstance, typeMap, info.getProperties()); + FieldMapper.instanceFromMap(newInstance, typeMap, info.getMappingInfo()); return newInstance; } diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java index 85c165a..727be3f 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java @@ -1,6 +1,7 @@ package de.exlll.configlib; import de.exlll.configlib.Converter.ConversionInfo; +import de.exlll.configlib.annotation.Format; import de.exlll.configlib.filter.FieldFilter; import de.exlll.configlib.format.FieldNameFormatter; @@ -13,14 +14,13 @@ import static de.exlll.configlib.Validator.*; enum FieldMapper { ; - static Map instanceToMap( - Object inst, Configuration.Properties props - ) { + static Map instanceToMap(Object inst, MappingInfo mappingInfo) { Map map = new LinkedHashMap<>(); + Configuration.Properties props = mappingInfo.getProperties(); FieldFilter filter = props.getFilter(); for (Field field : filter.filterDeclaredFieldsOf(inst.getClass())) { - Object val = toConvertibleObject(field, inst, props); - FieldNameFormatter fnf = props.getFormatter(); + Object val = toConvertibleObject(field, inst, mappingInfo); + FieldNameFormatter fnf = selectFormatter(mappingInfo); String fn = fnf.fromFieldName(field.getName()); map.put(fn, val); } @@ -28,10 +28,10 @@ enum FieldMapper { } private static Object toConvertibleObject( - Field field, Object instance, Configuration.Properties props + Field field, Object instance, MappingInfo mappingInfo ) { checkDefaultValueNull(field, instance); - ConversionInfo info = ConversionInfo.of(field, instance, props); + ConversionInfo info = ConversionInfo.from(field, instance, mappingInfo); checkFieldWithElementTypeIsContainer(info); Object converted = Converters.convertTo(info); checkConverterNotReturnsNull(converted, info); @@ -39,26 +39,27 @@ enum FieldMapper { } static void instanceFromMap( - Object inst, Map instMap, - Configuration.Properties props + Object inst, Map instMap, MappingInfo mappingInfo ) { - FieldFilter filter = props.getFilter(); + FieldFilter filter = mappingInfo.getProperties().getFilter(); for (Field field : filter.filterDeclaredFieldsOf(inst.getClass())) { - FieldNameFormatter fnf = props.getFormatter(); + FieldNameFormatter fnf = selectFormatter(mappingInfo); String fn = fnf.fromFieldName(field.getName()); Object mapValue = instMap.get(fn); if (mapValue != null) { - fromConvertedObject(field, inst, mapValue, props); + fromConvertedObject(field, inst, mapValue, mappingInfo); } } } private static void fromConvertedObject( Field field, Object instance, Object mapValue, - Configuration.Properties props + MappingInfo mappingInfo ) { checkDefaultValueNull(field, instance); - ConversionInfo info = ConversionInfo.of(field, instance, mapValue, props); + ConversionInfo info = ConversionInfo.from( + field, instance, mapValue, mappingInfo + ); checkFieldWithElementTypeIsContainer(info); Object convert = Converters.convertFrom(info); @@ -77,4 +78,43 @@ enum FieldMapper { Object val = Reflect.getValue(field, instance); checkNotNull(val, field.getName()); } + + static FieldNameFormatter selectFormatter(MappingInfo info) { + Configuration configuration = info.getConfiguration(); + Configuration.Properties props = info.getProperties(); + if ((configuration != null) && + Reflect.hasFormatter(configuration.getClass())) { + Format format = configuration.getClass() + .getAnnotation(Format.class); + return (format.formatterClass() != FieldNameFormatter.class) + ? Reflect.newInstance(format.formatterClass()) + : format.value(); + } + return props.getFormatter(); + } + + static final class MappingInfo { + private final Configuration configuration; + private final Configuration.Properties properties; + + MappingInfo( + Configuration configuration, + Configuration.Properties properties + ) { + this.configuration = configuration; + this.properties = properties; + } + + Configuration getConfiguration() { + return configuration; + } + + Configuration.Properties getProperties() { + return properties; + } + + static MappingInfo from(Configuration configuration) { + return new MappingInfo(configuration, configuration.getProperties()); + } + } } diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java index f0b9814..659f814 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java @@ -2,6 +2,7 @@ package de.exlll.configlib; import de.exlll.configlib.annotation.ConfigurationElement; import de.exlll.configlib.annotation.Convert; +import de.exlll.configlib.annotation.Format; import de.exlll.configlib.annotation.NoConvert; import java.lang.reflect.Constructor; @@ -37,22 +38,12 @@ enum Reflect { return cls.isEnum(); } - static boolean hasConverter(Field field) { - return field.isAnnotationPresent(Convert.class); - } - - static boolean hasNoConvert(Field field) { - return field.isAnnotationPresent(NoConvert.class); - } - static T newInstance(Class cls) { try { Constructor constructor = cls.getDeclaredConstructor(); constructor.setAccessible(true); return constructor.newInstance(); } catch (NoSuchMethodException e) { - /* This exception should not be thrown because we check - * the presence of a no-args constructor elsewhere. */ String msg = "Class " + cls.getSimpleName() + " doesn't have a " + "no-args constructor."; throw new ConfigurationException(msg, e); @@ -63,8 +54,6 @@ enum Reflect { " not accessible."; throw new ConfigurationException(msg, e); } catch (InstantiationException e) { - /* This exception should not be thrown because - * we call this method only for concrete types. */ String msg = "Class " + cls.getSimpleName() + " not instantiable."; throw new ConfigurationException(msg, e); } catch (InvocationTargetException e) { @@ -98,6 +87,18 @@ enum Reflect { } } + static boolean hasConverter(Field field) { + return field.isAnnotationPresent(Convert.class); + } + + static boolean hasNoConvert(Field field) { + return field.isAnnotationPresent(NoConvert.class); + } + + static boolean hasFormatter(Class cls) { + return cls.isAnnotationPresent(Format.class); + } + static boolean isConfigurationElement(Class cls) { return cls.isAnnotationPresent(ConfigurationElement.class); } diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java index c528c88..92bf1f6 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java @@ -24,7 +24,17 @@ import java.lang.annotation.Target; @Target(java.lang.annotation.ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ElementType { + /** + * Returns the type of elements a {@code Collection} or {@code Map} contains. + * + * @return type of elements. + */ Class value(); + /** + * Returns the nesting level + * + * @return nesting level + */ int nestingLevel() default 0; } diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java new file mode 100644 index 0000000..749eaba --- /dev/null +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java @@ -0,0 +1,38 @@ +package de.exlll.configlib.annotation; + +import de.exlll.configlib.format.FieldNameFormatter; +import de.exlll.configlib.format.FieldNameFormatters; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that a specific {@code FieldNameFormatter} is used. If a + * {@link #formatterClass()} is specified, the {@code FieldNameFormatters} + * returned by {@link #value()} is ignored. The {@code formatterClass} must + * be instantiable and must have a no-args constructor. + *

+ * This annotation takes precedence over the value set in properties object. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Format { + /** + * Returns the {@code FieldNameFormatters} instance to be for formatting. + * The return value of this method is ignored if a {@code formatterClass()} + * is set. + * + * @return {@code FieldNameFormatters} instance + */ + FieldNameFormatters value() default FieldNameFormatters.IDENTITY; + + /** + * Returns the class of a {@code FieldNameFormatter} implementation. + * The class must be instantiable and must have a no-args constructor. + * + * @return class of {@code FieldNameFormatter} implementation + */ + Class formatterClass() default FieldNameFormatter.class; +} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java index 310e980..76e2983 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java +++ b/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java @@ -31,5 +31,25 @@ public enum FieldNameFormatters implements FieldNameFormatter { } return builder.toString(); } + }, + /** + * Represents a {@code FieldNameFormatter} that transforms camelCase to + * UPPER_UNDERSCORE. + *

+ * For example, myPrivateField becomes MY_PRIVATE_FIELD. + */ + UPPER_UNDERSCORE { + @Override + public String fromFieldName(String fieldName) { + StringBuilder builder = new StringBuilder(fieldName.length()); + for (char c : fieldName.toCharArray()) { + if (Character.isLowerCase(c)) { + builder.append(Character.toUpperCase(c)); + } else if (Character.isUpperCase(c)) { + builder.append('_').append(c); + } + } + return builder.toString(); + } } } diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java index e072e72..31aa4ea 100644 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java +++ b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java @@ -1,5 +1,6 @@ package de.exlll.configlib; +import de.exlll.configlib.FieldMapper.MappingInfo; import de.exlll.configlib.annotation.ConfigurationElement; import java.util.Map; @@ -72,7 +73,8 @@ public class FieldMapperHelpers { public static Map instanceToMap( Object o, Configuration.Properties props ) { - return FieldMapper.instanceToMap(o, props); + MappingInfo mappingInfo = new MappingInfo(null, props); + return FieldMapper.instanceToMap(o, mappingInfo); } public static T instanceFromMap(T o, Map map) { @@ -82,7 +84,8 @@ public class FieldMapperHelpers { public static T instanceFromMap( T o, Map map, Configuration.Properties props ) { - FieldMapper.instanceFromMap(o, map, props); + MappingInfo mappingInfo = new MappingInfo(null, props); + FieldMapper.instanceFromMap(o, map, mappingInfo); return o; } } diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java index b36c476..ee77a3e 100644 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java +++ b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java @@ -1,11 +1,16 @@ package de.exlll.configlib; import de.exlll.configlib.Converter.ConversionInfo; +import de.exlll.configlib.FieldMapper.MappingInfo; import de.exlll.configlib.annotation.ElementType; +import de.exlll.configlib.annotation.Format; import de.exlll.configlib.annotation.NoConvert; import de.exlll.configlib.classes.TestClass; import de.exlll.configlib.classes.TestSubClass; import de.exlll.configlib.classes.TestSubClassConverter; +import de.exlll.configlib.configs.mem.InSharedMemoryConfiguration; +import de.exlll.configlib.configs.yaml.YamlConfiguration.YamlProperties; +import de.exlll.configlib.format.FieldNameFormatter; import de.exlll.configlib.format.FieldNameFormatters; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,6 +37,10 @@ class FieldMapperTest { private static final TestClass t = TestClass.TEST_VALUES; private static final Configuration.Properties DEFAULT = Configuration.Properties.builder().build(); + private static final YamlProperties WITH_UPPER_FORMATTER = YamlProperties + .builder() + .setFormatter(FieldNameFormatters.UPPER_UNDERSCORE) + .build(); private static final Map map = instanceToMap( TestClass.TEST_VALUES, DEFAULT ); @@ -41,7 +50,8 @@ class FieldMapperTest { @BeforeEach void setUp() { tmp = new TestClass(); - FieldMapper.instanceFromMap(tmp, map, DEFAULT); + MappingInfo mappingInfo = MappingInfo.from(tmp); + FieldMapper.instanceFromMap(tmp, map, mappingInfo); } @Test @@ -377,7 +387,8 @@ class FieldMapperTest { } catch (NoSuchFieldException e) { e.printStackTrace(); } - return ConversionInfo.of(field, o, null, null); + MappingInfo mappingInfo = new MappingInfo(null, WITH_UPPER_FORMATTER); + return ConversionInfo.from(field, o, mappingInfo); } private static ConversionInfo newInfo(String fieldName) { @@ -502,4 +513,80 @@ class FieldMapperTest { assertThat(a.d, is(4)); assertThat(a.e, is(5)); } + + @Test + void selectFieldNameFormatterReturnsPropertiesIfNoFormatAnnotation() { + class A extends InSharedMemoryConfiguration { + protected A() { super(WITH_UPPER_FORMATTER); } + } + A a = new A(); + FieldNameFormatter fnf = FieldMapper.selectFormatter(MappingInfo.from(a)); + assertThat(fnf, sameInstance(FieldNameFormatters.UPPER_UNDERSCORE)); + } + + @Test + void selectFieldNameFormatterReturnsIfFormatAnnotation() { + @Format(FieldNameFormatters.LOWER_UNDERSCORE) + class A extends InSharedMemoryConfiguration { + protected A() { super(WITH_UPPER_FORMATTER); } + } + @Format(formatterClass = MyFormatter.class) + class B extends InSharedMemoryConfiguration { + protected B() { super(WITH_UPPER_FORMATTER); } + } + A a = new A(); + FieldNameFormatter fnf = FieldMapper.selectFormatter(MappingInfo.from(a)); + assertThat(fnf, sameInstance(FieldNameFormatters.LOWER_UNDERSCORE)); + + B b = new B(); + fnf = FieldMapper.selectFormatter(MappingInfo.from(b)); + assertThat(fnf, instanceOf(MyFormatter.class)); + } + + @Test + void formatterClassTakesPrecedence() { + @Format( + value = FieldNameFormatters.LOWER_UNDERSCORE, + formatterClass = MyFormatter.class + ) + class A extends InSharedMemoryConfiguration { + protected A() { super(WITH_UPPER_FORMATTER); } + } + A a = new A(); + FieldNameFormatter fnf = FieldMapper.selectFormatter(MappingInfo.from(a)); + assertThat(fnf, instanceOf(MyFormatter.class)); + } + + private static final class MyFormatter implements FieldNameFormatter { + @Override + public String fromFieldName(String fieldName) { + return fieldName; + } + } + + @Test + void fieldMapperUsesSelectFieldNameFormatter() { + @Format(FieldNameFormatters.LOWER_UNDERSCORE) + class A extends InSharedMemoryConfiguration { + private int abc = 1; + private int eFg = 2; + private int hIJ = 3; + + protected A() { super(WITH_UPPER_FORMATTER); } + } + + A a = new A(); + MappingInfo mappingInfo = MappingInfo.from(a); + Map map = FieldMapper.instanceToMap(a, mappingInfo); + + assertThat(map.get("abc"), is(1)); + assertThat(map.get("e_fg"), is(2)); + assertThat(map.get("h_i_j"), is(3)); + + map = mapOf("abc", 4, "e_fg", 5, "h_i_j", 6); + FieldMapper.instanceFromMap(a, map, mappingInfo); + assertThat(a.abc, is(4)); + assertThat(a.eFg, is(5)); + assertThat(a.hIJ, is(6)); + } } \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java index 8f75e10..a20f975 100644 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java +++ b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java @@ -21,4 +21,11 @@ class FieldNameFormattersTest { assertThat(formatter.fromFieldName("fieldNameFormat"), is("field_name_format")); } + + @Test + void upperUnderscoreConvertsFromAndToCamelCase() { + FieldNameFormatter formatter = FieldNameFormatters.UPPER_UNDERSCORE; + + assertThat(formatter.fromFieldName("fieldNameFormat"), is("FIELD_NAME_FORMAT")); + } } \ No newline at end of file diff --git a/README.md b/README.md index 9c4455f..a8e7a7b 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,16 @@ YamlProperties properties = YamlProperties.builder() .build(); ``` +Alternatively, you can annotate your `Configuration` class with the `@Format` annotation. The `FieldNameFormatter` +returned by this annotation takes precedence over the `FieldNameFormatter` returned by a `Properties` object. + +```java +@Format(FieldNameFormatters.UPPER_UNDERSCORE) +class MyConfiguration extends YamlConfiguration { + // ... +} +``` + Note: You should neither remove nor replace a formatter with one that has a different formatting style because this could break existing configurations. @@ -465,14 +475,14 @@ public final class DatabasePlugin extends JavaPlugin { de.exlll configlib-bukkit - 2.1.0 + 2.2.0 de.exlll configlib-bungee - 2.1.0 + 2.2.0 ``` #### Gradle @@ -484,9 +494,9 @@ repositories { } dependencies { // for Bukkit plugins - compile group: 'de.exlll', name: 'configlib-bukkit', version: '2.1.0' + compile group: 'de.exlll', name: 'configlib-bukkit', version: '2.2.0' // for Bungee plugins - compile group: 'de.exlll', name: 'configlib-bungee', version: '2.1.0' + compile group: 'de.exlll', name: 'configlib-bungee', version: '2.2.0' } ``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index d0ae347..61d1082 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ allprojects { group 'de.exlll' - version '2.1.0' + version '2.2.0' } subprojects { apply plugin: 'java'