Skip to content

Commit 68c1798

Browse files
authored
Merge pull request #88 from avaje/module-level-builder
Module Wide Builders
2 parents 4d005ba + cfa79a6 commit 68c1798

File tree

4 files changed

+67
-13
lines changed

4 files changed

+67
-13
lines changed

avaje-record-builder-core/src/main/java/io/avaje/recordbuilder/internal/RecordProcessor.java

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010

1111
import java.io.IOException;
1212
import java.io.UncheckedIOException;
13+
import java.util.HashSet;
1314
import java.util.List;
1415
import java.util.Set;
16+
import java.util.stream.Stream;
1517

1618
import javax.annotation.processing.AbstractProcessor;
1719
import javax.annotation.processing.ProcessingEnvironment;
1820
import javax.annotation.processing.RoundEnvironment;
1921
import javax.annotation.processing.SupportedAnnotationTypes;
2022
import javax.lang.model.SourceVersion;
23+
import javax.lang.model.element.Element;
2124
import javax.lang.model.element.ElementKind;
2225
import javax.lang.model.element.Name;
2326
import javax.lang.model.element.RecordComponentElement;
@@ -53,7 +56,6 @@ public synchronized void init(ProcessingEnvironment env) {
5356

5457
@Override
5558
public boolean process(Set<? extends TypeElement> tes, RoundEnvironment roundEnv) {
56-
5759
APContext.setProjectModuleElement(tes, roundEnv);
5860
final var globalTypeInitializers =
5961
roundEnv.getElementsAnnotatedWith(typeElement(GlobalPrism.PRISM_TYPE)).stream()
@@ -62,9 +64,13 @@ public boolean process(Set<? extends TypeElement> tes, RoundEnvironment roundEnv
6264

6365
InitMap.putAll(globalTypeInitializers);
6466

65-
for (final TypeElement type :
66-
ElementFilter.typesIn(
67-
roundEnv.getElementsAnnotatedWith(typeElement(RecordBuilderPrism.PRISM_TYPE)))) {
67+
var elements = roundEnv.getElementsAnnotatedWith(typeElement(RecordBuilderPrism.PRISM_TYPE));
68+
69+
var records = new HashSet<>(ElementFilter.typesIn(elements));
70+
allPackages(elements)
71+
.forEach(e -> findRecordsInPackage(e, records));
72+
73+
for (final TypeElement type : records) {
6874
if (type.getKind() != ElementKind.RECORD) {
6975
logError(type, "Builders can only be generated for record classes");
7076
continue;
@@ -93,12 +99,46 @@ public boolean process(Set<? extends TypeElement> tes, RoundEnvironment roundEnv
9399
return false;
94100
}
95101

102+
private static Stream<? extends Element> allPackages(Set<? extends Element> elements) {
103+
return Stream.concat(
104+
modulePackages(elements),
105+
ElementFilter.packagesIn(elements).stream());
106+
}
107+
108+
private static Stream<? extends Element> modulePackages(Set<? extends Element> elements) {
109+
return ElementFilter.modulesIn(elements).stream()
110+
.map(Element::getEnclosedElements)
111+
.flatMap(List::stream);
112+
}
113+
114+
private void findRecordsInPackage(Element pkg, Set<TypeElement> records) {
115+
for (var enclosedElement : ElementFilter.typesIn(pkg.getEnclosedElements())) {
116+
if (enclosedElement.getKind() == ElementKind.RECORD) {
117+
records.add(enclosedElement);
118+
}
119+
findNestedRecords(enclosedElement, records);
120+
}
121+
}
122+
123+
private void findNestedRecords(TypeElement type, Set<TypeElement> types) {
124+
for (var enclosedElement : ElementFilter.typesIn(type.getEnclosedElements())) {
125+
if (enclosedElement.getKind() == ElementKind.RECORD) {
126+
types.add(enclosedElement);
127+
}
128+
findNestedRecords(enclosedElement, types);
129+
}
130+
}
131+
96132
private void readElement(TypeElement type) {
97-
readElement(type, RecordBuilderPrism.getInstanceOn(type));
133+
readElement(type, findRecordBuilderPrism(type));
98134
}
99135

100-
private void readElement(TypeElement type, BuilderPrism prism) {
136+
private static RecordBuilderPrism findRecordBuilderPrism(Element element) {
137+
var prism = RecordBuilderPrism.getInstanceOn(element);
138+
return prism != null ? prism : findRecordBuilderPrism(element.getEnclosingElement());
139+
}
101140

141+
private void readElement(TypeElement type, BuilderPrism prism) {
102142
final var components = type.getRecordComponents();
103143
final var packageElement = elements().getPackageOf(type);
104144
boolean isImported = prism.imported();
@@ -136,9 +176,7 @@ private void methods(
136176
boolean getters = prism.getters();
137177

138178
for (final var element : components) {
139-
140179
final var type = UType.parse(element.asType());
141-
142180
writer.append(
143181
MethodSetter.methodSetter(
144182
element.getSimpleName(), type.shortType(), builderName, typeParams));
@@ -147,16 +185,13 @@ private void methods(
147185
}
148186

149187
if (APContext.isAssignable(type.mainType(), "java.util.Collection")) {
150-
151188
String param0ShortType = type.param0().shortType();
152189
Name simpleName = element.getSimpleName();
153190
writer.append(
154-
MethodAdd.methodAdd(
155-
simpleName.toString(), builderName, param0ShortType, typeParams));
191+
MethodAdd.methodAdd(simpleName.toString(), builderName, param0ShortType, typeParams));
156192
}
157193

158194
if (APContext.isAssignable(type.mainType(), "java.util.Map")) {
159-
160195
String param0ShortType = type.param0().shortType();
161196
String param1ShortType = type.param1().shortType();
162197
Name simpleName = element.getSimpleName();

avaje-record-builder/src/main/java/io/avaje/recordbuilder/RecordBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
/** Generate a builder class for the given record */
1313
@Documented
14-
@Target(TYPE)
14+
@Target({TYPE, PACKAGE, MODULE})
1515
@Retention(SOURCE)
1616
public @interface RecordBuilder {
1717

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.avaje.recordbuilder.test.pkginfo;
2+
3+
public record Hornet(Silk silk) {
4+
5+
static HornetBuilder builder() {
6+
return HornetBuilder.builder();
7+
}
8+
9+
public record Silk(String song) {
10+
static Hornet$SilkBuilder builder() {
11+
return Hornet$SilkBuilder.builder();
12+
}
13+
}
14+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@NullMarked
2+
@io.avaje.recordbuilder.RecordBuilder
3+
package io.avaje.recordbuilder.test.pkginfo;
4+
5+
import org.jspecify.annotations.NullMarked;

0 commit comments

Comments
 (0)