+
+
-
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 488d7d4c7..8d26f7f87 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
@@ -235,48 +235,68 @@ public Location getJavaDefinition(QuteJavaDefinitionParams params, IJDTUtils uti
String parameterName = params.getSourceParameter();
boolean dataMethodInvocation = parameterName != null && params.isDataMethodInvocation();
- String fieldName = params.getSourceField();
- if (fieldName != null) {
- IField field = type.getField(fieldName);
- if (field == null || !field.exists()) {
- // The field doesn't exist
- return null;
- }
-
+ if (type.isRecord()) {
+ // The source type is a record
if (dataMethodInvocation) {
- // returns the location of "data" method invocation with the given parameter
- // name
- return TemplateDataSupport.getDataMethodInvocationLocation(field, parameterName, utils, monitor);
- }
- // returns field location
- return utils.toLocation(field);
- }
-
- String sourceMethod = params.getSourceMethod();
- if (sourceMethod != null) {
- IMethod method = findMethod(type, sourceMethod);
- if (method == null || !method.exists()) {
- // The method doesn't exist
- return null;
+ // returns the location of "data" method invocation with the given parameter
+ // name
+ return TemplateDataSupport.getDataMethodInvocationLocation(type, parameterName, utils,
+ monitor);
+
+ } else {
+ // Search field of the record
+ IField recordField = type.getRecordComponent(parameterName);
+ if (recordField != null && recordField.exists()) {
+ // returns the record field location
+ return utils.toLocation(recordField);
+ }
}
+ } else {
+ // The source type is a class
+ String fieldName = params.getSourceField();
+ if (fieldName != null) {
+ IField field = type.getField(fieldName);
+ if (field == null || !field.exists()) {
+ // The field doesn't exist
+ return null;
+ }
- if (parameterName != null) {
if (dataMethodInvocation) {
// returns the location of "data" method invocation with the given parameter
// name
- return TemplateDataSupport.getDataMethodInvocationLocation(method, parameterName, utils, monitor);
+ return TemplateDataSupport.getDataMethodInvocationLocation(field, parameterName, utils, monitor);
}
- ILocalVariable[] parameters = method.getParameters();
- for (ILocalVariable parameter : parameters) {
- if (parameterName.equals(parameter.getElementName())) {
- // returns the method parameter location
- return utils.toLocation(parameter);
+ // returns field location
+ return utils.toLocation(field);
+ }
+
+ String sourceMethod = params.getSourceMethod();
+ if (sourceMethod != null) {
+ IMethod method = findMethod(type, sourceMethod);
+ if (method == null || !method.exists()) {
+ // The method doesn't exist
+ return null;
+ }
+
+ if (parameterName != null) {
+ if (dataMethodInvocation) {
+ // returns the location of "data" method invocation with the given parameter
+ // name
+ return TemplateDataSupport.getDataMethodInvocationLocation(method, parameterName, utils,
+ monitor);
+ }
+ ILocalVariable[] parameters = method.getParameters();
+ for (ILocalVariable parameter : parameters) {
+ if (parameterName.equals(parameter.getElementName())) {
+ // returns the method parameter location
+ return utils.toLocation(parameter);
+ }
}
+ return null;
}
- return null;
+ // returns method location
+ return utils.toLocation(method);
}
- // returns method location
- return utils.toLocation(method);
}
// returns Java type location
return utils.toLocation(type);
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 e21422909..bcbf0368f 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
@@ -28,6 +28,8 @@ public class QuteJavaConstants {
public static final String TEMPLATE_CLASS = "io.quarkus.qute.Template";
+ public static final String TEMPLATE_INSTANCE_INTERFACE = "io.quarkus.qute.TemplateInstance";
+
public static final String ENGINE_BUILDER_CLASS = "io.quarkus.qute.EngineBuilder";
public static final String VALUE_ANNOTATION_NAME = "value";
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/AbstractQuteTemplateLinkCollector.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/AbstractQuteTemplateLinkCollector.java
index a9fc0f4af..fb8e99635 100644
--- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/AbstractQuteTemplateLinkCollector.java
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/AbstractQuteTemplateLinkCollector.java
@@ -27,11 +27,13 @@
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.RecordDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.StringLiteral;
@@ -55,6 +57,7 @@
*
* - declared methods which have class annotated with @CheckedTemplate.
* - declared field which have Template as type.
+ * - declared record which implements TemplateInstance.
*
*
* @author Angelo ZERR
@@ -92,6 +95,17 @@ public boolean visit(CompilationUnit node) {
return super.visit(node);
}
+ /**
+ * Support for "Template Fields"
+ *
+ *
+ * private Template items;
+ *
+ *
+ * @see Quarkus
+ * Integration
+ */
@Override
public boolean visit(FieldDeclaration node) {
Type type = node.getType();
@@ -141,6 +155,19 @@ private AnnotationLocationSupport getAnnotationLocationSupport() {
return annotationLocationSupport;
}
+ /**
+ * Support for "TypeSafe Templates"
+ *
+ *
+ *
+ * @CheckedTemplate public static class Templates { public static native
+ * TemplateInstance book(Book book);
+ *
+ *
+ * @see TypeSafe
+ * Templates
+ */
@SuppressWarnings("rawtypes")
@Override
public boolean visit(TypeDeclaration node) {
@@ -167,6 +194,20 @@ public boolean visit(TypeDeclaration node) {
return super.visit(node);
}
+ /**
+ * Support for "Template Records"
+ *
+ * @see Template
+ * Records
+ */
+ @Override
+ public boolean visit(RecordDeclaration node) {
+ String recordName = node.getName().getIdentifier();
+ collectTemplateLink(node, null, node, null, recordName, false);
+ return super.visit(node);
+ }
+
/**
* Returns true if @CheckedTemplate annotation declares that fragment must be
* ignored and false otherwise.
@@ -217,8 +258,8 @@ private void collectTemplateLink(MethodDeclaration methodDeclaration, TypeDeclar
collectTemplateLink(methodDeclaration, null, type, className, methodName, ignoreFragment);
}
- private void collectTemplateLink(ASTNode fieldOrMethod, StringLiteral locationAnnotation, TypeDeclaration type,
- String className, String fieldOrMethodName, boolean ignoreFragment) {
+ private void collectTemplateLink(ASTNode fieldOrMethod, StringLiteral locationAnnotation,
+ AbstractTypeDeclaration type, String className, String fieldOrMethodName, boolean ignoreFragment) {
try {
String location = locationAnnotation != null ? locationAnnotation.getLiteralValue() : null;
IProject project = typeRoot.getJavaProject().getProject();
@@ -256,12 +297,17 @@ protected Range createRange(ASTNode fieldOrMethod) throws JavaModelException {
SimpleName methodName = method.getName();
return utils.toRange(typeRoot, methodName.getStartPosition(), methodName.getLength());
}
+ case ASTNode.RECORD_DECLARATION: {
+ RecordDeclaration recordDecl = (RecordDeclaration) fieldOrMethod;
+ SimpleName recordName = recordDecl.getName();
+ return utils.toRange(typeRoot, recordName.getStartPosition(), recordName.getLength());
+ }
default:
return utils.toRange(typeRoot, fieldOrMethod.getStartPosition(), fieldOrMethod.getLength());
}
}
- protected abstract void collectTemplateLink(ASTNode node, ASTNode locationAnnotation, TypeDeclaration type,
+ protected abstract void collectTemplateLink(ASTNode node, ASTNode locationAnnotation, AbstractTypeDeclaration type,
String className, String fieldOrMethodName, String location, IFile templateFile,
TemplatePathInfo templatePathInfo) throws JavaModelException;
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaCodeLensCollector.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaCodeLensCollector.java
index fa50edc56..08b0fed07 100644
--- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaCodeLensCollector.java
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaCodeLensCollector.java
@@ -22,6 +22,7 @@
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
@@ -66,7 +67,7 @@ public QuteJavaCodeLensCollector(ITypeRoot typeRoot, List lenses, IJDT
}
@Override
- protected void collectTemplateLink(ASTNode fieldOrMethod, ASTNode locationAnnotation, TypeDeclaration type,
+ protected void collectTemplateLink(ASTNode fieldOrMethod, ASTNode locationAnnotation, AbstractTypeDeclaration type,
String className, String fieldOrMethodName, String location, IFile templateFile,
TemplatePathInfo templatePathInfo) throws JavaModelException {
if (!templatePathInfo.isValid()) {
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDiagnosticsCollector.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDiagnosticsCollector.java
index 0663b19d2..bea9771b9 100644
--- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDiagnosticsCollector.java
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDiagnosticsCollector.java
@@ -19,6 +19,7 @@
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Diagnostic;
@@ -52,7 +53,7 @@ public QuteJavaDiagnosticsCollector(ITypeRoot typeRoot, List diagnos
}
@Override
- protected void collectTemplateLink(ASTNode fieldOrMethod, ASTNode locationAnnotation, TypeDeclaration type,
+ protected void collectTemplateLink(ASTNode fieldOrMethod, ASTNode locationAnnotation, AbstractTypeDeclaration type,
String className, String fieldOrMethodName, String location, IFile templateFile,
TemplatePathInfo templatePathInfo) throws JavaModelException {
QuteErrorCode error = getQuteErrorCode(templatePathInfo, templateFile);
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDocumentLinkCollector.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDocumentLinkCollector.java
index 842ffae0a..509c565af 100644
--- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDocumentLinkCollector.java
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/java/QuteJavaDocumentLinkCollector.java
@@ -19,6 +19,7 @@
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.DocumentLink;
import org.eclipse.lsp4j.Range;
@@ -52,7 +53,7 @@ public QuteJavaDocumentLinkCollector(ITypeRoot typeRoot, List link
}
@Override
- protected void collectTemplateLink(ASTNode fieldOrMethod, ASTNode locationAnnotation, TypeDeclaration type,
+ protected void collectTemplateLink(ASTNode fieldOrMethod, ASTNode locationAnnotation, AbstractTypeDeclaration type,
String className, String fieldOrMethodName, String location, IFile templateFile,
TemplatePathInfo templatePathInfo) throws JavaModelException {
if (!templatePathInfo.isValid()) {
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/CheckedTemplateSupport.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/CheckedTemplateSupport.java
index 51f47d42d..05c6c7779 100644
--- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/CheckedTemplateSupport.java
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/CheckedTemplateSupport.java
@@ -65,7 +65,9 @@
* }
*
*
- *
+ * @see TypeSafe
+ * Templates
* @author Angelo ZERR
*
*/
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/TemplateFieldSupport.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/TemplateFieldSupport.java
index f24da8b45..cf4b06f95 100644
--- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/TemplateFieldSupport.java
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/TemplateFieldSupport.java
@@ -58,7 +58,10 @@
* return hello.data("name", name);
* }
*
- *
+ *
+ * @see Quarkus
+ * Integration
* @author Angelo ZERR
*
*/
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/TemplateRecordsSupport.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/TemplateRecordsSupport.java
new file mode 100644
index 000000000..0f9c05b42
--- /dev/null
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/datamodel/TemplateRecordsSupport.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+* Copyright (c) 2024 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.internal.template.datamodel;
+
+import static com.redhat.qute.jdt.internal.QuteJavaConstants.TEMPLATE_INSTANCE_INTERFACE;
+import static com.redhat.qute.jdt.utils.JDTQuteProjectUtils.getTemplatePath;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+
+import com.redhat.qute.commons.datamodel.DataModelParameter;
+import com.redhat.qute.commons.datamodel.DataModelTemplate;
+import com.redhat.qute.jdt.internal.resolver.ITypeResolver;
+import com.redhat.qute.jdt.internal.template.TemplateDataSupport;
+import com.redhat.qute.jdt.template.datamodel.AbstractInterfaceImplementationDataModelProvider;
+import com.redhat.qute.jdt.template.datamodel.SearchContext;
+
+/**
+ * Template Records support for template files:
+ *
+ *
+ * public class HelloResource {
+
+ * record Hello(String name) implements TemplateInstance {}
+ *
+ * ...
+ *
+ *
+ * @GET
+ * @Produces(MediaType.TEXT_PLAIN)
+ * public TemplateInstance get(@QueryParam("name") String name) {
+ * return new Hello(name).data("bar", 100);
+ * }
+ *
+ *
+ * @see Template
+ * Records
+ * @author Angelo ZERR
+ *
+ */
+public class TemplateRecordsSupport extends AbstractInterfaceImplementationDataModelProvider {
+
+ private static final String[] INTERFACE_NAMES = { TEMPLATE_INSTANCE_INTERFACE };
+
+ @Override
+ protected String[] getInterfaceNames() {
+ return INTERFACE_NAMES;
+ }
+
+ @Override
+ protected void processType(IType type, SearchContext context, IProgressMonitor monitor) throws JavaModelException {
+ if (!type.isRecord()) {
+ return;
+ }
+ ITypeResolver typeResolver = context.getTypeResolver(type);
+ collectDataModelTemplateForTemplateRecord(type, typeResolver, context.getDataModelProject().getTemplates(),
+ monitor);
+ }
+
+ private static void collectDataModelTemplateForTemplateRecord(IType type, ITypeResolver typeResolver,
+ List> templates, IProgressMonitor monitor) throws JavaModelException {
+ DataModelTemplate template = createTemplateDataModel(type, typeResolver, monitor);
+ templates.add(template);
+ }
+
+ private static DataModelTemplate createTemplateDataModel(IType type, ITypeResolver typeResolver,
+ IProgressMonitor monitor) throws JavaModelException {
+
+ String recordName = type.getElementName();
+ // src/main/resources/templates/${recordName}.qute.html
+ String templateUri = getTemplatePath(null, recordName, true).getTemplateUri();
+
+ // Create template data model with:
+ // - template uri : Qute template file which must be bind with data model.
+ // - source type : the record class which defines Templates
+ // -
+ DataModelTemplate template = new DataModelTemplate();
+ template.setParameters(new ArrayList<>());
+ template.setTemplateUri(templateUri);
+ template.setSourceType(type.getFullyQualifiedName());
+
+ // Collect data parameters from the record fields
+ for (IField field : type.getRecordComponents()) {
+ DataModelParameter parameter = new DataModelParameter();
+ parameter.setKey(field.getElementName());
+ parameter.setSourceType(typeResolver.resolveTypeSignature(field.getTypeSignature()));
+ template.getParameters().add(parameter);
+ }
+
+ // Collect data parameters for the given template
+ TemplateDataSupport.collectParametersFromDataMethodInvocation(type, template, monitor);
+ return template;
+ }
+
+}
diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/template/datamodel/AbstractDataModelProvider.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/template/datamodel/AbstractDataModelProvider.java
index 3f3c2a67d..b319485cd 100644
--- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/template/datamodel/AbstractDataModelProvider.java
+++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/template/datamodel/AbstractDataModelProvider.java
@@ -106,6 +106,20 @@ protected static SearchPattern createFieldDeclarationTypeReferenceSearchPattern(
IJavaSearchConstants.FIELD_DECLARATION_TYPE_REFERENCE, SearchPattern.R_EXACT_MATCH);
}
+ /**
+ * Create a search pattern to retrieve IType which implement the given
+ * interfaceName
interfaceName