1
0
Fork 0
PR1-Spreadsheet/Axel/src/de/hs_mannheim/informatik/spreadsheet/Spreadsheet.java

510 lines
16 KiB
Java

package de.hs_mannheim.informatik.spreadsheet;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
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 {
Cell[][] cells;
/**
* 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) {
if(rows>99)
rows = 99;
else if(rows<1)
rows = 1;
if(cols < 1)
cols = 1;
else if(cols > 26)
cols = 26;
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
private String get(int row, int col) {
return cells[row][col].getValue();
}
public String get(String cellName) {
cellName = cellName.toUpperCase();
return get(getRow(cellName), getCol(cellName));
}
public String getCellName(int row, int col) {
return ("" + ((char) ('A' + col))) + (row+1);
}
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);
}
private int getCol(String cellName) {
return cellName.charAt(0) - 'A';
}
private int getRow(String cellName) {
if(cellName.length()>2)
return Integer.parseInt(""+cellName.charAt(1) + cellName.charAt(2));
return cellName.charAt(1) - '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 startCellName The upper left cell where data from the CSV file should be inserted.
* @return Nothing.
* @throws IOException If path does not exist.
*/
public void readCsv(String path, char separator, String startCellName) throws FileNotFoundException {
Scanner sc = new Scanner(new File(path));
ArrayList<String> memList = new ArrayList<>();
while (sc.hasNextLine()) {
memList.add(sc.nextLine());
}
String[][] memArr = new String[memList.size()][];
for(int i = 0; i < memList.size(); i++)
memArr[i] = (memList.get(i)).split(String.valueOf(separator));
for (int r = getRow(startCellName); r < memList.size(); r++)
for (int c = getCol(startCellName); c < memList.get(0).length(); c++)
if(c<memArr[r].length)
put(r, c, memArr[r][c]);
sc.close();
}
/**
* A method for saving data to a CSV file.
*
* @param path The file to write.
* @return Nothing.
* @throws 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 row,col the name of row and column of the cell
* @return Nothing.
*/
private void evaluateCell(int row, int col) {
String formula = cells[row][col].getFormula();
String result = "";
if(formula.length()>10) {
String substringStartCell1 = formula.substring(formula.length() - 6, formula.length() - 4);
String substringStartCell2 = formula.substring(formula.length() - 7, formula.length() - 5);
String substringStartCell3 = formula.substring(formula.length() - 8, formula.length() - 5);
String substringEndCell1 = formula.substring(formula.length() - 3, formula.length() - 1);
String substringEndCell2 = formula.substring(formula.length() - 4, formula.length() - 1);
if (formula.startsWith("SUMME("))// e.g. SUMME(A3:A8)
if (formula.length() < 13)
result = "" + sum(substringStartCell1, substringEndCell1);
else if (formula.length() < 14)
result = "" + sum(substringStartCell2, substringEndCell2);
else if (formula.length() < 15)
result = "" + sum(substringStartCell3, substringEndCell2);
else
result = "0";
else if (formula.startsWith("PRODUKT(")) // e.g. PRODUKT(A3:B9)
if (formula.length() < 15)
result = "" + product(substringStartCell1, substringEndCell1);
else if (formula.length() < 16 && !substringEndCell2.contains(":"))
result = "" + product(substringStartCell2, substringEndCell2);
else if (formula.length() < 17)
result = "" + product(substringStartCell3, substringEndCell2);
else
result = "0";
else if (formula.startsWith("MITTELWERT(")) // e.g. MITTELWERT(A3:A5)
if (formula.length() < 17)
result = "" + average(substringStartCell1, substringEndCell1);
else if (formula.length() < 18)
result = "" + average(substringStartCell2, substringEndCell2);
else if (formula.length() < 19)
result = "" + average(substringStartCell3, substringEndCell2);
else
result = "0";
else if (formula.startsWith("STABW(")) // e.g. STABW(C6:D8) -> Standardabweichung
if (formula.length() < 13)
result = "" + standardDeviation(substringStartCell1, substringEndCell1);
else if (formula.length() < 14)
result = "" + standardDeviation(substringStartCell2, substringEndCell2);
else if (formula.length() < 15)
result = "" + standardDeviation(substringStartCell3, substringEndCell2);
else
result = "0";
else if (formula.startsWith("MIN(")) // e.g. MIN(C1:H13) -> kleinster Wert
if (formula.length() < 11)
result = "" + min(substringStartCell1, substringEndCell1);
else if (formula.length() < 12)
result = "" + min(substringStartCell2, substringEndCell2);
else if (formula.length() < 13)
result = "" + min(substringStartCell3, substringEndCell2);
else
result = "0";
else if (formula.startsWith("MAX(")) // e.g. MAX(A1:A10) -> größter Wert
if (formula.length() < 11)
result = "" + max(substringStartCell1, substringEndCell1);
else if (formula.length() < 12)
result = "" + max(substringStartCell2, substringEndCell2);
else if (formula.length() < 13)
result = "" + max(substringStartCell3, substringEndCell2);
else
result = "0";
}
else if (!formula.isEmpty()) {
try {
result = "" + calculate(formula);
} 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) {
long result = 0;
for(int r = getRow(startCellName); r<getRow(endCellName)+1; r++)
for(int c = getCol(startCellName); c<getCol(endCellName)+1; c++)
try {
result += Long.parseLong(cells[r][c].getValue());
} catch (NumberFormatException | ArrayIndexOutOfBoundsException a ){}
return result;
}
private long product(String startCellName, String endCellName) {
long result = 0;
int counter = 0;
for(int r = getRow(startCellName); r<getRow(endCellName)+1; r++)
for(int c = getCol(startCellName); c<getCol(endCellName)+1; c++)
try {
if(counter>0)
result *= Long.parseLong(cells[r][c].getValue());
else {
result = Long.parseLong(cells[r][c].getValue());
counter++;
}
} catch (NumberFormatException | ArrayIndexOutOfBoundsException a ){}
return result;
}
private double average(String startCellName, String endCellName) {
double result = 0;
int counter = 0;
for(int r = getRow(startCellName); r<getRow(endCellName)+1; r++)
for(int c = getCol(startCellName); c<getCol(endCellName)+1; c++)
try {
result += Long.parseLong(cells[r][c].getValue());
counter ++;
} catch (NumberFormatException | ArrayIndexOutOfBoundsException a ){}
result /= counter;
return result;
}
private double standardDeviation(String startCellName, String endCellName){
ArrayList<String> cellNames = new ArrayList<>();
double avg = 0;
int counter = 0;
for(int r = getRow(startCellName); r<getRow(endCellName); r++)
for(int c = getCol(startCellName); c<getCol(endCellName); c++)
if(!(cells[r][c].isEmpty())) {
avg += Double.parseDouble(cells[r][c].getValue());
counter++;
cellNames.add(getCellName(r, c));
}
avg /= counter;
//average/ add cell names to list
System.out.println(cellNames);
ArrayList<String> copyCellNames = new ArrayList<>(cellNames);
double[] frequency = new double[cellNames.size()];
Arrays.fill(frequency,1);
ArrayList<Double> relativeFrequency = new ArrayList<>();
double mem = 0;
for(int i = 0; i< cellNames.size(); i++) {
for(int t = 0; t<cellNames.size(); t++){
if(!((get(cellNames.get(i)).isEmpty())&&(get(cellNames.get(t)).isEmpty()))&&(t!=i)&&(Double.parseDouble(get(cellNames.get(i))))==(Double.parseDouble(get(cellNames.get(t))))){
cellNames.remove(t);
frequency[i] ++;
t = 0;
}
}
}
//delete all duplicates
for(int i = 0; i< cellNames.size(); i++)
relativeFrequency.add(i,frequency[i]/copyCellNames.size());
System.out.println("CellNames: "+cellNames);
if(get(cellNames.get(0)).isEmpty())
mem = ((0 - avg)*(0 - avg))
* relativeFrequency.get(0);
else
mem = ((Double.parseDouble(get(cellNames.get(0))) - avg)*(Double.parseDouble(get(cellNames.get(0))) - avg))
* relativeFrequency.get(0);
for(int i = 1; i<cellNames.size(); i++)
if(get(cellNames.get(i)).isEmpty())
mem += ((0 - avg)*(0 - avg))
* relativeFrequency.get(i);
else
mem += ((Double.parseDouble(get(cellNames.get(i))) - avg)*(Double.parseDouble(get(cellNames.get(i))) - avg))
* relativeFrequency.get(i);
return Math.sqrt(mem);
//standardDeviation formula
}
private long min(String startCellName, String endCellName){
ArrayList<String> cellNames = new ArrayList<>();
for(int r = getRow(startCellName); r<getRow(endCellName)+1; r++)
for(int c = getCol(startCellName); c<getCol(endCellName)+1; c++)
if(!cells[r][c].isEmpty())
cellNames.add(getCellName(r, c));
long result = Long.parseLong(get(cellNames.get(0)));
for(int i = 0; i< cellNames.size(); i++)
if(result>Long.parseLong(get(cellNames.get(i))))
result = Long.parseLong(get(cellNames.get(i)));
return result;
}
private long max(String startCellName, String endCellName){
ArrayList<String> cellNames = new ArrayList<>();
for(int r = getRow(startCellName); r<getRow(endCellName)+1; r++)
for(int c = getCol(startCellName); c<getCol(endCellName)+1; c++)
if(!cells[r][c].isEmpty())
cellNames.add(getCellName(r, c));
long result = Long.parseLong(get(cellNames.get(0)));
for(int i = 0; i< cellNames.size(); i++)
if(result<Long.parseLong(get(cellNames.get(i))))
result = Long.parseLong(get(cellNames.get(i)));
return result;
}
/**
* 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 {
Matcher m = Pattern.compile("([A-Z][0-9]*)|[-+*/]|[0-9]+").matcher(formula);
long result = 0;
long currentOperand = 0;
String currentOperator = "+";
boolean firstOperator = true;
while (m.find()) {
String s = m.group();
if (s.matches(("[0-9]+")))
currentOperand = Long.parseLong(s);
else if ((s.matches("[A-Z][1-9]*") || s.matches("[A-Z][1-9]*+")) && get(s).isEmpty())
currentOperand = 0;
else if ((s.matches("[A-Z][1-9]*") || s.matches("[A-Z][1-9]*+"))) {
currentOperand = Long.parseLong(get(s));
} else {
if (!firstOperator) {
result = evaluateOperator(result, currentOperand, currentOperator);
} else {
result = currentOperand;
firstOperator = false;
}
currentOperator = s;
}
}
if (!firstOperator) {
result = evaluateOperator(result, currentOperand, currentOperator);
} else
result = currentOperand;
return result;
}
private long evaluateOperator(long result, long currentOperand, String currentOperator) {
switch (currentOperator) {
case "+":
return result + currentOperand;
case "-":
return result - currentOperand;
case "*":
return result * currentOperand;
case "/":
return result / currentOperand;
default:
throw new IllegalArgumentException("Invalid operator: " + currentOperator);
}
}
///--- ui methods
public void updateSpreadsheet() {
for (int r = 0; r < cells.length; r++)
for (int c = 0; c < cells[r].length; c++)
evaluateCell(r, c);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(System.lineSeparator());
sb.append(" |");
for (int i = 0; i < cells[0].length; i++) {
sb.append(" " + (char) ('A' + i) + " |");
}
int rc = 1;
for (int r = 0; r < cells.length; r++) {
sb.append(System.lineSeparator());
for (int i = 0; i < cells[0].length; i++) {
sb.append("----------------");
}
sb.append("------");
sb.append(System.lineSeparator());
sb.append(String.format(" " + "%2s", rc++) + " |");
for (int c = 0; c < cells[r].length; c++) {
if((cells[r][c].getValue()).length()>13)
sb.append(" " + String.format("%13s", cells[r][c].getValue()).substring(0,13) + " |");
else
sb.append(" " + String.format("%13s", cells[r][c].getValue()) + " |");
}
}
sb.append(System.lineSeparator());
for (int i = 0; i < cells[0].length; i++) {
sb.append("----------------");
}
sb.append("------");
sb.append(System.lineSeparator());
return sb.toString();
}
public String toStringShowFormula() {
StringBuilder sb = new StringBuilder();
sb.append(System.lineSeparator());
sb.append(" |");
for (int i = 0; i < cells[0].length; i++) {
sb.append(" " + (char) ('A' + i) + " |");
}
int rc = 1;
for (int r = 0; r < cells.length; r++) {
sb.append(System.lineSeparator());
for (int i = 0; i < cells[0].length; i++) {
sb.append("----------------");
}
sb.append("------");
sb.append(System.lineSeparator());
sb.append(String.format(" " + "%2s", rc++) + " |");
for (int c = 0; c < cells[r].length; c++) {
if((cells[r][c].getFormula()).length()>13)
sb.append(" " + String.format("%13s", cells[r][c].getFormula()).substring(0,13) + " |");
else
sb.append(" " + String.format("%13s", cells[r][c].getFormula()) + " |");
}
}
sb.append(System.lineSeparator());
for (int i = 0; i < cells[0].length; i++) {
sb.append("----------------");
}
sb.append("------");
sb.append(System.lineSeparator());
return sb.toString();
}
}