Skip to content

Commit 2ece968

Browse files
mkoubagsmet
authored andcommitted
Qute: fix type-safe fragments defined as top-level records
- this workarounds the inconsistency in ClassInfo#simpleName() from Jandex: smallrye/jandex#526 - also fixes #47804 (cherry picked from commit 3a73c9b)
1 parent 7b8a1e9 commit 2ece968

File tree

4 files changed

+41
-12
lines changed

4 files changed

+41
-12
lines changed

docs/src/main/asciidoc/qute-reference.adoc

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,9 +2057,13 @@ public class ItemResource {
20572057
[[type_safe_fragments]]
20582058
==== Type-safe Fragments
20592059

2060-
You can also define a type-safe <<fragments,fragment>> in your Java code.
2061-
A _native static_ method with the name that contains a dollar sign `$` denotes a method that represents a fragment of a type-safe template.
2062-
The name of the fragment is derived from the annotated method name.
2060+
You can also define a type-safe <<fragments,fragment>> of a type-safe template in your Java code.
2061+
There are two ways to define a type-safe fragment:
2062+
2063+
1. A _native static_ method annotated with `@CheckedTemplate`, with a name that contains a dollar sign `$`.
2064+
2. A Java record that implements `io.quarkus.qute.TemplateInstance` and its name contains a dollar sign `$`.
2065+
2066+
The name of the fragment is derived from the annotated member name.
20632067
The part before the last occurrence of a dollar sign `$` is the method name of the related type-safe template.
20642068
The part after the last occurrence of a dollar sign is the fragment identifier.
20652069
The strategy defined by the relevant `CheckedTemplate#defaultName()` is honored when constructing the defaulted names.
@@ -2078,6 +2082,9 @@ class Templates {
20782082
20792083
// defines a fragment of Templates#items() with identifier "item"
20802084
static native TemplateInstance items$item(Item item); <1>
2085+
2086+
// type-safe fragment as a Java record - functionally equivalent to the items$item() method above
2087+
record items$item(Item item) implements TemplateInstance {}
20812088
}
20822089
----
20832090
<1> Quarkus validates at build time that each template that corresponds to the `Templates#items()` contains a fragment with identifier `item`. Moreover, the parameters of the fragment method are validated too. In general, all type-safe expressions that are found in the fragment and that reference some data from the original/outer template require a specific parameter to be present.

extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ private String getCheckedTemplateName(AnnotationTarget target, AnnotationInstanc
541541
} else {
542542
defaultName = nameValue.asString();
543543
}
544-
String name = target.kind() == Kind.METHOD ? target.asMethod().name() : target.asClass().simpleName();
544+
String name = getTargetName(target);
545545
if (checkedFragment) {
546546
// the name is the part before the last occurence of a dollar sign
547547
name = name.substring(0, name.lastIndexOf('$'));
@@ -556,7 +556,7 @@ private String getCheckedFragmentId(AnnotationTarget target, AnnotationInstance
556556
if (ignoreFragmentsValue != null && ignoreFragmentsValue.asBoolean()) {
557557
return null;
558558
}
559-
String name = target.kind() == Kind.METHOD ? target.asMethod().name() : target.asClass().simpleName();
559+
String name = getTargetName(target);
560560
// the id is the part after the last occurence of a dollar sign
561561
int idx = name.lastIndexOf('$');
562562
if (idx == -1 || idx == name.length()) {
@@ -574,6 +574,15 @@ private String getCheckedFragmentId(AnnotationTarget target, AnnotationInstance
574574
return defaultedName(defaultName, name.substring(idx + 1, name.length()));
575575
}
576576

577+
private String getTargetName(AnnotationTarget target) {
578+
if (target.kind() == Kind.METHOD) {
579+
return target.asMethod().name();
580+
}
581+
ClassInfo targetClass = target.asClass();
582+
return targetClass.nestingType() == NestingType.TOP_LEVEL ? targetClass.name().withoutPackagePrefix()
583+
: targetClass.simpleName();
584+
}
585+
577586
private String defaultedName(String defaultNameStrategy, String value) {
578587
switch (defaultNameStrategy) {
579588
case CheckedTemplate.ELEMENT_NAME:

extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/records/TemplateRecordTest.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ public class TemplateRecordTest {
2929
@RegisterExtension
3030
static final QuarkusUnitTest config = new QuarkusUnitTest()
3131
.withApplicationRoot((jar) -> jar
32-
.addClasses(HelloInt.class, helloWorld.class)
32+
.addClasses(HelloInt.class, helloWorld.class, hello.class, hello$name.class, hello$top.class)
3333
.addAsResource(new StringAsset("Hello {val}!"),
3434
"templates/TemplateRecordTest/HelloInt.txt")
3535
.addAsResource(new StringAsset("Hello {name}!"),
3636
"templates/hello_world.txt")
37-
.addAsResource(new StringAsset("Hello {#fragment name}{name}{/fragment} and {foo}!"),
37+
.addAsResource(
38+
new StringAsset(
39+
"Hello {#fragment name}{name}{/fragment}::{#fragment top}{index}{/fragment} and {foo}!"),
3840
"templates/hello.txt")
3941
.addAsResource(new StringAsset("{alpha}:{bravo}:{charlie}"),
4042
"templates/TemplateRecordTest/multiParams.txt"));
@@ -72,14 +74,18 @@ public void testTemplateRecords() throws InterruptedException, ExecutionExceptio
7274

7375
assertEquals("Hello Lu!", new helloWorld("Lu").render());
7476

75-
hello hello = new hello("Ma", "bar");
77+
hello hello = new hello("Ma", "bar", 1);
7678
assertFalse(hello.getTemplate().isFragment());
77-
assertEquals("Hello Ma and bar!", hello.render());
79+
assertEquals("Hello Ma::1 and bar!", hello.render());
7880

79-
hello$name hello$name = new hello$name("Lu");
81+
hello$name hello$name = new hello$name("Lu", 1);
8082
assertTrue(hello$name.getTemplate().isFragment());
8183
assertEquals("Lu", hello$name.render());
8284

85+
hello$top hello$top = new hello$top("Lu", 1);
86+
assertTrue(hello$top.getTemplate().isFragment());
87+
assertEquals("1", hello$top.render());
88+
8389
assertEquals("15:true:foo", new multiParams(true, 15, "foo").render());
8490
assertThrows(IllegalArgumentException.class, () -> new multiParams(false, 50, null));
8591
}
@@ -92,11 +98,11 @@ record helloWorld(String name) implements TemplateInstance {
9298
}
9399

94100
@CheckedTemplate(basePath = "")
95-
record hello(String name, String foo) implements TemplateInstance {
101+
record hello(String name, String foo, int index) implements TemplateInstance {
96102
}
97103

98104
@CheckedTemplate(basePath = "")
99-
record hello$name(String name) implements TemplateInstance {
105+
record hello$name(String name, int index) implements TemplateInstance {
100106
}
101107

102108
record multiParams(boolean bravo, int alpha, String charlie) implements TemplateInstance {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.quarkus.qute.deployment.records;
2+
3+
import io.quarkus.qute.TemplateInstance;
4+
5+
public record hello$top(String name, int index) implements TemplateInstance {
6+
7+
}

0 commit comments

Comments
 (0)