All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.smallrye.config.ConfigMappingContext Maven / Gradle / Ivy

package io.smallrye.config;

import static io.smallrye.config.ConfigMappingInterface.LeafProperty;
import static io.smallrye.config.ConfigMappingInterface.MapProperty;
import static io.smallrye.config.ConfigMappingInterface.PrimitiveProperty;
import static io.smallrye.config.ConfigMappingInterface.Property;
import static io.smallrye.config.ConfigMappingInterface.getConfigurationInterface;
import static io.smallrye.config.ConfigMappingInterface.rawTypeOf;
import static io.smallrye.config.ConfigMappingInterface.typeOfParameter;
import static io.smallrye.config.ConfigValidationException.Problem;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import org.eclipse.microprofile.config.spi.Converter;

/**
 * A mapping context. This is used by generated classes during configuration mapping, and is released once the configuration
 * mapping has completed.
 */
public final class ConfigMappingContext {
    private final Map, Map>> enclosedThings = new IdentityHashMap<>();
    private final Map, Map> roots = new IdentityHashMap<>();
    private final Map, Map>> convertersByTypeAndField = new IdentityHashMap<>();
    private final List, Map>>> keyConvertersByDegreeTypeAndField = new ArrayList<>();
    private final Map, Converter> converterInstances = new IdentityHashMap<>();
    private final List allInstances = new ArrayList<>();
    private final SmallRyeConfig config;
    private final StringBuilder stringBuilder = new StringBuilder();
    private final ArrayList problems = new ArrayList<>();

    ConfigMappingContext(final SmallRyeConfig config) {
        this.config = config;
    }

    public ConfigMappingObject getRoot(Class rootType, String rootPath) {
        return roots.getOrDefault(rootType, Collections.emptyMap()).get(rootPath);
    }

    public void registerRoot(Class rootType, String rootPath, ConfigMappingObject root) {
        roots.computeIfAbsent(rootType, x -> new HashMap<>()).put(rootPath, root);
    }

    public Object getEnclosedField(Class enclosingType, String key, Object enclosingObject) {
        return enclosedThings
                .getOrDefault(enclosingType, Collections.emptyMap())
                .getOrDefault(key, Collections.emptyMap())
                .get(enclosingObject);
    }

    public void registerEnclosedField(Class enclosingType, String key, Object enclosingObject, Object value) {
        enclosedThings
                .computeIfAbsent(enclosingType, x -> new HashMap<>())
                .computeIfAbsent(key, x -> new IdentityHashMap<>())
                .put(enclosingObject, value);
    }

    public  T constructGroup(Class interfaceType) {
        final T mappingObject = ConfigMappingLoader.configMappingObject(interfaceType, this);
        allInstances.add((ConfigMappingObject) mappingObject);
        return mappingObject;
    }

    @SuppressWarnings({ "unchecked", "unused" })
    public  Converter getValueConverter(Class enclosingType, String field) {
        return (Converter) convertersByTypeAndField
                .computeIfAbsent(enclosingType, x -> new HashMap<>())
                .computeIfAbsent(field, x -> {
                    ConfigMappingInterface ci = getConfigurationInterface(enclosingType);
                    Property property = ci.getProperty(field);
                    boolean optional = property.isOptional();
                    if (property.isLeaf() || optional && property.asOptional().getNestedProperty().isLeaf()) {
                        LeafProperty leafProperty = optional ? property.asOptional().getNestedProperty().asLeaf()
                                : property.asLeaf();
                        if (leafProperty.hasConvertWith()) {
                            Class> convertWith = leafProperty.getConvertWith();
                            // todo: generics
                            return getConverterInstance(convertWith);
                        } else {
                            // todo: replace with generic converter lookup
                            Class valueRawType = leafProperty.getValueRawType();
                            if (valueRawType == List.class) {
                                return Converters.newCollectionConverter(
                                        config.requireConverter(rawTypeOf(typeOfParameter(leafProperty.getValueType(), 0))),
                                        ArrayList::new);
                            } else if (valueRawType == Set.class) {
                                return Converters.newCollectionConverter(
                                        config.requireConverter(rawTypeOf(typeOfParameter(leafProperty.getValueType(), 0))),
                                        HashSet::new);
                            } else {
                                return config.requireConverter(valueRawType);
                            }
                        }
                    } else if (property.isPrimitive()) {
                        PrimitiveProperty primitiveProperty = property.asPrimitive();
                        if (primitiveProperty.hasConvertWith()) {
                            return getConverterInstance(primitiveProperty.getConvertWith());
                        } else {
                            return config.requireConverter(primitiveProperty.getBoxType());
                        }
                    } else {
                        throw new IllegalStateException();
                    }
                });
    }

