Skip to content

Commit f4d67ae

Browse files
Add support for enum javadoc (#261)
* Add support for enum javadoc Add an enum constant builder for storing enum constant properties. * fix build errors --------- Co-authored-by: elifKurtay <[email protected]> Co-authored-by: Elif Kurtay <[email protected]>
1 parent a53a8e0 commit f4d67ae

File tree

5 files changed

+126
-33
lines changed

5 files changed

+126
-33
lines changed

sourcegen-bytecode-writer/src/main/java/io/micronaut/sourcegen/bytecode/EnumGenUtils.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.micronaut.sourcegen.model.ClassDef;
2121
import io.micronaut.sourcegen.model.ClassTypeDef;
2222
import io.micronaut.sourcegen.model.EnumDef;
23+
import io.micronaut.sourcegen.model.EnumDef.EnumConstantDef;
2324
import io.micronaut.sourcegen.model.ExpressionDef;
2425
import io.micronaut.sourcegen.model.FieldDef;
2526
import io.micronaut.sourcegen.model.MethodDef;
@@ -71,14 +72,14 @@ public static ClassDef toClassDef(EnumDef enumDef) {
7172
}
7273

7374
int i = 0;
74-
for (Map.Entry<String, List<ExpressionDef>> e : enumDef.getEnumConstants().entrySet()) {
75+
for (EnumConstantDef e : enumDef.getEnumConstants()) {
7576

7677
List<ExpressionDef> values = new ArrayList<>();
77-
values.add(ExpressionDef.constant(e.getKey()));
78+
values.add(ExpressionDef.constant(e.name()));
7879
values.add(TypeDef.Primitive.INT.constant(i++));
79-
values.addAll(e.getValue());
80+
values.addAll(e.constructorArgs());
8081

81-
FieldDef enumField = FieldDef.builder(e.getKey(), enumTypeDef)
82+
FieldDef enumField = FieldDef.builder(e.name(), enumTypeDef)
8283
.addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC)
8384
.initializer(enumTypeDef.instantiate(values))
8485
.build();
@@ -108,9 +109,8 @@ public static ClassDef toClassDef(EnumDef enumDef) {
108109
enumTypeDef.array()
109110
.instantiate(
110111
enumDef.getEnumConstants()
111-
.keySet()
112112
.stream()
113-
.<ExpressionDef>map(name -> enumTypeDef.getStaticField(name, enumTypeDef))
113+
.<ExpressionDef>map(e -> enumTypeDef.getStaticField(e.name(), enumTypeDef))
114114
.toList()
115115
)
116116
.returning());

sourcegen-generator-java/src/main/java/io/micronaut/sourcegen/JavaPoetSourceGenerator.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,21 @@ private TypeSpec.Builder getEnumBuilder(EnumDef enumDef) {
168168
enumBuilder.addAnnotation(asAnnotationSpec(annotation));
169169
}
170170

171-
enumDef.getEnumConstants().forEach((name, exps) -> {
172-
if (exps != null) {
171+
enumDef.getEnumConstants().forEach(e -> {
172+
TypeSpec.Builder type = anonymousClassBuilder("");
173+
if (e.constructorArgs() != null) {
174+
List<ExpressionDef> constructorArgs = e.constructorArgs();
173175
CodeBlock.Builder expBuilder = CodeBlock.builder();
174-
for (int i = 0; i < exps.size(); i++) {
175-
expBuilder.add(renderExpression(null, null, Map.of(), exps.get(i)));
176-
if (i < exps.size() - 1) {
176+
for (int i = 0; i < constructorArgs.size(); i++) {
177+
expBuilder.add(renderExpression(null, null, Map.of(), constructorArgs.get(i)));
178+
if (i < constructorArgs.size() - 1) {
177179
expBuilder.add(", ");
178180
}
179181
}
180-
enumBuilder.addEnumConstant(name, anonymousClassBuilder(expBuilder.build()).build());
181-
} else {
182-
enumBuilder.addEnumConstant(name);
182+
type = anonymousClassBuilder(expBuilder.build());
183183
}
184+
e.javadoc().forEach(type::addJavadoc);
185+
enumBuilder.addEnumConstant(e.name(), type.build());
184186
});
185187

186188
buildProperties(enumDef, enumBuilder);

sourcegen-generator-java/src/test/java/io/micronaut/sourcegen/javapoet/write/EnumWriteTest.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.micronaut.sourcegen.JavaPoetSourceGenerator;
44
import io.micronaut.sourcegen.model.EnumDef;
5+
import io.micronaut.sourcegen.model.EnumDef.EnumConstantDef;
56
import io.micronaut.sourcegen.model.ExpressionDef;
67
import io.micronaut.sourcegen.model.FieldDef;
78
import io.micronaut.sourcegen.model.MethodDef;
@@ -43,6 +44,41 @@ enum Status {
4344
assertEquals(expected.strip(), result.strip());
4445
}
4546

47+
@Test
48+
public void writeEnumWithJavadoc() throws IOException {
49+
EnumDef enumDef = EnumDef.builder("test.Status")
50+
.addJavadoc("Enum representing the status of a process.")
51+
.addEnumConstant(EnumConstantDef.builder("ACTIVE").addJavadoc("The active state.").build())
52+
.addEnumConstant(EnumConstantDef.builder("IN_PROGRESS").addJavadoc("The in progress state.").build())
53+
.addEnumConstant(EnumConstantDef.builder("DELETED").addJavadoc("The in deleted state.").build())
54+
.build();
55+
var result = writeEnum(enumDef);
56+
57+
var expected = """
58+
package test;
59+
60+
/**
61+
* Enum representing the status of a process.
62+
*/
63+
enum Status {
64+
65+
/**
66+
* The active state.
67+
*/
68+
ACTIVE,
69+
/**
70+
* The in progress state.
71+
*/
72+
IN_PROGRESS,
73+
/**
74+
* The in deleted state.
75+
*/
76+
DELETED
77+
}
78+
""";
79+
assertEquals(expected.strip(), result.strip());
80+
}
81+
4682
@Test
4783
public void testExceptions() {
4884
assertThrows(IllegalArgumentException.class, () ->
@@ -205,7 +241,7 @@ private String writeEnum(EnumDef enumDef) throws IOException {
205241
}
206242

207243
// The regex will skip the imports and make sure it is a record
208-
final Pattern ENUM_REGEX = Pattern.compile("package [^;]+;[^/]+" +
244+
final Pattern ENUM_REGEX = Pattern.compile("package [^;]+;[^{]+" +
209245
"enum " + enumDef.getSimpleName() + " \\{\\s+" +
210246
"([\\s\\S]+)\\s+}\\s+");
211247
Matcher matcher = ENUM_REGEX.matcher(result);

sourcegen-generator-kotlin/src/main/kotlin/io/micronaut/sourcegen/KotlinPoetSourceGenerator.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import io.micronaut.core.reflect.ClassUtils
2828
import io.micronaut.inject.visitor.VisitorContext
2929
import io.micronaut.sourcegen.generator.SourceGenerator
3030
import io.micronaut.sourcegen.model.*
31+
import io.micronaut.sourcegen.model.EnumDef.EnumConstantDef
3132
import io.micronaut.sourcegen.model.ExpressionDef.*
3233
import io.micronaut.sourcegen.model.StatementDef.Assign
3334
import io.micronaut.sourcegen.model.StatementDef.DefineAndAssign
@@ -295,8 +296,9 @@ class KotlinPoetSourceGenerator : SourceGenerator {
295296
enumDef.annotations.stream().map { annotationDef: AnnotationDef -> asAnnotationSpec(annotationDef) }
296297
.forEach { annotationSpec: AnnotationSpec -> enumBuilder.addAnnotation(annotationSpec) }
297298

298-
enumDef.enumConstants.forEach { (name: String?, exps: List<ExpressionDef>?) ->
299-
if (exps != null && exps.isNotEmpty()) {
299+
enumDef.enumConstants.forEach { enumConstant: EnumConstantDef ->
300+
if (enumConstant.constructorArgs != null && enumConstant.constructorArgs.isNotEmpty()) {
301+
val exps = enumConstant.constructorArgs
300302
val expBuilder: CodeBlock.Builder = CodeBlock.builder()
301303
for (i in exps.indices) {
302304
expBuilder.add(renderExpressionCode(null,
@@ -307,13 +309,13 @@ class KotlinPoetSourceGenerator : SourceGenerator {
307309
}
308310
}
309311
enumBuilder.addEnumConstant(
310-
name,
312+
enumConstant.name,
311313
TypeSpec.companionObjectBuilder()
312314
.addSuperclassConstructorParameter(expBuilder.build())
313315
.build()
314316
)
315317
} else {
316-
enumBuilder.addEnumConstant(name)
318+
enumBuilder.addEnumConstant(enumConstant.name)
317319
}
318320
}
319321

sourcegen-model/src/main/java/io/micronaut/sourcegen/model/EnumDef.java

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
import java.util.Collections;
2626
import java.util.EnumSet;
2727
import java.util.HashSet;
28-
import java.util.LinkedHashMap;
2928
import java.util.List;
30-
import java.util.Map;
3129
import java.util.Objects;
3230
import java.util.Set;
3331

@@ -43,7 +41,7 @@
4341
public final class EnumDef extends ObjectDef {
4442

4543
private final List<FieldDef> fields;
46-
private final LinkedHashMap<String, List<ExpressionDef>> enumConstants;
44+
private final List<EnumConstantDef> enumConstants;
4745

4846
private EnumDef(ClassTypeDef.ClassName className,
4947
EnumSet<Modifier> modifiers,
@@ -52,7 +50,7 @@ private EnumDef(ClassTypeDef.ClassName className,
5250
List<PropertyDef> properties,
5351
List<AnnotationDef> annotations,
5452
List<String> javadoc,
55-
LinkedHashMap<String, List<ExpressionDef>> enumConstants,
53+
List<EnumConstantDef> enumConstants,
5654
List<TypeDef> superinterfaces,
5755
List<ObjectDef> innerTypes,
5856
boolean synthetic) {
@@ -74,7 +72,7 @@ public List<FieldDef> getFields() {
7472
return fields;
7573
}
7674

77-
public LinkedHashMap<String, List<ExpressionDef>> getEnumConstants() {
75+
public List<EnumConstantDef> getEnumConstants() {
7876
return enumConstants;
7977
}
8078

@@ -95,8 +93,10 @@ public FieldDef findField(String name) {
9593

9694
@NonNull
9795
public FieldDef getField(String name) {
98-
if (enumConstants.containsKey(name)) {
99-
return FieldDef.builder(name, asTypeDef()).build();
96+
for (EnumConstantDef constant: enumConstants) {
97+
if (constant.name().equals(name)) {
98+
return FieldDef.builder(name, asTypeDef()).build();
99+
}
100100
}
101101
FieldDef field = findField(name);
102102
if (field == null) {
@@ -110,6 +110,54 @@ public boolean hasField(String name) {
110110
return property != null;
111111
}
112112

113+
/**
114+
* A type defining an enum constant.
115+
* @since 1.7
116+
* @param name The enum constant name
117+
* @param constructorArgs The arguments passed to the enum constructor
118+
* @param javadoc The documentation
119+
*/
120+
@Experimental
121+
public record EnumConstantDef(
122+
String name,
123+
List<ExpressionDef> constructorArgs,
124+
List<String> javadoc
125+
) {
126+
public static EnumConstantDefBuilder builder(String name) {
127+
return new EnumConstantDefBuilder(name);
128+
}
129+
}
130+
131+
/**
132+
* Builder for {@link EnumConstantDef}.
133+
* @since 1.7
134+
*/
135+
@Experimental
136+
public static final class EnumConstantDefBuilder {
137+
private final String name;
138+
private List<ExpressionDef> constructorArgs;
139+
private List<String> javadoc = new ArrayList<>();
140+
141+
public EnumConstantDefBuilder(String name) {
142+
this.name = name;
143+
}
144+
145+
public EnumConstantDefBuilder withConstructorArgs(List<ExpressionDef> constructorArgs) {
146+
this.constructorArgs = constructorArgs;
147+
return this;
148+
}
149+
150+
public EnumConstantDefBuilder addJavadoc(String javadoc) {
151+
this.javadoc.add(javadoc);
152+
return this;
153+
}
154+
155+
public EnumConstantDef build() {
156+
return new EnumConstantDef(name, constructorArgs, javadoc);
157+
}
158+
159+
}
160+
113161
/**
114162
* The enum definition builder.
115163
*
@@ -120,7 +168,7 @@ public boolean hasField(String name) {
120168
public static final class EnumDefBuilder extends ObjectDefBuilder<EnumDefBuilder> {
121169

122170
private final List<FieldDef> fields = new ArrayList<>();
123-
private final LinkedHashMap<String, List<ExpressionDef>> enumConstants = new LinkedHashMap<>();
171+
private final List<EnumConstantDef> enumConstants = new ArrayList<>();
124172

125173
private EnumDefBuilder(String name) {
126174
super(name);
@@ -131,28 +179,33 @@ public EnumDefBuilder addField(FieldDef field) {
131179
return this;
132180
}
133181

182+
public EnumDefBuilder addEnumConstant(EnumConstantDef constant) {
183+
enumConstants.add(constant);
184+
return this;
185+
}
186+
134187
public EnumDefBuilder addEnumConstant(String name) {
135188
String constName = getConstantName(name);
136-
enumConstants.put(constName, List.of());
189+
enumConstants.add(new EnumConstantDef(constName, Collections.emptyList(), Collections.emptyList()));
137190
return this;
138191
}
139192

140193
public EnumDefBuilder addEnumConstant(String name, ExpressionDef... values) {
141194
Objects.requireNonNull(values, "Values cannot be null");
142195
String constName = getConstantName(name);
143-
enumConstants.put(constName, List.of(values));
196+
enumConstants.add(new EnumConstantDef(constName, List.of(values), Collections.emptyList()));
144197
return this;
145198
}
146199

147200
public EnumDef build() {
148201
if (!enumConstants.isEmpty()) {
149202
Set<Integer> valueCount = new HashSet<>();
150-
for (Map.Entry<String, List<ExpressionDef>> entry : enumConstants.entrySet()) {
151-
if (entry.getValue() == null || entry.getValue().isEmpty()) {
203+
for (EnumConstantDef constantDef : enumConstants) {
204+
if (constantDef.constructorArgs == null || constantDef.constructorArgs.isEmpty()) {
152205
continue;
153206
}
154207

155-
int constCount = entry.getValue().size();
208+
int constCount = constantDef.constructorArgs.size();
156209
if (valueCount.contains(constCount)) {
157210
continue;
158211
} else {
@@ -169,7 +222,7 @@ public EnumDef build() {
169222
}
170223
}
171224
if (!hasConstructor) {
172-
throw new IllegalStateException("Enum: " + name + " doesn't have a constructor for constant " + entry.getKey());
225+
throw new IllegalStateException("Enum: " + name + " doesn't have a matching constructor for constant " + constantDef.name);
173226
}
174227
}
175228
}

0 commit comments

Comments
 (0)