From 7c456fe49a245894f3d002654ccc545175b056e0 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Tue, 30 Jul 2024 14:00:18 +0100 Subject: [PATCH] Search for indexed property names before flattened comma separated value name when loading Collections (#1202) --- .../main/docs/config/indexed-properties.md | 4 +-- .../config/ConfigMappingGenerator.java | 5 +++ .../io/smallrye/config/SmallRyeConfig.java | 24 +++++++++++--- .../config/ConfigMappingCollectionsTest.java | 31 +++++++++++++++++++ .../smallrye/config/SmallRyeConfigTest.java | 16 +++++++++- 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/documentation/src/main/docs/config/indexed-properties.md b/documentation/src/main/docs/config/indexed-properties.md index 7c474de73..51c0daaf0 100644 --- a/documentation/src/main/docs/config/indexed-properties.md +++ b/documentation/src/main/docs/config/indexed-properties.md @@ -20,8 +20,8 @@ The indexed property syntax uses the property name and square brackets with an i A call to `Config#getValues("my.collection", String.class)`, will automatically create and convert a `List` that contains the values `dog`, `cat` and `turtle`. A call to `Config#getValues("my.indexed.collection", String.class)` -returns the exact same result. For compatibility reasons, if SmallRye Config finds the same property name in their -indexed and unindexed format, the unindexed value has priority. +returns the exact same result. If SmallRye Config finds the same property name in their indexed and unindexed format, +the indexed value has priority. The indexed property is sorted by its index before being added to the target `Collection`. Any gaps in the indexes do not resolve to the target `Collection`, which means that the `Collection` result will store all values without empty diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java index 1edac5b65..c834d7164 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java @@ -960,6 +960,11 @@ private static void generateDefaults(final ClassVisitor classVisitor, final Conf .get(mapping.getInterfaceType()) .get("").entrySet()) { if (entry.getValue().hasDefaultValue()) { + // Defaults for collections also come as a simple property with comma separated values, no need for the star name + if (entry.getKey().endsWith("[*]")) { + continue; + } + mv.visitVarInsn(ALOAD, 0); mv.visitLdcInsn(entry.getKey()); mv.visitLdcInsn(entry.getValue().getDefaultValue()); diff --git a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java index 4cf379f00..bd0d84cfb 100644 --- a/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java +++ b/implementation/src/main/java/io/smallrye/config/SmallRyeConfig.java @@ -29,6 +29,7 @@ import java.io.ObjectStreamException; import java.io.Serializable; +import java.lang.reflect.Array; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; @@ -183,9 +184,9 @@ public > C getValues(String name, Class itemClass, public > C getValues(String name, Converter converter, IntFunction collectionFactory) { try { - return getValue(name, newCollectionConverter(converter, collectionFactory)); - } catch (NoSuchElementException e) { return getIndexedValues(name, converter, collectionFactory); + } catch (NoSuchElementException e) { + return getValue(name, newCollectionConverter(converter, collectionFactory)); } } @@ -355,8 +356,23 @@ public Map getMapIndexedKeys(final String name) { } @Override - public T getValue(String name, Class aClass) { - return getValue(name, requireConverter(aClass)); + @SuppressWarnings("unchecked") + public T getValue(String name, Class propertyType) { + if (propertyType.isArray()) { + ConfigValue configValue = getConfigValue(name); + if (configValue.getValue() != null) { + return getValue(name, requireConverter(propertyType)); + } + + List values = getValues(name, propertyType.getComponentType()); + Object array = Array.newInstance(propertyType.getComponentType(), values.size()); + for (int i = 0, valuesSize = values.size(); i < valuesSize; i++) { + Array.set(array, i, values.get(i)); + } + return (T) array; + } + + return getValue(name, requireConverter(propertyType)); } /** diff --git a/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java b/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java index 88c7e5923..d097f75d5 100644 --- a/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java +++ b/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java @@ -1000,4 +1000,35 @@ interface AnotherNested { } } } + + @Test + void overrideListDefaults() { + SmallRyeConfig config = new SmallRyeConfigBuilder() + .withSources(config( + "list.defaults.values[0]", "baz", + "list.defaults.list-nested[0].value", "value")) + .withMapping(ListDefaults.class) + .build(); + + ListDefaults mapping = config.getConfigMapping(ListDefaults.class); + assertEquals(1, mapping.values().size()); + assertEquals("baz", mapping.values().get(0)); + assertNull(config.getRawValue("list.defaults.values[9]")); + } + + @ConfigMapping(prefix = "list.defaults") + interface ListDefaults { + @WithDefault("foo,bar") + List values(); + + List listNested(); + + interface Nested { + @WithDefault("value") + String value(); + + @WithDefault("one,two") + List list(); + } + } } diff --git a/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java b/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java index fb06533d0..4894bdd71 100644 --- a/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java +++ b/implementation/src/test/java/io/smallrye/config/SmallRyeConfigTest.java @@ -127,6 +127,19 @@ void getIndexedValues() { @Test void getValuesNotIndexed() { + SmallRyeConfig config = new SmallRyeConfigBuilder() + .withSources(config( + "server.environments", "dev,qa")) + .build(); + + List environments = config.getValues("server.environments", String.class); + assertEquals(2, environments.size()); + assertEquals("dev", environments.get(0)); + assertEquals("qa", environments.get(1)); + } + + @Test + void getValuesIndexedPriority() { SmallRyeConfig config = new SmallRyeConfigBuilder() .withSources(config( "server.environments", "dev,qa", @@ -136,9 +149,10 @@ void getValuesNotIndexed() { .build(); List environments = config.getValues("server.environments", String.class); - assertEquals(2, environments.size()); + assertEquals(3, environments.size()); assertEquals("dev", environments.get(0)); assertEquals("qa", environments.get(1)); + assertEquals("prod", environments.get(2)); } @Test