net.jbock.processor.MethodStep Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbock-compiler Show documentation
Show all versions of jbock-compiler Show documentation
jbock annotation processor
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 extends Element> 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));
}
}