Skip to content

Commit 30e9cc7

Browse files
authored
Enhance UType (#45)
* enhance UType even further * Update VisitorWriter.java
1 parent 52cba47 commit 30e9cc7

File tree

5 files changed

+132
-55
lines changed

5 files changed

+132
-55
lines changed

blackbox-test-prism/src/main/java/io/avaje/prisms/test/context/TypeUse.java renamed to blackbox-test-prism/src/main/java/io/avaje/prisms/test/context/UTypeTester.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import jakarta.validation.constraints.NotEmpty;
1212

1313
@TestAnnotation
14-
public class TypeUse<T extends CommonInterface & CommonInterface2> {
14+
public class UTypeTester<T extends CommonInterface & CommonInterface2> {
1515

1616
@NotEmpty @NotBlank
1717
Map<
@@ -25,7 +25,5 @@ public class TypeUse<T extends CommonInterface & CommonInterface2> {
2525
List<? extends Object> extendWild;
2626
List<?> wild;
2727

28-
<T2 extends CommonInterface> T2 test() {
29-
return null;
30-
}
28+
void testVoid(@NotEmpty(groups = int[].class) int[] param) {}
3129
}

blackbox-test-prism/src/test/java/io/avaje/prisms/test/TestProcessor.java

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
import javax.lang.model.element.Element;
1515
import javax.lang.model.element.ExecutableElement;
1616
import javax.lang.model.element.TypeElement;
17+
import javax.lang.model.type.TypeKind;
1718
import javax.lang.model.util.ElementFilter;
19+
import javax.management.relation.InvalidRelationIdException;
1820

1921
@SupportedAnnotationTypes("io.avaje.prisms.test.TestAnnotation")
2022
public class TestProcessor extends AbstractProcessor {
@@ -47,21 +49,53 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
4749

4850
assert !elements.isEmpty();
4951

50-
UtypeTests();
52+
utypeTests();
5153

5254
return false;
5355
}
5456

55-
private void UtypeTests() {
56-
var typeUse = APContext.typeElement("io.avaje.prisms.test.context.TypeUse");
57+
private void utypeTests() {
58+
var testElement = APContext.typeElement("io.avaje.prisms.test.context.UTypeTester");
5759

58-
var typeUseFields =
59-
ElementFilter.fieldsIn(typeUse.getEnclosedElements()).stream()
60+
intersectionTypes(testElement);
61+
62+
var methods = ElementFilter.methodsIn(testElement.getEnclosedElements());
63+
var fields =
64+
ElementFilter.fieldsIn(testElement.getEnclosedElements()).stream()
6065
.map(Element::asType)
6166
.map(UType::parse)
6267
.collect(toList());
68+
typeUseTests(fields);
69+
70+
var testVoid = methods.get(0);
71+
var arrayParam = UType.parse(testVoid.getParameters().get(0).asType());
72+
assertThat(arrayParam.kind()).isEqualTo(TypeKind.ARRAY);
73+
assertThat(arrayParam.full())
74+
.isEqualTo("@jakarta.validation.constraints.NotEmpty(groups={int[].class}) int[]");
75+
assertThat(arrayParam.mainType()).isEqualTo("int[]");
76+
var arrayType = arrayParam.param0();
77+
assertThat(arrayType.shortWithoutAnnotations()).isEqualTo("int");
78+
assertThat(arrayType.kind()).isEqualTo(TypeKind.INT);
79+
80+
var voidType = UType.parse(testVoid.getReturnType());
81+
assertThat(voidType.kind()).isEqualTo(TypeKind.VOID);
82+
assertThat(voidType.full()).isEqualTo("void");
83+
assertThat(voidType.mainType()).isEqualTo("void");
84+
}
85+
86+
private void typeUseTests(List<UType> typeUseFields) {
6387

6488
var map = typeUseFields.get(0);
89+
90+
assertThat(map.importTypes())
91+
.contains(
92+
"jakarta.validation.constraints.NotBlank",
93+
"java.util.Map",
94+
"jakarta.validation.constraints.NotEmpty",
95+
"io.avaje.prisms.test.context.V4Rusty");
96+
97+
assertThat(map.mainType()).isEqualTo("java.util.Map");
98+
6599
assertThat(map.full())
66100
.isEqualTo(
67101
"@jakarta.validation.constraints.NotEmpty @jakarta.validation.constraints.NotBlank java.util.Map<@jakarta.validation.constraints.NotBlank(groups={io.avaje.prisms.test.context.V4Rusty.Weapon.class}) java.lang.String, @jakarta.validation.constraints.NotEmpty(groups={io.avaje.prisms.test.context.V4Rusty.Weapon.class}) java.util.Map<io.avaje.prisms.test.context.V4Rusty.@jakarta.validation.constraints.NotBlank(groups={io.avaje.prisms.test.context.V4Rusty.Weapon.class}) Weapon, io.avaje.prisms.test.context.@jakarta.validation.constraints.NotBlank V4Rusty>>");
@@ -79,19 +113,29 @@ private void UtypeTests() {
79113

80114
var list = typeUseFields.get(1);
81115
assertThat(list.full()).isEqualTo("java.util.List<T>");
82-
assertThat(list.componentTypes().get(0).componentTypes().get(0).componentTypes().size())
83-
.isEqualTo(2);
116+
assertThat(list.componentTypes().get(0).componentTypes().get(0).componentTypes()).hasSize(2);
117+
}
84118

85-
var methods =
86-
ElementFilter.methodsIn(typeUse.getEnclosedElements()).stream()
87-
.map(ExecutableElement::getTypeParameters)
88-
.flatMap(List::stream)
89-
.map(Element::asType)
90-
.map(
91-
t -> {
92-
System.err.println();
93-
return UType.parse(t);
94-
})
95-
.collect(toList());
119+
private void intersectionTypes(TypeElement typeUse) {
120+
var typeUseUType = UType.parse(typeUse.asType());
121+
122+
assertThat(typeUseUType).isEqualTo(UType.parse(typeUse.asType()));
123+
assertThat(typeUseUType.kind()).isEqualTo(TypeKind.DECLARED);
124+
assertThat(typeUseUType.full()).isEqualTo("io.avaje.prisms.test.context.UTypeTester<T>");
125+
assertThat(typeUseUType.shortType()).isEqualTo("UTypeTester<T>");
126+
assertThat(typeUseUType.mainType()).isEqualTo("io.avaje.prisms.test.context.UTypeTester");
127+
128+
final var typeVar = typeUseUType.param0();
129+
assertThat(typeVar.mainType()).isEqualTo("T");
130+
131+
assertThat(typeVar.kind()).isEqualTo(TypeKind.TYPEVAR);
132+
assertThat(typeVar.componentTypes()).hasSize(1);
133+
var intersection = typeVar.param0();
134+
assertThat(intersection.mainType()).isNull();
135+
136+
assertThat(intersection.kind()).isEqualTo(TypeKind.INTERSECTION);
137+
138+
assertThat(intersection.param0().toString()).isEqualTo("io.avaje.prisms.test.CommonInterface");
139+
assertThat(intersection.param1().full()).isEqualTo("io.avaje.prisms.test.CommonInterface2");
96140
}
97141
}

prism-core/src/main/java/io/avaje/prism/internal/ModuleInfoReaderWriter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public static void write(PrintWriter out, String packageName) {
2525
+ "import java.util.regex.Matcher;\n"
2626
+ "import java.util.regex.Pattern;\n"
2727
+ "\n"
28+
+ "import javax.annotation.processing.Generated;\n"
2829
+ "import javax.lang.model.element.ModuleElement;\n"
2930
+ "import javax.lang.model.element.ModuleElement.DirectiveKind;\n"
3031
+ "import javax.lang.model.element.ModuleElement.RequiresDirective;\n"
@@ -37,6 +38,7 @@ public static void write(PrintWriter out, String packageName) {
3738
+ " * in some situations, so this class helps parse the module source file and get the relevant\n"
3839
+ " * information without breaking anything.\n"
3940
+ " */\n"
41+
+ "@Generated(\"avaje-prism-generator\")\n"
4042
+ "public class ModuleInfoReader {\n"
4143
+ "\n"
4244
+ " private static final String SPLIT_PATTERN = \"\\\\s*,\\\\s*\";\n"

prism-core/src/main/java/io/avaje/prism/internal/UTypeWriter.java

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,28 +64,34 @@ public static void write(PrintWriter out, String packageName) {
6464
+ " */\n"
6565
+ " String shortType();\n"
6666
+ "\n"
67-
+ " /** Return the first generic parameter. */\n"
67+
+ " /**\n"
68+
+ " * Return the first generic parameter.\n"
69+
+ " *\n"
70+
+ " * @see UType#componentTypes\n"
71+
+ " */\n"
6872
+ " default UType param0() {\n"
6973
+ " return null;\n"
7074
+ " }\n"
7175
+ "\n"
72-
+ " /** Return the second generic parameter. */\n"
76+
+ " /**\n"
77+
+ " * Return the second componentType.\n"
78+
+ " *\n"
79+
+ " * @see UType#componentTypes\n"
80+
+ " */\n"
7381
+ " default UType param1() {\n"
7482
+ " return null;\n"
7583
+ " }\n"
7684
+ "\n"
7785
+ " /**\n"
7886
+ " * Retrieve the component types associated with this mirror.\n"
7987
+ " *\n"
80-
+ " * <p>The annotated class must conform to the service provider specification. Specifically, it\n"
81-
+ " * must:\n"
82-
+ " *\n"
8388
+ " * <ul>\n"
84-
+ " * <li>{@code TypeKind.ARRAY}: will contain the array componentType\n"
85-
+ " * <li>{@code TypeKind.DECLARED}: will contain the generic parameters\n"
86-
+ " * <li>{@code TypeKind.TYPEVAR}: will contain the upper bound for the type variable\n"
87-
+ " * <li>{@code TypeKind.WILDCARD}: will contain the extends bound or super bound\n"
88-
+ " * <li>{@code TypeKind.INTERSECTION}: will contain the bounds of the intersection\n"
89+
+ " * <li>{@link TypeKind#ARRAY}: will contain the array componentType\n"
90+
+ " * <li>{@link TypeKind#DECLARED}: will contain the generic parameters\n"
91+
+ " * <li>{@link TypeKind#TYPEVAR}: will contain the upper bound for the type variable\n"
92+
+ " * <li>{@link TypeKind#WILDCARD}: will contain the extends bound or super bound\n"
93+
+ " * <li>{@link TypeKind#INTERSECTION}: will contain the bounds of the intersection\n"
94+
+ " * <li>{@link TypeKind#UNION}: will contain the alternative types\n"
8995
+ " * </ul>\n"
9096
+ " *\n"
9197
+ " * @return the component types\n"
@@ -94,11 +100,7 @@ public static void write(PrintWriter out, String packageName) {
94100
+ " return List.of();\n"
95101
+ " }\n"
96102
+ "\n"
97-
+ " /**\n"
98-
+ " * The kind of the type mirror used to create this Utype.\n"
99-
+ " *\n"
100-
+ " * @return the typekind\n"
101-
+ " */\n"
103+
+ " /** The {@link TypeKind} of the type mirror used to create this Utype. */\n"
102104
+ " TypeKind kind();\n"
103105
+ "\n"
104106
+ " /**\n"
@@ -113,15 +115,20 @@ public static void write(PrintWriter out, String packageName) {
113115
+ " /**\n"
114116
+ " * Return the annotation mirrors directly on the type.\n"
115117
+ " *\n"
118+
+ " * <p>For a {@code UType} representing {@code @NotEmpty Map<@Notblank String, Object>} you will\n"
119+
+ " * get mirrors for {@code @NotEmpty} only\n"
120+
+ " *\n"
116121
+ " * @return the annotations directly present\n"
117122
+ " */\n"
118123
+ " default List<AnnotationMirror> annotations() {\n"
119124
+ " return List.of();\n"
120125
+ " }\n"
121126
+ "\n"
122127
+ " /**\n"
123-
+ " * Return the annotation mirrors directly on the type and in within generic type use. e.g. for\n"
124-
+ " * {@code @NotEmpty Map<@Notblank String, Object>} you will get all the annotations not just\n"
128+
+ " * Return the annotation mirrors directly on the type and in within generic type use.\n"
129+
+ " *\n"
130+
+ " * <p>For a {@code UType} representing {@code @NotEmpty Map<@Notblank String, Object>} you will\n"
131+
+ " * get mirrors for {@code @NotEmpty} and {@code @Notblank}\n"
125132
+ " *\n"
126133
+ " * @return all annotations present on this type\n"
127134
+ " */\n"
@@ -145,8 +152,10 @@ public static void write(PrintWriter out, String packageName) {
145152
+ " */\n"
146153
+ " default String shortWithoutAnnotations() {\n"
147154
+ " return ProcessorUtils.trimAnnotations(shortType()).replace(\",\", \", \");\n"
148-
+ " }\n"
149-
+ "}\n"
150-
+ "");
155+
+ " }\n\n"
156+
+ " /** Compare whether the current full() type is identical to the given UType's full() type */\n"
157+
+ " @Override\n"
158+
+ " boolean equals(Object other);"
159+
+ "}");
151160
}
152161
}

prism-core/src/main/java/io/avaje/prism/internal/VisitorWriter.java

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public static void write(PrintWriter out, String packageName) {
2121
+ "import java.util.List;\n"
2222
+ "import java.util.Locale;\n"
2323
+ "import java.util.Map;\n"
24+
+ "import java.util.Objects;\n"
2425
+ "import java.util.Set;\n"
2526
+ "\n"
2627
+ "import javax.annotation.processing.Generated;\n"
@@ -32,15 +33,7 @@ public static void write(PrintWriter out, String packageName) {
3233
+ "import javax.lang.model.type.DeclaredType;\n"
3334
+ "import javax.lang.model.type.ErrorType;\n"
3435
+ "import javax.lang.model.type.ExecutableType;\n"
35-
+ "import javax.lang.model.type.IntersectionType;\n"
36-
+ "import javax.lang.model.type.NoType;\n"
37-
+ "import javax.lang.model.type.NullType;\n"
38-
+ "import javax.lang.model.type.PrimitiveType;\n"
39-
+ "import javax.lang.model.type.TypeKind;\n"
40-
+ "import javax.lang.model.type.TypeMirror;\n"
41-
+ "import javax.lang.model.type.TypeVariable;\n"
42-
+ "import javax.lang.model.type.UnionType;\n"
43-
+ "import javax.lang.model.type.WildcardType;\n"
36+
+ "import javax.lang.model.type.*;\n"
4437
+ "import javax.lang.model.util.AbstractTypeVisitor9;\n"
4538
+ "\n"
4639
+ "@Generated(\"avaje-prism-generator\")\n"
@@ -346,6 +339,7 @@ public static void write(PrintWriter out, String packageName) {
346339
+ " final var extendsBound = t.getExtendsBound();\n"
347340
+ " final var superBound = t.getSuperBound();\n"
348341
+ " kind = t.getKind();\n"
342+
+ " mainType = \"?\";\n"
349343
+ " for (final var ta : t.getAnnotationMirrors()) {\n"
350344
+ " p.append(ta.toString()).append(\" \");\n"
351345
+ " }\n"
@@ -363,12 +357,14 @@ public static void write(PrintWriter out, String packageName) {
363357
+ "\n"
364358
+ " @Override\n"
365359
+ " public StringBuilder visitExecutable(ExecutableType t, StringBuilder p) {\n"
366-
+ " throw new UnsupportedOperationException(\"don't support executables\");\n"
360+
+ " throw new UnsupportedOperationException(\"Does not support ExecutableType\");\n"
367361
+ " }\n"
368362
+ "\n"
369363
+ " @Override\n"
370364
+ " public StringBuilder visitNoType(NoType t, StringBuilder p) {\n"
371-
+ " throw new UnsupportedOperationException(\"don't support NoType\");\n"
365+
+ " kind = t.getKind();\n"
366+
+ " mainType = t.toString();\n"
367+
+ " return p.append(mainType);\n"
372368
+ " }\n"
373369
+ "\n"
374370
+ " @Override\n"
@@ -388,8 +384,36 @@ public static void write(PrintWriter out, String packageName) {
388384
+ "\n"
389385
+ " @Override\n"
390386
+ " public StringBuilder visitUnion(UnionType t, StringBuilder p) {\n"
391-
+ " throw new UnsupportedOperationException();\n"
387+
+ " kind = t.getKind();\n"
388+
+ " boolean first = true;\n"
389+
+ " for (final var b : t.getAlternatives()) {\n"
390+
+ " if (first) {\n"
391+
+ " first = false;\n"
392+
+ " } else {\n"
393+
+ " p.append(\" | \");\n"
394+
+ " }\n"
395+
+ " child(b, p);\n"
396+
+ " }\n"
397+
+ " return p;\n"
392398
+ " }\n"
393-
+ "}\n");
399+
+ "\n"
400+
+ " @Override\n"
401+
+ " public String toString() {\n"
402+
+ " return fullType;\n"
403+
+ " }\n"
404+
+ "\n"
405+
+ " @Override\n"
406+
+ " public int hashCode() {\n"
407+
+ " return Objects.hash(fullType);\n"
408+
+ " }\n"
409+
+ "\n"
410+
+ " @Override\n"
411+
+ " public boolean equals(Object obj) {\n"
412+
+ " if (this == obj) return true;\n"
413+
+ " if (obj == null || getClass() != obj.getClass()) return false;\n"
414+
+ " TypeMirrorVisitor other = (TypeMirrorVisitor) obj;\n"
415+
+ " return Objects.equals(fullType, other.fullType);\n"
416+
+ " }"
417+
+ "}");
394418
}
395419
}

0 commit comments

Comments
 (0)