From 9b46975196998ff3f2a30947dd9a7988d1d5010e Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:25:37 -0400 Subject: [PATCH 1/4] support module/package level builder --- .../internal/RecordProcessor.java | 45 ++++++++++++++++--- .../io/avaje/recordbuilder/RecordBuilder.java | 2 +- .../avaje/recordbuilder/extended/Nullity.java | 8 ++++ .../recordbuilder/extended/package-info.java | 4 ++ .../recordbuilder/test/pkginfo/Hornet.java | 5 +++ .../test/pkginfo/package-info.java | 5 +++ 6 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java create mode 100644 blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java create mode 100644 blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java create mode 100644 blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/package-info.java diff --git a/avaje-record-builder-core/src/main/java/io/avaje/recordbuilder/internal/RecordProcessor.java b/avaje-record-builder-core/src/main/java/io/avaje/recordbuilder/internal/RecordProcessor.java index 92527b4..b16126c 100644 --- a/avaje-record-builder-core/src/main/java/io/avaje/recordbuilder/internal/RecordProcessor.java +++ b/avaje-record-builder-core/src/main/java/io/avaje/recordbuilder/internal/RecordProcessor.java @@ -10,14 +10,17 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Stream; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Name; import javax.lang.model.element.RecordComponentElement; @@ -62,9 +65,17 @@ public boolean process(Set tes, RoundEnvironment roundEnv InitMap.putAll(globalTypeInitializers); - for (final TypeElement type : - ElementFilter.typesIn( - roundEnv.getElementsAnnotatedWith(typeElement(RecordBuilderPrism.PRISM_TYPE)))) { + var elements = roundEnv.getElementsAnnotatedWith(typeElement(RecordBuilderPrism.PRISM_TYPE)); + + var records = new HashSet<>(ElementFilter.typesIn(elements)); + Stream.concat( + ElementFilter.modulesIn(elements).stream() + .map(Element::getEnclosedElements) + .flatMap(List::stream), + ElementFilter.packagesIn(elements).stream()) + .forEach(e -> findRecordsInPackage(e, records)); + + for (final TypeElement type : records) { if (type.getKind() != ElementKind.RECORD) { logError(type, "Builders can only be generated for record classes"); continue; @@ -93,8 +104,31 @@ public boolean process(Set tes, RoundEnvironment roundEnv return false; } + private void findRecordsInPackage(Element pkg, Set records) { + for (var enclosedElement : ElementFilter.typesIn(pkg.getEnclosedElements())) { + if (enclosedElement.getKind() == ElementKind.RECORD) { + records.add(enclosedElement); + } + findNestedRecords(enclosedElement, records); + } + } + + private void findNestedRecords(TypeElement type, Set types) { + for (var enclosedElement : ElementFilter.typesIn(type.getEnclosedElements())) { + if (enclosedElement.getKind() == ElementKind.RECORD) { + types.add(enclosedElement); + } + findNestedRecords(enclosedElement, types); + } + } + private void readElement(TypeElement type) { - readElement(type, RecordBuilderPrism.getInstanceOn(type)); + readElement(type, getRecordBuilderPrism(type)); + } + + private static RecordBuilderPrism getRecordBuilderPrism(Element element) { + var prism = RecordBuilderPrism.getInstanceOn(element); + return prism != null ? prism : getRecordBuilderPrism(element.getEnclosingElement()); } private void readElement(TypeElement type, BuilderPrism prism) { @@ -151,8 +185,7 @@ private void methods( String param0ShortType = type.param0().shortType(); Name simpleName = element.getSimpleName(); writer.append( - MethodAdd.methodAdd( - simpleName.toString(), builderName, param0ShortType, typeParams)); + MethodAdd.methodAdd(simpleName.toString(), builderName, param0ShortType, typeParams)); } if (APContext.isAssignable(type.mainType(), "java.util.Map")) { diff --git a/avaje-record-builder/src/main/java/io/avaje/recordbuilder/RecordBuilder.java b/avaje-record-builder/src/main/java/io/avaje/recordbuilder/RecordBuilder.java index d8b54a9..e7157b8 100644 --- a/avaje-record-builder/src/main/java/io/avaje/recordbuilder/RecordBuilder.java +++ b/avaje-record-builder/src/main/java/io/avaje/recordbuilder/RecordBuilder.java @@ -11,7 +11,7 @@ /** Generate a builder class for the given record */ @Documented -@Target(TYPE) +@Target({TYPE, PACKAGE, MODULE}) @Retention(SOURCE) public @interface RecordBuilder { diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java new file mode 100644 index 0000000..c6b98f3 --- /dev/null +++ b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java @@ -0,0 +1,8 @@ +package io.avaje.recordbuilder.extended; + +import org.jspecify.annotations.NonNull; + +import io.avaje.recordbuilder.RecordBuilder; + +@RecordBuilder +public record Nullity(@NonNull String nonNull, String nully, String marked) {} diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java new file mode 100644 index 0000000..808590e --- /dev/null +++ b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.avaje.recordbuilder.extended; + +import org.jspecify.annotations.NullMarked; diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java new file mode 100644 index 0000000..5ae8ad1 --- /dev/null +++ b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java @@ -0,0 +1,5 @@ +package io.avaje.recordbuilder.test.pkginfo; + +public record Hornet(Silk silk) { + public record Silk(String song) {} +} diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/package-info.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/package-info.java new file mode 100644 index 0000000..8a762eb --- /dev/null +++ b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +@io.avaje.recordbuilder.RecordBuilder +package io.avaje.recordbuilder.test.pkginfo; + +import org.jspecify.annotations.NullMarked; From c7f8a36c24125a4e9801c31b7381e3f0b8b9de90 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:27:21 -0400 Subject: [PATCH 2/4] hm --- .../java/io/avaje/recordbuilder/extended/Nullity.java | 8 -------- .../io/avaje/recordbuilder/extended/package-info.java | 4 ---- 2 files changed, 12 deletions(-) delete mode 100644 blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java delete mode 100644 blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java deleted file mode 100644 index c6b98f3..0000000 --- a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/Nullity.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.avaje.recordbuilder.extended; - -import org.jspecify.annotations.NonNull; - -import io.avaje.recordbuilder.RecordBuilder; - -@RecordBuilder -public record Nullity(@NonNull String nonNull, String nully, String marked) {} diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java deleted file mode 100644 index 808590e..0000000 --- a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/extended/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package io.avaje.recordbuilder.extended; - -import org.jspecify.annotations.NullMarked; From 2be049a92ec026a3bc6892ee49690b9a8b227370 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:29:40 -0400 Subject: [PATCH 3/4] Update Hornet.java --- .../io/avaje/recordbuilder/test/pkginfo/Hornet.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java index 5ae8ad1..21fcbc5 100644 --- a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java +++ b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java @@ -1,5 +1,14 @@ package io.avaje.recordbuilder.test.pkginfo; public record Hornet(Silk silk) { - public record Silk(String song) {} + + HornetBuilder builder() { + return HornetBuilder.builder(); + } + + public record Silk(String song) { + Hornet$SilkBuilder builder() { + return Hornet$SilkBuilder.builder(); + } + } } From 3ac5f4ce1fa860fa9a8be7984f13499498601168 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Wed, 24 Sep 2025 12:17:58 -0400 Subject: [PATCH 4/4] Update Hornet.java --- .../main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java index 21fcbc5..82e5619 100644 --- a/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java +++ b/blackbox-test-records/src/main/java/io/avaje/recordbuilder/test/pkginfo/Hornet.java @@ -2,12 +2,12 @@ public record Hornet(Silk silk) { - HornetBuilder builder() { + static HornetBuilder builder() { return HornetBuilder.builder(); } public record Silk(String song) { - Hornet$SilkBuilder builder() { + static Hornet$SilkBuilder builder() { return Hornet$SilkBuilder.builder(); } }