Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement analyzer for salary-calculator #155

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/analyzer/AnalyzerRoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import analyzer.exercises.leap.LeapAnalyzer;
import analyzer.exercises.loglevels.LogLevelsAnalyzer;
import analyzer.exercises.needforspeed.NeedForSpeedAnalyzer;
import analyzer.exercises.salarycalculator.SalaryCalculatorAnalyzer;
import analyzer.exercises.secrets.SecretsAnalyzer;
import analyzer.exercises.twofer.TwoferAnalyzer;
import analyzer.exercises.wizardsandwarriors.WizardsAndWarriorsAnalyzer;
Expand Down Expand Up @@ -56,6 +57,7 @@ private static List<Analyzer> createAnalyzers(String slug) {
case "leap" -> analyzers.add(new LeapAnalyzer());
case "log-levels" -> analyzers.add(new LogLevelsAnalyzer());
case "need-for-speed" -> analyzers.add(new NeedForSpeedAnalyzer());
case "salary-calculator" -> analyzers.add(new SalaryCalculatorAnalyzer());
case "secrets" -> analyzers.add(new SecretsAnalyzer());
case "two-fer" -> analyzers.add(new TwoferAnalyzer());
case "wizards-and-warriors" -> analyzers.add(new WizardsAndWarriorsAnalyzer());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package analyzer.exercises.salarycalculator;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.ConditionalExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;

import analyzer.Analyzer;
import analyzer.OutputCollector;
import analyzer.Solution;
import analyzer.comments.ExemplarSolution;
import analyzer.comments.ReuseCode;

/**
* The {@link SalaryCalculatorAnalyzer} is the analyzer implementation for the {@code salary-calculator} 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/salary-calculator">The salary-calculator exercise on the Java track</a>
*/
public class SalaryCalculatorAnalyzer extends VoidVisitorAdapter<OutputCollector> implements Analyzer {
private static final String EXERCISE_NAME = "Salary Calculator";
private static final String SALARY_MULTIPLIER = "salaryMultiplier";
private static final String BONUS_MULTIPLIER = "bonusMultiplier";
private static final String FINAL_SALARY = "finalSalary";
private static final String BONUS_FOR_PRODUCTS_SOLD = "bonusForProductsSold";
private boolean essentialCommentAdded = false;

@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 (!essentialCommentAdded && isMethodThatMustContainTernaryOperators(node) && doesNotHasTernaryOperators(node)) {
output.addComment(new UseTernaryOperators(node.getNameAsString()));
essentialCommentAdded = true;
}

if (!essentialCommentAdded && node.getNameAsString().equals(BONUS_FOR_PRODUCTS_SOLD) && doesNotCallMethod(node, BONUS_MULTIPLIER)) {
output.addComment(new ReuseCode(BONUS_FOR_PRODUCTS_SOLD, BONUS_MULTIPLIER));
}

if (!essentialCommentAdded && node.getNameAsString().equals(FINAL_SALARY) && doesNotCallMethod(node, SALARY_MULTIPLIER)) {
output.addComment(new ReuseCode(FINAL_SALARY, SALARY_MULTIPLIER));
}

if (!essentialCommentAdded && node.getNameAsString().equals(FINAL_SALARY) && doesNotCallMethod(node, BONUS_FOR_PRODUCTS_SOLD)) {
output.addComment(new ReuseCode(FINAL_SALARY, BONUS_FOR_PRODUCTS_SOLD));
}

super.visit(node, output);
}

private static boolean isMethodThatMustContainTernaryOperators(MethodDeclaration node) {
return node.getNameAsString().equals(SALARY_MULTIPLIER) || node.getNameAsString().equals(BONUS_MULTIPLIER) || node.getNameAsString().equals(FINAL_SALARY);
}

private static boolean doesNotHasTernaryOperators(MethodDeclaration node) {
return node.findAll(ConditionalExpr.class).isEmpty();
}

private static boolean doesNotCallMethod(MethodDeclaration node, String otherMethodName) {
return node.findAll(MethodCallExpr.class, x -> x.getNameAsString().contains(otherMethodName)).isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package analyzer.exercises.salarycalculator;

import java.util.Map;

import analyzer.Comment;

/**
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/salary-calculator/use_ternary_operators.md">Markdown Template</a>
*/
class UseTernaryOperators extends Comment {
private final String inMethod;

public UseTernaryOperators(String inMethod) {
this.inMethod = inMethod;
}

@Override
public String getKey() {
return "java.salary-calculator.use_ternary_operators";
}

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

@Override
public Type getType() {
return Type.ESSENTIAL;
}
}
21 changes: 21 additions & 0 deletions src/test/java/analyzer/AnalyzerIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,25 @@ void wizardsandwarriors(String scenario) throws IOException {

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

@ParameterizedTest
@ValueSource(strings = {
"ExemplarSolution",
"NoReuseBonusForProductsSold",
"NoReuseBonusMultiplier",
"NoReuseSalaryMultiplier",
"NotReusingMethodsAtAll",
"NotUsingTernaryOperatorsAndNotReusingMethods",
"NotUsingTernaryOperatorsAtAll",
"NotUsingTernaryOperatorsOnBonusMultiplier",
"NotUsingTernaryOperatorsOnFinalSalary",
"NotUsingTernaryOperatorsOnSalaryMultiplier"
})
void salarycalculator(String scenario) throws IOException {
var path = Path.of("salary-calculator", scenario + ".java");
var solution = new SolutionFromFiles("salary-calculator", 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": "Salary Calculator"
},
"type": "celebratory"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"comments": [
{
"comment": "java.general.reuse_code",
"params": {
"callingMethod": "finalSalary",
"methodToCall": "bonusForProductsSold"
},
"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.general.reuse_code",
"params": {
"callingMethod": "bonusForProductsSold",
"methodToCall": "bonusMultiplier"
},
"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.general.reuse_code",
"params": {
"callingMethod": "finalSalary",
"methodToCall": "salaryMultiplier"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"comments": [
{
"comment": "java.general.reuse_code",
"params": {
"callingMethod": "bonusForProductsSold",
"methodToCall": "bonusMultiplier"
},
"type": "actionable"
},
{
"comment": "java.general.reuse_code",
"params": {
"callingMethod": "finalSalary",
"methodToCall": "salaryMultiplier"
},
"type": "actionable"
},
{
"comment": "java.general.reuse_code",
"params": {
"callingMethod": "finalSalary",
"methodToCall": "bonusForProductsSold"
},
"type": "actionable"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"comments": [
{
"comment": "java.salary-calculator.use_ternary_operators",
"params": {
"inMethod": "salaryMultiplier"
},
"type": "essential"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"comments": [
{
"comment": "java.salary-calculator.use_ternary_operators",
"params": {
"inMethod": "salaryMultiplier"
},
"type": "essential"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"comments": [
{
"comment": "java.salary-calculator.use_ternary_operators",
"params": {
"inMethod": "bonusMultiplier"
},
"type": "essential"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"comments": [
{
"comment": "java.salary-calculator.use_ternary_operators",
"params": {
"inMethod": "finalSalary"
},
"type": "essential"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"comments": [
{
"comment": "java.salary-calculator.use_ternary_operators",
"params": {
"inMethod": "salaryMultiplier"
},
"type": "essential"
},
{
"comment": "java.general.feedback_request",
"params": {},
"type": "informative"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package scenarios.salarycalculator;

public class SalaryCalculator {

public double salaryMultiplier(int daysSkipped) {
return daysSkipped < 5 ? 1 : 0.85;
}

public int bonusMultiplier(int productsSold) {
return productsSold < 20 ? 10 : 13;
}

public double bonusForProductsSold (int productsSold) {
return productsSold * bonusMultiplier(productsSold);
}

public double finalSalary (int daysSkipped, int productsSold) {
double finalSalary = 1000.0 * salaryMultiplier(daysSkipped) + bonusForProductsSold(productsSold);
return finalSalary > 2000.0 ? 2000.0 : finalSalary;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package scenarios.salarycalculator;

public class SalaryCalculator {

public double salaryMultiplier(int daysSkipped) {
return daysSkipped < 5 ? 1 : 0.85;
}

public int bonusMultiplier(int productsSold) {
return productsSold < 20 ? 10 : 13;
}

public double bonusForProductsSold (int productsSold) {
return productsSold * bonusMultiplier(productsSold);
}

public double finalSalary (int daysSkipped, int productsSold) {
double finalSalary = 1000.0 * salaryMultiplier(daysSkipped) + productsSold * bonusMultiplier(productsSold);
return finalSalary > 2000.0 ? 2000.0 : finalSalary;
}
}
Loading