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

com.netflix.archaius.AbstractRegistryDecoder Maven / Gradle / Ivy

There is a newer version: 2.8.3
Show newest version
package com.netflix.archaius;

import com.netflix.archaius.api.Decoder;
import com.netflix.archaius.api.TypeConverter;
import com.netflix.archaius.exceptions.ParseException;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

/**
 * A {@code Decoder} implementation that also implements {@code TypeConverter.Registry}, and delegates to a supplied
 * collection of converter factories.
 */
abstract class AbstractRegistryDecoder implements Decoder, TypeConverter.Registry {

    private final Map> cache = new ConcurrentHashMap<>();

    private final List factories;

    AbstractRegistryDecoder(Collection factories) {
        this.factories = Collections.unmodifiableList(new ArrayList<>(factories));
    }

    @Override
    public  T decode(Class type, String encoded) {
        return decode((Type) type, encoded);
    }

    @Override
    public  T decode(Type type, String encoded) {
        try {
            if (encoded == null) {
                return null;
            }
            @SuppressWarnings("unchecked")
            TypeConverter converter = (TypeConverter) getOrCreateConverter(type);
            if (converter == null) {
                throw new RuntimeException("No converter found for type '" + type + "'");
            }
            return converter.convert(encoded);
        } catch (Exception e) {
            throw new ParseException("Unable to decode `"
                                     + encoded
                                     + "` as type `" + type.getTypeName() + "`: "
                                     + e, e);
        }
    }

    @Override
    public Optional> get(Type type) {
        return Optional.ofNullable(getOrCreateConverter(type));
    }

    private TypeConverter getOrCreateConverter(Type type) {
        TypeConverter converter = cache.get(type);
        if (converter == null) {
            converter = resolve(type);
            if (converter == null) {
                return null;
            }
            TypeConverter existing = cache.putIfAbsent(type, converter);
            if (existing != null) {
                converter = existing;
            }
        }
        return converter;
    }

    /**
     * Iterate through all TypeConverter#Factory's and return the first TypeConverter that matches the given type.
     */
    private TypeConverter resolve(Type type) {
        return factories.stream()
                .flatMap(factory -> factory.get(type, this).map(Stream::of).orElseGet(Stream::empty))
                .findFirst()
                .orElseGet(() -> findValueOfTypeConverter(type));
    }

    /**
     * Return a converter that uses reflection on either a static valueOf method or a ctor(String)
     * to convert a string value to the requested type. Will return null if neither is found
     */
    private static  TypeConverter findValueOfTypeConverter(Type type) {
        if (!(type instanceof Class)) {
            return null;
        }

        @SuppressWarnings("unchecked")
        Class cls = (Class) type;

        // Look for a valueOf(String) static method. The code *assumes* that such a method will return a T
        Method method;
        try {
            method = cls.getMethod("valueOf", String.class);
            return value -> {
                try {
                    //noinspection unchecked
                    return (T) method.invoke(null, value);
                } catch (Exception e) {
                    throw new ParseException("Error converting value '" + value + "' to '" + type.getTypeName() + "'", e);
                }
            };
        } catch (NoSuchMethodException e1) {
            // Next, look for a T(String) constructor
            Constructor c;
            try {
                c = cls.getConstructor(String.class);
                return value -> {
                    try {
                        return (T) c.newInstance(value);
                    } catch (Exception e) {
                        throw new ParseException("Error converting value", e);
                    }
                };
            } catch (NoSuchMethodException e) {
                return null;
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy