-
Notifications
You must be signed in to change notification settings - Fork 154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MethodInvocationCounter Metric to Track Method Invocations within Classes #120
base: master
Are you sure you want to change the base?
Changes from 12 commits
61c1fdd
8d04175
4082a28
0b897a9
9fb521e
b791b4a
e615a7c
8ce9a32
937804d
efaf924
8a0d5c6
31293fd
0cdc04e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -195,10 +195,11 @@ Then, just run: | |
java -jar ck-x.x.x-SNAPSHOT-jar-with-dependencies.jar \ | ||
<project dir> \ | ||
<use jars:true|false> \ | ||
<max files per partition:0=automatic selection> \ | ||
<variables and fields metrics?:true|false> \ | ||
<max files per partition, 0=automatic selection> \ | ||
<variables and fields metrics? True|False> \ | ||
<output dir> \ | ||
[ignored directories...] | ||
[ignored directories...] \ | ||
<verbose flag for large metric outputs? true|false> | ||
``` | ||
|
||
`Project dir` refers to the directory where CK can find all the source code to be parsed. | ||
|
@@ -208,10 +209,10 @@ in the directory and use them to better resolve types. `Max files per partition` | |
of the batch to process. Let us decide that for you and start with 0; if problems happen (i.e., | ||
out of memory) you think of tuning it. `Variables and field metrics` indicates to CK whether | ||
you want metrics at variable- and field-levels too. They are highly fine-grained and produce a lot of output; | ||
you should skip it if you only need metrics at class or method level. Finally, `output dir` refer to the | ||
you should skip it if you only need metrics at class or method level. Aditionally, `output dir` refer to the | ||
directory where CK will export the csv file with metrics from the analyzed project. | ||
Optionally, you can specify any number ignored directories, separated by spaces (for example, `build/`). | ||
By default, `.git` and all other hidden folders are ignored. | ||
By default, `.git` and all other hidden folders are ignored. Finally, the `verbose flag` tells CK if it must process metrics tagged as large outputs. If you are not interested in the detailed output of the metrics, you can set it to false. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
The tool will generate three csv files: class, method, and variable levels. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.github.mauricioaniche.ck.metric; | ||
|
||
import com.github.mauricioaniche.ck.CKClassResult; | ||
import com.github.mauricioaniche.ck.util.MethodCounter; | ||
import org.eclipse.jdt.core.dom.*; | ||
|
||
import java.util.*; | ||
|
||
public class MethodInvocationCounter implements CKASTVisitor, ClassLevelMetric { | ||
private Map<String, MethodCounter.MethodInformation> methodInvocations = new HashMap<>(); | ||
private String currentMethod = null; | ||
|
||
@Override | ||
public void visit(MethodDeclaration node) { | ||
// Set the current method context | ||
this.currentMethod = node.getName().getIdentifier(); | ||
methodInvocations.putIfAbsent(currentMethod, new MethodCounter().new MethodInformation(currentMethod)); | ||
} | ||
|
||
@Override | ||
public void visit(MethodInvocation node) { | ||
if (currentMethod != null) { | ||
// Retrieve or create the MethodInformation for the current method | ||
MethodCounter.MethodInformation info = methodInvocations.get(currentMethod); | ||
String methodName = node.getName().getIdentifier(); | ||
Map<String, Integer> counts = info.getMethodInvocations(); | ||
counts.put(methodName, counts.getOrDefault(methodName, 0) + 1); | ||
} | ||
} | ||
|
||
@Override | ||
public void setResult(CKClassResult result) { | ||
// Process all collected method invocation information into the desired format | ||
List<MethodCounter.MethodInformation> infos = new ArrayList<>(methodInvocations.values()); | ||
String formattedResult = MethodCounter.formatResult(infos); | ||
result.setMethodInvocation(formattedResult); | ||
} | ||
|
||
@Override | ||
public boolean isVerbose() { | ||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package com.github.mauricioaniche.ck.util; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
public class MethodCounter { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The string produced by this class can be really long. I'm not sure I'd like to mix the CSV, which is mostly numbers with this super large string. Also, given that this string can be quite long, just concatenating it to the CSV might make the CSV invalid. We'd need to use proper escaping here. An alternative would be to output this to a separate file. And make this feature disabled by default, so we don't start producing large data files to people that have been using CK so far (and do not expect it). |
||
public class MethodInformation { | ||
private String parentName; | ||
private Map<String, Integer> methodInvocations = new HashMap<>(); | ||
|
||
public MethodInformation(String parentName) { | ||
this.parentName = parentName; | ||
} | ||
|
||
public String getParentName() { | ||
return parentName; | ||
} | ||
|
||
public Map<String, Integer> getMethodInvocations() { | ||
return methodInvocations; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "MethodInformation{" + | ||
"parentName='" + parentName + '\'' + | ||
", methodInvocation=" + methodInvocations + | ||
'}'; | ||
} | ||
|
||
public String toFormattedString() { | ||
return parentName + "[ " + formatMethods(sortMethods(methodInvocations)) + " ] "; | ||
} | ||
|
||
private Map<String, Integer> sortMethods(Map<String, Integer> methodInvocation) { | ||
return methodInvocation.entrySet().stream() | ||
.sorted((Map.Entry.<String, Integer>comparingByValue().reversed())) | ||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); | ||
} | ||
|
||
private String formatMethods(Map<String, Integer> methodInvocation) { | ||
return methodInvocation.entrySet().stream() | ||
.map(entry -> entry.getKey() + "()" + ":" + entry.getValue()) | ||
.collect(Collectors.joining(" ")); | ||
} | ||
|
||
} | ||
|
||
public static String formatResult(List<MethodInformation> methodInformations) { | ||
return methodInformations.stream().map(MethodInformation::toFormattedString).collect(Collectors.joining()); | ||
} | ||
public static List<MethodInformation> count(String methodList) { | ||
List<MethodInformation> methodInformationList = new ArrayList<>(); | ||
|
||
String[] methodNames = methodList.split(";"); | ||
for (String name : methodNames) { | ||
String[] parts = name.split("/"); | ||
String methodName = parts[0]; | ||
String parentName = parts[1]; | ||
|
||
MethodInformation methodInformation = new MethodCounter().new MethodInformation(parentName); | ||
if (methodInformationList.contains(methodInformation)) { | ||
methodInformation = methodInformationList.get(methodInformationList.indexOf(methodInformation)); | ||
} else { | ||
methodInformationList.add(methodInformation); | ||
} | ||
|
||
if (methodInformation.getMethodInvocations().containsKey(methodName)) { | ||
methodInformation.getMethodInvocations().put(methodName, methodInformation.getMethodInvocations().get(methodName) + 1); | ||
} else { | ||
methodInformation.getMethodInvocations().put(methodName, 1); | ||
} | ||
} | ||
|
||
return methodInformationList; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a typo here,
Additionally