2023-12-22 21:23:58 +01:00
|
|
|
package de.hs_mannheim.informatik.spreadsheet;
|
|
|
|
|
2024-01-08 02:40:29 +01:00
|
|
|
import java.io.BufferedReader;
|
2023-12-22 21:23:58 +01:00
|
|
|
import java.io.FileNotFoundException;
|
2024-01-08 02:40:29 +01:00
|
|
|
import java.io.FileReader;
|
2023-12-22 21:23:58 +01:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.PrintWriter;
|
2024-01-06 15:58:12 +01:00
|
|
|
import java.util.ArrayList;
|
2023-12-22 21:23:58 +01:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
public class Spreadsheet {
|
2024-01-08 02:40:29 +01:00
|
|
|
int rows=0;
|
|
|
|
int cols=0;
|
2023-12-22 21:23:58 +01:00
|
|
|
Cell[][] cells;
|
2024-01-08 02:40:29 +01:00
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
/**
|
|
|
|
* Constructor that creates a Spreadsheet of size rows * cols.
|
|
|
|
* @param rows number of rows
|
|
|
|
* @param cols number of columns
|
|
|
|
*/
|
|
|
|
public Spreadsheet(int rows, int cols) {
|
2024-01-08 02:40:29 +01:00
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
this.rows=Math.min(rows, 99);
|
|
|
|
this.cols=Math.min(cols, 26);
|
2024-01-08 02:40:29 +01:00
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
cells = new Cell[rows][cols];
|
|
|
|
|
|
|
|
for (int r = 0; r < rows; r++)
|
|
|
|
for (int c = 0; c < cols; c++)
|
|
|
|
cells[r][c] = new Cell();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----
|
|
|
|
// retrieve or change values of cells
|
2024-01-08 20:41:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
private String get(int row, int col) {
|
|
|
|
return cells[row][col].getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
public String get(String cellName) {
|
2024-01-06 15:58:12 +01:00
|
|
|
if(containsOnlyNumbers(cellName)) {
|
|
|
|
return cellName;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cellName = cellName.toUpperCase();
|
|
|
|
return get(getRow(cellName), getCol(cellName));
|
|
|
|
}
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void put(int row, int col, String value) {
|
|
|
|
if (!value.startsWith("="))
|
|
|
|
cells[row][col].setValue(value);
|
|
|
|
else {
|
|
|
|
cells[row][col].setFormula(value);
|
|
|
|
evaluateCell(row, col);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void put(String cellName, String value) {
|
|
|
|
cellName = cellName.toUpperCase();
|
|
|
|
put(getRow(cellName), getCol(cellName), value);
|
|
|
|
}
|
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
public int getCol(String cellName) {
|
2023-12-22 21:23:58 +01:00
|
|
|
return cellName.charAt(0) - 'A';
|
|
|
|
}
|
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
public int getRow(String cellName) {
|
2024-01-07 14:30:17 +01:00
|
|
|
if(cellName.length()>2) {
|
|
|
|
int tenDigit=cellName.charAt(1)-'0';
|
|
|
|
int oneDigit=cellName.charAt(2)-'0';
|
|
|
|
int combined= tenDigit*10+oneDigit;
|
2024-01-08 20:41:01 +01:00
|
|
|
return combined -1; //adjust for indexing at 0
|
2024-01-07 14:30:17 +01:00
|
|
|
}
|
|
|
|
else {
|
2024-01-08 20:41:01 +01:00
|
|
|
return cellName.charAt(1) - '1';
|
2024-01-07 14:30:17 +01:00
|
|
|
}
|
2023-12-22 21:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----
|
|
|
|
// 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.
|
|
|
|
* @return Nothing.
|
|
|
|
* @exception IOException If path does not exist.
|
|
|
|
*/
|
2024-01-08 02:40:29 +01:00
|
|
|
public void readCsv(String path, String separator) throws IOException {
|
|
|
|
BufferedReader reader = new BufferedReader(new FileReader(path));
|
|
|
|
|
|
|
|
for(int row=0;row<rows;row++) {
|
|
|
|
String line = reader.readLine();
|
|
|
|
String[] columns = line.split(",");
|
|
|
|
for (int col = 0; col < columns.length; col++) {
|
|
|
|
put(row, col, columns[col]);
|
|
|
|
}
|
|
|
|
}
|
2024-01-08 20:41:01 +01:00
|
|
|
reader.close();
|
2023-12-22 21:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A method for saving data to a CSV file.
|
|
|
|
* @param path The file to write.
|
|
|
|
* @return Nothing.
|
|
|
|
* @exception IOException If path does not exist.
|
|
|
|
*/
|
|
|
|
public void saveCsv(String path) throws FileNotFoundException {
|
|
|
|
PrintWriter out = new PrintWriter(path);
|
|
|
|
|
|
|
|
for (Cell[] row : cells) {
|
|
|
|
for (Cell cell : row) {
|
|
|
|
if (!cell.getFormula().isEmpty())
|
|
|
|
out.print("=" + cell.getFormula());
|
|
|
|
else
|
|
|
|
out.print(cell.getValue());
|
|
|
|
|
|
|
|
if (cell != row[cells[0].length-1])
|
|
|
|
out.print(",");
|
|
|
|
}
|
|
|
|
out.println();
|
|
|
|
}
|
|
|
|
|
|
|
|
out.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method does the actual evaluation/calcluation of a specific cell
|
|
|
|
* @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();
|
|
|
|
String result = "";
|
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
if (formula.startsWith("SUMME(")) {// e.g. SUMME(A3:A8)
|
|
|
|
String[] onlyCells = formula.substring(6, formula.length() - 1).split(":");
|
|
|
|
|
|
|
|
if(checkIfCellExists(onlyCells[0])&&checkIfCellExists(onlyCells[1])) {
|
|
|
|
result = "" + sum(onlyCells[0], onlyCells[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (formula.startsWith("PRODUKT(")) {// e.g. PRODUKT(A3:B9)
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
result = "TODO"; // TODO
|
2024-01-08 20:41:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
else if (formula.startsWith("MITTELWERT(")) // e.g. MITTELWERT(A3:A5)
|
|
|
|
result = "TODO"; // TODO
|
|
|
|
else if (formula.startsWith("STABW(")) // e.g. STABW(C6:D8) -> Standardabweichung
|
|
|
|
result = "TODO"; // TODO
|
|
|
|
else if (formula.startsWith("MIN(")) // e.g. MIN(C13:H13) -> größter Wert
|
|
|
|
result = "TODO"; // TODO
|
|
|
|
else if (formula.startsWith("MAX(")) // e.g. MAX(A1:A10) -> Standardabweichung
|
|
|
|
result = "TODO"; // TODO
|
|
|
|
else if (!formula.isEmpty()) {
|
|
|
|
try {
|
2024-01-06 15:58:12 +01:00
|
|
|
result = "" + calculate(formula); //calculates the result of a "normal" algebraic expression.
|
2023-12-22 21:23:58 +01:00
|
|
|
} catch(ArithmeticException ae) {
|
|
|
|
result = "exc.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cells[row][col].setValue("" + result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
* @return The sum calculated.
|
|
|
|
*/
|
|
|
|
private long sum(String startCellName, String endCellName) {
|
2024-01-08 20:41:01 +01:00
|
|
|
int werte[]=getValues(startCellName,endCellName);
|
|
|
|
|
|
|
|
int summe=0;
|
|
|
|
for(int i=0;i<werte.length;i++) {
|
|
|
|
summe+=werte[i];
|
|
|
|
}
|
|
|
|
return summe;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private long pro(String startCellName, String endCellName) {
|
|
|
|
int werte[]=getValues(startCellName,endCellName);
|
|
|
|
|
|
|
|
int produkt=0;
|
|
|
|
for(int i=0;i<werte.length;i++) {
|
|
|
|
produkt*=werte[i];
|
|
|
|
}
|
|
|
|
return produkt;
|
2023-12-22 21:23:58 +01:00
|
|
|
}
|
2024-01-08 20:41:01 +01:00
|
|
|
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
// TODO: Javadoc comments
|
|
|
|
private int[] getValues(String startCellName, String endCellName) {
|
|
|
|
int startZeile=getRow(startCellName);
|
|
|
|
int startSpalte=getCol(startCellName);
|
|
|
|
|
|
|
|
int endeZeile=getRow(endCellName);
|
|
|
|
int endeSpalte=getCol(endCellName);
|
|
|
|
|
|
|
|
int wahreStartZeile=Math.min(startZeile, endeZeile);
|
|
|
|
int wahreStartSpalte=Math.min(startSpalte, endeSpalte);
|
|
|
|
|
|
|
|
int wahreEndZeile=Math.max(startZeile, endeZeile);
|
|
|
|
int wahreEndSpalte=Math.max(startSpalte, endeSpalte);
|
|
|
|
|
|
|
|
int[] werte = new int[(wahreEndZeile - wahreStartZeile + 1) * (wahreEndSpalte - wahreStartSpalte + 1)];
|
|
|
|
|
|
|
|
int i=0;
|
|
|
|
for(int z=wahreStartZeile;z<=wahreEndZeile;z++) {
|
|
|
|
for(int s=wahreStartSpalte;s<=wahreEndSpalte;s++) {
|
|
|
|
werte[i] = Integer.parseInt(get(z, s));
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return werte;
|
|
|
|
}
|
2024-01-07 14:30:17 +01:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* continues, it is guaranteed that this is followed by an operator and either a number or a
|
|
|
|
* cell name again. It is NOT required to implement dot before dash or parentheses in formulas.
|
|
|
|
* @param formula The expression to be evaluated.
|
|
|
|
* @return The result calculated.
|
|
|
|
*/
|
|
|
|
private long calculate(String formula) throws ArithmeticException {
|
|
|
|
//System.out.println(formula);
|
|
|
|
Matcher m = Pattern.compile("([A-Z][0-9]*)|[-\\+\\*/]|[0-9]*").matcher(formula);
|
2023-12-22 21:23:58 +01:00
|
|
|
|
2024-01-07 14:30:17 +01:00
|
|
|
ArrayList<String> zahlen = new ArrayList<>();
|
|
|
|
|
|
|
|
while(m.find()) {
|
|
|
|
String s = m.group();
|
|
|
|
if (!s.isEmpty()) {
|
|
|
|
zahlen.add(s);
|
|
|
|
}
|
2024-01-06 15:58:12 +01:00
|
|
|
}
|
2023-12-22 21:23:58 +01:00
|
|
|
|
2024-01-07 14:30:17 +01:00
|
|
|
int ersteZahl = Integer.parseInt(get(zahlen.get(0)));
|
|
|
|
int zweiteZahl=0;
|
|
|
|
|
|
|
|
for (int i = 0; i < zahlen.size()-2; i+=2) {
|
|
|
|
String operant=zahlen.get(i+1);
|
|
|
|
zweiteZahl=Integer.parseInt(get(zahlen.get(i+2)));
|
|
|
|
|
|
|
|
switch(operant) {
|
|
|
|
case "+":
|
|
|
|
ersteZahl=ersteZahl+zweiteZahl;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "-":
|
|
|
|
ersteZahl=ersteZahl-zweiteZahl;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "*":
|
|
|
|
ersteZahl=ersteZahl*zweiteZahl;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "/":
|
|
|
|
ersteZahl=ersteZahl/zweiteZahl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ersteZahl;
|
|
|
|
}
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
// -----
|
|
|
|
|
2024-01-06 15:58:12 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This method checks if a given string contains one or more digits
|
|
|
|
* to make sure that the string consists of only numbers,
|
2024-01-08 02:40:29 +01:00
|
|
|
* @param input The input that is getting checked for only numbers
|
|
|
|
* @return returns true when the string contains only numbers
|
2024-01-06 15:58:12 +01:00
|
|
|
*/
|
|
|
|
public boolean containsOnlyNumbers(String input) {
|
|
|
|
return input.matches("[0-9]+");
|
|
|
|
}
|
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
public String toString() {
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
sb.append(" ");
|
|
|
|
for (int i = 0; i < cells[0].length; i++) {
|
|
|
|
sb.append(" " + (char)('A'+ i) + " | ");
|
|
|
|
}
|
|
|
|
|
|
|
|
int rc = 1;
|
|
|
|
for (Cell[] r : cells) {
|
2024-01-06 15:58:12 +01:00
|
|
|
sb.append(System.lineSeparator()); //newLine
|
|
|
|
sb.append(String.format("%2s", rc++) + ": "); //Number:
|
2023-12-22 21:23:58 +01:00
|
|
|
|
|
|
|
for (Cell c : r) {
|
2024-01-06 15:58:12 +01:00
|
|
|
sb.append(c + " | "); //cell value
|
2023-12-22 21:23:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return sb.toString();
|
|
|
|
}
|
|
|
|
|
2024-01-08 20:41:01 +01:00
|
|
|
|
|
|
|
//TODO: Javadoc Comment!!
|
|
|
|
public boolean checkIfCellExists(String cellName) {
|
|
|
|
if((getRow(cellName)+1)<=cells.length&&(getCol(cellName)+1)<=cells[0].length) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
System.err.println("Zelle '"+ cellName+ "' existiert nicht!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-22 21:23:58 +01:00
|
|
|
}
|