Skip to content

Commit ce6df28

Browse files
committed
Adding analyzer for log-levels concept exercise
1 parent 5e16028 commit ce6df28

File tree

43 files changed

+769
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+769
-2
lines changed

src/main/java/analyzer/AnalyzerRoot.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import analyzer.exercises.hamming.HammingAnalyzer;
77
import analyzer.exercises.lasagna.LasagnaAnalyzer;
88
import analyzer.exercises.leap.LeapAnalyzer;
9+
import analyzer.exercises.loglevels.LogLevelsAnalyzer;
910
import analyzer.exercises.needforspeed.NeedForSpeedAnalyzer;
1011
import analyzer.exercises.twofer.TwoferAnalyzer;
1112

@@ -51,6 +52,7 @@ private static List<Analyzer> createAnalyzers(String slug) {
5152
case "hamming" -> analyzers.add(new HammingAnalyzer());
5253
case "lasagna" -> analyzers.add(new LasagnaAnalyzer());
5354
case "leap" -> analyzers.add(new LeapAnalyzer());
55+
case "log-levels" -> analyzers.add(new LogLevelsAnalyzer());
5456
case "need-for-speed" -> analyzers.add(new NeedForSpeedAnalyzer());
5557
case "two-fer" -> analyzers.add(new TwoferAnalyzer());
5658
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package analyzer.exercises.loglevels;
2+
3+
import analyzer.Comment;
4+
5+
/**
6+
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/log-levels/avoid_using_string_format.md">Markdown Template</a>
7+
*/
8+
class AvoidUsingStringFormat extends Comment {
9+
10+
@Override
11+
public String getKey() {
12+
return "java.log-levels.avoid_using_string_format";
13+
}
14+
15+
@Override
16+
public Type getType() {
17+
return Type.INFORMATIVE;
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package analyzer.exercises.loglevels;
2+
3+
import analyzer.Comment;
4+
5+
/**
6+
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/log-levels/do_not_hardcode_log_levels.md">Markdown Template</a>
7+
*/
8+
class DoNotHardcodeLogLevels extends Comment{
9+
10+
@Override
11+
public String getKey() {
12+
return "java.log-levels.do_not_hardcode_log_levels";
13+
}
14+
15+
@Override
16+
public Type getType() {
17+
return Type.ESSENTIAL;
18+
}
19+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package analyzer.exercises.loglevels;
2+
3+
import com.github.javaparser.ast.CompilationUnit;
4+
import com.github.javaparser.ast.body.MethodDeclaration;
5+
import com.github.javaparser.ast.expr.MethodCallExpr;
6+
import com.github.javaparser.ast.expr.StringLiteralExpr;
7+
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
8+
9+
import analyzer.Analyzer;
10+
import analyzer.OutputCollector;
11+
import analyzer.Solution;
12+
import analyzer.comments.ExemplarSolution;
13+
14+
import java.util.List;
15+
16+
/**
17+
* The {@link LogLevelsAnalyzer} is the analyzer implementation for the {@code log-levels} practice exercise.
18+
* It extends from the {@link VoidVisitorAdapter} and uses the visitor pattern to traverse each compilation unit.
19+
*
20+
* @see <a href="https://github.com/exercism/java/tree/main/exercises/concept/log-levels">The log-levels exercise on the Java track</a>
21+
*/
22+
public class LogLevelsAnalyzer extends VoidVisitorAdapter<OutputCollector> implements Analyzer {
23+
private static final String EXERCISE_NAME = "Log Levels";
24+
private static final String REFORMAT = "reformat";
25+
private static final String MESSAGE = "message";
26+
private static final String LOG_LEVEL = "logLevel";
27+
private static final String SUBSTRING = "substring";
28+
private static final String FORMAT = "format";
29+
30+
@Override
31+
public void analyze(Solution solution, OutputCollector output) {
32+
for (CompilationUnit compilationUnit : solution.getCompilationUnits()) {
33+
compilationUnit.accept(this, output);
34+
}
35+
36+
if (output.getComments().isEmpty()) {
37+
output.addComment(new ExemplarSolution(EXERCISE_NAME));
38+
}
39+
}
40+
41+
@Override
42+
public void visit(MethodDeclaration node, OutputCollector output) {
43+
if (containsHarcodedString(node)) {
44+
output.addComment(new DoNotHardcodeLogLevels());
45+
}
46+
47+
if (node.getNameAsString().equals(REFORMAT) && doesNotCallMethod(node, MESSAGE)) {
48+
output.addComment(new ReuseCode(REFORMAT, MESSAGE));
49+
}
50+
51+
if (node.getNameAsString().equals(REFORMAT) && doesNotCallMethod(node, LOG_LEVEL)) {
52+
output.addComment(new ReuseCode(REFORMAT, LOG_LEVEL));
53+
}
54+
55+
if (node.getNameAsString().equals(MESSAGE) && doesNotCallMethod(node, SUBSTRING)) {
56+
output.addComment(new UseSubstringMethod(MESSAGE, SUBSTRING));
57+
}
58+
59+
if (node.getNameAsString().equals(LOG_LEVEL) && doesNotCallMethod(node, SUBSTRING)) {
60+
output.addComment(new UseSubstringMethod(LOG_LEVEL, SUBSTRING));
61+
}
62+
63+
if (node.getNameAsString().equals(REFORMAT) && !doesNotCallMethod(node, FORMAT)) {
64+
output.addComment(new AvoidUsingStringFormat());
65+
}
66+
67+
super.visit(node, output);
68+
}
69+
70+
private static boolean containsHarcodedString(MethodDeclaration node) {
71+
List<StringLiteralExpr> hardcodedStrings = node.findAll(StringLiteralExpr.class,
72+
x -> x.getValue().equals("[ERROR]:") || x.getValue().equals("[WARNING]:")
73+
|| x.getValue().equals("[INFO]:"));
74+
75+
return hardcodedStrings.size() > 1;
76+
}
77+
78+
private static boolean doesNotCallMethod(MethodDeclaration node, String otherMethodName) {
79+
return node.findAll(MethodCallExpr.class, x -> x.getNameAsString().contains(otherMethodName)).isEmpty();
80+
}
81+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package analyzer.exercises.loglevels;
2+
3+
import analyzer.Comment;
4+
5+
import java.util.Map;
6+
7+
/**
8+
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/log-levels/reuse_code.md">Markdown Template</a>
9+
*/
10+
class ReuseCode extends Comment {
11+
private final String callingMethod;
12+
private final String methodToCall;
13+
14+
ReuseCode(String callingMethod, String methodToCall) {
15+
this.callingMethod = callingMethod;
16+
this.methodToCall = methodToCall;
17+
}
18+
19+
@Override
20+
public String getKey() {
21+
return "java.log-levels.reuse_code";
22+
}
23+
24+
@Override
25+
public Map<String, String> getParameters() {
26+
return Map.of(
27+
"callingMethod", this.callingMethod,
28+
"methodToCall", this.methodToCall
29+
);
30+
}
31+
32+
@Override
33+
public Type getType() {
34+
return Type.ACTIONABLE;
35+
}
36+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package analyzer.exercises.loglevels;
2+
3+
import analyzer.Comment;
4+
5+
import java.util.Map;
6+
7+
/**
8+
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/log-levels/use_substring_method.md">Markdown Template</a>
9+
*/
10+
class UseSubstringMethod extends Comment {
11+
private final String callingMethod;
12+
private final String methodToCall;
13+
14+
15+
UseSubstringMethod(String callingMethod, String methodToCall) {
16+
this.callingMethod = callingMethod;
17+
this.methodToCall = methodToCall;
18+
}
19+
20+
@Override
21+
public String getKey() {
22+
return "java.log-levels.use_substring_method";
23+
}
24+
25+
@Override
26+
public Map<String, String> getParameters() {
27+
return Map.of(
28+
"callingMethod", this.callingMethod,
29+
"methodToCall", this.methodToCall
30+
);
31+
}
32+
33+
@Override
34+
public Type getType() {
35+
return Type.ACTIONABLE;
36+
}
37+
}

src/test/java/analyzer/AnalyzerIntegrationTest.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public void annalynsinfiltration(String scenario) throws IOException {
113113

114114
Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
115115
}
116-
116+
117117
@ParameterizedTest
118118
@ValueSource(strings = {
119119
"ExemplarSolution",
@@ -126,7 +126,26 @@ void needforspeed(String scenario) throws IOException {
126126
var path = Path.of("need-for-speed", scenario + ".java");
127127
var solution = new SolutionFromFiles("need-for-speed", SCENARIOS.resolve(path));
128128
var output = AnalyzerRoot.analyze(solution);
129-
129+
130+
Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
131+
}
132+
133+
@ParameterizedTest
134+
@ValueSource(strings = {
135+
"ExemplarSolution",
136+
"HardCodingLogLevels",
137+
"NoReuseLogLevel",
138+
"NoReuseMessage",
139+
"NoReuseOfBothMethods",
140+
"NotUsingSubstringOnLogLevel",
141+
"NotUsingSubstringOnMessage",
142+
"UsingStringFormat"
143+
})
144+
void loglevels(String scenario) throws IOException {
145+
var path = Path.of("log-levels", scenario + ".java");
146+
var solution = new SolutionFromFiles("log-levels", SCENARIOS.resolve(path));
147+
var output = AnalyzerRoot.analyze(solution);
148+
130149
Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
131150
}
132151
}
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": "Log Levels"
7+
},
8+
"type": "celebratory"
9+
}
10+
]
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.log-levels.do_not_hardcode_log_levels",
5+
"params": {},
6+
"type": "essential"
7+
},
8+
{
9+
"comment": "java.log-levels.use_substring_method",
10+
"params": {
11+
"callingMethod": "message",
12+
"methodToCall": "substring"
13+
},
14+
"type": "actionable"
15+
},
16+
{
17+
"comment": "java.general.feedback_request",
18+
"params": {},
19+
"type": "informative"
20+
}
21+
]
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.log-levels.reuse_code",
5+
"params": {
6+
"callingMethod": "reformat",
7+
"methodToCall": "logLevel"
8+
},
9+
"type": "actionable"
10+
},
11+
{
12+
"comment": "java.general.feedback_request",
13+
"params": {},
14+
"type": "informative"
15+
}
16+
]
17+
}

0 commit comments

Comments
 (0)