Skip to content

Commit

Permalink
qute(NoMatchingTemplate) if using @CheckedTemplate with basePath
Browse files Browse the repository at this point in the history
  • Loading branch information
angelozerr committed Jul 12, 2024
1 parent 52de798 commit dda2ab3
Show file tree
Hide file tree
Showing 17 changed files with 320 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import static io.quarkus.runtime.util.StringUtil.hyphenate;
import static io.quarkus.runtime.util.StringUtil.join;
import static io.quarkus.runtime.util.StringUtil.lowerCase;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getAnnotation;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getFirstAnnotation;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getAnnotationMemberValue;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.hasAnnotation;
import static org.eclipse.lsp4mp.jdt.core.utils.JDTTypeUtils.findType;
Expand Down Expand Up @@ -254,7 +254,7 @@ private static String convertName(IMember member, IAnnotation configMappingAnnot
// @WithName("name")
// String host();
// --> See https://quarkus.io/guides/config-mappings#withname
IAnnotation withNameAnnotation = getAnnotation((IAnnotatable) member, WITH_NAME_ANNOTATION);
IAnnotation withNameAnnotation = getFirstAnnotation((IAnnotatable) member, WITH_NAME_ANNOTATION);
if (withNameAnnotation != null) {
String name = getAnnotationMemberValue(withNameAnnotation, WITH_NAME_ANNOTATION_VALUE);
if (StringUtils.isNotEmpty(name)) {
Expand Down Expand Up @@ -304,7 +304,7 @@ private static String convertName(IMember member, IAnnotation configMappingAnnot
*/
private static String getWithDefault(IMember member) {
try {
IAnnotation withDefaultAnnotation = getAnnotation((IAnnotatable) member, WITH_DEFAULT_ANNOTATION);
IAnnotation withDefaultAnnotation = getFirstAnnotation((IAnnotatable) member, WITH_DEFAULT_ANNOTATION);
if (withDefaultAnnotation != null) {
String defaultValue = getAnnotationMemberValue(withDefaultAnnotation, WITH_DEFAULT_ANNOTATION_VALUE);
if (StringUtils.isNotEmpty(defaultValue)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import static io.quarkus.runtime.util.StringUtil.join;
import static io.quarkus.runtime.util.StringUtil.lowerCase;
import static io.quarkus.runtime.util.StringUtil.withoutSuffix;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getAnnotation;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getFirstAnnotation;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getAnnotationMemberValue;
import static org.eclipse.lsp4mp.jdt.core.utils.JDTTypeUtils.findType;
import static org.eclipse.lsp4mp.jdt.core.utils.JDTTypeUtils.getEnclosedType;
Expand Down Expand Up @@ -189,7 +189,7 @@ private void processConfigProperties(IJavaElement javaElement, IAnnotation confi
}
String name = null;
String defaultValue = null;
IAnnotation configPropertyAnnotation = getAnnotation(method,
IAnnotation configPropertyAnnotation = getFirstAnnotation(method,
MicroProfileConfigConstants.CONFIG_PROPERTY_ANNOTATION);
if (configPropertyAnnotation != null) {
name = getAnnotationMemberValue(configPropertyAnnotation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import static io.quarkus.runtime.util.StringUtil.lowerCaseFirst;
import static io.quarkus.runtime.util.StringUtil.toList;
import static io.quarkus.runtime.util.StringUtil.withoutSuffix;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getAnnotation;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getFirstAnnotation;
import static org.eclipse.lsp4mp.jdt.core.utils.AnnotationUtils.getAnnotationMemberValue;
import static org.eclipse.lsp4mp.jdt.core.utils.JDTTypeUtils.findType;
import static org.eclipse.lsp4mp.jdt.core.utils.JDTTypeUtils.getEnclosedType;
Expand Down Expand Up @@ -413,7 +413,7 @@ private void processConfigGroup(String extensionName, IJavaElement javaElement,
continue;
}

final IAnnotation configItemAnnotation = getAnnotation((IAnnotatable) field,
final IAnnotation configItemAnnotation = getFirstAnnotation((IAnnotatable) field,
QuarkusConstants.CONFIG_ITEM_ANNOTATION);
String name = configItemAnnotation == null ? hyphenate(field.getElementName())
: getAnnotationMemberValue(configItemAnnotation,
Expand All @@ -437,7 +437,7 @@ private void processConfigGroup(String extensionName, IJavaElement javaElement,

String fieldTypeName = getResolvedTypeName(field);
IType fieldClass = findType(field.getJavaProject(), fieldTypeName);
final IAnnotation configGroupAnnotation = getAnnotation((IAnnotatable) fieldClass,
final IAnnotation configGroupAnnotation = getFirstAnnotation((IAnnotatable) fieldClass,
QuarkusConstants.CONFIG_GROUP_ANNOTATION);
if (configGroupAnnotation != null) {
processConfigGroup(extensionName, fieldClass, subKey, configPhase, javadocCache, quarkusContext,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.acme.qute;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.CheckedTemplate;
import io.quarkus.qute.TemplateExtension;
import io.quarkus.qute.TemplateInstance;

@Path("items3")
public class ItemResourceWithCustomBasePath {

@CheckedTemplate(basePath="ItemResourceWithFragment")
static class Templates {
static native TemplateInstance items(List<Item> items);
static native TemplateInstance items$id1(List<Item> items);
static native TemplateInstance items3$id2(List<Item> items);
static native TemplateInstance items3$(List<Item> items);
}

@GET
@Produces(MediaType.TEXT_HTML)
public TemplateInstance get() {
List<Item> items = new ArrayList<>();
items.add(new Item(new BigDecimal(10), "Apple"));
items.add(new Item(new BigDecimal(16), "Pear"));
items.add(new Item(new BigDecimal(30), "Orange"));
return Templates.items(items);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.acme.qute;

import java.util.List;
import io.quarkus.qute.CheckedTemplate;
import io.quarkus.qute.TemplateInstance;

@CheckedTemplate(basePath="ItemResourceWithFragment")
public class ItemTemplatesCustomBasePath {

static native TemplateInstance items(List<Item> items);
static native TemplateInstance items$id1(List<Item> items);
static native TemplateInstance items3$id2(List<Item> items);
static native TemplateInstance items3$(List<Item> items);

}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,92 @@ public void checkedTemplateInInnerClass() throws CoreException, Exception {
"qute.command.generate.template.file", Arrays.asList(items2Uri)));
}

@Test
public void checkedTemplateWithCustomBasePath() throws Exception {

// @CheckedTemplate(basePath="ItemResourceWithFragment")
// public class ItemTemplatesCustomBasePath {
//
// static native TemplateInstance items(List<Item> items);
// static native TemplateInstance items$id1(List<Item> items);
// static native TemplateInstance items3$id2(List<Item> items);
// static native TemplateInstance items3$(List<Item> items);
// }

IJavaProject javaProject = loadMavenProject(QuteMavenProjectName.qute_quickstart);

QuteJavaCodeLensParams params = new QuteJavaCodeLensParams();
IFile javaFile = javaProject.getProject()
.getFile(new Path("src/main/java/org/acme/qute/ItemTemplatesCustomBasePath.java"));
params.setUri(javaFile.getLocation().toFile().toURI().toString());

List<? extends CodeLens> lenses = QuteSupportForJava.getInstance().codeLens(params, getJDTUtils(),
new NullProgressMonitor());
assertEquals(3, lenses.size());

String itemsUri = javaProject.getProject()
.getFile("src/main/resources/templates/ItemResourceWithFragment/items.html").getLocationURI()
.toString();
String items3Uri = javaProject.getProject()
.getFile("src/main/resources/templates/ItemResourceWithFragment/items3.html").getLocationURI()
.toString();

assertCodeLens(lenses, //
cl(r(9, 1, 9, 56), //
"Open `src/main/resources/templates/ItemResourceWithFragment/items.html`", //
"qute.command.open.uri", Arrays.asList(itemsUri)), //
cl(r(10, 1, 10, 60), //
"Open `id1` fragment of `src/main/resources/templates/ItemResourceWithFragment/items.html`", //
"qute.command.open.uri", Arrays.asList(itemsUri, "id1")), //
cl(r(11, 1, 11, 61), //
"Create `src/main/resources/templates/ItemResourceWithFragment/items3.html`", //
"qute.command.generate.template.file", Arrays.asList(items3Uri)));

}

@Test
public void checkedTemplateInInnerClassWithCustomBasePath() throws Exception {

// @CheckedTemplate(basePath="ItemResourceWithFragment")
// public class ItemTemplatesCustomBasePath {
//
// static native TemplateInstance items(List<Item> items);
// static native TemplateInstance items$id1(List<Item> items);
// static native TemplateInstance items3$id2(List<Item> items);
// static native TemplateInstance items3$(List<Item> items);
// }

IJavaProject javaProject = loadMavenProject(QuteMavenProjectName.qute_quickstart);

QuteJavaCodeLensParams params = new QuteJavaCodeLensParams();
IFile javaFile = javaProject.getProject()
.getFile(new Path("src/main/java/org/acme/qute/ItemTemplatesCustomBasePath.java"));
params.setUri(javaFile.getLocation().toFile().toURI().toString());

List<? extends CodeLens> lenses = QuteSupportForJava.getInstance().codeLens(params, getJDTUtils(),
new NullProgressMonitor());
assertEquals(3, lenses.size());

String itemsUri = javaProject.getProject()
.getFile("src/main/resources/templates/ItemResourceWithFragment/items.html").getLocationURI()
.toString();
String items3Uri = javaProject.getProject()
.getFile("src/main/resources/templates/ItemResourceWithFragment/items3.html").getLocationURI()
.toString();

assertCodeLens(lenses, //
cl(r(9, 1, 9, 56), //
"Open `src/main/resources/templates/ItemResourceWithFragment/items.html`", //
"qute.command.open.uri", Arrays.asList(itemsUri)), //
cl(r(10, 1, 10, 60), //
"Open `id1` fragment of `src/main/resources/templates/ItemResourceWithFragment/items.html`", //
"qute.command.open.uri", Arrays.asList(itemsUri, "id1")), //
cl(r(11, 1, 11, 61), //
"Create `src/main/resources/templates/ItemResourceWithFragment/items3.html`", //
"qute.command.generate.template.file", Arrays.asList(items3Uri)));

}

@Test
public void checkedTemplateWithFragment() throws CoreException, Exception {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,41 @@ public void checkedTemplateInWithFragment() throws CoreException, Exception {
DiagnosticSeverity.Error, "qute", QuteErrorCode.NoMatchingTemplate.name()));
}

@Test
public void checkedTemplateWithCustomBasePath() throws Exception {

// @CheckedTemplate(basePath="ItemResourceWithFragment")
// public class ItemTemplatesCustomBasePath {
//
// static native TemplateInstance items(List<Item> items);
// static native TemplateInstance items$id1(List<Item> items);
// static native TemplateInstance items3$id2(List<Item> items);
// static native TemplateInstance items3$(List<Item> items);
// }

IJavaProject javaProject = loadMavenProject(QuteMavenProjectName.qute_quickstart);

QuteJavaDiagnosticsParams params = new QuteJavaDiagnosticsParams();
IFile javaFile = javaProject.getProject()
.getFile(new Path("src/main/java/org/acme/qute/ItemTemplatesCustomBasePath.java"));
params.setUris(Arrays.asList(javaFile.getLocation().toFile().toURI().toString()));

List<PublishDiagnosticsParams> publishDiagnostics = QuteSupportForJava.getInstance().diagnostics(params,
getJDTUtils(), new NullProgressMonitor());
assertEquals(1, publishDiagnostics.size());

List<Diagnostic> diagnostics = publishDiagnostics.get(0).getDiagnostics();
assertEquals(2, diagnostics.size());

assertDiagnostic(diagnostics, //
new Diagnostic(r(11, 32, 11, 42),
"No template matching the path ItemResourceWithFragment/items3 could be found for: org.acme.qute.ItemTemplatesCustomBasePath",
DiagnosticSeverity.Error, "qute", QuteErrorCode.NoMatchingTemplate.name()), //
new Diagnostic(r(12, 32, 12, 39),
"Fragment [] not defined in template ItemResourceWithFragment/items3$",
DiagnosticSeverity.Error, "qute", QuteErrorCode.FragmentNotDefined.name()));
}

public static Range r(int line, int startChar, int endChar) {
return r(line, startChar, line, endChar);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class QuteJavaConstants {
public static final String OLD_CHECKED_TEMPLATE_ANNOTATION = "io.quarkus.qute.api.CheckedTemplate";

public static final String CHECKED_TEMPLATE_ANNOTATION_IGNORE_FRAGMENTS = "ignoreFragments";
public static final String CHECKED_TEMPLATE_ANNOTATION_BASE_PATH = "basePath";

// @TemplateExtension

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
package com.redhat.qute.jdt.internal.java;

import static com.redhat.qute.jdt.internal.QuteJavaConstants.CHECKED_TEMPLATE_ANNOTATION;
import static com.redhat.qute.jdt.internal.QuteJavaConstants.CHECKED_TEMPLATE_ANNOTATION_BASE_PATH;
import static com.redhat.qute.jdt.internal.QuteJavaConstants.CHECKED_TEMPLATE_ANNOTATION_IGNORE_FRAGMENTS;
import static com.redhat.qute.jdt.internal.QuteJavaConstants.OLD_CHECKED_TEMPLATE_ANNOTATION;
import static com.redhat.qute.jdt.internal.QuteJavaConstants.TEMPLATE_CLASS;
Expand Down Expand Up @@ -135,7 +136,7 @@ public boolean visit(FieldDeclaration node) {
.getLocationExpressionFromConstructorParameter(variable.getName().getIdentifier());
}
String fieldName = variable.getName().getIdentifier();
collectTemplateLink(node, locationExpression, getTypeDeclaration(node), null, fieldName, false);
collectTemplateLink(null, node, locationExpression, getTypeDeclaration(node), null, fieldName, false);
}
}
return super.visit(node);
Expand Down Expand Up @@ -182,10 +183,11 @@ public boolean visit(TypeDeclaration node) {
// public static class Templates {
// public static native TemplateInstance book(Book book);
boolean ignoreFragments = isIgnoreFragments(annotation);
String basePath = getBasePath(annotation);
List body = node.bodyDeclarations();
for (Object declaration : body) {
if (declaration instanceof MethodDeclaration) {
collectTemplateLink((MethodDeclaration) declaration, node, ignoreFragments);
collectTemplateLink(basePath, (MethodDeclaration) declaration, node, ignoreFragments);
}
}
}
Expand All @@ -204,7 +206,7 @@ public boolean visit(TypeDeclaration node) {
@Override
public boolean visit(RecordDeclaration node) {
String recordName = node.getName().getIdentifier();
collectTemplateLink(node, null, node, null, recordName, false);
collectTemplateLink(null, node, null, node, null, recordName, false);
return super.visit(node);
}

Expand Down Expand Up @@ -233,6 +235,29 @@ private static boolean isIgnoreFragments(Annotation checkedTemplateAnnotation) {
return ignoreFragment != null ? ignoreFragment.booleanValue() : false;
}

/**
* Returns the <code>basePath</code> value declared in the @CheckedTemplate
* annotation, relative to the templates root, to search the templates from.
* <code>
* @CheckedTemplate(basePath="somewhere")
*</code>
*
* @param checkedTemplateAnnotation the CheckedTemplate annotation.
* @return the <code>basePath</code> value declared in the @CheckedTemplate
* annotation
*/
public static String getBasePath(Annotation checkedTemplateAnnotation) {
String basePath = null;
try {
Expression ignoreFragmentExpr = AnnotationUtils.getAnnotationMemberValueExpression(
checkedTemplateAnnotation, CHECKED_TEMPLATE_ANNOTATION_BASE_PATH);
basePath = AnnotationUtils.getString(ignoreFragmentExpr);
} catch (Exception e) {
// Do nothing
}
return basePath;
}

@Override
public void endVisit(TypeDeclaration node) {
levelTypeDecl--;
Expand All @@ -247,25 +272,25 @@ private static TypeDeclaration getTypeDeclaration(ASTNode node) {
return parent != null && parent.getNodeType() == ASTNode.TYPE_DECLARATION ? (TypeDeclaration) parent : null;
}

private void collectTemplateLink(MethodDeclaration methodDeclaration, TypeDeclaration type,
private void collectTemplateLink(String basePath, MethodDeclaration methodDeclaration, TypeDeclaration type,
boolean ignoreFragment) {
String className = null;
boolean innerClass = levelTypeDecl > 1;
if (innerClass) {
className = JDTTypeUtils.getSimpleClassName(typeRoot.getElementName());
}
String methodName = methodDeclaration.getName().getIdentifier();
collectTemplateLink(methodDeclaration, null, type, className, methodName, ignoreFragment);
collectTemplateLink(basePath, methodDeclaration, null, type, className, methodName, ignoreFragment);
}

private void collectTemplateLink(ASTNode fieldOrMethod, StringLiteral locationAnnotation,
private void collectTemplateLink(String basePath, 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();
TemplatePathInfo templatePathInfo = location != null
? JDTQuteProjectUtils.getTemplatePath(null, location, ignoreFragment)
: JDTQuteProjectUtils.getTemplatePath(className, fieldOrMethodName, ignoreFragment);
? JDTQuteProjectUtils.getTemplatePath(basePath, null, location, ignoreFragment)
: JDTQuteProjectUtils.getTemplatePath(basePath, className, fieldOrMethodName, ignoreFragment);
IFile templateFile = null;
if (location == null) {
templateFile = getTemplateFile(project, templatePathInfo.getTemplateUri());
Expand All @@ -275,8 +300,8 @@ private void collectTemplateLink(ASTNode fieldOrMethod, StringLiteral locationAn
} else {
templateFile = project.getFile(templatePathInfo.getTemplateUri());
}
collectTemplateLink(fieldOrMethod, locationAnnotation, type, className, fieldOrMethodName, location,
templateFile, templatePathInfo);
collectTemplateLink(basePath, fieldOrMethod, locationAnnotation, type, className, fieldOrMethodName,
location, templateFile, templatePathInfo);
} catch (JavaModelException e) {
LOGGER.log(Level.SEVERE, "Error while creating Qute CodeLens for Java file.", e);
}
Expand Down Expand Up @@ -307,9 +332,9 @@ protected Range createRange(ASTNode fieldOrMethod) throws JavaModelException {
}
}

protected abstract void collectTemplateLink(ASTNode node, ASTNode locationAnnotation, AbstractTypeDeclaration type,
String className, String fieldOrMethodName, String location, IFile templateFile,
TemplatePathInfo templatePathInfo) throws JavaModelException;
protected abstract void collectTemplateLink(String basePath, ASTNode node, ASTNode locationAnnotation,
AbstractTypeDeclaration type, String className, String fieldOrMethodName, String location,
IFile templateFile, TemplatePathInfo templatePathInfo) throws JavaModelException;

private static IFile getTemplateFile(IProject project, String templateFilePathWithoutExtension) {
for (String suffix : suffixes) {
Expand Down
Loading

0 comments on commit dda2ab3

Please sign in to comment.