@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);
  }
Ejemplo n.º 2
0
  @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);
  }