forked from exercism/java-analyzer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLeapAnalyzer.java
104 lines (86 loc) · 3.58 KB
/
LeapAnalyzer.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
97
98
99
100
101
102
103
104
package analyzer.exercises.leap;
import analyzer.OutputCollector;
import analyzer.Analyzer;
import analyzer.Solution;
import analyzer.comments.AvoidHardCodedTestCases;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.BinaryExpr;
import com.github.javaparser.ast.expr.ConditionalExpr;
import com.github.javaparser.ast.expr.IntegerLiteralExpr;
import com.github.javaparser.ast.stmt.IfStmt;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import java.util.HashSet;
import java.util.Set;
/**
* The {@link LeapAnalyzer} is the analyzer implementation for the {@code leap} 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/practice/leap">The leap exercise on the Java track</a>
*/
public class LeapAnalyzer extends VoidVisitorAdapter<OutputCollector> implements Analyzer {
private static final Set<Integer> TEST_CASES = Set.of(1960, 1996, 2000, 2400);
private static final Set<String> DISALLOWED_IMPORTS = Set.of(
"java.time",
"java.util.GregorianCalendar"
);
private final Set<Integer> intLiterals = new HashSet<>();
@Override
public void analyze(Solution solution, OutputCollector output) {
for (CompilationUnit compilationUnit : solution.getCompilationUnits()) {
compilationUnit.accept(this, output);
}
}
@Override
public void visit(CompilationUnit node, OutputCollector output) {
// Reset state for each compilation unit
this.intLiterals.clear();
super.visit(node, output);
}
@Override
public void visit(ImportDeclaration node, OutputCollector output) {
if (isUsingBuiltInMethods(node)) {
output.addComment(new NoBuiltInMethods());
}
super.visit(node, output);
}
@Override
public void visit(IntegerLiteralExpr node, OutputCollector output) {
if (node.asNumber() instanceof Integer i) {
this.intLiterals.add(i);
}
if (this.intLiterals.containsAll(TEST_CASES)) {
output.addComment(new AvoidHardCodedTestCases());
}
super.visit(node, output);
}
@Override
public void visit(IfStmt node, OutputCollector output) {
output.addComment(new AvoidIfStatements());
super.visit(node, output);
}
@Override
public void visit(ConditionalExpr node, OutputCollector output) {
if (node.getThenExpr().isBooleanLiteralExpr() || node.getElseExpr().isBooleanLiteralExpr()) {
output.addComment(new AvoidRedundantTernary());
}
super.visit(node, output);
}
@Override
public void visit(MethodDeclaration node, OutputCollector output) {
if (node.getNameAsString().equals("isLeapYear") && hasMoreThanThreeChecks(node)) {
output.addComment(new UseMinimumNumberOfChecks());
}
super.visit(node, output);
}
private static boolean isUsingBuiltInMethods(ImportDeclaration node) {
var name = node.getNameAsString();
return DISALLOWED_IMPORTS.stream().anyMatch(name::contains);
}
private static boolean hasMoreThanThreeChecks(MethodDeclaration node) {
var booleanOperators = node.findAll(BinaryExpr.class,
x -> x.getOperator() == BinaryExpr.Operator.AND || x.getOperator() == BinaryExpr.Operator.OR);
return booleanOperators.size() > 2;
}
}