package io.myweb.processor; import io.myweb.api.*; import io.myweb.http.Method; import io.myweb.http.Request; import io.myweb.http.Response; import io.myweb.processor.model.ParsedFilter; import io.myweb.processor.model.ParsedMethod; import io.myweb.processor.model.Provider; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.tools.Diagnostic; import java.io.InvalidObjectException; import java.security.InvalidParameterException; import java.util.*; @SupportedSourceVersion(SourceVersion.RELEASE_6) public class MyAnnotationProcessor extends AbstractProcessor { private final List<InternalProcessor> processors; private List<ParsedMethod> parsedMethods = new LinkedList<ParsedMethod>(); private List<ParsedFilter> parsedFilters = new LinkedList<ParsedFilter>(); private Set<ExecutableElement> processed = new HashSet<ExecutableElement>(); private List<Provider> providers; public MyAnnotationProcessor() { ArrayList<InternalProcessor> p = new ArrayList<InternalProcessor>(); p.add(new HttpMethodProcessor()); p.add(new ContentProviderProcessor()); p.add(new FilterProcessor()); processors = Collections.unmodifiableList(p); } private void error(String msg) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg); } private void warning(String msg) { processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg); } @Override public Set<String> getSupportedAnnotationTypes() { Set<String> annotationTypes = new HashSet<String>(); for (InternalProcessor ip : processors) { annotationTypes.addAll(ip.supportedTypes()); } return annotationTypes; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (annotations.isEmpty()) return false; MyCodeGenerator mCodeGenerator = new MyCodeGenerator(processingEnv); try { providers = new LinkedList<Provider>(); for (TypeElement annotation : annotations) { // System.out.println("Processing: " + annotation.getQualifiedName().toString()); for(InternalProcessor ip: processors) { if (ip.consume(annotation, roundEnv)) break; } } mCodeGenerator.generateCode(parsedMethods, providers, parsedFilters); return true; } catch (Exception e) { // e.printStackTrace(); error("error(s) found - details above: " + e.getMessage()); } return false; } private static List<Method> convertMethods(List<Object> attrs) { LinkedList<Method> lm = new LinkedList<Method>(); if (attrs == null) { return Arrays.asList(Method.GET, Method.PUT, Method.POST, Method.DELETE); } for (Object attr : attrs) { String name = attr.toString(); lm.add(Method.findByName(name.substring(name.lastIndexOf(".") + 1))); } return lm; } private class InternalProcessor { private final List<String> types; protected InternalProcessor(List<String> types) { this.types = types; } public List<String> supportedTypes() { return types; } public boolean consume(TypeElement annotation, RoundEnvironment roundEnv) throws Exception { for(String name: types) { if (name.equals(annotation.getQualifiedName().toString())) { return process(annotation, roundEnv); } } return false; } protected boolean process(TypeElement annotation, RoundEnvironment roundEnv) throws Exception { return false; } } private class HttpMethodProcessor extends InternalProcessor { MyValidator mValidator = null; MyParser mParser = null; public HttpMethodProcessor() { super(Arrays.asList(GET.class.getName(), PUT.class.getName(), DELETE.class.getName(), POST.class.getName(), Produces.class.getName(), BindService.class.getName())); } @Override protected boolean process(TypeElement annotation, RoundEnvironment roundEnv) throws Exception { if (mParser == null) { mValidator = new MyValidator(processingEnv.getMessager()); mParser = new MyParser(processingEnv.getMessager(), mValidator); } for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) { ExecutableElement ee = (ExecutableElement) element; if (!processed.contains(ee)) { processed.add(ee); ParsedMethod parsedMethod = mParser.parse(ee); parsedMethods.add(parsedMethod); } } return true; } } private class ContentProviderProcessor extends InternalProcessor { public ContentProviderProcessor() { super(Arrays.asList(ContentProvider.class.getName())); } @Override @SuppressWarnings("unchecked") protected boolean process(TypeElement annotation, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) { TypeElement ce = (TypeElement) element; if (ce.getQualifiedName().toString().equals("")) error("Annotation @" + annotation.getSimpleName() + " is not allowed for local or anonymous classes!"); String value = getAnnotationValue(annotation, ce, "value").toString(); if (value != null && value.length() > 0) { List<Method> methods = convertMethods((List<Object>) getAnnotationValue(annotation, ce, "methods")); Provider p = new Provider(value, ce.getSimpleName().toString(), methods); providers.add(p); } } return true; } } private Object getAnnotationValue(TypeElement annotation, Element element, String name) { for (AnnotationMirror am: element.getAnnotationMirrors()) { if (am.getAnnotationType().toString().equals(annotation.getQualifiedName().toString())) { for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) { if (entry.getKey().getSimpleName().toString().equals(name)) { return entry.getValue().getValue(); } } } } return null; } private class FilterProcessor extends InternalProcessor { MyValidator mValidator = null; public FilterProcessor() { super(Arrays.asList(Before.class.getName(), After.class.getName())); } @Override protected boolean process(TypeElement annotation, RoundEnvironment roundEnv) throws Exception { if (mValidator == null) { mValidator = new MyValidator(processingEnv.getMessager()); } for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) { ExecutableElement ee = (ExecutableElement) element; String value = getAnnotationValue(annotation, element, "value").toString(); boolean isBefore = annotation.getQualifiedName().toString().equals(Before.class.getName()); // validate parameters and return type ParsedFilter filter = mValidator.validateFilterAnnotation(value, isBefore, ee); parsedFilters.add(filter); return true; } return false; } } }