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

dagger.internal.codegen.writing.LazyClassKeyProviders Maven / Gradle / Ivy

/*
 * Copyright (C) 2024 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.internal.codegen.writing;

import static dagger.internal.codegen.base.MapKeyAccessibility.isMapKeyAccessibleFrom;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.STATIC;

import androidx.room.compiler.processing.XAnnotation;
import com.google.common.base.Preconditions;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.codegen.base.UniqueNameSet;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.Key;
import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
import java.util.HashMap;
import java.util.Map;

/** Keeps track of all providers for DaggerMap keys. */
public final class LazyClassKeyProviders {
  public static final String MAP_KEY_PROVIDER_NAME = "LazyClassKeyProvider";
  private final ClassName mapKeyProviderType;
  private final Map entries = new HashMap<>();
  private final Map keepClassNamesFields = new HashMap<>();
  private final UniqueNameSet uniqueFieldNames = new UniqueNameSet();
  private final ShardImplementation shardImplementation;
  private boolean providerAdded = false;

  LazyClassKeyProviders(ShardImplementation shardImplementation) {
    String name = shardImplementation.getUniqueClassName(MAP_KEY_PROVIDER_NAME);
    mapKeyProviderType = shardImplementation.name().nestedClass(name);
    this.shardImplementation = shardImplementation;
  }

  /** Returns a reference to a field in LazyClassKeyProvider that corresponds to this binding. */
  CodeBlock getMapKeyExpression(Key key) {
    // This is for avoid generating empty LazyClassKeyProvider in codegen tests
    if (!providerAdded) {
      shardImplementation.addTypeSupplier(this::build);
      providerAdded = true;
    }
    if (!entries.containsKey(key)) {
      addField(key);
    }
    return CodeBlock.of("$T.$N", mapKeyProviderType, entries.get(key));
  }

  private void addField(Key key) {
    Preconditions.checkArgument(
        key.multibindingContributionIdentifier().isPresent()
            && key.multibindingContributionIdentifier()
                .get()
                .bindingMethod()
                .xprocessing()
                .hasAnnotation(TypeNames.LAZY_CLASS_KEY));
    XAnnotation lazyClassKeyAnnotation =
        key.multibindingContributionIdentifier()
            .get()
            .bindingMethod()
            .xprocessing()
            .getAnnotation(TypeNames.LAZY_CLASS_KEY);
    ClassName lazyClassKey =
        lazyClassKeyAnnotation.getAsType("value").getTypeElement().getClassName();
    entries.put(
        key,
        FieldSpec.builder(
                TypeNames.STRING,
                uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_')))
            // TODO(b/217435141): Leave the field as non-final. We will apply @IdentifierNameString
            // on the field, which doesn't work well with static final fields.
            .addModifiers(STATIC)
            .initializer("$S", lazyClassKey.reflectionName())
            .build());
    // To be able to apply -includedescriptorclasses rule to keep the class names referenced by
    // LazyClassKey, we need to generate fields that uses those classes as type in
    // LazyClassKeyProvider. For types that are not accessible from the generated component, we
    // generate fields in the proxy class.
    // Note: the generated field should not be initialized to avoid class loading.
    if (isMapKeyAccessibleFrom(lazyClassKeyAnnotation, shardImplementation.name().packageName())) {
      keepClassNamesFields.put(
          key,
          FieldSpec.builder(
                  lazyClassKey,
                  uniqueFieldNames.getUniqueName(lazyClassKey.canonicalName().replace('.', '_')))
              .addAnnotation(TypeNames.KEEP_FIELD_TYPE)
              .build());
    }
  }

  private TypeSpec build() {
    TypeSpec.Builder builder =
        TypeSpec.classBuilder(mapKeyProviderType)
            .addAnnotation(TypeNames.IDENTIFIER_NAME_STRING)
            .addModifiers(PRIVATE, STATIC, FINAL)
            .addFields(entries.values())
            .addFields(keepClassNamesFields.values());
    return builder.build();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy