Skip to content

Commit 7ffd74f

Browse files
committed
can show call graph but have some bug
1 parent f9fe9e5 commit 7ffd74f

File tree

7 files changed

+4995
-1
lines changed

7 files changed

+4995
-1
lines changed

gradle.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ versionNeo4jJavaBoltDriver=5.11.0
1717
versionTestcontainers=1.17.6
1818

1919
# Performance
20-
org.gradle.jvmargs=-Xmx2048m
20+
org.gradle.jvmargs=-Xmx2048m -DsocksProxyHost=127.0.0.1 -DsocksProxyPort=7890
21+
22+
#systemProp.socks.proxyHost=127.0.0.1
23+
#systemProp.socks.proxyPort=7890

log.txt

Lines changed: 4708 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.albertoventurini.graphdbplugin.jetbrains.ui.console.graph;
2+
3+
import com.albertoventurini.graphdbplugin.database.api.data.GraphNode;
4+
import com.albertoventurini.graphdbplugin.database.api.data.GraphRelationship;
5+
import com.intellij.ide.SelectInEditorManager;
6+
import com.intellij.openapi.project.Project;
7+
import com.intellij.psi.*;
8+
import com.intellij.psi.util.PsiTreeUtil;
9+
import com.intellij.usageView.UsageInfo;
10+
import prefuse.visual.VisualItem;
11+
12+
import java.awt.event.MouseEvent;
13+
import java.util.Collection;
14+
15+
/**
16+
* @author daozhe@alibaba-inc.com
17+
* @date 2023/12/3 16:14
18+
*/
19+
public class EdgeNavigateAction {
20+
private final Project project;
21+
public EdgeNavigateAction(Project project){
22+
this.project = project;
23+
}
24+
25+
public void navigateToInvocation(GraphRelationship relationship, VisualItem item, MouseEvent e){
26+
JavaInvokeInsn invocation = getJavaInvokeInsn(relationship);
27+
PsiMethod method = getPsiMethod(invocation);
28+
PsiCallExpression methodCall = getPsiMethodCall(invocation, method);
29+
navigate(methodCall);
30+
}
31+
32+
public PsiCallExpression getPsiMethodCall(JavaInvokeInsn invocation, PsiMethod method){
33+
UsageInfo usage = new UsageInfo(method);
34+
PsiMethod psiMethod = PsiTreeUtil.getParentOfType(usage.getFile().findElementAt(usage.getSegment().getEndOffset()), PsiMethod.class);
35+
Collection<PsiCallExpression> callExpressions = PsiTreeUtil.collectElementsOfType(psiMethod, PsiCallExpression.class);
36+
return callExpressions.stream().filter(call -> matchInvocation(call, invocation)).findFirst().get();
37+
}
38+
39+
public boolean matchInvocation(PsiCallExpression call, JavaInvokeInsn invocation){
40+
if (call instanceof PsiMethodCallExpression) {
41+
String fqn = getFqn((PsiMethodCallExpression) call);
42+
return invocation.getCallee().endsWith(fqn);
43+
}
44+
45+
PsiMethod callee = call.resolveMethod();
46+
if (callee == null){
47+
return call.getText().contains(invocation.getCalleeMethodName() + "(");
48+
}else{
49+
PsiClass calleeClass = callee.getContainingClass();
50+
String methodName = callee.getName();
51+
String className = calleeClass.getQualifiedName();
52+
String fqn = className + "." + methodName;
53+
return fqn.equals(invocation.getCallee());
54+
}
55+
}
56+
57+
public String getFqn(PsiMethodCallExpression expression){
58+
59+
PsiReferenceExpression methodRef = expression.getMethodExpression();
60+
String methodName = methodRef.getReferenceName();
61+
String fqn = methodName;
62+
PsiElement qualifier = methodRef.getQualifier();
63+
if (qualifier != null){
64+
if (qualifier instanceof PsiExpression){
65+
PsiExpression qualifierExpression = (PsiExpression) qualifier;
66+
PsiType type = qualifierExpression.getType();
67+
if (type != null){
68+
fqn = type.getCanonicalText() + "." + methodName;
69+
}else{
70+
fqn = qualifierExpression.getText() + "." + methodName;
71+
}
72+
}
73+
}
74+
75+
return fqn;
76+
}
77+
78+
public JavaInvokeInsn getJavaInvokeInsn(GraphRelationship relationship){
79+
String insn = (String)relationship.getPropertyContainer().getProperties().get("insn");
80+
JavaInvokeInsn invocation = new JavaInvokeInsn(insn);
81+
return invocation;
82+
}
83+
84+
public PsiMethod getPsiMethod(JavaInvokeInsn invocation){
85+
PsiMethod method = invocation.getCaller(this.project);
86+
return method;
87+
}
88+
89+
90+
91+
private void navigate(PsiElement element){
92+
UsageInfo usage = new UsageInfo(element);
93+
SelectInEditorManager.getInstance(this.project)
94+
.selectInEditor(usage.getVirtualFile(), usage.getSegment().getStartOffset(), usage.getSegment().getEndOffset(), true, false);
95+
}
96+
}

