• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

exercism / java-analyzer / 23730374670

30 Mar 2026 06:03AM UTC coverage: 92.837% (+0.01%) from 92.823%
23730374670

Pull #321

github

web-flow
Merge 122bb0c4f into 22749eaee
Pull Request #321: Make Hamming check for method refs

332 of 375 branches covered (88.53%)

Branch coverage included in aggregate %.

640 of 672 relevant lines covered (95.24%)

4.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.44
src/main/java/analyzer/exercises/hamming/HammingWalker.java
1
package analyzer.exercises.hamming;
2

3
import com.github.javaparser.Range;
4
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
5
import com.github.javaparser.ast.body.ConstructorDeclaration;
6
import com.github.javaparser.ast.body.MethodDeclaration;
7
import com.github.javaparser.ast.expr.CharLiteralExpr;
8
import com.github.javaparser.ast.expr.LambdaExpr;
9
import com.github.javaparser.ast.expr.MethodCallExpr;
10
import com.github.javaparser.ast.expr.MethodReferenceExpr;
11
import com.github.javaparser.ast.nodeTypes.NodeWithRange;
12
import com.github.javaparser.ast.stmt.*;
13

14
import java.util.*;
15
import java.util.function.Consumer;
16
import java.util.stream.Collectors;
17
import java.util.stream.Stream;
18

