ignores;
private boolean ignoreSuperclasses;
private boolean properties;
+ private String namespace;
+ private AnnotationTarget annotationTarget;
public TemplateDataBuilder() {
ignores = new ArrayList<>();
ignoreSuperclasses = false;
properties = false;
+ namespace = TemplateData.UNDERSCORED_FQCN;
}
/**
@@ -46,6 +53,16 @@ public TemplateDataBuilder properties(boolean value) {
return this;
}
+ public TemplateDataBuilder namespace(String value) {
+ namespace = Objects.requireNonNull(value);
+ return this;
+ }
+
+ public TemplateDataBuilder annotationTarget(AnnotationTarget value) {
+ annotationTarget = Objects.requireNonNull(value);
+ return this;
+ }
+
public AnnotationInstance build() {
AnnotationValue ignoreValue;
if (ignores.isEmpty()) {
@@ -60,8 +77,11 @@ public AnnotationInstance build() {
AnnotationValue propertiesValue = AnnotationValue.createBooleanValue(ValueResolverGenerator.PROPERTIES, properties);
AnnotationValue ignoreSuperclassesValue = AnnotationValue.createBooleanValue(ValueResolverGenerator.IGNORE_SUPERCLASSES,
ignoreSuperclasses);
- return AnnotationInstance.create(ValueResolverGenerator.TEMPLATE_DATA, null,
- new AnnotationValue[] { ignoreValue, propertiesValue, ignoreSuperclassesValue });
+ AnnotationValue namespaceValue = AnnotationValue.createStringValue("namespace", namespace);
+ AnnotationValue targetValue = AnnotationValue.createClassValue("target",
+ Type.create(ValueResolverGenerator.TEMPLATE_DATA, Kind.CLASS));
+ return AnnotationInstance.create(ValueResolverGenerator.TEMPLATE_DATA, annotationTarget,
+ new AnnotationValue[] { targetValue, ignoreValue, propertiesValue, ignoreSuperclassesValue, namespaceValue });
}
}
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumIgnoredTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumIgnoredTest.java
new file mode 100644
index 0000000000000..765737622afc8
--- /dev/null
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumIgnoredTest.java
@@ -0,0 +1,47 @@
+package io.quarkus.qute.deployment.enums;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.qute.Engine;
+import io.quarkus.qute.TemplateData;
+import io.quarkus.qute.TemplateEnum;
+import io.quarkus.qute.TemplateException;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class TemplateEnumIgnoredTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot(root -> root
+ .addClasses(TransactionType.class));
+
+ @Inject
+ Engine engine;
+
+ @Test
+ public void testTemplateData() {
+ assertEquals("FOO",
+ engine.parse("{io_quarkus_qute_deployment_enums_TemplateEnumIgnoredTest_TransactionType:FOO}").render());
+ assertThatExceptionOfType(TemplateException.class)
+ .isThrownBy(() -> engine.parse("{TransactionType:FOO}", null, "bar").render())
+ .withMessage(
+ "No namespace resolver found for [TransactionType] in expression {TransactionType:FOO} in template bar on line 1");
+
+ }
+
+ @TemplateEnum // ignored
+ @TemplateData(namespace = TemplateData.UNDERSCORED_FQCN)
+ public static enum TransactionType {
+
+ FOO,
+ BAR
+
+ }
+
+}
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumInvalidTargetTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumInvalidTargetTest.java
new file mode 100644
index 0000000000000..c0ac7eefd33ab
--- /dev/null
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumInvalidTargetTest.java
@@ -0,0 +1,41 @@
+package io.quarkus.qute.deployment.enums;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.qute.Engine;
+import io.quarkus.qute.TemplateEnum;
+import io.quarkus.qute.TemplateException;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class TemplateEnumInvalidTargetTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot(root -> root
+ .addClasses(Transactions.class));
+
+ @Inject
+ Engine engine;
+
+ @Test
+ public void testTemplateEnum() {
+ assertThatExceptionOfType(TemplateException.class)
+ .isThrownBy(() -> engine.parse("{Transactions:VAL}", null, "bar").render())
+ .withMessage(
+ "No namespace resolver found for [Transactions] in expression {Transactions:VAL} in template bar on line 1");
+
+ }
+
+ @TemplateEnum // ignored
+ public static class Transactions {
+
+ public static final int VAL = 10;
+
+ }
+
+}
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumNamespaceValidationTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumNamespaceValidationTest.java
new file mode 100644
index 0000000000000..6527dcd4afdb4
--- /dev/null
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumNamespaceValidationTest.java
@@ -0,0 +1,56 @@
+package io.quarkus.qute.deployment.enums;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.qute.TemplateData;
+import io.quarkus.qute.TemplateEnum;
+import io.quarkus.qute.TemplateException;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class TemplateEnumNamespaceValidationTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot((jar) -> jar
+ .addClasses(TransactionType.class, Transactions.class))
+ .assertException(t -> {
+ Throwable e = t;
+ TemplateException te = null;
+ while (e != null) {
+ if (e instanceof TemplateException) {
+ te = (TemplateException) e;
+ break;
+ }
+ e = e.getCause();
+ }
+ assertNotNull(te);
+ assertTrue(te.getMessage().contains(
+ "The namespace [TransactionType] is defined by multiple @TemplateData and/or @TemplateEnum annotations"),
+ te.getMessage());
+ });
+
+ @Test
+ public void test() {
+ fail();
+ }
+
+ // namespace is TransactionType
+ @TemplateEnum
+ public static enum TransactionType {
+
+ FOO,
+ BAR
+
+ }
+
+ @TemplateData(namespace = "TransactionType")
+ public static class Transactions {
+
+ }
+
+}
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumTest.java
new file mode 100644
index 0000000000000..c8cf86ed2b530
--- /dev/null
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumTest.java
@@ -0,0 +1,42 @@
+package io.quarkus.qute.deployment.enums;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import javax.inject.Inject;
+
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.qute.Template;
+import io.quarkus.qute.TemplateEnum;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class TemplateEnumTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot((jar) -> jar
+ .addClasses(TransactionType.class)
+ .addAsResource(new StringAsset(
+ "{#if tx == TransactionType:FOO}OK{/if}::{TransactionType:BAR}::{TransactionType:values[0]}"),
+ "templates/bar.txt"));
+
+ @Inject
+ Template bar;
+
+ @Test
+ public void testTemplateData() {
+ assertEquals("OK::BAR::FOO", bar.data("tx", TransactionType.FOO).render());
+ }
+
+ // namespace is TransactionType
+ @TemplateEnum
+ public static enum TransactionType {
+
+ FOO,
+ BAR
+
+ }
+
+}
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumValidationFailureTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumValidationFailureTest.java
new file mode 100644
index 0000000000000..7d5dd2270bc8a
--- /dev/null
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumValidationFailureTest.java
@@ -0,0 +1,56 @@
+package io.quarkus.qute.deployment.enums;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.qute.TemplateEnum;
+import io.quarkus.qute.TemplateException;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class TemplateEnumValidationFailureTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot(root -> root
+ .addClasses(TransactionType.class)
+ .addAsResource(new StringAsset(
+ "{TransactionType:FOO}{TransactionType:BAR.scores}"),
+ "templates/foo.txt"))
+ .assertException(t -> {
+ Throwable e = t;
+ TemplateException te = null;
+ while (e != null) {
+ if (e instanceof TemplateException) {
+ te = (TemplateException) e;
+ break;
+ }
+ e = e.getCause();
+ }
+ assertNotNull(te);
+ assertTrue(te.getMessage().contains("Found template problems (1)"), te.getMessage());
+ assertTrue(te.getMessage().contains("TransactionType:BAR.scores"), te.getMessage());
+ });
+
+ @Test
+ public void test() {
+ fail();
+ }
+
+ @TemplateEnum
+ public static enum TransactionType {
+
+ FOO,
+ BAR;
+
+ public int getScore() {
+ return 42;
+ }
+
+ }
+
+}
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumValidationSuccessTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumValidationSuccessTest.java
new file mode 100644
index 0000000000000..382ae1cfca9d1
--- /dev/null
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/enums/TemplateEnumValidationSuccessTest.java
@@ -0,0 +1,45 @@
+package io.quarkus.qute.deployment.enums;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import javax.inject.Inject;
+
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.qute.Template;
+import io.quarkus.qute.TemplateEnum;
+import io.quarkus.test.QuarkusUnitTest;
+
+public class TemplateEnumValidationSuccessTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .withApplicationRoot(root -> root
+ .addClasses(TransactionType.class)
+ .addAsResource(new StringAsset(
+ "{TransactionType:FOO}::{TransactionType:BAR.score}"),
+ "templates/foo.txt"));
+
+ @Inject
+ Template foo;
+
+ @Test
+ public void testEnum() {
+ assertEquals("FOO::42", foo.render());
+ }
+
+ @TemplateEnum
+ public static enum TransactionType {
+
+ FOO,
+ BAR;
+
+ public int getScore() {
+ return 42;
+ }
+
+ }
+
+}
diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/TemplateEnum.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/TemplateEnum.java
new file mode 100644
index 0000000000000..a7c737b93b2f3
--- /dev/null
+++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/TemplateEnum.java
@@ -0,0 +1,26 @@
+package io.quarkus.qute;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is functionally equivalent to {@code @TemplateData(namespace = TemplateData.SIMPLENAME)}, i.e. a namespace
+ * resolver is automatically generated for the target enum. The simple name of the target enum is used as the namespace. The
+ * generated namespace resolver can be used to access enum constants, static methods, etc.
+ *
+ * If an enum also declares the {@link TemplateData} annotation or is specified by any {@link TemplateData#target()} then the
+ * {@link TemplateEnum} annotation is ignored.
+ *
+ * {@link TemplateEnum} declared on non-enum classes is ignored.
+ *
+ * @see TemplateData
+ * @see NamespaceResolver
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+public @interface TemplateEnum {
+
+}