diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java index 512fe078c..2a487ec0a 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingContext.java @@ -167,6 +167,54 @@ Map, Map> getMappings() { return mappings; } + void reportUnknown(final Set ignoredPaths) { + Set ignoredNames = new HashSet<>(); + Set ignoredPrefixes = new HashSet<>(); + for (String ignoredPath : ignoredPaths) { + if (ignoredPath.endsWith(".**")) { + ignoredPrefixes.add(ignoredPath.substring(0, ignoredPath.length() - 3)); + } else { + ignoredNames.add(new PropertyName(ignoredPath)); + } + } + + Set prefixes = new HashSet<>(); + for (Map value : this.mappings.values()) { + prefixes.addAll(value.keySet()); + } + if (prefixes.contains("")) { + prefixes.clear(); + } + + propertyNames: for (String propertyName : config.getPropertyNames()) { + if (usedProperties.contains(propertyName)) { + continue; + } + + if (ignoredNames.contains(new PropertyName(propertyName))) { + continue; + } + + for (String ignoredPrefix : ignoredPrefixes) { + if (propertyName.startsWith(ignoredPrefix)) { + continue propertyNames; + } + } + + for (String prefix : prefixes) { + if (isPropertyInRoot(propertyName, prefix)) { + ConfigValue configValue = config.getConfigValue(propertyName); + // TODO - https://github.com/quarkusio/quarkus/issues/38479 + if (configValue.getSourceName() != null && configValue.getSourceName().startsWith(EnvConfigSource.NAME)) { + continue; + } + problems.add(new Problem( + ConfigMessages.msg.propertyDoesNotMapToAnyRoot(propertyName, configValue.getLocation()))); + } + } + } + } + // TODO - We shouldn't be mutating the EnvSource. // We should do the calculation when creating the EnvSource, but right now mappings and sources are not well integrated. private void matchPropertiesWithEnv(final Map, Set> mappings) { @@ -281,54 +329,6 @@ private static List indexOfDashes(final String mappedProperty, final St return dashesPosition; } - void reportUnknown(final Set ignoredPaths) { - Set ignoredNames = new HashSet<>(); - Set ignoredPrefixes = new HashSet<>(); - for (String ignoredPath : ignoredPaths) { - if (ignoredPath.endsWith(".**")) { - ignoredPrefixes.add(ignoredPath.substring(0, ignoredPath.length() - 3)); - } else { - ignoredNames.add(new PropertyName(ignoredPath)); - } - } - - Set prefixes = new HashSet<>(); - for (Map value : this.mappings.values()) { - prefixes.addAll(value.keySet()); - } - if (prefixes.contains("")) { - prefixes.clear(); - } - - propertyNames: for (String propertyName : config.getPropertyNames()) { - if (usedProperties.contains(propertyName)) { - continue; - } - - if (ignoredNames.contains(new PropertyName(propertyName))) { - continue; - } - - for (String ignoredPrefix : ignoredPrefixes) { - if (propertyName.startsWith(ignoredPrefix)) { - continue propertyNames; - } - } - - for (String prefix : prefixes) { - if (isPropertyInRoot(propertyName, prefix)) { - ConfigValue configValue = config.getConfigValue(propertyName); - // TODO - https://github.com/quarkusio/quarkus/issues/38479 - if (configValue.getSourceName() != null && configValue.getSourceName().startsWith(EnvConfigSource.NAME)) { - continue; - } - problems.add(new Problem( - ConfigMessages.msg.propertyDoesNotMapToAnyRoot(propertyName, configValue.getLocation()))); - } - } - } - } - private static boolean isPropertyInRoot(final String property, final String root) { if (property.equals(root)) { return true; @@ -626,22 +626,40 @@ public Optional apply(final String path) { return this; } + public static V value( + final ConfigMappingContext context, + final String propertyName, + final Class valueRawType, + final Class> valueConvertWith) { + context.usedProperties.add(propertyName); + Converter valueConverter = getConverter(context, valueRawType, valueConvertWith); + return context.config.getValue(propertyName, valueConverter); + } + public ObjectCreator value( final Class valueRawType, final Class> valueConvertWith) { for (Consumer> creator : creators) { creator.accept(new Function() { @Override - public T apply(final String propertyName) { - usedProperties.add(propertyName); - Converter valueConverter = getConverter(valueRawType, valueConvertWith); - return config.getValue(propertyName, valueConverter); + public Object apply(final String propertyName) { + return value(ConfigMappingContext.this, propertyName, valueRawType, valueConvertWith); } }); } return this; } + public static Optional optionalValue( + final ConfigMappingContext context, + final String propertyName, + final Class valueRawType, + final Class> valueConvertWith) { + context.usedProperties.add(propertyName); + Converter valueConverter = getConverter(context, valueRawType, valueConvertWith); + return context.config.getOptionalValue(propertyName, valueConverter); + } + public ObjectCreator optionalValue( final Class valueRawType, final Class> valueConvertWith) { @@ -649,15 +667,26 @@ public ObjectCreator optionalValue( creator.accept(new Function() { @Override public Optional apply(final String propertyName) { - usedProperties.add(propertyName); - Converter valueConverter = getConverter(valueRawType, valueConvertWith); - return config.getOptionalValue(propertyName, valueConverter); + return optionalValue(ConfigMappingContext.this, propertyName, valueRawType, valueConvertWith); } }); } return this; } + public static > C values( + final ConfigMappingContext context, + final String propertyName, + final Class itemRawType, + final Class> itemConvertWith, + final Class collectionRawType) { + context.usedProperties.add(propertyName); + context.usedProperties.addAll(context.config.getIndexedProperties(propertyName)); + Converter itemConverter = getConverter(context, itemRawType, itemConvertWith); + IntFunction collectionFactory = (IntFunction) createCollectionFactory(collectionRawType); + return context.config.getValues(propertyName, itemConverter, collectionFactory); + } + public > ObjectCreator values( final Class itemRawType, final Class> itemConvertWith, @@ -665,19 +694,27 @@ public > ObjectCreator values( for (Consumer> creator : creators) { creator.accept(new Function() { @Override - public T apply(final String propertyName) { - usedProperties.add(propertyName); - usedProperties.addAll(config.getIndexedProperties(propertyName)); - Converter itemConverter = itemConvertWith == null ? config.requireConverter(itemRawType) - : getConverterInstance(itemConvertWith); - IntFunction collectionFactory = (IntFunction) createCollectionFactory(collectionRawType); - return (T) config.getValues(propertyName, itemConverter, collectionFactory); + public Object apply(final String propertyName) { + return values(ConfigMappingContext.this, propertyName, itemRawType, itemConvertWith, collectionRawType); } }); } return this; } + public static > Optional optionalValues( + final ConfigMappingContext context, + final String propertyName, + final Class itemRawType, + final Class> itemConvertWith, + final Class collectionRawType) { + context.usedProperties.add(propertyName); + context.usedProperties.addAll(context.config.getIndexedProperties(propertyName)); + Converter itemConverter = getConverter(context, itemRawType, itemConvertWith); + IntFunction collectionFactory = (IntFunction) createCollectionFactory(collectionRawType); + return context.config.getOptionalValues(propertyName, itemConverter, collectionFactory); + } + public > ObjectCreator optionalValues( final Class itemRawType, final Class> itemConvertWith, @@ -685,18 +722,60 @@ public > ObjectCreator optionalValues( for (Consumer> creator : creators) { creator.accept(new Function() { @Override - public T apply(final String propertyName) { - usedProperties.add(propertyName); - usedProperties.addAll(config.getIndexedProperties(propertyName)); - Converter itemConverter = getConverter(itemRawType, itemConvertWith); - IntFunction collectionFactory = (IntFunction) createCollectionFactory(collectionRawType); - return (T) config.getOptionalValues(propertyName, itemConverter, collectionFactory); + public Object apply(final String propertyName) { + return optionalValues(ConfigMappingContext.this, propertyName, itemRawType, itemConvertWith, + collectionRawType); } }); } return this; } + public static Map values( + final ConfigMappingContext context, + final String propertyName, + final Class keyRawType, + final Class> keyConvertWith, + final Class valueRawType, + final Class> valueConvertWith, + final Iterable keys, + final String defaultValue) { + + Converter keyConverter = getConverter(context, keyRawType, keyConvertWith); + Converter valueConverter = getConverter(context, valueRawType, valueConvertWith); + + Map mapKeys = new HashMap<>(); + if (keys != null) { + for (String key : keys) { + mapKeys.put(key, propertyName + "." + quoted(key)); + } + } + if (mapKeys.isEmpty()) { + mapKeys = context.config.getMapKeys(propertyName); + } + + IntFunction> mapFactory; + if (defaultValue != null) { + mapFactory = new IntFunction<>() { + @Override + public Map apply(final int value) { + return new MapWithDefault<>(valueConverter.convert(defaultValue)); + } + }; + } else { + mapFactory = new IntFunction>() { + @Override + public Map apply(final int size) { + return new HashMap<>(size); + } + }; + } + + context.usedProperties.add(propertyName); + context.usedProperties.addAll(mapKeys.values()); + return context.config.getMapValues(mapKeys, keyConverter, valueConverter, mapFactory); + } + public ObjectCreator values( final Class keyRawType, final Class> keyConvertWith, @@ -705,47 +784,66 @@ public ObjectCreator values( final Iterable keys, final String defaultValue) { for (Consumer> creator : creators) { - Function values = new Function<>() { + creator.accept(new Function() { @Override public Object apply(final String propertyName) { - Converter keyConverter = getConverter(keyRawType, keyConvertWith); - Converter valueConverter = getConverter(valueRawType, valueConvertWith); + return values(ConfigMappingContext.this, propertyName, keyRawType, keyConvertWith, valueRawType, + valueConvertWith, keys, defaultValue); + } + }); + } + return this; + } - Map mapKeys = new HashMap<>(); - if (keys != null) { - for (String key : keys) { - mapKeys.put(key, propertyName + "." + quoted(key)); - } - } - if (mapKeys.isEmpty()) { - mapKeys = config.getMapKeys(propertyName); - } + public static > Map values( + final ConfigMappingContext context, + final String propertyName, + final Class keyRawType, + final Class> keyConvertWith, + final Class valueRawType, + final Class> valueConvertWith, + final Class collectionRawType, + final Iterable keys, + final String defaultValue) { - IntFunction> mapFactory; - if (defaultValue != null) { - mapFactory = new IntFunction<>() { - @Override - public Map apply(final int value) { - return new MapWithDefault<>(valueConverter.convert(defaultValue)); - } - }; - } else { - mapFactory = new IntFunction>() { - @Override - public Map apply(final int size) { - return new HashMap<>(size); - } - }; - } + Converter keyConverter = getConverter(context, keyRawType, keyConvertWith); + Converter valueConverter = getConverter(context, valueRawType, valueConvertWith); + + Map mapKeys = new HashMap<>(); + if (keys != null) { + for (String key : keys) { + mapKeys.put(key, propertyName + "." + quoted(key)); + } + } + if (mapKeys.isEmpty()) { + mapKeys = context.config.getMapIndexedKeys(propertyName); + } - usedProperties.add(propertyName); - usedProperties.addAll(mapKeys.values()); - return config.getMapValues(mapKeys, keyConverter, valueConverter, mapFactory); + IntFunction collectionFactory = (IntFunction) createCollectionFactory(collectionRawType); + IntFunction> mapFactory; + if (defaultValue != null) { + mapFactory = new IntFunction<>() { + @Override + public Map apply(final int value) { + return new MapWithDefault<>( + Converters.newCollectionConverter(valueConverter, collectionFactory) + .convert(defaultValue)); + } + }; + } else { + mapFactory = new IntFunction>() { + @Override + public Map apply(final int size) { + return new HashMap<>(size); } }; - creator.accept(values); } - return this; + + context.usedProperties.add(propertyName); + context.usedProperties.addAll(mapKeys.values()); + // map keys can be indexed or unindexed, so we need to find which ones exist to mark them as used + context.usedProperties.addAll(context.config.getMapKeys(propertyName).values()); + return context.config.getMapIndexedValues(mapKeys, keyConverter, valueConverter, mapFactory, collectionFactory); } public > ObjectCreator values( @@ -757,50 +855,13 @@ public > ObjectCreator values( final Iterable keys, final String defaultValue) { for (Consumer> creator : creators) { - Function values = new Function<>() { + creator.accept(new Function() { @Override public Object apply(final String propertyName) { - Converter keyConverter = getConverter(keyRawType, keyConvertWith); - Converter valueConverter = getConverter(valueRawType, valueConvertWith); - - Map mapKeys = new HashMap<>(); - if (keys != null) { - for (String key : keys) { - mapKeys.put(key, propertyName + "." + quoted(key)); - } - } - if (mapKeys.isEmpty()) { - mapKeys = config.getMapIndexedKeys(propertyName); - } - - IntFunction collectionFactory = (IntFunction) createCollectionFactory(collectionRawType); - IntFunction> mapFactory; - if (defaultValue != null) { - mapFactory = new IntFunction<>() { - @Override - public Map apply(final int value) { - return new MapWithDefault<>( - Converters.newCollectionConverter(valueConverter, collectionFactory) - .convert(defaultValue)); - } - }; - } else { - mapFactory = new IntFunction>() { - @Override - public Map apply(final int size) { - return new HashMap<>(size); - } - }; - } - - usedProperties.add(propertyName); - usedProperties.addAll(mapKeys.values()); - // map keys can be indexed or unindexed, so we need to find which ones exist to mark them as used - usedProperties.addAll(config.getMapKeys(propertyName).values()); - return config.getMapIndexedValues(mapKeys, keyConverter, valueConverter, mapFactory, collectionFactory); + return values(ConfigMappingContext.this, propertyName, keyRawType, keyConvertWith, valueRawType, + valueConvertWith, collectionRawType, keys, defaultValue); } - }; - creator.accept(values); + }); } return this; } @@ -809,10 +870,6 @@ public T get() { return root; } - private Converter getConverter(final Class rawType, final Class> convertWith) { - return convertWith == null ? config.requireConverter(rawType) : getConverterInstance(convertWith); - } - /** * Matches that at least one runtime configuration name is in relative path of a mapping class. This is * required to trigger the construction of lazy mapping objects like Optional or Map. @@ -850,7 +907,12 @@ private boolean createRequired(final Class groupType, final String path) return false; } - private IntFunction> createCollectionFactory(final Class type) { + private static Converter getConverter(final ConfigMappingContext context, final Class rawType, + final Class> convertWith) { + return convertWith == null ? context.config.requireConverter(rawType) : context.getConverterInstance(convertWith); + } + + private static IntFunction> createCollectionFactory(final Class type) { if (type == List.class) { return ArrayList::new; } @@ -862,7 +924,7 @@ private IntFunction> createCollectionFactory(final Class type) throw new IllegalArgumentException(); } - private String quoted(final String key) { + private static String quoted(final String key) { NameIterator keyIterator = new NameIterator(key); keyIterator.next(); return keyIterator.hasNext() ? "\"" + key + "\"" : key; diff --git a/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java b/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java index 013ad7e6d..1dd2942d2 100644 --- a/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java +++ b/implementation/src/main/java/io/smallrye/config/ConfigMappingGenerator.java @@ -60,8 +60,10 @@ import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Collection; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; @@ -100,14 +102,19 @@ public class ConfigMappingGenerator { private static final String I_CLASS = getInternalName(Class.class); private static final String I_FIELD = getInternalName(Field.class); + private static final String I_CONFIGURATION_OBJECT = getInternalName(ConfigMappingObject.class); private static final String I_MAPPING_CONTEXT = getInternalName(ConfigMappingContext.class); - private static final String I_NAMING_STRATEGY = getInternalName(NamingStrategy.class); private static final String I_OBJECT_CREATOR = getInternalName(ConfigMappingContext.ObjectCreator.class); - private static final String I_OBJECT = getInternalName(Object.class); - private static final String I_RUNTIME_EXCEPTION = getInternalName(RuntimeException.class); + private static final String I_NAMING_STRATEGY = getInternalName(NamingStrategy.class); private static final String I_STRING_BUILDER = getInternalName(StringBuilder.class); + + private static final String I_RUNTIME_EXCEPTION = getInternalName(RuntimeException.class); + private static final String I_OBJECT = getInternalName(Object.class); private static final String I_STRING = getInternalName(String.class); + private static final String I_OPTIONAL = getInternalName(Optional.class); + private static final String I_MAP = getInternalName(Map.class); + private static final String I_COLLECTION = getInternalName(Collection.class); private static final String I_ITERABLE = getInternalName(Iterable.class); private static final int V_THIS = 0; @@ -434,22 +441,12 @@ private static void addProperties( Label _continue = new Label(); ctor.visitTryCatchBlock(_try, _catch, _catch, I_RUNTIME_EXCEPTION); - appendPropertyName(ctor, property); - ctor.visitVarInsn(ALOAD, V_THIS); - ctor.visitTypeInsn(NEW, I_OBJECT_CREATOR); - ctor.visitInsn(DUP); - ctor.visitVarInsn(ALOAD, V_MAPPING_CONTEXT); - ctor.visitVarInsn(ALOAD, V_STRING_BUILDER); - ctor.visitMethodInsn(INVOKEVIRTUAL, I_STRING_BUILDER, "toString", "()L" + I_STRING + ';', false); - ctor.visitMethodInsn(INVOKESPECIAL, I_OBJECT_CREATOR, "", "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + ";)V", - false); - // try ctor.visitLabel(_try); + ctor.visitVarInsn(ALOAD, V_THIS); generateProperty(ctor, property); - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "get", "()L" + I_OBJECT + ";", false); if (property.isPrimitive()) { PrimitiveProperty primitive = property.asPrimitive(); ctor.visitTypeInsn(CHECKCAST, getInternalName(primitive.getBoxType())); @@ -492,56 +489,218 @@ private static void addProperties( } private static void generateProperty(final MethodVisitor ctor, final Property property) { - if (property.isLeaf() || property.isPrimitive() || property.isLeaf() && property.isOptional()) { - Class rawType = property.isLeaf() ? property.asLeaf().getValueRawType() : property.asPrimitive().getBoxType(); - ctor.visitLdcInsn(Type.getType(rawType)); + appendPropertyName(ctor, property); + + if (property.isLeaf() && !property.isOptional() || property.isPrimitive()) { + toStringPropertyName(ctor); + ctor.visitLdcInsn(Type + .getType(property.isLeaf() ? property.asLeaf().getValueRawType() : property.asPrimitive().getBoxType())); if (property.hasConvertWith() || property.isLeaf() && property.asLeaf().hasConvertWith()) { ctor.visitLdcInsn(getType( property.isLeaf() ? property.asLeaf().getConvertWith() : property.asPrimitive().getConvertWith())); } else { ctor.visitInsn(ACONST_NULL); } - if (property.isOptional()) { - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "optionalValue", - "(L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); + ctor.visitMethodInsn(INVOKESTATIC, I_OBJECT_CREATOR, "value", + "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + ";L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OBJECT + ";", + false); + } else if (property.isOptional() && property.isLeaf()) { + toStringPropertyName(ctor); + LeafProperty optionalProperty = property.asLeaf(); + ctor.visitLdcInsn(Type.getType(optionalProperty.getValueRawType())); + if (optionalProperty.hasConvertWith()) { + ctor.visitLdcInsn(getType(optionalProperty.getConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitMethodInsn(INVOKESTATIC, I_OBJECT_CREATOR, "optionalValue", + "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + ";L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OPTIONAL + ";", + false); + } else if (property.isMap() && property.asMap().getValueProperty().isLeaf()) { + toStringPropertyName(ctor); + MapProperty mapProperty = property.asMap(); + Property valueProperty = mapProperty.getValueProperty(); + ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); + if (mapProperty.hasKeyConvertWith()) { + ctor.visitLdcInsn(getType(mapProperty.getKeyConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + LeafProperty leafProperty = valueProperty.asLeaf(); + ctor.visitLdcInsn(getType(leafProperty.getValueRawType())); + if (leafProperty.hasConvertWith()) { + ctor.visitLdcInsn(getType(leafProperty.getConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + if (mapProperty.hasKeyProvider()) { + generateMapKeysProvider(ctor, mapProperty.getKeysProvider()); + } else { + ctor.visitInsn(ACONST_NULL); + } + if (mapProperty.hasDefaultValue() && mapProperty.getDefaultValue() != null) { + ctor.visitLdcInsn(mapProperty.getDefaultValue()); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitMethodInsn(INVOKESTATIC, I_OBJECT_CREATOR, "values", + "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + ";L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";L" + + I_CLASS + ";L" + I_ITERABLE + ";L" + I_STRING + ";)L" + I_MAP + ";", + false); + } else if (property.isMap() && property.asMap().getValueProperty().isCollection() + && property.asMap().getValueProperty().asCollection().getElement().isLeaf()) { + toStringPropertyName(ctor); + MapProperty mapProperty = property.asMap(); + Property valueProperty = mapProperty.getValueProperty(); + ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); + if (mapProperty.hasKeyConvertWith()) { + ctor.visitLdcInsn(getType(mapProperty.getKeyConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + LeafProperty leafProperty = valueProperty.asCollection().getElement().asLeaf(); + ctor.visitLdcInsn(getType(leafProperty.getValueRawType())); + if (leafProperty.hasConvertWith()) { + ctor.visitLdcInsn(getType(leafProperty.getConvertWith())); } else { - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "value", - "(L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); + ctor.visitInsn(ACONST_NULL); + } + ctor.visitLdcInsn(getType(valueProperty.asCollection().getCollectionRawType())); + if (mapProperty.hasKeyProvider()) { + generateMapKeysProvider(ctor, mapProperty.getKeysProvider()); + } else { + ctor.visitInsn(ACONST_NULL); } - } else if (property.isGroup()) { + if (mapProperty.hasDefaultValue()) { + ctor.visitLdcInsn(mapProperty.getDefaultValue()); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitMethodInsn(INVOKESTATIC, I_OBJECT_CREATOR, "values", + "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + ";L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";L" + + I_CLASS + ";L" + I_CLASS + ";L" + I_ITERABLE + ";L" + I_STRING + ";)L" + I_MAP + ";", + false); + } else if (property.isCollection() && property.asCollection().getElement().isLeaf()) { + toStringPropertyName(ctor); + CollectionProperty collectionProperty = property.asCollection(); + ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getValueRawType())); + if (collectionProperty.getElement().hasConvertWith()) { + ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); + ctor.visitMethodInsn(INVOKESTATIC, I_OBJECT_CREATOR, "values", "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + ";L" + + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_COLLECTION + ";", false); + } else if (property.isOptional() && property.asOptional().getNestedProperty().isCollection() + && property.asOptional().getNestedProperty().asCollection().getElement().isLeaf()) { + toStringPropertyName(ctor); + CollectionProperty collectionProperty = property.asOptional().getNestedProperty().asCollection(); + ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getValueRawType())); + if (collectionProperty.getElement().hasConvertWith()) { + ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); + ctor.visitMethodInsn(INVOKESTATIC, I_OBJECT_CREATOR, "optionalValues", "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + + ";L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OPTIONAL + ";", false); + } else { + ctor.visitTypeInsn(NEW, I_OBJECT_CREATOR); + ctor.visitInsn(DUP); + toStringPropertyName(ctor); + ctor.visitMethodInsn(INVOKESPECIAL, I_OBJECT_CREATOR, "", "(L" + I_MAPPING_CONTEXT + ";L" + I_STRING + ";)V", + false); + + generateNestedProperty(ctor, property); + } + } + + private static void generateNestedProperty(final MethodVisitor ctor, final Property property) { + if (property.isGroup()) { ctor.visitLdcInsn(getType(property.asGroup().getGroupType().getInterfaceType())); - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "group", "(L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "group", + "(L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "get", "()L" + I_OBJECT + ";", false); + } else if (property.isMap() && property.asMap().getValueProperty().isLeaf()) { + MapProperty mapProperty = property.asMap(); + Property valueProperty = mapProperty.getValueProperty(); + ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); + if (mapProperty.hasKeyConvertWith()) { + ctor.visitLdcInsn(getType(mapProperty.getKeyConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + LeafProperty leafProperty = valueProperty.asLeaf(); + ctor.visitLdcInsn(getType(leafProperty.getValueRawType())); + if (leafProperty.hasConvertWith()) { + ctor.visitLdcInsn(getType(leafProperty.getConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + if (mapProperty.hasKeyProvider()) { + generateMapKeysProvider(ctor, mapProperty.getKeysProvider()); + } else { + ctor.visitInsn(ACONST_NULL); + } + if (mapProperty.hasDefaultValue() && mapProperty.getDefaultValue() != null) { + ctor.visitLdcInsn(mapProperty.getDefaultValue()); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "values", "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + + ";L" + I_CLASS + ";L" + I_ITERABLE + ";L" + I_STRING + ";)L" + I_OBJECT_CREATOR + ";", false); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "get", "()L" + I_OBJECT + ";", false); + } else if (property.isMap() && property.asMap().getValueProperty().isCollection() + && property.asMap().getValueProperty().asCollection().getElement().isLeaf()) { + MapProperty mapProperty = property.asMap(); + Property valueProperty = mapProperty.getValueProperty(); + ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); + if (mapProperty.hasKeyConvertWith()) { + ctor.visitLdcInsn(getType(mapProperty.getKeyConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + LeafProperty leafProperty = valueProperty.asCollection().getElement().asLeaf(); + ctor.visitLdcInsn(getType(leafProperty.getValueRawType())); + if (leafProperty.hasConvertWith()) { + ctor.visitLdcInsn(getType(leafProperty.getConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitLdcInsn(getType(valueProperty.asCollection().getCollectionRawType())); + if (mapProperty.hasKeyProvider()) { + generateMapKeysProvider(ctor, mapProperty.getKeysProvider()); + } else { + ctor.visitInsn(ACONST_NULL); + } + if (mapProperty.hasDefaultValue()) { + ctor.visitLdcInsn(mapProperty.getDefaultValue()); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "values", "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + + ";L" + I_CLASS + ";L" + I_CLASS + ";L" + I_ITERABLE + ";L" + I_STRING + ";)L" + I_OBJECT_CREATOR + ";", + false); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "get", "()L" + I_OBJECT + ";", false); + } else if (property.isOptional() && property.asOptional().getNestedProperty().isCollection() + && property.asOptional().getNestedProperty().asCollection().getElement().isLeaf()) { + CollectionProperty collectionProperty = property.asOptional().getNestedProperty().asCollection(); + ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getValueRawType())); + if (collectionProperty.getElement().hasConvertWith()) { + ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getConvertWith())); + } else { + ctor.visitInsn(ACONST_NULL); + } + ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "optionalValues", + "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "get", "()L" + I_OBJECT + ";", false); } else if (property.isMap()) { MapProperty mapProperty = property.asMap(); Property valueProperty = mapProperty.getValueProperty(); - if (valueProperty.isLeaf()) { - ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); - if (mapProperty.hasKeyConvertWith()) { - ctor.visitLdcInsn(getType(mapProperty.getKeyConvertWith())); - } else { - ctor.visitInsn(ACONST_NULL); - } - LeafProperty leafProperty = valueProperty.asLeaf(); - ctor.visitLdcInsn(getType(leafProperty.getValueRawType())); - if (leafProperty.hasConvertWith()) { - ctor.visitLdcInsn(getType(leafProperty.getConvertWith())); - } else { - ctor.visitInsn(ACONST_NULL); - } - if (mapProperty.hasKeyProvider()) { - generateMapKeysProvider(ctor, mapProperty.getKeysProvider()); - } else { - ctor.visitInsn(ACONST_NULL); - } - if (mapProperty.hasDefaultValue() && mapProperty.getDefaultValue() != null) { - ctor.visitLdcInsn(mapProperty.getDefaultValue()); - } else { - ctor.visitInsn(ACONST_NULL); - } - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "values", "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS - + ";L" + I_CLASS + ";L" + I_ITERABLE + ";L" + I_STRING + ";)L" + I_OBJECT_CREATOR + ";", false); - } else if (valueProperty.isGroup()) { + if (valueProperty.isGroup()) { ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); if (mapProperty.hasKeyConvertWith()) { ctor.visitLdcInsn(getType(mapProperty.getKeyConvertWith())); @@ -575,78 +734,25 @@ private static void generateProperty(final MethodVisitor ctor, final Property pr ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "lazyGroup", "(L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); } - } else if (valueProperty.isCollection() && valueProperty.asCollection().getElement().isLeaf()) { - ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); - if (mapProperty.hasKeyConvertWith()) { - ctor.visitLdcInsn(getType(mapProperty.getKeyConvertWith())); - } else { - ctor.visitInsn(ACONST_NULL); - } - LeafProperty leafProperty = mapProperty.getValueProperty().asCollection().getElement().asLeaf(); - ctor.visitLdcInsn(getType(leafProperty.getValueRawType())); - if (leafProperty.hasConvertWith()) { - ctor.visitLdcInsn(getType(leafProperty.getConvertWith())); - } else { - ctor.visitInsn(ACONST_NULL); - } - ctor.visitLdcInsn(getType(mapProperty.getValueProperty().asCollection().getCollectionRawType())); - if (mapProperty.hasKeyProvider()) { - generateMapKeysProvider(ctor, mapProperty.getKeysProvider()); - } else { - ctor.visitInsn(ACONST_NULL); - } - if (mapProperty.hasDefaultValue()) { - ctor.visitLdcInsn(mapProperty.getDefaultValue()); - } else { - ctor.visitInsn(ACONST_NULL); - } - ctor.visitMethodInsn( - INVOKEVIRTUAL, I_OBJECT_CREATOR, "values", "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";L" - + I_CLASS + ";L" + I_CLASS + ";L" + I_ITERABLE + ";L" + I_STRING + ";)L" + I_OBJECT_CREATOR - + ";", - false); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "get", "()L" + I_OBJECT + ";", false); } else { - unwrapProperty(ctor, property); + unwrapNestedProperty(ctor, property); } } else if (property.isCollection()) { - CollectionProperty collectionProperty = property.asCollection(); - if (collectionProperty.getElement().isLeaf()) { - ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getValueRawType())); - if (collectionProperty.getElement().hasConvertWith()) { - ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getConvertWith())); - } else { - ctor.visitInsn(ACONST_NULL); - } - ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "values", - "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); - } else { - unwrapProperty(ctor, property); - } + unwrapNestedProperty(ctor, property); } else if (property.isOptional()) { final MayBeOptionalProperty nestedProperty = property.asOptional().getNestedProperty(); if (nestedProperty.isGroup()) { ctor.visitLdcInsn(getType(nestedProperty.asGroup().getGroupType().getInterfaceType())); ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "optionalGroup", "(L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "get", "()L" + I_OBJECT + ";", false); } else if (nestedProperty.isCollection()) { CollectionProperty collectionProperty = nestedProperty.asCollection(); - if (collectionProperty.getElement().isLeaf()) { - ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getValueRawType())); - if (collectionProperty.getElement().hasConvertWith()) { - ctor.visitLdcInsn(getType(collectionProperty.getElement().asLeaf().getConvertWith())); - } else { - ctor.visitInsn(ACONST_NULL); - } - ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "optionalValues", - "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); - } else { - ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); - ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "optionalCollection", - "(L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); - generateProperty(ctor, collectionProperty.getElement()); - } + ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "optionalCollection", + "(L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); + generateNestedProperty(ctor, collectionProperty.getElement()); } else { throw new UnsupportedOperationException(); } @@ -655,7 +761,7 @@ private static void generateProperty(final MethodVisitor ctor, final Property pr } } - private static void unwrapProperty(final MethodVisitor ctor, final Property property) { + private static void unwrapNestedProperty(final MethodVisitor ctor, final Property property) { if (property.isMap()) { MapProperty mapProperty = property.asMap(); ctor.visitLdcInsn(getType(mapProperty.getKeyRawType())); @@ -677,13 +783,13 @@ private static void unwrapProperty(final MethodVisitor ctor, final Property prop ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "map", "(L" + I_CLASS + ";L" + I_CLASS + ";L" + I_STRING + ";L" + I_ITERABLE + ";)L" + I_OBJECT_CREATOR + ";", false); - generateProperty(ctor, mapProperty.getValueProperty()); + generateNestedProperty(ctor, mapProperty.getValueProperty()); } else if (property.isCollection()) { CollectionProperty collectionProperty = property.asCollection(); ctor.visitLdcInsn(getType(collectionProperty.getCollectionRawType())); ctor.visitMethodInsn(INVOKEVIRTUAL, I_OBJECT_CREATOR, "collection", "(L" + I_CLASS + ";)L" + I_OBJECT_CREATOR + ";", false); - generateProperty(ctor, collectionProperty.getElement()); + generateNestedProperty(ctor, collectionProperty.getElement()); } else { throw new UnsupportedOperationException(); } @@ -731,6 +837,12 @@ private static void appendPropertyName(final MethodVisitor ctor, final Property ctor.visitInsn(POP); } + private static void toStringPropertyName(final MethodVisitor ctor) { + ctor.visitVarInsn(ALOAD, V_MAPPING_CONTEXT); + ctor.visitVarInsn(ALOAD, V_STRING_BUILDER); + ctor.visitMethodInsn(INVOKEVIRTUAL, I_STRING_BUILDER, "toString", "()L" + I_STRING + ';', false); + } + private static void restoreLength(final MethodVisitor ctor) { ctor.visitVarInsn(ALOAD, V_STRING_BUILDER); ctor.visitVarInsn(ILOAD, V_LENGTH); diff --git a/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java b/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java index b0775a774..35ea50d36 100644 --- a/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java +++ b/implementation/src/test/java/io/smallrye/config/ConfigMappingCollectionsTest.java @@ -900,8 +900,12 @@ void nestedMapsLists() { .withSources(config( "nested.key-converter.1[0].two", "one[0]-two")) .withSources(config( - "nested.mixed.one[0].one[0].one", "one[0].one[0].one", - "nested.mixed.one[0].one[0].two", "one[0].one[0].two")) + "nested.map-map-list.one.two[0]", "one.two[0]")) + .withSources(config( + "nested.map-optional-list.one[0]", "one[0]")) + .withSources(config( + "nested.map-list-map-list-map.one[0].one[0].one", "one[0].one[0].one", + "nested.map-list-map-list-map.one[0].one[0].two", "one[0].one[0].two")) .withMapping(NestedMapsLists.class) .build(); @@ -915,8 +919,13 @@ void nestedMapsLists() { assertEquals("one[0]-two", mapping.keyConverter().get("one").get(0).get("two")); - assertEquals("one[0].one[0].one", mapping.mixed().get("one").get(0).get("one").get(0).get("one")); - assertEquals("one[0].one[0].two", mapping.mixed().get("one").get(0).get("one").get(0).get("two")); + assertEquals("one.two[0]", mapping.mapMapList().get("one").get("two").get(0)); + + assertTrue(mapping.mapOptionalList().get("one").isPresent()); + assertEquals("one[0]", mapping.mapOptionalList().get("one").get().get(0)); + + assertEquals("one[0].one[0].one", mapping.mapListMapListMap().get("one").get(0).get("one").get(0).get("one")); + assertEquals("one[0].one[0].two", mapping.mapListMapListMap().get("one").get(0).get("one").get(0).get("two")); } @ConfigMapping(prefix = "nested") @@ -927,7 +936,11 @@ interface NestedMapsLists { Map<@WithConverter(KeyConverter.class) String, List>> keyConverter(); - Map>>>> mixed(); + Map>> mapMapList(); + + Map>> mapOptionalList(); + + Map>>>> mapListMapListMap(); class KeyConverter implements Converter { @Override diff --git a/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java b/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java index 84554170e..2f1ae770b 100644 --- a/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java +++ b/implementation/src/test/java/io/smallrye/config/ObjectCreatorTest.java @@ -175,9 +175,7 @@ public ObjectCreatorImpl(ConfigMappingContext context) { sb.setLength(length); sb.append(ns.apply("value")); - ConfigMappingContext.ObjectCreator value = context.new ObjectCreator(sb.toString()) - .value(String.class, null); - this.value = value.get(); + this.value = ConfigMappingContext.ObjectCreator.value(context, sb.toString(), String.class, null); sb.setLength(length); sb.append(ns.apply("optional-value"));