-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathLogLevelsAnalyzer.java
96 lines (78 loc) · 3.89 KB
/
LogLevelsAnalyzer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package analyzer.exercises.loglevels;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import analyzer.Analyzer;
import analyzer.OutputCollector;
import analyzer.Solution;
import analyzer.comments.AvoidHardCodedTestCases;
import analyzer.comments.ExemplarSolution;
import analyzer.comments.PreferStringConcatenation;
import analyzer.comments.ReuseCode;
import java.util.List;
/**
* The {@link LogLevelsAnalyzer} is the analyzer implementation for the {@code log-levels} practice exercise.
* It extends from the {@link VoidVisitorAdapter} and uses the visitor pattern to traverse each compilation unit.
*
* @see <a href="https://github.com/exercism/java/tree/main/exercises/concept/log-levels">The log-levels exercise on the Java track</a>
*/
public class LogLevelsAnalyzer extends VoidVisitorAdapter<OutputCollector> implements Analyzer {
private static final String EXERCISE_NAME = "Log Levels";
private static final String REFORMAT = "reformat";
private static final String MESSAGE = "message";
private static final String LOG_LEVEL = "logLevel";
private static final String FORMAT = "format";
private static List<String> EXPECTED_METHODS = List.of("substring", "split");
private static final List<String> METHODS_TO_ANALYZE = List.of(MESSAGE, LOG_LEVEL, REFORMAT);
@Override
public void analyze(Solution solution, OutputCollector output) {
for (CompilationUnit compilationUnit : solution.getCompilationUnits()) {
compilationUnit.accept(this, output);
}
if (output.getComments().isEmpty()) {
output.addComment(new ExemplarSolution(EXERCISE_NAME));
}
}
@Override
public void visit(MethodDeclaration node, OutputCollector output) {
if (!METHODS_TO_ANALYZE.contains(node.getNameAsString())) {
return;
}
if (containsHarcodedString(node)) {
output.addComment(new AvoidHardCodedTestCases());
return;
}
if (!node.getNameAsString().equals(REFORMAT) && doesNotCallMethods(node, EXPECTED_METHODS)) {
output.addComment(new UseSubstringMethod(node.getNameAsString()));
return;
}
if (node.getNameAsString().equals(REFORMAT) && doesNotCallMethod(node, MESSAGE)) {
output.addComment(new ReuseCode(REFORMAT, MESSAGE));
}
if (node.getNameAsString().equals(REFORMAT) && doesNotCallMethod(node, LOG_LEVEL)) {
output.addComment(new ReuseCode(REFORMAT, LOG_LEVEL));
}
if (node.getNameAsString().equals(REFORMAT) && callsMethod(node, FORMAT)) {
output.addComment(new PreferStringConcatenation());
}
super.visit(node, output);
}
private static boolean containsHarcodedString(MethodDeclaration node) {
List<StringLiteralExpr> hardcodedStrings = node.findAll(StringLiteralExpr.class, x -> {
String value = x.getValue().toLowerCase();
return value.contains("error") || value.contains("warning") || value.contains("info");
});
return hardcodedStrings.size() > 1;
}
private static boolean doesNotCallMethod(MethodDeclaration node, String otherMethodName) {
return node.findAll(MethodCallExpr.class, x -> x.getNameAsString().contains(otherMethodName)).isEmpty();
}
private static boolean doesNotCallMethods(MethodDeclaration node, List<String> allowedMethods) {
return allowedMethods.stream().allMatch(method -> doesNotCallMethod(node, method));
}
private static boolean callsMethod(MethodDeclaration node, String otherMethodName) {
return !node.findAll(MethodCallExpr.class, x -> x.getNameAsString().contains(otherMethodName)).isEmpty();
}
}