19
class HammingWalker implements Consumer<ClassOrInterfaceDeclaration> {
2✔
20
    private ClassOrInterfaceDeclaration hammingClass;
21
    private final Set<ConstructorDeclaration> constructors = new HashSet<>();
5✔
22
    private ConstructorDeclaration constructor;
23
    private final Map<String, List<MethodDeclaration>> methods = new HashMap<>();
5✔
24
    private final Set<String> methodsCalledByConstructor = new HashSet<>();
5✔
25
    private boolean constructorMayCalculateDistanceDirectly;
26
    private final Set<String> methodsCalledByGetHammingDistance = new HashSet<>();
6✔
27
    private boolean getHammingDistanceMayCalculateDistanceDirectly;
28

29
    @Override
30
    public void accept(ClassOrInterfaceDeclaration node) {
31
        if (node.getNameAsString().equals("Hamming")) {
5!
32
            hammingClass = node;
3✔
33
            walkHammingClass();
2✔
34
        }
35
    }
1✔
36

37
    private void walkHammingClass() {
38
        methods.putAll(getMethodsByName());
5✔
39
        constructors.addAll(hammingClass.getConstructors());
7✔
40

41
        findConstructor().ifPresent(this::walkConstructor);
5✔
42

43
        findGetHammingDistanceMethod().ifPresent(this::walkGetHammingDistanceMethod);
5✔
44

45
    }
1✔
46

47
    private Map<String, List<MethodDeclaration>> getMethodsByName() {
48
        return hammingClass.findAll(MethodDeclaration.class)
5✔
49
                .stream()
2✔
50
                .collect(Collectors.groupingBy(MethodDeclaration::getNameAsString));
3✔
51
    }
52

53
    private Optional<ConstructorDeclaration> findConstructor() {
54
        return hammingClass.getConstructorByParameterTypes(String.class, String.class);
14✔
55
    }
56

57
    private void walkConstructor(ConstructorDeclaration foundConstructor) {
58
        constructor = foundConstructor;
3✔
59

60
        constructor.getBody().getStatements().forEach(this::walkConstructorStatement);
7✔
61
    }
1✔
62

63
    private void walkConstructorStatement(Statement statement) {
64

65
        if (statementMayCalculateHammingDistance(statement)) {
4✔
66
            constructorMayCalculateDistanceDirectly = true;
3✔
67
        }
68

69
        getMethodCallNames(statement)
5✔
70
                .forEach(methodName ->
1✔
71
                        recursivelyAddMethodsCalled(methodName, methodsCalledByConstructor));
6✔
72
    }
1✔
73

74
    private boolean isMethodCall(Statement statement) {
75
        return !statement.findAll(MethodCallExpr.class).isEmpty();
9✔
76
    }
77

78
    private Stream<String> getMethodCallNames(Statement statement) {
79
        Stream<String> methodCalled = statement.findAll(MethodCallExpr.class).stream()
5✔
80
                .map(MethodCallExpr::getNameAsString);
2✔
81
        Stream<String> methodRefs = statement.findAll(MethodReferenceExpr.class).stream()
5✔
82
                .map(MethodReferenceExpr::getIdentifier);
2✔
83

84
        return Stream.concat(methodCalled, methodRefs).distinct();
5✔
85
    }
86

87
    private Optional<MethodDeclaration> findGetHammingDistanceMethod() {
88
        return methods.get("getHammingDistance").stream()
8✔
89
                // we only care about the one with no parameters
90
                .filter(method -> method.getParameters().isEmpty())
5✔
91
                .findFirst();
1✔
92
    }
93

94
    private void walkGetHammingDistanceMethod(MethodDeclaration getHammingDistanceMethod) {
95
        getHammingDistanceMethod.getBody().ifPresent(this::walkGetHammingDistanceMethod);
5✔
96
    }
1✔
97

98
    private void walkGetHammingDistanceMethod(BlockStmt body) {
99
        body.getStatements().forEach(this::walkGetHammingDistanceStatement);
5✔
100
    }
1✔
101

102
    private void walkGetHammingDistanceStatement(Statement statement) {
103
        if (statementMayCalculateHammingDistance(statement)) {
4✔
104
            getHammingDistanceMayCalculateDistanceDirectly = true;
3✔
105
        }
106

107
        getMethodCallNames(statement)
5✔
108
                .forEach(methodName ->
1✔
109
                        recursivelyAddMethodsCalled(methodName, methodsCalledByGetHammingDistance));
6✔
110
    }
1✔
111

112
    private void recursivelyAddMethodsCalled(
113
            String methodName,
114
            Set<String> methodsCalled) {
115
        if (methodsCalled.contains(methodName)) {
4✔
116
            return;
1✔
117
        }
118
        methodsCalled.add(methodName);
4✔
119

120
        getMethodsCalledBy(methodName)
3✔
121
                .distinct()
4✔
122
                .forEach(calledMethod ->
1✔
123
                        recursivelyAddMethodsCalled(calledMethod, methodsCalled));
5✔
124
    }
1✔
125

126
    private Stream<String> getMethodsCalledBy(String methodName) {
127
        return methods.getOrDefault(methodName, List.of()).stream()
10✔
128
                .flatMap(this::getMethodsCalledBy);
1✔
129
    }
130

131
    private Stream<String> getMethodsCalledBy(MethodDeclaration method) {
132
        return method.getBody()
5✔
133
                .map(this::getMethodsCalledBy)
3✔
134
                .orElse(Stream.of());
3✔
135
    }
136

137
    private Stream<String> getMethodsCalledBy(BlockStmt body) {
138
        return body.getStatements().stream()
6✔
139
                .filter(this::isMethodCall)
3✔
140
                .flatMap(this::getMethodCallNames);
1✔
141
    }
142

143
    public boolean constructorHasMethodCalls() {
144
        return !methodsCalledByConstructor.isEmpty();
×
145
    }
146

147
    public boolean constructorMayCalculateDistance() {
148
        return constructorMayCalculateDistanceDirectly
5✔
149
                || constructorCallsMethodThatMayCalculateDistance();
5✔
150
    }
151

152
    private boolean constructorCallsMethodThatMayCalculateDistance() {
153
        return methodsCalledByConstructor.stream()
6✔
154
                .anyMatch(this::methodMayCalculateHammingDistance);
1✔
155
    }
156

157
    public boolean getHammingDistanceMethodMayCalculateDistance() {
158
        return getHammingDistanceMayCalculateDistanceDirectly
×
159
                || getHammingDistanceCallsMethodThatMayCalculateDistance();
×
160
    }
161

162
    private boolean getHammingDistanceCallsMethodThatMayCalculateDistance() {
163
        return methodsCalledByGetHammingDistance.stream()
×
164
                .anyMatch(this::methodMayCalculateHammingDistance);
×
165
    }
166

167
    private boolean methodMayCalculateHammingDistance(String methodName) {
168
        return methods.getOrDefault(methodName, List.of()).stream()
10✔
169
                .anyMatch(this::methodMayCalculateHammingDistance);
1✔
170
    }
171

172
    private boolean methodMayCalculateHammingDistance(MethodDeclaration method) {
173
        return method.getBody()
5✔
174
                .map(this::methodBodyMayCalculateHammingDistance)
2✔
175
                .orElse(false);
4✔
176
    }
177

178
    private boolean methodBodyMayCalculateHammingDistance(BlockStmt body) {
179
        return body.getStatements().stream()
6✔
180
                .anyMatch(this::statementMayCalculateHammingDistance);
1✔
181
    }
182

183
    private boolean statementMayCalculateHammingDistance(Statement statement) {
184
        return hasLoopStatement(statement) || hasLambdaExpression(statement);
12✔
185
    }
186

187
    private boolean hasLoopStatement(Statement statement) {
188
        return !statement.findAll(ForStmt.class).isEmpty()
8✔
189
                || !statement.findAll(ForEachStmt.class).isEmpty()
5!
190
                || !statement.findAll(WhileStmt.class).isEmpty();
6!
191
    }
192

193
    private boolean hasLambdaExpression(Statement statement) {
194
        return !statement.findAll(LambdaExpr.class).isEmpty();
9✔
195
    }
196

197
    public Set<String> getLongConstructors() {
198
        return constructors.stream()
6✔
199
                .filter(this::isLongNode)
2✔
200
                .map(ConstructorDeclaration::getNameAsString)
1✔
201
                .collect(Collectors.toUnmodifiableSet());
3✔
202
    }
203

204
    public Set<String> getLongMethods() {
205
        return methods.values().stream()
6✔
206
                .flatMap(Collection::stream)
3✔
207
                .filter(this::isLongNode)
2✔
208
                .map(MethodDeclaration::getNameAsString)
1✔
209
                .collect(Collectors.toUnmodifiableSet());
3✔
210
    }
211

212
    private boolean isLongNode(NodeWithRange<?> node) {
213
        return node.getRange().map(Range::getLineCount).orElse(0) > 20;
15✔
214
    }
215

216
    public boolean usesCharacterLiterals() {
217
        return !hammingClass.findAll(CharLiteralExpr.class).isEmpty();
10✔
218
    }
219

220
    public boolean usesStringCharAtOrCodePointAt() {
221
        return usesMethod("charAt") || usesMethod("codePointAt");
12!
222
    }
223

224
    public boolean shouldUseStreamFilterAndCount() {
225
        return usesMethod("reduce");
4✔
226
    }
227

228
    private boolean usesMethod(String methodName) {
229
        return methodsCalledByConstructor.contains(methodName)
9✔
230
                || methodsCalledByGetHammingDistance.contains(methodName);
5✔
231
    }
232
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc