Ejemplo n.º 1
0
  @Override
  public String format(BindingDeclaration bindingDeclaration) {
    if (bindingDeclaration instanceof SubcomponentDeclaration) {
      return formatSubcomponentDeclaration((SubcomponentDeclaration) bindingDeclaration);
    }

    if (bindingDeclaration instanceof ContributionBinding) {
      ContributionBinding binding = (ContributionBinding) bindingDeclaration;
      switch (binding.bindingKind()) {
        case SYNTHETIC_RELEASABLE_REFERENCE_MANAGER:
          return String.format(
              "binding for %s from the scope declaration",
              stripCommonTypePrefixes(keyFormatter.format(binding.key())));
        case SYNTHETIC_RELEASABLE_REFERENCE_MANAGERS:
          return String.format(
              "Dagger-generated binding for %s",
              stripCommonTypePrefixes(keyFormatter.format(binding.key())));
        default:
          break;
      }
    }

    checkArgument(
        bindingDeclaration.bindingElement().isPresent(),
        "Cannot format bindings without source elements: %s",
        bindingDeclaration);

    Element bindingElement = bindingDeclaration.bindingElement().get();
    switch (bindingElement.asType().getKind()) {
      case EXECUTABLE:
        return methodSignatureFormatter.format(
            MoreElements.asExecutable(bindingElement),
            bindingDeclaration
                .contributingModule()
                .map(module -> MoreTypes.asDeclared(module.asType())));
      case DECLARED:
        return stripCommonTypePrefixes(bindingElement.asType().toString());
      default:
        throw new IllegalArgumentException("Formatting unsupported for element: " + bindingElement);
    }
  }
  @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.º 3
0
 @Override
 public DeclaredType apply(TypeElement typeElement) {
   return MoreTypes.asDeclared(typeElement.asType());
 }
