1
0
Fork 0

Compare commits

..

1 Commits
main ... main

Author SHA1 Message Date
Oliver Hummel ada8f538a2 Bugfix - OutofBoundsException. 2023-12-19 15:25:55 +01:00
5 changed files with 59 additions and 1175 deletions

View File

@ -1,11 +1,4 @@
1,5,=SUMME(A1:B4),,,,,,,,200
2,6,,,,,,,,,
3,7,,,,,,,,,
4,8,,=1+4*A2,,,2,,,4,
,,,,,3,3,,,,
,,,,,,,,,,
,,,,,,,,,,
,,,,,,,,,,
,,,,,,,,,,
,,,,,,,,69,,
,,,,,,,=STABW(K1:K11),,,100
1,2
3,4
5,6
7,8

1 1 5 2 =SUMME(A1:B4) 200
2 2 3 6 4
3 3 5 7 6
4 4 7 8 8 =1+4*A2 2 4
3 3
69
=STABW(K1:K11) 100

View File

@ -1,408 +1,29 @@
package de.hs_mannheim.informatik.spreadsheet;
import java.io.File;
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.
*
* @author Oliver Hummel & Victor Waitz
* @author Oliver Hummel
*/
public class Axel {
static Scanner keyboard = new Scanner(System.in);
static Spreadsheet spr = new Spreadsheet(11, 11);
static String saveFilePath = "Axel\\resources\\zahlen.csv";
public static void main(String[] args) throws FileNotFoundException {
System.out.println("Welcome to Axel (Totally not Excel)");
System.out.println();
spr.loadSpecialTable();
Spreadsheet spr = new Spreadsheet(10,10);
spr.put("A3", "123");
spr.put("A2", "1");
spr.put("B9", "=41+A2");
spr.put("J5", "=7*6");
spr.put("J6", "=3/2");
System.out.println(spr);
// spr.saveCsv("/tmp/test.csv");
while (true) {
String userCommandPositionInput = userCommandPositionInput();
if (userCommandPositionInput.charAt(0) == '*') {
executeCommand(userCommandPositionInput);
System.out.println(spr);
continue;
}
String userPositionInput = userCommandPositionInput;
String userValueFormulaInput = userValueFormulaInput(userPositionInput);
spr.put(userPositionInput, userValueFormulaInput);
System.out.println(spr);
}
}
//? User input for a program command or a position
/**
* Prompts the user to input a command for the program or a position.
* May be called multiple times, if the input is faulty.
* @return The user inputted position or command as a string.
*/
public static String userCommandPositionInput() {
System.out.println();
System.out.println("Input a command for the program or a position");
System.out.printf("List of program commands: *clear, *load, *save, *exit or *help %nFormat for position: A1 or B14%n");
System.out.print("Input: ");
String userCommandPositionInput = keyboard.nextLine();
return userCommandPositionComputation(userCommandPositionInput);
}
/**
* First method in the error check chain for the user input for a command or a position.
* This method may call userCommandPositionInput() again, if the input is faulty.
* Checks if the input is empty and if not decides if the input is a command or a position.
* Calls different methods to check for different errors.
* @param userCommandPositionInput The user inputted position or command as a string.
* @return The user inputted position or command as a string.
*/
public static String userCommandPositionComputation(String userCommandPositionInput) {
if (userCommandPositionInput.isEmpty()) {
System.out.println("Input is empty!");
return userCommandPositionInput();
}
//? Program command
if (userCommandPositionInput.charAt(0) == '*') {
if (!(userCommandErrorCheck(userCommandPositionInput))) {
System.out.println("Invalid command!");
return userCommandPositionInput();
}
return userCommandPositionInput;
}
//? Position
if (!(userPositionErrorCheck(userCommandPositionInput, spr))) {
System.out.println("Invalid position!");
return userCommandPositionInput();
}
if (!(userPositionBoundsErrorCheck(userCommandPositionInput, spr))) {
System.out.println("Position out of bounds!");
return userCommandPositionInput();
}
return userCommandPositionInput;
}
/**
* Checks if the user inputted command is valid.
* Simply checks if the input equals one of the program commands.
* Case does not matter.
* @param CommandToCheck The user inputted command as a string.
* @return true if the command is valid, false otherwise.
*/
public static boolean userCommandErrorCheck(String CommandToCheck) {
//? true if valid
//? valid inputs are: *clear, *exit, *save, *load, *help (and all upper case variants)
CommandToCheck = CommandToCheck.toLowerCase();
if (CommandToCheck.equals("*clear") || CommandToCheck.equals("*exit") || CommandToCheck.equals("*save") || CommandToCheck.equals("*load") || CommandToCheck.equals("*help")) {
return true;
}
return false;
}
/**
* Checks if the user inputted position is valid.
* Checks if the input is the right length and if it is in the right format.
* Case does not matter.
* @param positionToCheck The user inputted position as a string.
* @param spr The spreadsheet object. (This input is needed for tests)
* @return true if the position is valid, false otherwise.
*/
public static boolean userPositionErrorCheck(String positionToCheck, Spreadsheet spr) {
//? true if valid
//? valid inputs are: A1, B14, C79, E99, F1, G99, J1, M98, ... (and all lower case variants)
positionToCheck = positionToCheck.toUpperCase();
//? Check if input is the right length
if (positionToCheck.length() < 2 || positionToCheck.length() > 3) {
return false;
}
//? Check if input is in the right format
if (!(Character.isLetter(positionToCheck.charAt(0)) && Character.isDigit(positionToCheck.charAt(1)))) {
return false;
}
if (positionToCheck.length() == 3) {
if (!(Character.isDigit(positionToCheck.charAt(2)))) {
return false;
}
}
return true;
}
/**
* Checks if the user inputted position is in the bounds of the spreadsheet.
* Case does not matter.
* @param positionToCheck The user inputted position as a string.
* @param spr The spreadsheet object. (This input is needed for tests)
* @return true if the position is in the bounds, false otherwise.
*/
public static boolean userPositionBoundsErrorCheck(String positionToCheck, Spreadsheet spr) {
//? true if valid
positionToCheck = positionToCheck.toUpperCase();
if ((spr.getRow(positionToCheck) >= spr.getRowsLCount()) || (spr.getCol(positionToCheck) >= spr.getColsCount())) {
return false;
}
return true;
}
//? User input for a value or a formula
/**
* Prompts the user to input a value or a formula for the selected position.
* May be called multiple times, if the input is faulty.
* @param currentPos The position the user is currently inputting a value or a formula for.
* @return The user inputted value or formula as a string.
*/
public static String userValueFormulaInput(String currentPos) {
System.out.println();
System.out.printf("Input a value of a formula for the selected position: %s.%n", currentPos);
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();
return userValueFormulaComputation(userCommandInput, currentPos);
}
/**
* First method in the error check chain for the user input for a value or a formula.
* This method may call userValueFormulaInput() again, if the input is faulty.
* Checks if the input is empty and if not decides if the input is a value or a formula.
* Calls different methods to check for different errors.
* @param userValueFormulaInput The user inputted value or formula as a string.
* @param currentPos The position the user is currently inputting a value or a formula for.
* @return The user inputted value or formula as a string.
*/
public static String userValueFormulaComputation(String userValueFormulaInput, String currentPos) {
if (userValueFormulaInput.isEmpty()) {
System.out.println("Input is empty!");
return userValueFormulaInput(currentPos);
}
//? Formula
if (userValueFormulaInput.charAt(0) == '=') {
if (!(userFormulaErrorCheck(userValueFormulaInput))) {
System.out.println("Invalid formula!");
return userValueFormulaInput(currentPos);
}
return userValueFormulaInput;
}
//? Value
if (!(userValueErrorCheck(userValueFormulaInput))) {
System.out.println("Invalid value!");
return userValueFormulaInput(currentPos);
}
return userValueFormulaInput;
}
/**
* Checks if the user inputted formula is a function or expression.
* Removes the '=' at the beginning.
* To decide if a formula is a function, it simply checks if the formula starts with one of the function names.
* Calls different methods to check for different errors.
* Case does not matter.
* @param formulaToCheck The user inputted formula as a string.
* @return true if the formula is valid, false otherwise.
*/
public static boolean userFormulaErrorCheck(String formulaToCheck) {
//? true if valid
//? valid inputs are: =7*6, =SUM(A1:A3), =A1, =A1+A2, =A1-A2, ... (and all lower case variants)
//? remove '=' at the beginning and make everything upper case
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 userFormulaFunctionErrorCheck(formulaToCheck);
}
return userFormulaExpressionErrorCheck(formulaToCheck);
}
/**
* Checks a function formula for errors.
* Checks if the formula is in the right format and if the cells in the formula are in the bounds of the spreadsheet.
* Checks for some special cases for different functions.
* @param functionToCheck The user inputted formula as a string.
* @return true if the formula is valid, false otherwise.
*/
public static boolean userFormulaFunctionErrorCheck(String functionToCheck) {
String[] functionCorners = spr.isolateFunctionCorners(functionToCheck);
if (functionCorners.length != 2) {
return false;
}
for (String functionCorner : functionCorners) {
if (!(spr.isValueCellName(functionCorner).equals("cellName"))) {
return false;
}
if (!userPositionBoundsErrorCheck(functionCorner, spr)) {
System.out.print("Out of bounds - ");
return false;
}
}
if (functionToCheck.startsWith("MITTELWERT(") || functionToCheck.startsWith("STABW(")) {
String[] functionBlock = spr.wholeFunctionBlock(functionToCheck);
boolean allEmpty = true;
for (String cell : functionBlock) {
if (!cell.isEmpty()) {
allEmpty = false;
}
}
if (allEmpty) {
System.out.print("Division by zero - ");
return false;
}
}
if (functionToCheck.startsWith("STABW(")) {
String[] functionBlock = spr.wholeFunctionBlock(functionToCheck);
String[] notEmptyValues = spr.extractNotEmptyCells(functionBlock);
if (notEmptyValues.length < 2) {
System.out.print("Division by zero - ");
return false;
}
}
return true;
}
/**
* Checks an expression formula for errors.
* To be done in the future.
* @param expressionToCheck The user inputted expression formula as a string.
* @return true if the formula is valid, false otherwise.
*/
public static boolean userFormulaExpressionErrorCheck(String expressionToCheck) {
//TODO ME
return true;
}
/**
* Checks if the user inputted value is valid.
* Checks if the input is a positive or negative integer.
* @param valueToCheck The user inputted value as a string.
* @return true if the value is valid, false otherwise.
*/
public static boolean userValueErrorCheck(String valueToCheck) {
//? true if valid
//? valid inputs are: 7, 1337, 0, , -213,...
//? For floating point numbers
// String digitCheckRegex = "-?\\d+(\\.\\d+)?";
//? For integers
String digitCheckRegex = "-?\\d+";
if (!(valueToCheck.matches(digitCheckRegex))) {
return false;
}
return true;
}
/**
* Executes a program command.
* @param command The user inputted command as a string.
* @return Nothing.
* @throws FileNotFoundException If the file to load or save to is not found.
*/
public static void executeCommand(String command) throws FileNotFoundException {
switch (command) {
case "*clear":
progClear();
break;
case "*load":
progLoad();
break;
case "*save":
progSave();
break;
case "*exit":
progExit();
break;
}
}
/**
* Executes the program command to clear the table.
* @return Nothing.
*/
private static void progClear(){
System.out.println("Are you sure you want to clear the table? (yes/no)");
System.out.print("Input: ");
String userClearInput = keyboard.nextLine().toLowerCase();
if (userClearInput.equals("yes") || userClearInput.equals("y")){
spr.clearTable();
System.out.println("Table cleared!");
}
}
/**
* Executes the program command to load a table from a csv file.
* @return Nothing.
* @throws FileNotFoundException If the file to load is not found.
*/
private static void progLoad() throws FileNotFoundException {
spr.readCsv(saveFilePath, ",", "Amogus");
System.out.println("File loaded!");
}
/**
* Executes the program command to save the table to a csv file.
* @return Nothing.
* @throws FileNotFoundException If the file to save to is not found.
*/
private static void progSave() throws FileNotFoundException {
String savePath = saveFilePath;
spr.saveCsv(savePath);
System.out.println("File saved");
}
/**
* Executes the program command to exit the program and asks the user to save the table.
* Case does not matter.
* @return Nothing.
* @throws FileNotFoundException If the file to save to is not found.
*/
private static void progExit() throws FileNotFoundException {
System.out.println("Do you want to save befor you exit? (yes/no)");
System.out.print("Input: ");
String userExitInput = keyboard.nextLine().toLowerCase();
if (userExitInput.equals("yes") || userExitInput.equals("y")) {
progSave();
}
System.out.println("Goodbye!");
spr.saveCsv("/tmp/test.csv");
// TODO: You might want to put "UI loop" for entering value and formulas here resp. in some UI methods.
}
}

