Compare commits
5 Commits
record-bui
...
record-bui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
821718ac3f | ||
|
|
8ac34529e7 | ||
|
|
c701a09039 | ||
|
|
328bb7d660 | ||
|
|
41eddd46a3 |
4
pom.xml
4
pom.xml
@@ -5,7 +5,7 @@
|
||||
<groupId>io.soabase.record-builder</groupId>
|
||||
<artifactId>record-builder</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>24</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-24</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>24</version>
|
||||
<version>25-java15</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -136,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>24</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,6 +87,7 @@ class InternalRecordBuilderProcessor {
|
||||
add1SetterMethod(component);
|
||||
add1GetterMethod(component);
|
||||
});
|
||||
collectionBuilderUtils.addShims(builder);
|
||||
builderType = builder.build();
|
||||
}
|
||||
|
||||
@@ -95,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());
|
||||
@@ -103,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());
|
||||
}
|
||||
@@ -220,7 +240,7 @@ class InternalRecordBuilderProcessor {
|
||||
|
||||
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,9 +257,9 @@ class InternalRecordBuilderProcessor {
|
||||
if (parameterIndex > 0) {
|
||||
codeBlockBuilder.add(", ");
|
||||
}
|
||||
ClassType parameterComponent = recordComponents.get(parameterIndex);
|
||||
RecordClassType parameterComponent = recordComponents.get(parameterIndex);
|
||||
if (parameterIndex == index) {
|
||||
codeBlockBuilder.add(parameterComponent.name());
|
||||
collectionBuilderUtils.add(codeBlockBuilder, parameterComponent);
|
||||
} else {
|
||||
codeBlockBuilder.add("$L()", parameterComponent.name());
|
||||
}
|
||||
@@ -278,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());
|
||||
@@ -454,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()) {
|
||||
@@ -590,10 +610,34 @@ class InternalRecordBuilderProcessor {
|
||||
.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
|
||||
.addAnnotation(generatedRecordBuilderAnnotation)
|
||||
.returns(component.typeName());
|
||||
component.getAccessorAnnotations().forEach(annotationMirror -> methodSpecBuilder.addAnnotation(AnnotationSpec.get(annotationMirror)));
|
||||
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) {
|
||||
/*
|
||||
For a single record component, add a getter similar to:
|
||||
@@ -608,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());
|
||||
}
|
||||
|
||||
@@ -622,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>24</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 {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>24</version>
|
||||
<version>25-java15</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user