Expression Calc
Addes calculation for expressions + tests, added user input error check for formel functions + tests, small bug fixes
parent
6d8f8e7520
commit
f94028b6c8
|
@ -1,7 +1,10 @@
|
|||
package de.hs_mannheim.informatik.spreadsheet;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Part of a simplified spreadsheet system for the PR1 programming lab at Hochschule Mannheim.
|
||||
|
@ -10,15 +13,19 @@ import java.util.Scanner;
|
|||
*/
|
||||
public class Axel {
|
||||
static Scanner keyboard = new Scanner(System.in);
|
||||
static Spreadsheet spr = new Spreadsheet(6,8);
|
||||
static Spreadsheet spr = new Spreadsheet(11,11);
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException {
|
||||
// Spreadsheet spr = new Spreadsheet(6,9);
|
||||
|
||||
|
||||
spr.put("A1", "1");
|
||||
spr.put("A2", "1");
|
||||
spr.put("A3", "123");
|
||||
spr.put("A2", "1");
|
||||
|
||||
spr.put("B1", "2");
|
||||
spr.put("B2", "4");
|
||||
spr.put("B3", "1");
|
||||
|
||||
spr.put("B4", "=41+A2");
|
||||
spr.put("C5", "=7*6");
|
||||
spr.put("D1", "=3/2");
|
||||
|
@ -36,6 +43,7 @@ public class Axel {
|
|||
String userCommandPositionInput = userCommandPositionInput();
|
||||
if (userCommandPositionInput.charAt(0) == '*'){
|
||||
executeCommand(userCommandPositionInput);
|
||||
System.out.println(spr);
|
||||
continue;
|
||||
}
|
||||
String userPositionInput = userCommandPositionInput;
|
||||
|
@ -139,11 +147,12 @@ public class Axel {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
//? User input for a value or a formula
|
||||
public static String userValueFormulaInput(){
|
||||
System.out.println();
|
||||
System.out.printf("Input a value of a formula for the selected position.%n");
|
||||
System.out.printf("Format for a value: 7 or 1337 %nFormat for a formula: =7*6 or =SUM(A1:A3)%n");
|
||||
System.out.printf("Format for a value: 7 or 1337 %nFormat for a formula: =7*6 or =SUMME(A1:A3)%n");
|
||||
System.out.print("Input: ");
|
||||
String userCommandInput = keyboard.nextLine();
|
||||
|
||||
|
@ -185,17 +194,46 @@ public class Axel {
|
|||
formulaToCheck = formulaToCheck.toUpperCase().substring(1);
|
||||
|
||||
if (formulaToCheck.startsWith("SUMME(") || formulaToCheck.startsWith("PRODUKT(") || formulaToCheck.startsWith("MITTELWERT(") || formulaToCheck.startsWith("STABW(") || formulaToCheck.startsWith("MIN(") || formulaToCheck.startsWith("MAX(")){
|
||||
return true;
|
||||
return userFormulaFunctionErrorCheck(formulaToCheck);
|
||||
}
|
||||
|
||||
return userFormulaExpressionErrorCheck(formulaToCheck);
|
||||
}
|
||||
|
||||
public static boolean userFormulaFunctionErrorCheck(String functionToCheck){
|
||||
String[] functionCorners = spr.isolateFunctionCorners(functionToCheck);
|
||||
String[] functionBlock= spr.wholeFunctionBlock(functionToCheck);
|
||||
|
||||
if (functionCorners.length != 2){
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String functionCorner: functionCorners){
|
||||
System.out.printf("Corner: %s - Cell test: %s %n", functionCorner, spr.isValueCellName(functionCorner));
|
||||
if (!(spr.isValueCellName(functionCorner).equals("cellName"))){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!userPositionBoundsErrorCheck(functionCorner, spr)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean userFormulaExpressionErrorCheck(String expressionToCheck){
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean userValueErrorCheck(String valueToCheck){
|
||||
//? true if valid
|
||||
//? valid inputs are: 7, 1337, 0, ...
|
||||
//? valid inputs are: 7, 1337, 0, , -213,...
|
||||
|
||||
String digitCheckRegex = "-?\\d+(\\.\\d+)?";
|
||||
//? For floating point numbers
|
||||
// String digitCheckRegex = "-?\\d+(\\.\\d+)?";
|
||||
//? For integers
|
||||
String digitCheckRegex = "-?\\d+";
|
||||
if (!(valueToCheck.matches(digitCheckRegex))){
|
||||
return false;
|
||||
}
|
||||
|
@ -207,5 +245,34 @@ public class Axel {
|
|||
public static void executeCommand(String command) {
|
||||
System.out.printf("Executing command: %s%n", command);
|
||||
//TODO ME:
|
||||
switch (command){
|
||||
case "*load":
|
||||
progLoad();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void progLoad(){
|
||||
int rows = spr.getRowsLCount();
|
||||
int cols = spr.getColsCount();
|
||||
|
||||
for (int r = 0; r < rows; r++){
|
||||
for (int c = 0; c < cols; c++){
|
||||
spr.cells[r][c].setValue("");
|
||||
}
|
||||
}
|
||||
spr.put("A1", "1");
|
||||
spr.put("A2", "2");
|
||||
spr.put("A3", "3");
|
||||
spr.put("A4", "4");
|
||||
spr.put("B1", "5");
|
||||
spr.put("B2", "6");
|
||||
spr.put("B3", "7");
|
||||
spr.put("B4", "8");
|
||||
spr.put("K1", "200");
|
||||
spr.put("K11", "100");
|
||||
spr.put("J4", "4");
|
||||
spr.put("I10", "69");
|
||||
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package de.hs_mannheim.informatik.spreadsheet;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -62,18 +63,21 @@ public class Spreadsheet {
|
|||
}
|
||||
|
||||
public int getRow(String cellName) {
|
||||
//TODO ME: Implement row numbers with two digits
|
||||
return cellName.charAt(1) - '1';
|
||||
String row = "";
|
||||
for (int i = 1; i < cellName.length(); i++){
|
||||
row += cellName.charAt(i);
|
||||
}
|
||||
return Integer.parseInt(row) - 1;
|
||||
}
|
||||
|
||||
// -----
|
||||
// business logic
|
||||
|
||||
/*
|
||||
/**
|
||||
* A method for reading in data from a CSV file.
|
||||
* @param path The file to read.
|
||||
* @param separator The char used to split up the input, e.g. a comma or a semicolon.
|
||||
* @param starCellName The upper left cell where data from the CSV file should be inserted.
|
||||
* @param startCellName The upper left cell where data from the CSV file should be inserted.
|
||||
* @return Nothing.
|
||||
* @exception IOException If path does not exist.
|
||||
*/
|
||||
|
@ -81,7 +85,7 @@ public class Spreadsheet {
|
|||
// TODO: implement this
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* A method for saving data to a CSV file.
|
||||
* @param path The file to write.
|
||||
* @return Nothing.
|
||||
|
@ -106,16 +110,17 @@ public class Spreadsheet {
|
|||
out.close();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* This method does the actual evaluation/calcluation of a specific cell
|
||||
* @param cellName the name of the cell to be evaluated
|
||||
* @param col the col of the cell to be evaluated
|
||||
* @param row the row of the cell to be evaluated
|
||||
* @return Nothing.
|
||||
*/
|
||||
|
||||
private void evaluateCell(int row, int col) {
|
||||
String formula = cells[row][col].getFormula();
|
||||
String result = "";
|
||||
|
||||
isolateFunctionCorners(formula);
|
||||
if (formula.startsWith("SUMME(")) // e.g. SUMME(A3:A8)
|
||||
result = "" + sum(formula.substring(6, 8), formula.substring(9, 11)); // TODO adapt to cells with two digits
|
||||
|
||||
|
@ -145,7 +150,43 @@ public class Spreadsheet {
|
|||
cells[row][col].setValue("" + result);
|
||||
}
|
||||
|
||||
/*
|
||||
public String[] isolateFunctionCorners(String formula){
|
||||
//? isolate corners
|
||||
ArrayList<String> corners = new ArrayList<String>();
|
||||
|
||||
Matcher m = Pattern.compile("[A-Z]\\d+").matcher(formula);
|
||||
while(m.find()) {
|
||||
String s = m.group();
|
||||
|
||||
if (!s.isEmpty()){
|
||||
corners.add(s);
|
||||
}
|
||||
}
|
||||
return corners.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public String[] wholeFunctionBlock(String formula){
|
||||
String[] corners = isolateFunctionCorners(formula);
|
||||
|
||||
String cornerOne = corners[0];
|
||||
char colOne = cornerOne.charAt(0);
|
||||
int rowOne = Integer.parseInt(cornerOne.substring(1));
|
||||
|
||||
String cornerTwo = corners[1];
|
||||
char colTwo = cornerTwo.charAt(0);
|
||||
int rowTwo = Integer.parseInt(cornerTwo.substring(1));
|
||||
|
||||
ArrayList<String> block = new ArrayList<>();
|
||||
|
||||
for (int rowInt = rowOne; rowInt <= rowTwo; rowInt++) {
|
||||
for (char charCol = colOne; charCol <= colTwo; charCol++) {
|
||||
block.add(String.format("%c%d", charCol, rowInt));
|
||||
}
|
||||
}
|
||||
return block.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for calculating the sum of a rectangular block of cells, such as from A1 to B3.
|
||||
* @param startCellName The name of the cell in the upper left corner of the rectangle.
|
||||
* @param endCellName The name of the cell in the lower right corner of the rectangle.
|
||||
|
@ -157,7 +198,7 @@ public class Spreadsheet {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* This method calculates the result of a "normal" algebraic expression. It only needs to support
|
||||
* expressions like =B4 or =2+A3-B2, i.e. only with int numbers and other cells and with plus,
|
||||
* minus, times, split only. An expression always starts with either a number or a cell name. If it
|
||||
|
@ -166,24 +207,97 @@ public class Spreadsheet {
|
|||
* @param formula The expression to be evaluated.
|
||||
* @return The result calculated.
|
||||
*/
|
||||
private long calculate(String formula) throws ArithmeticException {
|
||||
Matcher m = Pattern.compile("([A-Z][0-9]*)|[-\\+\\*/]|[0-9]*").matcher(formula);
|
||||
|
||||
public long calculate(String formula) throws ArithmeticException {
|
||||
long res = 0;
|
||||
|
||||
// TODO implement
|
||||
|
||||
// uncomment the following to see an example how the elements of a formula can be accessed
|
||||
while (m.find()) { // m.find() must always be used before m.group()
|
||||
String s = m.group();
|
||||
if (!s.isEmpty()) {
|
||||
System.out.println(s);
|
||||
}
|
||||
ArrayList<String> formulaElements = extractExpressionElements(formula);
|
||||
|
||||
String firstElement = formulaElements.get(0);
|
||||
|
||||
if (isValueCellName(firstElement).equals("cellName")){
|
||||
String cellValue = get(firstElement);
|
||||
res = cellValue.isEmpty() ? 0 : Long.parseLong(cellValue);
|
||||
}
|
||||
else {
|
||||
res = Long.parseLong(firstElement);
|
||||
}
|
||||
|
||||
for (int i = 1; i < formulaElements.size(); i = i+2) {
|
||||
res = singleCalculation(res, formulaElements.get(i+1), formulaElements.get(i));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ArrayList<String> extractExpressionElements(String formula){
|
||||
Matcher m = Pattern.compile("([A-Z][0-9]*)|[-\\+\\*/]|[0-9]*").matcher(formula);
|
||||
|
||||
ArrayList<String> formulaElements = new ArrayList<String>();
|
||||
|
||||
while (m.find()) {
|
||||
String s = m.group();
|
||||
if (!s.isEmpty()) {
|
||||
formulaElements.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
return formulaElements;
|
||||
}
|
||||
|
||||
private long singleCalculation(long res, String element, String operator){
|
||||
String elementType = isValueCellName(element);
|
||||
|
||||
if (elementType.equals("cellName")){
|
||||
element = get(element);
|
||||
}
|
||||
long value = element.isEmpty() ? 0 : Long.parseLong(element);
|
||||
|
||||
|
||||
switch (operator) {
|
||||
case "+":
|
||||
res += value;
|
||||
break;
|
||||
case "-":
|
||||
res -= value;
|
||||
break;
|
||||
case "*":
|
||||
res *= value;
|
||||
break;
|
||||
case "/":
|
||||
if (value == 0){
|
||||
throw new ArithmeticException("Division by zero");
|
||||
}
|
||||
res /= value;
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void emptyCellZero(){
|
||||
|
||||
}
|
||||
|
||||
public String isValueCellName(String sToCheck){
|
||||
//? 7 -> "value", A1 -> "cellName", 7A -> "invalid"
|
||||
|
||||
//? For floating point numbers
|
||||
// String digitCheckRegex = "-?\\d+(\\.\\d+)?";
|
||||
//? For integers
|
||||
String digitCheckRegex = "-?\\d+";
|
||||
if (sToCheck.matches(digitCheckRegex)){
|
||||
return "value";
|
||||
}
|
||||
|
||||
//? For cell names
|
||||
String cellNameCheckRegex = "[A-Z][0-9]*";
|
||||
if (sToCheck.matches(cellNameCheckRegex)){
|
||||
return "cellName";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
public int getRowsLCount() {
|
||||
|
|
|
@ -2,12 +2,14 @@ package de.hs_mannheim.informatik.spreadsheet;
|
|||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
class AxelTest {
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("User command or position input")
|
||||
void userCommandPositionComputation() {
|
||||
}
|
||||
// --------- Class Vars ---------
|
||||
Spreadsheet testspr = new Spreadsheet(11,11);
|
||||
|
||||
|
||||
// --------- Input tests ---------
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("Command input error check")
|
||||
|
@ -24,7 +26,6 @@ class AxelTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("Position input error check")
|
||||
void userPositionErrorCheck() {
|
||||
|
@ -56,11 +57,6 @@ class AxelTest {
|
|||
|
||||
}
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("User value or formula input")
|
||||
void userValueFormulaComputation() {
|
||||
}
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("Value input error check")
|
||||
void userValueErrorCheck() {
|
||||
|
@ -80,4 +76,88 @@ class AxelTest {
|
|||
@org.junit.jupiter.api.DisplayName("Formula input error check")
|
||||
void userFormulaErrorCheck() {
|
||||
}
|
||||
|
||||
// --------- Calculation tests ---------
|
||||
|
||||
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("Normal calculation")
|
||||
void calculate() {
|
||||
|
||||
putTestSpr(testspr);
|
||||
|
||||
String[] successTestList = {"=2*3", "=2+3", "=5-2", "=6/2", "7/2", "101/2", "=5+6-10*2", "=4-3+50-1/2", "=A1", "=K11", "=A1+A2+A3+A4", "=K11/2/5*2-15", "=a1+A2*a4-6+E7", "=a2-A1+J4-3"};
|
||||
long[] successTestResult = {6, 5, 3, 3, 3, 50, 2, 25, 1, 100, 10, 5, 6, 2};
|
||||
|
||||
String[] failureTestList = {"5+1", "7-4", "6*2", "15/5", "5+4", "10-7", "5*3", "9/3", "=A1", "=K11", "=A1+A3", "=K11-10/2+B1"};
|
||||
long[] failureTestResult = {5, 7, 10, 4, 8, 4, 16, 2, 2, 200, 3, 40};
|
||||
|
||||
for (int i = 0; i < successTestList.length; i++) {
|
||||
// Uppercasing the formula happens in the code too, just in a different place which is not tested
|
||||
String formula = successTestList[i].toUpperCase();
|
||||
assertEquals(successTestResult[i], testspr.calculate(formula));
|
||||
}
|
||||
|
||||
for (int i = 0; i < failureTestList.length; i++) {
|
||||
String formula = failureTestList[i].toUpperCase();
|
||||
assertNotEquals(failureTestResult[i], testspr.calculate(failureTestList[i]));
|
||||
}
|
||||
}
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("Is CellName")
|
||||
void isValueCellName(){
|
||||
String[] successValueTestList = {"7", "0", "1", "420", "424", "23212"};
|
||||
String[] successCellNameTestList = {"A1", "K11", "B6", "g5", "k11"};
|
||||
|
||||
for (String successValue : successValueTestList){
|
||||
successValue = successValue.toUpperCase();
|
||||
assertEquals("value", testspr.isValueCellName(successValue));
|
||||
}
|
||||
|
||||
for (String successCellName : successCellNameTestList){
|
||||
successCellName = successCellName.toUpperCase();
|
||||
assertEquals("cellName", testspr.isValueCellName(successCellName));
|
||||
}
|
||||
}
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("Isolate Corner")
|
||||
void isolateFunctionCorners(){
|
||||
String[] successTestList = {"SUMME(A1:B3)", "SUMME(a3:k5)", "PRODUKT(C3:G6)", "PRODUKT(c2:g5)", "MITTELWERT(A2:j9)", "mittelwert(b4:H8)", "STABW(c7:g7)", "MIN(a1:A6)", "max(f4:h7)", "MAX(E2:j7)"};
|
||||
String[][] successResultList = {{"A1", "B3"}, {"A3", "K5"}, {"C3", "G6"}, {"C2", "G5"}, {"A2", "J9"}, {"B4", "H8"}, {"C7", "G7"}, {"A1", "A6"}, {"F4", "H7"}, {"E2", "J7"}};
|
||||
|
||||
for (int i = 0; i < successTestList.length; i++){
|
||||
String formula = successTestList[i].toUpperCase();
|
||||
assertArrayEquals(successResultList[i], testspr.isolateFunctionCorners(formula));
|
||||
}
|
||||
}
|
||||
|
||||
@org.junit.jupiter.api.Test
|
||||
@org.junit.jupiter.api.DisplayName("Extract whole block")
|
||||
void wholeFunctionBlock(){
|
||||
String[] successTestList = {"SUMME(A1:C4)", "summe(F4:H7)", "Max(D4:D7)", "min(a1:d1)"};
|
||||
String[][] successResultList = {{"A1", "B1", "C1", "A2", "B2", "C2", "A3", "B3", "C3", "A4", "B4", "C4"}, {"F4", "G4", "H4", "F5", "G5", "H5", "F6", "G6", "H6", "F7", "G7", "H7"}, {"D4", "D5", "D6", "D7"}, {"A1", "B1", "C1", "D1"}};
|
||||
|
||||
for (int i = 0; i < successTestList.length; i++){
|
||||
String testElement = successTestList[i].toUpperCase();
|
||||
assertArrayEquals(successResultList[i], testspr.wholeFunctionBlock(testElement));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void putTestSpr(Spreadsheet testspr){
|
||||
testspr.put("A1", "1");
|
||||
testspr.put("A2", "2");
|
||||
testspr.put("A3", "3");
|
||||
testspr.put("A4", "4");
|
||||
testspr.put("B1", "5");
|
||||
testspr.put("B2", "6");
|
||||
testspr.put("B3", "7");
|
||||
testspr.put("B4", "8");
|
||||
testspr.put("K1", "200");
|
||||
testspr.put("K11", "100");
|
||||
testspr.put("J4", "4");
|
||||
testspr.put("I10", "69");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue