-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #84 from kwong21/74-string-calculator
[TDD] Kata - String Calculator
- Loading branch information
Showing
2 changed files
with
133 additions
and
0 deletions.
There are no files selected for viewing
92 changes: 92 additions & 0 deletions
92
src/main/java/com/ordestiny/tdd/kata/StringCalculator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
41
src/test/java/com/ordestiny/tdd/kata/StringCalculatorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |