Add flag to add concrete setters for optionals (#94)
* Add flag to add concrete setters for optionals * Add another test with concrete optionals disabled
This commit is contained in:
@@ -144,6 +144,11 @@ public @interface RecordBuilder {
|
||||
*/
|
||||
boolean emptyDefaultForOptional() default true;
|
||||
|
||||
/**
|
||||
* Add non-optional setter methods for optional record components.
|
||||
*/
|
||||
boolean addConcreteSettersForOptional() default false;
|
||||
|
||||
/**
|
||||
* Add not-null checks for record components annotated with any annotation named either "NotNull",
|
||||
* "NoNull", or "NonNull" (see {@link #interpretNotNullsPattern()} for the actual regex matching pattern).
|
||||
|
||||
@@ -47,10 +47,6 @@ class InternalRecordBuilderProcessor {
|
||||
private final CollectionBuilderUtils collectionBuilderUtils;
|
||||
|
||||
private static final TypeName overrideType = TypeName.get(Override.class);
|
||||
private static final TypeName optionalType = TypeName.get(Optional.class);
|
||||
private static final TypeName optionalIntType = TypeName.get(OptionalInt.class);
|
||||
private static final TypeName optionalLongType = TypeName.get(OptionalLong.class);
|
||||
private static final TypeName optionalDoubleType = TypeName.get(OptionalDouble.class);
|
||||
private static final TypeName validatorTypeName = ClassName.get("io.soabase.recordbuilder.validator", "RecordBuilderValidator");
|
||||
private static final TypeVariableName rType = TypeVariableName.get("R");
|
||||
private final ProcessingEnvironment processingEnv;
|
||||
@@ -99,6 +95,9 @@ class InternalRecordBuilderProcessor {
|
||||
if (metaData.enableGetters()) {
|
||||
add1GetterMethod(component);
|
||||
}
|
||||
if (metaData.addConcreteSettersForOptional()) {
|
||||
add1ConcreteOptionalSetterMethod(component);
|
||||
}
|
||||
var collectionMetaData = collectionBuilderUtils.singleItemsMetaData(component, EXCLUDE_WILDCARD_TYPES);
|
||||
collectionMetaData.ifPresent(meta -> add1CollectionBuilders(meta, component));
|
||||
});
|
||||
@@ -708,31 +707,17 @@ class InternalRecordBuilderProcessor {
|
||||
*/
|
||||
var fieldSpecBuilder = FieldSpec.builder(component.typeName(), component.name(), Modifier.PRIVATE);
|
||||
if (metaData.emptyDefaultForOptional()) {
|
||||
TypeName thisOptionalType = null;
|
||||
if (isOptional(component)) {
|
||||
thisOptionalType = optionalType;
|
||||
} else if (component.typeName().equals(optionalIntType)) {
|
||||
thisOptionalType = optionalIntType;
|
||||
} else if (component.typeName().equals(optionalLongType)) {
|
||||
thisOptionalType = optionalLongType;
|
||||
} else if (component.typeName().equals(optionalDoubleType)) {
|
||||
thisOptionalType = optionalDoubleType;
|
||||
}
|
||||
if (thisOptionalType != null) {
|
||||
var codeBlock = CodeBlock.builder().add("$T.empty()", thisOptionalType).build();
|
||||
Optional<OptionalType> thisOptionalType = OptionalType.fromClassType(component);
|
||||
if (thisOptionalType.isPresent()) {
|
||||
var codeBlock = CodeBlock.builder()
|
||||
.add("$T.empty()", thisOptionalType.get().typeName())
|
||||
.build();
|
||||
fieldSpecBuilder.initializer(codeBlock);
|
||||
}
|
||||
}
|
||||
builder.addField(fieldSpecBuilder.build());
|
||||
}
|
||||
|
||||
private boolean isOptional(ClassType component) {
|
||||
if (component.typeName().equals(optionalType)) {
|
||||
return true;
|
||||
}
|
||||
return (component.typeName() instanceof ParameterizedTypeName parameterizedTypeName) && parameterizedTypeName.rawType.equals(optionalType);
|
||||
}
|
||||
|
||||
private void addNestedGetterMethod(TypeSpec.Builder classBuilder, RecordClassType component, String methodName) {
|
||||
/*
|
||||
For a single record component, add a getter similar to:
|
||||
@@ -944,6 +929,33 @@ class InternalRecordBuilderProcessor {
|
||||
builder.addMethod(methodSpec.build());
|
||||
}
|
||||
|
||||
private void add1ConcreteOptionalSetterMethod(RecordClassType component) {
|
||||
/*
|
||||
For a single optional record component, add a concrete setter similar to:
|
||||
|
||||
public MyRecordBuilder p(T p) {
|
||||
this.p = p;
|
||||
return this;
|
||||
}
|
||||
*/
|
||||
var optionalType = OptionalType.fromClassType(component);
|
||||
if (optionalType.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
var type = optionalType.get();
|
||||
var methodSpec = MethodSpec.methodBuilder(prefixedName(component, false))
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.returns(builderClassType.typeName());
|
||||
|
||||
var parameterSpecBuilder = ParameterSpec.builder(type.valueType(), component.name());
|
||||
methodSpec.addJavadoc("Set a new value for the {@code $L} record component in the builder\n", component.name())
|
||||
.addStatement("this.$L = $T.of($L)", component.name(), type.typeName(), component.name());
|
||||
addConstructorAnnotations(component, parameterSpecBuilder);
|
||||
methodSpec.addStatement("return this").addParameter(parameterSpecBuilder.build());
|
||||
builder.addMethod(methodSpec.build());
|
||||
}
|
||||
|
||||
private List<TypeVariableName> typeVariablesWithReturn() {
|
||||
var variables = new ArrayList<TypeVariableName>();
|
||||
variables.add(rType);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright 2019 Jordan Zimmerman
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.soabase.recordbuilder.processor;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalDouble;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.OptionalLong;
|
||||
import com.squareup.javapoet.ParameterizedTypeName;
|
||||
import com.squareup.javapoet.TypeName;
|
||||
|
||||
public record OptionalType(TypeName typeName, TypeName valueType) {
|
||||
|
||||
private static final TypeName optionalType = TypeName.get(Optional.class);
|
||||
private static final TypeName optionalIntType = TypeName.get(OptionalInt.class);
|
||||
private static final TypeName optionalLongType = TypeName.get(OptionalLong.class);
|
||||
private static final TypeName optionalDoubleType = TypeName.get(OptionalDouble.class);
|
||||
|
||||
private static boolean isOptional(ClassType component) {
|
||||
if (component.typeName().equals(optionalType)) {
|
||||
return true;
|
||||
}
|
||||
return (component.typeName() instanceof ParameterizedTypeName parameterizedTypeName)
|
||||
&& parameterizedTypeName.rawType.equals(optionalType);
|
||||
}
|
||||
|
||||
static Optional<OptionalType> fromClassType(final ClassType component) {
|
||||
if (isOptional(component)) {
|
||||
if (!(component.typeName() instanceof ParameterizedTypeName parameterizedType)) {
|
||||
return Optional.of(new OptionalType(optionalType, TypeName.get(Object.class)));
|
||||
}
|
||||
final TypeName containingType = parameterizedType.typeArguments.isEmpty()
|
||||
? TypeName.get(Object.class)
|
||||
: parameterizedType.typeArguments.get(0);
|
||||
return Optional.of(new OptionalType(optionalType, containingType));
|
||||
}
|
||||
if (component.typeName().equals(optionalIntType)) {
|
||||
return Optional.of(new OptionalType(optionalIntType, TypeName.get(int.class)));
|
||||
}
|
||||
if (component.typeName().equals(optionalLongType)) {
|
||||
return Optional.of(new OptionalType(optionalLongType, TypeName.get(long.class)));
|
||||
}
|
||||
if (component.typeName().equals(optionalDoubleType)) {
|
||||
return Optional.of(new OptionalType(optionalDoubleType, TypeName.get(double.class)));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,6 @@ import java.util.OptionalDouble;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
@RecordBuilder.Options(emptyDefaultForOptional = true)
|
||||
@RecordBuilder.Options(emptyDefaultForOptional = true, addConcreteSettersForOptional = true)
|
||||
@RecordBuilder
|
||||
public record RecordWithOptional(Optional<String> value, Optional raw, OptionalInt i, OptionalLong l, OptionalDouble d) {}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright 2019 Jordan Zimmerman
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.soabase.recordbuilder.test;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalDouble;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.OptionalLong;
|
||||
import io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
@RecordBuilder.Options(emptyDefaultForOptional = true)
|
||||
@RecordBuilder
|
||||
public record RecordWithOptional2(Optional<String> value, Optional raw, OptionalInt i, OptionalLong l, OptionalDouble d) {}
|
||||
@@ -33,4 +33,36 @@ class TestOptional {
|
||||
Assertions.assertEquals(OptionalLong.empty(), record.l());
|
||||
Assertions.assertEquals(OptionalDouble.empty(), record.d());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRawSetters() {
|
||||
var record = RecordWithOptionalBuilder.builder()
|
||||
.value("value")
|
||||
.raw("rawValue")
|
||||
.i(42)
|
||||
.l(424242L)
|
||||
.d(42.42)
|
||||
.build();
|
||||
Assertions.assertEquals(Optional.of("value"), record.value());
|
||||
Assertions.assertEquals(Optional.of("rawValue"), record.raw());
|
||||
Assertions.assertEquals(OptionalInt.of(42), record.i());
|
||||
Assertions.assertEquals(OptionalLong.of(424242L), record.l());
|
||||
Assertions.assertEquals(OptionalDouble.of(42.42), record.d());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOptionalSetters() {
|
||||
var record = RecordWithOptional2Builder.builder()
|
||||
.value(Optional.of("value"))
|
||||
.raw(Optional.of("rawValue"))
|
||||
.i(OptionalInt.of(42))
|
||||
.l(OptionalLong.of(424242L))
|
||||
.d(OptionalDouble.of(42.42))
|
||||
.build();
|
||||
Assertions.assertEquals(Optional.of("value"), record.value());
|
||||
Assertions.assertEquals(Optional.of("rawValue"), record.raw());
|
||||
Assertions.assertEquals(OptionalInt.of(42), record.i());
|
||||
Assertions.assertEquals(OptionalLong.of(424242L), record.l());
|
||||
Assertions.assertEquals(OptionalDouble.of(42.42), record.d());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user