From 811534a78b07230a93ce98f12ba255c3d25e756c Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 4 Jul 2024 15:04:02 +0200 Subject: [PATCH] Qute: introduce ValidationParserHookBuildItem - that can be used to hook into the parser logic during validation at build time --- .../qute/deployment/QuteProcessor.java | 11 ++- .../ValidationParserHookBuildItem.java | 30 ++++++++ .../parserhook/CustomParserHookBuildTest.java | 6 -- .../parserhook/ValidationHookTest.java | 76 +++++++++++++++++++ 4 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java create mode 100644 extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java 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 68ddc6e7154be..b1da22bc75976 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 @@ -591,8 +591,11 @@ private boolean isNotLocatedByCustomTemplateLocator( @BuildStep TemplatesAnalysisBuildItem analyzeTemplates(List templatePaths, - TemplateFilePathsBuildItem filePaths, List checkedTemplates, - List messageBundleMethods, List globals, QuteConfig config, + TemplateFilePathsBuildItem filePaths, + List checkedTemplates, + List messageBundleMethods, + List globals, QuteConfig config, + List validationParserHooks, Optional engineConfigurations, BeanArchiveIndexBuildItem beanArchiveIndex, BuildProducer checkedFragmentValidations) { @@ -724,6 +727,10 @@ public void beforeParsing(ParserHelper parserHelper) { parserHelper.addParameter(UserTagSectionHelper.Factory.ARGS, UserTagSectionHelper.Arguments.class.getName()); } + + for (ValidationParserHookBuildItem hook : validationParserHooks) { + hook.accept(parserHelper); + } } // If needed add params to message bundle templates diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java new file mode 100644 index 0000000000000..656a17a4d0d09 --- /dev/null +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java @@ -0,0 +1,30 @@ +package io.quarkus.qute.deployment; + +import java.util.Objects; +import java.util.function.Consumer; + +import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.qute.ParserHelper; + +/** + * This build item can be used to hook into the parser logic during validation at build time. + *

+ * Validation parser hooks are never used at runtime. + */ +public final class ValidationParserHookBuildItem extends MultiBuildItem { + + private final Consumer hook; + + public ValidationParserHookBuildItem(Consumer hook) { + this.hook = Objects.requireNonNull(hook); + } + + public Consumer getHook() { + return hook; + } + + public void accept(ParserHelper helper) { + hook.accept(helper); + } + +} diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java index b2b7f2af94323..62b4192bcb31c 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java @@ -4,13 +4,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import jakarta.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.Engine; import io.quarkus.qute.EngineConfiguration; import io.quarkus.qute.ParserHelper; import io.quarkus.qute.ParserHook; @@ -39,9 +36,6 @@ public class CustomParserHookBuildTest { assertTrue(te.getMessage().contains("{foo.bar}"), te.getMessage()); });; - @Inject - Engine engine; - @Test public void test() { fail(); diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java new file mode 100644 index 0000000000000..e76dc7b8c851a --- /dev/null +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java @@ -0,0 +1,76 @@ +package io.quarkus.qute.deployment.engineconfigurations.parserhook; + +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 java.util.function.Consumer; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.builder.BuildChainBuilder; +import io.quarkus.builder.BuildContext; +import io.quarkus.builder.BuildStep; +import io.quarkus.qute.TemplateException; +import io.quarkus.qute.deployment.ValidationParserHookBuildItem; +import io.quarkus.test.QuarkusUnitTest; + +public class ValidationHookTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot( + root -> root.addClasses(Foo.class) + .addAsResource(new StringAsset("{foo.bar}"), "templates/foo.html")) + .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 incorrect expressions (1)"), te.getMessage()); + assertTrue(te.getMessage().contains("{foo.bar}"), te.getMessage()); + }).addBuildChainCustomizer(buildCustomizer()); + + static Consumer buildCustomizer() { + return new Consumer() { + @Override + public void accept(BuildChainBuilder builder) { + builder.addBuildStep(new BuildStep() { + @Override + public void execute(BuildContext context) { + context.produce(new ValidationParserHookBuildItem(helper -> { + if (helper.getTemplateId().contains("foo")) { + helper.addParameter("foo", Foo.class.getName()); + } + })); + } + }).produces(ValidationParserHookBuildItem.class) + .build(); + + } + }; + } + + @Test + public void test() { + fail(); + } + + public static class Foo { + + // package-private method is ignored + String bar() { + return null; + } + + } + +}