Skip to content

Commit

Permalink
Merge pull request #84 from kwong21/74-string-calculator
Browse files Browse the repository at this point in the history
[TDD] Kata - String Calculator
  • Loading branch information
fengyuanyang committed Nov 2, 2020
2 parents 592732d + 630ef27 commit 947a156
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
92 changes: 92 additions & 0 deletions src/main/java/com/ordestiny/tdd/kata/StringCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.ordestiny.tdd.kata;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.regex.Pattern;

public class StringCalculator {

private static final String NEWLINE = "\n";
private static final String CUSTOM_DELIMITER_SUFFIX = NEWLINE;
private static final String DEFAULT_DELIMITERS = ",|" + NEWLINE;
private static final String CUSTOM_DELIMITER_PREFIX = "//";

public String add(String number) {
String delimiter = DEFAULT_DELIMITERS;
String numbers = number;

if (number.startsWith(CUSTOM_DELIMITER_PREFIX)) {
delimiter = getCustomDelimiter(number);
numbers = getNumbersInput(number);
validateInput(numbers, delimiter);
}

if (numbers.isEmpty()) {
return "0";
}

if (isInvalidLastCharacterIn(numbers)) {
throw new IllegalArgumentException("Number expected but found EOF");
}

double result = getSum(numbers, delimiter);
return format(result);
}

private String getCustomDelimiter(String input) {
int indexOfDelimiterStart = input.lastIndexOf(CUSTOM_DELIMITER_PREFIX) + 2;
int indexOfNumbersStart = input.indexOf(CUSTOM_DELIMITER_SUFFIX);

String unquotedDelimiter = input.substring(indexOfDelimiterStart, indexOfNumbersStart);

return unquotedDelimiter;
}

private String getNumbersInput(String input) {
return input.substring(input.lastIndexOf(NEWLINE) + 1);
}

private boolean isInvalidLastCharacterIn(String numbers) {
return Character.digit(numbers.charAt(numbers.length() - 1), 10) < 0;
}

private void validateInput(String input, String delimiter) {
for (int i = 1; i < input.length(); i += 2) {
int expectedDelimiterIndex = i + delimiter.length();

if (expectedDelimiterIndex < input.length()) {
String delimiterInString = input.substring(i, expectedDelimiterIndex);

if (!delimiterInString.equals(delimiter)) {
String exceptionMessage = String.format("'%s' expected but '%s', found at position %d", delimiter,
delimiterInString, i);
throw new IllegalArgumentException(exceptionMessage);
}
}
}
}

private double getSum(String numbers, String delimiter) {
if (delimiter.matches("[^A-Za-z0-9]")) {
// Quote any delimiter that is a special character, such as `|`
delimiter = Pattern.quote(delimiter);
}

String[] arrayOfStringNumbers = numbers.split(delimiter);

return sum(arrayOfStringNumbers);
}

private double sum(String[] numbers) {
Double sum = Arrays.stream(numbers)
.map(number-> Double.valueOf(number))
.reduce(0.0, Double::sum);

return sum;
}

private String format(double value) {
DecimalFormat format = new DecimalFormat("0.#");
return format.format(value);
}
}
41 changes: 41 additions & 0 deletions src/test/java/com/ordestiny/tdd/kata/StringCalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.ordestiny.tdd.kata;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

public class StringCalculatorTest {

private static StringCalculator fixture;

@BeforeAll
public static void setup() {
fixture = new StringCalculator();
}

@ParameterizedTest
@CsvSource({ "'',0", "1,1", "'1.1,2.2',3.3", "'1\n2,3', 6", "'//;\n1;2',3", "'//|\n1|2|3', 6", "'//sep\n2sep3',5",
"'//;\n',0" })
public void add_WithValidNumber_SumOfNumbers(String input, String expected) {
final String actual = fixture.add(input);

assertEquals(expected, actual);
}

@ParameterizedTest
@ValueSource(strings = { "//|\n1|2,3", "//a\n1a2a3a4b5", "//%\n1%4$5", "//c\n1c2c" })
public void add_WithInvalidInput_ThrowException(String input) {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
fixture.add(input);
});

String expectedMessage = "expected but";
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
}

0 comments on commit 947a156

Please sign in to comment.