Skip to content

Commit

Permalink
Refactor BeanGenerator#generateBeanType to move logic for creating em…
Browse files Browse the repository at this point in the history
…pty beans to separate method (#2088)

Refactor BeanGenerator#generateBeanType to move logic for creating empty beans to separate method
  • Loading branch information
mpritham authored Aug 23, 2023
1 parent 0f161e1 commit de0aa30
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 66 deletions.
6 changes: 6 additions & 0 deletions changelog/@unreleased/pr-2088.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type: improvement
improvement:
description: Refactor BeanGenerator#generateBeanType to move logic for creating
empty beans to separate method
links:
- https://github.com/palantir/conjure-java/pull/2088
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,16 @@ public static JavaFile generateBeanType(

TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(prefixedName.getName())
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotations(safety)
.addFields(poetFields)
.addMethod(createConstructor(fields, poetFields))
.addMethods(createGetters(fields, typesMap, options, safetyEvaluator));
.addAnnotations(safety);

if (poetFields.isEmpty()) {
addEmptyBean(typeBuilder, prefixedName, safety, objectClass, options);
} else {
typeBuilder
.addFields(poetFields)
.addMethod(createConstructor(fields, poetFields))
.addMethods(createGetters(fields, typesMap, options, safetyEvaluator));

if (!poetFields.isEmpty()) {
boolean useCachedHashCode = useCachedHashCode(fields);
typeBuilder
.addMethod(MethodSpecs.createEquals(objectClass))
Expand All @@ -117,48 +121,59 @@ public static JavaFile generateBeanType(
} else {
typeBuilder.addMethod(MethodSpecs.createHashCode(poetFields));
}
}

typeBuilder.addMethod(MethodSpecs.createToString(
prefixedName.getName(),
fields.stream().map(EnrichedField::fieldName).collect(Collectors.toList()))
.toBuilder()
.addAnnotations(safety)
.build());

if (poetFields.size() <= MAX_NUM_PARAMS_FOR_FACTORY) {
typeBuilder.addMethod(createStaticFactoryMethod(
fields,
objectClass,
safetyEvaluator,
options.useStagedBuilders() && !options.useStrictStagedBuilders()));
}
typeBuilder.addMethod(MethodSpecs.createToString(
prefixedName.getName(),
fields.stream().map(EnrichedField::fieldName).collect(Collectors.toList()))
.toBuilder()
.addAnnotations(safety)
.build());

if (poetFields.size() <= MAX_NUM_PARAMS_FOR_FACTORY) {
typeBuilder.addMethod(createStaticFactoryMethod(
fields,
objectClass,
safetyEvaluator,
options.useStagedBuilders() && !options.useStrictStagedBuilders()));
}

if (!nonPrimitiveEnrichedFields.isEmpty()) {
typeBuilder
.addMethod(createValidateFields(nonPrimitiveEnrichedFields))
.addMethod(createAddFieldIfMissing(nonPrimitiveEnrichedFields.size()));
}
if (!nonPrimitiveEnrichedFields.isEmpty()) {
typeBuilder
.addMethod(createValidateFields(nonPrimitiveEnrichedFields))
.addMethod(createAddFieldIfMissing(nonPrimitiveEnrichedFields.size()));
}

if (poetFields.isEmpty()) {
// Need to add JsonSerialize annotation which indicates that the empty bean serializer should be used to
// serialize this class. Without this annotation no serializer will be set for this class, thus preventing
// serialization.
typeBuilder.addAnnotation(JsonSerialize.class).addField(createSingletonField(objectClass));
if (!options.strictObjects()) {
typeBuilder.addAnnotation(AnnotationSpec.builder(JsonIgnoreProperties.class)
.addMember("ignoreUnknown", "$L", true)
.build());
if (options.useStrictStagedBuilders()) {
BeanBuilderGenerator.addStrictStagedBuilder(
typeBuilder,
typeMapper,
safetyEvaluator,
objectClass,
builderClass,
typeDef,
typesMap,
options);
} else if (options.useStagedBuilders()) {
BeanBuilderGenerator.addStagedBuilder(
typeBuilder,
typeMapper,
safetyEvaluator,
objectClass,
builderClass,
typeDef,
typesMap,
options);
} else {
BeanBuilderGenerator.addBuilder(
typeBuilder,
typeMapper,
safetyEvaluator,
objectClass,
builderClass,
typeDef,
typesMap,
options);
}
} else if (options.useStrictStagedBuilders()) {
BeanBuilderGenerator.addStrictStagedBuilder(
typeBuilder, typeMapper, safetyEvaluator, objectClass, builderClass, typeDef, typesMap, options);
} else if (options.useStagedBuilders()) {
BeanBuilderGenerator.addStagedBuilder(
typeBuilder, typeMapper, safetyEvaluator, objectClass, builderClass, typeDef, typesMap, options);
} else {
BeanBuilderGenerator.addBuilder(
typeBuilder, typeMapper, safetyEvaluator, objectClass, builderClass, typeDef, typesMap, options);
}
typeBuilder.addAnnotation(ConjureAnnotations.getConjureGeneratedAnnotation(BeanGenerator.class));

Expand All @@ -170,6 +185,31 @@ public static JavaFile generateBeanType(
.build();
}

private static void addEmptyBean(
TypeSpec.Builder typeBuilder,
com.palantir.conjure.spec.TypeName prefixedName,
ImmutableList<AnnotationSpec> safety,
ClassName objectClass,
Options options) {
typeBuilder.addMethod(createConstructor(ImmutableList.of(), ImmutableList.of()));

typeBuilder.addMethod(MethodSpecs.createToString(prefixedName.getName(), Collections.emptyList()).toBuilder()
.addAnnotations(safety)
.build());

typeBuilder.addMethod(createStaticFactoryMethodForEmptyBean(objectClass));

// Need to add JsonSerialize annotation which indicates that the empty bean serializer should be used to
// serialize this class. Without this annotation no serializer will be set for this class, thus preventing
// serialization.
typeBuilder.addAnnotation(JsonSerialize.class).addField(createSingletonField(objectClass));
if (!options.strictObjects()) {
typeBuilder.addAnnotation(AnnotationSpec.builder(JsonIgnoreProperties.class)
.addMember("ignoreUnknown", "$L", true)
.build());
}
}

private static boolean useCachedHashCode(Collection<EnrichedField> fields) {
if (fields.size() == 1) {
EnrichedField field = Iterables.getOnlyElement(fields);
Expand Down Expand Up @@ -331,37 +371,45 @@ private static MethodSpec createStaticFactoryMethod(
ClassName objectClass,
SafetyEvaluator safetyEvaluator,
boolean useNonStrictStagedBuilders) {
if (fields.isEmpty()) {
createStaticFactoryMethodForEmptyBean(objectClass);
}

MethodSpec.Builder builder = MethodSpec.methodBuilder("of")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(objectClass);

if (fields.isEmpty()) {
builder.addAnnotation(ConjureAnnotations.delegatingJsonCreator())
.addCode("return $L;", SINGLETON_INSTANCE_NAME);
} else {
builder.addCode("return builder()");
fields.forEach(field -> builder.addParameter(ParameterSpec.builder(
getTypeNameWithoutOptional(field.poetSpec()), field.poetSpec().name)
.addAnnotations(ConjureAnnotations.safety(safetyEvaluator.getUsageTimeSafety(field.conjureDef())))
.build()));

Stream<EnrichedField> methodArgs = useNonStrictStagedBuilders
? fields.stream()
.sorted(Comparator.comparing(BeanBuilderGenerator::stagedBuilderFieldShouldBeInFinalStage))
: fields.stream();
methodArgs.map(EnrichedField::poetSpec).forEach(spec -> {
if (isOptional(spec)) {
builder.addCode("\n .$L(Optional.of($L))", spec.name, spec.name);
} else {
builder.addCode("\n .$L($L)", spec.name, spec.name);
}
});
builder.addCode("\n .build();\n");
}
builder.addCode("return builder()");
fields.forEach(field -> builder.addParameter(ParameterSpec.builder(
getTypeNameWithoutOptional(field.poetSpec()), field.poetSpec().name)
.addAnnotations(ConjureAnnotations.safety(safetyEvaluator.getUsageTimeSafety(field.conjureDef())))
.build()));

Stream<EnrichedField> methodArgs = useNonStrictStagedBuilders
? fields.stream()
.sorted(Comparator.comparing(BeanBuilderGenerator::stagedBuilderFieldShouldBeInFinalStage))
: fields.stream();
methodArgs.map(EnrichedField::poetSpec).forEach(spec -> {
if (isOptional(spec)) {
builder.addCode("\n .$L(Optional.of($L))", spec.name, spec.name);
} else {
builder.addCode("\n .$L($L)", spec.name, spec.name);
}
});
builder.addCode("\n .build();\n");

return builder.build();
}

private static MethodSpec createStaticFactoryMethodForEmptyBean(ClassName objectClass) {
return MethodSpec.methodBuilder("of")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(objectClass)
.addAnnotation(ConjureAnnotations.delegatingJsonCreator())
.addCode("return $L;", SINGLETON_INSTANCE_NAME)
.build();
}

private static MethodSpec createAddFieldIfMissing(int fieldCount) {
ParameterizedTypeName listOfStringType = ParameterizedTypeName.get(List.class, String.class);
ParameterSpec listParam =
Expand Down

0 comments on commit de0aa30

Please sign in to comment.