package com.stanfy.enroscar.async.internal; import com.stanfy.enroscar.async.Async; import com.stanfy.enroscar.async.Load; import com.stanfy.enroscar.async.Send; import com.stanfy.enroscar.async.rx.RxLoad; import com.stanfy.enroscar.async.rx.RxSend; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import static javax.tools.Diagnostic.Kind.ERROR; /** * @author Roman Mazur - Stanfy (http://stanfy.com) */ public final class AsyncProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes() { return new HashSet<>(Arrays.asList( Load.class.getCanonicalName(), Send.class.getCanonicalName(), RxLoad.class.getCanonicalName(), RxSend.class.getCanonicalName() )); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) { Map<TypeElement, List<MethodData>> classMethods = new LinkedHashMap<>(); collectAndValidate(classMethods, Load.class, roundEnv); collectAndValidate(classMethods, Send.class, roundEnv); collectAndValidate(classMethods, RxLoad.class, roundEnv); collectAndValidate(classMethods, RxSend.class, roundEnv); for (Map.Entry<TypeElement, List<MethodData>> e : classMethods.entrySet()) { generateCode(e.getKey(), e.getValue()); } return false; } private void collectAndValidate(final Map<TypeElement, List<MethodData>> classMethods, final Class<? extends Annotation> annotation, final RoundEnvironment roundEnv) { for (Element m : roundEnv.getElementsAnnotatedWith(annotation)) { Element encl = m.getEnclosingElement(); if (!(m instanceof ExecutableElement)) { // this can happen in case of compilation errors // just skip it continue; } if (!(encl instanceof TypeElement)) { throw new IllegalStateException(m + " annotated with @" + annotation.getSimpleName() + " in " + encl + ". Enclosing element is not a type."); } ExecutableElement method = (ExecutableElement) m; TypeElement type = (TypeElement) encl; String returnType = GenUtils.getReturnType(method); TypeSupport operatorTypeSupport = null; if (returnType.startsWith(Async.class.getName().concat("<"))) { operatorTypeSupport = TypeSupport.ASYNC; } else if (returnType.startsWith(TypeSupport.RX_OBSERVABLE_CLASS.concat("<"))) { operatorTypeSupport = TypeSupport.RX; } if (operatorTypeSupport == null) { error(method, "Method annotated with @" + annotation.getSimpleName() + " must return either Async<T> or rx.Observable<T>"); continue; } TypeSupport loaderDescriptionTypeSupport = operatorTypeSupport; if (annotation == RxLoad.class || annotation == RxSend.class) { loaderDescriptionTypeSupport = TypeSupport.RX; } List<MethodData> methods = classMethods.get(type); if (methods == null) { methods = new ArrayList<>(); classMethods.put(type, methods); } methods.add(new MethodData(method, operatorTypeSupport, loaderDescriptionTypeSupport)); } } private void generateCode(final TypeElement baseType, final List<MethodData> methods) { new LoaderDescriptionGenerator(processingEnv, baseType, methods).generateCode(); new OperatorGenerator(processingEnv, baseType, methods).generateCode(); } private void error(final Element element, final String message) { processingEnv.getMessager().printMessage(ERROR, message, element); } }