diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMapping.java b/implementation/src/main/java/io/smallrye/config/ConfigMapping.java
index 16577577e..0710f75b0 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigMapping.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigMapping.java
@@ -52,7 +52,21 @@
*/
NamingStrategy namingStrategy() default NamingStrategy.KEBAB_CASE;
- enum NamingStrategy {
+ /**
+ * Match config mapping method names and configuration properties using bean-style getter names. This removes the
+ * prefixes get
or is
and decapitalizes the next character. By default, bean-style getter
+ * matching is disabled
.
+ *
+ * Config mapping declarations should prefer to use simple names that match one-to-one with their configuration
+ * names. Bean-style getter matching allows multiple method names to match the same configuration name. For
+ * instance, getFoo
and isFoo
both match foo
, which may not be intended.
+ *
+ * @return a boolean true
to enable bean style getter names matching, or false
to disable
+ * it.
+ */
+ boolean beanStyleGetters() default false;
+
+ enum NamingStrategy implements Function {
/**
* The method name is used as is to map the configuration property.
*/
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java
index 4a95875b4..405fd3da4 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java
@@ -1,5 +1,6 @@
package io.smallrye.config;
+import static io.smallrye.config.ConfigMappingLoader.configMappingNames;
import static io.smallrye.config.ConfigValidationException.Problem;
import static io.smallrye.config.ProfileConfigSourceInterceptor.activeName;
import static io.smallrye.config.common.utils.StringUtil.unindexed;
@@ -38,8 +39,9 @@ public final class ConfigMappingContext {
private final Map, Map> roots = new IdentityHashMap<>();
private final Map, Converter>> converterInstances = new IdentityHashMap<>();
- private NamingStrategy namingStrategy;
- private String rootPath;
+ private NamingStrategy namingStrategy = NamingStrategy.KEBAB_CASE;
+ private boolean beanStyleGetters = false;
+ private String rootPath = null;
private final StringBuilder nameBuilder = new StringBuilder();
private final Set usedProperties = new HashSet<>();
private final List problems = new ArrayList<>();
@@ -51,8 +53,7 @@ public Map>> get() {
// All mapping names must be loaded first because of split mappings
Map>> names = new HashMap<>();
for (Map.Entry, Set> mapping : roots.entrySet()) {
- for (Map.Entry>> entry : ConfigMappingLoader
- .configMappingNames(mapping.getKey()).entrySet()) {
+ for (Map.Entry>> entry : configMappingNames(mapping.getKey()).entrySet()) {
names.putIfAbsent(entry.getKey(), new HashMap<>());
names.get(entry.getKey()).putAll(entry.getValue());
}
@@ -73,9 +74,7 @@ public Map>> get() {
for (Map.Entry, Set> mapping : roots.entrySet()) {
Map mappingObjects = new HashMap<>();
for (String rootPath : mapping.getValue()) {
- applyNamingStrategy(null);
applyRootPath(rootPath);
- applyNameBuilder(rootPath);
mappingObjects.put(rootPath, (ConfigMappingObject) constructRoot(mapping.getKey()));
}
this.roots.put(mapping.getKey(), mappingObjects);
@@ -88,8 +87,10 @@ T constructRoot(Class interfaceType) {
public T constructGroup(Class interfaceType) {
NamingStrategy namingStrategy = this.namingStrategy;
+ boolean beanStyleGetters = this.beanStyleGetters;
T mappingObject = ConfigMappingLoader.configMappingObject(interfaceType, this);
- this.namingStrategy = applyNamingStrategy(namingStrategy);
+ applyNamingStrategy(namingStrategy);
+ applyBeanStyleGetters(beanStyleGetters);
return mappingObject;
}
@@ -121,23 +122,37 @@ public Converter getConverterInstance(Class extends Converter extends
});
}
- public NamingStrategy applyNamingStrategy(final NamingStrategy namingStrategy) {
+ public void applyNamingStrategy(final NamingStrategy namingStrategy) {
if (namingStrategy != null) {
this.namingStrategy = namingStrategy;
- } else if (this.namingStrategy == null) {
- this.namingStrategy = NamingStrategy.KEBAB_CASE;
}
- return this.namingStrategy;
}
- public String applyRootPath(final String rootPath) {
- this.rootPath = rootPath;
- return this.rootPath;
+ public void applyBeanStyleGetters(final Boolean beanStyleGetters) {
+ if (beanStyleGetters != null) {
+ this.beanStyleGetters = beanStyleGetters;
+ }
}
- public StringBuilder applyNameBuilder(final String rootPath) {
+ public void applyRootPath(final String rootPath) {
+ this.rootPath = rootPath;
this.nameBuilder.replace(0, nameBuilder.length(), rootPath);
- return this.nameBuilder;
+ }
+
+ private static final Function BEAN_STYLE_GETTERS = new Function() {
+ @Override
+ public String apply(final String name) {
+ if (name.startsWith("get") && name.length() > 3) {
+ return Character.toLowerCase(name.charAt(3)) + name.substring(4);
+ } else if (name.startsWith("is") && name.length() > 2) {
+ return Character.toLowerCase(name.charAt(2)) + name.substring(3);
+ }
+ return name;
+ }
+ };
+
+ public Function propertyName() {
+ return beanStyleGetters ? BEAN_STYLE_GETTERS.andThen(namingStrategy) : namingStrategy;
}
public StringBuilder getNameBuilder() {
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java
index 7992604d4..7dffea9f6 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java
@@ -61,6 +61,7 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
@@ -110,7 +111,7 @@ public class ConfigMappingGenerator {
private static final int V_MAPPING_CONTEXT = 1;
private static final int V_STRING_BUILDER = 2;
private static final int V_LENGTH = 3;
- private static final int V_NAMING_STRATEGY = 4;
+ private static final int V_NAMING_FUNCTION = 4;
/**
* Generates the backing implementation of an interface annotated with the {@link ConfigMapping} annotation.
@@ -164,19 +165,25 @@ static byte[] generate(final ConfigMappingInterface mapping) {
ctor.visitLabel(ctorLenStart);
ctor.visitVarInsn(ISTORE, V_LENGTH);
- Label ctorNsStart = new Label();
- ctor.visitLabel(ctorNsStart);
- ctor.visitVarInsn(ALOAD, V_MAPPING_CONTEXT);
+ Label ctorNfStart = new Label();
+ ctor.visitLabel(ctorNfStart);
- if (mapping.hasNamingStrategy()) {
+ if (mapping.hasConfigMapping()) {
+ ctor.visitVarInsn(ALOAD, V_MAPPING_CONTEXT);
ctor.visitFieldInsn(GETSTATIC, I_NAMING_STRATEGY, mapping.getNamingStrategy().name(),
"L" + I_NAMING_STRATEGY + ";");
- } else {
- ctor.visitInsn(ACONST_NULL);
+ ctor.visitMethodInsn(INVOKEVIRTUAL, I_MAPPING_CONTEXT, "applyNamingStrategy", "(L" + I_NAMING_STRATEGY + ";)V",
+ false);
+
+ ctor.visitVarInsn(ALOAD, V_MAPPING_CONTEXT);
+ ctor.visitInsn(mapping.isBeanStyleGetters() ? ICONST_1 : ICONST_0);
+ ctor.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
+ ctor.visitMethodInsn(INVOKEVIRTUAL, I_MAPPING_CONTEXT, "applyBeanStyleGetters", "(Ljava/lang/Boolean;)V", false);
}
- ctor.visitMethodInsn(INVOKEVIRTUAL, I_MAPPING_CONTEXT, "applyNamingStrategy",
- "(L" + I_NAMING_STRATEGY + ";)L" + I_NAMING_STRATEGY + ";", false);
- ctor.visitVarInsn(ASTORE, V_NAMING_STRATEGY);
+
+ ctor.visitVarInsn(ALOAD, V_MAPPING_CONTEXT);
+ ctor.visitMethodInsn(INVOKEVIRTUAL, I_MAPPING_CONTEXT, "propertyName", "()Ljava/util/function/Function;", false);
+ ctor.visitVarInsn(ASTORE, V_NAMING_FUNCTION);
addProperties(visitor, ctor, new HashSet<>(), mapping, mapping.getClassInternalName());
@@ -185,8 +192,7 @@ static byte[] generate(final ConfigMappingInterface mapping) {
ctor.visitLocalVariable("mc", 'L' + I_MAPPING_CONTEXT + ';', null, ctorStart, ctorEnd, V_MAPPING_CONTEXT);
ctor.visitLocalVariable("sb", 'L' + I_STRING_BUILDER + ';', null, ctorSbStart, ctorEnd, V_STRING_BUILDER);
ctor.visitLocalVariable("len", "I", null, ctorLenStart, ctorEnd, V_LENGTH);
- ctor.visitLocalVariable("ns", "Lio/smallrye/config/ConfigMapping$NamingStrategy;", null, ctorNsStart, ctorEnd,
- V_NAMING_STRATEGY);
+ ctor.visitLocalVariable("nf", "Ljava/util/function/Function;", null, ctorNfStart, ctorEnd, V_NAMING_FUNCTION);
ctor.visitEnd();
ctor.visitMaxs(0, 0);
visitor.visitEnd();
@@ -711,9 +717,11 @@ private static void appendPropertyName(final MethodVisitor ctor, final Property
if (property.hasPropertyName()) {
ctor.visitLdcInsn(property.getPropertyName());
} else {
- ctor.visitVarInsn(ALOAD, V_NAMING_STRATEGY);
+ ctor.visitVarInsn(ALOAD, V_NAMING_FUNCTION);
ctor.visitLdcInsn(property.getPropertyName());
- ctor.visitMethodInsn(INVOKEVIRTUAL, I_NAMING_STRATEGY, "apply", "(L" + I_STRING + ";)L" + I_STRING + ";", false);
+ ctor.visitMethodInsn(INVOKEINTERFACE, getInternalName(Function.class), "apply",
+ "(L" + I_OBJECT + ";)L" + I_OBJECT + ";", true);
+ ctor.visitTypeInsn(CHECKCAST, "java/lang/String");
}
ctor.visitMethodInsn(INVOKEVIRTUAL, I_STRING_BUILDER, "append", "(L" + I_STRING + ";)L" + I_STRING_BUILDER + ';',
diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingInterface.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingInterface.java
index 7edc1a368..0b61b88c7 100644
--- a/implementation/src/main/java/io/smallrye/config/ConfigMappingInterface.java
+++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingInterface.java
@@ -60,12 +60,8 @@ protected ConfigMappingInterface computeValue(final Class> type) {
toStringMethod = (ToStringMethod) property;
}
}
- if (toStringMethod == null) {
- toStringMethod = ToStringMethod.NONE;
- }
-
this.properties = filteredProperties.toArray(new Property[0]);
- this.toStringMethod = toStringMethod;
+ this.toStringMethod = toStringMethod != null ? toStringMethod : ToStringMethod.NONE;
}
static String getImplementationClassName(Class> type) {
@@ -153,7 +149,11 @@ private static Map getSuperProperties(ConfigMappingInterface t
return properties;
}
- public boolean hasNamingStrategy() {
+ ToStringMethod getToStringMethod() {
+ return toStringMethod;
+ }
+
+ public boolean hasConfigMapping() {
return interfaceType.getAnnotation(ConfigMapping.class) != null;
}
@@ -162,8 +162,9 @@ public NamingStrategy getNamingStrategy() {
return configMapping != null ? configMapping.namingStrategy() : NamingStrategy.KEBAB_CASE;
}
- ToStringMethod getToStringMethod() {
- return toStringMethod;
+ public boolean isBeanStyleGetters() {
+ ConfigMapping configMapping = interfaceType.getAnnotation(ConfigMapping.class);
+ return configMapping != null && configMapping.beanStyleGetters();
}
String getClassInternalName() {
diff --git a/implementation/src/test/java/io/smallrye/config/ConfigMappingNamingStrategyTest.java b/implementation/src/test/java/io/smallrye/config/ConfigMappingNamingStrategyTest.java
index 1c45aadf1..969a58d42 100644
--- a/implementation/src/test/java/io/smallrye/config/ConfigMappingNamingStrategyTest.java
+++ b/implementation/src/test/java/io/smallrye/config/ConfigMappingNamingStrategyTest.java
@@ -13,6 +13,7 @@
import java.util.Optional;
import org.eclipse.microprofile.config.inject.ConfigProperties;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
public class ConfigMappingNamingStrategyTest {
@@ -283,4 +284,67 @@ interface VerbatimDefaults {
String verbatimDefault();
}
}
+
+ @Test
+ @Disabled
+ void beanStyleGetters() {
+ SmallRyeConfig config = new SmallRyeConfigBuilder()
+ .withSources(config(
+ "get.name", "value",
+ "get.boolean", "true",
+ "get.get", "value",
+ "get.is", "true",
+ "get.x", "value",
+ "get.nested.name", "value",
+ "get.nested.reset.get-name", "value",
+ "get.nested.reset.get-reset-again.name", "value"))
+ .withMapping(BeanStyleGetters.class)
+ .build();
+
+ BeanStyleGetters mapping = config.getConfigMapping(BeanStyleGetters.class);
+ assertEquals("value", mapping.getName());
+ assertTrue(mapping.isBoolean());
+ assertEquals("value", mapping.get());
+ assertTrue(mapping.is());
+ assertEquals("value", mapping.getX());
+ assertEquals("value", mapping.isX());
+ assertEquals("value", mapping.getNested().getName());
+ assertEquals("value", mapping.getNested().getReset().getName());
+ assertEquals("value", mapping.getNested().getReset().getResetAgain().getName());
+ }
+
+ @ConfigMapping(prefix = "get", beanStyleGetters = true)
+ interface BeanStyleGetters {
+ String getName();
+
+ boolean isBoolean();
+
+ String get();
+
+ boolean is();
+
+ String getX();
+
+ String isX();
+
+ Nested getNested();
+
+ interface Nested {
+ String getName();
+
+ Reset getReset();
+
+ @ConfigMapping
+ interface Reset {
+ String getName();
+
+ ResetAgain getResetAgain();
+
+ @ConfigMapping(beanStyleGetters = true)
+ interface ResetAgain {
+ String getName();
+ }
+ }
+ }
+ }
}
diff --git a/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java b/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java
index 78daece46..49d90ca23 100644
--- a/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java
+++ b/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java
@@ -340,7 +340,6 @@ void unnamedKeys() {
ConfigMappingContext context = new ConfigMappingContext(config,
ConfigMappingLoader.getConfigMapping(UnnamedKeys.class).getNames(), new HashMap<>());
context.applyRootPath("unnamed");
- context.getNameBuilder().append("unnamed");
UnnamedKeys mapping = new UnnamedKeysImpl(context);
assertEquals("unnamed", mapping.map().get(null).value());
@@ -400,7 +399,6 @@ void mapDefaults() {
ConfigMappingContext context = new ConfigMappingContext(config,
ConfigMappingLoader.getConfigMapping(MapDefaults.class).getNames(), new HashMap<>());
context.applyRootPath("map");
- context.getNameBuilder().append("map.");
MapDefaults mapping = new MapDefaultsImpl(context);
assertEquals("value", mapping.defaults().get("one"));
@@ -451,6 +449,7 @@ public MapDefaultsImpl(ConfigMappingContext context) {
int length = sb.length();
ConfigMapping.NamingStrategy ns = ConfigMapping.NamingStrategy.KEBAB_CASE;
+ sb.append(".");
sb.append(ns.apply("defaults"));
ConfigMappingContext.ObjectCreator