diff --git a/projects/qute/projects/maven/qute-record/src/main/java/org/acme/sample/HelloResource.java b/projects/qute/projects/maven/qute-record/src/main/java/org/acme/sample/HelloResource.java index a1fbb5679..56faceee2 100644 --- a/projects/qute/projects/maven/qute-record/src/main/java/org/acme/sample/HelloResource.java +++ b/projects/qute/projects/maven/qute-record/src/main/java/org/acme/sample/HelloResource.java @@ -1,5 +1,6 @@ package org.acme.sample; +import io.quarkus.qute.CheckedTemplate; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -18,6 +19,9 @@ record Bonjour(String name) implements TemplateInstance {} record Status() {} + @CheckedTemplate(basePath="Foo", defaultName=CheckedTemplate.HYPHENATED_ELEMENT_NAME) + record HelloWorld(String name) implements TemplateInstance {} + @GET @Produces(MediaType.TEXT_PLAIN) public TemplateInstance get(@QueryParam("name") String name) { diff --git a/projects/qute/projects/maven/qute-record/src/main/resources/templates/Hello.html b/projects/qute/projects/maven/qute-record/src/main/resources/templates/HelloResource/Hello.html similarity index 100% rename from projects/qute/projects/maven/qute-record/src/main/resources/templates/Hello.html rename to projects/qute/projects/maven/qute-record/src/main/resources/templates/HelloResource/Hello.html diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java index fee4dc31c..011eb830f 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/java/AbstractQuteTemplateLinkCollector.java @@ -160,18 +160,16 @@ public void visitClass(PsiClass node) { */ private void visitClassType(PsiClass node) { levelTypeDecl++; - for (PsiAnnotation annotation : node.getAnnotations()) { - if (AnnotationUtils.isMatchAnnotation(annotation, CHECKED_TEMPLATE_ANNOTATION) - || AnnotationUtils.isMatchAnnotation(annotation, OLD_CHECKED_TEMPLATE_ANNOTATION)) { - // @CheckedTemplate - // public static class Templates { - // public static native TemplateInstance book(Book book); - boolean ignoreFragments = isIgnoreFragments(annotation); - String basePath = getBasePath(annotation); - TemplateNameStrategy templateNameStrategy = getDefaultName(annotation); - for (PsiMethod method : node.getMethods()) { - collectTemplateLink(basePath, method, node, ignoreFragments, templateNameStrategy); - } + PsiAnnotation checkedAnnotation = getCheckedAnnotation(node); + if (checkedAnnotation != null) { + // @CheckedTemplate + // public static class Templates { + // public static native TemplateInstance book(Book book); + boolean ignoreFragments = isIgnoreFragments(checkedAnnotation); + String basePath = getBasePath(checkedAnnotation); + TemplateNameStrategy templateNameStrategy = getDefaultName(checkedAnnotation); + for (PsiMethod method : node.getMethods()) { + collectTemplateLinkForMethodOrRecord(basePath, method, method.getName(), node, ignoreFragments, templateNameStrategy); } } super.visitClass(node); @@ -187,8 +185,16 @@ private void visitClassType(PsiClass node) { */ private void visitRecordType(PsiClass node) { if (isImplementTemplateInstance(node)) { + + // public class HelloResource { + // record Hello(String name) implements TemplateInstance {} String recordName = node.getName(); - collectTemplateLink(null, node, null, node, null, recordName, false, TemplateNameStrategy.ELEMENT_NAME); + PsiAnnotation checkedAnnotation = getCheckedAnnotation(node); + boolean ignoreFragments = isIgnoreFragments(checkedAnnotation); + String basePath = getBasePath(checkedAnnotation); + TemplateNameStrategy templateNameStrategy = getDefaultName(checkedAnnotation); + collectTemplateLinkForMethodOrRecord(basePath, node, recordName, node, ignoreFragments, + templateNameStrategy); } } @@ -216,14 +222,18 @@ private static PsiClass getTypeDeclaration(PsiElement node) { return PsiTreeUtil.getParentOfType(node, PsiClass.class); } - private void collectTemplateLink(String basePath, PsiMethod methodDeclaration, PsiClass type, boolean ignoreFragments, TemplateNameStrategy templateNameStrategy) { + private void collectTemplateLinkForMethodOrRecord(String basePath, + PsiElement methodOrRecord, + String methodOrRecordName, + PsiClass type, + boolean ignoreFragments, + TemplateNameStrategy templateNameStrategy) { String className = null; - boolean innerClass = levelTypeDecl > 1; + boolean innerClass = methodOrRecord instanceof PsiClass ? levelTypeDecl >= 1 : levelTypeDecl > 1; if (innerClass) { className = PsiTypeUtils.getSimpleClassName(typeRoot.getName()); } - String methodName = methodDeclaration.getName(); - collectTemplateLink(basePath, methodDeclaration, null, type, className, methodName, ignoreFragments,templateNameStrategy); + collectTemplateLink(basePath, methodOrRecord, null, type, className, methodOrRecordName, ignoreFragments, templateNameStrategy); } private void collectTemplateLink(String basePath, PsiElement fieldOrMethod, PsiLiteralValue locationAnnotation, PsiClass type, String className, diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/CheckedTemplateSupport.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/CheckedTemplateSupport.java index fa2a1a4c1..49ceec73f 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/CheckedTemplateSupport.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/CheckedTemplateSupport.java @@ -39,6 +39,7 @@ import com.redhat.qute.commons.datamodel.DataModelFragment; import com.redhat.qute.commons.datamodel.DataModelParameter; import com.redhat.qute.commons.datamodel.DataModelTemplate; +import org.jetbrains.annotations.Nullable; /** * CheckedTemplate support for template files: @@ -88,6 +89,16 @@ protected void processAnnotation(PsiElement javaElement, PsiAnnotation checkedTe } } + public static PsiAnnotation getCheckedAnnotation(PsiJvmModifiersOwner node) { + for (PsiAnnotation annotation : node.getAnnotations()) { + if (AnnotationUtils.isMatchAnnotation(annotation, CHECKED_TEMPLATE_ANNOTATION) + || AnnotationUtils.isMatchAnnotation(annotation, OLD_CHECKED_TEMPLATE_ANNOTATION)) { + return annotation; + } + } + return null; + } + /** * Returns true if @CheckedTemplate annotation declares that fragment must be * ignored and false otherwise. @@ -99,22 +110,24 @@ protected void processAnnotation(PsiElement javaElement, PsiAnnotation checkedTe * ignored and false otherwise. * @CheckedTemplate(ignoreFragments=true) */ - public static boolean isIgnoreFragments(PsiAnnotation checkedTemplateAnnotation) { + public static boolean isIgnoreFragments(@Nullable PsiAnnotation checkedTemplateAnnotation) { Boolean ignoreFragment = null; - try { - for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) { - if (CHECKED_TEMPLATE_ANNOTATION_IGNORE_FRAGMENTS.equalsIgnoreCase(pair.getAttributeName())) { - ignoreFragment = AnnotationUtils.getValueAsBoolean(pair); + if (checkedTemplateAnnotation != null) { + try { + for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) { + if (CHECKED_TEMPLATE_ANNOTATION_IGNORE_FRAGMENTS.equalsIgnoreCase(pair.getAttributeName())) { + ignoreFragment = AnnotationUtils.getValueAsBoolean(pair); + } } + } catch (ProcessCanceledException e) { + //Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility + //TODO delete block when minimum required version is 2024.2 + throw e; + } catch (IndexNotReadyException | CancellationException e) { + throw e; + } catch (Exception e) { + // Do nothing } - } catch (ProcessCanceledException e) { - //Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility - //TODO delete block when minimum required version is 2024.2 - throw e; - } catch (IndexNotReadyException | CancellationException e) { - throw e; - } catch (Exception e) { - // Do nothing } return ignoreFragment != null ? ignoreFragment.booleanValue() : false; } @@ -127,22 +140,24 @@ public static boolean isIgnoreFragments(PsiAnnotation checkedTemplateAnnotation) * @return the basePath value declared in the @CheckedTemplate annotation * @CheckedTemplate(basePath="somewhere") */ - public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) { + public static String getBasePath(@Nullable PsiAnnotation checkedTemplateAnnotation) { String basePath = null; - try { - for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) { - if (CHECKED_TEMPLATE_ANNOTATION_BASE_PATH.equalsIgnoreCase(pair.getAttributeName())) { - basePath = pair.getLiteralValue(); + if (checkedTemplateAnnotation != null) { + try { + for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) { + if (CHECKED_TEMPLATE_ANNOTATION_BASE_PATH.equalsIgnoreCase(pair.getAttributeName())) { + basePath = pair.getLiteralValue(); + } } + } catch (ProcessCanceledException e) { + //Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility + //TODO delete block when minimum required version is 2024.2 + throw e; + } catch (IndexNotReadyException | CancellationException e) { + throw e; + } catch (Exception e) { + // Do nothing } - } catch (ProcessCanceledException e) { - //Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility - //TODO delete block when minimum required version is 2024.2 - throw e; - } catch (IndexNotReadyException | CancellationException e) { - throw e; - } catch (Exception e) { - // Do nothing } return basePath; } @@ -157,28 +172,30 @@ public static String getBasePath(PsiAnnotation checkedTemplateAnnotation) { */ public static TemplateNameStrategy getDefaultName(PsiAnnotation checkedTemplateAnnotation) { TemplateNameStrategy templateNameStrategy = TemplateNameStrategy.ELEMENT_NAME; - try { - for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) { - if (CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME.equalsIgnoreCase(pair.getAttributeName())) { - if (pair.getValue() != null - && pair.getValue().getReference() != null - && pair.getValue().getReference().resolve() != null && - pair.getValue().getReference().resolve() instanceof PsiField field) { - Object value = field.computeConstantValue(); - if (value != null) { - templateNameStrategy = getDefaultName(value.toString()); + if (checkedTemplateAnnotation != null) { + try { + for (PsiNameValuePair pair : checkedTemplateAnnotation.getParameterList().getAttributes()) { + if (CHECKED_TEMPLATE_ANNOTATION_DEFAULT_NAME.equalsIgnoreCase(pair.getAttributeName())) { + if (pair.getValue() != null + && pair.getValue().getReference() != null + && pair.getValue().getReference().resolve() != null && + pair.getValue().getReference().resolve() instanceof PsiField field) { + Object value = field.computeConstantValue(); + if (value != null) { + templateNameStrategy = getDefaultName(value.toString()); + } } } } + } catch (ProcessCanceledException e) { + //Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility + //TODO delete block when minimum required version is 2024.2 + throw e; + } catch (IndexNotReadyException | CancellationException e) { + throw e; + } catch (Exception e) { + // Do nothing } - } catch (ProcessCanceledException e) { - //Since 2024.2 ProcessCanceledException extends CancellationException so we can't use multicatch to keep backward compatibility - //TODO delete block when minimum required version is 2024.2 - throw e; - } catch (IndexNotReadyException | CancellationException e) { - throw e; - } catch (Exception e) { - // Do nothing } return templateNameStrategy; } @@ -211,9 +228,7 @@ private static void collectDataModelTemplateForCheckedTemplate(PsiClass type, ITypeResolver typeResolver, List> templates, ProgressIndicator monitor) { - boolean innerClass = type.getContainingClass() != null; - String className = !innerClass ? null - : PsiTypeUtils.getSimpleClassName(type.getContainingFile().getName()); + String className = getParentClassName(type); // Loop for each method (book, book) and create a template data model per // method. @@ -254,6 +269,13 @@ private static void collectDataModelTemplateForCheckedTemplate(PsiClass type, } } + public static @Nullable String getParentClassName(PsiClass type) { + boolean innerClass = type.getContainingClass() != null; + String className = !innerClass ? null + : PsiTypeUtils.getSimpleClassName(type.getContainingFile().getName()); + return className; + } + private static DataModelTemplate createTemplateDataModel(String templateUri, PsiMethod method, PsiClass type) { String methodName = method.getName(); diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/TemplateRecordsSupport.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/TemplateRecordsSupport.java index 9e87ba28e..23b62e6cf 100644 --- a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/TemplateRecordsSupport.java +++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/datamodel/TemplateRecordsSupport.java @@ -12,6 +12,7 @@ package com.redhat.devtools.intellij.qute.psi.internal.template.datamodel; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiRecordComponent; import com.redhat.devtools.intellij.qute.psi.internal.template.TemplateDataSupport; @@ -26,6 +27,7 @@ import java.util.List; import static com.redhat.devtools.intellij.qute.psi.internal.QuteJavaConstants.TEMPLATE_INSTANCE_INTERFACE; +import static com.redhat.devtools.intellij.qute.psi.internal.template.datamodel.CheckedTemplateSupport.*; import static com.redhat.devtools.intellij.qute.psi.utils.PsiQuteProjectUtils.getTemplatePath; /** @@ -36,6 +38,9 @@ *

* record Hello(String name) implements TemplateInstance {} *

+ *

+ * @CheckedTemplate(basePath="Foo", defaultName=CheckedTemplate.HYPHENATED_ELEMENT_NAME) + * record HelloWorld(String name) implements TemplateInstance {} * ... *

*

@@ -76,13 +81,18 @@ private static void collectDataModelTemplateForTemplateRecord(PsiClass type, templates.add(template); } - private static DataModelTemplate createTemplateDataModel(PsiClass type, + private static DataModelTemplate createTemplateDataModel(PsiClass recordType, String relativeTemplateBaseDir, ProgressIndicator monitor) { - String recordName = type.getName(); + PsiAnnotation checkedTemplateAnnotation = getCheckedAnnotation(recordType); + boolean ignoreFragments = isIgnoreFragments(checkedTemplateAnnotation); + String basePath = getBasePath(checkedTemplateAnnotation); + TemplateNameStrategy templateNameStrategy = getDefaultName(checkedTemplateAnnotation); + String className = getParentClassName(recordType); + String recordName = recordType.getName(); // src/main/resources/templates/${recordName}.qute.html - String templateUri = getTemplatePath(relativeTemplateBaseDir, null, null, recordName, true, TemplateNameStrategy.ELEMENT_NAME).getTemplateUri(); + String templateUri = getTemplatePath(relativeTemplateBaseDir, basePath, className, recordName, ignoreFragments, templateNameStrategy).getTemplateUri(); // Create template data model with: // - template uri : Qute template file which must be bind with data model. @@ -91,10 +101,10 @@ private static DataModelTemplate createTemplateDataModel(Psi DataModelTemplate template = new DataModelTemplate(); template.setParameters(new ArrayList<>()); template.setTemplateUri(templateUri); - template.setSourceType(type.getQualifiedName()); + template.setSourceType(recordType.getQualifiedName()); // Collect data parameters from the record fields - for (PsiRecordComponent field : type.getRecordComponents()) { + for (PsiRecordComponent field : recordType.getRecordComponents()) { DataModelParameter parameter = new DataModelParameter(); parameter.setKey(field.getName()); parameter.setSourceType(PsiTypeUtils.resolveSignature(field.getType(), field.isVarArgs())); @@ -106,7 +116,7 @@ private static DataModelTemplate createTemplateDataModel(Psi } // Collect data parameters for the given template - TemplateDataSupport.collectParametersFromDataMethodInvocation(type, template, monitor); + TemplateDataSupport.collectParametersFromDataMethodInvocation(recordType, template, monitor); return template; } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 4a95c30ce..35c11a0ae 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -6,6 +6,11 @@ 2.0.2 +

+ Learn more in the changelog.

2.0.1