Ejemplo n.º 4
0
  @Override
  Optional<TypeSpec.Builder> write(ClassName generatedTypeName, ProvisionBinding binding) {
    // We don't want to write out resolved bindings -- we want to write out the generic version.
    checkState(!binding.unresolved().isPresent());

    TypeMirror keyType =
        binding.contributionType().equals(ContributionType.MAP)
            ? MapType.from(binding.key().type()).unwrappedValueType(Provider.class)
            : binding.key().type();
    TypeName providedTypeName = TypeName.get(keyType);
    ParameterizedTypeName parameterizedFactoryName = factoryOf(providedTypeName);
    Optional<ParameterizedTypeName> factoryOfRawTypeName = Optional.absent();
    TypeSpec.Builder factoryBuilder;
    Optional<MethodSpec.Builder> constructorBuilder = Optional.absent();
    ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
    ImmutableMap<BindingKey, FrameworkField> fields =
        generateBindingFieldsForDependencies(dependencyRequestMapper, binding);
    switch (binding.factoryCreationStrategy()) {
      case ENUM_INSTANCE:
        factoryBuilder = enumBuilder(generatedTypeName.simpleName()).addEnumConstant("INSTANCE");
        // If we have type parameters, then remove the parameters from our providedTypeName,
        // since we'll be implementing an erased version of it.
        if (!typeParameters.isEmpty()) {
          factoryBuilder.addAnnotation(SUPPRESS_WARNINGS_RAWTYPES);
          // TODO(ronshapiro): instead of reassigning, introduce an optional/second parameter
          providedTypeName = ((ParameterizedTypeName) providedTypeName).rawType;
          factoryOfRawTypeName = Optional.of(factoryOf(providedTypeName));
        }
        break;
      case CLASS_CONSTRUCTOR:
        factoryBuilder =
            classBuilder(generatedTypeName.simpleName())
                .addTypeVariables(typeParameters)
                .addModifiers(FINAL);
        constructorBuilder = Optional.of(constructorBuilder().addModifiers(PUBLIC));
        if (binding.bindingKind().equals(PROVISION)
            && !binding.bindingElement().getModifiers().contains(STATIC)) {
          addConstructorParameterAndTypeField(
              TypeName.get(binding.bindingTypeElement().asType()),
              "module",
              factoryBuilder,
              constructorBuilder.get());
        }
        for (FrameworkField bindingField : fields.values()) {
          addConstructorParameterAndTypeField(
              bindingField.javapoetFrameworkType(),
              bindingField.name(),
              factoryBuilder,
              constructorBuilder.get());
        }
        break;
      default:
        throw new AssertionError();
    }

    factoryBuilder
        .addModifiers(PUBLIC)
        .addSuperinterface(factoryOfRawTypeName.or(parameterizedFactoryName));

    // If constructing a factory for @Inject or @Provides bindings, 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.)
    Optional<MethodSpec> createMethod;
    switch (binding.bindingKind()) {
      case INJECTION:
      case PROVISION:
        // The return type is usually the same as the implementing type, except in the case
        // of enums with type variables (where we cast).
        MethodSpec.Builder createMethodBuilder =
            methodBuilder("create")
                .addModifiers(PUBLIC, STATIC)
                .addTypeVariables(typeParameters)
                .returns(parameterizedFactoryName);
        List<ParameterSpec> params =
            constructorBuilder.isPresent()
                ? constructorBuilder.get().build().parameters
                : ImmutableList.<ParameterSpec>of();
        createMethodBuilder.addParameters(params);
        switch (binding.factoryCreationStrategy()) {
          case ENUM_INSTANCE:
            if (typeParameters.isEmpty()) {
              createMethodBuilder.addStatement("return INSTANCE");
            } else {
              // We use an unsafe cast here because the types are different.
              // It's safe because the type is never referenced anywhere.
              createMethodBuilder.addStatement("return ($T) INSTANCE", TypeNames.FACTORY);
              createMethodBuilder.addAnnotation(SUPPRESS_WARNINGS_UNCHECKED);
            }
            break;

          case CLASS_CONSTRUCTOR:
            createMethodBuilder.addStatement(
                "return new $T($L)",
                javapoetParameterizedGeneratedTypeNameForBinding(binding),
                makeParametersCodeBlock(Lists.transform(params, CodeBlocks.PARAMETER_NAME)));
            break;
          default:
            throw new AssertionError();
        }
        createMethod = Optional.of(createMethodBuilder.build());
        break;
      default:
        createMethod = Optional.absent();
    }

    if (constructorBuilder.isPresent()) {
      factoryBuilder.addMethod(constructorBuilder.get().build());
    }

    List<CodeBlock> parameters = Lists.newArrayList();
    for (DependencyRequest dependency : binding.dependencies()) {
      parameters.add(
          frameworkTypeUsageStatement(
              CodeBlocks.format("$L", fields.get(dependency.bindingKey()).name()),
              dependency.kind()));
    }
    CodeBlock parametersCodeBlock = makeParametersCodeBlock(parameters);

    MethodSpec.Builder getMethodBuilder =
        methodBuilder("get")
            .returns(providedTypeName)
            .addAnnotation(Override.class)
            .addModifiers(PUBLIC);

    if (binding.bindingKind().equals(PROVISION)) {
      CodeBlock.Builder providesMethodInvocationBuilder = CodeBlock.builder();
      if (binding.bindingElement().getModifiers().contains(STATIC)) {
        providesMethodInvocationBuilder.add("$T", ClassName.get(binding.bindingTypeElement()));
      } else {
        providesMethodInvocationBuilder.add("module");
      }
      providesMethodInvocationBuilder.add(
          ".$L($L)", binding.bindingElement().getSimpleName(), parametersCodeBlock);
      CodeBlock providesMethodInvocation = providesMethodInvocationBuilder.build();

      if (binding.provisionType().equals(SET)) {
        TypeName paramTypeName =
            TypeName.get(MoreTypes.asDeclared(keyType).getTypeArguments().get(0));
        // TODO(cgruber): only be explicit with the parameter if paramType contains wildcards.
        getMethodBuilder.addStatement(
            "return $T.<$T>singleton($L)",
            Collections.class,
            paramTypeName,
            providesMethodInvocation);
      } else if (binding.nullableType().isPresent()
          || nullableValidationType.equals(Diagnostic.Kind.WARNING)) {
        if (binding.nullableType().isPresent()) {
          getMethodBuilder.addAnnotation((ClassName) TypeName.get(binding.nullableType().get()));
        }
        getMethodBuilder.addStatement("return $L", providesMethodInvocation);
      } else {
        String failMsg = CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD;
        getMethodBuilder
            .addStatement(
                "$T provided = $L", getMethodBuilder.build().returnType, providesMethodInvocation)
            .addCode("if (provided == null) { ")
            .addStatement("throw new $T($S)", NullPointerException.class, failMsg)
            .addCode("}")
            .addStatement("return provided");
      }
    } else if (binding.membersInjectionRequest().isPresent()) {
      getMethodBuilder.addStatement(
          "$1T instance = new $1T($2L)", providedTypeName, parametersCodeBlock);
      getMethodBuilder.addStatement(
          "$L.injectMembers(instance)",
          fields.get(binding.membersInjectionRequest().get().bindingKey()).name());
      getMethodBuilder.addStatement("return instance");
    } else {
      getMethodBuilder.addStatement("return new $T($L)", providedTypeName, parametersCodeBlock);
    }

    factoryBuilder.addMethod(getMethodBuilder.build());
    if (createMethod.isPresent()) {
      factoryBuilder.addMethod(createMethod.get());
    }

    // TODO(gak): write a sensible toString
    return Optional.of(factoryBuilder);
  }
 String format(BindingMethodIdentifier bindingMethodIdentifier) {
   return format(
       MoreElements.asExecutable(bindingMethodIdentifier.bindingMethod()),
       Optional.of(MoreTypes.asDeclared(bindingMethodIdentifier.contributingModule().asType())));
 }