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());
}