Skip to content

Commit 50d729d

Browse files
authored
Implement analyzer for salary-calculator after revert (#157)
* Revert "Revert "Implement analyzer for salary-calculator (#155)" (#156)" This reverts commit fee570b. * Applying suggestion to make the code more readable on secrets and salary-calculator analyzers
1 parent dd8637b commit 50d729d

File tree

37 files changed

+751
-6
lines changed

37 files changed

+751
-6
lines changed

src/main/java/analyzer/AnalyzerRoot.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import analyzer.exercises.leap.LeapAnalyzer;
99
import analyzer.exercises.loglevels.LogLevelsAnalyzer;
1010
import analyzer.exercises.needforspeed.NeedForSpeedAnalyzer;
11+
import analyzer.exercises.salarycalculator.SalaryCalculatorAnalyzer;
1112
import analyzer.exercises.secrets.SecretsAnalyzer;
1213
import analyzer.exercises.twofer.TwoferAnalyzer;
1314
import analyzer.exercises.wizardsandwarriors.WizardsAndWarriorsAnalyzer;
@@ -56,6 +57,7 @@ private static List<Analyzer> createAnalyzers(String slug) {
5657
case "leap" -> analyzers.add(new LeapAnalyzer());
5758
case "log-levels" -> analyzers.add(new LogLevelsAnalyzer());
5859
case "need-for-speed" -> analyzers.add(new NeedForSpeedAnalyzer());
60+
case "salary-calculator" -> analyzers.add(new SalaryCalculatorAnalyzer());
5961
case "secrets" -> analyzers.add(new SecretsAnalyzer());
6062
case "two-fer" -> analyzers.add(new TwoferAnalyzer());
6163
case "wizards-and-warriors" -> analyzers.add(new WizardsAndWarriorsAnalyzer());
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package analyzer.exercises.salarycalculator;
2+
3+
import com.github.javaparser.ast.CompilationUnit;
4+
import com.github.javaparser.ast.body.MethodDeclaration;
5+
import com.github.javaparser.ast.expr.ConditionalExpr;
6+
import com.github.javaparser.ast.expr.MethodCallExpr;
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+
import analyzer.comments.ReuseCode;
14+
15+
/**
16+
* The {@link SalaryCalculatorAnalyzer} is the analyzer implementation for the {@code salary-calculator} practice exercise.
17+
* It extends from the {@link VoidVisitorAdapter} and uses the visitor pattern to traverse each compilation unit.
18+
*
19+
* @see <a href="https://github.com/exercism/java/tree/main/exercises/concept/salary-calculator">The salary-calculator exercise on the Java track</a>
20+
*/
21+
public class SalaryCalculatorAnalyzer extends VoidVisitorAdapter<OutputCollector> implements Analyzer {
22+
private static final String EXERCISE_NAME = "Salary Calculator";
23+
private static final String SALARY_MULTIPLIER = "salaryMultiplier";
24+
private static final String BONUS_MULTIPLIER = "bonusMultiplier";
25+
private static final String FINAL_SALARY = "finalSalary";
26+
private static final String BONUS_FOR_PRODUCTS_SOLD = "bonusForProductsSold";
27+
private boolean essentialCommentAdded = false;
28+
29+
@Override
30+
public void analyze(Solution solution, OutputCollector output) {
31+
for (CompilationUnit compilationUnit : solution.getCompilationUnits()) {
32+
compilationUnit.accept(this, output);
33+
}
34+
35+
if (output.getComments().isEmpty()) {
36+
output.addComment(new ExemplarSolution(EXERCISE_NAME));
37+
}
38+
}
39+
40+
@Override
41+
public void visit(MethodDeclaration node, OutputCollector output) {
42+
if (essentialCommentAdded) {
43+
return;
44+
}
45+
46+
if (isMethodThatMustContainTernaryOperators(node) && doesNotHasTernaryOperators(node)) {
47+
output.addComment(new UseTernaryOperators(node.getNameAsString()));
48+
essentialCommentAdded = true;
49+
}
50+
51+
if (node.getNameAsString().equals(BONUS_FOR_PRODUCTS_SOLD) && doesNotCallMethod(node, BONUS_MULTIPLIER)) {
52+
output.addComment(new ReuseCode(BONUS_FOR_PRODUCTS_SOLD, BONUS_MULTIPLIER));
53+
}
54+
55+
if (node.getNameAsString().equals(FINAL_SALARY) && doesNotCallMethod(node, SALARY_MULTIPLIER)) {
56+
output.addComment(new ReuseCode(FINAL_SALARY, SALARY_MULTIPLIER));
57+
}
58+
59+
if (node.getNameAsString().equals(FINAL_SALARY) && doesNotCallMethod(node, BONUS_FOR_PRODUCTS_SOLD)) {
60+
output.addComment(new ReuseCode(FINAL_SALARY, BONUS_FOR_PRODUCTS_SOLD));
61+
}
62+
63+
super.visit(node, output);
64+
}
65+
66+
private static boolean isMethodThatMustContainTernaryOperators(MethodDeclaration node) {
67+
return node.getNameAsString().equals(SALARY_MULTIPLIER) || node.getNameAsString().equals(BONUS_MULTIPLIER) || node.getNameAsString().equals(FINAL_SALARY);
68+
}
69+
70+
private static boolean doesNotHasTernaryOperators(MethodDeclaration node) {
71+
return node.findAll(ConditionalExpr.class).isEmpty();
72+
}
73+
74+
private static boolean doesNotCallMethod(MethodDeclaration node, String otherMethodName) {
75+
return node.findAll(MethodCallExpr.class, x -> x.getNameAsString().contains(otherMethodName)).isEmpty();
76+
}
77+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package analyzer.exercises.salarycalculator;
2+
3+
import java.util.Map;
4+
5+
import analyzer.Comment;
6+
7+
/**
8+
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/salary-calculator/use_ternary_operators.md">Markdown Template</a>
9+
*/
10+
class UseTernaryOperators extends Comment {
11+
private final String inMethod;
12+
13+
public UseTernaryOperators(String inMethod) {
14+
this.inMethod = inMethod;
15+
}
16+
17+
@Override
18+
public String getKey() {
19+
return "java.salary-calculator.use_ternary_operators";
20+
}
21+
22+
@Override
23+
public Map<String, String> getParameters() {
24+
return Map.of(
25+
"inMethod", this.inMethod);
26+
}
27+
28+
@Override
29+
public Type getType() {
30+
return Type.ESSENTIAL;
31+
}
32+
}

src/main/java/analyzer/exercises/secrets/SecretsAnalyzer.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,32 +41,35 @@ public void analyze(Solution solution, OutputCollector output) {
4141

4242
@Override
4343
public void visit(MethodDeclaration node, OutputCollector output) {
44+
if (essentialCommentAdded) {
45+
return;
46+
}
4447

45-
if (!essentialCommentAdded && node.getNameAsString().equals(SHIFT_BACK) && doesNotUseOperator(node, BinaryExpr.Operator.UNSIGNED_RIGHT_SHIFT)) {
48+
if (node.getNameAsString().equals(SHIFT_BACK) && doesNotUseOperator(node, BinaryExpr.Operator.UNSIGNED_RIGHT_SHIFT)) {
4649
output.addComment(new UseBitwiseOperator(">>>", SHIFT_BACK));
4750
essentialCommentAdded = true;
4851
}
4952

50-
if (!essentialCommentAdded && node.getNameAsString().equals(SET_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.BINARY_OR)) {
53+
if (node.getNameAsString().equals(SET_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.BINARY_OR)) {
5154
output.addComment(new UseBitwiseOperator("|", SET_BITS));
5255
essentialCommentAdded = true;
5356
}
5457

55-
if (!essentialCommentAdded && node.getNameAsString().equals(FLIP_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.XOR)) {
58+
if (node.getNameAsString().equals(FLIP_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.XOR)) {
5659
output.addComment(new UseBitwiseOperator("^", FLIP_BITS));
5760
essentialCommentAdded = true;
5861
}
5962

60-
if (!essentialCommentAdded && node.getNameAsString().equals(CLEAR_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.BINARY_AND)) {
63+
if (node.getNameAsString().equals(CLEAR_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.BINARY_AND)) {
6164
output.addComment(new UseBitwiseOperator("&", CLEAR_BITS));
6265
essentialCommentAdded = true;
6366
}
6467

65-
if (!essentialCommentAdded && node.getNameAsString().equals(CLEAR_BITS) && !doesNotUseOperator(node, BinaryExpr.Operator.BINARY_AND) && doesNotImplementBitwiseNot(node)) {
68+
if (node.getNameAsString().equals(CLEAR_BITS) && !doesNotUseOperator(node, BinaryExpr.Operator.BINARY_AND) && doesNotImplementBitwiseNot(node)) {
6669
output.addComment(new PreferBitwiseNot());
6770
}
6871

69-
if (!essentialCommentAdded && hasConditional(node)) {
72+
if (hasConditional(node)) {
7073
output.addComment(new AvoidConditionalLogic());
7174
}
7275

src/test/java/analyzer/AnalyzerIntegrationTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,25 @@ void wizardsandwarriors(String scenario) throws IOException {
184184

185185
Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
186186
}
187+
188+
@ParameterizedTest
189+
@ValueSource(strings = {
190+
"ExemplarSolution",
191+
"NoReuseBonusForProductsSold",
192+
"NoReuseBonusMultiplier",
193+
"NoReuseSalaryMultiplier",
194+
"NotReusingMethodsAtAll",
195+
"NotUsingTernaryOperatorsAndNotReusingMethods",
196+
"NotUsingTernaryOperatorsAtAll",
197+
"NotUsingTernaryOperatorsOnBonusMultiplier",
198+
"NotUsingTernaryOperatorsOnFinalSalary",
199+
"NotUsingTernaryOperatorsOnSalaryMultiplier"
200+
})
201+
void salarycalculator(String scenario) throws IOException {
202+
var path = Path.of("salary-calculator", scenario + ".java");
203+
var solution = new SolutionFromFiles("salary-calculator", SCENARIOS.resolve(path));
204+
var output = AnalyzerRoot.analyze(solution);
205+
206+
Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario));
207+
}
187208
}
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": "Salary Calculator"
7+
},
8+
"type": "celebratory"
9+
}
10+
]
11+
}
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.general.reuse_code",
5+
"params": {
6+
"callingMethod": "finalSalary",
7+
"methodToCall": "bonusForProductsSold"
8+
},
9+
"type": "actionable"
10+
},
11+
{
12+
"comment": "java.general.feedback_request",
13+
"params": {},
14+
"type": "informative"
15+
}
16+
]
17+
}
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.general.reuse_code",
5+
"params": {
6+
"callingMethod": "bonusForProductsSold",
7+
"methodToCall": "bonusMultiplier"
8+
},
9+
"type": "actionable"
10+
},
11+
{
12+
"comment": "java.general.feedback_request",
13+
"params": {},
14+
"type": "informative"
15+
}
16+
]
17+
}
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.general.reuse_code",
5+
"params": {
6+
"callingMethod": "finalSalary",
7+
"methodToCall": "salaryMultiplier"
8+
},
9+
"type": "actionable"
10+
},
11+
{
12+
"comment": "java.general.feedback_request",
13+
"params": {},
14+
"type": "informative"
15+
}
16+
]
17+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"comments": [
3+
{
4+
"comment": "java.general.reuse_code",
5+
"params": {
6+
"callingMethod": "bonusForProductsSold",
7+
"methodToCall": "bonusMultiplier"
8+
},
9+
"type": "actionable"
10+
},
11+
{
12+
"comment": "java.general.reuse_code",
13+
"params": {
14+
"callingMethod": "finalSalary",
15+
"methodToCall": "salaryMultiplier"
16+
},
17+
"type": "actionable"
18+
},
19+
{
20+
"comment": "java.general.reuse_code",
21+
"params": {
22+
"callingMethod": "finalSalary",
23+
"methodToCall": "bonusForProductsSold"
24+
},
25+
"type": "actionable"
26+
},
27+
{
28+
"comment": "java.general.feedback_request",
29+
"params": {},
30+
"type": "informative"
31+
}
32+
]
33+
}

0 commit comments

Comments
 (0)