Example #1
0
 private CodeBlock getInstanceCodeBlockWithPotentialCast(
     Element injectionSiteElement, Element bindingElement) {
   if (injectionSiteElement.equals(bindingElement)) {
     return CodeBlocks.format("instance");
   }
   TypeName injectionSiteName = TypeName.get(injectionSiteElement.asType());
   if (injectionSiteName instanceof ParameterizedTypeName) {
     injectionSiteName = ((ParameterizedTypeName) injectionSiteName).rawType;
   }
   return CodeBlocks.format("(($T) instance)", injectionSiteName);
 }
Example #2
0
 /**
  * Returns a code block that injects the instance's field or method by calling a static method on
  * the parent MembersInjector class.
  */
 private CodeBlock delegateInjectMemberCodeBlock(
     ImmutableMap<BindingKey, FieldSpec> dependencyFields, InjectionSite injectionSite) {
   return CodeBlocks.format(
       "$L.$L($L);",
       javapoetMembersInjectorNameForType(
           MoreElements.asType(injectionSite.element().getEnclosingElement())),
       injectionSiteDelegateMethodName(injectionSite.element()),
       makeParametersCodeBlock(
           new ImmutableList.Builder<CodeBlock>()
               .add(CodeBlocks.format("instance"))
               .addAll(parameterCodeBlocks(dependencyFields, injectionSite.dependencies(), false))
               .build()));
 }
Example #3
0
 /** Returns a code block that directly injects the instance's field or method. */
 private CodeBlock directInjectMemberCodeBlock(
     MembersInjectionBinding binding,
     ImmutableMap<BindingKey, FieldSpec> dependencyFields,
     InjectionSite injectionSite) {
   return CodeBlocks.format(
       injectionSite.element().getKind().isField() ? "$L.$L = $L;" : "$L.$L($L);",
       getInstanceCodeBlockWithPotentialCast(
           injectionSite.element().getEnclosingElement(), binding.bindingElement()),
       injectionSite.element().getSimpleName(),
       makeParametersCodeBlock(
           parameterCodeBlocks(dependencyFields, injectionSite.dependencies(), true)));
 }
Example #4
0
 /**
  * Returns the parameters for injecting a member.
  *
  * @param passValue if {@code true}, each parameter code block will be the result of converting
  *     the field from the framework type ({@link Provider}, {@link Producer}, etc.) to the real
  *     value; if {@code false}, each parameter code block will be just the field
  */
 private ImmutableList<CodeBlock> parameterCodeBlocks(
     ImmutableMap<BindingKey, FieldSpec> dependencyFields,
     ImmutableSet<DependencyRequest> dependencies,
     boolean passValue) {
   ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
   for (DependencyRequest dependency : dependencies) {
     CodeBlock fieldCodeBlock =
         CodeBlocks.format("$L", dependencyFields.get(dependency.bindingKey()).name);
     parameters.add(
         passValue
             ? frameworkTypeUsageStatement(fieldCodeBlock, dependency.kind())
             : fieldCodeBlock);
   }
   return parameters.build();
 }
Example #5
0
 private MethodSpec injectorMethodForSubclasses(
     ImmutableMap<BindingKey, FieldSpec> dependencyFields,
     List<TypeVariableName> typeParameters,
     TypeName injectedTypeName,
     Element injectionElement,
     ImmutableSet<DependencyRequest> dependencies) {
   MethodSpec.Builder methodBuilder =
       MethodSpec.methodBuilder(injectionSiteDelegateMethodName(injectionElement))
           .addModifiers(PUBLIC, STATIC)
           .addParameter(injectedTypeName, "instance")
           .addTypeVariables(typeParameters);
   ImmutableList.Builder<CodeBlock> providedParameters = ImmutableList.builder();
   Set<String> parameterNames = new HashSet<>();
   for (DependencyRequest dependency : dependencies) {
     FieldSpec field = dependencyFields.get(dependency.bindingKey());
     ParameterSpec parameter =
         ParameterSpec.builder(
                 field.type,
                 staticInjectMethodDependencyParameterName(parameterNames, dependency, field))
             .build();
     methodBuilder.addParameter(parameter);
     providedParameters.add(
         frameworkTypeUsageStatement(CodeBlocks.format("$N", parameter), dependency.kind()));
   }
   if (injectionElement.getKind().isField()) {
     methodBuilder.addStatement(
         "instance.$L = $L",
         injectionElement.getSimpleName(),
         getOnlyElement(providedParameters.build()));
   } else {
     methodBuilder.addStatement(
         "instance.$L($L)",
         injectionElement.getSimpleName(),
         makeParametersCodeBlock(providedParameters.build()));
   }
   return methodBuilder.build();
 }
Example #6
0
  private MethodSpec buildCreateMethod(
      ClassName mapKeyGeneratedTypeName, TypeElement annotationElement) {
    String createMethodName = "create" + annotationElement.getSimpleName();
    MethodSpec.Builder createMethod =
        methodBuilder(createMethodName)
            .addAnnotation(AutoAnnotation.class)
            .addModifiers(PUBLIC, STATIC)
            .returns(TypeName.get(annotationElement.asType()));

    ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
    for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
      String parameterName = annotationMember.getSimpleName().toString();
      TypeName parameterType = TypeName.get(annotationMember.getReturnType());
      createMethod.addParameter(parameterType, parameterName);
      parameters.add(CodeBlocks.format("$L", parameterName));
    }

    ClassName autoAnnotationClass =
        mapKeyGeneratedTypeName.peerClass(
            "AutoAnnotation_" + mapKeyGeneratedTypeName.simpleName() + "_" + createMethodName);
    createMethod.addStatement(
        "return new $T($L)", autoAnnotationClass, makeParametersCodeBlock(parameters.build()));
    return createMethod.build();
  }
Example #7
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);
  }
Example #8
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);
  }