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

dagger.hilt.android.processor.internal.bindvalue.BindValueMetadata Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2020 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.hilt.android.processor.internal.bindvalue;

import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.xprocessing.XElements.asField;

import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XElementKt;
import androidx.room.compiler.processing.XFieldElement;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.ProcessorErrors;
import dagger.hilt.processor.internal.Processors;
import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XElements;
import java.util.Collection;
import java.util.Optional;

/**
 * Represents metadata for a test class that has {@code BindValue} fields.
 */
@AutoValue
abstract class BindValueMetadata {
  static final ImmutableSet BIND_VALUE_ANNOTATIONS =
      ImmutableSet.of(
          ClassNames.ANDROID_BIND_VALUE);
  static final ImmutableSet BIND_VALUE_INTO_SET_ANNOTATIONS =
      ImmutableSet.of(
          ClassNames.ANDROID_BIND_VALUE_INTO_SET);
  static final ImmutableSet BIND_ELEMENTS_INTO_SET_ANNOTATIONS =
      ImmutableSet.of(
          ClassNames.ANDROID_BIND_ELEMENTS_INTO_SET);
  static final ImmutableSet BIND_VALUE_INTO_MAP_ANNOTATIONS =
      ImmutableSet.of(
          ClassNames.ANDROID_BIND_VALUE_INTO_MAP);

  /**
   * @return the {@code TestRoot} annotated class's name.
   */
  abstract XTypeElement testElement();

  /** @return a {@link ImmutableSet} of elements annotated with @BindValue. */
  abstract ImmutableSet bindValueElements();

  /**
   * @return a new BindValueMetadata instance.
   */
  static BindValueMetadata create(
      XTypeElement testElement, Collection bindValueElements) {

    ImmutableSet.Builder elements = ImmutableSet.builder();
    for (XElement element : bindValueElements) {
      elements.add(BindValueElement.create(element));
    }

    return new AutoValue_BindValueMetadata(testElement, elements.build());
  }

  @AutoValue
  abstract static class BindValueElement {
    abstract XFieldElement fieldElement();

    abstract ClassName annotationName();

    abstract Optional qualifier();

    abstract Optional mapKey();

    abstract Optional getterElement();

    static BindValueElement create(XElement element) {
      ImmutableList bindValues =
          BindValueProcessingStep.getBindValueAnnotations(element);
      ProcessorErrors.checkState(
          bindValues.size() == 1,
          element,
          "Fields can be annotated with only one of @BindValue, @BindValueIntoMap,"
              + " @BindElementsIntoSet, @BindValueIntoSet. Found: %s",
          bindValues.stream().map(m -> "@" + m.simpleName()).collect(toImmutableList()));
      ClassName annotationClassName = getOnlyElement(bindValues);

      ProcessorErrors.checkState(
          XElementKt.isField(element),
          element,
          "@%s can only be used with fields. Found: %s",
          annotationClassName.simpleName(),
          XElements.toStableString(element));

      XFieldElement field = asField(element);
      Optional propertyGetter = Optional.ofNullable(field.getGetter());
      if (propertyGetter.isPresent()) {
        ProcessorErrors.checkState(
            !propertyGetter.get().isPrivate(),
            field,
            "@%s field getter cannot be private. Found: %s",
            annotationClassName.simpleName(),
            XElements.toStableString(field));
      } else {
        ProcessorErrors.checkState(
            !XElements.isPrivate(field),
            field,
            "@%s fields cannot be private. Found: %s",
            annotationClassName.simpleName(),
            XElements.toStableString(field));
      }

      ProcessorErrors.checkState(
          !field.hasAnnotation(ClassNames.INJECT),
          field,
          "@%s fields cannot be used with @Inject annotation. Found %s",
          annotationClassName.simpleName(),
          XElements.toStableString(field));

      ImmutableList qualifiers = Processors.getQualifierAnnotations(field);
      ProcessorErrors.checkState(
          qualifiers.size() <= 1,
          field,
          "@%s fields cannot have more than one qualifier. Found %s",
          annotationClassName.simpleName(),
          qualifiers.stream().map(XAnnotations::toStableString).collect(toImmutableList()));

      ImmutableList mapKeys = Processors.getMapKeyAnnotations(field);
      Optional optionalMapKeys;
      if (BIND_VALUE_INTO_MAP_ANNOTATIONS.contains(annotationClassName)) {
        ProcessorErrors.checkState(
            mapKeys.size() == 1,
            field,
            "@BindValueIntoMap fields must have exactly one @MapKey. Found %s",
            mapKeys.stream().map(XAnnotations::toStableString).collect(toImmutableList()));
        optionalMapKeys = Optional.of(mapKeys.get(0));
      } else {
        ProcessorErrors.checkState(
            mapKeys.isEmpty(),
            field,
            "@MapKey can only be used on @BindValueIntoMap fields, not @%s fields",
            annotationClassName.simpleName());
        optionalMapKeys = Optional.empty();
      }

      ImmutableList scopes = Processors.getScopeAnnotations(field);
      ProcessorErrors.checkState(
          scopes.isEmpty(),
          field,
          "@%s fields cannot be scoped. Found %s",
          annotationClassName.simpleName(),
          scopes.stream().map(XAnnotations::toStableString).collect(toImmutableList()));

      return new AutoValue_BindValueMetadata_BindValueElement(
          field,
          annotationClassName,
          qualifiers.isEmpty()
              ? Optional.empty()
              : Optional.of(qualifiers.get(0)),
          optionalMapKeys,
          propertyGetter);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy