Compare commits
13 Commits
record-bui
...
record-bui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
821718ac3f | ||
|
|
8ac34529e7 | ||
|
|
c701a09039 | ||
|
|
328bb7d660 | ||
|
|
41eddd46a3 | ||
|
|
b0f4bfbe88 | ||
|
|
09168b6104 | ||
|
|
d4e24993c9 | ||
|
|
8304cb1f83 | ||
|
|
7215ad3241 | ||
|
|
f7d65c7619 | ||
|
|
29ebe52914 | ||
|
|
d9c143aa8b |
33
README.md
33
README.md
@@ -184,29 +184,26 @@ public class NameAndAgeBuilder {
|
||||
&& Objects.equals(name, b.name)
|
||||
&& (age == b.age));
|
||||
}
|
||||
|
||||
/**
|
||||
* Downcast to {@code NameAndAge}
|
||||
*/
|
||||
private static NameAndAge _downcast(Object obj) {
|
||||
try {
|
||||
return (NameAndAge)obj;
|
||||
}
|
||||
catch (ClassCastException dummy) {
|
||||
throw new RuntimeException("NameAndAgeBuilder.With can only be implemented for NameAndAge");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add withers to {@code NameAndAge}
|
||||
*/
|
||||
public interface With {
|
||||
/**
|
||||
* Return the current value for the {@code name} record component in the builder
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Return the current value for the {@code age} record component in the builder
|
||||
*/
|
||||
int age();
|
||||
|
||||
/**
|
||||
* Return a new record builder using the current values
|
||||
*/
|
||||
default NameAndAgeBuilder with() {
|
||||
NameAndAge r = _downcast(this);
|
||||
return NameAndAgeBuilder.builder(r);
|
||||
return new NameAndAgeBuilder(name(), age());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,16 +219,14 @@ public class NameAndAgeBuilder {
|
||||
* Return a new instance of {@code NameAndAge} with a new value for {@code name}
|
||||
*/
|
||||
default NameAndAge withName(String name) {
|
||||
NameAndAge r = _downcast(this);
|
||||
return new NameAndAge(name, r.age());
|
||||
return new NameAndAge(name, age());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance of {@code NameAndAge} with a new value for {@code age}
|
||||
*/
|
||||
default NameAndAge withAge(int age) {
|
||||
NameAndAge r = _downcast(this);
|
||||
return new NameAndAge(r.name(), age);
|
||||
return new NameAndAge(name(), age);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
pom.xml
4
pom.xml
@@ -5,7 +5,7 @@
|
||||
<groupId>io.soabase.record-builder</groupId>
|
||||
<artifactId>record-builder</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>21</version>
|
||||
<version>25-java15</version>
|
||||
|
||||
<modules>
|
||||
<module>record-builder-core</module>
|
||||
@@ -80,7 +80,7 @@
|
||||
<url>https://github.com/randgalt/record-builder</url>
|
||||
<connection>scm:git:https://github.com/randgalt/record-builder.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:randgalt/record-builder.git</developerConnection>
|
||||
<tag>record-builder-21</tag>
|
||||
<tag>record-builder-25-java15</tag>
|
||||
</scm>
|
||||
|
||||
<issueManagement>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>io.soabase.record-builder</groupId>
|
||||
<artifactId>record-builder</artifactId>
|
||||
<version>21</version>
|
||||
<version>25-java15</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -69,11 +69,6 @@ public @interface RecordBuilder {
|
||||
*/
|
||||
String buildMethodName() default "build";
|
||||
|
||||
/**
|
||||
* The name to use for the downcast method
|
||||
*/
|
||||
String downCastMethodName() default "_downcast";
|
||||
|
||||
/**
|
||||
* The name to use for the method that returns the record components as a stream
|
||||
*/
|
||||
@@ -141,6 +136,14 @@ public @interface RecordBuilder {
|
||||
* named {@code RecordBuilderValidator} which has a public static method: {@code public static <T> T validate(T o)}.</p>
|
||||
*/
|
||||
boolean useValidationApi() default false;
|
||||
|
||||
/**
|
||||
* Adds special handling for record components of type {@link java.util.List}, {@link java.util.Set},
|
||||
* {@link java.util.Map} and {@link java.util.Collection}. When the record is built, any components
|
||||
* of these types are passed through an added shim method that uses the corresponding immutable collection
|
||||
* (e.g. {@code List.copyOf(o)}) or an empty immutable collection if the component is {@code null}.
|
||||
*/
|
||||
boolean useImmutableCollections() default false;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>io.soabase.record-builder</groupId>
|
||||
<artifactId>record-builder</artifactId>
|
||||
<version>21</version>
|
||||
<version>25-java15</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* 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 com.squareup.javapoet.*;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
import static io.soabase.recordbuilder.processor.RecordBuilderProcessor.generatedRecordBuilderAnnotation;
|
||||
|
||||
class CollectionBuilderUtils {
|
||||
private final boolean enabled;
|
||||
private final String listShimName;
|
||||
private final String mapShimName;
|
||||
private final String setShimName;
|
||||
private final String collectionShimName;
|
||||
|
||||
private boolean needsListShim;
|
||||
private boolean needsMapShim;
|
||||
private boolean needsSetShim;
|
||||
private boolean needsCollectionShim;
|
||||
|
||||
private static final TypeName listType = TypeName.get(List.class);
|
||||
private static final TypeName mapType = TypeName.get(Map.class);
|
||||
private static final TypeName setType = TypeName.get(Set.class);
|
||||
private static final TypeName collectionType = TypeName.get(Collection.class);
|
||||
|
||||
private static final TypeVariableName tType = TypeVariableName.get("T");
|
||||
private static final TypeVariableName kType = TypeVariableName.get("K");
|
||||
private static final TypeVariableName vType = TypeVariableName.get("V");
|
||||
private static final ParameterizedTypeName parameterizedListType = ParameterizedTypeName.get(ClassName.get(List.class), tType);
|
||||
private static final ParameterizedTypeName parameterizedMapType = ParameterizedTypeName.get(ClassName.get(Map.class), kType, vType);
|
||||
private static final ParameterizedTypeName parameterizedSetType = ParameterizedTypeName.get(ClassName.get(Set.class), tType);
|
||||
private static final ParameterizedTypeName parameterizedCollectionType = ParameterizedTypeName.get(ClassName.get(Collection.class), tType);
|
||||
|
||||
CollectionBuilderUtils(List<RecordClassType> recordComponents, boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
|
||||
listShimName = adjustShimName(recordComponents, "__list", 0);
|
||||
mapShimName = adjustShimName(recordComponents, "__map", 0);
|
||||
setShimName = adjustShimName(recordComponents, "__set", 0);
|
||||
collectionShimName = adjustShimName(recordComponents, "__collection", 0);
|
||||
}
|
||||
|
||||
void add(CodeBlock.Builder builder, RecordClassType component) {
|
||||
if (enabled) {
|
||||
if (component.rawTypeName().equals(listType)) {
|
||||
needsListShim = true;
|
||||
builder.add("$L($L)", listShimName, component.name());
|
||||
} else if (component.rawTypeName().equals(mapType)) {
|
||||
needsMapShim = true;
|
||||
builder.add("$L($L)", mapShimName, component.name());
|
||||
} else if (component.rawTypeName().equals(setType)) {
|
||||
needsSetShim = true;
|
||||
builder.add("$L($L)", setShimName, component.name());
|
||||
} else if (component.rawTypeName().equals(collectionType)) {
|
||||
needsCollectionShim = true;
|
||||
builder.add("$L($L)", collectionShimName, component.name());
|
||||
} else {
|
||||
builder.add("$L", component.name());
|
||||
}
|
||||
} else {
|
||||
builder.add("$L", component.name());
|
||||
}
|
||||
}
|
||||
|
||||
void addShims(TypeSpec.Builder builder) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (needsListShim) {
|
||||
builder.addMethod(buildMethod(listShimName, listType, parameterizedListType, tType));
|
||||
}
|
||||
if (needsSetShim) {
|
||||
builder.addMethod(buildMethod(setShimName, setType, parameterizedSetType, tType));
|
||||
}
|
||||
if (needsMapShim) {
|
||||
builder.addMethod(buildMethod(mapShimName, mapType, parameterizedMapType, kType, vType));
|
||||
}
|
||||
if (needsCollectionShim) {
|
||||
builder.addMethod(buildCollectionsMethod());
|
||||
}
|
||||
}
|
||||
|
||||
private String adjustShimName(List<RecordClassType> recordComponents, String baseName, int index)
|
||||
{
|
||||
var name = (index == 0) ? baseName : (baseName + index);
|
||||
if (recordComponents.stream().anyMatch(component -> component.name().equals(name))) {
|
||||
return adjustShimName(recordComponents, baseName, index + 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private MethodSpec buildMethod(String name, TypeName mainType, ParameterizedTypeName parameterizedType, TypeVariableName... typeVariables) {
|
||||
var code = CodeBlock.of("return (o != null) ? $T.copyOf(o) : $T.of()", mainType, mainType);
|
||||
return MethodSpec.methodBuilder(name)
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
|
||||
.addTypeVariables(Arrays.asList(typeVariables))
|
||||
.returns(parameterizedType)
|
||||
.addParameter(parameterizedType, "o")
|
||||
.addStatement(code)
|
||||
.build();
|
||||
}
|
||||
|
||||
private MethodSpec buildCollectionsMethod() {
|
||||
var code = CodeBlock.builder()
|
||||
.add("if (o instanceof Set) {\n")
|
||||
.indent()
|
||||
.addStatement("return (o != null) ? $T.copyOf(o) : $T.of()", setType, setType)
|
||||
.unindent()
|
||||
.addStatement("}")
|
||||
.addStatement("return (o != null) ? $T.copyOf(o) : $T.of()", listType, listType)
|
||||
.build();
|
||||
return MethodSpec.methodBuilder(collectionShimName)
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
|
||||
.addTypeVariable(tType)
|
||||
.returns(parameterizedCollectionType)
|
||||
.addParameter(parameterizedCollectionType, "o")
|
||||
.addCode(code)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -108,8 +108,10 @@ public class ElementUtils {
|
||||
return new ClassType(ParameterizedTypeName.get(builderClassName, typeNames), builderClassName.simpleName());
|
||||
}
|
||||
|
||||
public static RecordClassType getRecordClassType(RecordComponentElement recordComponent, List<? extends AnnotationMirror> accessorAnnotations, List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
|
||||
return new RecordClassType(TypeName.get(recordComponent.asType()), recordComponent.getSimpleName().toString(), accessorAnnotations, canonicalConstructorAnnotations);
|
||||
public static RecordClassType getRecordClassType(ProcessingEnvironment processingEnv, RecordComponentElement recordComponent, List<? extends AnnotationMirror> accessorAnnotations, List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
|
||||
var typeName = TypeName.get(recordComponent.asType());
|
||||
var rawTypeName = TypeName.get(processingEnv.getTypeUtils().erasure(recordComponent.asType()));
|
||||
return new RecordClassType(typeName, rawTypeName, recordComponent.getSimpleName().toString(), accessorAnnotations, canonicalConstructorAnnotations);
|
||||
}
|
||||
|
||||
public static String getWithMethodName(ClassType component, String prefix) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.soabase.recordbuilder.processor;
|
||||
import com.squareup.javapoet.*;
|
||||
import io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
@@ -41,27 +42,33 @@ class InternalRecordBuilderProcessor {
|
||||
private final TypeSpec.Builder builder;
|
||||
private final String uniqueVarName;
|
||||
private final Pattern notNullPattern;
|
||||
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 final ProcessingEnvironment processingEnv;
|
||||
|
||||
InternalRecordBuilderProcessor(TypeElement record, RecordBuilder.Options metaData, Optional<String> packageNameOpt) {
|
||||
InternalRecordBuilderProcessor(ProcessingEnvironment processingEnv, TypeElement record, RecordBuilder.Options metaData, Optional<String> packageNameOpt) {
|
||||
this.processingEnv = processingEnv;
|
||||
var recordActualPackage = ElementUtils.getPackageName(record);
|
||||
this.metaData = getMetaData(record, metaData);
|
||||
recordClassType = ElementUtils.getClassType(record, record.getTypeParameters());
|
||||
packageName = packageNameOpt.orElseGet(() -> ElementUtils.getPackageName(record));
|
||||
packageName = packageNameOpt.orElse(recordActualPackage);
|
||||
builderClassType = ElementUtils.getClassType(packageName, getBuilderName(record, metaData, recordClassType, metaData.suffix()), record.getTypeParameters());
|
||||
typeVariables = record.getTypeParameters().stream().map(TypeVariableName::get).collect(Collectors.toList());
|
||||
recordComponents = buildRecordComponents(record);
|
||||
uniqueVarName = getUniqueVarName();
|
||||
notNullPattern = Pattern.compile(metaData.interpretNotNullsPattern());
|
||||
collectionBuilderUtils = new CollectionBuilderUtils(recordComponents, this.metaData.useImmutableCollections());
|
||||
|
||||
builder = TypeSpec.classBuilder(builderClassType.name())
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.addTypeVariables(typeVariables);
|
||||
addVisibility(recordActualPackage.equals(packageName), record.getModifiers());
|
||||
addWithNestedClass();
|
||||
addDefaultConstructor();
|
||||
addStaticBuilder();
|
||||
@@ -80,7 +87,7 @@ class InternalRecordBuilderProcessor {
|
||||
add1SetterMethod(component);
|
||||
add1GetterMethod(component);
|
||||
});
|
||||
addStaticDowncastMethod();
|
||||
collectionBuilderUtils.addShims(builder);
|
||||
builderType = builder.build();
|
||||
}
|
||||
|
||||
@@ -96,6 +103,18 @@ class InternalRecordBuilderProcessor {
|
||||
return builderType;
|
||||
}
|
||||
|
||||
private void addVisibility(boolean builderIsInRecordPackage, Set<Modifier> modifiers) {
|
||||
if (builderIsInRecordPackage) {
|
||||
if (modifiers.contains(Modifier.PUBLIC) || modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
|
||||
builder.addModifiers(Modifier.PUBLIC); // builders are top level classes - can only be public or package-private
|
||||
}
|
||||
// is package-private
|
||||
}
|
||||
else {
|
||||
builder.addModifiers(Modifier.PUBLIC);
|
||||
}
|
||||
}
|
||||
|
||||
private List<RecordClassType> buildRecordComponents(TypeElement record) {
|
||||
var accessorAnnotations = record.getRecordComponents().stream().map(e -> e.getAccessor().getAnnotationMirrors()).collect(Collectors.toList());
|
||||
var canonicalConstructorAnnotations = ElementUtils.findCanonicalConstructor(record).map(constructor -> ((ExecutableElement) constructor).getParameters().stream().map(Element::getAnnotationMirrors).collect(Collectors.toList())).orElse(List.of());
|
||||
@@ -104,7 +123,7 @@ class InternalRecordBuilderProcessor {
|
||||
.mapToObj(index -> {
|
||||
var thisAccessorAnnotations = (accessorAnnotations.size() > index) ? accessorAnnotations.get(index) : List.<AnnotationMirror>of();
|
||||
var thisCanonicalConstructorAnnotations = (canonicalConstructorAnnotations.size() > index) ? canonicalConstructorAnnotations.get(index) : List.<AnnotationMirror>of();
|
||||
return ElementUtils.getRecordClassType(recordComponents.get(index), thisAccessorAnnotations, thisCanonicalConstructorAnnotations);
|
||||
return ElementUtils.getRecordClassType(processingEnv, recordComponents.get(index), thisAccessorAnnotations, thisCanonicalConstructorAnnotations);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@@ -129,6 +148,7 @@ class InternalRecordBuilderProcessor {
|
||||
.addJavadoc("Add withers to {@code $L}\n", recordClassType.name())
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addTypeVariables(typeVariables);
|
||||
recordComponents.forEach(component -> addWithGetterMethod(classBuilder, component));
|
||||
addWithBuilderMethod(classBuilder);
|
||||
addWithSuppliedBuilderMethod(classBuilder);
|
||||
IntStream.range(0, recordComponents.size()).forEach(index -> add1WithMethod(classBuilder, recordComponents.get(index), index));
|
||||
@@ -167,13 +187,13 @@ class InternalRecordBuilderProcessor {
|
||||
Adds a method that returns a pre-filled copy builder similar to:
|
||||
|
||||
default MyRecordBuilder with() {
|
||||
MyRecord r = _downcast(this);
|
||||
return MyRecordBuilder.builder(r);
|
||||
}
|
||||
*/
|
||||
var codeBlockBuilder = CodeBlock.builder()
|
||||
.add("$T $L = $L(this);\n", recordClassType.typeName(), uniqueVarName, metaData.downCastMethodName())
|
||||
.add("return $L.$L($L);", builderClassType.name(), metaData.copyMethodName(), uniqueVarName);
|
||||
.add("return new $L(", builderClassType.name());
|
||||
addComponentCallsAsArguments(-1, codeBlockBuilder);
|
||||
codeBlockBuilder.add(");");
|
||||
var methodSpec = MethodSpec.methodBuilder(metaData.withClassMethodPrefix())
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.addJavadoc("Return a new record builder using the current values")
|
||||
@@ -201,31 +221,26 @@ class InternalRecordBuilderProcessor {
|
||||
Adds a with method for the component similar to:
|
||||
|
||||
default MyRecord withName(String name) {
|
||||
MyRecord r = _downcast(this);
|
||||
return new MyRecord(name, r.age());
|
||||
}
|
||||
*/
|
||||
var codeBlockBuilder = CodeBlock.builder();
|
||||
if (recordComponents.size() > 1) {
|
||||
codeBlockBuilder.add("$T $L = $L(this);\n", recordClassType.typeName(), uniqueVarName, metaData.downCastMethodName());
|
||||
addNullCheckCodeBlock(codeBlockBuilder, index);
|
||||
codeBlockBuilder.add("$[return ");
|
||||
if (metaData.useValidationApi()) {
|
||||
codeBlockBuilder.add("$T.validate(", validatorTypeName);
|
||||
}
|
||||
codeBlockBuilder.add("return new $T(", recordClassType.typeName());
|
||||
IntStream.range(0, recordComponents.size()).forEach(parameterIndex -> {
|
||||
if (parameterIndex > 0) {
|
||||
codeBlockBuilder.add(", ");
|
||||
}
|
||||
ClassType parameterComponent = recordComponents.get(parameterIndex);
|
||||
if (parameterIndex == index) {
|
||||
codeBlockBuilder.add(parameterComponent.name());
|
||||
} else {
|
||||
codeBlockBuilder.add("$L.$L()", uniqueVarName, parameterComponent.name());
|
||||
}
|
||||
});
|
||||
codeBlockBuilder.add(");");
|
||||
codeBlockBuilder.add("new $T(", recordClassType.typeName());
|
||||
addComponentCallsAsArguments(index, codeBlockBuilder);
|
||||
codeBlockBuilder.add(")");
|
||||
if (metaData.useValidationApi()) {
|
||||
codeBlockBuilder.add(")");
|
||||
}
|
||||
codeBlockBuilder.add(";$]");
|
||||
|
||||
var methodName = getWithMethodName(component, metaData.withClassMethodPrefix());
|
||||
var parameterSpecBuilder = ParameterSpec.builder(component.typeName(), component.name());
|
||||
component.getCanonicalConstructorAnnotations().forEach(annotationMirror -> parameterSpecBuilder.addAnnotation(AnnotationSpec.get(annotationMirror)));
|
||||
addConstructorAnnotations(component, parameterSpecBuilder);
|
||||
var methodSpec = MethodSpec.methodBuilder(methodName)
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.addJavadoc("Return a new instance of {@code $L} with a new value for {@code $L}\n", recordClassType.name(), component.name())
|
||||
@@ -237,6 +252,20 @@ class InternalRecordBuilderProcessor {
|
||||
classBuilder.addMethod(methodSpec);
|
||||
}
|
||||
|
||||
private void addComponentCallsAsArguments(int index, CodeBlock.Builder codeBlockBuilder) {
|
||||
IntStream.range(0, recordComponents.size()).forEach(parameterIndex -> {
|
||||
if (parameterIndex > 0) {
|
||||
codeBlockBuilder.add(", ");
|
||||
}
|
||||
RecordClassType parameterComponent = recordComponents.get(parameterIndex);
|
||||
if (parameterIndex == index) {
|
||||
collectionBuilderUtils.add(codeBlockBuilder, parameterComponent);
|
||||
} else {
|
||||
codeBlockBuilder.add("$L()", parameterComponent.name());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addDefaultConstructor() {
|
||||
/*
|
||||
Adds a default constructor similar to:
|
||||
@@ -269,7 +298,7 @@ class InternalRecordBuilderProcessor {
|
||||
.addCode(codeBlock);
|
||||
recordComponents.forEach(component -> {
|
||||
var parameterSpecBuilder = ParameterSpec.builder(component.typeName(), component.name());
|
||||
component.getCanonicalConstructorAnnotations().forEach(annotationMirror -> parameterSpecBuilder.addAnnotation(AnnotationSpec.get(annotationMirror)));
|
||||
addConstructorAnnotations(component, parameterSpecBuilder);
|
||||
builder.addParameter(parameterSpecBuilder.build());
|
||||
});
|
||||
this.builder.addMethod(builder.build());
|
||||
@@ -277,10 +306,18 @@ class InternalRecordBuilderProcessor {
|
||||
|
||||
private void addNullCheckCodeBlock(CodeBlock.Builder builder) {
|
||||
if (metaData.interpretNotNulls()) {
|
||||
recordComponents.stream()
|
||||
.filter(component -> !component.typeName().isPrimitive())
|
||||
.filter(this::isNullAnnotated)
|
||||
.forEach(component -> builder.addStatement("$T.requireNonNull($L, $S)", Objects.class, component.name(), component.name() + " is required"));
|
||||
for (int i = 0; i < recordComponents.size(); ++i) {
|
||||
addNullCheckCodeBlock(builder, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addNullCheckCodeBlock(CodeBlock.Builder builder, int index) {
|
||||
if (metaData.interpretNotNulls()) {
|
||||
var component = recordComponents.get(index);
|
||||
if (!component.typeName().isPrimitive() && isNullAnnotated(component)) {
|
||||
builder.addStatement("$T.requireNonNull($L, $S)", Objects.class, component.name(), component.name() + " is required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,7 +474,7 @@ class InternalRecordBuilderProcessor {
|
||||
if (index > 0) {
|
||||
codeBuilder.add(", ");
|
||||
}
|
||||
codeBuilder.add("$L", recordComponents.get(index).name());
|
||||
collectionBuilderUtils.add(codeBuilder, recordComponents.get(index));
|
||||
});
|
||||
codeBuilder.add(")");
|
||||
if (metaData.useValidationApi()) {
|
||||
@@ -529,37 +566,6 @@ class InternalRecordBuilderProcessor {
|
||||
builder.addMethod(methodSpec);
|
||||
}
|
||||
|
||||
private void addStaticDowncastMethod() {
|
||||
/*
|
||||
Adds a method that downcasts to the record type
|
||||
|
||||
private static MyRecord _downcast(Object this) {
|
||||
return (MyRecord)this;
|
||||
}
|
||||
*/
|
||||
var codeBlockBuilder = CodeBlock.builder()
|
||||
.add("try {\n")
|
||||
.indent()
|
||||
.add("return ($T)obj;\n", recordClassType.typeName())
|
||||
.unindent()
|
||||
.add("}\n")
|
||||
.add("catch (ClassCastException dummy) {\n")
|
||||
.indent()
|
||||
.add("throw new RuntimeException($S);\n", builderClassType.name() + "." + metaData.withClassName() + " can only be implemented by " + recordClassType.name())
|
||||
.unindent()
|
||||
.add("}");
|
||||
var methodSpec = MethodSpec.methodBuilder(metaData.downCastMethodName())
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.addJavadoc("Downcast to {@code $L}\n", recordClassType.name())
|
||||
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
|
||||
.addParameter(Object.class, "obj")
|
||||
.addTypeVariables(typeVariables)
|
||||
.returns(recordClassType.typeName())
|
||||
.addCode(codeBlockBuilder.build())
|
||||
.build();
|
||||
builder.addMethod(methodSpec);
|
||||
}
|
||||
|
||||
private void add1Field(ClassType component) {
|
||||
/*
|
||||
For a single record component, add a field similar to:
|
||||
@@ -590,7 +596,46 @@ class InternalRecordBuilderProcessor {
|
||||
if (component.typeName().equals(optionalType)) {
|
||||
return true;
|
||||
}
|
||||
return (component.typeName() instanceof ParameterizedTypeName) && ((ParameterizedTypeName) component.typeName()).rawType.equals(optionalType);
|
||||
return (component.typeName() instanceof ParameterizedTypeName parameterizedTypeName) && parameterizedTypeName.rawType.equals(optionalType);
|
||||
}
|
||||
|
||||
private void addWithGetterMethod(TypeSpec.Builder classBuilder, RecordClassType component) {
|
||||
/*
|
||||
For a single record component, add a getter similar to:
|
||||
|
||||
T p();
|
||||
*/
|
||||
var methodSpecBuilder = MethodSpec.methodBuilder(component.name())
|
||||
.addJavadoc("Return the current value for the {@code $L} record component in the builder\n", component.name())
|
||||
.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.returns(component.typeName());
|
||||
addAccessorAnnotations(component, methodSpecBuilder);
|
||||
classBuilder.addMethod(methodSpecBuilder.build());
|
||||
}
|
||||
|
||||
private boolean filterOutOverride(AnnotationSpec annotationSpec) {
|
||||
return !annotationSpec.type.equals(overrideType);
|
||||
}
|
||||
|
||||
private void addConstructorAnnotations(RecordClassType component, ParameterSpec.Builder parameterSpecBuilder) {
|
||||
if (metaData.inheritComponentAnnotations()) {
|
||||
component.getCanonicalConstructorAnnotations()
|
||||
.stream()
|
||||
.map(AnnotationSpec::get)
|
||||
.filter(this::filterOutOverride)
|
||||
.forEach(parameterSpecBuilder::addAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAccessorAnnotations(RecordClassType component, MethodSpec.Builder methodSpecBuilder) {
|
||||
if (metaData.inheritComponentAnnotations()) {
|
||||
component.getAccessorAnnotations()
|
||||
.stream()
|
||||
.map(AnnotationSpec::get)
|
||||
.filter(this::filterOutOverride)
|
||||
.forEach(methodSpecBuilder::addAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
private void add1GetterMethod(RecordClassType component) {
|
||||
@@ -607,7 +652,7 @@ class InternalRecordBuilderProcessor {
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.returns(component.typeName())
|
||||
.addStatement("return $L", component.name());
|
||||
component.getAccessorAnnotations().forEach(annotationMirror -> methodSpecBuilder.addAnnotation(AnnotationSpec.get(annotationMirror)));
|
||||
addAccessorAnnotations(component, methodSpecBuilder);
|
||||
builder.addMethod(methodSpecBuilder.build());
|
||||
}
|
||||
|
||||
@@ -621,7 +666,7 @@ class InternalRecordBuilderProcessor {
|
||||
}
|
||||
*/
|
||||
var parameterSpecBuilder = ParameterSpec.builder(component.typeName(), component.name());
|
||||
component.getCanonicalConstructorAnnotations().forEach(annotationMirror -> parameterSpecBuilder.addAnnotation(AnnotationSpec.get(annotationMirror)));
|
||||
addConstructorAnnotations(component, parameterSpecBuilder);
|
||||
|
||||
var methodSpec = MethodSpec.methodBuilder(component.name())
|
||||
.addJavadoc("Set a new value for the {@code $L} record component in the builder\n", component.name())
|
||||
|
||||
@@ -181,7 +181,7 @@ public class RecordBuilderProcessor
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "RecordBuilder only valid for records.", record);
|
||||
return;
|
||||
}
|
||||
var internalProcessor = new InternalRecordBuilderProcessor(record, metaData, packageName);
|
||||
var internalProcessor = new InternalRecordBuilderProcessor(processingEnv, record, metaData, packageName);
|
||||
writeRecordBuilderJavaFile(record, internalProcessor.packageName(), internalProcessor.builderClassType(), internalProcessor.builderType(), metaData);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,15 +21,21 @@ import javax.lang.model.element.AnnotationMirror;
|
||||
import java.util.List;
|
||||
|
||||
public class RecordClassType extends ClassType {
|
||||
private final TypeName rawTypeName;
|
||||
private final List<? extends AnnotationMirror> accessorAnnotations;
|
||||
private final List<? extends AnnotationMirror> canonicalConstructorAnnotations;
|
||||
|
||||
public RecordClassType(TypeName typeName, String name, List<? extends AnnotationMirror> accessorAnnotations, List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
|
||||
public RecordClassType(TypeName typeName, TypeName rawTypeName, String name, List<? extends AnnotationMirror> accessorAnnotations, List<? extends AnnotationMirror> canonicalConstructorAnnotations) {
|
||||
super(typeName, name);
|
||||
this.rawTypeName = rawTypeName;
|
||||
this.accessorAnnotations = accessorAnnotations;
|
||||
this.canonicalConstructorAnnotations = canonicalConstructorAnnotations;
|
||||
}
|
||||
|
||||
public TypeName rawTypeName() {
|
||||
return rawTypeName;
|
||||
}
|
||||
|
||||
public List<? extends AnnotationMirror> getAccessorAnnotations() {
|
||||
return accessorAnnotations;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>io.soabase.record-builder</groupId>
|
||||
<artifactId>record-builder</artifactId>
|
||||
<version>21</version>
|
||||
<version>25-java15</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RecordBuilder
|
||||
@RecordBuilder.Options(useImmutableCollections = true)
|
||||
public record CollectionRecord<T, X extends Point>(List<T> l, Set<T> s, Map<T, X> m, Collection<X> c) implements CollectionRecordBuilder.With<T, X> {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RecordBuilder
|
||||
@RecordBuilder.Options(useImmutableCollections = true)
|
||||
public record CollectionRecordConflicts(List<String> __list, Set<String> __set, Map<String, String> __map, Collection<String> __collection) implements CollectionRecordConflictsBuilder.With {
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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 io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
import javax.lang.model.type.ErrorType;
|
||||
import java.util.List;
|
||||
|
||||
@RecordBuilder
|
||||
public record ExceptionDetails(
|
||||
String internalMessage, String endUserMessage, String httpStatus,
|
||||
ErrorType errorType, List<String> jsonProblems, Throwable cause
|
||||
) {
|
||||
@Override
|
||||
public List<String> jsonProblems() {
|
||||
if (jsonProblems == null) {
|
||||
return List.of();
|
||||
}
|
||||
return jsonProblems;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@RecordBuilder()
|
||||
@RecordBuilder.Options(inheritComponentAnnotations = false)
|
||||
public record IgnoreAnnotated(@NotNull String s) {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RecordBuilder
|
||||
public record MutableCollectionRecord(List<String> l) implements MutableCollectionRecordBuilder.With {
|
||||
}
|
||||
@@ -21,5 +21,5 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
@RecordBuilder.Options(interpretNotNulls = true)
|
||||
@RecordBuilder
|
||||
public record RequiredRecord(@NotNull String hey, @NotNull int i) {
|
||||
public record RequiredRecord(@NotNull String hey, @NotNull int i) implements RequiredRecordBuilder.With {
|
||||
}
|
||||
|
||||
@@ -21,5 +21,5 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
@RecordBuilder.Options(useValidationApi = true)
|
||||
@RecordBuilder
|
||||
public record RequiredRecord2(@NotNull String hey, @NotNull int i) {
|
||||
public record RequiredRecord2(@NotNull String hey, @NotNull int i) implements RequiredRecord2Builder.With {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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.visibility;
|
||||
|
||||
import io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
@RecordBuilder
|
||||
record PackagePrivateRecord(int i) {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.visibility;
|
||||
|
||||
import io.soabase.recordbuilder.core.RecordBuilder;
|
||||
|
||||
class Wrapper {
|
||||
@RecordBuilder
|
||||
protected record ProtectedRecord(int i) {
|
||||
}
|
||||
|
||||
@RecordBuilder
|
||||
record PackagePrivateRecord(int i) {
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,12 @@ import javax.validation.constraints.Null;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
|
||||
class TestAnnotated {
|
||||
@Test
|
||||
void testInheritComponentAnnotationsFalse() throws NoSuchMethodException {
|
||||
var method = IgnoreAnnotatedBuilder.class.getMethod("s");
|
||||
Assertions.assertNull(method.getAnnotation(NotNull.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStaticConstructor() throws NoSuchMethodException {
|
||||
var method = AnnotatedBuilder.class.getMethod("Annotated", String.class, Integer.TYPE, Double.TYPE);
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 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 org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static io.soabase.recordbuilder.test.foo.PointBuilder.Point;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TestCollections {
|
||||
@Test
|
||||
void testCollectionRecordDefaultValues() {
|
||||
var defaultValues = CollectionRecordBuilder.builder().build();
|
||||
assertNotNull(defaultValues.l());
|
||||
assertNotNull(defaultValues.s());
|
||||
assertNotNull(defaultValues.m());
|
||||
assertNotNull(defaultValues.c());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectionRecordImmutable() {
|
||||
var list = new ArrayList<String>();
|
||||
list.add("one");
|
||||
var set = new HashSet<String>();
|
||||
set.add("one");
|
||||
var map = new HashMap<String, Point>();
|
||||
map.put("one", Point(10, 20));
|
||||
var collectionAsSet = new HashSet<Point>();
|
||||
collectionAsSet.add(Point(30, 40));
|
||||
var r = CollectionRecordBuilder.<String, Point>builder()
|
||||
.l(list)
|
||||
.s(set)
|
||||
.m(map)
|
||||
.c(collectionAsSet)
|
||||
.build();
|
||||
|
||||
assertValues(r, list, set, map, collectionAsSet);
|
||||
assertValueChanges(r, list, set, map, collectionAsSet);
|
||||
assertImmutable(r);
|
||||
|
||||
var collectionAsList = new ArrayList<Point>();
|
||||
var x = CollectionRecordBuilder.<String, Point>builder()
|
||||
.l(list)
|
||||
.s(set)
|
||||
.m(map)
|
||||
.c(collectionAsList)
|
||||
.build();
|
||||
assertTrue(x.c() instanceof List);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCollectionRecordImmutableWithers() {
|
||||
var r = CollectionRecordBuilder.<String, Point>builder().build();
|
||||
|
||||
var list = new ArrayList<String>();
|
||||
list.add("one");
|
||||
var set = new HashSet<String>();
|
||||
set.add("one");
|
||||
var map = new HashMap<String, Point>();
|
||||
map.put("one", Point(10, 20));
|
||||
var collectionAsSet = new HashSet<Point>();
|
||||
collectionAsSet.add(Point(30, 40));
|
||||
var x = r.withL(list).withS(set).withM(map).withC(collectionAsSet);
|
||||
|
||||
assertValues(x, list, set, map, collectionAsSet);
|
||||
assertValueChanges(x, list, set, map, collectionAsSet);
|
||||
assertImmutable(x);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutableCollectionRecord() {
|
||||
var r = MutableCollectionRecordBuilder.builder().build();
|
||||
assertNull(r.l());
|
||||
|
||||
var list = new ArrayList<String>();
|
||||
list.add("one");
|
||||
r = MutableCollectionRecordBuilder.builder().l(list).build();
|
||||
|
||||
assertEquals(r.l(), list);
|
||||
list.add("two");
|
||||
assertEquals(r.l(), list);
|
||||
}
|
||||
|
||||
private void assertImmutable(CollectionRecord<String, Point> r) {
|
||||
assertThrows(UnsupportedOperationException.class, () -> r.l().add("hey"));
|
||||
assertThrows(UnsupportedOperationException.class, () -> r.s().add("hey"));
|
||||
assertThrows(UnsupportedOperationException.class, () -> r.m().put("hey", Point(1, 2)));
|
||||
assertThrows(UnsupportedOperationException.class, () -> r.c().add(Point(1, 2)));
|
||||
}
|
||||
|
||||
private void assertValueChanges(CollectionRecord<String, Point> r, ArrayList<String> list, HashSet<String> set, HashMap<String, Point> map, HashSet<Point> collectionAsSet) {
|
||||
list.add("two");
|
||||
set.add("two");
|
||||
map.put("two", Point(50, 60));
|
||||
collectionAsSet.add(Point(70, 80));
|
||||
|
||||
assertNotEquals(r.l(), list);
|
||||
assertNotEquals(r.s(), set);
|
||||
assertNotEquals(r.m(), map);
|
||||
assertNotEquals(r.c(), collectionAsSet);
|
||||
}
|
||||
|
||||
private void assertValues(CollectionRecord<String, Point> r, ArrayList<String> list, HashSet<String> set, HashMap<String, Point> map, HashSet<Point> collectionAsSet) {
|
||||
assertEquals(r.l(), list);
|
||||
assertEquals(r.s(), set);
|
||||
assertEquals(r.m(), map);
|
||||
assertEquals(r.c(), collectionAsSet);
|
||||
assertTrue(r.c() instanceof Set);
|
||||
}
|
||||
}
|
||||
@@ -30,4 +30,16 @@ class TestValidation {
|
||||
void testValidation() {
|
||||
Assertions.assertThrows(ValidationException.class, () -> RequiredRecord2Builder.builder().build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotNullsWithNewProperty() {
|
||||
var valid = RequiredRecordBuilder.builder().hey("hey").i(1).build();
|
||||
Assertions.assertThrows(NullPointerException.class, () -> valid.withHey(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidationWithNewProperty() {
|
||||
var valid = RequiredRecord2Builder.builder().hey("hey").i(1).build();
|
||||
Assertions.assertThrows(ValidationException.class, () -> valid.withHey(null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +59,4 @@ class TestWithers {
|
||||
Assertions.assertEquals(20, r3.i());
|
||||
Assertions.assertEquals("twenty", r3.s());
|
||||
}
|
||||
|
||||
private static class BadSubclass implements PersonRecordBuilder.With {}
|
||||
|
||||
@Test
|
||||
void testBadWithSubclass() {
|
||||
Assertions.assertThrows(RuntimeException.class, () -> new BadSubclass().withAge(10));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.visibility;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
class TestVisibility {
|
||||
@Test
|
||||
void testMatches() {
|
||||
Assertions.assertFalse(Modifier.isPublic(PackagePrivateRecordBuilder.class.getModifiers()));
|
||||
Assertions.assertFalse(Modifier.isPrivate(PackagePrivateRecordBuilder.class.getModifiers()));
|
||||
Assertions.assertFalse(Modifier.isProtected(PackagePrivateRecordBuilder.class.getModifiers()));
|
||||
|
||||
Assertions.assertTrue(Modifier.isPublic(WrapperProtectedRecordBuilder.class.getModifiers()));
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>io.soabase.record-builder</groupId>
|
||||
<artifactId>record-builder</artifactId>
|
||||
<version>21</version>
|
||||
<version>25-java15</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user