Skip to content

Commit 3a73c9b

Browse files
committed
Qute: fix type-safe fragments defined as top-level records
- this workarounds the inconsistency in ClassInfo#simpleName() from Jandex: smallrye/jandex#526 - also fixes quarkusio#47804
1 parent 119f8eb commit 3a73c9b

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
@@ -2061,9 +2061,13 @@ public class ItemResource {
20612061
[[type_safe_fragments]]
20622062
==== Type-safe Fragments
20632063

2064-
You can also define a type-safe <<fragments,fragment>> in your Java code.
2065-
A _native static_ method with the name that contains a dollar sign `$` denotes a method that represents a fragment of a type-safe template.
2066-
The name of the fragment is derived from the annotated method name.
2064+
You can also define a type-safe <<fragments,fragment>> of a type-safe template in your Java code.
2065+
There are two ways to define a type-safe fragment:
2066+
2067+
1. A _native static_ method annotated with `@CheckedTemplate`, with a name that contains a dollar sign `$`.
2068+
2. A Java record that implements `io.quarkus.qute.TemplateInstance` and its name contains a dollar sign `$`.
2069+
2070+
The name of the fragment is derived from the annotated member name.
20672071
The part before the last occurrence of a dollar sign `$` is the method name of the related type-safe template.
20682072
The part after the last occurrence of a dollar sign is the fragment identifier.
20692073
The strategy defined by the relevant `CheckedTemplate#defaultName()` is honored when constructing the defaulted names.
@@ -2082,6 +2086,9 @@ class Templates {
20822086
20832087
// defines a fragment of Templates#items() with identifier "item"
20842088
static native TemplateInstance items$item(Item item); <1>
2089+
2090+
// type-safe fragment as a Java record - functionally equivalent to the items$item() method above
2091+
record items$item(Item item) implements TemplateInstance {}
20852092
}
20862093
----
20872094
<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
@@ -543,7 +543,7 @@ private String getCheckedTemplateName(AnnotationTarget target, AnnotationInstanc
543543
} else {
544544
defaultName = nameValue.asString();
545545
}
546-
String name = target.kind() == Kind.METHOD ? target.asMethod().name() : target.asClass().simpleName();
546+
String name = getTargetName(target);
547547
if (checkedFragment) {
548548
// the name is the part before the last occurence of a dollar sign
549549
name = name.substring(0, name.lastIndexOf('$'));
@@ -558,7 +558,7 @@ private String getCheckedFragmentId(AnnotationTarget target, AnnotationInstance
558558
if (ignoreFragmentsValue != null && ignoreFragmentsValue.asBoolean()) {
559559
return null;
560560
}
561-
String name = target.kind() == Kind.METHOD ? target.asMethod().name() : target.asClass().simpleName();
561+
String name = getTargetName(target);
562562
// the id is the part after the last occurence of a dollar sign
563563
int idx = name.lastIndexOf('$');
564564
if (idx == -1 || idx == name.length()) {
@@ -576,6 +576,15 @@ private String getCheckedFragmentId(AnnotationTarget target, AnnotationInstance
576576
return defaultedName(defaultName, name.substring(idx + 1, name.length()));
577577
}
578578

579+
private String getTargetName(AnnotationTarget target) {
580+
if (target.kind() == Kind.METHOD) {
581+
return target.asMethod().name();
582+
}
583+
ClassInfo targetClass = target.asClass();
584+
return targetClass.nestingType() == NestingType.TOP_LEVEL ? targetClass.name().withoutPackagePrefix()
585+
: targetClass.simpleName();
586+
}
587+
579588
private String defaultedName(String defaultNameStrategy, String value) {
580589
switch (defaultNameStrategy) {
581590
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)