All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.android.processor.ContributesAndroidInjectorProcessingStep Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2017 The Dagger Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dagger.android.processor;

import static androidx.room.compiler.processing.JavaPoetExtKt.addOriginatingElement;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static com.squareup.javapoet.TypeSpec.interfaceBuilder;
import static dagger.android.processor.DelegateAndroidProcessor.FLAG_EXPERIMENTAL_USE_STRING_KEYS;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import static javax.tools.Diagnostic.Kind.ERROR;

import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XFiler;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import dagger.internal.codegen.xprocessing.XElements;

/** Generates the implementation specified in {@code ContributesAndroidInjector}. */
final class ContributesAndroidInjectorProcessingStep extends BaseProcessingStep {
  private final AndroidInjectorDescriptor.Validator validator;
  private final XProcessingEnv processingEnv;

  ContributesAndroidInjectorProcessingStep(XProcessingEnv processingEnv) {
    this.processingEnv = processingEnv;
    this.validator = new AndroidInjectorDescriptor.Validator(processingEnv.getMessager());
  }

  @Override
  public ImmutableSet annotationClassNames() {
    return ImmutableSet.of(TypeNames.CONTRIBUTES_ANDROID_INJECTOR);
  }

  @Override
  public void process(XElement element, ImmutableSet annotationNames) {
    validator.createIfValid(XElements.asMethod(element)).ifPresent(this::generate);
  }

  private void generate(AndroidInjectorDescriptor descriptor) {
    ClassName moduleName =
        descriptor
            .enclosingModule()
            .topLevelClassName()
            .peerClass(
                Joiner.on('_').join(descriptor.enclosingModule().simpleNames())
                    + "_"
                    + LOWER_CAMEL.to(UPPER_CAMEL, XElements.getSimpleName(descriptor.method())));

    String baseName = descriptor.injectedType().simpleName();
    ClassName subcomponentName = moduleName.nestedClass(baseName + "Subcomponent");
    ClassName subcomponentFactoryName = subcomponentName.nestedClass("Factory");

    TypeSpec.Builder module =
        classBuilder(moduleName)
            .addAnnotation(
                AnnotationSpec.builder(TypeNames.MODULE)
                    .addMember("subcomponents", "$T.class", subcomponentName)
                    .build())
            .addModifiers(PUBLIC, ABSTRACT)
            .addMethod(bindAndroidInjectorFactory(descriptor, subcomponentFactoryName))
            .addType(subcomponent(descriptor, subcomponentName, subcomponentFactoryName))
            .addMethod(constructorBuilder().addModifiers(PRIVATE).build());

    addOriginatingElement(module, descriptor.method());

    XTypeElement generatedAnnotation = processingEnv.findGeneratedAnnotation();
    if (generatedAnnotation != null) {
      module.addAnnotation(
          AnnotationSpec.builder(generatedAnnotation.getClassName())
              .addMember(
                  "value", "$S", ClassName.get("dagger.android.processor", "AndroidProcessor"))
              .build());
    }

    processingEnv
        .getFiler()
        .write(
            JavaFile.builder(moduleName.packageName(), module.build())
                .skipJavaLangImports(true)
                .build(),
            XFiler.Mode.Isolating);
  }

  private static boolean useStringKeys(XProcessingEnv processingEnv) {
    if (!processingEnv.getOptions().containsKey(FLAG_EXPERIMENTAL_USE_STRING_KEYS)) {
      return false;
    }
    String flagValue = processingEnv.getOptions().get(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
    if (flagValue == null || Ascii.equalsIgnoreCase(flagValue, "true")) {
      return true;
    } else if (Ascii.equalsIgnoreCase(flagValue, "false")) {
      return false;
    } else {
      processingEnv
          .getMessager()
          .printMessage(
              ERROR,
              String.format(
                  "Unknown flag value: %s. %s must be set to either 'true' or 'false'.",
                  flagValue, FLAG_EXPERIMENTAL_USE_STRING_KEYS));
      return false;
    }
  }

  private MethodSpec bindAndroidInjectorFactory(
      AndroidInjectorDescriptor descriptor, ClassName subcomponentBuilderName) {
    return methodBuilder("bindAndroidInjectorFactory")
        .addAnnotation(TypeNames.BINDS)
        .addAnnotation(TypeNames.INTO_MAP)
        .addAnnotation(androidInjectorMapKey(descriptor))
        .addModifiers(ABSTRACT)
        .returns(
            ParameterizedTypeName.get(
                TypeNames.ANDROID_INJECTOR_FACTORY, WildcardTypeName.subtypeOf(TypeName.OBJECT)))
        .addParameter(subcomponentBuilderName, "builder")
        .build();
  }

  private AnnotationSpec androidInjectorMapKey(AndroidInjectorDescriptor descriptor) {
    if (useStringKeys(processingEnv)) {
      return AnnotationSpec.builder(TypeNames.ANDROID_INJECTION_KEY)
          .addMember("value", "$S", descriptor.injectedType().toString())
          .build();
    }
    return AnnotationSpec.builder(TypeNames.CLASS_KEY)
        .addMember("value", "$T.class", descriptor.injectedType())
        .build();
  }

  private TypeSpec subcomponent(
      AndroidInjectorDescriptor descriptor,
      ClassName subcomponentName,
      ClassName subcomponentFactoryName) {
    AnnotationSpec.Builder subcomponentAnnotation = AnnotationSpec.builder(TypeNames.SUBCOMPONENT);
    for (ClassName module : descriptor.modules()) {
      subcomponentAnnotation.addMember("modules", "$T.class", module);
    }

    return interfaceBuilder(subcomponentName)
        .addModifiers(PUBLIC)
        .addAnnotation(subcomponentAnnotation.build())
        .addAnnotations(descriptor.scopes())
        .addSuperinterface(
            ParameterizedTypeName.get(TypeNames.ANDROID_INJECTOR, descriptor.injectedType()))
        .addType(subcomponentFactory(descriptor, subcomponentFactoryName))
        .build();
  }

  private TypeSpec subcomponentFactory(
      AndroidInjectorDescriptor descriptor, ClassName subcomponentFactoryName) {
    return interfaceBuilder(subcomponentFactoryName)
        .addAnnotation(TypeNames.SUBCOMPONENT_FACTORY)
        .addModifiers(PUBLIC, STATIC)
        .addSuperinterface(
            ParameterizedTypeName.get(
                TypeNames.ANDROID_INJECTOR_FACTORY, descriptor.injectedType()))
        .build();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy