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

io.muserver.rest.BuiltInParamConverterProvider Maven / Gradle / Ivy

There is a newer version: 2.0.3
Show newest version
package io.muserver.rest;

import io.muserver.Mutils;

import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Arrays.asList;

class BuiltInParamConverterProvider implements ParamConverterProvider {

    private final ParamConverter stringParamConverter = new ParamConverter() {
        public String fromString(String value) {
            return value;
        }
        public String toString(String value) {
            return value;
        }
    };

    private static final List primitiveConverters = asList(
        new PrimitiveConverter<>(int.class, 0, Integer::parseInt),
        new PrimitiveConverter<>(long.class, 0L, Long::parseLong),
        new PrimitiveConverter<>(short.class, (short)0, Short::parseShort),
        new PrimitiveConverter<>(char.class, '\u0000', s -> s.charAt(0)),
        new PrimitiveConverter<>(byte.class, (byte)0, Byte::parseByte),
        new PrimitiveConverter<>(float.class, 0.0f, Float::parseFloat),
        new PrimitiveConverter<>(double.class, 0.0d, Double::parseDouble),
        new PrimitiveConverter<>(boolean.class, false, Boolean::parseBoolean)
    );
    private static final List boxedPrimitiveConverters = asList(
        new BoxedPrimitiveConverter<>(Integer.class, Integer::parseInt),
        new BoxedPrimitiveConverter<>(Long.class, Long::parseLong),
        new BoxedPrimitiveConverter<>(Short.class, Short::parseShort),
        new BoxedPrimitiveConverter<>(Character.class, s -> s.charAt(0)),
        new BoxedPrimitiveConverter<>(Byte.class, Byte::parseByte),
        new BoxedPrimitiveConverter<>(Float.class, Float::parseFloat),
        new BoxedPrimitiveConverter<>(Double.class, Double::parseDouble),
        new BoxedPrimitiveConverter<>(Boolean.class, Boolean::parseBoolean)
    );


