Skip to content

Commit

Permalink
Adding analyzer for log-levels concept exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
manumafe98 committed Feb 19, 2024
1 parent 5e16028 commit ce6df28
Show file tree
Hide file tree
Showing 43 changed files with 769 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/main/java/analyzer/AnalyzerRoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import analyzer.exercises.hamming.HammingAnalyzer;
import analyzer.exercises.lasagna.LasagnaAnalyzer;
import analyzer.exercises.leap.LeapAnalyzer;
import analyzer.exercises.loglevels.LogLevelsAnalyzer;
import analyzer.exercises.needforspeed.NeedForSpeedAnalyzer;
import analyzer.exercises.twofer.TwoferAnalyzer;

Expand Down Expand Up @@ -51,6 +52,7 @@ private static List<Analyzer> createAnalyzers(String slug) {
case "hamming" -> analyzers.add(new HammingAnalyzer());
case "lasagna" -> analyzers.add(new LasagnaAnalyzer());
case "leap" -> analyzers.add(new LeapAnalyzer());
case "log-levels" -> analyzers.add(new LogLevelsAnalyzer());
case "need-for-speed" -> analyzers.add(new NeedForSpeedAnalyzer());
case "two-fer" -> analyzers.add(new TwoferAnalyzer());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package analyzer.exercises.loglevels;

import analyzer.Comment;

/**
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/log-levels/avoid_using_string_format.md">Markdown Template</a>
*/
class AvoidUsingStringFormat extends Comment {

@Override
public String getKey() {
return "java.log-levels.avoid_using_string_format";
}

@Override
public Type getType() {
return Type.INFORMATIVE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package analyzer.exercises.loglevels;

import analyzer.Comment;

/**
* @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>
*/
class DoNotHardcodeLogLevels extends Comment{

@Override
public String getKey() {
return "java.log-levels.do_not_hardcode_log_levels";
}

@Override
public Type getType() {
return Type.ESSENTIAL;
}
}
81 changes: 81 additions & 0 deletions src/main/java/analyzer/exercises/loglevels/LogLevelsAnalyzer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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.ExemplarSolution;

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 SUBSTRING = "substring";
private static final String FORMAT = "format";

@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 (containsHarcodedString(node)) {
output.addComment(new DoNotHardcodeLogLevels());
}

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(MESSAGE) && doesNotCallMethod(node, SUBSTRING)) {
output.addComment(new UseSubstringMethod(MESSAGE, SUBSTRING));
}

if (node.getNameAsString().equals(LOG_LEVEL) && doesNotCallMethod(node, SUBSTRING)) {
output.addComment(new UseSubstringMethod(LOG_LEVEL, SUBSTRING));
}

if (node.getNameAsString().equals(REFORMAT) && !doesNotCallMethod(node, FORMAT)) {
output.addComment(new AvoidUsingStringFormat());
}

super.visit(node, output);
}

private static boolean containsHarcodedString(MethodDeclaration node) {
List<StringLiteralExpr> hardcodedStrings = node.findAll(StringLiteralExpr.class,
x -> x.getValue().equals("[ERROR]:") || x.getValue().equals("[WARNING]:")
|| x.getValue().equals("[INFO]:"));

return hardcodedStrings.size() > 1;
}

private static boolean doesNotCallMethod(MethodDeclaration node, String otherMethodName) {
return node.findAll(MethodCallExpr.class, x -> x.getNameAsString().contains(otherMethodName)).isEmpty();
}
}
36 changes: 36 additions & 0 deletions src/main/java/analyzer/exercises/loglevels/ReuseCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package analyzer.exercises.loglevels;

import analyzer.Comment;

import java.util.Map;

/**
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/log-levels/reuse_code.md">Markdown Template</a>
*/
class ReuseCode extends Comment {
private final String callingMethod;
private final String methodToCall;

ReuseCode(String callingMethod, String methodToCall) {
this.callingMethod = callingMethod;
this.methodToCall = methodToCall;
}

@Override
public String getKey() {
return "java.log-levels.reuse_code";
}

@Override
public Map<String, String> getParameters() {
return Map.of(
"callingMethod", this.callingMethod,
"methodToCall", this.methodToCall
);
}

@Override
public Type getType() {
return Type.ACTIONABLE;
}
}
37 changes: 37 additions & 0 deletions src/main/java/analyzer/exercises/loglevels/UseSubstringMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package analyzer.exercises.loglevels;

import analyzer.Comment;

import java.util.Map;

/**
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/log-levels/use_substring_method.md">Markdown Template</a>
*/
class UseSubstringMethod extends Comment {
private final String callingMethod;
private final String methodToCall;


UseSubstringMethod(String callingMethod, String methodToCall) {
this.callingMethod = callingMethod;
this.methodToCall = methodToCall;
}

@Override
public String getKey() {
return "java.log-levels.use_substring_method";
}

@Override
public Map<String, String> getParameters() {
return Map.of(
"callingMethod", this.callingMethod,
"methodToCall", this.methodToCall
);
}

@Override
public Type getType() {
return Type.ACTIONABLE;
}
}
23 changes: 21 additions & 2 deletions src/test/java/analyzer/AnalyzerIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public void annalynsinfiltration(String scenario) throws IOException {

Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
}

@ParameterizedTest
@ValueSource(strings = {
"ExemplarSolution",
Expand All @@ -126,7 +126,26 @@ void needforspeed(String scenario) throws IOException {
var path = Path.of("need-for-speed", scenario + ".java");
var solution = new SolutionFromFiles("need-for-speed", SCENARIOS.resolve(path));
var output = AnalyzerRoot.analyze(solution);


Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
}

@ParameterizedTest
@ValueSource(strings = {
"ExemplarSolution",
"HardCodingLogLevels",
"NoReuseLogLevel",
"NoReuseMessage",
"NoReuseOfBothMethods",
"NotUsingSubstringOnLogLevel",
"NotUsingSubstringOnMessage",
"UsingStringFormat"
})
void loglevels(String scenario) throws IOException {
var path = Path.of("log-levels", scenario + ".java");
var solution = new SolutionFromFiles("log-levels", SCENARIOS.resolve(path));
var output = AnalyzerRoot.analyze(solution);

Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"comments": [
{
"comment": "java.general.exemplar",
"params": {
"exerciseName": "Log Levels"
},
"type": "celebratory"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"comments": [
{
"comment": "java.log-levels.do_not_hardcode_log_levels",
"params": {},
"type": "essential"
},
{
"comment": "java.log-levels.use_substring_method",
"params": {
"callingMethod": "message",
"methodToCall": "substring"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"comments": [
{
"comment": "java.log-levels.reuse_code",
"params": {
"callingMethod": "reformat",
"methodToCall": "logLevel"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"comments": [
{
"comment": "java.log-levels.reuse_code",
"params": {
"callingMethod": "reformat",
"methodToCall": "message"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"comments": [
{
"comment": "java.log-levels.reuse_code",
"params": {
"callingMethod": "reformat",
"methodToCall": "message"
},
"type": "actionable"
},
{
"comment": "java.log-levels.reuse_code",
"params": {
"callingMethod": "reformat",
"methodToCall": "logLevel"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"comments": [
{
"comment": "java.log-levels.use_substring_method",
"params": {
"callingMethod": "logLevel",
"methodToCall": "substring"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"comments": [
{
"comment": "java.log-levels.use_substring_method",
"params": {
"callingMethod": "message",
"methodToCall": "substring"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Loading

0 comments on commit ce6df28

Please sign in to comment.