From a3d3f5487786c2d32255dc0be65b84c0f1506f2b Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 29 Apr 2022 16:40:46 +0200 Subject: [PATCH] Provide qute.native.enabled setting Fixes #629 Signed-off-by: azerr --- .../qute/ItemWithRegisterForReflection.java | 26 ++ .../org/acme/qute/ItemWithTemplateData.java | 27 ++ .../TemplateGetResolvedJavaTypeTest.java | 87 ++++++- .../RegisterForReflectionAnnotation.java | 64 +++++ .../qute/commons/ResolvedJavaTypeInfo.java | 50 ++++ .../qute/commons/TemplateDataAnnotation.java | 43 ++++ .../qute/jdt/QuteSupportForTemplate.java | 4 +- .../qute/jdt/internal/QuteJavaConstants.java | 28 ++- .../utils/QuteReflectionAnnotationUtils.java | 107 ++++++++ .../RegisterForReflectionAnnotation.java | 64 +++++ .../qute/commons/ResolvedJavaTypeInfo.java | 50 ++++ .../qute/commons/TemplateDataAnnotation.java | 43 ++++ .../redhat/qute/ls/QuteLanguageServer.java | 2 +- .../TemplateFileTextDocumentService.java | 6 +- .../qute/project/QuteProjectRegistry.java | 116 +++++++-- .../project/datamodel/JavaDataModelCache.java | 10 +- .../redhat/qute/services/QuteCompletions.java | 20 +- .../redhat/qute/services/QuteDiagnostics.java | 164 +++++++++--- .../qute/services/QuteLanguageService.java | 30 ++- .../QuteCompletionsForExpression.java | 233 +++++++++++------- .../services/diagnostics/QuteErrorCode.java | 17 +- .../services/nativemode/JavaTypeFilter.java | 46 ++++ .../JavaTypeFilterInNativeMode.java | 139 +++++++++++ .../JavaTypeFilterInNoNativeMode.java | 60 +++++ .../services/nativemode/NativeModeUtils.java | 67 +++++ .../redhat/qute/settings/BaseSettings.java | 12 + .../settings/QuteGeneralClientSettings.java | 85 ++++++- .../qute/settings/QuteNativeSettings.java | 63 +++++ .../test/java/com/redhat/qute/QuteAssert.java | 13 +- .../qute/project/QuteQuickStartProject.java | 22 +- ...eCompletionInParameterDeclarationTest.java | 40 ++- .../QuteDiagnosticsInfixNotationTest.java | 2 +- ...nosticsWithTemplateDataAnnotationTest.java | 112 +++++++++ 33 files changed, 1656 insertions(+), 196 deletions(-) create mode 100644 qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithRegisterForReflection.java create mode 100644 qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithTemplateData.java create mode 100644 qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java create mode 100644 qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java create mode 100644 qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/QuteReflectionAnnotationUtils.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilter.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNativeMode.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNoNativeMode.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/NativeModeUtils.java create mode 100644 qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeSettings.java create mode 100644 qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsWithTemplateDataAnnotationTest.java diff --git a/qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithRegisterForReflection.java b/qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithRegisterForReflection.java new file mode 100644 index 000000000..f938dcba2 --- /dev/null +++ b/qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithRegisterForReflection.java @@ -0,0 +1,26 @@ +package org.acme.qute; + +import java.math.BigDecimal; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection(fields = false) +public class ItemWithRegisterForReflection { + + public final String name; + + public final BigDecimal price; + + public ItemWithRegisterForReflection(BigDecimal price, String name) { + this.price = price; + this.name = name; + } + + public Item[] getDerivedItems() { + return null; + } + + public static BigDecimal staticMethod(Item item) { + return item.price.multiply(new BigDecimal("0.9")); + } +} \ No newline at end of file diff --git a/qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithTemplateData.java b/qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithTemplateData.java new file mode 100644 index 000000000..4188c2558 --- /dev/null +++ b/qute.jdt/com.redhat.qute.jdt.test/projects/maven/qute-quickstart/src/main/java/org/acme/qute/ItemWithTemplateData.java @@ -0,0 +1,27 @@ +package org.acme.qute; + +import java.math.BigDecimal; + +import io.quarkus.qute.TemplateData; + +@TemplateData(target = BigDecimal.class) +@TemplateData(ignoreSuperclasses = true) +public class ItemWithTemplateData { + + public final String name; + + public final BigDecimal price; + + public ItemWithTemplateData(BigDecimal price, String name) { + this.price = price; + this.name = name; + } + + public Item[] getDerivedItems() { + return null; + } + + public static BigDecimal staticMethod(Item item) { + return item.price.multiply(new BigDecimal("0.9")); + } +} \ No newline at end of file diff --git a/qute.jdt/com.redhat.qute.jdt.test/src/main/java/com/redhat/qute/jdt/template/TemplateGetResolvedJavaTypeTest.java b/qute.jdt/com.redhat.qute.jdt.test/src/main/java/com/redhat/qute/jdt/template/TemplateGetResolvedJavaTypeTest.java index 3c88ba794..ac07f32c1 100644 --- a/qute.jdt/com.redhat.qute.jdt.test/src/main/java/com/redhat/qute/jdt/template/TemplateGetResolvedJavaTypeTest.java +++ b/qute.jdt/com.redhat.qute.jdt.test/src/main/java/com/redhat/qute/jdt/template/TemplateGetResolvedJavaTypeTest.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2021 Red Hat Inc. and others. +0* Copyright (c) 2021 Red Hat Inc. and others. * All rights reserved. This program and the accompanying materials * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v20.html @@ -17,6 +17,7 @@ import java.util.List; import java.util.Optional; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.junit.Assert; import org.junit.Assume; @@ -294,6 +295,90 @@ public void string() throws Exception { assertExtendedTypes("java.lang.String", "java.lang.CharSequence", extendedTypes); } + @Test + public void templateData() throws CoreException, Exception { + loadMavenProject(QuteMavenProjectName.qute_quickstart); + + QuteResolvedJavaTypeParams params = new QuteResolvedJavaTypeParams("org.acme.qute.ItemWithTemplateData", + QuteMavenProjectName.qute_quickstart); + ResolvedJavaTypeInfo result = QuteSupportForTemplate.getInstance().getResolvedJavaType(params, getJDTUtils(), + new NullProgressMonitor()); + Assert.assertNotNull(result); + Assert.assertEquals("org.acme.qute.ItemWithTemplateData", result.getSignature()); + Assert.assertFalse(result.isIterable()); + + // @TemplateData + + // @TemplateData(target = BigDecimal.class) + // @TemplateData(ignoreSuperclasses = true) + // public class ItemWithTemplateData { + Assert.assertNotNull(result.getTemplateDataAnnotations()); + Assert.assertEquals(2, result.getTemplateDataAnnotations().size()); + // @TemplateData(target = BigDecimal.class) + Assert.assertFalse(result.getTemplateDataAnnotations().get(0).isIgnoreSuperclasses()); + // @TemplateData(ignoreSuperclasses = true) + Assert.assertTrue(result.getTemplateDataAnnotations().get(1).isIgnoreSuperclasses()); + + // Fields + Assert.assertNotNull(result.getFields()); + Assert.assertEquals(2, result.getFields().size()); + Assert.assertEquals("name", result.getFields().get(0).getName()); + Assert.assertEquals("java.lang.String", result.getFields().get(0).getType()); + Assert.assertEquals("price", result.getFields().get(1).getName()); + Assert.assertEquals("java.math.BigDecimal", result.getFields().get(1).getType()); + + // Methods + Assert.assertNotNull(result.getMethods()); + Assert.assertEquals(1, result.getMethods().size()); + Assert.assertEquals("getDerivedItems() : org.acme.qute.Item[]", result.getMethods().get(0).getSignature()); + + // Invalid methods(static method) + JavaMethodInfo discountedPriceMethod = findMethod(result, "staticMethod"); + Assert.assertNull(discountedPriceMethod); + InvalidMethodReason reason = result.getInvalidMethodReason("staticMethod"); + Assert.assertEquals(InvalidMethodReason.Static, reason); + } + + @Test + public void registerForReflection() throws CoreException, Exception { + loadMavenProject(QuteMavenProjectName.qute_quickstart); + + QuteResolvedJavaTypeParams params = new QuteResolvedJavaTypeParams("org.acme.qute.ItemWithRegisterForReflection", + QuteMavenProjectName.qute_quickstart); + ResolvedJavaTypeInfo result = QuteSupportForTemplate.getInstance().getResolvedJavaType(params, getJDTUtils(), + new NullProgressMonitor()); + Assert.assertNotNull(result); + Assert.assertEquals("org.acme.qute.ItemWithRegisterForReflection", result.getSignature()); + Assert.assertFalse(result.isIterable()); + + // @RegisterForReflection + + // @RegisterForReflection(fields = false) + // public class ItemWithRegisterForReflection { + Assert.assertNotNull(result.getRegisterForReflectionAnnotation()); + Assert.assertFalse(result.getRegisterForReflectionAnnotation().isFields()); + Assert.assertTrue(result.getRegisterForReflectionAnnotation().isMethods()); + + // Fields + Assert.assertNotNull(result.getFields()); + Assert.assertEquals(2, result.getFields().size()); + Assert.assertEquals("name", result.getFields().get(0).getName()); + Assert.assertEquals("java.lang.String", result.getFields().get(0).getType()); + Assert.assertEquals("price", result.getFields().get(1).getName()); + Assert.assertEquals("java.math.BigDecimal", result.getFields().get(1).getType()); + + // Methods + Assert.assertNotNull(result.getMethods()); + Assert.assertEquals(1, result.getMethods().size()); + Assert.assertEquals("getDerivedItems() : org.acme.qute.Item[]", result.getMethods().get(0).getSignature()); + + // Invalid methods(static method) + JavaMethodInfo discountedPriceMethod = findMethod(result, "staticMethod"); + Assert.assertNull(discountedPriceMethod); + InvalidMethodReason reason = result.getInvalidMethodReason("staticMethod"); + Assert.assertEquals(InvalidMethodReason.Static, reason); + } + private static void assertExtendedTypes(String type, String extendedType, List extendedTypes) { Assert.assertTrue("The Java type '" + type + "' should extends '" + extendedType + "'.", extendedTypes.contains(extendedType)); diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java new file mode 100644 index 000000000..bd1c536df --- /dev/null +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java @@ -0,0 +1,64 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.commons; + +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * '@io.quarkus.runtime.annotations.RegisterForReflection' annotation + * + * This annotation that can be used to force a class to be registered for + * reflection in native image mode + */ +public class RegisterForReflectionAnnotation { + + /** + * If the methods should be registered + */ + private Boolean methods; + + /** + * If the fields should be registered + */ + private Boolean fields; + + public boolean isMethods() { + if (methods == null) { + return true; + } + return methods.booleanValue(); + } + + public void setMethods(Boolean methods) { + this.methods = methods; + } + + public boolean isFields() { + if (fields == null) { + return true; + } + return fields.booleanValue(); + } + + public void setFields(Boolean fields) { + this.fields = fields; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("fields", this.isFields()); + b.add("methods", this.isMethods()); + return b.toString(); + } + +} diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java index 713ea9b86..1649adafb 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java @@ -40,6 +40,10 @@ public class ResolvedJavaTypeInfo extends JavaTypeInfo { private Boolean isIterable; + private RegisterForReflectionAnnotation registerForReflectionAnnotation; + + private List templateDataAnnotations; + /** * Returns list of extended types. * @@ -178,6 +182,50 @@ private synchronized boolean computeIsIterable() { return iterable; } + /** + * Returns the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @return the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + */ + public List getTemplateDataAnnotations() { + return templateDataAnnotations; + } + + /** + * Set the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @param templateDataAnnotations the list of '@io.quarkus.qute.TemplateData' + * annotations for the Java type. + */ + public void setTemplateDataAnnotations(List templateDataAnnotations) { + this.templateDataAnnotations = templateDataAnnotations; + } + + /** + * Returns '@io.quarkus.runtime.annotations.RegisterForReflection' for the Java + * type. + * + * @return '@io.quarkus.runtime.annotations.RegisterForReflection' for the Java + * type. + */ + public RegisterForReflectionAnnotation getRegisterForReflectionAnnotation() { + return registerForReflectionAnnotation; + } + + /** + * Set the '@io.quarkus.runtime.annotations.RegisterForReflection' for the Java + * type. + * + * @param registerForReflectionAnnotation '@io.quarkus.runtime.annotations.RegisterForReflection' + * for the Java type. + */ + public void setRegisterForReflectionAnnotation(RegisterForReflectionAnnotation registerForReflectionAnnotation) { + this.registerForReflectionAnnotation = registerForReflectionAnnotation; + } + @Override public String toString() { ToStringBuilder b = new ToStringBuilder(this); @@ -185,6 +233,8 @@ public String toString() { b.add("signature", this.getSignature()); b.add("iterableOf", this.getIterableOf()); b.add("iterableType", this.getIterableType()); + b.add("templateDataAnnotations", this.getTemplateDataAnnotations()); + b.add("registerForReflectionAnnotation", this.getRegisterForReflectionAnnotation()); return b.toString(); } diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java new file mode 100644 index 000000000..59e1c150e --- /dev/null +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.commons; + +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * '@io.quarkus.qute.TemplateData' annotation. + * + * @author Angelo ZERR + * + */ +public class TemplateDataAnnotation { + + /** + * If set to true do not automatically analyze superclasses. + */ + private Boolean ignoreSuperclasses; + + public boolean isIgnoreSuperclasses() { + return ignoreSuperclasses != null && ignoreSuperclasses.booleanValue(); + } + + public void setIgnoreSuperclasses(Boolean ignoreSuperclasses) { + this.ignoreSuperclasses = ignoreSuperclasses; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("ignoreSuperclasses", this.isIgnoreSuperclasses()); + return b.toString(); + } +} diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java index d7c784f0c..e2d5e16bd 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/QuteSupportForTemplate.java @@ -69,6 +69,7 @@ import com.redhat.qute.jdt.internal.template.TemplateDataSupport; import com.redhat.qute.jdt.utils.IJDTUtils; import com.redhat.qute.jdt.utils.JDTQuteProjectUtils; +import com.redhat.qute.jdt.utils.QuteReflectionAnnotationUtils; /** * Qute support for Template file. @@ -409,7 +410,7 @@ public ResolvedJavaTypeInfo getResolvedJavaType(QuteResolvedJavaTypeParams param } } else { // ex : String implements CharSequence, .... - ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(monitor); + ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(monitor); IType[] allSuperTypes = typeHierarchy.getSupertypes(type); extendedTypes = Stream.of(allSuperTypes) // .map(superType -> superType.getFullyQualifiedName()) // @@ -427,6 +428,7 @@ public ResolvedJavaTypeInfo getResolvedJavaType(QuteResolvedJavaTypeParams param resolvedType.setMethods(methodsInfo); resolvedType.setInvalidMethods(invalidMethods); resolvedType.setExtendedTypes(extendedTypes); + QuteReflectionAnnotationUtils.collectAnnotations(resolvedType, type); return resolvedType; } diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java index a3e61b958..713c6c796 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/QuteJavaConstants.java @@ -21,21 +21,39 @@ public class QuteJavaConstants { public static final String JAVAX_INJECT_NAMED_ANNOTATION = "javax.inject.Named"; + public static final String LOCATION_ANNOTATION = "io.quarkus.qute.Location"; + + public static final String TEMPLATE_CLASS = "io.quarkus.qute.Template"; + + public static final String ENGINE_BUILDER_CLASS = "io.quarkus.qute.EngineBuilder"; + + public static final String VALUE_ANNOTATION_NAME = "value"; + + // @CheckedTemplate + public static final String CHECKED_TEMPLATE_ANNOTATION = "io.quarkus.qute.CheckedTemplate"; public static final String OLD_CHECKED_TEMPLATE_ANNOTATION = "io.quarkus.qute.api.CheckedTemplate"; + // @TemplateExtension + public static final String TEMPLATE_EXTENSION_ANNOTATION = "io.quarkus.qute.TemplateExtension"; public static final String TEMPLATE_EXTENSION_ANNOTATION_NAMESPACE = "namespace"; public static final String TEMPLATE_EXTENSION_ANNOTATION_MATCH_NAME = "matchName"; - - public static final String LOCATION_ANNOTATION = "io.quarkus.qute.Location"; - public static final String TEMPLATE_CLASS = "io.quarkus.qute.Template"; + // @TemplateData - public static final String ENGINE_BUILDER_CLASS = "io.quarkus.qute.EngineBuilder"; + public static final String TEMPLATE_DATA_ANNOTATION = "io.quarkus.qute.TemplateData"; - public static final String VALUE_ANNOTATION_NAME = "value"; + public static final String TEMPLATE_DATA_ANNOTATION_IGNORE_SUPER_CLASSES = "ignoreSuperclasses"; + + // @io.quarkus.runtime.annotations.RegisterForReflection + + public static final String REGISTER_FOR_REFLECTION_ANNOTATION = "io.quarkus.runtime.annotations.RegisterForReflection"; + + public static final String REGISTER_FOR_REFLECTION_ANNOTATION_FIELDS = "fields"; + + public static final String REGISTER_FOR_REFLECTION_ANNOTATION_METHODS = "methods"; } diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/QuteReflectionAnnotationUtils.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/QuteReflectionAnnotationUtils.java new file mode 100644 index 000000000..dc4fd90fd --- /dev/null +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/utils/QuteReflectionAnnotationUtils.java @@ -0,0 +1,107 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.jdt.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; + +import com.redhat.qute.commons.RegisterForReflectionAnnotation; +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.commons.TemplateDataAnnotation; +import com.redhat.qute.jdt.internal.QuteJavaConstants; + +/** + * Utilities for collecting @TemplateData and @RegisterForReflection. + * + * @author Angelo ZERR + * + */ +public class QuteReflectionAnnotationUtils { + + private static final Logger LOGGER = Logger.getLogger(QuteReflectionAnnotationUtils.class.getName()); + private static final String TRUE_VALUE = "true"; + private static final String FALSE_VALUE = "false"; + + /** + * Collect + * + * @param resolvedType + * @param type + * @throws JavaModelException + */ + public static void collectAnnotations(ResolvedJavaTypeInfo resolvedType, IType type) throws JavaModelException { + List templateDataAnnotations = null; + RegisterForReflectionAnnotation registerForReflectionAnnotation = null; + IAnnotation[] annotations = type.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (AnnotationUtils.isMatchAnnotation(annotation, QuteJavaConstants.TEMPLATE_DATA_ANNOTATION)) { + // @TemplateData + if (templateDataAnnotations == null) { + templateDataAnnotations = new ArrayList<>(); + } + templateDataAnnotations.add(createTemplateData(annotation)); + } else if (AnnotationUtils.isMatchAnnotation(annotation, + QuteJavaConstants.REGISTER_FOR_REFLECTION_ANNOTATION)) { + // @RegisterForReflection + registerForReflectionAnnotation = createRegisterForReflection(annotation); + } + } + resolvedType.setTemplateDataAnnotations(templateDataAnnotations); + resolvedType.setRegisterForReflectionAnnotation(registerForReflectionAnnotation); + } + + private static TemplateDataAnnotation createTemplateData(IAnnotation templateDataAnnotation) { + TemplateDataAnnotation templateData = new TemplateDataAnnotation(); + try { + // @TemplateData/ignoreSuperclasses + String ignoreSuperclasses = AnnotationUtils.getAnnotationMemberValue(templateDataAnnotation, + QuteJavaConstants.TEMPLATE_DATA_ANNOTATION_IGNORE_SUPER_CLASSES); + if (TRUE_VALUE.equals(ignoreSuperclasses)) { + templateData.setIgnoreSuperclasses(Boolean.TRUE); + } + } catch (JavaModelException e) { + LOGGER.log(Level.SEVERE, + "Error while getting member values of '" + templateDataAnnotation.getElementName() + "'.", e); + } + return templateData; + } + + private static RegisterForReflectionAnnotation createRegisterForReflection( + IAnnotation registerForReflectionAnnotation) { + RegisterForReflectionAnnotation registerForReflection = new RegisterForReflectionAnnotation(); + try { + // @RegisterForReflection/methods + String methods = AnnotationUtils.getAnnotationMemberValue(registerForReflectionAnnotation, + QuteJavaConstants.REGISTER_FOR_REFLECTION_ANNOTATION_METHODS); + if (FALSE_VALUE.equals(methods)) { + registerForReflection.setMethods(Boolean.FALSE); + } + // @RegisterForReflection/fields + String fields = AnnotationUtils.getAnnotationMemberValue(registerForReflectionAnnotation, + QuteJavaConstants.REGISTER_FOR_REFLECTION_ANNOTATION_FIELDS); + if (FALSE_VALUE.equals(fields)) { + registerForReflection.setFields(Boolean.FALSE); + } + } catch (JavaModelException e) { + LOGGER.log(Level.SEVERE, + "Error while getting member values of '" + registerForReflectionAnnotation.getElementName() + "'.", + e); + } + return registerForReflection; + } +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java new file mode 100644 index 000000000..bd1c536df --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/RegisterForReflectionAnnotation.java @@ -0,0 +1,64 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.commons; + +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * '@io.quarkus.runtime.annotations.RegisterForReflection' annotation + * + * This annotation that can be used to force a class to be registered for + * reflection in native image mode + */ +public class RegisterForReflectionAnnotation { + + /** + * If the methods should be registered + */ + private Boolean methods; + + /** + * If the fields should be registered + */ + private Boolean fields; + + public boolean isMethods() { + if (methods == null) { + return true; + } + return methods.booleanValue(); + } + + public void setMethods(Boolean methods) { + this.methods = methods; + } + + public boolean isFields() { + if (fields == null) { + return true; + } + return fields.booleanValue(); + } + + public void setFields(Boolean fields) { + this.fields = fields; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("fields", this.isFields()); + b.add("methods", this.isMethods()); + return b.toString(); + } + +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java index 713ea9b86..1649adafb 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/ResolvedJavaTypeInfo.java @@ -40,6 +40,10 @@ public class ResolvedJavaTypeInfo extends JavaTypeInfo { private Boolean isIterable; + private RegisterForReflectionAnnotation registerForReflectionAnnotation; + + private List templateDataAnnotations; + /** * Returns list of extended types. * @@ -178,6 +182,50 @@ private synchronized boolean computeIsIterable() { return iterable; } + /** + * Returns the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @return the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + */ + public List getTemplateDataAnnotations() { + return templateDataAnnotations; + } + + /** + * Set the list of '@io.quarkus.qute.TemplateData' annotations for the Java + * type. + * + * @param templateDataAnnotations the list of '@io.quarkus.qute.TemplateData' + * annotations for the Java type. + */ + public void setTemplateDataAnnotations(List templateDataAnnotations) { + this.templateDataAnnotations = templateDataAnnotations; + } + + /** + * Returns '@io.quarkus.runtime.annotations.RegisterForReflection' for the Java + * type. + * + * @return '@io.quarkus.runtime.annotations.RegisterForReflection' for the Java + * type. + */ + public RegisterForReflectionAnnotation getRegisterForReflectionAnnotation() { + return registerForReflectionAnnotation; + } + + /** + * Set the '@io.quarkus.runtime.annotations.RegisterForReflection' for the Java + * type. + * + * @param registerForReflectionAnnotation '@io.quarkus.runtime.annotations.RegisterForReflection' + * for the Java type. + */ + public void setRegisterForReflectionAnnotation(RegisterForReflectionAnnotation registerForReflectionAnnotation) { + this.registerForReflectionAnnotation = registerForReflectionAnnotation; + } + @Override public String toString() { ToStringBuilder b = new ToStringBuilder(this); @@ -185,6 +233,8 @@ public String toString() { b.add("signature", this.getSignature()); b.add("iterableOf", this.getIterableOf()); b.add("iterableType", this.getIterableType()); + b.add("templateDataAnnotations", this.getTemplateDataAnnotations()); + b.add("registerForReflectionAnnotation", this.getRegisterForReflectionAnnotation()); return b.toString(); } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java new file mode 100644 index 000000000..59e1c150e --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateDataAnnotation.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.commons; + +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * '@io.quarkus.qute.TemplateData' annotation. + * + * @author Angelo ZERR + * + */ +public class TemplateDataAnnotation { + + /** + * If set to true do not automatically analyze superclasses. + */ + private Boolean ignoreSuperclasses; + + public boolean isIgnoreSuperclasses() { + return ignoreSuperclasses != null && ignoreSuperclasses.booleanValue(); + } + + public void setIgnoreSuperclasses(Boolean ignoreSuperclasses) { + this.ignoreSuperclasses = ignoreSuperclasses; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("ignoreSuperclasses", this.isIgnoreSuperclasses()); + return b.toString(); + } +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java index a9e0b97db..3c0e0269b 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/QuteLanguageServer.java @@ -166,7 +166,7 @@ public synchronized void updateSettings(Object initializationOptionsSettings) { // Some inlay hint settings changed, ask the client to refresh all inlay hints. getLanguageClient().refreshInlayHints(); } - if (result.isValidationSettingsChanged()) { + if (result.isValidationSettingsChanged() || result.isNativeImagesSettingsChanged()) { // Some validation settings changed textDocumentService.validationSettingsChanged(); } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java index 23db65a0b..d5edd00a1 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java @@ -121,7 +121,8 @@ public CompletableFuture, CompletionList>> completio (cancelChecker, template) -> { return getQuteLanguageService() .doComplete(template, params.getPosition(), sharedSettings.getCompletionSettings(), - sharedSettings.getFormattingSettings(), cancelChecker) // + sharedSettings.getFormattingSettings(), sharedSettings.getNativeSettings(), + cancelChecker) // .thenApply(list -> { return Either.forRight(list); }); @@ -283,7 +284,8 @@ private void triggerValidationFor(QuteTextDocument document) { ResolvingJavaTypeContext resolvingJavaTypeContext = new ResolvingJavaTypeContext(template, quteLanguageServer.getDataModelCache()); List diagnostics = getQuteLanguageService().doDiagnostics(template, - getSharedSettings().getValidationSettings(template.getUri()), resolvingJavaTypeContext, + getSharedSettings().getValidationSettings(template.getUri()), + getSharedSettings().getNativeSettings(), resolvingJavaTypeContext, () -> template.checkCanceled()); // Diagnostics has been collected, before diagnostics publishing, check if the diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java index 5cab20b03..37043c7f6 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java @@ -62,6 +62,7 @@ import com.redhat.qute.project.datamodel.resolvers.TypeValueResolver; import com.redhat.qute.project.datamodel.resolvers.ValueResolver; import com.redhat.qute.project.datamodel.resolvers.ValueResolversRegistry; +import com.redhat.qute.services.nativemode.JavaTypeFilter; import com.redhat.qute.utils.StringUtils; /** @@ -292,10 +293,14 @@ private CompletableFuture> getMethodValueResolvers(Str public CompletableFuture getDataModelTemplate(Template template) { String projectUri = template.getProjectUri(); if (StringUtils.isEmpty(projectUri)) { - // The project uri is not already get (it occurs when Qute template is opened and the project information takes some times). + // The project uri is not already get (it occurs when Qute template is opened + // and the project information takes some times). // Load the project information and call the data model. return template.getProjectFuture() // .thenCompose(project -> { + if (project == null) { + return null; + } return getDataModelTemplate(template, project.getUri()); }); } @@ -350,6 +355,64 @@ public JavaMemberInfo findMember(ResolvedJavaTypeInfo baseType, String property, if (baseType == null) { return null; } + JavaMemberInfo member = findPropertyWithJavaReflection(baseType, property, projectUri); + if (member != null) { + return member; + } + return findValueResolver(baseType, property, projectUri); + } + + public JavaMemberResult findProperty(ResolvedJavaTypeInfo baseType, String property, JavaTypeFilter filter, + String projectUri) { + JavaMemberResult result = new JavaMemberResult(); + + boolean nativeImages = filter.isInNativeMode(); + if (!nativeImages) { + + // Find member with Java reflection. + JavaMemberInfo member = findPropertyWithJavaReflection(baseType, property, projectUri); + if (member != null) { + result.setMember(member); + return result; + } + + // Find member with value resolvers. + member = findValueResolver(baseType, property, projectUri); + if (member != null) { + result.setMember(member); + } + return result; + } + + // Find member with value resolvers. + JavaMemberInfo member = findValueResolver(baseType, property, projectUri); + if (member != null) { + result.setMember(member); + return result; + } + + // Find member with Java reflection. + member = findPropertyWithJavaReflection(baseType, property, projectUri); + if (member != null) { + result.setMember(member); + return result; + } + return result; + } + + /** + * Returns the Java member from the given base type wich matches the given + * property by using Java reflection and null otherwise. + * + * @param baseType the Java type. + * @param property the property member. + * @param projectUri the project Uri. + * + * @return the Java member from the given base type wich matches the given + * property by using Java reflection and null otherwise. + */ + private JavaMemberInfo findPropertyWithJavaReflection(ResolvedJavaTypeInfo baseType, String property, + String projectUri) { if (baseType.isIterable() && !baseType.isArray()) { // Expression uses iterable type // {@java.util.List @@ -375,20 +438,15 @@ public JavaMemberInfo findMember(ResolvedJavaTypeInfo baseType, String property, for (String superType : baseType.getExtendedTypes()) { ResolvedJavaTypeInfo resolvedSuperType = resolveJavaType(superType, projectUri).getNow(null); if (resolvedSuperType != null) { - JavaMemberInfo superMemberInfo = findMember(resolvedSuperType, property, projectUri); + JavaMemberInfo superMemberInfo = findPropertyWithJavaReflection(resolvedSuperType, property, + projectUri); if (superMemberInfo != null) { return superMemberInfo; } } } } - // Search in value resolver - String literalType = LiteralSupport.getLiteralJavaType(property); - if (literalType != null) { - // ex : @java.lang.Integer(base : T[]) : T (see qute-resolvers.jsonc) - property = "@" + literalType; - } - return findValueResolver(property, baseType, projectUri); + return null; } /** @@ -462,6 +520,7 @@ protected static JavaMethodInfo findMethod(ResolvedJavaTypeInfo baseType, String * @param namespace the namespace part and null otherwise. * @param methodName the method name to search. * @param parameterTypes the parameter types of the method to search. + * @param nativeImages the native images context. * @param projectUri the project Uri. * * @return the Java method from the given Java type baseType which @@ -469,27 +528,39 @@ protected static JavaMethodInfo findMethod(ResolvedJavaTypeInfo baseType, String * parameter types parameterTypes. */ public JavaMemberResult findMethod(ResolvedJavaTypeInfo baseType, String namespace, String methodName, - List parameterTypes, String projectUri) { + List parameterTypes, JavaTypeFilter filter, String projectUri) { // Search in the java root type JavaMemberResult result = new JavaMemberResult(); - if (baseType != null) { + + boolean nativeImages = filter.isInNativeMode(); + if (!nativeImages && baseType != null) { + // In NO native images context, search with Java reflection at the begin if (findMethod(baseType, methodName, parameterTypes, result, projectUri)) { return result; } + } - // Search in value resolvers + // Search in template extension value resolvers retrieved by @TemplateExtension + List dynamicResolvers = getMethodValueResolvers(projectUri).getNow(null); + if (findMethodResolver(baseType, namespace, methodName, parameterTypes, dynamicResolvers, result, projectUri)) { + return result; + } + + if (baseType != null) { // Search in static value resolvers (ex : orEmpty, take, etc) List staticResolvers = valueResolversRegistry.getResolvers(); if (findMethodResolver(baseType, null, methodName, parameterTypes, staticResolvers, result, projectUri)) { return result; } - } - // Search in template extension value resolvers retrieved by @TemplateExtension - List dynamicResolvers = getMethodValueResolvers(projectUri).getNow(null); - if (findMethodResolver(baseType, namespace, methodName, parameterTypes, dynamicResolvers, result, projectUri)) { - return result; + if (nativeImages) { + // In native images context, search with Java reflection at the end + if (findMethod(baseType, methodName, parameterTypes, result, projectUri)) { + return result; + } + } } + return result; } @@ -623,9 +694,14 @@ private static boolean isEmpty(String value) { return value == null || value.isEmpty(); } - public MethodValueResolver findValueResolver(String property, ResolvedJavaTypeInfo resolvedType, - String projectUri) { - List resolvers = getResolversFor(resolvedType, projectUri); + public MethodValueResolver findValueResolver(ResolvedJavaTypeInfo baseType, String property, String projectUri) { + // Search in value resolver + String literalType = LiteralSupport.getLiteralJavaType(property); + if (literalType != null) { + // ex : @java.lang.Integer(base : T[]) : T (see qute-resolvers.jsonc) + property = "@" + literalType; + } + List resolvers = getResolversFor(baseType, projectUri); for (MethodValueResolver resolver : resolvers) { if (isMatchMethod(resolver, property)) { return resolver; diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java index 699667a72..61b77009f 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/JavaDataModelCache.java @@ -39,6 +39,7 @@ import com.redhat.qute.project.QuteProjectRegistry; import com.redhat.qute.project.datamodel.resolvers.MethodValueResolver; import com.redhat.qute.project.datamodel.resolvers.ValueResolver; +import com.redhat.qute.services.nativemode.JavaTypeFilter; import com.redhat.qute.utils.StringUtils; public class JavaDataModelCache implements DataModelTemplateProvider { @@ -257,6 +258,11 @@ public JavaMemberInfo findMember(ResolvedJavaTypeInfo baseType, String property, return projectRegistry.findMember(baseType, property, projectUri); } + public JavaMemberResult findProperty(Part part, ResolvedJavaTypeInfo baseType, + JavaTypeFilter filter, String projectUri) { + return projectRegistry.findProperty(baseType, part.getPartName(), filter, projectUri); + } + public boolean hasNamespace(String namespace, String projectUri) { return projectRegistry.hasNamespace(namespace, projectUri); } @@ -275,8 +281,8 @@ public CompletableFuture getNamespaceResolverInfo(String } public JavaMemberResult findMethod(ResolvedJavaTypeInfo baseType, String namespace, String methodName, - List parameterTypes, String projectUri) { - return projectRegistry.findMethod(baseType, namespace, methodName, parameterTypes, projectUri); + List parameterTypes, JavaTypeFilter filter, String projectUri) { + return projectRegistry.findMethod(baseType, namespace, methodName, parameterTypes, filter, projectUri); } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java index a8056bf6e..37b34e21d 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteCompletions.java @@ -40,6 +40,7 @@ import com.redhat.qute.services.completions.QuteCompletionsForSnippets; import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; +import com.redhat.qute.settings.QuteNativeSettings; /** * The Qute completions @@ -74,16 +75,17 @@ public QuteCompletions(JavaDataModelCache javaCache, SnippetRegistryProvider doComplete(Template template, Position position, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { + QuteNativeSettings nativeImagesSettings, CancelChecker cancelChecker) { CompletionRequest completionRequest = null; try { completionRequest = new CompletionRequest(template, position, completionSettings, formattingSettings); @@ -112,7 +114,7 @@ public CompletableFuture doComplete(Template template, Position expression = ((Part) node).getParent().getParent(); } return completionForExpression.doCompleteExpression(completionRequest, expression, nodeExpression, template, - offset, completionSettings, formattingSettings, cancelChecker); + offset, completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } else if (node.getKind() == NodeKind.Text) { // The completion is triggered in text node (before node) Section parent = node.getParentSection(); @@ -149,7 +151,7 @@ public CompletableFuture doComplete(Template template, Position if (nbBrackets % 2 != 0) { // The completion is triggered in text node after bracket '{' character return completionForExpression.doCompleteExpression(completionRequest, null, node, template, offset, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } return EMPTY_FUTURE_COMPLETION; } @@ -165,7 +167,7 @@ public CompletableFuture doComplete(Template template, Position if (parameter.isAfterAssign(offset)) { // {# let name=| return completionForExpression.doCompleteExpression(completionRequest, null, null, template, offset, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } } return collectSnippetSuggestions(completionRequest); diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java index a9f198329..af78fbc6b 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java @@ -33,6 +33,7 @@ import org.eclipse.lsp4j.jsonrpc.CancelChecker; import com.redhat.qute.commons.InvalidMethodReason; +import com.redhat.qute.commons.JavaElementKind; import com.redhat.qute.commons.JavaMemberInfo; import com.redhat.qute.commons.JavaMethodInfo; import com.redhat.qute.commons.JavaParameterInfo; @@ -66,6 +67,10 @@ import com.redhat.qute.services.diagnostics.DiagnosticDataFactory; import com.redhat.qute.services.diagnostics.QuteDiagnosticsForSyntax; import com.redhat.qute.services.diagnostics.QuteErrorCode; +import com.redhat.qute.services.nativemode.JavaTypeFilter; +import com.redhat.qute.services.nativemode.JavaTypeFilter.JavaMemberdAccess; +import com.redhat.qute.services.nativemode.NativeModeUtils; +import com.redhat.qute.settings.QuteNativeSettings; import com.redhat.qute.settings.QuteValidationSettings; import com.redhat.qute.utils.QutePositionUtility; import com.redhat.qute.utils.StringUtils; @@ -148,13 +153,15 @@ public QuteDiagnostics(JavaDataModelCache javaCache) { /** * Validate the given Qute template. * - * @param template the Qute template. - * @param validationSettings the validation settings. - * @param cancelChecker the cancel checker. + * @param template the Qute template. + * @param validationSettings the validation settings. + * @param nativeImagesSettings the native images settings. + * @param cancelChecker the cancel checker. * @return the result of the validation. */ public List doDiagnostics(Template template, QuteValidationSettings validationSettings, - ResolvingJavaTypeContext resolvingJavaTypeContext, CancelChecker cancelChecker) { + QuteNativeSettings nativeImagesSettings, ResolvingJavaTypeContext resolvingJavaTypeContext, + CancelChecker cancelChecker) { cancelChecker.checkCanceled(); if (validationSettings == null) { validationSettings = QuteValidationSettings.DEFAULT; @@ -181,8 +188,8 @@ public List doDiagnostics(Template template, QuteValidationSettings } // Validate data model try { - validateDataModel(template, template, validationSettings, resolvingJavaTypeContext, new ResolutionContext(), - diagnostics); + validateDataModel(template, template, validationSettings, nativeImagesSettings, resolvingJavaTypeContext, + new ResolutionContext(), diagnostics); } catch (CancellationException e) { throw e; } catch (Exception e) { @@ -193,8 +200,8 @@ public List doDiagnostics(Template template, QuteValidationSettings } private void validateDataModel(Node parent, Template template, QuteValidationSettings validationSettings, - ResolvingJavaTypeContext resolvingJavaTypeContext, ResolutionContext currentContext, - List diagnostics) { + QuteNativeSettings nativeImagesSettings, ResolvingJavaTypeContext resolvingJavaTypeContext, + ResolutionContext currentContext, List diagnostics) { ResolutionContext previousContext = currentContext; List children = parent.getChildren(); for (Node node : children) { @@ -251,7 +258,8 @@ private void validateDataModel(Node parent, Template template, QuteValidationSet if (expression != null) { // Validate object, property, method parts from the expression ResolvedJavaTypeInfo result = validateExpression(expression, section, template, - validationSettings, previousContext, resolvingJavaTypeContext, diagnostics); + validationSettings, nativeImagesSettings, previousContext, resolvingJavaTypeContext, + diagnostics); switch (section.getSectionKind()) { case FOR: case EACH: @@ -281,14 +289,14 @@ private void validateDataModel(Node parent, Template template, QuteValidationSet break; } case Expression: { - validateExpression((Expression) node, null, template, validationSettings, previousContext, - resolvingJavaTypeContext, diagnostics); + validateExpression((Expression) node, null, template, validationSettings, nativeImagesSettings, + previousContext, resolvingJavaTypeContext, diagnostics); break; } default: } - validateDataModel(node, template, validationSettings, resolvingJavaTypeContext, currentContext, - diagnostics); + validateDataModel(node, template, validationSettings, nativeImagesSettings, resolvingJavaTypeContext, + currentContext, diagnostics); } } @@ -376,8 +384,9 @@ private static void validateIncludeSection(IncludeSection includeSection, List diagnostics) { + QuteValidationSettings validationSettings, QuteNativeSettings nativeImagesSettings, + ResolutionContext resolutionContext, ResolvingJavaTypeContext resolvingJavaTypeContext, + List diagnostics) { try { String projectUri = template.getProjectUri(); String literalJavaType = expression.getLiteralJavaType(); @@ -402,7 +411,8 @@ private ResolvedJavaTypeInfo validateExpression(Expression expression, Section o if (expressionChild.getKind() == NodeKind.ExpressionParts) { Parts parts = (Parts) expressionChild; resolvedJavaType = validateExpressionParts(parts, ownerSection, template, projectUri, - validationSettings, resolutionContext, resolvingJavaTypeContext, diagnostics); + validationSettings, nativeImagesSettings, resolutionContext, resolvingJavaTypeContext, + diagnostics); } } return resolvedJavaType; @@ -416,8 +426,9 @@ private ResolvedJavaTypeInfo validateExpression(Expression expression, Section o } private ResolvedJavaTypeInfo validateExpressionParts(Parts parts, Section ownerSection, Template template, - String projectUri, QuteValidationSettings validationSettings, ResolutionContext resolutionContext, - ResolvingJavaTypeContext resolvingJavaTypeContext, List diagnostics) { + String projectUri, QuteValidationSettings validationSettings, QuteNativeSettings nativeImagesSettings, + ResolutionContext resolutionContext, ResolvingJavaTypeContext resolvingJavaTypeContext, + List diagnostics) { ResolvedJavaTypeInfo baseType = null; String namespace = null; for (int i = 0; i < parts.getChildCount(); i++) { @@ -476,7 +487,8 @@ private ResolvedJavaTypeInfo validateExpressionParts(Parts parts, Section ownerS } } - baseType = validateMemberPart(current, ownerSection, template, projectUri, validationSettings, + JavaTypeFilter filter = NativeModeUtils.getJavaTypeFilter(baseType, nativeImagesSettings); + baseType = validateMemberPart(current, ownerSection, template, projectUri, validationSettings, filter, resolutionContext, baseType, iter, diagnostics, resolvingJavaTypeContext); break; } @@ -629,19 +641,19 @@ private ResolvedJavaTypeInfo validateObjectPart(String namespace, ObjectPart obj * @return the Java type returned by the member part and null otherwise. */ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, Template template, - String projectUri, QuteValidationSettings validationSettings, ResolutionContext resolutionContext, - ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iterableOfType, List diagnostics, - ResolvingJavaTypeContext resolvingJavaTypeContext) { + String projectUri, QuteValidationSettings validationSettings, JavaTypeFilter filter, + ResolutionContext resolutionContext, ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iterableOfType, + List diagnostics, ResolvingJavaTypeContext resolvingJavaTypeContext) { if (part.getPartKind() == PartKind.Method) { // Validate method part // ex : {foo.method(1, 2)} - return validateMethodPart((MethodPart) part, ownerSection, template, projectUri, validationSettings, + return validateMethodPart((MethodPart) part, ownerSection, template, projectUri, validationSettings, filter, resolutionContext, baseType, iterableOfType, diagnostics, resolvingJavaTypeContext); } // Validate property part // ex : {foo.property} return validatePropertyPart((PropertyPart) part, ownerSection, template, projectUri, resolutionContext, - baseType, iterableOfType, diagnostics, resolvingJavaTypeContext); + baseType, iterableOfType, filter, diagnostics, resolvingJavaTypeContext); } /** @@ -654,6 +666,7 @@ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, * @param resolutionContext the resolution context. * @param baseType the base object type. * @param iterableOfType the iterable of type. + * @param nativeImagesSettings * @param diagnostics the diagnostic list to fill. * @param resolvingJavaTypeContext the resolving Java type context. * @@ -661,12 +674,13 @@ private ResolvedJavaTypeInfo validateMemberPart(Part part, Section ownerSection, */ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section ownerSection, Template template, String projectUri, ResolutionContext resolutionContext, ResolvedJavaTypeInfo baseType, - ResolvedJavaTypeInfo iterableOfType, List diagnostics, + ResolvedJavaTypeInfo iterableOfType, JavaTypeFilter filter, List diagnostics, ResolvingJavaTypeContext resolvingJavaTypeContext) { if (baseType == null) { return null; } - JavaMemberInfo javaMember = javaCache.findMember(part, baseType, projectUri); + JavaMemberResult result = javaCache.findProperty(part, baseType, filter, projectUri); + JavaMemberInfo javaMember = result.getMember(); if (javaMember == null) { // ex : {@org.acme.Item item} // "{item.XXXX} @@ -675,9 +689,52 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own part.getPartName(), baseType.getSignature()); diagnostics.add(diagnostic); return null; + } else if (filter.isInNativeMode()) { + // The property (field, method) has been retrieved, check if it is a valid in + // native mode + if (!filter.isJavaTypeAllowed(baseType)) { + Range range = QutePositionUtility.createRange(part); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.PropertyNotSupportedInNativeMode, part.getPartName(), baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + if (!filter.isSuperClassAllowed(javaMember)) { + // @TemplateData(ignoreSuperclasses = true) + Range range = QutePositionUtility.createRange(part); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.InheritedPropertyNotSupportedInNativeMode, part.getPartName(), + baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + + JavaMemberdAccess access = filter.getJavaMemberAccess(javaMember); + switch (access) { + case FORBIDDEN_BY_TEMPLATE_DATA_IGNORE: { + // TODO : support error message + return null; + } + + case FORBIDDEN_BY_REGISTER_FOR_REFLECTION_FIELDS: + case FORBIDDEN_BY_REGISTER_FOR_REFLECTION_METHODS: { + Range range = QutePositionUtility.createRange(part); + QuteErrorCode errorCode = javaMember.getJavaElementKind() == JavaElementKind.METHOD + ? QuteErrorCode.ForbiddenByRegisterForReflectionMethods + : QuteErrorCode.ForbiddenByRegisterForReflectionFields; + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, errorCode, part.getPartName(), + baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + + default: + } } + String memberType = javaMember.resolveJavaElementType(iterableOfType); - return validateJavaTypePart(part, ownerSection, projectUri, diagnostics, resolvingJavaTypeContext, memberType, null); + return validateJavaTypePart(part, ownerSection, projectUri, diagnostics, resolvingJavaTypeContext, memberType, + null); } /** @@ -687,6 +744,7 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own * @param ownerSection the owner section and null otherwise. * @param template the template. * @param projectUri the project Uri. + * @param nativeImagesSettings * @param resolutionContext the resolution context. * @param baseType the base object type. * @param iterableOfType the iterable of type. @@ -696,9 +754,9 @@ private ResolvedJavaTypeInfo validatePropertyPart(PropertyPart part, Section own * @return the Java type returned by the member part and null otherwise. */ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section ownerSection, Template template, - String projectUri, QuteValidationSettings validationSettings, ResolutionContext resolutionContext, - ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iter, List diagnostics, - ResolvingJavaTypeContext resolvingJavaTypeContext) { + String projectUri, QuteValidationSettings validationSettings, JavaTypeFilter filter, + ResolutionContext resolutionContext, ResolvedJavaTypeInfo baseType, ResolvedJavaTypeInfo iter, + List diagnostics, ResolvingJavaTypeContext resolvingJavaTypeContext) { if (methodPart.isInfixNotation()) { // ex : foo or '1' // The method part used with infix notation must have one parameter @@ -718,8 +776,8 @@ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section o ResolvedJavaTypeInfo result = null; Expression expression = parameter.getJavaTypeExpression(); if (expression != null) { - result = validateExpression(expression, ownerSection, template, validationSettings, resolutionContext, - resolvingJavaTypeContext, diagnostics); + result = validateExpression(expression, ownerSection, template, validationSettings, + filter.getNativeSettings(), resolutionContext, resolvingJavaTypeContext, diagnostics); } if (result == null) { undefinedType = true; @@ -739,7 +797,8 @@ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section o // to the computed parameter types String methodName = methodPart.getPartName(); String namespace = methodPart.getNamespace(); - JavaMemberResult result = javaCache.findMethod(baseType, namespace, methodName, parameterTypes, projectUri); + JavaMemberResult result = javaCache.findMethod(baseType, namespace, methodName, parameterTypes, filter, + projectUri); JavaMethodInfo method = (JavaMethodInfo) result.getMember(); if (method == null) { QuteErrorCode errorCode = QuteErrorCode.UnknownMethod; @@ -772,6 +831,45 @@ private ResolvedJavaTypeInfo validateMethodPart(MethodPart methodPart, Section o Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, errorCode, methodName, arg); diagnostics.add(diagnostic); return null; + } else if (!method.isVirtual() && filter.isInNativeMode()) { + // The (non virtual) method has been retrieved, check if it is a valid in native + // mode + if (!filter.isJavaTypeAllowed(baseType)) { + Range range = QutePositionUtility.createRange(methodPart); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.MethodNotSupportedInNativeMode, method.getName(), baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + if (!filter.isSuperClassAllowed(method)) { + Range range = QutePositionUtility.createRange(methodPart); + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, + QuteErrorCode.InheritedMethodNotSupportedInNativeMode, method.getName(), + baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + JavaMemberdAccess access = filter.getJavaMemberAccess(method); + switch (access) { + case FORBIDDEN_BY_TEMPLATE_DATA_IGNORE: { + // TODO : support error message + return null; + } + + case FORBIDDEN_BY_REGISTER_FOR_REFLECTION_FIELDS: + case FORBIDDEN_BY_REGISTER_FOR_REFLECTION_METHODS: { + Range range = QutePositionUtility.createRange(methodPart); + QuteErrorCode errorCode = method.getJavaElementKind() == JavaElementKind.METHOD + ? QuteErrorCode.ForbiddenByRegisterForReflectionMethods + : QuteErrorCode.ForbiddenByRegisterForReflectionFields; + Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, errorCode, method.getName(), + baseType.getSignature()); + diagnostics.add(diagnostic); + return null; + } + + default: + } } boolean matchVirtualMethod = result.isMatchVirtualMethod(); diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java index 83878c697..9c639f5be 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteLanguageService.java @@ -41,6 +41,7 @@ import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; import com.redhat.qute.settings.QuteInlayHintSettings; +import com.redhat.qute.settings.QuteNativeSettings; import com.redhat.qute.settings.QuteValidationSettings; import com.redhat.qute.settings.SharedSettings; @@ -85,17 +86,19 @@ public QuteLanguageService(JavaDataModelCache javaCache) { /** * Returns completion list for the given position * - * @param template the Qute template - * @param position the position where completion was triggered - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. - * @param cancelChecker the cancel checker + * @param template the Qute template + * @param position the position where completion was triggered + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native image settings. + * @param cancelChecker the cancel checker * @return completion list for the given position */ public CompletableFuture doComplete(Template template, Position position, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { - return completions.doComplete(template, position, completionSettings, formattingSettings, cancelChecker); + QuteNativeSettings nativeImagesSettings, CancelChecker cancelChecker) { + return completions.doComplete(template, position, completionSettings, formattingSettings, nativeImagesSettings, + cancelChecker); } public CompletableFuture> getCodeLens(Template template, SharedSettings settings, @@ -138,14 +141,17 @@ public List findSymbolInformations(Template template, CancelC /** * Validate the given Qute template. * - * @param template the Qute template. - * @param validationSettings the validation settings. - * @param cancelChecker the cancel checker. + * @param template the Qute template. + * @param validationSettings the validation settings. + * @param nativeImagesSettings the native image settings. + * @param cancelChecker the cancel checker. * @return the result of the validation. */ public List doDiagnostics(Template template, QuteValidationSettings validationSettings, - ResolvingJavaTypeContext resolvingJavaTypeFutures, CancelChecker cancelChecker) { - return diagnostics.doDiagnostics(template, validationSettings, resolvingJavaTypeFutures, cancelChecker); + QuteNativeSettings nativeImagesSettings, ResolvingJavaTypeContext resolvingJavaTypeFutures, + CancelChecker cancelChecker) { + return diagnostics.doDiagnostics(template, validationSettings, nativeImagesSettings, resolvingJavaTypeFutures, + cancelChecker); } public List findReferences(Template template, Position position, ReferenceContext context, diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java index 65856aa9a..9c881292c 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java @@ -59,8 +59,12 @@ import com.redhat.qute.project.datamodel.resolvers.FieldValueResolver; import com.redhat.qute.project.datamodel.resolvers.MethodValueResolver; import com.redhat.qute.project.datamodel.resolvers.ValueResolver; +import com.redhat.qute.services.nativemode.JavaTypeFilter; +import com.redhat.qute.services.nativemode.JavaTypeFilter.JavaMemberdAccess; +import com.redhat.qute.services.nativemode.NativeModeUtils; import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; +import com.redhat.qute.settings.QuteNativeSettings; import com.redhat.qute.utils.QutePositionUtility; import com.redhat.qute.utils.StringUtils; import com.redhat.qute.utils.UserTagUtils; @@ -87,31 +91,32 @@ public QuteCompletionsForExpression(QuteCompletionForTagSection completionForTag * Returns the completion result of the given offset inside the given * expression. * - * @param expression the expression where the completion has been - * triggered and null otherwise. - * @param nodeExpression the node expression where the completion has been - * triggered and null otherwise. - * @param template the owner template. - * @param offset the offset where the completion has been triggered. - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. - * - * @param cancelChecker the cancel checker. + * @param expression the expression where the completion has been + * triggered and null otherwise. + * @param nodeExpression the node expression where the completion has been + * triggered and null otherwise. + * @param template the owner template. + * @param offset the offset where the completion has been + * triggered. + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native images settings. + * @param cancelChecker the cancel checker. * * @return the completion result as future. */ public CompletableFuture doCompleteExpression(CompletionRequest completionRequest, Expression expression, Node nodeExpression, Template template, int offset, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { + QuteNativeSettings nativeImagesSettings, CancelChecker cancelChecker) { if (nodeExpression == null) { // ex : { | } return doCompleteExpressionForObjectPart(completionRequest, expression, null, null, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } else if (expression == null) { // ex : {| return doCompleteExpressionForObjectPart(completionRequest, null, null, nodeExpression, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } if (nodeExpression.getKind() == NodeKind.ExpressionPart) { @@ -120,13 +125,13 @@ public CompletableFuture doCompleteExpression(CompletionRequest case Object: // ex : { ite|m } return doCompleteExpressionForObjectPart(null, expression, part.getNamespace(), part, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); case Property: { // ex : { item.n| } // ex : { item.n|ame } Parts parts = part.getParent(); return doCompleteExpressionForMemberPart(part, parts, template, false, completionSettings, - formattingSettings); + formattingSettings, nativeImagesSettings); } case Method: { // ex : { item.getN|ame() } @@ -135,12 +140,12 @@ public CompletableFuture doCompleteExpression(CompletionRequest if (methodPart.isInParameters(offset)) { // ex : { item.getName(|) } return doCompleteExpressionForObjectPart(null, expression, null, null, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } // ex : { item.getN|ame() } Parts parts = part.getParent(); return doCompleteExpressionForMemberPart(part, parts, template, methodPart.isInfixNotation(), - completionSettings, formattingSettings); + completionSettings, formattingSettings, nativeImagesSettings); } default: break; @@ -157,7 +162,7 @@ public CompletableFuture doCompleteExpression(CompletionRequest Parts parts = (Parts) nodeExpression; Part part = parts.getPartAt(offset + 1); return doCompleteExpressionForObjectPart(null, expression, parts.getNamespace(), part, offset, template, - completionSettings, formattingSettings, cancelChecker); + completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } case '.': { // ex : { item.| } @@ -166,7 +171,7 @@ public CompletableFuture doCompleteExpression(CompletionRequest Parts parts = (Parts) nodeExpression; Part part = parts.getPartAt(offset + 1); return doCompleteExpressionForMemberPart(part, parts, template, false, completionSettings, - formattingSettings); + formattingSettings, nativeImagesSettings); } case ' ': { // Infix notation @@ -180,12 +185,12 @@ public CompletableFuture doCompleteExpression(CompletionRequest && ((MethodPart) previousPart).isOperator()) { // ex : { item ?: |name } return doCompleteExpressionForObjectPart(null, expression, parts.getNamespace(), part, offset, - template, completionSettings, formattingSettings, cancelChecker); + template, completionSettings, formattingSettings, nativeImagesSettings, cancelChecker); } // ex : { item | } // ex : { item |name } return doCompleteExpressionForMemberPart(part, parts, template, true, completionSettings, - formattingSettings); + formattingSettings, nativeImagesSettings); } } } @@ -196,20 +201,21 @@ public CompletableFuture doCompleteExpression(CompletionRequest * Returns the completion result for Java fields, methods of the Java type * class, interface of the given part part * - * @param part the part. - * @param parts the owner parts. - * @param template the owner template. - * @param infixNotation true if the completion generates completion items - * for infix notation (with spaces) and false - * otherwise. - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. + * @param part the part. + * @param parts the owner parts. + * @param template the owner template. + * @param infixNotation true if the completion generates completion items + * for infix notation (with spaces) and false + * otherwise. + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native image settings. * * @return the completion list. */ private CompletableFuture doCompleteExpressionForMemberPart(Part part, Parts parts, Template template, boolean infixNotation, QuteCompletionSettings completionSettings, - QuteFormattingSettings formattingSettings) { + QuteFormattingSettings formattingSettings, QuteNativeSettings nativeImagesSettings) { int start = part != null ? part.getStart() : parts.getEnd(); int end = part != null ? part.getEnd() : parts.getEnd(); String projectUri = template.getProjectUri(); @@ -230,13 +236,14 @@ private CompletableFuture doCompleteExpressionForMemberPart(Part return EMPTY_COMPLETION; } return doCompleteForJavaTypeMembers(resolvedIterableType, start, end, template, - infixNotation, completionSettings, formattingSettings); + infixNotation, completionSettings, formattingSettings, + nativeImagesSettings); }); } // Completion for member of the given Java class // ex : org.acme.Item CompletionList list = doCompleteForJavaTypeMembers(resolvedType, start, end, template, - infixNotation, completionSettings, formattingSettings); + infixNotation, completionSettings, formattingSettings, nativeImagesSettings); return CompletableFuture.completedFuture(list); }); @@ -246,46 +253,56 @@ private CompletableFuture doCompleteExpressionForMemberPart(Part * Returns the completion result for Java fields, methods of the given Java type * class, interface resolvedType * - * @param resolvedType the Java class, interface. - * @param start the part start index to replace. - * @param end the part end index to replace. - * @param template the owner Qute template. - * @param infixNotation true if the completion generates completion items - * for infix notation (with spaces) and false - * otherwise. - * @param completionSettings the completion settings. - * @param formattingSettings the formatting settings. + * @param baseType the Java class, interface. + * @param start the part start index to replace. + * @param end the part end index to replace. + * @param template the owner Qute template. + * @param infixNotation true if the completion generates completion items + * for infix notation (with spaces) and false + * otherwise. + * @param completionSettings the completion settings. + * @param formattingSettings the formatting settings. + * @param nativeImagesSettings the native image settings. * * @return the completion list. */ - private CompletionList doCompleteForJavaTypeMembers(ResolvedJavaTypeInfo resolvedType, int start, int end, + private CompletionList doCompleteForJavaTypeMembers(ResolvedJavaTypeInfo baseType, int start, int end, Template template, boolean infixNotation, QuteCompletionSettings completionSettings, - QuteFormattingSettings formattingSettings) { + QuteFormattingSettings formattingSettings, QuteNativeSettings nativeImagesSettings) { CompletionList list = new CompletionList(); list.setItems(new ArrayList<>()); Range range = QutePositionUtility.createRange(start, end, template); String projectUri = template.getProjectUri(); Set existingProperties = new HashSet<>(); + Set existingMethodSignatures = new HashSet<>(); - if (!infixNotation) { - // Completion for Java fields - fillCompletionFields(resolvedType, range, projectUri, existingProperties, list); - } + JavaTypeFilter filter = NativeModeUtils.getJavaTypeFilter(baseType, nativeImagesSettings); + if (filter.isJavaTypeAllowed(baseType)) { - // Completion for Java methods - Set existingMethodSignatures = new HashSet<>(); - fillCompletionMethods(resolvedType, range, projectUri, infixNotation, completionSettings, formattingSettings, - existingProperties, existingMethodSignatures, list); + // Some fields and methods from Java reflection must be shown. + // - in NO native images mode : all fields and methods must be shown + // - in native images mode : some fields and methods must be shown according the + // @TemplateData / @RegisterForReflection annotations. + + if (!infixNotation) { + // Completion for Java fields + fillCompletionFields(baseType, filter, range, projectUri, existingProperties, list); + } + + // Completion for Java methods + fillCompletionMethods(baseType, filter, range, projectUri, infixNotation, completionSettings, + formattingSettings, existingProperties, existingMethodSignatures, list); + } // Completion for virtual methods (from value resolvers) // - Static value resolvers (orEmpty, etc) // - Dynamic value resolvers (from @TemplateExtension) - List resolvers = javaCache.getResolversFor(resolvedType, projectUri); + List resolvers = javaCache.getResolversFor(baseType, projectUri); for (MethodValueResolver method : resolvers) { if (method.isValidName() && isValidInfixNotation(method, infixNotation)) { - fillCompletionMethod(method, range, infixNotation, completionSettings, formattingSettings, list); + fillCompletionMethod(method, null, range, infixNotation, completionSettings, formattingSettings, list); } } return list; @@ -294,18 +311,20 @@ private CompletionList doCompleteForJavaTypeMembers(ResolvedJavaTypeInfo resolve /** * Fill completion list list with the methods of the given Java * type resolvedType. - * - * @param resolvedType the Java type class, interface. + * + * @param rootBaseType the Java root type class, interface. + * @param baseType the Java type class, interface. * @param range the range. + * @param ignoreSuperclasses * @param projectUri the project Uri * @param existingProperties the existing properties and method, field * signature. * @param list the completion list. */ - private void fillCompletionFields(ResolvedJavaTypeInfo resolvedType, Range range, String projectUri, - Set existingProperties, CompletionList list) { + private void fillCompletionFields(ResolvedJavaTypeInfo baseType, JavaTypeFilter filter, Range range, + String projectUri, Set existingProperties, CompletionList list) { // Fill completion with Java type fields. - for (JavaFieldInfo field : resolvedType.getFields()) { + for (JavaFieldInfo field : baseType.getFields()) { String fieldName = field.getName(); // It is necessary to check if the name of the given field has already been // added in the completion @@ -314,25 +333,31 @@ private void fillCompletionFields(ResolvedJavaTypeInfo resolvedType, Range range // class A extends B {String foo;} // class B {String foo;} if (!existingProperties.contains(fieldName)) { - fillCompletionField(field, range, list); + fillCompletionField(field, filter, range, list); existingProperties.add(fieldName); } } - // Fill completion with extended Java type fields. - List extendedTypes = resolvedType.getExtendedTypes(); - if (extendedTypes != null) { - for (String extendedType : extendedTypes) { - ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) - .getNow(null); - if (resolvedExtendedType != null) { - fillCompletionFields(resolvedExtendedType, range, projectUri, existingProperties, list); + if (!isIgnoreSuperclasses(filter)) { + // Fill completion with extended Java type fields. + List extendedTypes = baseType.getExtendedTypes(); + if (extendedTypes != null) { + for (String extendedType : extendedTypes) { + ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) + .getNow(null); + if (resolvedExtendedType != null) { + fillCompletionFields(resolvedExtendedType, filter, range, projectUri, existingProperties, list); + } } } } } - private static CompletionItem fillCompletionField(JavaFieldInfo field, Range range, CompletionList list) { - return fillCompletionField(field, null, false, range, list); + private static void fillCompletionField(JavaFieldInfo field, JavaTypeFilter filter, Range range, + CompletionList list) { + if (filter != null && filter.getJavaMemberAccess(field) != JavaMemberdAccess.ALLOWED) { + return; + } + fillCompletionField(field, null, false, range, list); } private static CompletionItem fillCompletionField(JavaFieldInfo field, String namespace, @@ -355,9 +380,11 @@ private static CompletionItem fillCompletionField(JavaFieldInfo field, String na /** * Fill completion list list with the methods of the given Java * type resolvedType. - * - * @param resolvedType the Java type class, interface. + * + * @param rootBaseType the Java root type class, interface. + * @param baseType the Java type class, interface. * @param range the range. + * @param ignoreSuperclasses * @param projectUri the project Uri * @param infixNotation true if the completion generates completion items * for infix notation (with spaces) and false @@ -368,10 +395,11 @@ private static CompletionItem fillCompletionField(JavaFieldInfo field, String na * signature. * @param list the completion list. */ - private void fillCompletionMethods(ResolvedJavaTypeInfo resolvedType, Range range, String projectUri, - boolean infixNotation, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - Set existingProperties, Set existingMethodSignatures, CompletionList list) { - for (JavaMethodInfo method : resolvedType.getMethods()) { + private void fillCompletionMethods(ResolvedJavaTypeInfo baseType, JavaTypeFilter filter, Range range, + String projectUri, boolean infixNotation, QuteCompletionSettings completionSettings, + QuteFormattingSettings formattingSettings, Set existingProperties, + Set existingMethodSignatures, CompletionList list) { + for (JavaMethodInfo method : baseType.getMethods()) { if (isValidInfixNotation(method, infixNotation)) { String methodSignature = method.getSignature(); if (!existingMethodSignatures.contains(methodSignature)) { @@ -392,23 +420,32 @@ private void fillCompletionMethods(ResolvedJavaTypeInfo resolvedType, Range rang } // Completion for method name (getValue) - fillCompletionMethod(method, range, infixNotation, completionSettings, formattingSettings, list); + fillCompletionMethod(method, filter, range, infixNotation, completionSettings, formattingSettings, + list); } } } - List extendedTypes = resolvedType.getExtendedTypes(); - if (extendedTypes != null) { - for (String extendedType : extendedTypes) { - ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) - .getNow(null); - if (resolvedExtendedType != null) { - fillCompletionMethods(resolvedExtendedType, range, projectUri, infixNotation, completionSettings, - formattingSettings, existingProperties, existingMethodSignatures, list); + + if (!isIgnoreSuperclasses(filter)) { + List extendedTypes = baseType.getExtendedTypes(); + if (extendedTypes != null) { + for (String extendedType : extendedTypes) { + ResolvedJavaTypeInfo resolvedExtendedType = javaCache.resolveJavaType(extendedType, projectUri) + .getNow(null); + if (resolvedExtendedType != null) { + fillCompletionMethods(resolvedExtendedType, filter, range, projectUri, infixNotation, + completionSettings, formattingSettings, existingProperties, existingMethodSignatures, + list); + } } } } } + private static boolean isIgnoreSuperclasses(JavaTypeFilter filter) { + return filter != null && filter.isIgnoreSuperclasses(); + } + private static boolean isValidInfixNotation(JavaMethodInfo method, boolean infixNotation) { if (!infixNotation) { return true; @@ -417,10 +454,13 @@ private static boolean isValidInfixNotation(JavaMethodInfo method, boolean infix return nbParameters == 1; } - private static CompletionItem fillCompletionMethod(JavaMethodInfo method, Range range, boolean infixNotation, - QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, CompletionList list) { - return fillCompletionMethod(method, null, false, range, infixNotation, completionSettings, formattingSettings, - list); + private static void fillCompletionMethod(JavaMethodInfo method, JavaTypeFilter filter, Range range, + boolean infixNotation, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, + CompletionList list) { + if (filter != null && filter.getJavaMemberAccess(method) != JavaMemberdAccess.ALLOWED) { + return; + } + fillCompletionMethod(method, null, false, range, infixNotation, completionSettings, formattingSettings, list); } private static CompletionItem fillCompletionMethod(JavaMethodInfo method, String namespace, @@ -501,7 +541,7 @@ private static String createMethodSnippet(JavaMethodInfo method, boolean infixNo private CompletableFuture doCompleteExpressionForObjectPart(CompletionRequest completionRequest, Expression expression, String namespace, Node part, int offset, Template template, QuteCompletionSettings completionSettings, QuteFormattingSettings formattingSettings, - CancelChecker cancelChecker) { + QuteNativeSettings nativeImagesSettings, CancelChecker cancelChecker) { // Completion for root object int partStart = part != null && part.getKind() != NodeKind.Text ? part.getStart() : offset; int partEnd = part != null && part.getKind() != NodeKind.Text ? part.getEnd() : offset; @@ -523,7 +563,8 @@ private CompletableFuture doCompleteExpressionForObjectPart(Comp // Collect declared model inside section, let, etc Set existingVars = new HashSet<>(); doCompleteExpressionForObjectPartWithParentNodes(part, expression != null ? expression : part, range, - offset, template.getProjectUri(), existingVars, completionSettings, formattingSettings, list); + offset, template.getProjectUri(), existingVars, completionSettings, formattingSettings, + nativeImagesSettings, list); // Section tag if (completionRequest != null) { @@ -624,7 +665,7 @@ public void doCompleteNamespaceResolvers(String namespace, Template template, Ra private void doCompleteExpressionForObjectPartWithParentNodes(Node part, Node node, Range range, int offset, String projectUri, Set existingVars, QuteCompletionSettings completionSettings, - QuteFormattingSettings formattingSettings, CompletionList list) { + QuteFormattingSettings formattingSettings, QuteNativeSettings nativeImagesSettings, CompletionList list) { Section section = node != null ? node.getParentSection() : null; if (section == null) { return; @@ -728,9 +769,11 @@ private void doCompleteExpressionForObjectPartWithParentNodes(Node part, Node no ResolvedJavaTypeInfo withJavaTypeInfo = javaCache.resolveJavaType(object, projectUri) .getNow(null); if (withJavaTypeInfo != null) { - fillCompletionFields(withJavaTypeInfo, range, projectUri, existingVars, list); - fillCompletionMethods(withJavaTypeInfo, range, projectUri, false, completionSettings, - formattingSettings, existingVars, new HashSet<>(), list); + JavaTypeFilter filter = NativeModeUtils.getJavaTypeFilter(withJavaTypeInfo, + nativeImagesSettings); + fillCompletionFields(withJavaTypeInfo, filter, range, projectUri, existingVars, list); + fillCompletionMethods(withJavaTypeInfo, filter, range, projectUri, false, + completionSettings, formattingSettings, existingVars, new HashSet<>(), list); } } break; @@ -739,7 +782,7 @@ private void doCompleteExpressionForObjectPartWithParentNodes(Node part, Node no } } doCompleteExpressionForObjectPartWithParentNodes(part, section, range, offset, projectUri, existingVars, - completionSettings, formattingSettings, list); + completionSettings, formattingSettings, nativeImagesSettings, list); } private void doCompleteExpressionForObjectPartWithParameterAlias(Template template, Range range, diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java index 2773c93f4..1de838b86 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/diagnostics/QuteErrorCode.java @@ -25,7 +25,7 @@ public enum QuteErrorCode implements IQuteErrorCode { // Error code for operator InvalidOperator("Invalid `{0}` operator for section `#{1}`. Allowed operators are `{2}`."), // - + // Error code for object, property, method parts UndefinedObject("`{0}` cannot be resolved to an object."), // UnknownType("`{0}` cannot be resolved to a type."), // @@ -38,9 +38,20 @@ public enum QuteErrorCode implements IQuteErrorCode { InvalidVirtualMethod( "The virtual method `{0}` in the type `{1}` is not applicable for the base object `{2}` type."), // InfixNotationParameterRequired("A parameter for the infix notation method `{0}` is required."), // - InvalidMethodInfixNotation("The method `{0}` cannot be used with infix notation, because it has not `1` parameter."), // - + InvalidMethodInfixNotation( + "The method `{0}` cannot be used with infix notation, because it does not have exactly `1` parameter."), // + MethodNotSupportedInNativeMode("Method `{0}` of `{1}` Java type cannot be used in native image mode."), // + InheritedMethodNotSupportedInNativeMode( + "Inherited method `{0}` of `{1}` Java type cannot be used in native image mode because Java type is annotated with `@TemplateData#ignoreSuperclasses = true`."), // + ForbiddenByRegisterForReflectionMethods( + "The method `{0}` of `{1}` Java type cannot be used in native image mode because Java type is annotated with `@RegisterForReflection#methods = false`."), // + UnknownProperty("`{0}` cannot be resolved or is not a field of `{1}` Java type."), // + PropertyNotSupportedInNativeMode("Property `{0}` of `{1}` Java type cannot be used in native image mode."), // + InheritedPropertyNotSupportedInNativeMode( + "Inherited property `{0}` of `{1}` Java type cannot be used in native image mode because Java type is annotated with `@TemplateData#ignoreSuperclasses = true`."), // + ForbiddenByRegisterForReflectionFields( + "The field `{0}` of `{1}` Java type cannot be used in native image mode because Java type is annotated with `@RegisterForReflection#fields = false`."), // // Error code for #for / #each section IterationError("Iteration error: '{'{0}'}' resolved to [{1}] which is not iterable."), diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilter.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilter.java new file mode 100644 index 000000000..033cd1202 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilter.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.services.nativemode; + +import com.redhat.qute.commons.JavaMemberInfo; +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.settings.QuteNativeSettings; + +public interface JavaTypeFilter { + + public enum JavaMemberdAccess { + ALLOWED, // + FORBIDDEN_BY_TEMPLATE_DATA_IGNORE, // + FORBIDDEN_BY_REGISTER_FOR_REFLECTION_FIELDS, // + FORBIDDEN_BY_REGISTER_FOR_REFLECTION_METHODS; + }; + + boolean isJavaTypeAllowed(ResolvedJavaTypeInfo member); + + JavaMemberdAccess getJavaMemberAccess(JavaMemberInfo member); + + boolean isSuperClassAllowed(JavaMemberInfo member); + + /** + * Returns true if super classes must be ignored while searching fields method + * of the given Java base type and false otherwise. + * + * @return true if super classes must be ignored while searching fields method + * of the given Java base type and false otherwise. + */ + boolean isIgnoreSuperclasses(); + + boolean isInNativeMode(); + + QuteNativeSettings getNativeSettings(); + +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNativeMode.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNativeMode.java new file mode 100644 index 000000000..cc5369b6a --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNativeMode.java @@ -0,0 +1,139 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.services.nativemode; + +import java.util.List; + +import com.redhat.qute.commons.JavaFieldInfo; +import com.redhat.qute.commons.JavaMemberInfo; +import com.redhat.qute.commons.JavaMethodInfo; +import com.redhat.qute.commons.RegisterForReflectionAnnotation; +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.commons.TemplateDataAnnotation; +import com.redhat.qute.settings.QuteNativeSettings; + +/** + * Java type filter in native mode. + * + * @author Angelo ZERR + * + */ +public class JavaTypeFilterInNativeMode implements JavaTypeFilter { + + private final QuteNativeSettings nativeSettings; + + private final ResolvedJavaTypeInfo rootJavaType; + + JavaTypeFilterInNativeMode(ResolvedJavaTypeInfo rootJavaType, QuteNativeSettings nativeSettings) { + this.nativeSettings = nativeSettings; + this.rootJavaType = rootJavaType; + } + + @Override + public boolean isJavaTypeAllowed(ResolvedJavaTypeInfo javaType) { + // Native images mode : the java reflection is supported only if the Java type + // is + // annotated with: + // - @TemplateData + // - @RegisterForReflection + if (NativeModeUtils.canSupportJavaReflectionInNativeMode(javaType)) { + return true; + } + if (javaType != rootJavaType) { + // TODO : target + + } + return false; + } + + public JavaMemberdAccess getJavaMemberAccess(JavaFieldInfo field) { + RegisterForReflectionAnnotation annotation = rootJavaType.getRegisterForReflectionAnnotation(); + if (annotation != null) { + if (!annotation.isFields()) { + // @RegisterForReflection(fields = false) + return JavaMemberdAccess.FORBIDDEN_BY_REGISTER_FOR_REFLECTION_FIELDS; + } + } + return JavaMemberdAccess.ALLOWED; + } + + @Override + public JavaMemberdAccess getJavaMemberAccess(JavaMemberInfo member) { + switch (member.getJavaElementKind()) { + case FIELD: + return getJavaFieldAccess((JavaFieldInfo) member); + case METHOD: + return getJavaMethodAccess((JavaMethodInfo) member); + default: + return JavaMemberdAccess.ALLOWED; + } + } + + private JavaMemberdAccess getJavaFieldAccess(JavaFieldInfo field) { + RegisterForReflectionAnnotation annotation = rootJavaType.getRegisterForReflectionAnnotation(); + if (annotation != null) { + if (!annotation.isFields()) { + // @RegisterForReflection(fields = false) + return JavaMemberdAccess.FORBIDDEN_BY_REGISTER_FOR_REFLECTION_FIELDS; + } + } + return JavaMemberdAccess.ALLOWED; + } + + private JavaMemberdAccess getJavaMethodAccess(JavaMethodInfo method) { + RegisterForReflectionAnnotation annotation = rootJavaType.getRegisterForReflectionAnnotation(); + if (annotation != null) { + if (!annotation.isMethods()) { + // @RegisterForReflection(methods = false) + return JavaMemberdAccess.FORBIDDEN_BY_REGISTER_FOR_REFLECTION_METHODS; + } + } + return JavaMemberdAccess.ALLOWED; + } + + /** + * Returns true if super classes must be ignored while searching fields method + * of the given Java base type and false otherwise. + * + * @return true if super classes must be ignored while searching fields method + * of the given Java base type and false otherwise. + */ + public boolean isIgnoreSuperclasses() { + // Here we are in native images context, the Java Type must be annotated with + // @TempateData#ignoreSuperclasses : + + // @TemplateData(ignoreSuperclasses = true) + // public class Book extends PanacheEntity + List annotations = rootJavaType.getTemplateDataAnnotations(); + if (annotations != null) { + return annotations.stream() // + .anyMatch(TemplateDataAnnotation::isIgnoreSuperclasses); + } + return false; + } + + public boolean isInNativeMode() { + return nativeSettings.isEnabled(); + } + + public QuteNativeSettings getNativeSettings() { + return nativeSettings; + } + + @Override + public boolean isSuperClassAllowed(JavaMemberInfo member) { + if (isIgnoreSuperclasses()) { + return rootJavaType.equals(member.getJavaTypeInfo()); + } + return true; + } +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNoNativeMode.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNoNativeMode.java new file mode 100644 index 000000000..113ca0ac9 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/JavaTypeFilterInNoNativeMode.java @@ -0,0 +1,60 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.services.nativemode; + +import com.redhat.qute.commons.JavaMemberInfo; +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.settings.QuteNativeSettings; + +/** + * Java type filter in NO native mode (everything are allowed). + * + * @author Angelo ZERR + * + */ +public class JavaTypeFilterInNoNativeMode implements JavaTypeFilter { + + public static final JavaTypeFilter INSTANCE = new JavaTypeFilterInNoNativeMode(); + + @Override + public boolean isJavaTypeAllowed(ResolvedJavaTypeInfo member) { + return true; + } + + @Override + public JavaMemberdAccess getJavaMemberAccess(JavaMemberInfo member) { + return JavaMemberdAccess.ALLOWED; + } + + @Override + public boolean isIgnoreSuperclasses() { + // In NO native images mode, Java reflection is allowed, in other words super + // classes must not be ignored. + return false; + } + + @Override + public boolean isInNativeMode() { + return false; + } + + @Override + public QuteNativeSettings getNativeSettings() { + return null; + } + + @Override + public boolean isSuperClassAllowed(JavaMemberInfo javaMember) { + return true; + } + +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/NativeModeUtils.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/NativeModeUtils.java new file mode 100644 index 000000000..32473ee42 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/nativemode/NativeModeUtils.java @@ -0,0 +1,67 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.services.nativemode; + +import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.settings.QuteNativeSettings; + +/** + * Utilities for native mode. + * + * @author Angelo ZERR + * + */ +public class NativeModeUtils { + + /** + * Returns the java type filter according the given root java type and the + * native mode. + * + * @param rootJavaType the Java root type. + * @param nativeImagesSettings the native images settings. + * + * @return the java type filter according the given root java type and the + * native mode. + */ + public static JavaTypeFilter getJavaTypeFilter(ResolvedJavaTypeInfo rootJavaType, + QuteNativeSettings nativeImagesSettings) { + if (nativeImagesSettings != null && nativeImagesSettings.isEnabled()) { + return new JavaTypeFilterInNativeMode(rootJavaType, nativeImagesSettings); + } + return JavaTypeFilterInNoNativeMode.INSTANCE; + } + + /** + * Returns true if the Java reflection can be supported in native images mode + * and false otherwise. + * + * @param baseType the Java type. + * + * @return true if the Java reflection can be supported in native images mode + * and false otherwise. + */ + public static boolean canSupportJavaReflectionInNativeMode(ResolvedJavaTypeInfo baseType) { + if (baseType == null) { + return false; + } + if (baseType.getTemplateDataAnnotations() != null && !baseType.getTemplateDataAnnotations().isEmpty()) { + // The Java type have some @TemplateData annotation + return true; + } + if (baseType.getRegisterForReflectionAnnotation() != null) { + // The Java type is annotated with @RegisterForReflection + return true; + } + return false; + } + +} diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java index 17adc99e4..786ef1c8a 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/BaseSettings.java @@ -25,10 +25,13 @@ public class BaseSettings { private final QuteValidationSettings validationSettings; + private final QuteNativeSettings nativeImagesSettings; + public BaseSettings() { this.validationSettings = new QuteValidationSettings(); this.codeLensSettings = new QuteCodeLensSettings(); this.inlayHintSettings = new QuteInlayHintSettings(); + this.nativeImagesSettings = new QuteNativeSettings(); } /** @@ -57,4 +60,13 @@ public QuteInlayHintSettings getInlayHintSettings() { public QuteValidationSettings getValidationSettings() { return validationSettings; } + + /** + * Returns the Qute native images settings. + * + * @return the Qute native images settings. + */ + public QuteNativeSettings getNativeSettings() { + return nativeImagesSettings; + } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java index c98ce842f..2422f18c3 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteGeneralClientSettings.java @@ -13,6 +13,7 @@ import java.util.Map; +import com.google.gson.annotations.SerializedName; import com.redhat.qute.utils.JSONUtility; /** @@ -40,10 +41,23 @@ public class QuteGeneralClientSettings { private QuteValidationSettings validation; + @SerializedName(value = "native") + private QuteNativeSettings nativeImages; + + /** + * Returns the code lens settings. + * + * @return the code lens settings. + */ public QuteCodeLensSettings getCodeLens() { return codeLens; } + /** + * Returns the inlay hint settings. + * + * @return the inlay hint settings. + */ public QuteInlayHintSettings getInlayHint() { return inlayHint; } @@ -57,6 +71,15 @@ public QuteValidationSettings getValidation() { return validation; } + /** + * Returns the native images settings. + * + * @return the native images settings. + */ + public QuteNativeSettings getNativeImages() { + return nativeImages; + } + /** * Set the CodeLens setting. * @@ -66,6 +89,11 @@ public void setCodeLens(QuteCodeLensSettings codeLens) { this.codeLens = codeLens; } + /** + * Set the inlay hint settings. + * + * @param inlayHint the inlay hint settings. + */ public void setInlayHint(QuteInlayHintSettings inlayHint) { this.inlayHint = inlayHint; } @@ -79,6 +107,15 @@ public void setValidation(QuteValidationSettings validation) { this.validation = validation; } + /** + * Set the native images settings. + * + * @param nativeImages the native images settings. + */ + public void setNativeImages(QuteNativeSettings nativeImages) { + this.nativeImages = nativeImages; + } + public Map getWorkspaceFolders() { return workspaceFolders; } @@ -109,11 +146,14 @@ public static class SettingsUpdateState { private final boolean inlayHintSettingsChanged; + private boolean nativeImagesSettingsChanged; + public SettingsUpdateState(boolean validationSettingsChanged, boolean codeLensSettingsChanged, - boolean inlayHintSettingsChanged) { + boolean inlayHintSettingsChanged, boolean nativeImagesSettingsChanged) { this.validationSettingsChanged = validationSettingsChanged; this.codeLensSettingsChanged = codeLensSettingsChanged; this.inlayHintSettingsChanged = inlayHintSettingsChanged; + this.nativeImagesSettingsChanged = nativeImagesSettingsChanged; } /** @@ -143,6 +183,14 @@ public boolean isInlayHintSettingsChanged() { return inlayHintSettingsChanged; } + /** + * Returns true if native images settings changed and false otherwise. + * + * @return true if native images settings changed and false otherwise. + */ + public boolean isNativeImagesSettingsChanged() { + return nativeImagesSettingsChanged; + } } /** @@ -176,7 +224,14 @@ public static SettingsUpdateState update(SharedSettings sharedSettings, QuteGene validationSettingsChanged = true; } - return new SettingsUpdateState(validationSettingsChanged, codeLensSettingsChanged, inlayHintSettingsChanged); + // Update native images settings + boolean nativeImagesSettingsChanged = updateNativeImagesSettings(sharedSettings, clientSettings); + if (workspaceChanged) { + nativeImagesSettingsChanged = true; + } + + return new SettingsUpdateState(validationSettingsChanged, codeLensSettingsChanged, inlayHintSettingsChanged, + nativeImagesSettingsChanged); } private static boolean updateCodeLensSettings(SharedSettings sharedSettings, @@ -254,4 +309,30 @@ private static boolean updateValidationSettings(BaseSettings sharedSettings, Qut return false; } + private static boolean updateNativeImagesSettings(SharedSettings sharedSettings, + QuteGeneralClientSettings clientSettings) { + // Global nativeImages settings + boolean nativeImagesSettingsChanged = updateNativeImagesSettings(sharedSettings, + clientSettings.getNativeImages()); + // Workspace folder nativeImages settings + Map workspaceFolders = clientSettings.getWorkspaceFolders(); + if (workspaceFolders != null) { + for (Map.Entry entry : workspaceFolders + .entrySet()) { + String workspaceFolderUri = entry.getKey(); + BaseSettings settings = sharedSettings.getWorkspaceFolderSettings(workspaceFolderUri); + nativeImagesSettingsChanged |= updateNativeImagesSettings(settings, entry.getValue().getNativeImages()); + } + } + return nativeImagesSettingsChanged; + } + + private static boolean updateNativeImagesSettings(BaseSettings sharedSettings, QuteNativeSettings nativeImages) { + if (nativeImages != null && !nativeImages.equals(sharedSettings.getNativeSettings())) { + sharedSettings.getNativeSettings().update(nativeImages); + return true; + } + return false; + } + } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeSettings.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeSettings.java new file mode 100644 index 000000000..83f6183a1 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/settings/QuteNativeSettings.java @@ -0,0 +1,63 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.settings; + +/** + * Qute native image settings. + * + * @author Angelo ZERR + * + */ +public class QuteNativeSettings { + + private boolean enabled; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * Update the native images settings with the given new native images settings. + * + * @param newNativeImages the new native images settings. + */ + public void update(QuteNativeSettings newNativeImages) { + this.setEnabled(newNativeImages.isEnabled()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (enabled ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + QuteNativeSettings other = (QuteNativeSettings) obj; + if (enabled != other.enabled) + return false; + return true; + } + +} \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java index 09dd5c256..37818bcb4 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java @@ -82,6 +82,7 @@ import com.redhat.qute.settings.QuteCompletionSettings; import com.redhat.qute.settings.QuteFormattingSettings; import com.redhat.qute.settings.QuteInlayHintSettings; +import com.redhat.qute.settings.QuteNativeSettings; import com.redhat.qute.settings.QuteValidationSettings; import com.redhat.qute.settings.SharedSettings; import com.redhat.qute.utils.StringUtils; @@ -164,10 +165,11 @@ public static void testCompletionFor(String value, boolean snippetSupport, Strin completionSettings.setCapabilities(completionCapabilities); QuteFormattingSettings formattingSettings = new QuteFormattingSettings(); + QuteNativeSettings nativeImagesSettings = new QuteNativeSettings(); QuteLanguageService languageService = new QuteLanguageService(new JavaDataModelCache(projectRegistry)); CompletionList list = languageService - .doComplete(template, position, completionSettings, formattingSettings, () -> { + .doComplete(template, position, completionSettings, formattingSettings, nativeImagesSettings, () -> { }).get(); // no duplicate labels @@ -287,13 +289,20 @@ public static void testDiagnosticsFor(String value, QuteValidationSettings valid public static void testDiagnosticsFor(String value, String fileUri, String templateId, String projectUri, String templateBaseDir, boolean filter, QuteValidationSettings validationSettings, Diagnostic... expected) { + testDiagnosticsFor(value, fileUri, templateId, projectUri, templateBaseDir, filter, validationSettings, + new QuteNativeSettings(), expected); + } + + public static void testDiagnosticsFor(String value, String fileUri, String templateId, String projectUri, + String templateBaseDir, boolean filter, QuteValidationSettings validationSettings, + QuteNativeSettings nativeImagesSettings, Diagnostic... expected) { QuteProjectRegistry projectRegistry = new MockQuteProjectRegistry(); Template template = createTemplate(value, fileUri, projectUri, templateBaseDir, projectRegistry); template.setTemplateId(templateId); JavaDataModelCache javaCache = new JavaDataModelCache(projectRegistry); QuteLanguageService languageService = new QuteLanguageService(javaCache); - List actual = languageService.doDiagnostics(template, validationSettings, + List actual = languageService.doDiagnostics(template, validationSettings, nativeImagesSettings, new ResolvingJavaTypeContext(template, javaCache), () -> { }); if (expected == null) { diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java index 4b00e87c6..9c59ef505 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/project/QuteQuickStartProject.java @@ -22,6 +22,7 @@ import com.redhat.qute.commons.JavaTypeKind; import com.redhat.qute.commons.ProjectInfo; import com.redhat.qute.commons.ResolvedJavaTypeInfo; +import com.redhat.qute.commons.TemplateDataAnnotation; import com.redhat.qute.commons.datamodel.DataModelParameter; import com.redhat.qute.commons.datamodel.DataModelTemplate; import com.redhat.qute.commons.datamodel.resolvers.NamespaceResolverInfo; @@ -80,12 +81,13 @@ protected List createResolvedTypes() { ResolvedJavaTypeInfo abstractItem = createResolvedJavaTypeInfo("org.acme.AbstractItem", cache); registerField("abstractName : java.lang.String", abstractItem); registerMethod("convert(item : org.acme.AbstractItem) : int", abstractItem); - + ResolvedJavaTypeInfo baseItem = createResolvedJavaTypeInfo("org.acme.BaseItem", cache, abstractItem); registerField("base : java.lang.String", baseItem); registerField("name : java.lang.String", baseItem); registerMethod("getReviews() : java.util.List", baseItem); + // org.acme.Item ResolvedJavaTypeInfo item = createResolvedJavaTypeInfo("org.acme.Item", cache, baseItem); // Override BaseItem#name registerField("name : java.lang.String", item); @@ -105,6 +107,24 @@ protected List createResolvedTypes() { createResolvedJavaTypeInfo("java.lang.Iterable", "java.lang.Iterable", "org.acme.Item", cache); createResolvedJavaTypeInfo("org.acme.Item[]", null, "org.acme.Item", cache); + // @TemplateData + // org.acme.ItemWithTemplateData + ResolvedJavaTypeInfo itemWithTemplateData = createResolvedJavaTypeInfo("org.acme.ItemWithTemplateData", cache, + baseItem); + TemplateDataAnnotation annotation = new TemplateDataAnnotation(); + itemWithTemplateData.setTemplateDataAnnotations(Arrays.asList(annotation)); + + // @TemplateData(ignoreSuperclasses = true) + // org.acme.ItemWithTemplateData + ResolvedJavaTypeInfo itemWithTemplateDataIgnoreSubClasses = createResolvedJavaTypeInfo( + "org.acme.ItemWithTemplateDataIgnoreSubClasses", cache, baseItem); + registerField("price : java.math.BigInteger", itemWithTemplateDataIgnoreSubClasses); + registerMethod("getSubClasses() : int", itemWithTemplateDataIgnoreSubClasses); + + annotation = new TemplateDataAnnotation(); + annotation.setIgnoreSuperclasses(true); + itemWithTemplateDataIgnoreSubClasses.setTemplateDataAnnotations(Arrays.asList(annotation)); + ResolvedJavaTypeInfo iterable = createResolvedJavaTypeInfo("java.lang.Iterable", "java.lang.Iterable", "T", cache); diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/QuteCompletionInParameterDeclarationTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/QuteCompletionInParameterDeclarationTest.java index e0b418b2e..aa63c7c35 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/QuteCompletionInParameterDeclarationTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/QuteCompletionInParameterDeclarationTest.java @@ -110,19 +110,29 @@ public void completionInParameterDeclarationForJavaClassNotClosedFollowedByExpre // Without snippet testCompletionFor(template, // - false, 3, + false, 5, // Class completion c("java.lang.Integer", "java.lang.Integer integer}", r(0, 2, 0, 4)), // c("org.acme.Item", "org.acme.Item item}", r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateData", "org.acme.ItemWithTemplateData itemWithTemplateData}", + r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateDataIgnoreSubClasses", + "org.acme.ItemWithTemplateDataIgnoreSubClasses itemWithTemplateDataIgnoreSubClasses}", + r(0, 2, 0, 4)), // c("org.acme.ItemResource", "org.acme.ItemResource itemResource}", r(0, 2, 0, 4))); // With snippet support testCompletionFor(template, // true, // snippet support - 3, + 5, // Class completion c("java.lang.Integer", "java.lang.Integer ${1:integer}}$0", r(0, 2, 0, 4)), // c("org.acme.Item", "org.acme.Item ${1:item}}$0", r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateData", "org.acme.ItemWithTemplateData ${1:itemWithTemplateData}}$0", + r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateDataIgnoreSubClasses", + "org.acme.ItemWithTemplateDataIgnoreSubClasses ${1:itemWithTemplateDataIgnoreSubClasses}}$0", + r(0, 2, 0, 4)), // c("org.acme.ItemResource", "org.acme.ItemResource ${1:itemResource}}$0", r(0, 2, 0, 4))); } @@ -133,36 +143,54 @@ public void completionInParameterDeclarationForJavaClassNotClosedWithPattern() t // Without snippet testCompletionFor(template, // - false, 3, + false, 5, // Class completion c("java.lang.Integer", "java.lang.Integer integer}", r(0, 2, 0, 4)), // c("org.acme.Item", "org.acme.Item item}", r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateData", "org.acme.ItemWithTemplateData itemWithTemplateData}", + r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateDataIgnoreSubClasses", + "org.acme.ItemWithTemplateDataIgnoreSubClasses itemWithTemplateDataIgnoreSubClasses}", + r(0, 2, 0, 4)), // c("org.acme.ItemResource", "org.acme.ItemResource itemResource}", r(0, 2, 0, 4))); // With snippet support testCompletionFor(template, // true, // snippet support - 3, + 5, // Class completion c("java.lang.Integer", "java.lang.Integer ${1:integer}}$0", r(0, 2, 0, 4)), // c("org.acme.Item", "org.acme.Item ${1:item}}$0", r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateData", "org.acme.ItemWithTemplateData ${1:itemWithTemplateData}}$0", + r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateDataIgnoreSubClasses", + "org.acme.ItemWithTemplateDataIgnoreSubClasses ${1:itemWithTemplateDataIgnoreSubClasses}}$0", + r(0, 2, 0, 4)), // c("org.acme.ItemResource", "org.acme.ItemResource ${1:itemResource}}$0", r(0, 2, 0, 4))); template = "{@It|\r\n"; // Without snippet testCompletionFor(template, // - false, 2, + false, 4, // Class completion c("org.acme.Item", "org.acme.Item item}", r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateData", "org.acme.ItemWithTemplateData itemWithTemplateData}", r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateDataIgnoreSubClasses", "org.acme.ItemWithTemplateDataIgnoreSubClasses itemWithTemplateDataIgnoreSubClasses}", + r(0, 2, 0, 4)), // c("org.acme.ItemResource", "org.acme.ItemResource itemResource}", r(0, 2, 0, 4))); // With snippet support testCompletionFor(template, // true, // snippet support - 2, + 4, // Class completion c("org.acme.Item", "org.acme.Item ${1:item}}$0", r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateData", "org.acme.ItemWithTemplateData ${1:itemWithTemplateData}}$0", + r(0, 2, 0, 4)), // + c("org.acme.ItemWithTemplateDataIgnoreSubClasses", + "org.acme.ItemWithTemplateDataIgnoreSubClasses ${1:itemWithTemplateDataIgnoreSubClasses}}$0", + r(0, 2, 0, 4)), // c("org.acme.ItemResource", "org.acme.ItemResource ${1:itemResource}}$0", r(0, 2, 0, 4))); } diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInfixNotationTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInfixNotationTest.java index 8b81df4b4..517d3716c 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInfixNotationTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInfixNotationTest.java @@ -33,7 +33,7 @@ public void inValidMethodForInfix() throws Exception { "{foo codePointCount 1}"; testDiagnosticsFor(template, // d(1, 5, 1, 19, QuteErrorCode.InvalidMethodInfixNotation, - "The method `codePointCount` cannot be used with infix notation, because it has not `1` parameter.", + "The method `codePointCount` cannot be used with infix notation, because it does not have exactly `1` parameter.", DiagnosticSeverity.Error)); } diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsWithTemplateDataAnnotationTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsWithTemplateDataAnnotationTest.java new file mode 100644 index 000000000..d2639c052 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsWithTemplateDataAnnotationTest.java @@ -0,0 +1,112 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.services.diagnostics; + +import static com.redhat.qute.QuteAssert.d; + +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.junit.jupiter.api.Test; + +import com.redhat.qute.QuteAssert; +import com.redhat.qute.project.QuteQuickStartProject; +import com.redhat.qute.settings.QuteNativeSettings; +import com.redhat.qute.settings.QuteValidationSettings; + +/** + * Test with expressions with '@TemplateData'. + * + * @author Angelo ZERR + * + */ +public class QuteDiagnosticsWithTemplateDataAnnotationTest { + + @Test + public void propertyNotSupportedInNativeImages() { + QuteNativeSettings nativeImagesSettings = new QuteNativeSettings(); + nativeImagesSettings.setEnabled(true); + + // public class Item + String template = "{@org.acme.Item item}\r\n" + // + "{item.name}"; + testDiagnosticsFor(template, nativeImagesSettings, // + d(1, 6, 1, 10, QuteErrorCode.PropertyNotSupportedInNativeMode, + "Property `name` of `org.acme.Item` Java type cannot be used in native image mode.", + DiagnosticSeverity.Error)); + + // @TemplateData + // public class ItemWithTemplateData + template = "{@org.acme.ItemWithTemplateData item}\r\n" + // + "{item.name}"; + testDiagnosticsFor(template, nativeImagesSettings); + } + + @Test + public void inheritedPropertyNotSupportedInNativeImages() { + QuteNativeSettings nativeImagesSettings = new QuteNativeSettings(); + nativeImagesSettings.setEnabled(true); + + // @TemplateData(ignoreSubClasses = true) + // public class ItemWithTemplateDataIgnoreSubClasses + String template = "{@org.acme.ItemWithTemplateDataIgnoreSubClasses item}\r\n" + // + "{item.price}\r\n" + // <-- no error because price comes from ItemWithTemplateDataIgnoreSubClasses + "{item.name}"; // <-- error because name comes from super class BaseItem + testDiagnosticsFor(template, nativeImagesSettings, // + d(2, 6, 2, 10, QuteErrorCode.InheritedPropertyNotSupportedInNativeMode, + "Inherited property `name` of `org.acme.ItemWithTemplateDataIgnoreSubClasses` Java type cannot be used in native image mode because Java type is annotated with `@TemplateData#ignoreSuperclasses = true`.", + DiagnosticSeverity.Error)); + } + + @Test + public void methodNotSupportedInNativeImages() { + QuteNativeSettings nativeImagesSettings = new QuteNativeSettings(); + nativeImagesSettings.setEnabled(true); + + // public class Item + String template = "{@org.acme.Item item}\r\n" + // + "{item.isAvailable()}"; + testDiagnosticsFor(template, nativeImagesSettings, // + d(1, 6, 1, 17, QuteErrorCode.MethodNotSupportedInNativeMode, + "Method `isAvailable` of `org.acme.Item` Java type cannot be used in native image mode.", + DiagnosticSeverity.Error)); + + // @TemplateData + // public class ItemWithTemplateData + template = "{@org.acme.ItemWithTemplateData item}\r\n" + // + "{item.name}"; + testDiagnosticsFor(template, nativeImagesSettings); + } + + @Test + public void inheritedMethodNotSupportedInNativeImages() { + QuteNativeSettings nativeImagesSettings = new QuteNativeSettings(); + nativeImagesSettings.setEnabled(true); + + // @TemplateData(ignoreSubClasses = true) + // public class ItemWithTemplateDataIgnoreSubClasses + String template = "{@org.acme.ItemWithTemplateDataIgnoreSubClasses item}\r\n" + // + "{item.getSubClasses()}\r\n" + // <-- no error because size comes from + // ItemWithTemplateDataIgnoreSubClasses + "{item.getReviews()}"; // <-- error because getReviews comes from super class BaseItem + testDiagnosticsFor(template, nativeImagesSettings, // + d(2, 6, 2, 16, QuteErrorCode.InheritedMethodNotSupportedInNativeMode, + "Inherited method `getReviews` of `org.acme.ItemWithTemplateDataIgnoreSubClasses` Java type cannot be used in native image mode because Java type is annotated with `@TemplateData#ignoreSuperclasses = true`.", + DiagnosticSeverity.Error)); + } + + private static void testDiagnosticsFor(String value, QuteNativeSettings nativeImagesSettings, + Diagnostic... expected) { + QuteValidationSettings validationSettings = new QuteValidationSettings(); + QuteAssert.testDiagnosticsFor(value, "test.qute", null, QuteQuickStartProject.PROJECT_URI, + QuteAssert.TEMPLATE_BASE_DIR, false, validationSettings, nativeImagesSettings, expected); + } +}