Skip to content

Commit

Permalink
Add an ability to introspect builders (#135)
Browse files Browse the repository at this point in the history
Adds the ability to specify `annotatedWith` on `@Builder` and `@SuperBuilder` thus allowing additional annotations to be added to the generated code.
  • Loading branch information
andriy-dmytruk authored Aug 21, 2024
1 parent 6a55708 commit 779854f
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package io.micronaut.sourcegen.annotations;

import io.micronaut.core.annotation.Introspected;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand All @@ -32,4 +35,14 @@
@Retention(RUNTIME)
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
public @interface Builder {

/**
* Define what annotations should be added to the generated builder. By default,
* the builder will have {@link io.micronaut.core.annotation.Introspected} annotation
* so that introspection can be created for it.
*
* @return Array of annotations to apply on the builder
*/
Class<? extends Annotation>[] annotatedWith() default Introspected.class;

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package io.micronaut.sourcegen.annotations;

import io.micronaut.core.annotation.Introspected;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
Expand All @@ -33,4 +36,14 @@
@Retention(RUNTIME)
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
public @interface SuperBuilder {

/**
* Define what annotations should be added to the generated builder. By default,
* the builder will have {@link io.micronaut.core.annotation.Introspected} annotation
* so that introspection can be created for it.
*
* @return Array of annotations to apply on the builder
*/
Class<? extends Annotation>[] annotatedWith() default Introspected.class;

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
*/
package io.micronaut.sourcegen.generator.visitors;

import io.micronaut.core.annotation.AnnotationClassValue;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.Creator;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.bind.annotation.Bindable;
Expand All @@ -32,6 +36,7 @@
import io.micronaut.sourcegen.generator.SourceGenerator;
import io.micronaut.sourcegen.generator.SourceGenerators;
import io.micronaut.sourcegen.model.ClassDef;
import io.micronaut.sourcegen.model.ClassDef.ClassDefBuilder;
import io.micronaut.sourcegen.model.ClassTypeDef;
import io.micronaut.sourcegen.model.ExpressionDef;
import io.micronaut.sourcegen.model.FieldDef;
Expand All @@ -41,7 +46,6 @@
import io.micronaut.sourcegen.model.TypeDef;
import io.micronaut.sourcegen.model.VariableDef;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
import javax.lang.model.element.Modifier;
Expand Down Expand Up @@ -71,6 +75,8 @@
@Internal
public final class BuilderAnnotationVisitor implements TypeElementVisitor<Builder, Object> {

public static final String BUILDER_ANNOTATED_WITH_MEMBER = "annotatedWith";

private final Set<String> processed = new HashSet<>();

@Override
Expand Down Expand Up @@ -101,6 +107,7 @@ public void visitClass(ClassElement element, VisitorContext context) {

ClassDef.ClassDefBuilder builder = ClassDef.builder(builderClassName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
addAnnotations(builder, element.getAnnotation(Builder.class));

List<PropertyElement> properties = element.getBeanProperties();
for (PropertyElement beanProperty : properties) {
Expand Down Expand Up @@ -150,8 +157,22 @@ public void visitClass(ClassElement element, VisitorContext context) {
}
}

private MethodDef createAllPropertiesConstructor(ClassTypeDef builderType, List<PropertyElement> properties) {
MethodDef.MethodDefBuilder builder = MethodDef.constructor();
static void addAnnotations(ClassDefBuilder builder, AnnotationValue<?> annotation) {
Optional<AnnotationClassValue[]> annotatedWith = annotation.getConvertibleValues()
.get(BUILDER_ANNOTATED_WITH_MEMBER, AnnotationClassValue[].class);
if (annotatedWith.isEmpty()) {
// Apply the default annotation
builder.addAnnotation(Introspected.class);
} else {
for (AnnotationClassValue<?> value: annotatedWith.get()) {
builder.addAnnotation(value.getName());
}
}
}

static MethodDef createAllPropertiesConstructor(ClassTypeDef builderType, List<PropertyElement> properties) {
MethodDef.MethodDefBuilder builder = MethodDef.constructor()
.addAnnotation(Creator.class);
VariableDef.This self = new VariableDef.This(builderType);
for (PropertyElement parameter : properties) {
ParameterDef parameterDef = ParameterDef.of(parameter.getName(), TypeDef.of(parameter.getType()));
Expand Down Expand Up @@ -180,7 +201,7 @@ private MethodDef createAllPropertiesConstructor(ClassTypeDef builderType, List<
return builder.build();
}

private StatementDef iterableToArrayListStatement(VariableDef.This self, ParameterDef parameterDef) {
private static StatementDef iterableToArrayListStatement(VariableDef.This self, ParameterDef parameterDef) {
return ClassTypeDef.of(ArrayList.class)
.instantiate()
.newLocal(parameterDef.getName() + "ArrayList", arrayListVar ->
Expand All @@ -198,7 +219,7 @@ private StatementDef iterableToArrayListStatement(VariableDef.This self, Paramet
)));
}

private StatementDef mapToArrayListStatement(VariableDef.This self, ParameterDef parameterDef) {
private static StatementDef mapToArrayListStatement(VariableDef.This self, ParameterDef parameterDef) {
return self.field(parameterDef.getName(), parameterDef.getType())
.assign(
ClassTypeDef.of(ArrayList.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
import io.micronaut.sourcegen.model.MethodDef;
import io.micronaut.sourcegen.model.TypeDef;

import java.util.HashSet;
import java.util.Set;
import javax.lang.model.element.Modifier;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static io.micronaut.sourcegen.generator.visitors.BuilderAnnotationVisitor.addAnnotations;
import static io.micronaut.sourcegen.generator.visitors.BuilderAnnotationVisitor.createModifyPropertyMethod;

/**
Expand Down Expand Up @@ -148,6 +149,12 @@ public void visitClass(ClassElement element, VisitorContext context) {
)
)
);
addAnnotations(builder, element.getAnnotation(SuperBuilder.class));

builder.addMethod(MethodDef.constructor().build());
if (!properties.isEmpty()) {
builder.addMethod(BuilderAnnotationVisitor.createAllPropertiesConstructor(builderType, properties));
}

builder.addMethod(createSelfMethod());
builder.addMethod(BuilderAnnotationVisitor.createBuildMethod(element));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class BuilderAnnotationVisitorSpec extends AbstractTypeElementSpec {
package test;
import io.micronaut.sourcegen.annotations.Builder;
@Builder
@Builder(annotatedWith = {})
public record Walrus(
String name,
int age,
Expand All @@ -35,7 +35,7 @@ class BuilderAnnotationVisitorSpec extends AbstractTypeElementSpec {
package test;
import io.micronaut.sourcegen.annotations.Builder;
@Builder
@Builder(annotatedWith = {})
public record Walrus() {
}
""")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
package io.micronaut.sourcegen.example;

import java.lang.reflect.Modifier;

import io.micronaut.core.beans.BeanIntrospection;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class AnimalSuperBuilderTest {
Expand Down Expand Up @@ -61,6 +64,14 @@ public void testDog() {
}
//end::test[]

@Test
public void dogIntrospection() {
var introspection = BeanIntrospection.getIntrospection(DogSuperBuilder.class);
assertNotNull(introspection);
assertEquals(0, introspection.getBeanProperties().size());
assertEquals(6, introspection.getConstructorArguments().length);
}

@Test
public void internalTest() {
assertTrue(Modifier.isPublic(CatSuperBuilder.class.getModifiers()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
*/
package io.micronaut.sourcegen.example;

import io.micronaut.core.beans.BeanIntrospection;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

class PersonBuilderTest {

Expand All @@ -36,6 +38,14 @@ public void buildsPerson() {
}
//end::test[]

@Test
public void personIntrospection() {
var introspection = BeanIntrospection.getIntrospection(PersonBuilder.class);
assertNotNull(introspection);
assertEquals(0, introspection.getBeanProperties().size());
assertEquals(3, introspection.getConstructorArguments().length);
}

@Test
public void buildsPersonWithPrimitiveDefaults() {
var person = Person2Builder.builder()
Expand Down

0 comments on commit 779854f

Please sign in to comment.