View File

@ -4,57 +4,33 @@ package de.hs_mannheim.informatik.spreadsheet;
* Part of a simplified spreadsheet system for the PR1 programming lab at Hochschule Mannheim.
* A cell needs to be able to hold a formula and a value
*
* @author Oliver Hummel & Victor Waitz
* @author Oliver Hummel
*/
public class Cell {
private String formula = "";
private String value = "";
/**
* Public method to get the formula of the cell.
* @return formula of the cell.
*/
public String getFormula() {
return formula;
}
/**
* Public method to set the formula of the cell.
* @param formula formula of the cell.
*/
public void setFormula(String formula) {
if (!formula.isEmpty())
this.formula = formula.toUpperCase().substring(1); // removes '=' at the beginning
}
/**
* Public method to get the value of the cell.
* @return value of the cell
*/
public String getValue() {
return value;
}
/**
* Public method to set the value of the cell.
* @param value value of the cell.
*/
public void setValue(String value) {
this.value = value;
}
/**
* Public method to get cell value as a string.
* @return value as string.
*/
public String toString() {
return String.format("%4s", value);
}
/**
* Public method to check if the cell is empty.
* @return true if the cell is empty, false otherwise.
*/
public boolean isEmpty() {
return value.isEmpty();
}

View File

@ -1,12 +1,8 @@
package de.hs_mannheim.informatik.spreadsheet;
import javax.naming.ldap.spi.LdapDnsProviderResult;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -14,7 +10,7 @@ import java.util.regex.Pattern;
* A simplified spreadsheet class for the PR1 programming lab at Hochschule Mannheim.
* One aspect worth mentioning is that it only supports long numbers, not doubles.
*
* @author Oliver Hummel & Victor Waitz
* @author Oliver Hummel
*/
public class Spreadsheet {
Cell[][] cells;
@ -26,14 +22,7 @@ public class Spreadsheet {
*/
public Spreadsheet(int rows, int cols) {
if (rows > 99){
System.out.printf("Row size %d is bigger then 99, value will be set to 99! %n", rows);
rows = 99;
}
if (cols > 26){
System.out.printf("Col size %d is bigger then 26, value will be set to 26! %n", cols);
cols = 26;
}
// TODO limit the maximum size on 99 (1..99) rows and 26 (A..Z) columns
cells = new Cell[rows][cols];
@ -45,75 +34,35 @@ public class Spreadsheet {
// -----
// retrieve or change values of cells
/**
* Private method to get a value from a cell in a spreadsheet.
* @param row The row of the cell.
* @param col The column of the cell.
* @return The value of the cell.
*/
private String get(int row, int col) {
return cells[row][col].getValue();
}
/**
* Public method to get a value from a cell in a spreadsheet.
* @param cellName The name of the cell.
* @return The value of the cell.
*/
public String get(String cellName) {
cellName = cellName.toUpperCase();
return get(getRow(cellName), getCol(cellName));
}
/**
* Private methode to put a value into a cell in a spreadsheet.
* @param row The row of the cell.
* @param col The column of the cell.
* @param value The value to put into the cell.
* @return Nothing.
*/
private void put(int row, int col, String value) {
if (!value.startsWith("="))
cells[row][col].setValue(value);
else {
System.out.printf("Formula: %s %n", value);
cells[row][col].setFormula(value);
evaluateCell(row, col);
}
}
/**
* Public method to put a value into a cell in a spreadsheet.
* Calls private methode put.
* @param cellName The name of the cell.
* @param value The value to put into the cell.
* @return Nothing.
*/
public void put(String cellName, String value) {
cellName = cellName.toUpperCase();
put(getRow(cellName), getCol(cellName), value);
}
/**
* Method to get the column of a cell in a spreadsheet.
* @param cellName The name of the cell.
* @return The column of the cell.
*/
public int getCol(String cellName) {
private int getCol(String cellName) {
return cellName.charAt(0) - 'A';
}
/**
* Method to get the row of a cell in a spreadsheet.
* @param cellName The name of the cell.
* @return The row of the cell.
*/
public int getRow(String cellName) {
String row = "";
for (int i = 1; i < cellName.length(); i++){
row += cellName.charAt(i);
}
return Integer.parseInt(row) - 1;
private int getRow(String cellName) {
return cellName.charAt(1) - '1';
}
// -----
@ -121,62 +70,24 @@ public class Spreadsheet {
/**
* A method for reading in data from a CSV file.
* @param filePath The file to read.
* @param path The file to read.
* @param separator The char used to split up the input, e.g. a comma or a semicolon.
* @param startCellName The upper left cell where data from the CSV file should be inserted.
* @param starCellName The upper left cell where data from the CSV file should be inserted.
* @return Nothing.
* @exception IOException If path does not exist.
*/
public void readCsv(String filePath, String separator, String startCellName) throws FileNotFoundException {
ArrayList<String> fileRows = new ArrayList<>();
Scanner sc = new Scanner(new File(filePath));
while (sc.hasNextLine()) {
fileRows.add(sc.nextLine());
}
clearTable();
ArrayList <int[]> formulas = new ArrayList<>();
for (int rowI = 0; rowI < fileRows.size(); rowI++) {
String row = fileRows.get(rowI);
String[] cells = row.split(separator);
for (int colI = 0; colI < cells.length; colI++) {
String cellContent = cells[colI].toUpperCase();
if (cellContent.startsWith("=")){
formulas.add(new int[]{rowI, colI});
}
else {
put(rowI, colI, cellContent);
}
}
}
for (int[] formulaPos : formulas){
int rowI = formulaPos[0];
int colI = formulaPos[1];
System.out.printf("Formula at %d-%d %n", rowI, colI);
String row = fileRows.get(rowI);
String formulaToFill = row.split(separator)[colI];
put(rowI, colI, formulaToFill);
}
sc.close();
public void readCsv(String path, char separator, String startCellName) throws FileNotFoundException {
// TODO: implement this
}
/**
* A method for saving data to a CSV file.
* @param filePath The file to write.
* @param path The file to write.
* @return Nothing.
* @exception FileNotFoundException If path does not exist.
* @exception IOException If path does not exist.
*/
public void saveCsv(String filePath) throws FileNotFoundException {
PrintWriter out = new PrintWriter(filePath);
public void saveCsv(String path) throws FileNotFoundException {
PrintWriter out = new PrintWriter(path);
for (Cell[] row : cells) {
for (Cell cell : row) {
@ -196,34 +107,25 @@ public class Spreadsheet {
/**
* This method does the actual evaluation/calcluation of a specific cell
* @param col the col of the cell to be evaluated
* @param row the row of the cell to be evaluated
* @param cellName the name of the cell to be evaluated
* @return Nothing.
*/
private void evaluateCell(int row, int col) {
String formula = cells[row][col].getFormula();
System.out.printf("Formel in eval: %s %n", formula);
String result = "";
if (formula.startsWith("SUMME(")) // e.g. SUMME(A3:A8)
result = "" + sum(formula);
result = "" + sum(formula.substring(6, 8), formula.substring(9, 11)); // TODO adapt to cells with two digits
else if (formula.startsWith("PRODUKT(")) // e.g. PRODUKT(A3:B9)
result = "" + prod(formula);
result = "TODO"; // TODO
else if (formula.startsWith("MITTELWERT(")) // e.g. MITTELWERT(A3:A5)
result = "" + avrg(formula);
result = "TODO"; // TODO
else if (formula.startsWith("STABW(")) // e.g. STABW(C6:D8) -> Standardabweichung
result = "" + stdDevp(formula);
result = "TODO"; // TODO
else if (formula.startsWith("MIN(")) // e.g. MIN(C13:H13) -> größter Wert
result = "" + min(formula);
result = "TODO"; // TODO
else if (formula.startsWith("MAX(")) // e.g. MAX(A1:A10) -> Standardabweichung
result = "" + max(formula);
result = "TODO"; // TODO
else if (!formula.isEmpty()) {
try {
result = "" + calculate(formula);
@ -231,239 +133,23 @@ public class Spreadsheet {
result = "exc.";
}
}
cells[row][col].setValue("" + result);
}
/**
* Method to extract the corner cells for a function formula.
* @param formula The formula to isolate the corners for
* @return The formulas corners.
*/
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]);
}
/**
* A methode to extract a block of cells used in a function formula,
* such as A1 and B3 for SUMME(A1:B3).
* Calls the isolateFunctionCorners methode to get the blocks corners.
* @param formula The formula to get the block for.
* @return The cell block.
*/
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 formula The formula to calculate the sum.
* @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.
* @return The sum calculated.
*/
public long sum(String formula) {
String[] block = wholeFunctionBlock(formula);
long res = 0;
private long sum(String startCellName, String endCellName) {
// TODO implement
for (String cellName : block){
String cellValue = get(cellName);
long value = cellValue.isEmpty() ? 0 : Long.parseLong(cellValue);
res += value;
}
return res;
return 0;
}
/**
* Method for calculating the product of a rectangular block of cells, such as from A1 to B3.
* @param formula The formula to calculate the product.
* @return The product calculated.
*/
public long prod(String formula){
String[] block = wholeFunctionBlock(formula);
long res = 1;
boolean allEmpty = true;
for (String cellName : block){
String cellValue = get(cellName);
if (!cellValue.isEmpty()){
allEmpty = false;
}
long value = cellValue.isEmpty() ? 1 : Long.parseLong(cellValue);
res *= value;
}
if (allEmpty){
res = 0;
}
return res;
}
/**
* Method for calculating the average of a rectangular block of cells, such as from A1 to B3.
* @param formula The formula to calculate the average.
* @return The average calculated.
*/
public long avrg(String formula){
String[] block = wholeFunctionBlock(formula);
boolean allEmpty = true;
long res = 0;
int counter = 0;
for (String cellName : block){
String cellValue = get(cellName);
if (cellValue.isEmpty()){
continue;
}
allEmpty = false;
res += Long.parseLong(cellValue);
counter++;
}
if (allEmpty){
System.out.println("Division by zero! Value set to 0");
return 0;
}
res = res/counter;
return res;
}
/**
* Method for calculating the standard deviation of a rectangular block of cells, such as from A1 to B3.
* Calls the average method.
* @param formula The formula to calculate the standard deviation.
* @return The standard deviation calculated.
*/
public long stdDevp(String formula){
String[] block = wholeFunctionBlock(formula);
String[] notEmptyValues = extractNotEmptyCells(block);
if(notEmptyValues.length < 2){
System.out.println("Division by zero! Value set to 0");
return 0;
}
long meanValue = avrg(formula);
long squaredDiffsSum = 0;
for (String value : notEmptyValues){
long difference = Long.parseLong(value) - meanValue;
squaredDiffsSum += (long) Math.pow(difference, 2);
}
long variance = squaredDiffsSum / (notEmptyValues.length - 1);
return (long) Math.sqrt(variance);
}
/**
* Method for selecting the minimum of a rectangular block of cells, such as from A1 to B3.
* @param formula The formula to select the minimum from.
* @return The minimum selected.
*/
public long min(String formula){
String[] block = wholeFunctionBlock(formula);
String firstElement = "";
long currentSmallest = 0;
for (int i = 0; i < block.length; i++) {
String cellValue = get(block[i]);
if (cellValue.isEmpty()) {
continue;
}
if (firstElement.isEmpty()){
firstElement = get(block[i]);
currentSmallest = Long.parseLong(firstElement);
}
long value = Long.parseLong(cellValue);
if (value < currentSmallest){
currentSmallest = value;
}
}
return currentSmallest;
}
/**
* Method for selecting the maximum of a rectangular block of cells, such as from A1 to B3.
* @param formula The formula to select the maximum from.
* @return The maximum selected.
*/
public long max(String formula){
String[] block = wholeFunctionBlock(formula);
String firstElement = "";
long currentBiggest = 0;
for (int i = 0; i < block.length; i++) {
String cellValue = get(block[i]);
if (cellValue.isEmpty()) {
continue;
}
if (firstElement.isEmpty()){
firstElement = get(block[i]);
currentBiggest = Long.parseLong(firstElement);
}
long value = Long.parseLong(cellValue);
if (value > currentBiggest){
currentBiggest = value;
}
}
return currentBiggest;
}
/**
* Method to extract only cells which are not empty from a previously calculated block from a
* function formula.
* @param block Block with empty and not empty cells.
* @return Block with only not empty cells.
*/
public String[] extractNotEmptyCells(String[] block){
ArrayList<String> notEmptyValues = new ArrayList<String>();
for (String cell : block) {
String cellValue = get(cell);
if (!cellValue.isEmpty()){
notEmptyValues.add(cellValue);
}
}
return notEmptyValues.toArray(new String[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
@ -472,176 +158,26 @@ public class Spreadsheet {
* @param formula The expression to be evaluated.
* @return The result calculated.
*/
public long calculate(String formula) throws ArithmeticException {
long res = 0;
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;
}
/**
* Method to extract every single element of an expression and stores them as a separate element in the
* String ArrayList formulaElements and returns it.
* An expression element is either a number, a cell name or an operator.
* @param formula The formula expression to extract elements from.
* @return The elements as single elements.
*/
public static ArrayList<String> extractExpressionElements(String formula){
private long calculate(String formula) throws ArithmeticException {
Matcher m = Pattern.compile("([A-Z][0-9]*)|[-\\+\\*/]|[0-9]*").matcher(formula);
ArrayList<String> formulaElements = new ArrayList<String>();
long res = 0;
while (m.find()) {
// 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()) {
formulaElements.add(s);
System.out.println(s);
}
}
return formulaElements;
}
/**
* Method to calculate a single calculation. A single calculation always uses one operator and one value part.
* The value part can be a cell name or a value. The calculation takes the given previous solution and uses the given
* operator and the given value part to calculate and return a new solution.
* @param res The previous result of a calculation.
* @param element The element to calculate to the previous result.
* @param operator The operator used to calculate previous result and element.
* @return The new result.
*/
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;
}
/**
* Methode to determine if a value part is either a cell name or a value.
* Has the possibility to determine floating point numbers for the future.
* @param sToCheck The string to decide.
* @return The String containing the information if it is a cell name or a value.
*/
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";
}
// -----
/**
* Returns the number of rows in the spreadsheet.
* @return amount rows.
*/
public int getRowsLCount() {
return cells.length;
}
/**
* Returns the number of columns in the spreadsheet.
* @return amount columns.
*/
public int getColsCount() {
return cells[0].length;
}
/**
* Clears the spreadsheet, it fills every value and formula to zero.
* @return Nothing.
*/
public void clearTable() {
int rows = getRowsLCount();
int cols = getColsCount();
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
cells[r][c].setValue("");
cells[r][c].setFormula("");
}
}
}
/**
* Loads a special table for testing purposes.
* @return Nothing.
*/
public void loadSpecialTable(){
clearTable();
put("A1", "1");
put("A2", "2");
put("A3", "3");
put("A4", "4");
put("B1", "5");
put("B2", "6");
put("B3", "7");
put("B4", "8");
put("K1", "200");
put("K11", "100");
put("J4", "4");
put("I10", "69");
put("F5", "3");
put("G4", "2");
put("G5", "3");
}
/**
* Returns a string representation of the spreadsheet.
* @return The spreadsheet as a string.
*/
public String toString() {
StringBuilder sb = new StringBuilder();
@ -662,4 +198,5 @@ public class Spreadsheet {
}
return sb.toString();
}
}

View File

@ -1,243 +0,0 @@
package de.hs_mannheim.informatik.spreadsheet;
import static org.junit.jupiter.api.Assertions.*;
class AxelTest {
// --------- Class Vars ---------
Spreadsheet testspr = new Spreadsheet(11,11);
// --------- Input tests ---------
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Command input error check")
void userCommandErrorCheck() {
String[] successTestList = {"*exit", "*save", "*load", "*help", "*eXiT", "*SAVE", "*LoAd", "*hElP"};
String[] failureTestList = {"exit", "save", "load", "help", "eXiT", "SAVE", "LoAd", "hElP", "*exit*", "*save*", "*load*", "*help*", "*eXiT*", "*SAVE*", "*LoAd*", "*hElP*", "*test", "*TEST", "*saev", "*xeit"};
for (String successTest : successTestList) {
assertTrue(Axel.userCommandErrorCheck(successTest));
}
for (String failureTest : failureTestList) {
assertFalse(Axel.userCommandErrorCheck(failureTest));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Position input error check")
void userPositionErrorCheck() {
//? Overall format test
String[] successTestList = {"A1", "B14", "C79", "E99", "F1", "G99", "J1", "M98", "N67", "o45", "p23", "q12", "r2"};
String[] failureTestList = {"A100", "Z101", "AA1", "AB2", "ZZ1", "A1A", "FHT", "AAA", "G3U", "ZZZ", "2A", "47H", "AAAA", "test", "TEST", "save", "*save", "*exit"};
for (String successTest : successTestList) {
assertTrue(Axel.userPositionErrorCheck(successTest, testspr));
}
for (String failureTest : failureTestList) {
assertFalse(Axel.userPositionErrorCheck(failureTest, testspr));
}
//? Out of bounds test
Spreadsheet smallTestSpr = new Spreadsheet(6,8);
String[] successBoundsTest = {"A1", "a1", "b3", "c5", "A3", "H6", "h6"};
String[] failureBoundsTest = {"i2", "J6", "B7", "K9", "Z99"};
for (String successTest : successBoundsTest) {
assertTrue(Axel.userPositionBoundsErrorCheck(successTest, smallTestSpr));
}
for (String failureTest : failureBoundsTest) {
assertFalse(Axel.userPositionBoundsErrorCheck(failureTest, smallTestSpr));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Value input error check")
void userValueErrorCheck() {
String[] successTestList = {"0", "1", "-1", "1234", "4324", "-465"};
String[] failureTestList = {"a", "aa", "A!", "A1", "wef", "-a", "a-","-1a"};
for (String successTest : successTestList) {
assertTrue(Axel.userValueErrorCheck(successTest));
}
for (String failureTest : failureTestList) {
assertFalse(Axel.userValueErrorCheck(failureTest));
}
}
@org.junit.jupiter.api.Test
@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();
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));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Sum")
void sum(){
putTestSpr();
String[] successTestList = {"SUMME(A1:B2)", "summe(B1:e1)", "Summe(B4:I10)", "SUMME(K1:K11)", "SUMME(F1:H1)", "SUMME(F4:G5)", "SUMME(A3:a3)"};
long[] successResultList = {14, 5, 85, 300, 0, 8, 3};
for (int i = 0; i < successTestList.length; i++) {
String testElement = successTestList[i].toUpperCase();
assertEquals(successResultList[i], testspr.sum(testElement));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Product")
void prod(){
putTestSpr();
String[] successTestList = {"PRODUKT(A1:B2)", "PRODUKT(A2:B3)", "PRODUKT(B1:C4)", "Produkt(F1:H1)", "produkt(f4:g5)", "PRODUKT(A3:a3)"};
long[] successResultList = {60, 252, 1680, 0, 18, 3};
for (int i = 0; i < successTestList.length; i++) {
String testElement = successTestList[i].toUpperCase();
assertEquals(successResultList[i], testspr.prod(testElement));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Average")
void avrg(){
putTestSpr();
String[] successTestList = {"MITTELWERT(A1:B4)", "MITTELWERT(A3:B4)", "MITTELWERT(A1:A4)", "mittelwert(K1:K11)","Mittelwert(F1:H1)" ,"MITTELWERT(f4:g5)", "MITTELWERT(A3:a3)"};
long[] successResultList = {4, 5, 2, 150, 0, 2, 3};
for (int i = 0; i < successTestList.length; i++) {
String testElement = successTestList[i].toUpperCase();
assertEquals(successResultList[i], testspr.avrg(testElement));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Standard deviation")
void stdDevp(){
putTestSpr();
String[] successTestList = {"STABW(A1:B4)", "STABW(A2:B3)", "STABW(A1:A4)", "STABW(K1:K11)", "stabw(f1:H1)", "Stabw(f4:g5)", "STAbW(a3:a3)"};
long[] successResultList = {2, 2, 1, 70, 0, 1, 0};
for (int i = 0; i < successTestList.length; i++) {
String testElement = successTestList[i].toUpperCase();
assertEquals(successResultList[i], testspr.stdDevp(testElement));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Min value")
void min(){
putTestSpr();
String[] successTestList = {"MIN(A1:B4)", "MIN(A2:B3)", "MIN(A1:A4)", "MIN(K1:K11)", "min(f1:H1)", "Min(f4:g5)", "min(a3:a3)"};
long[] successResultList = {1, 2, 1, 100, 0, 2, 3};
for (int i = 0; i < successTestList.length; i++) {
String testElement = successTestList[i].toUpperCase();
assertEquals(successResultList[i], testspr.min(testElement));
}
}
@org.junit.jupiter.api.Test
@org.junit.jupiter.api.DisplayName("Max value")
void max(){
putTestSpr();
String[] successTestList = {"MAX(A1:B4)", "MAX(A2:B3)", "MAX(A1:A4)", "MAX(K1:K11)", "max(f1:H1)", "Max(f4:g5)", "max(a3:a3)"};
long[] successResultList = {8, 7, 4, 200, 0, 3, 3};
for (int i = 0; i < successTestList.length; i++) {
String testElement = successTestList[i].toUpperCase();
assertEquals(successResultList[i], testspr.max(testElement));
}
}
private void putTestSpr(){
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");
testspr.put("F5", "3");
testspr.put("G4", "2");
testspr.put("G5", "3");
}
}