    @Override
    public  ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) {


        if (String.class.isAssignableFrom(rawType)) {
            return stringParamConverter;
        }
        for (PrimitiveConverter converter : primitiveConverters) {
            if (converter.primitiveClass.equals(rawType)) {
                return converter;
            }
        }
        for (BoxedPrimitiveConverter converter : boxedPrimitiveConverters) {
            if (converter.boxedClass.isAssignableFrom(rawType)) {
                return converter;
            }
        }
        if (rawType.isEnum()) {
            return new EnumConverter(rawType);
        }
        ConstructorConverter cc = ConstructorConverter.tryToCreate(rawType);
        if (cc != null) {
            return cc;
        }
        StaticMethodConverter smc = StaticMethodConverter.tryToCreate(rawType);
        if (smc != null) {
            return smc;
        }

        if (Collection.class.isAssignableFrom(rawType) && genericType instanceof ParameterizedType) {
            Type type = ((ParameterizedType) genericType).getActualTypeArguments()[0];
            if (type instanceof Class) {
                Class genericClass = (Class) type;
                ParamConverter genericTypeConverter = getConverter(genericClass, type, annotations);
                if (genericTypeConverter != null) {
                    CollectionConverter collectionConverter = CollectionConverter.create(rawType, genericClass, genericTypeConverter);
                    if (collectionConverter != null) {
                        return collectionConverter;
                    }
                }
            }
        }
        return null;
    }

    private static class CollectionConverter implements ParamConverter {
        private final ParamConverter genericTypeConverter;
        private final Supplier collectionSupplier;
        private CollectionConverter(ParamConverter genericTypeConverter, Supplier collectionSupplier) {
            this.genericTypeConverter = genericTypeConverter;
            this.collectionSupplier = collectionSupplier;
        }
        public Object fromString(String value) {
            Collection values = (Collection)collectionSupplier.get();
            if (!Mutils.nullOrEmpty(value)) {
                String[] parts = value.split("\\s*,\\s*");
                Stream.of(parts).map(v -> genericTypeConverter.fromString(v))
                    .forEach(values::add);
            }
            return values;
        }
        public String toString(Object value) {
            Collection collection = (Collection) value;
            return collection.stream().map(genericTypeConverter::toString).collect(Collectors.joining(""));
        }
        public static CollectionConverter create(Class collectionType, Class genericClass, ParamConverter genericTypeConverter) {
            Supplier supplier;
            if (SortedSet.class.equals(collectionType)) {
                if (!Comparable.class.isAssignableFrom(genericClass)) {
                    throw new InternalServerErrorException("The class " + genericClass + " does not implement Comparable so cannot be used in a SortedSet");
                }
                supplier = TreeSet::new;
            } else if (Set.class.equals(collectionType)) {
                supplier = HashSet::new;
            } else if (List.class.equals(collectionType)) {
                supplier = ArrayList::new;
            } else {
                return null;
            }
            return new CollectionConverter(genericTypeConverter, supplier);
        }
        public String toString() {
            return "CollectionConverter<" + genericTypeConverter + '>';
        }
    }

    private static class BoxedPrimitiveConverter implements ParamConverter {
        private final Class boxedClass;
        private final Function stringToValue;
        public BoxedPrimitiveConverter(Class boxedClass, Function stringToValue) {
            this.boxedClass = boxedClass;
            this.stringToValue = stringToValue;
        }
        public T fromString(String value) {
            if (Mutils.nullOrEmpty(value)) return null;
            return stringToValue.apply(value);
        }
        public String toString(T value) {
            return String.valueOf(value);
        }
        public String toString() {
            return boxedClass.getSimpleName() + " param converter";
        }
    }

    private static class PrimitiveConverter implements ParamConverter, ResourceMethodParam.HasDefaultValue {
        private final T defaultValue;
        private final Class primitiveClass;
        private final Function stringToValue;

        public PrimitiveConverter(Class primitiveClass, T defaultValue, Function stringToValue) {
            this.defaultValue = defaultValue;
            this.primitiveClass = primitiveClass;
            this.stringToValue = stringToValue;
        }
        public T fromString(String value) {
            if (value == null || value.isEmpty()) {
                return defaultValue;
            }
            return stringToValue.apply(value);
        }
        public String toString(T value) {
            return String.valueOf(value);
        }
        public Object getDefault() {
            return defaultValue;
        }
        public String toString() {
            return primitiveClass.getSimpleName() + " param converter";
        }
    }

    private static class EnumConverter>  implements ParamConverter {
        private final Class enumClass;
        private EnumConverter(Class enumClass) {
            this.enumClass = enumClass;
        }
        public E fromString(String value) {
            if (Mutils.nullOrEmpty(value)) return null;
            return Enum.valueOf(enumClass, value);
        }
        public String toString(E value) {
            return value.name();
        }
        public String toString() {
            return enumClass.getSimpleName() + " converter";
        }
    }

    private static class ConstructorConverter implements ParamConverter {
        private final Constructor constructor;
        private ConstructorConverter(Constructor constructor) {
            this.constructor = constructor;
        }
        public T fromString(String value) {
            if (Mutils.nullOrEmpty(value)) return null;
            try {
                return constructor.newInstance(value);
            } catch (Exception e) {
                throw new IllegalArgumentException("Could not convert \"" + value + "\" to a " + constructor.getDeclaringClass().getSimpleName(), e);
            }
        }
        public String toString(T value) {
            return String.valueOf(value);
        }
        public String toString() {
            return "ConstructorConverter{" + constructor + '}';
        }
        static  ConstructorConverter tryToCreate(Class clazz) {
            try {
                Constructor constructor = clazz.getConstructor(String.class);
                int modifiers = constructor.getModifiers();
                if (Modifier.isPublic(modifiers)) {
                    constructor.setAccessible(true);
                    return new ConstructorConverter<>(constructor);
                }
                return null;
            } catch (NoSuchMethodException e) {
                return null;
            }
        }
    }

    private static class StaticMethodConverter implements ParamConverter {
        private final Method staticMethod;
        private StaticMethodConverter(Method staticMethod) {
            this.staticMethod = staticMethod;
        }
        public T fromString(String value) {
            if (Mutils.nullOrEmpty(value)) return null;
            try {
                return (T) staticMethod.invoke(null, value);
            } catch (Exception e) {
                throw new IllegalArgumentException("Could not convert \"" + value + "\" to a " + staticMethod.getDeclaringClass().getSimpleName() + " because " + e.getMessage(), e);
            }
        }
        public String toString(T value) {
            return String.valueOf(value);
        }
        public String toString() {
            return "StaticMethodConverter{" + staticMethod + '}';
        }

        static  StaticMethodConverter tryToCreate(Class clazz) {
            Method[] declaredMethods = clazz.getDeclaredMethods();
            Method staticMethod = getPublicStaticMethodNamed(clazz, declaredMethods, "valueOf");
            if (staticMethod == null) {
                staticMethod = getPublicStaticMethodNamed(clazz, declaredMethods, "fromString");
            }
            if (staticMethod == null) {
                return null;
            }
            staticMethod.setAccessible(true);
            return new StaticMethodConverter<>(staticMethod);
        }

        private static Method getPublicStaticMethodNamed(Class clazz, Method[] declaredMethods, String name) {
            Method staticMethod = null;
            for (Method method : declaredMethods) {
                int modifiers = method.getModifiers();
                if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers) && method.getReturnType().equals(clazz)) {
                    if (method.getName().equals(name)) {
                        staticMethod = method;
                        break;
                    }
                }
            }
            return staticMethod;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy