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

net.jbock.convert.map.ConverterValidator Maven / Gradle / Ivy

There is a newer version: 5.18
Show newest version
package net.jbock.convert.map;

import io.jbock.simple.Inject;
import io.jbock.util.Either;
import net.jbock.annotated.Item;
import net.jbock.common.SafeElements;
import net.jbock.common.SafeTypes;
import net.jbock.common.TypeTool;
import net.jbock.common.ValidationFailure;
import net.jbock.convert.Mapping;
import net.jbock.convert.match.Match;
import net.jbock.util.StringConverter;

import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import java.util.Optional;
import java.util.function.Supplier;

import static io.jbock.util.Either.left;
import static io.jbock.util.Either.right;
import static net.jbock.common.TypeTool.AS_DECLARED;

public final class ConverterValidator {

    private final SafeTypes types;
    private final SafeElements elements;
    private final MappingFactory.Factory mappingFactoryFactory;

    @Inject
    public ConverterValidator(
            TypeTool tool,
            MappingFactory.Factory mappingFactoryFactory) {
        this.types = tool.types();
        this.elements = tool.elements();
        this.mappingFactoryFactory = mappingFactoryFactory;
    }

    public 
    Either> findMapping(
            Match match,
            TypeElement converter) {
        return checkSuppliedConverter(converter, match)
                .or(() -> checkDirectConverter(converter, match))
                .orElseGet(() -> left(match.fail(errorConverterType())))
                .flatMap(MappingFactory::checkMatchingMatch);
    }

    private 
    Either> handleConverter(
            TypeElement converter,
            Match match,
            DeclaredType converterType,
            boolean isSupplier) {
        if (converterType.getTypeArguments().size() != 1) {
            return left(match.fail(converterRawType(converterType)));
        }
        TypeMirror typeArgument = converterType.getTypeArguments().get(0);
        return right(mappingFactoryFactory.create(converter, typeArgument, match, isSupplier));
    }

    private 
    Optional>> checkSuppliedConverter(
            TypeElement converter, Match match) {
        return converter.getInterfaces().stream()
                .filter(inter -> isSameErasure(inter, Supplier.class))
                .map(AS_DECLARED::visit)
                .flatMap(Optional::stream)
                .findFirst()
                .map(declaredType -> checkSuppliedConverter(converter, match, declaredType));
    }

    private 
    Either> checkSuppliedConverter(
            TypeElement converter,
            Match match,
            DeclaredType supplierType) {
        if (supplierType.getTypeArguments().size() != 1) {
            return left(match.fail(converterRawType(supplierType)));
        }
        return AS_DECLARED.visit(supplierType.getTypeArguments().get(0))
                .filter(typeArgument -> isSameErasure(typeArgument, StringConverter.class))
                .>map(Either::right)
                .orElseGet(() -> left(match.fail(errorConverterType())))
                .flatMap(suppliedType -> handleConverter(converter, match, suppliedType, true));
    }

    private 
    Optional>> checkDirectConverter(
            TypeElement converter, Match match) {
        return Optional.of(converter.getSuperclass())
                .filter(inter -> isSameErasure(inter, StringConverter.class))
                .flatMap(AS_DECLARED::visit)
                .map(converterType ->
                        handleConverter(converter, match, converterType, false));
    }

    private boolean isSameErasure(TypeMirror x, Class y) {
        return elements.getTypeElement(y.getCanonicalName())
                .map(TypeElement::asType)
                .map(type -> types.isSameType(types.erasure(x), types.erasure(type)))
                .orElse(false);
    }

    private String errorConverterType() {
        return "invalid converter class: converter must extend " + StringConverter.class.getSimpleName() +
                " or implement " + Supplier.class.getSimpleName() +
                "<" + StringConverter.class.getSimpleName() + ">";
    }

    private String converterRawType(DeclaredType declaredType) {
        return "invalid converter class: missing a type parameter in type '" +
                declaredType.asElement().getSimpleName() + "'";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy