public Optional generate()

in mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/mapper/MapperDaoFactoryMethodGenerator.java [64:197]


  public Optional<MethodSpec> generate() {

    // Validate the return type, which tells us what DAO to build, and whether the method should be
    // async.
    ClassName daoImplementationName = null;
    boolean isAsync = false;
    TypeMirror returnTypeMirror = methodElement.getReturnType();
    if (returnTypeMirror.getKind() == TypeKind.DECLARED) {
      DeclaredType declaredReturnType = (DeclaredType) returnTypeMirror;
      if (declaredReturnType.getTypeArguments().isEmpty()) {
        Element returnTypeElement = declaredReturnType.asElement();
        if (returnTypeElement.getAnnotation(Dao.class) != null) {
          daoImplementationName =
              GeneratedNames.daoImplementation(((TypeElement) returnTypeElement));
        }
      } else if (context.getClassUtils().isFuture(declaredReturnType)) {
        TypeMirror typeArgument = declaredReturnType.getTypeArguments().get(0);
        if (typeArgument.getKind() == TypeKind.DECLARED) {
          Element typeArgumentElement = ((DeclaredType) typeArgument).asElement();
          if (typeArgumentElement.getAnnotation(Dao.class) != null) {
            daoImplementationName =
                GeneratedNames.daoImplementation(((TypeElement) typeArgumentElement));
            isAsync = true;
          }
        }
      }
    }
    if (daoImplementationName == null) {
      context
          .getMessager()
          .error(
              methodElement,
              "Invalid return type: %s methods must return a %s-annotated interface, "
                  + "or future thereof",
              DaoFactory.class.getSimpleName(),
              Dao.class.getSimpleName());
      return Optional.empty();
    }

    // Validate the arguments
    String keyspaceArgumentName = null;
    String tableArgumentName = null;
    String profileArgumentName = null;
    boolean profileIsClass = false;

    for (VariableElement parameterElement : methodElement.getParameters()) {
      if (parameterElement.getAnnotation(DaoKeyspace.class) != null) {
        keyspaceArgumentName =
            validateKeyspaceOrTableParameter(
                parameterElement, keyspaceArgumentName, DaoKeyspace.class, context);
        if (keyspaceArgumentName == null) {
          return Optional.empty();
        }
      } else if (parameterElement.getAnnotation(DaoTable.class) != null) {
        tableArgumentName =
            validateKeyspaceOrTableParameter(
                parameterElement, tableArgumentName, DaoTable.class, context);
        if (tableArgumentName == null) {
          return Optional.empty();
        }
      } else if (parameterElement.getAnnotation(DaoProfile.class) != null) {

        profileArgumentName =
            validateExecutionProfile(parameterElement, profileArgumentName, context);
        profileIsClass =
            context.getClassUtils().isSame(parameterElement.asType(), DriverExecutionProfile.class);
        if (profileArgumentName == null) {
          return Optional.empty();
        }
      } else {
        context
            .getMessager()
            .error(
                methodElement,
                "Invalid parameter annotations: "
                    + "%s method parameters must be annotated with @%s, @%s or @%s",
                DaoFactory.class.getSimpleName(),
                DaoKeyspace.class.getSimpleName(),
                DaoTable.class.getSimpleName(),
                DaoProfile.class.getSimpleName());
        return Optional.empty();
      }
    }
    boolean isCachedByMethodArguments =
        (keyspaceArgumentName != null || tableArgumentName != null || profileArgumentName != null);

    TypeName returnTypeName = ClassName.get(methodElement.getReturnType());
    String suggestedFieldName = methodElement.getSimpleName() + "Cache";
    String fieldName =
        isCachedByMethodArguments
            ? enclosingClass.addDaoMapField(suggestedFieldName, returnTypeName)
            : enclosingClass.addDaoSimpleField(
                suggestedFieldName, returnTypeName, daoImplementationName, isAsync);

    MethodSpec.Builder overridingMethodBuilder = GeneratedCodePatterns.override(methodElement);

    if (isCachedByMethodArguments) {
      // DaoCacheKey key = new DaoCacheKey(<ks>, <table>, <profileName>, <profile>)
      // where <ks>, <table> is either the name of the parameter or "(CqlIdentifier)null"
      overridingMethodBuilder.addCode("$1T key = new $1T(", DaoCacheKey.class);
      if (keyspaceArgumentName == null) {
        overridingMethodBuilder.addCode("($T)null", CqlIdentifier.class);
      } else {
        overridingMethodBuilder.addCode("$L", keyspaceArgumentName);
      }
      overridingMethodBuilder.addCode(", ");
      if (tableArgumentName == null) {
        overridingMethodBuilder.addCode("($T)null", CqlIdentifier.class);
      } else {
        overridingMethodBuilder.addCode("$L", tableArgumentName);
      }
      overridingMethodBuilder.addCode(", ");
      if (profileArgumentName == null) {
        overridingMethodBuilder.addCode("null, null);\n");
      } else {
        if (profileIsClass) {
          overridingMethodBuilder.addCode("null, $L);\n", profileArgumentName);
        } else {
          overridingMethodBuilder.addCode("$L, null);\n", profileArgumentName);
        }
      }

      overridingMethodBuilder.addStatement(
          "return $L.computeIfAbsent(key, "
              + "k -> $T.$L(context.withDaoParameters(k.getKeyspaceId(), k.getTableId(), "
              + "k.getExecutionProfileName(), k.getExecutionProfile())))",
          fieldName,
          daoImplementationName,
          isAsync ? "initAsync" : "init");
    } else {
      overridingMethodBuilder.addStatement("return $L.get()", fieldName);
    }
    return Optional.of(overridingMethodBuilder.build());
  }