    @SuppressWarnings("unchecked")
    public  Converter getKeyConverter(Class enclosingType, String field, int degree) {
        List, Map>>> list = this.keyConvertersByDegreeTypeAndField;
        while (list.size() <= degree) {
            list.add(new IdentityHashMap<>());
        }
        Map, Map>> map = list.get(degree);
        return (Converter) map
                .computeIfAbsent(enclosingType, x -> new HashMap<>())
                .computeIfAbsent(field, x -> {
                    ConfigMappingInterface ci = getConfigurationInterface(enclosingType);
                    MapProperty property = ci.getProperty(field).asMap();
                    while (degree + 1 > property.getLevels()) {
                        property = property.getValueProperty().asMap();
                    }
                    if (property.hasKeyConvertWith()) {
                        return getConverterInstance(property.getKeyConvertWith());
                    } else {
                        // todo: replace with generic converter lookup
                        Class valueRawType = property.getKeyRawType();
                        if (valueRawType == List.class) {
                            return Converters.newCollectionConverter(
                                    config.requireConverter(rawTypeOf(typeOfParameter(property.getKeyType(), 0))),
                                    ArrayList::new);
                        } else if (valueRawType == Set.class) {
                            return Converters.newCollectionConverter(
                                    config.requireConverter(rawTypeOf(typeOfParameter(property.getKeyType(), 0))),
                                    HashSet::new);
                        } else {
                            return config.requireConverter(valueRawType);
                        }
                    }
                });
    }

    @SuppressWarnings("unchecked")
    public  Converter getConverterInstance(Class> converterType) {
        return (Converter) converterInstances.computeIfAbsent(converterType, t -> {
            try {
                return (Converter) t.getConstructor().newInstance();
            } catch (InstantiationException e) {
                throw new InstantiationError(e.getMessage());
            } catch (IllegalAccessException e) {
                throw new IllegalAccessError(e.getMessage());
            } catch (InvocationTargetException e) {
                try {
                    throw e.getCause();
                } catch (RuntimeException | Error e2) {
                    throw e2;
                } catch (Throwable t2) {
                    throw new UndeclaredThrowableException(t2);
                }
            } catch (NoSuchMethodException e) {
                throw new NoSuchMethodError(e.getMessage());
            }
        });
    }

    public NoSuchElementException noSuchElement(Class type) {
        return new NoSuchElementException("A required configuration group of type " + type.getName() + " was not provided");
    }

    public void unknownConfigElement(final String propertyName) {
        problems.add(new Problem(propertyName + " does not map to any root"));
    }

    void fillInOptionals() {
        for (ConfigMappingObject instance : allInstances) {
            instance.fillInOptionals(this);
        }
    }

    public SmallRyeConfig getConfig() {
        return config;
    }

    public StringBuilder getStringBuilder() {
        return stringBuilder;
    }

    public void reportProblem(RuntimeException problem) {
        problems.add(new Problem(problem.toString()));
    }

    ArrayList getProblems() {
        return problems;
    }

    Map, Map> getRootsMap() {
        return roots;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy