GH-1330: Explain AOP annotations with copilot
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Constants containing various fully-qualified annotation names.
|
||||
*
|
||||
@@ -85,6 +87,16 @@ public class Annotations {
|
||||
|
||||
public static final String SCHEDULED = "org.springframework.scheduling.annotation.Scheduled";
|
||||
|
||||
public static final Map<String, String> AOP_ANNOTATIONS = Map.of(
|
||||
"org.aspectj.lang.annotation.Pointcut", "Pointcut",
|
||||
"org.aspectj.lang.annotation.Before", "Before",
|
||||
"org.aspectj.lang.annotation.Around", "Around",
|
||||
"org.aspectj.lang.annotation.After", "After",
|
||||
"org.aspectj.lang.annotation.AfterReturning", "AfterReturning",
|
||||
"org.aspectj.lang.annotation.AfterThrowing", "AfterThrowing",
|
||||
"org.aspectj.lang.annotation.DeclareParents", "DeclareParents"
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.springframework.ide.vscode.boot.java.handlers.BootJavaWorkspaceSymbol
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HighlightProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.QueryCodeLensProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CopilotCodeLensProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.ActiveProfilesProvider;
|
||||
@@ -319,7 +319,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
protected BootJavaCodeLensEngine createCodeLensEngine(SpringSymbolIndex index, JavaProjectFinder projectFinder, SimpleLanguageServer server, SpelSemanticTokens spelSemanticTokens) {
|
||||
Collection<CodeLensProvider> codeLensProvider = new ArrayList<>();
|
||||
codeLensProvider.add(new WebfluxHandlerCodeLensProvider(index));
|
||||
codeLensProvider.add(new QueryCodeLensProvider(projectFinder, server, spelSemanticTokens));
|
||||
codeLensProvider.add(new CopilotCodeLensProvider(projectFinder, server, spelSemanticTokens));
|
||||
|
||||
return new BootJavaCodeLensEngine(this, codeLensProvider);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ package org.springframework.ide.vscode.boot.java.handlers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -25,13 +27,17 @@ import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.MethodInvocation;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.lsp4j.CodeLens;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.spel.AnnotationParamSpelExtractor;
|
||||
import org.springframework.ide.vscode.boot.java.spel.AnnotationParamSpelExtractor.Snippet;
|
||||
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
|
||||
@@ -49,9 +55,9 @@ import com.google.gson.JsonPrimitive;
|
||||
/**
|
||||
* @author Udayani V
|
||||
*/
|
||||
public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
public class CopilotCodeLensProvider implements CodeLensProvider {
|
||||
|
||||
protected static Logger logger = LoggerFactory.getLogger(QueryCodeLensProvider.class);
|
||||
protected static Logger logger = LoggerFactory.getLogger(CopilotCodeLensProvider.class);
|
||||
|
||||
public static final String CMD_ENABLE_COPILOT_FEATURES = "sts/enable/copilot/features";
|
||||
|
||||
@@ -66,13 +72,13 @@ public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
private SpelSemanticTokens spelSemanticTokens;
|
||||
|
||||
private static boolean showCodeLenses;
|
||||
|
||||
public QueryCodeLensProvider(JavaProjectFinder projectFinder, SimpleLanguageServer server, SpelSemanticTokens spelSemanticTokens) {
|
||||
|
||||
public CopilotCodeLensProvider(JavaProjectFinder projectFinder, SimpleLanguageServer server, SpelSemanticTokens spelSemanticTokens) {
|
||||
this.projectFinder = projectFinder;
|
||||
this.spelSemanticTokens = spelSemanticTokens;
|
||||
server.onCommand(CMD_ENABLE_COPILOT_FEATURES, params -> {
|
||||
if (params.getArguments().get(0) instanceof JsonPrimitive) {
|
||||
QueryCodeLensProvider.showCodeLenses = ((JsonPrimitive) params.getArguments().get(0)).getAsBoolean();
|
||||
CopilotCodeLensProvider.showCodeLenses = ((JsonPrimitive) params.getArguments().get(0)).getAsBoolean();
|
||||
}
|
||||
return CompletableFuture.completedFuture(showCodeLenses);
|
||||
});
|
||||
@@ -84,6 +90,9 @@ public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
if (!showCodeLenses) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> pointcutMap = findPointcuts(cu);
|
||||
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
@@ -91,14 +100,15 @@ public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
Arrays.stream(spelExtractors).map(e -> e.getSpelRegion(node)).filter(o -> o.isPresent())
|
||||
.map(o -> o.get()).forEach(snippet -> {
|
||||
String additionalContext = parseSpelAndFetchContext(cu, snippet.text());
|
||||
provideCodeLensForSpelExpression(cancelToken, node, document, snippet,
|
||||
additionalContext, resultAccumulator);
|
||||
provideCodeLensForSpelExpression(cancelToken, node, document, snippet, additionalContext, resultAccumulator);
|
||||
});
|
||||
|
||||
if (isQueryAnnotation(node)) {
|
||||
String queryPrompt = determineQueryPrompt(document);
|
||||
provideCodeLensForQuery(cancelToken, node, document, node.getValue(), queryPrompt,
|
||||
resultAccumulator);
|
||||
QueryType queryType = determineQueryType(document);
|
||||
provideCodeLensForExpression(cancelToken, node, document, queryType, "", resultAccumulator);
|
||||
} else if (isAopAnnotation(node)) {
|
||||
String additionalPointcutContext = extractPointcutReference(node.getValue(), pointcutMap);
|
||||
provideCodeLensForExpression(cancelToken, node, document, QueryType.AOP, additionalPointcutContext, resultAccumulator);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
@@ -106,31 +116,40 @@ public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
|
||||
|
||||
Arrays.stream(spelExtractors).map(e -> e.getSpelRegion(node)).filter(o -> o.isPresent())
|
||||
.map(o -> o.get()).forEach(snippet -> {
|
||||
String additionalContext = parseSpelAndFetchContext(cu, snippet.text());
|
||||
provideCodeLensForSpelExpression(cancelToken, node, document, snippet, additionalContext,
|
||||
resultAccumulator);
|
||||
provideCodeLensForSpelExpression(cancelToken, node, document, snippet, additionalContext, resultAccumulator);
|
||||
});
|
||||
|
||||
if (isQueryAnnotation(node)) {
|
||||
String queryPrompt = determineQueryPrompt(document);
|
||||
for (Object value : node.values()) {
|
||||
if (value instanceof MemberValuePair) {
|
||||
MemberValuePair pair = (MemberValuePair) value;
|
||||
if ("value".equals(pair.getName().getIdentifier())) {
|
||||
provideCodeLensForQuery(cancelToken, node, document, pair.getValue(), queryPrompt,
|
||||
resultAccumulator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
QueryType queryType = determineQueryType(document);
|
||||
provideCodeLensForExpression(cancelToken, node, document, queryType, "", resultAccumulator);
|
||||
} else if (isAopAnnotation(node)) {
|
||||
Expression value = getMemberValue(node);
|
||||
String additionalPointcutContext = null;
|
||||
if (value != null) {
|
||||
additionalPointcutContext = extractPointcutReference(value, pointcutMap);
|
||||
}
|
||||
provideCodeLensForExpression(cancelToken, node, document, QueryType.AOP, additionalPointcutContext, resultAccumulator);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
private Expression getMemberValue(NormalAnnotation node) {
|
||||
for (Object value : node.values()) {
|
||||
if (value instanceof MemberValuePair) {
|
||||
MemberValuePair pair = (MemberValuePair) value;
|
||||
if ("pointcut".equals(pair.getName().getIdentifier())) {
|
||||
return pair.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -163,20 +182,26 @@ public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
}
|
||||
}
|
||||
|
||||
protected void provideCodeLensForQuery(CancelChecker cancelToken, Annotation node, TextDocument document,
|
||||
Expression valueExp, String query, List<CodeLens> resultAccumulator) {
|
||||
protected void provideCodeLensForExpression(CancelChecker cancelToken, Annotation node, TextDocument document,
|
||||
QueryType queryType, String additionalContext, List<CodeLens> resultAccumulator) {
|
||||
cancelToken.checkCanceled();
|
||||
|
||||
if (valueExp != null) {
|
||||
if (node != null) {
|
||||
try {
|
||||
|
||||
|
||||
String context = additionalContext != null && !additionalContext.isEmpty() ? String.format(
|
||||
"""
|
||||
This is the pointcut definition referenced in the above annotation. \n\n %s \n\nProvide a brief summary of the pointcut's role within the annotation.
|
||||
Avoid detailed implementation steps and avoid repeating information covered earlier.
|
||||
""",additionalContext) : "";
|
||||
|
||||
CodeLens codeLens = new CodeLens();
|
||||
codeLens.setRange(document.toRange(valueExp.getStartPosition(), valueExp.getLength()));
|
||||
codeLens.setRange(document.toRange(node.getStartPosition(), node.getLength()));
|
||||
|
||||
Command cmd = new Command();
|
||||
cmd.setTitle(QueryType.DEFAULT.getTitle());
|
||||
cmd.setTitle(queryType.getTitle());
|
||||
cmd.setCommand(CMD);
|
||||
cmd.setArguments(ImmutableList.of(query + valueExp.toString()));
|
||||
cmd.setArguments(ImmutableList.of(queryType.getPrompt() + node.toString() + "\n\n" +context));
|
||||
codeLens.setCommand(cmd);
|
||||
|
||||
resultAccumulator.add(codeLens);
|
||||
@@ -190,15 +215,15 @@ public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
return FQN_QUERY.equals(a.getTypeName().getFullyQualifiedName())
|
||||
|| QUERY.equals(a.getTypeName().getFullyQualifiedName());
|
||||
}
|
||||
|
||||
private String determineQueryPrompt(TextDocument document) {
|
||||
|
||||
private QueryType determineQueryType(TextDocument document) {
|
||||
Optional<IJavaProject> optProject = projectFinder.find(document.getId());
|
||||
if (optProject.isPresent()) {
|
||||
IJavaProject jp = optProject.get();
|
||||
return SpringProjectUtil.hasDependencyStartingWith(jp, "hibernate-core", null) ? QueryType.HQL.getPrompt()
|
||||
: QueryType.JPQL.getPrompt();
|
||||
return SpringProjectUtil.hasDependencyStartingWith(jp, "hibernate-core", null) ? QueryType.HQL
|
||||
: QueryType.JPQL;
|
||||
}
|
||||
return QueryType.DEFAULT.getPrompt();
|
||||
return QueryType.DEFAULT;
|
||||
}
|
||||
|
||||
private String parseSpelAndFetchContext(CompilationUnit cu, String spelExpression) {
|
||||
@@ -238,4 +263,49 @@ public class QueryCodeLensProvider implements CodeLensProvider {
|
||||
return methodContext;
|
||||
}
|
||||
|
||||
private boolean isAopAnnotation(Annotation a) {
|
||||
String annotationFQN = a.getTypeName().getFullyQualifiedName();
|
||||
return Annotations.AOP_ANNOTATIONS.containsKey(annotationFQN)
|
||||
|| Annotations.AOP_ANNOTATIONS.containsValue(annotationFQN);
|
||||
}
|
||||
|
||||
private Map<String, String> findPointcuts(CompilationUnit cu) {
|
||||
Map<String, String> pointcutMap = new HashMap<>();
|
||||
cu.accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
for (Object modifierObj : node.modifiers()) {
|
||||
if (modifierObj instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifierObj;
|
||||
if ("Pointcut".equals(annotation.getTypeName().getFullyQualifiedName())) {
|
||||
String methodName = node.getName().getIdentifier();
|
||||
pointcutMap.put(methodName, node.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
return pointcutMap;
|
||||
|
||||
}
|
||||
|
||||
private String extractPointcutReference(org.eclipse.jdt.core.dom.Expression expression, Map<String, String> pointcutMap) {
|
||||
if (expression instanceof MethodInvocation) {
|
||||
return ((MethodInvocation) expression).getName().getIdentifier();
|
||||
} else if (expression instanceof SimpleName) {
|
||||
return ((SimpleName) expression).getIdentifier();
|
||||
} else if (expression instanceof StringLiteral) {
|
||||
String literalValue = ((StringLiteral) expression).getLiteralValue();
|
||||
StringBuilder pointcuts = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : pointcutMap.entrySet()) {
|
||||
if (literalValue.contains(entry.getKey())) {
|
||||
pointcuts.append(entry.getValue());
|
||||
}
|
||||
}
|
||||
return pointcuts.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
public enum QueryType {
|
||||
SPEL("Explain SpEL Expression using Copilot", "Explain the following SpEL Expression with a clear summary first, followed by a breakdown of the expression with details: \n\n"),
|
||||
JPQL("Explain Query using Copilot", "Explain the following JPQL query with a clear summary first, followed by a detailed explanation. If the query contains any SpEL expressions, explain those parts as well: \n\n"),
|
||||
HQL("Explain Query using Copilot", "Explain the following HQL query with a clear summary first, followed by a detailed explanation. If the query contains any SpEL expressions, explain those parts as well: \n\n"),
|
||||
DEFAULT("Explain Query using Copilot", "Explain the following query with a clear summary first, followed by a detailed explanation: \n\n");
|
||||
SPEL("Explain SpEL Expression with Copilot", "Explain the following SpEL Expression with a clear summary first, followed by a breakdown of the expression with details: \n\n"),
|
||||
JPQL("Explain Query with Copilot", "Explain the following JPQL query with a clear summary first, followed by a detailed explanation. If the query contains any SpEL expressions, explain those parts as well: \n\n"),
|
||||
HQL("Explain Query with Copilot", "Explain the following HQL query with a clear summary first, followed by a detailed explanation. If the query contains any SpEL expressions, explain those parts as well: \n\n"),
|
||||
AOP("Explain AOP annotation with Copilot", "Explain the following AOP annotation with a clear summary first, followed by a detailed contextual explanation of annotation and its purpose: \n\n"),
|
||||
DEFAULT("Explain Query with Copilot", "Explain the following query with a clear summary first, followed by a detailed explanation: \n\n");
|
||||
|
||||
private final String title;
|
||||
private final String prompt;
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.QueryCodeLensProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CopilotCodeLensProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.QueryType;
|
||||
import org.springframework.ide.vscode.boot.java.spel.SpelSemanticTokens;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
@@ -59,7 +59,7 @@ import com.google.gson.JsonPrimitive;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class QueryCodeLensProviderTest {
|
||||
public class CopilotCodeLensProviderTest {
|
||||
|
||||
@Autowired
|
||||
private BootLanguageServerHarness harness;
|
||||
@@ -73,17 +73,17 @@ public class QueryCodeLensProviderTest {
|
||||
private SpelSemanticTokens spelSemanticTokens;
|
||||
|
||||
private ArgumentCaptor<ExecuteCommandHandler> commandHandlerCaptor;
|
||||
private QueryCodeLensProvider queryCodeLensProvider;
|
||||
private CopilotCodeLensProvider queryCodeLensProvider;
|
||||
private File directory;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spel-query-codelense/").toURI());
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spel-query-aop-codelenses/").toURI());
|
||||
String projectDir = directory.toURI().toString();
|
||||
server = mock(SimpleLanguageServer.class);
|
||||
commandHandlerCaptor = ArgumentCaptor.forClass(ExecuteCommandHandler.class);
|
||||
queryCodeLensProvider = new QueryCodeLensProvider(projectFinder, server, spelSemanticTokens);
|
||||
queryCodeLensProvider = new CopilotCodeLensProvider(projectFinder, server, spelSemanticTokens);
|
||||
|
||||
// trigger project creation
|
||||
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
@@ -91,7 +91,7 @@ public class QueryCodeLensProviderTest {
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
|
||||
verify(server).onCommand(eq(QueryCodeLensProvider.CMD_ENABLE_COPILOT_FEATURES), commandHandlerCaptor.capture());
|
||||
verify(server).onCommand(eq(CopilotCodeLensProvider.CMD_ENABLE_COPILOT_FEATURES), commandHandlerCaptor.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,9 +107,9 @@ public class QueryCodeLensProviderTest {
|
||||
|
||||
assertEquals(3, codeLenses.size());
|
||||
|
||||
assertTrue(containsCodeLens(codeLenses.get(0), QueryType.DEFAULT.getTitle(), 9, 8, 9, 108));
|
||||
assertTrue(containsCodeLens(codeLenses.get(1), QueryType.DEFAULT.getTitle(), 13, 8, 13, 39));
|
||||
assertTrue(containsCodeLens(codeLenses.get(2), QueryType.DEFAULT.getTitle(), 17, 14, 17, 92));
|
||||
assertTrue(containsCodeLens(codeLenses.get(0), QueryType.DEFAULT.getTitle(), 9, 1, 9, 109));
|
||||
assertTrue(containsCodeLens(codeLenses.get(1), QueryType.DEFAULT.getTitle(), 13, 1, 13, 40));
|
||||
assertTrue(containsCodeLens(codeLenses.get(2), QueryType.DEFAULT.getTitle(), 17, 1, 17, 93));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -194,6 +194,85 @@ public static String concat(String str1,String str2){
|
||||
assertEquals(expectedPrompt, actualPrompt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowCodeLensesTrueForAOP() throws Exception {
|
||||
|
||||
setCommandParamsHandler(true);
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MyAspect.java").toUri().toString();
|
||||
TextDocumentInfo doc = harness.getOrReadFile(new File(new URI(docUri)), LanguageId.JAVA.getId());
|
||||
TextDocumentInfo openedDoc = harness.openDocument(doc);
|
||||
|
||||
List<? extends CodeLens> codeLenses = harness.getCodeLenses(openedDoc);
|
||||
|
||||
assertEquals(7, codeLenses.size());
|
||||
|
||||
assertTrue(containsCodeLens(codeLenses.get(0), QueryType.AOP.getTitle(), 9, 1, 9, 53));
|
||||
assertTrue(containsCodeLens(codeLenses.get(1), QueryType.AOP.getTitle(), 14, 1, 14, 24));
|
||||
assertTrue(containsCodeLens(codeLenses.get(2), QueryType.AOP.getTitle(), 19, 1, 19, 51));
|
||||
assertTrue(containsCodeLens(codeLenses.get(3), QueryType.AOP.getTitle(), 27, 1, 27, 50));
|
||||
assertTrue(containsCodeLens(codeLenses.get(4), QueryType.AOP.getTitle(), 32, 1, 32, 92));
|
||||
assertTrue(containsCodeLens(codeLenses.get(5), QueryType.AOP.getTitle(), 37, 1, 37, 86));
|
||||
assertTrue(containsCodeLens(codeLenses.get(6), QueryType.AOP.getTitle(), 42, 1, 42, 65));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowCodeLensesTrueForAopPointcutExamples() throws Exception {
|
||||
|
||||
setCommandParamsHandler(true);
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/PointcutExamples.java").toUri().toString();
|
||||
TextDocumentInfo doc = harness.getOrReadFile(new File(new URI(docUri)), LanguageId.JAVA.getId());
|
||||
TextDocumentInfo openedDoc = harness.openDocument(doc);
|
||||
|
||||
String expectedPrompt = """
|
||||
Explain the following AOP annotation with a clear summary first, followed by a detailed contextual explanation of annotation and its purpose: \n
|
||||
@Pointcut("cflow(execution(* com.example..*.*(..)))")
|
||||
|
||||
""";
|
||||
|
||||
String expectedPromptWithContext = """
|
||||
Explain the following AOP annotation with a clear summary first, followed by a detailed contextual explanation of annotation and its purpose: \n
|
||||
@AfterReturning(pointcut="targetService()",returning="result")
|
||||
|
||||
This is the pointcut definition referenced in the above annotation. \n
|
||||
@Pointcut("target(com.example.service.MyService)") public void targetService(){
|
||||
}
|
||||
\n
|
||||
Provide a brief summary of the pointcut's role within the annotation.
|
||||
Avoid detailed implementation steps and avoid repeating information covered earlier.
|
||||
""";
|
||||
String expectedPromptWithMultiPointcutRef = """
|
||||
Explain the following AOP annotation with a clear summary first, followed by a detailed contextual explanation of annotation and its purpose: \n
|
||||
@Pointcut("serviceLayer() || repositoryLayer()")
|
||||
|
||||
This is the pointcut definition referenced in the above annotation. \n
|
||||
@Pointcut("within(com.example.repository..*)") public void repositoryLayer(){
|
||||
}
|
||||
@Pointcut("execution(* com.example.service.*.*(..))") public void serviceLayer(){
|
||||
}
|
||||
\n
|
||||
Provide a brief summary of the pointcut's role within the annotation.
|
||||
Avoid detailed implementation steps and avoid repeating information covered earlier.
|
||||
""";
|
||||
|
||||
List<? extends CodeLens> codeLenses = harness.getCodeLenses(openedDoc);
|
||||
|
||||
assertEquals(8, codeLenses.size());
|
||||
|
||||
assertTrue(containsCodeLens(codeLenses.get(0), QueryType.AOP.getTitle(), 4, 1, 4, 54));
|
||||
assertTrue(containsCodeLens(codeLenses.get(3), QueryType.AOP.getTitle(), 15, 1, 15, 64));
|
||||
|
||||
String actualPrompt = codeLenses.get(0).getCommand().getArguments().get(0).toString();
|
||||
String actualPromptWithContext = codeLenses.get(3).getCommand().getArguments().get(0).toString();
|
||||
String actualPromptWithMultiPointcutRef = codeLenses.get(7).getCommand().getArguments().get(0).toString();
|
||||
|
||||
assertEquals(expectedPrompt, actualPrompt);
|
||||
assertEquals(expectedPromptWithContext, actualPromptWithContext);
|
||||
assertEquals(expectedPromptWithMultiPointcutRef, actualPromptWithMultiPointcutRef);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowCodeLensesFalseForQuery() throws Exception {
|
||||
|
||||
@@ -222,6 +301,20 @@ public static String concat(String str1,String str2){
|
||||
assertEquals(0, codeLenses.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowCodeLensesFalseForAOP() throws Exception {
|
||||
|
||||
setCommandParamsHandler(false);
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MyAspect.java").toUri().toString();
|
||||
TextDocumentInfo doc = harness.getOrReadFile(new File(new URI(docUri)), LanguageId.JAVA.getId());
|
||||
TextDocumentInfo openedDoc = harness.openDocument(doc);
|
||||
|
||||
List<? extends CodeLens> codeLenses = harness.getCodeLenses(openedDoc);
|
||||
|
||||
assertEquals(0, codeLenses.size());
|
||||
}
|
||||
|
||||
private void setCommandParamsHandler(boolean value) throws InterruptedException, ExecutionException {
|
||||
ExecuteCommandHandler handler = commandHandlerCaptor.getValue();
|
||||
ExecuteCommandParams params = new ExecuteCommandParams();
|
||||
@@ -35,6 +35,11 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Context for SpEL -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.test;
|
||||
|
||||
import org.aspectj.lang.annotation.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Aspect
|
||||
public class MyAspect {
|
||||
|
||||
@Pointcut("execution(* com.example.aopdemo..*(..))")
|
||||
public void myPointcut() {
|
||||
// Pointcut definition
|
||||
}
|
||||
|
||||
@Before("myPointcut()")
|
||||
public void beforeAdvice() {
|
||||
System.out.println("Before advice executed");
|
||||
}
|
||||
|
||||
@Around("execution(* com.example.aopdemo..*(..))")
|
||||
public Object aroundAdvice(org.aspectj.lang.ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
System.out.println("Around advice: before method execution");
|
||||
Object result = joinPoint.proceed();
|
||||
System.out.println("Around advice: after method execution");
|
||||
return result;
|
||||
}
|
||||
|
||||
@After("execution(* com.example.aopdemo..*(..))")
|
||||
public void afterAdvice() {
|
||||
System.out.println("After advice executed");
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "execution(* com.example.aopdemo..*(..))", returning = "result")
|
||||
public void afterReturningAdvice(Object result) {
|
||||
System.out.println("After returning advice executed, returned: " + result);
|
||||
}
|
||||
|
||||
@AfterThrowing(pointcut = "execution(* com.example.aopdemo..*(..))", throwing = "ex")
|
||||
public void afterThrowingAdvice(Exception ex) {
|
||||
System.out.println("After throwing advice executed, exception: " + ex.getMessage());
|
||||
}
|
||||
|
||||
@DeclareParents(value = "org.test..*", defaultImpl = Test.class)
|
||||
public static MyInterface mixin;
|
||||
}
|
||||
|
||||
interface MyInterface {
|
||||
void someMethod();
|
||||
}
|
||||
|
||||
class Test implements MyInterface {
|
||||
@Override
|
||||
public void someMethod() {
|
||||
System.out.println("Default implementation");
|
||||
}
|
||||
}
|
||||
@@ -123,16 +123,4 @@ public class Owner extends Person {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("id", this.getId())
|
||||
.append("new", this.isNew())
|
||||
.append("lastName", this.getLastName())
|
||||
.append("firstName", this.getFirstName())
|
||||
.append("address", this.address)
|
||||
.append("city", this.city)
|
||||
.append("telephone", this.telephone)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.samples.petclinic.model.NamedEntity;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderBy;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "pets")
|
||||
public class Pet extends NamedEntity {
|
||||
|
||||
@Column(name = "birth_date")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate birthDate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "type_id")
|
||||
private PetType type;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "pet_id")
|
||||
@OrderBy("visit_date ASC")
|
||||
private Set<Visit> visits = new LinkedHashSet<>();
|
||||
|
||||
public void setBirthDate(LocalDate birthDate) {
|
||||
this.birthDate = birthDate;
|
||||
}
|
||||
|
||||
public LocalDate getBirthDate() {
|
||||
return this.birthDate;
|
||||
}
|
||||
|
||||
public PetType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(PetType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Collection<Visit> getVisits() {
|
||||
return this.visits;
|
||||
}
|
||||
|
||||
public void addVisit(Visit visit) {
|
||||
getVisits().add(visit);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.test;
|
||||
|
||||
public class PointcutExamples {
|
||||
|
||||
@Pointcut("cflow(execution(* com.example..*.*(..)))")
|
||||
public void myPointcutFlow() {}
|
||||
|
||||
@Pointcut("target(com.example.service.MyService)")
|
||||
public void targetService() {}
|
||||
|
||||
@Before("myPointcutFlow()")
|
||||
public void beforeAdviceFlow() {
|
||||
System.out.println("Before advice triggered by control flow.");
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut="targetService()", returning="result")
|
||||
public void afterReturningAdvice(Object result) {
|
||||
System.out.println("After returning advice: " + result);
|
||||
}
|
||||
|
||||
@Around("targetService()")
|
||||
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
System.out.println("Around advice: before method execution");
|
||||
Object result = joinPoint.proceed();
|
||||
System.out.println("Around advice: after method execution");
|
||||
return result;
|
||||
}
|
||||
|
||||
@Pointcut("execution(* com.example.service.*.*(..))")
|
||||
public void serviceLayer() {}
|
||||
|
||||
@Pointcut("within(com.example.repository..*)")
|
||||
public void repositoryLayer() {}
|
||||
|
||||
@Pointcut("serviceLayer() || repositoryLayer()")
|
||||
public void applicationLayer() {}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user