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

dagger.internal.codegen.base.SourceFileHjarGenerator 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.internal.codegen.base;

import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
import static javax.lang.model.element.Modifier.PRIVATE;

import androidx.room.compiler.processing.XConstructorElement;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XExecutableParameterElement;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import dagger.internal.codegen.javapoet.CodeBlocks;
import dagger.internal.codegen.javapoet.TypeNames;
import java.util.Optional;
import javax.lang.model.element.Modifier;

/**
 * A source file generator that only writes the relevant code necessary for Bazel to create a
 * correct header (ABI) jar.
 */
public final class SourceFileHjarGenerator extends SourceFileGenerator {
  public static  SourceFileGenerator wrap(
      SourceFileGenerator delegate, XProcessingEnv processingEnv) {
    return new SourceFileHjarGenerator<>(delegate, processingEnv);
  }

  private final SourceFileGenerator delegate;
  private final XProcessingEnv processingEnv;

  private SourceFileHjarGenerator(SourceFileGenerator delegate, XProcessingEnv processingEnv) {
    super(delegate);
    this.delegate = delegate;
    this.processingEnv = processingEnv;
  }

  @Override
  public XElement originatingElement(T input) {
    return delegate.originatingElement(input);
  }

  @Override
  public ImmutableList topLevelTypes(T input) {
    String packageName = closestEnclosingTypeElement(originatingElement(input)).getPackageName();
    return delegate.topLevelTypes(input).stream()
        .map(completeType -> skeletonType(packageName, completeType.build()))
        .collect(toImmutableList());
  }

  private TypeSpec.Builder skeletonType(String packageName, TypeSpec completeType) {
    TypeSpec.Builder skeleton =
        classBuilder(completeType.name)
            .addSuperinterfaces(completeType.superinterfaces)
            .addTypeVariables(completeType.typeVariables)
            .addModifiers(completeType.modifiers.toArray(new Modifier[0]))
            .addAnnotations(completeType.annotations);

    if (!completeType.superclass.equals(ClassName.OBJECT)) {
      skeleton.superclass(completeType.superclass);
    }

    completeType.methodSpecs.stream()
        .filter(method -> !method.modifiers.contains(PRIVATE) || method.isConstructor())
        .map(completeMethod -> skeletonMethod(packageName, completeType, completeMethod))
        .forEach(skeleton::addMethod);

    completeType.fieldSpecs.stream()
        .filter(field -> !field.modifiers.contains(PRIVATE))
        .map(this::skeletonField)
        .forEach(skeleton::addField);

    completeType.typeSpecs.stream()
        .map(type -> skeletonType(packageName, type).build())
        .forEach(skeleton::addType);

    completeType.alwaysQualifiedNames
        .forEach(skeleton::alwaysQualify);

    return skeleton;
  }

  private MethodSpec skeletonMethod(
      String packageName, TypeSpec completeType, MethodSpec completeMethod) {
    MethodSpec.Builder skeleton =
        completeMethod.isConstructor()
            ? constructorBuilder()
            : methodBuilder(completeMethod.name).returns(completeMethod.returnType);

    if (completeMethod.isConstructor()) {
      getRequiredSuperCall(packageName, completeType)
          .ifPresent(superCall -> skeleton.addStatement("$L", superCall));
    } else if (!completeMethod.returnType.equals(TypeName.VOID)) {
      skeleton.addStatement("return $L", getDefaultValueCodeBlock(completeMethod.returnType));
    }

    return skeleton
        .addModifiers(completeMethod.modifiers)
        .addTypeVariables(completeMethod.typeVariables)
        .addParameters(completeMethod.parameters)
        .addExceptions(completeMethod.exceptions)
        .varargs(completeMethod.varargs)
        .addAnnotations(completeMethod.annotations)
        .build();
  }

  private Optional getRequiredSuperCall(String packageName, TypeSpec completeType) {
    if (completeType.superclass.equals(TypeName.OBJECT)) {
      return Optional.empty();
    }

    ClassName rawSuperClass = (ClassName) TypeNames.rawTypeName(completeType.superclass);
    XTypeElement superTypeElement =
        processingEnv.requireTypeElement(rawSuperClass.canonicalName());

    ImmutableSet accessibleConstructors =
        superTypeElement.getConstructors().stream()
            .filter(
                constructor ->
                    // isElementAccessibleFrom doesn't take protected into account so check manually
                    constructor.isProtected()
                        || isElementAccessibleFrom(constructor, packageName))
            .collect(toImmutableSet());

    // If there's an accessible default constructor we don't need to call super() manually.
    if (accessibleConstructors.isEmpty()
            || accessibleConstructors.stream()
                .anyMatch(constructor -> constructor.getParameters().isEmpty())) {
      return Optional.empty();
    }

    return Optional.of(
        CodeBlock.of(
            "super($L)",
            CodeBlocks.makeParametersCodeBlock(
                // We just choose the first constructor (it doesn't really matter since we're just
                // trying to ensure the constructor body compiles).
                accessibleConstructors.stream().findFirst().get().getParameters().stream()
                    .map(XExecutableParameterElement::getType)
                    .map(XType::getTypeName)
                    .map(SourceFileHjarGenerator::getDefaultValueCodeBlock)
                    .collect(toImmutableList()))));
  }

  /**
   * Returns a {@link CodeBlock} containing the default value for the given {@code typeName}.
   *
   * 

See https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html. */ private static CodeBlock getDefaultValueCodeBlock(TypeName typeName) { if (typeName.isPrimitive()) { if (typeName.equals(TypeName.BOOLEAN)) { return CodeBlock.of("false"); } else if (typeName.equals(TypeName.CHAR)) { return CodeBlock.of("'\u0000'"); } else if (typeName.equals(TypeName.BYTE)) { return CodeBlock.of("0"); } else if (typeName.equals(TypeName.SHORT)) { return CodeBlock.of("0"); } else if (typeName.equals(TypeName.INT)) { return CodeBlock.of("0"); } else if (typeName.equals(TypeName.LONG)) { return CodeBlock.of("0L"); } else if (typeName.equals(TypeName.FLOAT)) { return CodeBlock.of("0.0f"); } else if (typeName.equals(TypeName.DOUBLE)) { return CodeBlock.of("0.0d"); } else { throw new AssertionError("Unexpected type: " + typeName); } } return CodeBlock.of("null"); } private FieldSpec skeletonField(FieldSpec completeField) { return FieldSpec.builder( completeField.type, completeField.name, completeField.modifiers.toArray(new Modifier[0])) .addAnnotations(completeField.annotations) .build(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy