From e6357d8331a5571360f0f21680c1c69f7de95f78 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Fri, 27 Aug 2021 12:34:46 +0200 Subject: [PATCH] Qute type-safe validation - include all interface methods in hierarchy - resolves #19564 (cherry picked from commit 55288bf3b595f23080b872aee155cba3535ec8f9) --- .../qute/deployment/QuteProcessor.java | 9 ++- .../InterfaceValidationFailureTest.java | 46 +++++++++++++++ .../InterfaceValidationSuccessTest.java | 58 ++++++++++++++++++- 3 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationFailureTest.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 344aab0f71aa1..76a7c6d1a04bc 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 @@ -1738,14 +1738,13 @@ private static AnnotationTarget findProperty(String name, ClassInfo clazz, Index clazz = index.getClassByName(clazz.superName()); } } - // Try the default methods + // Try interface methods for (DotName interfaceName : interfaceNames) { ClassInfo interfaceClassInfo = index.getClassByName(interfaceName); if (interfaceClassInfo != null) { for (MethodInfo method : interfaceClassInfo.methods()) { - // A default method is a public non-abstract instance method if (Modifier.isPublic(method.flags()) && !Modifier.isStatic(method.flags()) - && !ValueResolverGenerator.isSynthetic(method.flags()) && !Modifier.isAbstract(method.flags()) + && !ValueResolverGenerator.isSynthetic(method.flags()) && (method.name().equals(name) || ValueResolverGenerator.getPropertyName(method.name()).equals(name))) { return method; @@ -1787,14 +1786,14 @@ && methodMatches(method, virtualMethod, expression, index, templateIdToPathFun, clazz = index.getClassByName(clazz.superName()); } } - // Try the default methods + // Try interface methods for (DotName interfaceName : interfaceNames) { ClassInfo interfaceClassInfo = index.getClassByName(interfaceName); if (interfaceClassInfo != null) { for (MethodInfo method : interfaceClassInfo.methods()) { // A default method is a public non-abstract instance method if (Modifier.isPublic(method.flags()) && !Modifier.isStatic(method.flags()) - && !ValueResolverGenerator.isSynthetic(method.flags()) && !Modifier.isAbstract(method.flags()) + && !ValueResolverGenerator.isSynthetic(method.flags()) && methodMatches(method, virtualMethod, expression, index, templateIdToPathFun, results)) { return method; } diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationFailureTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationFailureTest.java new file mode 100644 index 0000000000000..df9f119c87a13 --- /dev/null +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationFailureTest.java @@ -0,0 +1,46 @@ +package io.quarkus.qute.deployment.typesafe; + +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.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.qute.TemplateException; +import io.quarkus.test.QuarkusUnitTest; + +public class InterfaceValidationFailureTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(InterfaceValidationSuccessTest.Metrics.class, InterfaceValidationSuccessTest.Count.class, + InterfaceValidationSuccessTest.Wrapper.class) + .addAsResource(new StringAsset( + "{@io.quarkus.qute.deployment.typesafe.InterfaceValidationSuccessTest$Metrics metrics}" + + "{metrics.responses.values}"), + "templates/metrics.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 template problems (1)"), te.getMessage()); + assertTrue(te.getMessage().contains("{metrics.responses.values}"), te.getMessage()); + }); + + @Test + public void test() { + fail(); + } +} diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationSuccessTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationSuccessTest.java index 8405afaf972bb..c98a77f1821cd 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationSuccessTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/InterfaceValidationSuccessTest.java @@ -20,17 +20,69 @@ public class InterfaceValidationSuccessTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) - .addClasses(Movie.class, MovieExtensions.class) + .addClasses(Metrics.class, Count.class, Wrapper.class) .addAsResource(new StringAsset("{@java.util.List list}" + "{list.empty}:{list.toString}"), - "templates/list.html")); + "templates/list.html") + .addAsResource( + new StringAsset( + "{@io.quarkus.qute.deployment.typesafe.InterfaceValidationSuccessTest$Metrics metrics}" + + "{metrics.responses.value}:{metrics.responses.name(1)}:{metrics.requests.value??}"), + "templates/metrics.html")); @Inject Template list; + @Inject + Template metrics; + @Test - public void testResult() { + public void testInterfaceMethod() { assertEquals("true:[]", list.data("list", Collections.emptyList()).render()); } + @Test + public void testInterfaceHierarchy() { + assertEquals("5:Andy:", + metrics.data("metrics", new Metrics() { + + @Override + public Count responses() { + return new Count() { + + @Override + public Integer value() { + return 5; + } + + @Override + public String name(int age) { + return "Andy"; + } + }; + } + + @Override + public Count requests() { + return null; + } + }).render()); + + } + + public interface Wrapper { + T value(); + + String name(int age); + } + + public interface Count extends Wrapper { + } + + public interface Metrics { + + Count requests(); + + Count responses(); + } }