public Optional generate()

in mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/dao/DaoIncrementMethodGenerator.java [81:226]


  public Optional<MethodSpec> generate() {

    TypeElement entityElement = getEntityClassFromAnnotation(Increment.class);
    EntityDefinition entityDefinition;
    if (entityElement == null) {
      context
          .getMessager()
          .error(
              methodElement,
              "Missing entity class: %s methods must always have an 'entityClass' argument",
              Increment.class.getSimpleName());
      return Optional.empty();
    } else {
      entityDefinition = context.getEntityFactory().getDefinition(entityElement);
    }

    // Validate the parameters:
    // - all the PK components of the entity, in order.
    // - one or more increment parameters that must match non-PK columns.
    // - a Function<BoundStatementBuilder, BoundStatementBuilder> can be added in last position.
    List<? extends VariableElement> parameters = methodElement.getParameters();
    VariableElement boundStatementFunction = findBoundStatementFunction(methodElement);
    if (boundStatementFunction != null) {
      parameters = parameters.subList(0, parameters.size() - 1);
    }

    List<? extends VariableElement> primaryKeyParameters = parameters;
    // Must have at least enough parameters for the full PK
    if (primaryKeyParameters.size() < entityDefinition.getPrimaryKey().size()) {
      List<TypeName> primaryKeyTypes =
          entityDefinition.getPrimaryKey().stream()
              .map(d -> d.getType().asTypeName())
              .collect(Collectors.toList());
      context
          .getMessager()
          .error(
              methodElement,
              "Invalid parameter list: %s methods must specify the entire primary key "
                  + "(expected primary keys of %s: %s)",
              Increment.class.getSimpleName(),
              entityElement.getSimpleName(),
              primaryKeyTypes);
      return Optional.empty();
    } else {
      primaryKeyParameters =
          primaryKeyParameters.subList(0, entityDefinition.getPrimaryKey().size());
      warnIfCqlNamePresent(primaryKeyParameters);
    }
    // PK parameter types must match
    if (!EntityUtils.areParametersValid(
        entityElement,
        entityDefinition,
        primaryKeyParameters,
        Increment.class,
        context,
        methodElement,
        processedType,
        "" /* no condition, @Increment must always have the full PK */)) {
      return Optional.empty();
    }

    // The remaining parameters are the increments to the counter columns
    List<? extends VariableElement> incrementParameters =
        parameters.subList(primaryKeyParameters.size(), parameters.size());
    if (!validateCqlNamesPresent(incrementParameters)) {
      return Optional.empty();
    }
    for (VariableElement parameter : incrementParameters) {
      TypeMirror type = parameter.asType();
      if (type.getKind() != TypeKind.LONG && !context.getClassUtils().isSame(type, Long.class)) {
        context
            .getMessager()
            .error(
                methodElement,
                "Invalid argument type: increment parameters of %s methods can only be "
                    + "primitive longs or java.lang.Long. Offending parameter: '%s' (%s)",
                Increment.class.getSimpleName(),
                parameter.getSimpleName(),
                type);
        return Optional.empty();
      }
    }

    // Validate the return type:
    DaoReturnType returnType =
        parseAndValidateReturnType(getSupportedReturnTypes(), Increment.class.getSimpleName());
    if (returnType == null) {
      return Optional.empty();
    }

    // Generate the method:
    String helperFieldName = enclosingClass.addEntityHelperField(ClassName.get(entityElement));
    String statementName =
        enclosingClass.addPreparedStatement(
            methodElement,
            (methodBuilder, requestName) ->
                generatePrepareRequest(
                    methodBuilder,
                    requestName,
                    entityDefinition,
                    helperFieldName,
                    incrementParameters));

    CodeBlock.Builder updateStatementBlock = CodeBlock.builder();

    updateStatementBlock.addStatement(
        "$T boundStatementBuilder = $L.boundStatementBuilder()",
        BoundStatementBuilder.class,
        statementName);

    populateBuilderWithStatementAttributes(updateStatementBlock, methodElement);
    populateBuilderWithFunction(updateStatementBlock, boundStatementFunction);

    // Bind the counter increments. The bind parameter names are always the raw parameter names, see
    // generatePrepareRequest.
    List<CodeBlock> bindMarkerNames =
        incrementParameters.stream()
            .map(p -> CodeBlock.of("$S", p.getSimpleName()))
            .collect(Collectors.toList());
    // Force the null saving strategy. This will fail if the user targets Cassandra 2.2, but
    // SET_TO_NULL would not work with counters anyway.
    updateStatementBlock.addStatement(
        "final $1T nullSavingStrategy = $1T.$2L",
        NullSavingStrategy.class,
        NullSavingStrategy.DO_NOT_SET);
    GeneratedCodePatterns.bindParameters(
        incrementParameters, bindMarkerNames, updateStatementBlock, enclosingClass, context, true);

    // Bind the PK columns
    List<CodeBlock> primaryKeyNames =
        entityDefinition.getPrimaryKey().stream()
            .map(PropertyDefinition::getCqlName)
            .collect(Collectors.toList());
    GeneratedCodePatterns.bindParameters(
        primaryKeyParameters,
        primaryKeyNames,
        updateStatementBlock,
        enclosingClass,
        context,
        false);

    updateStatementBlock.addStatement(
        "$T boundStatement = boundStatementBuilder.build()", BoundStatement.class);

    return crudMethod(updateStatementBlock, returnType, helperFieldName);
  }