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

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

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

import com.squareup.javapoet.CodeBlock;
import io.jbock.util.Either;
import net.jbock.Converter;
import net.jbock.annotated.AnnotatedMethod;
import net.jbock.common.Util;
import net.jbock.common.ValidationFailure;
import net.jbock.convert.Mapping;
import net.jbock.convert.reference.ReferenceTool;
import net.jbock.convert.reference.StringConverterType;
import net.jbock.processor.SourceElement;
import net.jbock.util.StringConverter;
import net.jbock.validate.ValidateScope;

import javax.inject.Inject;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Types;
import java.util.Optional;

import static javax.lang.model.element.Modifier.ABSTRACT;

@ValidateScope
public class ConverterValidator {

    private final ReferenceTool referenceTool;
    private final Util util;
    private final SourceElement sourceElement;
    private final Types types;
    private final MatchFinder matchFinder;

    @Inject
    ConverterValidator(
            ReferenceTool referenceTool,
            Util util,
            SourceElement sourceElement,
            Types types,
            MatchFinder matchFinder) {
        this.referenceTool = referenceTool;
        this.util = util;
        this.sourceElement = sourceElement;
        this.types = types;
        this.matchFinder = matchFinder;
    }

    public 
    Either> findMapping(
            M sourceMethod,
            TypeElement converter) {
        return util.commonTypeChecks(converter)
                .or(() -> checkNotAbstract(sourceMethod, converter))
                .or(() -> checkNoTypeVars(sourceMethod, converter))
                .or(() -> checkConverterAnnotationPresent(sourceMethod, converter))
                .>map(Either::left)
                .orElseGet(() -> referenceTool.getReferencedType(sourceMethod, converter))
                .mapLeft(failure -> failure.prepend("invalid converter class: "))
                .flatMap(converterType -> tryAllMatchers(converterType, sourceMethod, converter));
    }

    private 
    Either> tryAllMatchers(
            StringConverterType converterType,
            M sourceMethod,
            TypeElement converter) {
        return matchFinder.findMatch(sourceMethod)
                .filter(match -> isValidMatch(match, converterType))
                .map(match -> Mapping.create(getMapExpr(converterType, converter), match, false));
    }

    /* Left-Optional
     */
    private 
    Optional checkConverterAnnotationPresent(
            M sourceMethod,
            TypeElement converter) {
        Converter converterAnnotation = converter.getAnnotation(Converter.class);
        boolean nestedMapper = util.getEnclosingElements(converter).contains(sourceElement.element());
        if (converterAnnotation == null && !nestedMapper) {
            return Optional.of(sourceMethod.fail("converter must be an inner class of the command class, or carry the @"
                    + Converter.class.getSimpleName() + " annotation"));
        }
        return Optional.empty();
    }

    /* Left-Optional
     */
    private 
    Optional checkNotAbstract(
            M sourceMethod,
            TypeElement converter) {
        if (converter.getModifiers().contains(ABSTRACT)) {
            return Optional.of(sourceMethod.fail("converter class may not be abstract"));
        }
        return Optional.empty();
    }

    /* Left-Optional
     */
    private 
    Optional checkNoTypeVars(
            M sourceMethod,
            TypeElement converter) {
        if (!converter.getTypeParameters().isEmpty()) {
            return Optional.of(sourceMethod.fail("type parameters are not allowed in converter class declaration"));
        }
        return Optional.empty();
    }

    private CodeBlock getMapExpr(
            StringConverterType functionType,
            TypeElement converter) {
        if (functionType.isSupplier()) {
            return CodeBlock.of("new $T().get()", converter.asType());
        }
        return CodeBlock.of("new $T()", converter.asType());
    }

    private 
    Optional isValidMatch(
            ValidMatch match,
            StringConverterType converterType) {
        if (!types.isSameType(converterType.outputType(), match.baseType())) {
            return Optional.of(match.sourceMethod().fail("invalid converter class: should extend " +
                    StringConverter.class.getSimpleName() +
                    "<" + util.typeToString(match.baseType()) + ">"));
        }
        return Optional.empty();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy