diff --git a/src/main/java/analyzer/AnalyzerRoot.java b/src/main/java/analyzer/AnalyzerRoot.java index 6e249ec3..2da5c7f9 100644 --- a/src/main/java/analyzer/AnalyzerRoot.java +++ b/src/main/java/analyzer/AnalyzerRoot.java @@ -10,6 +10,7 @@ import analyzer.exercises.needforspeed.NeedForSpeedAnalyzer; import analyzer.exercises.salarycalculator.SalaryCalculatorAnalyzer; import analyzer.exercises.secrets.SecretsAnalyzer; +import analyzer.exercises.timfrommarketing.TimFromMarketingAnalyzer; import analyzer.exercises.twofer.TwoferAnalyzer; import analyzer.exercises.wizardsandwarriors.WizardsAndWarriorsAnalyzer; @@ -59,6 +60,7 @@ private static List createAnalyzers(String slug) { case "need-for-speed" -> analyzers.add(new NeedForSpeedAnalyzer()); case "salary-calculator" -> analyzers.add(new SalaryCalculatorAnalyzer()); case "secrets" -> analyzers.add(new SecretsAnalyzer()); + case "tim-from-marketing" -> analyzers.add(new TimFromMarketingAnalyzer()); case "two-fer" -> analyzers.add(new TwoferAnalyzer()); case "wizards-and-warriors" -> analyzers.add(new WizardsAndWarriorsAnalyzer()); } diff --git a/src/main/java/analyzer/exercises/timfrommarketing/TimFromMarketingAnalyzer.java b/src/main/java/analyzer/exercises/timfrommarketing/TimFromMarketingAnalyzer.java new file mode 100644 index 00000000..5cc6557e --- /dev/null +++ b/src/main/java/analyzer/exercises/timfrommarketing/TimFromMarketingAnalyzer.java @@ -0,0 +1,54 @@ +package analyzer.exercises.timfrommarketing; + +import analyzer.Analyzer; +import analyzer.OutputCollector; +import analyzer.Solution; +import analyzer.comments.ExemplarSolution; +import analyzer.comments.PreferStringConcatenation; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; + +public class TimFromMarketingAnalyzer extends VoidVisitorAdapter implements Analyzer { + + private static final String EXERCISE_NAME = "Tim from Marketing"; + private static final String FORMAT = "format"; + private static final String OPTIONAL = "Optional"; + + @Override + public void analyze(Solution solution, OutputCollector output) { + + for (var 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(useOptionals(node)){ + output.addComment(new UseNullLiteral()); + } + + if(useStringFormat(node)) { + output.addComment(new PreferStringConcatenation()); + } + + super.visit(node, output); + } + + private static boolean useStringFormat(MethodDeclaration node) { + return node.findAll(MethodCallExpr.class).stream() + .anyMatch(m -> m.getNameAsString().contains(FORMAT)); + } + + + private static boolean useOptionals(MethodDeclaration node) { + return node.findAll(MethodCallExpr.class).stream() + .anyMatch(m -> m.toString().contains(OPTIONAL)); + } +} diff --git a/src/main/java/analyzer/exercises/timfrommarketing/UseNullLiteral.java b/src/main/java/analyzer/exercises/timfrommarketing/UseNullLiteral.java new file mode 100644 index 00000000..0ab15d9d --- /dev/null +++ b/src/main/java/analyzer/exercises/timfrommarketing/UseNullLiteral.java @@ -0,0 +1,17 @@ +package analyzer.exercises.timfrommarketing; + +import analyzer.Comment; + + +public class UseNullLiteral extends Comment { + + @Override + public String getKey() { + return "java.tim-from-marketing.use_null_literal"; + } + + @Override + public Type getType() { + return Type.ACTIONABLE; + } +} diff --git a/src/test/java/analyzer/AnalyzerIntegrationTest.java b/src/test/java/analyzer/AnalyzerIntegrationTest.java index 4b8cccde..10b59264 100644 --- a/src/test/java/analyzer/AnalyzerIntegrationTest.java +++ b/src/test/java/analyzer/AnalyzerIntegrationTest.java @@ -83,6 +83,21 @@ void leap(String scenario) throws IOException { Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); } + @ParameterizedTest + @ValueSource(strings = { + "ExemplarSolution", + "UseOptionals", + "UseStringFormat", + "UseOptionalsAndStringFormat", + }) + void timfrommarketing(String scenario) throws IOException { + var path = Path.of("tim-from-marketing", scenario + ".java"); + var solution = new SolutionFromFiles("tim-from-marketing", SCENARIOS.resolve(path)); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); + } + @ParameterizedTest @ValueSource(strings = { "HardCodedTestCases", diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.ExemplarSolution.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.ExemplarSolution.approved.txt new file mode 100644 index 00000000..4984daff --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.ExemplarSolution.approved.txt @@ -0,0 +1,11 @@ +{ + "comments": [ + { + "comment": "java.general.exemplar", + "params": { + "exerciseName": "Tim from Marketing" + }, + "type": "celebratory" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseOptionals.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseOptionals.approved.txt new file mode 100644 index 00000000..04e490ef --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseOptionals.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.tim-from-marketing.use_null_literal", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseOptionalsAndStringFormat.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseOptionalsAndStringFormat.approved.txt new file mode 100644 index 00000000..46f0862c --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseOptionalsAndStringFormat.approved.txt @@ -0,0 +1,19 @@ +{ + "comments": [ + { + "comment": "java.tim-from-marketing.use_null_literal", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.prefer_string_concatenation", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseStringFormat.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseStringFormat.approved.txt new file mode 100644 index 00000000..b01ad7f7 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.timfrommarketing.UseStringFormat.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.general.prefer_string_concatenation", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/scenarios/tim-from-marketing/ExemplarSolution.java b/src/test/resources/scenarios/tim-from-marketing/ExemplarSolution.java new file mode 100644 index 00000000..7299a697 --- /dev/null +++ b/src/test/resources/scenarios/tim-from-marketing/ExemplarSolution.java @@ -0,0 +1,20 @@ +public class Badge { + + public String print(Integer id, String name, String department) { + + String worksAt; + + if (department == null) { + worksAt = "OWNER"; + } else { + worksAt = department.toUpperCase(); + } + + if (id == null) { + return name + " - " + worksAt; + } + + return "[" + id + "] - " + name + " - " + worksAt; + } + +} diff --git a/src/test/resources/scenarios/tim-from-marketing/UseOptionals.java b/src/test/resources/scenarios/tim-from-marketing/UseOptionals.java new file mode 100644 index 00000000..41c4ede6 --- /dev/null +++ b/src/test/resources/scenarios/tim-from-marketing/UseOptionals.java @@ -0,0 +1,11 @@ +public class Badge { + public String print(Integer id, String name, String department) { + Optional optionalId = Optional.ofNullable(id); + Optional optionalDepartment = Optional.ofNullable(department); + + String idPart = optionalId.map(i -> "[" + i + "] - ").orElse(""); + String departmentPart = optionalDepartment.orElse("OWNER").toUpperCase(); + + return idPart + name + " - " + departmentPart; + } +} diff --git a/src/test/resources/scenarios/tim-from-marketing/UseOptionalsAndStringFormat.java b/src/test/resources/scenarios/tim-from-marketing/UseOptionalsAndStringFormat.java new file mode 100644 index 00000000..ff0e6eac --- /dev/null +++ b/src/test/resources/scenarios/tim-from-marketing/UseOptionalsAndStringFormat.java @@ -0,0 +1,11 @@ +public class Badge { + public String print(Integer id, String name, String department) { + Optional optionalId = Optional.ofNullable(id); + Optional optionalDepartment = Optional.ofNullable(department); + + String idPart = optionalId.map(i -> String.format("[%d] - ", i)).orElse(""); + String departmentPart = optionalDepartment.orElse("OWNER").toUpperCase(); + + return String.format("%s%s - %s", idPart, name, departmentPart); + } +} diff --git a/src/test/resources/scenarios/tim-from-marketing/UseStringFormat.java b/src/test/resources/scenarios/tim-from-marketing/UseStringFormat.java new file mode 100644 index 00000000..7467a33d --- /dev/null +++ b/src/test/resources/scenarios/tim-from-marketing/UseStringFormat.java @@ -0,0 +1,15 @@ +public class Badge { + public String print(Integer id, String name, String department) { + if (department == null) { + department = "OWNER"; + } else { + department = department.toUpperCase(); + } + + if (id == null) { + return String.format("%s - %s", name, department); + } else { + return String.format("[%d] - %s - %s", id, name, department); + } + } +} diff --git a/tests/tim-from-marketing/exemplar-solution/.meta/config.json b/tests/tim-from-marketing/exemplar-solution/.meta/config.json new file mode 100644 index 00000000..c1b8f026 --- /dev/null +++ b/tests/tim-from-marketing/exemplar-solution/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "smcg468" + ], + "files": { + "solution": [ + "src/main/java/Badge.java" + ], + "test": [ + "src/test/java/BadgeTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Badge.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/tim-from-marketing" + ], + "blurb": "Learn about the null literal and nullable variables in java by printing name badges." +} diff --git a/tests/tim-from-marketing/exemplar-solution/expected_analysis.json b/tests/tim-from-marketing/exemplar-solution/expected_analysis.json new file mode 100644 index 00000000..e948093e --- /dev/null +++ b/tests/tim-from-marketing/exemplar-solution/expected_analysis.json @@ -0,0 +1,11 @@ +{ + "comments": [ + { + "comment": "java.general.exemplar", + "params": { + "exerciseName": "Tim from Marketing" + }, + "type": "celebratory" + } + ] +} \ No newline at end of file diff --git a/tests/tim-from-marketing/exemplar-solution/expected_tags.json b/tests/tim-from-marketing/exemplar-solution/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/tim-from-marketing/exemplar-solution/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/tim-from-marketing/exemplar-solution/src/main/java/Badge.java b/tests/tim-from-marketing/exemplar-solution/src/main/java/Badge.java new file mode 100644 index 00000000..7299a697 --- /dev/null +++ b/tests/tim-from-marketing/exemplar-solution/src/main/java/Badge.java @@ -0,0 +1,20 @@ +public class Badge { + + public String print(Integer id, String name, String department) { + + String worksAt; + + if (department == null) { + worksAt = "OWNER"; + } else { + worksAt = department.toUpperCase(); + } + + if (id == null) { + return name + " - " + worksAt; + } + + return "[" + id + "] - " + name + " - " + worksAt; + } + +} diff --git a/tests/tim-from-marketing/use-optionals/.meta/config.json b/tests/tim-from-marketing/use-optionals/.meta/config.json new file mode 100644 index 00000000..c1b8f026 --- /dev/null +++ b/tests/tim-from-marketing/use-optionals/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "smcg468" + ], + "files": { + "solution": [ + "src/main/java/Badge.java" + ], + "test": [ + "src/test/java/BadgeTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Badge.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/tim-from-marketing" + ], + "blurb": "Learn about the null literal and nullable variables in java by printing name badges." +} diff --git a/tests/tim-from-marketing/use-optionals/expected_analysis.json b/tests/tim-from-marketing/use-optionals/expected_analysis.json new file mode 100644 index 00000000..f7f2934b --- /dev/null +++ b/tests/tim-from-marketing/use-optionals/expected_analysis.json @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.tim-from-marketing.use_null_literal", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/tim-from-marketing/use-optionals/expected_tags.json b/tests/tim-from-marketing/use-optionals/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/tim-from-marketing/use-optionals/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/tim-from-marketing/use-optionals/src/main/java/Badge.java b/tests/tim-from-marketing/use-optionals/src/main/java/Badge.java new file mode 100644 index 00000000..41c4ede6 --- /dev/null +++ b/tests/tim-from-marketing/use-optionals/src/main/java/Badge.java @@ -0,0 +1,11 @@ +public class Badge { + public String print(Integer id, String name, String department) { + Optional optionalId = Optional.ofNullable(id); + Optional optionalDepartment = Optional.ofNullable(department); + + String idPart = optionalId.map(i -> "[" + i + "] - ").orElse(""); + String departmentPart = optionalDepartment.orElse("OWNER").toUpperCase(); + + return idPart + name + " - " + departmentPart; + } +} diff --git a/tests/tim-from-marketing/use-string-format/.meta/config.json b/tests/tim-from-marketing/use-string-format/.meta/config.json new file mode 100644 index 00000000..c1b8f026 --- /dev/null +++ b/tests/tim-from-marketing/use-string-format/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "smcg468" + ], + "files": { + "solution": [ + "src/main/java/Badge.java" + ], + "test": [ + "src/test/java/BadgeTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Badge.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/tim-from-marketing" + ], + "blurb": "Learn about the null literal and nullable variables in java by printing name badges." +} diff --git a/tests/tim-from-marketing/use-string-format/expected_analysis.json b/tests/tim-from-marketing/use-string-format/expected_analysis.json new file mode 100644 index 00000000..2b150682 --- /dev/null +++ b/tests/tim-from-marketing/use-string-format/expected_analysis.json @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.general.prefer_string_concatenation", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/tim-from-marketing/use-string-format/expected_tags.json b/tests/tim-from-marketing/use-string-format/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/tim-from-marketing/use-string-format/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/tim-from-marketing/use-string-format/src/main/java/Badge.java b/tests/tim-from-marketing/use-string-format/src/main/java/Badge.java new file mode 100644 index 00000000..7467a33d --- /dev/null +++ b/tests/tim-from-marketing/use-string-format/src/main/java/Badge.java @@ -0,0 +1,15 @@ +public class Badge { + public String print(Integer id, String name, String department) { + if (department == null) { + department = "OWNER"; + } else { + department = department.toUpperCase(); + } + + if (id == null) { + return String.format("%s - %s", name, department); + } else { + return String.format("[%d] - %s - %s", id, name, department); + } + } +}