public Optional generate()

in mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/entity/EntityHelperGetMethodGenerator.java [53:245]


  public Optional<MethodSpec> generate() {
    MethodSpec.Builder getBuilder =
        MethodSpec.methodBuilder("get")
            .addAnnotation(Override.class)
            .addModifiers(Modifier.PUBLIC)
            .addParameter(
                ParameterSpec.builder(ClassName.get(GettableByName.class), "source").build())
            .addParameter(ParameterSpec.builder(TypeName.BOOLEAN, "lenient").build())
            .returns(entityDefinition.getClassName());

    TypeName returnType = entityDefinition.getClassName();
    String resultName = "returnValue";
    boolean mutable = entityDefinition.isMutable();
    if (mutable) {
      // Create an instance now, we'll call the setters as we go through the properties
      getBuilder.addStatement("$1T $2L = new $1T()", returnType, resultName);
    }

    // We store each read property into a local variable, store the names here (this is only used if
    // the entity is immutable, we'll call the all-arg constructor at the end).
    List<String> propertyValueNames = new ArrayList<>();

    for (PropertyDefinition property : entityDefinition.getAllValues()) {
      PropertyType type = property.getType();
      CodeBlock cqlName = property.getCqlName();
      String setterName = property.getSetterName();
      String propertyValueName = enclosingClass.getNameIndex().uniqueField("propertyValue");
      propertyValueNames.add(propertyValueName);

      if (type instanceof PropertyType.Simple) {
        TypeName typeName = ((PropertyType.Simple) type).typeName;
        String primitiveAccessor = GeneratedCodePatterns.PRIMITIVE_ACCESSORS.get(typeName);
        if (primitiveAccessor != null) {
          // Primitive type: use dedicated getter, since it is optimized to avoid boxing
          //     int propertyValue1 = source.getInt("length");
          if (mutable) {
            getBuilder
                .beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName)
                .addStatement(
                    "$T $L = source.get$L($L)",
                    typeName,
                    propertyValueName,
                    primitiveAccessor,
                    cqlName)
                .addStatement("$L.$L($L)", resultName, setterName, propertyValueName)
                .endControlFlow();
          } else {
            getBuilder.addStatement(
                "$T $L = !lenient || hasProperty(source, $L) ? source.get$L($L) : $L",
                typeName,
                propertyValueName,
                cqlName,
                primitiveAccessor,
                cqlName,
                typeName.equals(TypeName.BOOLEAN) ? false : 0);
          }
        } else if (typeName instanceof ClassName) {
          // Unparameterized class: use the generic, class-based getter:
          //     UUID propertyValue1 = source.get("id", UUID.class);
          if (mutable) {
            getBuilder
                .beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName)
                .addStatement(
                    "$T $L = source.get($L, $T.class)",
                    typeName,
                    propertyValueName,
                    cqlName,
                    typeName)
                .addStatement("$L.$L($L)", resultName, setterName, propertyValueName)
                .endControlFlow();
          } else {
            getBuilder.addStatement(
                "$T $L = !lenient || hasProperty(source, $L) ? source.get($L, $T.class) : null",
                typeName,
                propertyValueName,
                cqlName,
                cqlName,
                typeName);
          }
        } else {
          // Parameterized type: create a constant and use the GenericType-based getter:
          //     private static final GenericType<List<String>> GENERIC_TYPE =
          //         new GenericType<List<String>>(){};
          //     List<String> propertyValue1 = source.get("names", GENERIC_TYPE);
          // Note that lists, sets and maps of unparameterized classes also fall under that
          // category. Their getter creates a GenericType under the hood, so there's no performance
          // advantage in calling them instead of the generic get().
          if (mutable) {
            getBuilder
                .beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName)
                .addStatement(
                    "$T $L = source.get($L, $L)",
                    typeName,
                    propertyValueName,
                    cqlName,
                    enclosingClass.addGenericTypeConstant(typeName))
                .addStatement("$L.$L($L)", resultName, setterName, propertyValueName)
                .endControlFlow();
          } else {
            getBuilder.addStatement(
                "$T $L = !lenient || hasProperty(source, $L) ? source.get($L, $L) : null",
                typeName,
                propertyValueName,
                cqlName,
                cqlName,
                enclosingClass.addGenericTypeConstant(typeName));
          }
        }
      } else if (type instanceof PropertyType.SingleEntity) {
        ClassName entityClass = ((PropertyType.SingleEntity) type).entityName;
        // Other entity class: the CQL column is a mapped UDT:
        //     Dimensions propertyValue1;
        //     UdtValue udtValue1 = source.getUdtValue("dimensions");
        //     propertyValue1 = udtValue1 == null ? null : dimensionsHelper.get(udtValue1);
        String udtValueName = enclosingClass.getNameIndex().uniqueField("udtValue");
        if (mutable) {
          getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
          getBuilder.addStatement("$T $L", entityClass, propertyValueName);
        } else {
          getBuilder.addStatement("$T $L = null", entityClass, propertyValueName);
          getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
        }
        getBuilder.addStatement(
            "$T $L = source.getUdtValue($L)", UdtValue.class, udtValueName, cqlName);

        // Get underlying udt object and set it on return type
        String childHelper = enclosingClass.addEntityHelperField(entityClass);
        getBuilder.addStatement(
            "$L = $L == null ? null : $L.get($L, lenient)",
            propertyValueName,
            udtValueName,
            childHelper,
            udtValueName);

        if (mutable) {
          getBuilder.addStatement("$L.$L($L)", resultName, setterName, propertyValueName);
        }
        getBuilder.endControlFlow();
      } else {
        // Collection of other entity class(es): the CQL column is a collection of mapped UDTs
        // Build a copy of the value, decoding all UdtValue instances into entities on the fly.
        //     CollectionTypeT propertyValue1;
        //     RawCollectionTypeT rawCollection1 = source.get("column", GENERIC_TYPE);
        //     if (rawCollection1 == null) {
        //       propertyValue1 = null;
        //     } else {
        //       traverse rawCollection1 and convert all UdtValue into entity classes, recursing
        //       into nested collections if necessary
        //     }
        if (mutable) {
          getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
          getBuilder.addStatement("$T $L", type.asTypeName(), propertyValueName);
        } else {
          getBuilder.addStatement("$T $L = null", type.asTypeName(), propertyValueName);
          getBuilder.beginControlFlow("if (!lenient || hasProperty(source, $L))", cqlName);
        }

        String rawCollectionName = enclosingClass.getNameIndex().uniqueField("rawCollection");
        TypeName rawCollectionType = type.asRawTypeName();
        getBuilder.addStatement(
            "$T $L = source.get($L, $L)",
            rawCollectionType,
            rawCollectionName,
            cqlName,
            enclosingClass.addGenericTypeConstant(rawCollectionType));

        getBuilder
            .beginControlFlow("if ($L == null)", rawCollectionName)
            .addStatement("$L = null", propertyValueName)
            .nextControlFlow("else");
        convertUdtsIntoEntities(rawCollectionName, propertyValueName, type, getBuilder);
        getBuilder.endControlFlow();

        if (mutable) {
          getBuilder.addStatement("$L.$L($L)", resultName, setterName, propertyValueName);
        }
        getBuilder.endControlFlow();
      }
    }

    if (mutable) {
      // We've already created an instance and filled the properties as we went
      getBuilder.addStatement("return returnValue");
    } else {
      // Assume an all-arg constructor exists, and call it with all the temporary variables
      getBuilder.addCode("$[return new $T(", returnType);
      for (int i = 0; i < propertyValueNames.size(); i++) {
        getBuilder.addCode((i == 0 ? "\n$L" : ",\n$L"), propertyValueNames.get(i));
      }
      getBuilder.addCode(")$];");
    }
    return Optional.of(getBuilder.build());
  }