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

net.jbock.processor.MethodStep Maven / Gradle / Ivy

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

import io.jbock.auto.common.BasicAnnotationProcessor.Step;
import io.jbock.simple.Inject;
import net.jbock.Command;
import net.jbock.SuperCommand;
import net.jbock.common.Util;
import net.jbock.common.ValidationFailure;

import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeKind;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toSet;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.util.ElementFilter.methodsIn;
import static net.jbock.common.Annotations.methodLevelAnnotations;

final class MethodStep implements Step {

    private static final Set FORBIDDEN_KINDS = EnumSet.of(
            TypeKind.VOID,
            TypeKind.TYPEVAR,
            TypeKind.WILDCARD,
            TypeKind.OTHER,
            TypeKind.UNION,
            TypeKind.NONE);

    private final Messager messager;
    private final Util util;

    @Inject
    MethodStep(ProcessingEnvironment processingEnvironment, Util util) {
        this.messager = processingEnvironment.getMessager();
        this.util = util;
    }

    @Override
    public Set annotations() {
        return methodLevelAnnotations().stream()
                .map(Class::getCanonicalName)
                .collect(toSet());
    }

    @Override
    public Set process(Map> elementsByAnnotation) {
        List elements = elementsByAnnotation.values().stream()
                .flatMap(Set::stream)
                .collect(Collectors.toList());
        for (ExecutableElement method : methodsIn(elements)) {
            validateCommandAnnotationPresent(method)
                    .or(() -> validateAbstract(method))
                    .or(() -> validateTypeParameters(method))
                    .or(() -> validateReturnType(method))
                    .or(() -> util.checkExceptionsInDeclaration(method))
                    .ifPresent(failure -> failure.writeTo(messager));
        }
        return Set.of();
    }

    private Optional validateCommandAnnotationPresent(ExecutableElement method) {
        Element enclosingElement = method.getEnclosingElement();
        if (enclosingElement.getAnnotation(Command.class) != null
                || enclosingElement.getAnnotation(SuperCommand.class) != null) {
            return Optional.empty();
        }
        String enclosingElementKind = enclosingElement.getKind() == ElementKind.INTERFACE ?
                "interface" : "abstract class";
        return Optional.of(new ValidationFailure("missing command annotation: " +
                enclosingElementKind + " '" + enclosingElement.getSimpleName() +
                "' must be annotated with " + Command.class.getCanonicalName() + " or " + SuperCommand.class.getCanonicalName(),
                enclosingElement));
    }

    private Optional validateAbstract(ExecutableElement method) {
        if (method.getModifiers().contains(ABSTRACT)) {
            return Optional.empty();
        }
        return Optional.of(new ValidationFailure("missing method modifier: annotated method '" +
                method.getSimpleName() +
                "' must be abstract", method));
    }

    private Optional validateTypeParameters(ExecutableElement method) {
        if (method.getTypeParameters().isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(new ValidationFailure("invalid type parameters: annotated method '" +
                method.getSimpleName() +
                "' may not have type parameters, but found: " +
                method.getTypeParameters(), method));
    }

    private Optional validateReturnType(ExecutableElement method) {
        TypeKind kind = method.getReturnType().getKind();
        if (!FORBIDDEN_KINDS.contains(kind)) {
            return Optional.empty();
        }
        return Optional.of(new ValidationFailure("invalid return type: annotated method '" +
                method.getSimpleName() +
                "' may not return " + kind, method));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy