@Override ImmutableSet<JavaWriter> write(ClassName generatedTypeName, ProductionBinding binding) { TypeMirror keyType = binding.productionType().equals(Type.MAP) ? Util.getProvidedValueTypeOfMap(MoreTypes.asDeclared(binding.key().type())) : binding.key().type(); TypeName providedTypeName = TypeNames.forTypeMirror(keyType); TypeName futureTypeName = ParameterizedTypeName.create(ClassName.fromClass(ListenableFuture.class), providedTypeName); JavaWriter writer = JavaWriter.inPackage(generatedTypeName.packageName()); ClassWriter factoryWriter = writer.addClass(generatedTypeName.simpleName()); ConstructorWriter constructorWriter = factoryWriter.addConstructor(); constructorWriter.addModifiers(PUBLIC); factoryWriter.addField(binding.bindingTypeElement(), "module").addModifiers(PRIVATE, FINAL); constructorWriter.addParameter(binding.bindingTypeElement(), "module"); constructorWriter .body() .addSnippet("assert module != null;") .addSnippet("this.module = module;"); factoryWriter.addField(Executor.class, "executor").addModifiers(PRIVATE, FINAL); constructorWriter.addParameter(Executor.class, "executor"); constructorWriter .body() .addSnippet("assert executor != null;") .addSnippet("this.executor = executor;"); factoryWriter.annotate(Generated.class).setValue(ComponentProcessor.class.getName()); factoryWriter.addModifiers(PUBLIC); factoryWriter.addModifiers(FINAL); factoryWriter.setSuperclass( ParameterizedTypeName.create(AbstractProducer.class, providedTypeName)); MethodWriter getMethodWriter = factoryWriter.addMethod(futureTypeName, "compute"); getMethodWriter.annotate(Override.class); getMethodWriter.addModifiers(PROTECTED); final ImmutableMap<BindingKey, FrameworkField> fields = SourceFiles.generateBindingFieldsForDependencies( dependencyRequestMapper, binding.dependencies()); for (FrameworkField bindingField : fields.values()) { TypeName fieldType = bindingField.frameworkType(); FieldWriter field = factoryWriter.addField(fieldType, bindingField.name()); field.addModifiers(PRIVATE, FINAL); constructorWriter.addParameter(field.type(), field.name()); constructorWriter .body() .addSnippet("assert %s != null;", field.name()) .addSnippet("this.%1$s = %1$s;", field.name()); } boolean returnsFuture = binding.bindingKind().equals(ProductionBinding.Kind.FUTURE_PRODUCTION); ImmutableList<DependencyRequest> asyncDependencies = FluentIterable.from(binding.dependencies()) .filter( new Predicate<DependencyRequest>() { @Override public boolean apply(DependencyRequest dependency) { return isAsyncDependency(dependency); } }) .toList(); for (DependencyRequest dependency : asyncDependencies) { ParameterizedTypeName futureType = ParameterizedTypeName.create( ClassName.fromClass(ListenableFuture.class), asyncDependencyType(dependency)); String name = fields.get(dependency.bindingKey()).name(); Snippet futureAccess = Snippet.format("%s.get()", name); getMethodWriter .body() .addSnippet( "%s %sFuture = %s;", futureType, name, dependency.kind().equals(DependencyRequest.Kind.PRODUCED) ? Snippet.format( "%s.createFutureProduced(%s)", ClassName.fromClass(Producers.class), futureAccess) : futureAccess); } if (asyncDependencies.isEmpty()) { ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder(); for (DependencyRequest dependency : binding.dependencies()) { parameterSnippets.add( frameworkTypeUsageStatement( Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind())); } final boolean wrapWithFuture = false; // since submitToExecutor will create the future Snippet invocationSnippet = getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build()); TypeName callableReturnType = returnsFuture ? futureTypeName : providedTypeName; Snippet throwsClause = getThrowsClause(binding.thrownTypes()); Snippet callableSnippet = Snippet.format( Joiner.on('\n') .join( "new %1$s<%2$s>() {", " @Override public %2$s call() %3$s{", " return %4$s;", " }", "}"), ClassName.fromClass(Callable.class), callableReturnType, throwsClause, invocationSnippet); getMethodWriter .body() .addSnippet( "%s future = %s.submitToExecutor(%s, executor);", ParameterizedTypeName.create( ClassName.fromClass(ListenableFuture.class), callableReturnType), ClassName.fromClass(Producers.class), callableSnippet); getMethodWriter .body() .addSnippet( "return %s;", returnsFuture ? Snippet.format("%s.dereference(future)", ClassName.fromClass(Futures.class)) : "future"); } else { final Snippet futureSnippet; final Snippet transformSnippet; if (asyncDependencies.size() == 1) { DependencyRequest asyncDependency = Iterables.getOnlyElement(asyncDependencies); futureSnippet = Snippet.format("%s", fields.get(asyncDependency.bindingKey()).name() + "Future"); String argName = asyncDependency.requestElement().getSimpleName().toString(); ImmutableList.Builder<Snippet> parameterSnippets = ImmutableList.builder(); for (DependencyRequest dependency : binding.dependencies()) { // We really want to compare instances here, because asyncDependency is an element in the // set binding.dependencies(). if (dependency == asyncDependency) { parameterSnippets.add(Snippet.format("%s", argName)); } else { parameterSnippets.add( frameworkTypeUsageStatement( Snippet.format(fields.get(dependency.bindingKey()).name()), dependency.kind())); } } boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future Snippet invocationSnippet = getInvocationSnippet(wrapWithFuture, binding, parameterSnippets.build()); Snippet throwsClause = getThrowsClause(binding.thrownTypes()); transformSnippet = Snippet.format( Joiner.on('\n') .join( "new %1$s<%2$s, %3$s>() {", " @Override public %4$s apply(%2$s %5$s) %6$s{", " return %7$s;", " }", "}"), ClassName.fromClass(AsyncFunction.class), asyncDependencyType(asyncDependency), providedTypeName, futureTypeName, argName, throwsClause, invocationSnippet); } else { futureSnippet = Snippet.format( "%s.<%s>allAsList(%s)", ClassName.fromClass(Futures.class), ClassName.fromClass(Object.class), Joiner.on(",") .join( FluentIterable.from(asyncDependencies) .transform( new Function<DependencyRequest, String>() { @Override public String apply(DependencyRequest dependency) { return fields.get(dependency.bindingKey()).name() + "Future"; } }))); ImmutableList<Snippet> parameterSnippets = getParameterSnippets(binding, fields, "args"); boolean wrapWithFuture = !returnsFuture; // only wrap if we don't already have a future Snippet invocationSnippet = getInvocationSnippet(wrapWithFuture, binding, parameterSnippets); ParameterizedTypeName listOfObject = ParameterizedTypeName.create( ClassName.fromClass(List.class), ClassName.fromClass(Object.class)); Snippet throwsClause = getThrowsClause(binding.thrownTypes()); transformSnippet = Snippet.format( Joiner.on('\n') .join( "new %1$s<%2$s, %3$s>() {", " @SuppressWarnings(\"unchecked\") // safe by specification", " @Override public %4$s apply(%2$s args) %5$s{", " return %6$s;", " }", "}"), ClassName.fromClass(AsyncFunction.class), listOfObject, providedTypeName, futureTypeName, throwsClause, invocationSnippet); } getMethodWriter .body() .addSnippet( "return %s.%s(%s, %s, executor);", ClassName.fromClass(Futures.class), "transform", futureSnippet, transformSnippet); } // TODO(gak): write a sensible toString return ImmutableSet.of(writer); }
@Override Optional<TypeSpec.Builder> write(ClassName generatedTypeName, MembersInjectionBinding binding) { // Empty members injection bindings are special and don't need source files. if (binding.injectionSites().isEmpty()) { return Optional.absent(); } // We don't want to write out resolved bindings -- we want to write out the generic version. checkState(!binding.unresolved().isPresent()); ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding); TypeSpec.Builder injectorTypeBuilder = TypeSpec.classBuilder(generatedTypeName.simpleName()) .addModifiers(PUBLIC, FINAL) .addTypeVariables(typeParameters); TypeName injectedTypeName = TypeName.get(binding.key().type()); TypeName implementedType = membersInjectorOf(injectedTypeName); injectorTypeBuilder.addSuperinterface(implementedType); MethodSpec.Builder injectMembersBuilder = MethodSpec.methodBuilder("injectMembers") .returns(TypeName.VOID) .addModifiers(PUBLIC) .addAnnotation(Override.class) .addParameter(injectedTypeName, "instance") .addCode("if (instance == null) {") .addStatement( "throw new $T($S)", NullPointerException.class, "Cannot inject members into a null reference") .addCode("}"); ImmutableMap<BindingKey, FrameworkField> fields = SourceFiles.generateBindingFieldsForDependencies(dependencyRequestMapper, binding); ImmutableMap.Builder<BindingKey, FieldSpec> dependencyFieldsBuilder = ImmutableMap.builder(); MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder().addModifiers(PUBLIC); // We use a static create method so that generated components can avoid having // to refer to the generic types of the factory. // (Otherwise they may have visibility problems referring to the types.) MethodSpec.Builder createMethodBuilder = MethodSpec.methodBuilder("create") .returns(implementedType) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariables(typeParameters); createMethodBuilder.addCode( "return new $T(", javapoetParameterizedGeneratedTypeNameForBinding(binding)); ImmutableList.Builder<CodeBlock> constructorInvocationParameters = ImmutableList.builder(); boolean usesRawFrameworkTypes = false; UniqueNames fieldNames = new UniqueNames(); for (Entry<BindingKey, FrameworkField> fieldEntry : fields.entrySet()) { BindingKey bindingKey = fieldEntry.getKey(); FrameworkField bindingField = fieldEntry.getValue(); // If the dependency type is not visible to this members injector, then use the raw framework // type for the field. boolean useRawFrameworkType = !VISIBLE_TO_MEMBERS_INJECTOR.visit(bindingKey.key().type(), binding); String fieldName = fieldNames.getUniqueName(bindingField.name()); TypeName fieldType = useRawFrameworkType ? bindingField.javapoetFrameworkType().rawType : bindingField.javapoetFrameworkType(); FieldSpec.Builder fieldBuilder = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL); ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(fieldType, fieldName); // If we're using the raw type for the field, then suppress the injectMembers method's // unchecked-type warning and the field's and the constructor and create-method's // parameters' raw-type warnings. if (useRawFrameworkType) { usesRawFrameworkTypes = true; fieldBuilder.addAnnotation(SUPPRESS_WARNINGS_RAWTYPES); parameterBuilder.addAnnotation(SUPPRESS_WARNINGS_RAWTYPES); } constructorBuilder.addParameter(parameterBuilder.build()); createMethodBuilder.addParameter(parameterBuilder.build()); FieldSpec field = fieldBuilder.build(); injectorTypeBuilder.addField(field); constructorBuilder.addStatement("assert $N != null", field); constructorBuilder.addStatement("this.$N = $N", field, field); dependencyFieldsBuilder.put(bindingKey, field); constructorInvocationParameters.add(CodeBlocks.format("$N", field)); } createMethodBuilder.addCode(CodeBlocks.join(constructorInvocationParameters.build(), ", ")); createMethodBuilder.addCode(");"); injectorTypeBuilder.addMethod(constructorBuilder.build()); injectorTypeBuilder.addMethod(createMethodBuilder.build()); Set<String> delegateMethods = new HashSet<>(); ImmutableMap<BindingKey, FieldSpec> dependencyFields = dependencyFieldsBuilder.build(); List<MethodSpec> injectMethodsForSubclasses = new ArrayList<>(); for (InjectionSite injectionSite : binding.injectionSites()) { injectMembersBuilder.addCode( visibleToMembersInjector(binding, injectionSite.element()) ? directInjectMemberCodeBlock(binding, dependencyFields, injectionSite) : delegateInjectMemberCodeBlock(dependencyFields, injectionSite)); if (!injectionSite.element().getModifiers().contains(PUBLIC) && injectionSite.element().getEnclosingElement().equals(binding.bindingElement()) && delegateMethods.add(injectionSiteDelegateMethodName(injectionSite.element()))) { injectMethodsForSubclasses.add( injectorMethodForSubclasses( dependencyFields, typeParameters, injectedTypeName, injectionSite.element(), injectionSite.dependencies())); } } if (usesRawFrameworkTypes) { injectMembersBuilder.addAnnotation(SUPPRESS_WARNINGS_UNCHECKED); } injectorTypeBuilder.addMethod(injectMembersBuilder.build()); for (MethodSpec methodSpec : injectMethodsForSubclasses) { injectorTypeBuilder.addMethod(methodSpec); } return Optional.of(injectorTypeBuilder); }