@SuppressWarnings({"unchecked", "ConstantConditions"}) private void manageType(TypeElement enclosingElement, Logger logger) { // Make sure we don't process twice the same type String simpleName = enclosingElement.getSimpleName().toString(); String qualifiedName = enclosingElement.getQualifiedName().toString(); String packageName = Utils.getElementPackageName(enclosingElement); if (managedTypes.contains(qualifiedName)) return; managedTypes.add(qualifiedName); // Prepare the output file try { JavaFileObject classFile = processingEnv.getFiler().createSourceFile(qualifiedName + INJECTOR_SUFFIX); logger.note("Writing " + classFile.toUri().getRawPath()); Writer out = classFile.openWriter(); JavaWriter writer = new JavaWriter(out); writer.emitPackage(packageName); // Initial imports writer .emitImports(AsyncService.class, Injector.class, Message.class, Set.class, HashSet.class) .emitImports( "com.joanzapata.android.asyncservice.api.annotation.OnMessage.Priority", "android.os.Handler", "android.os.Looper") .emitStaticImports( "com.joanzapata.android.asyncservice.api.annotation.OnMessage.Priority.*"); // Generates "public final class XXXInjector extends Injector<XXX>" writer .emitEmptyLine() .beginType( simpleName + INJECTOR_SUFFIX, "class", of(PUBLIC, FINAL), "Injector<" + simpleName + ">"); // Generate a handler to execute runnables on the UI Thread writer .emitEmptyLine() .emitField( "Handler", "__handler", of(PRIVATE, FINAL), "new Handler(Looper.getMainLooper())"); // Keep trace of when a method has received data which is not from cache writer .emitEmptyLine() .emitField( "Set<String>", "__receivedFinalResponses", of(PRIVATE, FINAL), "new HashSet<String>()"); // Generates "protected void inject(XXX target) { ..." writer .emitEmptyLine() .emitAnnotation(Override.class) .beginMethod("void", "inject", of(PROTECTED), simpleName, "target"); // Here, inject all services List<Element> elementsAnnotatedWith = findElementsAnnotatedWith(enclosingElement, InjectService.class); for (Element element : elementsAnnotatedWith) { if (isPublicOrPackagePrivate(element)) { writer.emitStatement( "target.%s = new %s(target)", element.getSimpleName(), element.asType().toString() + AsyncServiceAP.GENERATED_CLASS_SUFFIX); } } // End of inject() writer.endMethod().emitEmptyLine(); // Generates "protected void dispatch(XXX target, Message event)" writer .emitAnnotation(Override.class) .beginMethod( "void", "dispatch", of(PROTECTED), "final " + simpleName, "target", "final " + Message.class.getSimpleName(), "event", Priority.class.getSimpleName(), "priority"); // Once the user has received a "remote" result, make sure no cache is sent anymore writer .emitField( "boolean", "__hasBeenReceivedAlready", of(FINAL), "event.getQuery() != null && __receivedFinalResponses.contains(event.getQuery())") .emitStatement("if (event.isCached() && __hasBeenReceivedAlready) return") .emitStatement( "if (!__hasBeenReceivedAlready && !event.isCached() && priority == LAST) __receivedFinalResponses.add(event.getQuery())"); // Here, dispatch events to methods List<Element> responseReceivers = findElementsAnnotatedWith(enclosingElement, OnMessage.class); for (Element responseReceiver : responseReceivers) { ExecutableElement annotatedMethod = (ExecutableElement) responseReceiver; AnnotationMirror annotationMirror = getAnnotation(annotatedMethod, OnMessage.class); List<? extends VariableElement> parameters = annotatedMethod.getParameters(); if (parameters.size() > 1) logger.error( responseReceiver, "@OnMessage annotated methods can't have more than 1 argument"); // Define event type given parameter or @InjectResponse value List<String> eventTypes; boolean hasArg = parameters.size() == 1; if (hasArg) { TypeMirror typeMirror = parameters.get(0).asType(); eventTypes = asList(typeMirror.toString()); if (hasTypeParameters(processingEnv, typeMirror)) logger.error( parameters.get(0), "You can't receive typed parameters in @OnMessage annotated methods"); } else { List<AnnotationValue> parameterTypeClasses = getAnnotationValue(annotationMirror, "value"); // Validate each parameter type given in the annotation eventTypes = new ArrayList<String>(); for (AnnotationValue value : parameterTypeClasses) { DeclaredType parameterTypeClass = (DeclaredType) value.getValue(); if (parameterTypeClass == null) logger.error( annotatedMethod, "Either declare an argument or give @OnMessage a value."); if (hasTypeParameters(processingEnv, parameterTypeClass)) logger.error( annotatedMethod, annotationMirror, "value", "You can't receive typed parameters in @OnMessage method"); eventTypes.add(parameterTypeClass.toString()); } } // Define whether we should check emitter or not dependeing on the annotation value VariableElement from = getAnnotationValue(annotationMirror, "from"); boolean checkEmitter = !ALL.toString().equals("" + from); // Check the priority of the method VariableElement priorityValue = getAnnotationValue(annotationMirror, "priority"); Priority priority = !LAST.toString().equals("" + priorityValue) ? FIRST : LAST; writer.beginControlFlow("if (priority == %s)", priority); // Write the code to call the user method if (checkEmitter) writer.beginControlFlow("if (event.getEmitter() == getTarget())"); // Create a new inner class for the Runnable to run on UI thread StringWriter buffer = new StringWriter(); JavaWriter inner = new JavaWriter(buffer); inner .emitPackage("") .beginType("Runnable()", "new") .emitAnnotation("Override") .beginMethod("void", "run", of(PUBLIC)); if (hasArg) inner.emitStatement( "target.%s((%s) event.getPayload())", annotatedMethod.getSimpleName(), eventTypes.get(0)); else inner.emitStatement("target.%s()", annotatedMethod.getSimpleName()); inner.endMethod().endType(); // For each type (can be multiple) for (int i = 0; i < eventTypes.size(); i++) { String eventType = eventTypes.get(i); writer .beginControlFlow( "%sif (event.getPayload() instanceof %s)", i != 0 ? "else " : "", eventType) .emitStatement("__handler.post(%s)", buffer.toString()) .endControlFlow(); } if (checkEmitter) writer.endControlFlow(); writer.endControlFlow(); } // End of inject(); writer.endMethod().emitEmptyLine(); // End of file writer.endType(); out.flush(); out.close(); } catch (IOException e) { throw new IllegalStateException("Error while create the injector for " + qualifiedName, e); } }