Skip to content

Commit

Permalink
Check if record implement TemplateInstance to provide the support
Browse files Browse the repository at this point in the history
Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jul 12, 2024
1 parent c46f4f9 commit fcf5fe3
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public class HelloResource {

record Hello(String name) implements TemplateInstance {}

record Bonjour(String name) implements TemplateInstance {}

record Status() {}

@GET
@Produces(MediaType.TEXT_PLAIN)
public TemplateInstance get(@QueryParam("name") String name) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.acme.sample;

public class SomeService {

record RunnerState(Status status) {

public enum Status {
dead, alive, saved, inactive
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,36 @@ public void checkedTemplateWithCustomBasePath() throws Exception {
DiagnosticSeverity.Error, "qute", QuteErrorCode.FragmentNotDefined.name()));
}

@Test
public void templateRecord() throws Exception {

// public class HelloResource {

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

// record Bonjour(String name) implements TemplateInstance {}

// record Status() {}

IJavaProject javaProject = loadMavenProject(QuteMavenProjectName.qute_record);

QuteJavaDiagnosticsParams params = new QuteJavaDiagnosticsParams();
IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/org/acme/sample/HelloResource.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(1, diagnostics.size());

assertDiagnostic(diagnostics, //
new Diagnostic(r(16, 11, 16, 18),
"No template matching the path Bonjour could be found for: org.acme.sample.HelloResource$Bonjour",
DiagnosticSeverity.Error, "qute", QuteErrorCode.NoMatchingTemplate.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 @@ -236,6 +236,37 @@ public void checkedTemplateWithCustomBasePath() throws Exception {
templateFileUri, "Create `src/main/resources/templates/ItemResourceWithFragment/items3.html`"));
}

@Test
public void templateRecord() throws Exception {

// public class HelloResource {

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

// record Bonjour(String name) implements TemplateInstance {}

// record Status() {}

IJavaProject javaProject = loadMavenProject(QuteMavenProjectName.qute_record);

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

List<DocumentLink> links = QuteSupportForJava.getInstance().documentLink(params, getJDTUtils(),
new NullProgressMonitor());
assertEquals(2, links.size());

String templateFileUri = javaProject.getProject().getFile("src/main/resources/templates/Hello.html")
.getLocationURI().toString();

assertDocumentLink(links, //
dl(r(14, 11, 14, 16), //
templateFileUri, "Open `src/main/resources/templates/Hello.html`"), //
dl(r(16, 11, 16, 18), //
templateFileUri, "Create `src/main/resources/templates/Bonjour.html`"));
}

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 @@ -344,6 +344,27 @@ public void record() throws Exception {
Assert.assertEquals("java.math.BigDecimal", result.getFields().get(1).getType());
}

@Test
public void enumDeclaredInRecord() throws Exception {
loadMavenProject(QuteMavenProjectName.qute_record);

QuteResolvedJavaTypeParams params = new QuteResolvedJavaTypeParams("org.acme.sample.SomeService$RunnerState$Status",
QuteMavenProjectName.qute_record);
ResolvedJavaTypeInfo result = QuteSupportForTemplate.getInstance().getResolvedJavaType(params, getJDTUtils(),
new NullProgressMonitor());
Assert.assertNotNull(result);
Assert.assertEquals("org.acme.sample.SomeService$RunnerState$Status", result.getSignature());
Assert.assertEquals(JavaTypeKind.Enum, result.getJavaTypeKind());

// Enum
Assert.assertNotNull(result.getFields());
Assert.assertEquals(4, result.getFields().size());
Assert.assertEquals("dead", result.getFields().get(0).getName());
Assert.assertEquals("org.acme.sample.SomeService$RunnerState$Status", result.getFields().get(0).getType());
Assert.assertEquals("alive", result.getFields().get(1).getName());
Assert.assertEquals("org.acme.sample.SomeService$RunnerState$Status", result.getFields().get(1).getType());
}

@Test
public void templateData() throws CoreException, Exception {
loadMavenProject(QuteMavenProjectName.qute_quickstart);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,27 +233,29 @@ public Location getJavaDefinition(QuteJavaDefinitionParams params, IJDTUtils uti
}

String parameterName = params.getSourceParameter();
String fieldName = params.getSourceField();
boolean dataMethodInvocation = parameterName != null && params.isDataMethodInvocation();

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(type, parameterName, utils,
monitor);

// 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);
String recordFieldName = parameterName != null ? parameterName : fieldName;
if (recordFieldName != null) {
IField recordField = type.getRecordComponent(recordFieldName);
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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
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.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.RecordDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
Expand All @@ -45,6 +46,7 @@
import org.eclipse.lsp4j.Range;

import com.redhat.qute.jdt.internal.AnnotationLocationSupport;
import com.redhat.qute.jdt.internal.QuteJavaConstants;
import com.redhat.qute.jdt.utils.AnnotationUtils;
import com.redhat.qute.jdt.utils.IJDTUtils;
import com.redhat.qute.jdt.utils.JDTQuteProjectUtils;
Expand Down Expand Up @@ -205,11 +207,38 @@ public boolean visit(TypeDeclaration node) {
*/
@Override
public boolean visit(RecordDeclaration node) {
String recordName = node.getName().getIdentifier();
collectTemplateLink(null, node, null, node, null, recordName, false);
if (isImplementTemplateInstance(node)) {
String recordName = node.getName().getIdentifier();
collectTemplateLink(null, node, null, node, null, recordName, false);
}
return super.visit(node);
}

/**
* Returns true if the record implements the "io.quarkus.qute.TemplateInstance"
* interface and false otherwise.
*
* @param node the record node.
* @return true if the record implements the "io.quarkus.qute.TemplateInstance"
* interface and false otherwise.
*/
private static boolean isImplementTemplateInstance(RecordDeclaration node) {
ITypeBinding binding = node.resolveBinding();
if (binding == null) {
return false;
}
ITypeBinding[] interfaces = binding.getInterfaces();
if (interfaces == null || interfaces.length == 0) {
return false;
}
for (ITypeBinding current : interfaces) {
if (QuteJavaConstants.TEMPLATE_INSTANCE_INTERFACE.equals(current.getQualifiedName())) {
return true;
}
}
return false;
}

/**
* Returns true if @CheckedTemplate annotation declares that fragment must be
* ignored and false otherwise.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ public List<String> resolveExtendedType() {
try {
String superTypeSignature = primaryType.getSuperclassTypeSignature();
if (superTypeSignature != null) {
extendedTypes.add(resolveTypeSignature(superTypeSignature));
extendedTypes.add(resolveTypeSignature(superTypeSignature, primaryType));
}
String[] superInterfaceTypeSignature = primaryType.getSuperInterfaceTypeSignatures();
if (superInterfaceTypeSignature != null) {
for (String string : superInterfaceTypeSignature) {
extendedTypes.add(resolveTypeSignature(string));
extendedTypes.add(resolveTypeSignature(string, primaryType));
}
}

Expand All @@ -89,7 +89,7 @@ public String resolveFieldSignature(IField field) {
StringBuilder signature = new StringBuilder(field.getElementName());
signature.append(" : ");
try {
signature.append(resolveTypeSignature(field.getTypeSignature(), false));
signature.append(resolveTypeSignature(field.getTypeSignature(), false, field.getDeclaringType()));
} catch (JavaModelException e) {
LOGGER.log(Level.SEVERE, "Error while resolving field type '" + field.getElementName() + "'", e);
}
Expand All @@ -111,8 +111,7 @@ public String resolveMethodSignature(IMethod method) {
ILocalVariable parameter = parameters[i];
signature.append(parameter.getElementName());
signature.append(" : ");
signature.append(
resolveLocalVariableSignature(parameter, varargs && i == parameters.length - 1));
signature.append(resolveLocalVariableSignature(parameter, varargs && i == parameters.length - 1));
}
}
} catch (JavaModelException e) {
Expand All @@ -121,7 +120,7 @@ public String resolveMethodSignature(IMethod method) {
}
signature.append(')');
try {
String returnType = resolveTypeSignature(method.getReturnType(), false);
String returnType = resolveTypeSignature(method.getReturnType(), false, method.getDeclaringType());
signature.append(" : ");
signature.append(returnType);
} catch (JavaModelException e) {
Expand All @@ -133,23 +132,24 @@ public String resolveMethodSignature(IMethod method) {

@Override
public String resolveLocalVariableSignature(ILocalVariable parameter, boolean varargs) {
return resolveTypeSignature(parameter.getTypeSignature(), varargs);
return resolveTypeSignature(parameter.getTypeSignature(), varargs,
parameter.getDeclaringMember().getDeclaringType());
}

@Override
public String resolveTypeSignature(String typeSignature) {
return resolveTypeSignature(typeSignature, false);
public String resolveTypeSignature(String typeSignature, IType declaringType) {
return resolveTypeSignature(typeSignature, false, declaringType);
}

private String resolveTypeSignature(String typeSignature, boolean varargs) {
private String resolveTypeSignature(String typeSignature, boolean varargs, IType declaringType) {
if (typeSignature.charAt(0) == '[') {
return doResolveTypeSignature(typeSignature.substring(1, typeSignature.length()))
return doResolveTypeSignature(typeSignature.substring(1, typeSignature.length()), declaringType)
+ (varargs ? "..." : "[]");
}
return doResolveTypeSignature(typeSignature);
return doResolveTypeSignature(typeSignature, declaringType);
}

private String doResolveTypeSignature(String typeSignature) {
private String doResolveTypeSignature(String typeSignature, IType declaringType) {
// Example: for 'class A<A1, A2> extends B<A2, String>' the type signature is
// 'QB<QA2;QString;>;'
// The method should return 'B<A2,java.lang.String>'
Expand All @@ -162,30 +162,51 @@ private String doResolveTypeSignature(String typeSignature) {
}
case Signature.C_RESOLVED: {
if (typeSignature.indexOf('$') != -1) {
// We cannot use Signature.toString(typeSignature); from JDT because it replaces '$' (for inner class) with '.'
// ex : Ljava.util.Set<Ljava.util.Map$Entry<TK;TV;>;>; the JDT Signature.toString returns java.util.Set<java.util.Map.Entry<K,V>>
// We cannot use Signature.toString(typeSignature); from JDT because it replaces
// '$' (for inner class) with '.'
// ex : Ljava.util.Set<Ljava.util.Map$Entry<TK;TV;>;>; the JDT
// Signature.toString returns java.util.Set<java.util.Map.Entry<K,V>>
// and not java.util.Set<java.util.Map$Entry<K,V>>
return doResolveTypeSignatureWithoutJDT(typeSignature);
return doResolveTypeSignatureWithoutJDT(typeSignature, declaringType);
}
return Signature.toString(typeSignature);
}
case Signature.C_UNRESOLVED:
return doResolveTypeSignatureWithoutJDT(typeSignature);
return doResolveTypeSignatureWithoutJDT(typeSignature, declaringType);
}
// ex :
// - Ljava.lang.Long;
// - Ljava.util.Set<Ljava.util.Map$Entry<TK;TV;>;>;
return Signature.toString(typeSignature);
}

public String doResolveTypeSignatureWithoutJDT(String typeSignature) {
public String doResolveTypeSignatureWithoutJDT(String typeSignature, IType declaringType) {
int startGeneric = typeSignature.indexOf('<');
boolean hasGeneric = startGeneric != -1;
if (!hasGeneric) {
// Example : 'QString';
// Remove the 'Q' start and the ';' end.
boolean endsWithColon = typeSignature.charAt(typeSignature.length() - 1) == Signature.C_NAME_END;
return resolveSimpleType(typeSignature.substring(1, typeSignature.length() - (endsWithColon ? 1 : 0)));
String simpleType = typeSignature.substring(1, typeSignature.length() - (endsWithColon ? 1 : 0));
String resolved = resolveSimpleType(simpleType);

if (resolved.equals(simpleType) && declaringType != null) {
try {
if (declaringType.isEnum()) {
return resolveJavaTypeSignature(declaringType);
}
} catch (JavaModelException e) {
}
IType type = declaringType.getType(simpleType);
while (type != null && type.exists()) {
resolved = resolveJavaTypeSignature(type);
if (!resolved.equals(simpleType)) {
return resolved;
}
type = type.getDeclaringType();
}
}
return resolved;
}

// Example :
Expand All @@ -195,7 +216,7 @@ public String doResolveTypeSignatureWithoutJDT(String typeSignature) {

String typeErasure = typeSignature.substring(0, startGeneric); // ex : List
StringBuilder result = new StringBuilder();
result.append(doResolveTypeSignature(typeErasure + Signature.C_NAME_END));
result.append(doResolveTypeSignature(typeErasure + Signature.C_NAME_END, declaringType));
result.append('<');

int bracket = 0;
Expand All @@ -216,7 +237,7 @@ public String doResolveTypeSignatureWithoutJDT(String typeSignature) {
result.append(",");
}
String s = typeSignature.substring(start, i);
result.append(doResolveTypeSignature(s + Signature.C_NAME_END));
result.append(doResolveTypeSignature(s + Signature.C_NAME_END, declaringType));
nbTypeParams++;
start = i + 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;

/**
* Type resolver API.
Expand Down Expand Up @@ -85,7 +86,7 @@ default String resolveSignature(IJavaElement javaElement) {
* @return the resolved Java type signature from the given String
* <code>typeSignature</code>.
*/
String resolveTypeSignature(String typeSignature);
String resolveTypeSignature(String typeSignature, IType declaringType);

/**
* Returns the Java field signature from the given JDT <code>field</code>.
Expand Down
Loading

0 comments on commit fcf5fe3

Please sign in to comment.