ui/jetbrains/src/main/java/com/albertoventurini/graphdbplugin/jetbrains/ui/console/graph/GraphPanelInteractions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public class GraphPanelInteractions {
2828
private final QueryExecutionService queryExecutionService;
2929
private final VisualizationApi visualization;
3030

31+
private final NodeNavigateAction nodeNavigateAction;
32+
private final EdgeNavigateAction edgeNavigateAction;
33+
3134
public GraphPanelInteractions(
3235
@NotNull final Project project,
3336
final GraphConsoleView graphConsoleView,
@@ -37,6 +40,8 @@ public GraphPanelInteractions(
3740
this.messageBus = messageBus;
3841
this.visualization = visualization;
3942
this.queryExecutionService = new QueryExecutionService(project, messageBus);
43+
this.nodeNavigateAction = new NodeNavigateAction(project);
44+
this.edgeNavigateAction = new EdgeNavigateAction(project);
4045

4146
registerMessageBusSubscribers();
4247
registerVisualisationEvents();
@@ -88,7 +93,9 @@ public void executionCompleted(ExecuteQueryPayload payload) {
8893

8994
private void registerVisualisationEvents() {
9095
visualization.addNodeListener(EventType.CLICK, graphConsoleView.getGraphPanel()::showNodeData);
96+
visualization.addNodeListener(EventType.CLICK, this.nodeNavigateAction::navigateToMethodDeclaration);
9197
visualization.addEdgeListener(EventType.CLICK, graphConsoleView.getGraphPanel()::showRelationshipData);
98+
visualization.addEdgeListener(EventType.CLICK, this.edgeNavigateAction::navigateToInvocation);
9299
visualization.addNodeListener(EventType.HOVER_START, graphConsoleView.getGraphPanel()::showTooltip);
93100
visualization.addNodeListener(EventType.HOVER_END, graphConsoleView.getGraphPanel()::hideTooltip);
94101
visualization.addEdgeListener(EventType.HOVER_START, graphConsoleView.getGraphPanel()::showTooltip);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.albertoventurini.graphdbplugin.jetbrains.ui.console.graph;
2+
3+
import com.intellij.openapi.project.Project;
4+
import com.intellij.psi.PsiMethod;
5+
6+
/**
7+
* @author daozhe@alibaba-inc.com
8+
* @date 2023/12/3 16:05
9+
*/
10+
public class JavaInvokeInsn {
11+
private JavaMethodSignature callerSignature;
12+
private String callee;
13+
private Integer index;
14+
15+
public JavaInvokeInsn(String insn){
16+
String[] parts = insn.split("/");
17+
if (parts.length != 3) {
18+
throw new IllegalArgumentException("Invalid instruction format");
19+
}
20+
21+
this.callerSignature = new JavaMethodSignature(parts[0]);
22+
this.callee = parts[1].replace("$", ".");
23+
24+
try {
25+
this.index = Integer.parseInt(parts[2]);
26+
} catch (NumberFormatException e) {
27+
throw new IllegalArgumentException("Invalid index value", e);
28+
}
29+
}
30+
31+
public PsiMethod getCaller(Project project){
32+
return this.callerSignature.getMethod(project);
33+
}
34+
35+
public String getCallee(){
36+
return this.callee;
37+
}
38+
39+
public String getCalleeMethodName(){
40+
return this.callee.substring(callee.lastIndexOf(".")+1);
41+
}
42+
43+
44+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.albertoventurini.graphdbplugin.jetbrains.ui.console.graph;
2+
3+
import com.intellij.openapi.project.Project;
4+
import com.intellij.psi.*;
5+
import com.intellij.psi.search.GlobalSearchScope;
6+
7+
import java.util.regex.Matcher;
8+
import java.util.regex.Pattern;
9+
10+
/**
11+
* @author daozhe@alibaba-inc.com
12+
* @date 2023/12/2 21:10
13+
*/
14+
public class JavaMethodSignature {
15+
private String signature;
16+
private String className;
17+
private String methodName;
18+
private final String[] paramTypes;
19+
20+
private PsiElement element;
21+
22+
private static final Pattern SIGNATURE_PATTERN = Pattern.compile(
23+
"^<([^:]+):\\s*(\\S+)\\s+([^\\(]+)\\(([^\\)]*)\\)>$"
24+
);
25+
26+
public JavaMethodSignature(String signature){
27+
this.signature = signature;
28+
// 假设输入的格式为 <org.apache.logging.log4j.core.filter.Filterable: boolean isFiltered(org.apache.logging.log4j.core.LogEvent)>
29+
Matcher matcher = SIGNATURE_PATTERN.matcher(signature);
30+
if (!matcher.matches()) {
31+
throw new IllegalArgumentException("Invalid method signature format");
32+
}
33+
34+
this.className = matcher.group(1).trim();
35+
// Group 2 is the return type which is not used in this implementation
36+
this.methodName = matcher.group(3).trim();
37+
38+
if (this.methodName.equals("<init>")){
39+
this.methodName = className.substring(className.lastIndexOf(".") + 1);
40+
}
41+
42+
String params = matcher.group(4).trim();
43+
this.paramTypes = params.isEmpty() ? new String[0] : params.split(",");
44+
}
45+
46+
public String getClassName(){
47+
return this.methodName;
48+
}
49+
50+
public String getMethodName(){
51+
return this.methodName;
52+
}
53+
54+
public String[] getParamTypes(){
55+
return this.paramTypes;
56+
}
57+
58+
public PsiMethod getMethod(Project project) {
59+
JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
60+
PsiClass psiClass = facade.findClass(this.className.replace("$", "."), GlobalSearchScope.allScope(project));
61+
62+
if (psiClass != null) {
63+
for (PsiMethod method : psiClass.getMethods()) {
64+
if (method.getName().equals(methodName) && parametersMatch(method.getParameterList().getParameters())) {
65+
return method;
66+
}
67+
}
68+
}
69+
return null;
70+
}
71+
72+
private boolean parametersMatch(PsiParameter[] parameters) {
73+
if (parameters.length != this.paramTypes.length) {
74+
return false;
75+
}
76+
for (int i = 0; i < parameters.length; i++) {
77+
PsiType type = parameters[i].getType();
78+
if (!type.getCanonicalText().equals(this.paramTypes[i])) {
79+
return false;
80+
}
81+
}
82+
return true;
83+
}
84+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.albertoventurini.graphdbplugin.jetbrains.ui.console.graph;
2+
3+
import com.albertoventurini.graphdbplugin.database.api.data.GraphNode;
4+
import com.intellij.ide.SelectInEditorManager;
5+
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
6+
import com.intellij.openapi.project.Project;
7+
import com.intellij.openapi.vfs.VirtualFile;
8+
import com.intellij.psi.*;
9+
import com.intellij.psi.search.GlobalSearchScope;
10+
import com.intellij.usageView.UsageInfo;
11+
import prefuse.visual.VisualItem;
12+
13+
import java.awt.event.MouseEvent;
14+
15+
/**
16+
* @author daozhe@alibaba-inc.com
17+
* @date 2023/12/2 20:40
18+
*/
19+
public class NodeNavigateAction {
20+
private final Project project;
21+
public NodeNavigateAction(Project project){
22+
this.project = project;
23+
}
24+
public void navigateToMethodDeclaration(GraphNode node, VisualItem item, MouseEvent e){
25+
PsiMethod method = getPsiMethod(node);
26+
navigate(method);
27+
//navigateToMethod(method);
28+
}
29+
30+
public PsiMethod getPsiMethod(GraphNode node){
31+
String methodSignature = (String)node.getPropertyContainer().getProperties().get("method");
32+
JavaMethodSignature signature = new JavaMethodSignature(methodSignature);
33+
PsiMethod method = signature.getMethod(this.project);
34+
return method;
35+
}
36+
37+
private void navigateToMethod(PsiMethod method) {
38+
PsiFile containingFile = method.getContainingFile();
39+
if (containingFile != null) {
40+
VirtualFile virtualFile = containingFile.getVirtualFile();
41+
if (virtualFile != null) {
42+
new OpenFileDescriptor(method.getProject(), virtualFile, method.getTextOffset()).navigate(true);
43+
}
44+
}
45+
}
46+
47+
private void navigate(PsiMethod method){
48+
UsageInfo usage = new UsageInfo(method);
49+
SelectInEditorManager.getInstance(this.project)
50+
.selectInEditor(usage.getVirtualFile(), usage.getSegment().getStartOffset(), usage.getSegment().getEndOffset(), true, false);
51+
}
52+
}

0 commit comments

Comments
 (0)