- {count}. |
+ {service_count}. |
{#when service.status}
{#is SERVING}
diff --git a/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/managed-entities.html b/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/managed-entities.html
index c2dcc5e8d7d611..b606c7932f755b 100644
--- a/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/managed-entities.html
+++ b/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/managed-entities.html
@@ -25,7 +25,7 @@ Persistence Unit {pu.
{#for entity in pu.managedEntities}
- {count}. |
+ {entity_count}. |
{entity.className} |
{entity.tableName} |
diff --git a/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/named-queries.html b/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/named-queries.html
index 53bfd00e69a8fb..14f1425ce31693 100644
--- a/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/named-queries.html
+++ b/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/named-queries.html
@@ -28,7 +28,7 @@ Persistence Unit {pu.
{#for query in pu.allNamedQueries}
- {count}. |
+ {query_count}. |
{query.name} |
{query.query} |
{query.lockMode} |
diff --git a/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/persistence-units.html b/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/persistence-units.html
index e624bd56570bf5..1349bb6c6e09da 100644
--- a/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/persistence-units.html
+++ b/extensions/hibernate-orm/deployment/src/main/resources/dev-templates/persistence-units.html
@@ -32,37 +32,37 @@
-
+
Create Script
-
+
Copy
|
-
- {pu.createDDL}
+ |
+ {pu.createDDL}
|
-
+
Drop Script
-
+
Copy
|
-
- {pu.dropDDL}
+ |
+ {pu.dropDDL}
|
diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java
index 9f4b2ad3779203..2aac1a962aceec 100644
--- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java
+++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java
@@ -481,7 +481,7 @@ public String apply(String id) {
for (Expression param : params) {
if (param.hasTypeInfo()) {
Map results = new HashMap<>();
- QuteProcessor.validateNestedExpressions(exprEntry.getKey(), defaultBundleInterface,
+ QuteProcessor.validateNestedExpressions(config, exprEntry.getKey(), defaultBundleInterface,
results, excludes, incorrectExpressions, expression, index,
implicitClassToMembersUsed, templateIdToPathFun, generatedIdsToMatches,
checkedTemplate, lookupConfig, namedBeans, namespaceTemplateData,
diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java
index 6804ae0c9462bd..4e4b72d4a44ed7 100644
--- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java
+++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java
@@ -130,6 +130,9 @@ public class QuteProcessor {
private static final String CHECKED_TEMPLATE_REQUIRE_TYPE_SAFE = "requireTypeSafeExpressions";
private static final String CHECKED_TEMPLATE_BASE_PATH = "basePath";
+ private static final Set ITERATION_METADATA_KEYS = Set.of("count", "index", "indexParity", "hasNext", "odd",
+ "isOdd", "even", "isEven", "isLast", "isFirst");
+
private static final Function GETTER_FUN = new Function() {
@Override
public String apply(FieldInfo field) {
@@ -521,7 +524,8 @@ public String apply(String id) {
if (expression.isLiteral()) {
continue;
}
- Match match = validateNestedExpressions(templateAnalysis, null, new HashMap<>(), excludes, incorrectExpressions,
+ Match match = validateNestedExpressions(config, templateAnalysis, null, new HashMap<>(), excludes,
+ incorrectExpressions,
expression, index, implicitClassToMembersUsed, templateIdToPathFun, generatedIdsToMatches,
checkedTemplate, lookupConfig, namedBeans, namespaceTemplateData, regularExtensionMethods,
namespaceExtensionMethods);
@@ -581,7 +585,8 @@ static String buildIgnorePattern(Iterable names) {
return ignorePattern.toString();
}
- static Match validateNestedExpressions(TemplateAnalysis templateAnalysis, ClassInfo rootClazz, Map results,
+ static Match validateNestedExpressions(QuteConfig config, TemplateAnalysis templateAnalysis, ClassInfo rootClazz,
+ Map results,
List excludes, BuildProducer incorrectExpressions,
Expression expression, IndexView index,
Map> implicitClassToMembersUsed, Function templateIdToPathFun,
@@ -596,7 +601,7 @@ static Match validateNestedExpressions(TemplateAnalysis templateAnalysis, ClassI
if (part.isVirtualMethod()) {
for (Expression param : part.asVirtualMethod().getParameters()) {
if (!results.containsKey(param.toOriginalString())) {
- validateNestedExpressions(templateAnalysis, null, results, excludes,
+ validateNestedExpressions(config, templateAnalysis, null, results, excludes,
incorrectExpressions, param, index, implicitClassToMembersUsed, templateIdToPathFun,
generatedIdsToMatches, checkedTemplate, lookupConfig, namedBeans, namespaceTemplateData,
regularExtensionMethods, namespaceExtensionMethods);
@@ -641,14 +646,39 @@ static Match validateNestedExpressions(TemplateAnalysis templateAnalysis, ClassI
}
if (checkedTemplate != null && checkedTemplate.requireTypeSafeExpressions && !expression.hasTypeInfo()) {
- incorrectExpressions.produce(new IncorrectExpressionBuildItem(expression.toOriginalString(),
- "Only type-safe expressions are allowed in the checked template defined via: "
- + checkedTemplate.method.declaringClass().name() + "."
- + checkedTemplate.method.name()
- + "(); an expression must be based on a checked template parameter "
- + checkedTemplate.bindings.keySet()
- + ", or bound via a param declaration, or the requirement must be relaxed via @CheckedTemplate(requireTypeSafeExpressions = false)",
- expression.getOrigin()));
+ if (!expression.hasNamespace() && expression.getParts().size() == 1
+ && ITERATION_METADATA_KEYS.contains(expression.getParts().get(0).getName())) {
+ String prefixInfo;
+ if (config.iterationMetadataPrefix
+ .equals(LoopSectionHelper.Factory.ITERATION_METADATA_PREFIX_ALIAS_UNDERSCORE)) {
+ prefixInfo = String.format(
+ "based on the iteration alias, i.e. the correct key should be something like {it_%1$s} or {element_%1$s}",
+ expression.getParts().get(0).getName());
+ } else if (config.iterationMetadataPrefix
+ .equals(LoopSectionHelper.Factory.ITERATION_METADATA_PREFIX_ALIAS_QM)) {
+ prefixInfo = String.format(
+ "based on the iteration alias, i.e. the correct key should be something like {it?%1$s} or {element?%1$s}",
+ expression.getParts().get(0).getName());
+ } else {
+ prefixInfo = ": " + config.iterationMetadataPrefix + ", i.e. the correct key should be: "
+ + config.iterationMetadataPrefix + expression.getParts().get(0).getName();
+ }
+ incorrectExpressions.produce(new IncorrectExpressionBuildItem(expression.toOriginalString(),
+ "An invalid iteration metadata key is probably used\n\t- The configured iteration metadata prefix is "
+ + prefixInfo
+ + "\n\t- You can configure the prefix via the io.quarkus.qute.iteration-metadata-prefix configuration property",
+ expression.getOrigin()));
+ } else {
+ incorrectExpressions.produce(new IncorrectExpressionBuildItem(expression.toOriginalString(),
+ "Only type-safe expressions are allowed in the checked template defined via: "
+ + checkedTemplate.method.declaringClass().name() + "."
+ + checkedTemplate.method.name()
+ + "(); an expression must be based on a checked template parameter "
+ + checkedTemplate.bindings.keySet()
+ + ", or bound via a param declaration, or the requirement must be relaxed via @CheckedTemplate(requireTypeSafeExpressions = false)",
+ expression.getOrigin()));
+
+ }
return putResult(match, results, expression);
}
diff --git a/extensions/qute/deployment/src/main/resources/dev-templates/preview.html b/extensions/qute/deployment/src/main/resources/dev-templates/preview.html
index a0c2ae909fb7a5..47c59d988aa049 100644
--- a/extensions/qute/deployment/src/main/resources/dev-templates/preview.html
+++ b/extensions/qute/deployment/src/main/resources/dev-templates/preview.html
@@ -61,7 +61,7 @@
{#if template.parameters}
testData{dataCount} = '{';
{#each template.parameters}
- testData{dataCount} += '\n // Template parameter {count}: {it.value.raw}\n';
+ testData{dataCount} += '\n // Template parameter {template_count}: {it.value.raw}\n';
testData{dataCount} += ' "{it.key}" : null';
{#if hasNext}
testData{dataCount} += ',';
diff --git a/extensions/qute/deployment/src/main/resources/dev-templates/templates.html b/extensions/qute/deployment/src/main/resources/dev-templates/templates.html
index c26e52159f2aa9..5cdf5a88db0680 100644
--- a/extensions/qute/deployment/src/main/resources/dev-templates/templates.html
+++ b/extensions/qute/deployment/src/main/resources/dev-templates/templates.html
@@ -19,7 +19,7 @@
{#for template in info:devQuteInfos.templates}
- {count}. |
+ {template_count}. |
{template.path}
|
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/CheckedTemplateRequireTypeSafeTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/CheckedTemplateRequireTypeSafeTest.java
index 43247ae7b86bbe..6d7ecf8a20ec3d 100644
--- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/CheckedTemplateRequireTypeSafeTest.java
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/CheckedTemplateRequireTypeSafeTest.java
@@ -29,8 +29,11 @@ public class CheckedTemplateRequireTypeSafeTest {
+ "{any} "
+ "{inject:fool.getJoke(identifier)} "
+ "{#each name.chars.iterator}"
- + "{! {index} is not considered an error because the binding is registered by the loop section !}"
- + "{index}. {it}"
+ // {it_index} is not considered an error because the binding is registered by the loop section !}
+ + "{it_index}."
+ // however, {index} is an error
+ + "{index}"
+ + "{it}"
+ "{/each}"),
"templates/CheckedTemplateRequireTypeSafeTest/hola.txt"))
.assertException(t -> {
@@ -44,9 +47,10 @@ public class CheckedTemplateRequireTypeSafeTest {
e = e.getCause();
}
assertNotNull(te);
- assertTrue(te.getMessage().contains("Found template problems (2)"), te.getMessage());
+ assertTrue(te.getMessage().contains("Found template problems (3)"), te.getMessage());
assertTrue(te.getMessage().contains("any"), te.getMessage());
assertTrue(te.getMessage().contains("identifier"), te.getMessage());
+ assertTrue(te.getMessage().contains("index"), te.getMessage());
});
@Test
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ParamDeclarationTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ParamDeclarationTest.java
index 82c81d4fd9d1c2..a298086b7bb8f1 100644
--- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ParamDeclarationTest.java
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ParamDeclarationTest.java
@@ -21,7 +21,7 @@ public class ParamDeclarationTest {
.addAsResource(new StringAsset("{@io.quarkus.qute.deployment.typesafe.Movie movie}"
+ "{movie.mainCharacters.size}: {#for character in movie.mainCharacters}"
+ "{character}"
- + "{#if hasNext}, {/}"
+ + "{#if character_hasNext}, {/}"
+ "{/}"), "templates/movie.html"));
@Inject
diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java
index e154b2c1e25e91..6ade3b94b19e54 100644
--- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java
+++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java
@@ -120,6 +120,9 @@ public EngineProducer(QuteContext context, QuteConfig config, QuteRuntimeConfig
// Remove standalone lines if desired
builder.removeStandaloneLines(runtimeConfig.removeStandaloneLines);
+ // Iteration metadata prefix
+ builder.iterationMetadataPrefix(config.iterationMetadataPrefix);
+
// Allow anyone to customize the builder
builderReady.fire(builder);
diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/QuteConfig.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/QuteConfig.java
index f6a2d7c92159ce..370c0555c0402f 100644
--- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/QuteConfig.java
+++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/QuteConfig.java
@@ -44,4 +44,21 @@ public class QuteConfig {
@ConfigItem
public Optional> typeCheckExcludes;
+ /**
+ * The prefix is used to access the iteration metadata inside a loop section.
+ *
+ * A valid prefix consists of alphanumeric characters and underscores.
+ * Three special constants can be used:
+ *
+ * - {@code } - the alias of an iterated element suffixed with an underscore is used, e.g. {@code item_hasNext}
+ * and {@code it_count}
+ * - {@code } - the alias of an iterated element suffixed with a question mark is used, e.g. {@code item?hasNext}
+ * and {@code it?count}
+ * - {@code } - no prefix is used, e.g. {@code hasNext} and {@code count}
+ *
+ * By default, the {@code } constant is set.
+ */
+ @ConfigItem(defaultValue = "")
+ public String iterationMetadataPrefix;
+
}
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html
index 22e0b1d52ad2dc..0cba8e7850b777 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/resources/dev-templates/scores.html
@@ -147,16 +147,16 @@
- |