Skip to content

Commit f3615aa

Browse files
Implement analyzer for wizards-and-warriors-2 (#262)
1 parent 1fc0930 commit f3615aa

27 files changed

+592
-2
lines changed

src/main/java/analyzer/AnalyzerRoot.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import analyzer.exercises.timfrommarketing.TimFromMarketingAnalyzer;
1414
import analyzer.exercises.twofer.TwoferAnalyzer;
1515
import analyzer.exercises.wizardsandwarriors.WizardsAndWarriorsAnalyzer;
16+
import analyzer.exercises.wizardsandwarriors2.WizardsAndWarriors2Analyzer;
1617

1718
import java.util.ArrayList;
1819
import java.util.List;
@@ -34,7 +35,6 @@ private AnalyzerRoot() {
3435
*/
3536
public static Output analyze(Solution solution) {
3637
var outputBuilder = new OutputBuilder();
37-
3838
for (Analyzer analyzer : createAnalyzers(solution.getSlug())) {
3939
analyzer.analyze(solution, outputBuilder);
4040
}
@@ -50,7 +50,6 @@ private static List<Analyzer> createAnalyzers(String slug) {
5050
var analyzers = new ArrayList<Analyzer>();
5151

5252
analyzers.add(new GlobalAnalyzer());
53-
5453
switch (slug) {
5554
case "annalyns-infiltration" -> analyzers.add(new AnnalynsInfiltrationAnalyzer());
5655
case "hamming" -> analyzers.add(new HammingAnalyzer());
@@ -63,6 +62,7 @@ private static List<Analyzer> createAnalyzers(String slug) {
6362
case "tim-from-marketing" -> analyzers.add(new TimFromMarketingAnalyzer());
6463
case "two-fer" -> analyzers.add(new TwoferAnalyzer());
6564
case "wizards-and-warriors" -> analyzers.add(new WizardsAndWarriorsAnalyzer());
65+
case "wizards-and-warriors-2" -> analyzers.add(new WizardsAndWarriors2Analyzer());
6666
}
6767

6868
return List.copyOf(analyzers);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package analyzer.exercises.wizardsandwarriors2;
2+
3+
import analyzer.Comment;
4+
5+
public class ReuseCodeFromDescribeThreeParams extends Comment {
6+
7+
@Override
8+
public String getKey() {
9+
return "java.wizards-and-warriors-2.reuse_code_from_describe_three_params";
10+
}
11+
12+
@Override
13+
public Type getType() {
14+
return Type.ACTIONABLE;
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package analyzer.exercises.wizardsandwarriors2;
2+
3+
import analyzer.Comment;
4+
5+
public class ReuseCodeFromDescribeTwoParams extends Comment {
6+
7+
@Override
8+
public String getKey() {
9+
return "java.wizards-and-warriors-2.reuse_code_from_describe_two_params";
10+
}
11+
12+
@Override
13+
public Type getType() {
14+
return Type.ACTIONABLE;
15+
}
16+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package analyzer.exercises.wizardsandwarriors2;
2+
3+
import analyzer.Analyzer;
4+
import analyzer.OutputCollector;
5+
import analyzer.Solution;
6+
import analyzer.comments.ExemplarSolution;
7+
import analyzer.comments.PreferStringConcatenation;
8+
import com.github.javaparser.ast.body.MethodDeclaration;
9+
import com.github.javaparser.ast.body.Parameter;
10+
import com.github.javaparser.ast.expr.MethodCallExpr;
11+
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
12+
13+
import java.util.List;
14+
15+
/**
16+
* The {@link WizardsAndWarriors2Analyzer} is the analyzer implementation for the {@code wizards-and-warriors-2} concept exercise.
17+
*
18+
* @see <a href="https://github.com/exercism/java/tree/main/exercises/concept/wizards-and-warriors-2">The wizards-and-warriors exercise on the Java track</a>
19+
*/
20+
public class WizardsAndWarriors2Analyzer extends VoidVisitorAdapter<OutputCollector> implements Analyzer {
21+
22+
private static final String EXERCISE_NAME = "Wizards and Warriors 2";
23+
private static final String GAME_MASTER = "GameMaster";
24+
private static final String DESCRIBE = "describe";
25+
private static final String FORMAT = "format";
26+
private static final String DESTINATION_TYPE = "Destination";
27+
private static final String TRAVEL_METHOD_TYPE = "TravelMethod";
28+
private static final String CHARACTER_TYPE = "Character";
29+
30+
@Override
31+
public void analyze(Solution solution, OutputCollector output) {
32+
33+
for (var compilationUnit : solution.getCompilationUnits()) {
34+
compilationUnit.getClassByName(GAME_MASTER).ifPresent(c -> c.accept(this, output));
35+
}
36+
37+
if (output.getComments().isEmpty()) {
38+
output.addComment(new ExemplarSolution(EXERCISE_NAME));
39+
}
40+
}
41+
42+
@Override
43+
public void visit(MethodDeclaration node, OutputCollector output) {
44+
45+
if(!node.getNameAsString().equals(DESCRIBE)) {
46+
return;
47+
}
48+
49+
if(node.getParameters().size() == 2 && !reuseMethod(node, 2)) {
50+
output.addComment(new ReuseCodeFromDescribeTwoParams());
51+
}
52+
53+
if(node.getParameters().size() == 3 && !reuseMethod(node, 3)) {
54+
output.addComment(new ReuseCodeFromDescribeThreeParams());
55+
}
56+
57+
if(useStringFormat(node)) {
58+
output.addComment(new PreferStringConcatenation());
59+
}
60+
61+
super.visit(node, output);
62+
}
63+
64+
private static boolean reuseMethod(MethodDeclaration node, int paramCount) {
65+
66+
List<String> paramsType = node.getParameters().stream().map(Parameter::getTypeAsString).toList();
67+
List<MethodCallExpr> describeCalls = node.findAll(MethodCallExpr.class).stream()
68+
.filter(m -> m.getNameAsString().equals(DESCRIBE))
69+
.toList();
70+
71+
if (paramCount == 2 && paramsType.contains(DESTINATION_TYPE) && paramsType.contains(CHARACTER_TYPE)) {
72+
return describeCalls.size() == 1 || describeCalls.size() == 3;
73+
}
74+
75+
if (paramCount == 3 && paramsType.contains(DESTINATION_TYPE) && paramsType.contains(TRAVEL_METHOD_TYPE) && paramsType.contains(CHARACTER_TYPE)) {
76+
return describeCalls.size() == 3;
77+
}
78+
79+
return false;
80+
}
81+
82+
private static boolean useStringFormat(MethodDeclaration node) {
83+
return node.findAll(MethodCallExpr.class).stream()
84+
.anyMatch(m -> m.getNameAsString().contains(FORMAT));
85+
}
86+
87+
}

src/test/java/analyzer/AnalyzerIntegrationTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,20 @@ void salarycalculator(String scenario) throws IOException {
221221

222222
Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
223223
}
224+
225+
@ParameterizedTest
226+
@ValueSource(strings = {
227+
"ExemplarSolution",
228+
"NotReuseMethod",
229+
"PartialReuseOfMethod",
230+
"UseStringFormat",
231+
"UseStringFormatAndNotReuseMethod",
232+
})
233+
void wizardsandwarriors2(String scenario) throws IOException {
234+
var path = Path.of("wizards-and-warriors-2", scenario + ".java");
235+
var solution = new SolutionFromFiles("wizards-and-warriors-2", SCENARIOS.resolve(path));
236+
var output = AnalyzerRoot.analyze(solution);
237+
238+
Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
239+
}
224240
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.general.exemplar",
5+
"params": {
6+
"exerciseName": "Wizards and Warriors 2"
7+
},
8+
"type": "celebratory"
9+
}
10+
]
11+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.wizards-and-warriors-2.reuse_code_from_describe_three_params",
5+
"params": {},
6+
"type": "actionable"
7+
},
8+
{
9+
"comment": "java.wizards-and-warriors-2.reuse_code_from_describe_two_params",
10+
"params": {},
11+
"type": "actionable"
12+
},
13+
{
14+
"comment": "java.general.feedback_request",
15+
"params": {},
16+
"type": "informative"
17+
}
18+
]
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.wizards-and-warriors-2.reuse_code_from_describe_two_params",
5+
"params": {},
6+
"type": "actionable"
7+
},
8+
{
9+
"comment": "java.general.feedback_request",
10+
"params": {},
11+
"type": "informative"
12+
}
13+
]
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.general.prefer_string_concatenation",
5+
"params": {},
6+
"type": "informative"
7+
},
8+
{
9+
"comment": "java.general.feedback_request",
10+
"params": {},
11+
"type": "informative"
12+
}
13+
]
14+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.wizards-and-warriors-2.reuse_code_from_describe_three_params",
5+
"params": {},
6+
"type": "actionable"
7+
},
8+
{
9+
"comment": "java.wizards-and-warriors-2.reuse_code_from_describe_two_params",
10+
"params": {},
11+
"type": "actionable"
12+
},
13+
{
14+
"comment": "java.general.prefer_string_concatenation",
15+
"params": {},
16+
"type": "informative"
17+
},
18+
{
19+
"comment": "java.general.feedback_request",
20+
"params": {},
21+
"type": "informative"
22+
}
23+
]
24+
}

0 commit comments

Comments
 (0)