613 lines
14 KiB
Java
613 lines
14 KiB
Java
package de.hs_mannheim.informatik.spreadsheet;
|
|
|
|
import java.io.FileNotFoundException;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Scanner;
|
|
|
|
/**
|
|
* 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) {
|
|
|
|
// TODO limit the maximum size on 99 (1..99) rows and 26 (A..Z) columns
|
|
|
|
//limit maximum size
|
|
|
|
if(rows>99) {
|
|
|
|
rows=99;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
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) {
|
|
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 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());
|
|
}
|
|
|
|
ArrayList <int[]> formulas = new ArrayList<>();
|
|
|
|
for (int r = 0; r < fileRows.size(); r++) {
|
|
String row = fileRows.get(r);
|
|
String[] cells = row.split(separator);
|
|
|
|
for (int c = 0; c < cells.length; c++) {
|
|
String cellFill = cells[c].toUpperCase();
|
|
|
|
if (cellFill.startsWith("=")){
|
|
formulas.add(new int[]{r, c});
|
|
}
|
|
else {
|
|
put(r, c, cellFill);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
for (int[] formulaAt : formulas){
|
|
int r = formulaAt[0];
|
|
int c = formulaAt[1];
|
|
System.out.printf("Formula at %d-%d %n", r, c);
|
|
|
|
String row = fileRows.get(r);
|
|
String formulaToFill = row.split(separator)[c];
|
|
|
|
put(r, c, formulaToFill);
|
|
}
|
|
|
|
sc.close();
|
|
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* 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 = "";
|
|
String sum1;
|
|
String sum2;
|
|
|
|
if (formula.startsWith("SUMME(")) // e.g. SUMME(A3:A8) //SUMME(A13:B22)
|
|
result = "" + sum(getStartCellName(formula), getEndCellName(formula)); // TODO adapt to cells with two digits
|
|
|
|
else if (formula.startsWith("PRODUKT(")) // e.g. PRODUKT(A3:B9)
|
|
result = "" + prod(getStartCellName(formula),getEndCellName(formula)); // TODO
|
|
else if (formula.startsWith("MITTELWERT(")) // e.g. MITTELWERT(A3:A5)
|
|
result = ""+ average(getStartCellName(formula),getEndCellName(formula)); // TODO
|
|
else if (formula.startsWith("STABW(")) // e.g. STABW(C6:D8) -> Standardabweichung
|
|
result = "" + stDev(getStartCellName(formula),getEndCellName(formula)); // TODO
|
|
else if (formula.startsWith("MIN(")) // e.g. MIN(C13:H13) -> größter Wert
|
|
result = ""+ min(getStartCellName(formula), getEndCellName(formula));
|
|
else if (formula.startsWith("MAX(")) // e.g. MAX(A1:A10) -> Standardabweichung
|
|
result = ""+ max(getStartCellName(formula), getEndCellName(formula)); // TODO
|
|
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 ofODO the rectangle.
|
|
* @return The sum calculated.
|
|
*/
|
|
private long sum(String startCellName, String endCellName) {
|
|
// TODO implement
|
|
|
|
int startRow=0;
|
|
int startCol=0;
|
|
|
|
int endRow=0;
|
|
int endCol;
|
|
long sum=0;
|
|
int temp;
|
|
|
|
startRow= getRow(startCellName);
|
|
startCol= getCol(startCellName);
|
|
|
|
endRow= getRow(endCellName);
|
|
endCol= getCol(endCellName);
|
|
|
|
for(int i= startRow; i<=endRow; i++) {
|
|
for(int j=startCol; j<=endCol; j++) {
|
|
|
|
temp= Integer.parseInt(cells[i][j].getValue());
|
|
sum=sum+temp;
|
|
|
|
}
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
/**
|
|
* Method for calculating the product 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 ofODO the rectangle.
|
|
* @return The product calculated.
|
|
*/
|
|
|
|
private long prod(String startCellName, String endCellName) {
|
|
|
|
int startRow=0;
|
|
int startCol=0;
|
|
|
|
int endRow=0;
|
|
int endCol;
|
|
long prod=1;
|
|
long temp;
|
|
|
|
startRow= getRow(startCellName);
|
|
startCol= getCol(startCellName);
|
|
|
|
endRow= getRow(endCellName);
|
|
endCol= getCol(endCellName);
|
|
|
|
for(int i= startRow; i<=endRow; i++) {
|
|
for(int j=startCol; j<=endCol; j++) {
|
|
|
|
temp= Integer.parseInt(cells[i][j].getValue());
|
|
prod=prod*temp;
|
|
}
|
|
}
|
|
return prod;
|
|
}
|
|
|
|
|
|
/**
|
|
* Method for calculating the average 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 ofODO the rectangle.
|
|
* @return The average calculated.
|
|
*/
|
|
|
|
private long average(String startCellName, String endCellName) {
|
|
|
|
int startRow=0;
|
|
int startCol=0;
|
|
|
|
int endRow=0;
|
|
int endCol;
|
|
long sum=0;
|
|
long temp;
|
|
int part=0;
|
|
long avg=0;;
|
|
|
|
startRow= getRow(startCellName);
|
|
startCol= getCol(startCellName);
|
|
|
|
endRow= getRow(endCellName);
|
|
endCol= getCol(endCellName);
|
|
|
|
for(int i= startRow; i<=endRow; i++) {
|
|
for(int j=startCol; j<=endCol; j++) {
|
|
|
|
temp= Integer.parseInt(cells[i][j].getValue());
|
|
sum=sum+temp;
|
|
part++;
|
|
}
|
|
}
|
|
avg=sum/part;
|
|
|
|
return avg;
|
|
}
|
|
|
|
/**
|
|
* Method for calculating the minimum of a rectangular block of cells
|
|
* @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 minimum.
|
|
*/
|
|
|
|
private int min(String startCellName, String endCellName) {
|
|
int startRow=0;
|
|
int startCol=0;
|
|
|
|
int endRow=0;
|
|
int endCol;
|
|
int min;
|
|
int temp;
|
|
|
|
startRow= getRow(startCellName);
|
|
startCol= getCol(startCellName);
|
|
|
|
endRow= getRow(endCellName);
|
|
endCol= getCol(endCellName);
|
|
|
|
min=Integer.parseInt(cells[startRow][startCol].getValue());
|
|
|
|
for(int i= startRow; i<=endRow; i++) {
|
|
for(int j=startCol; j<=endCol; j++) {
|
|
|
|
temp= Integer.parseInt(cells[i][j].getValue());
|
|
if(min>temp) {
|
|
min=temp;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
return min;
|
|
}
|
|
|
|
|
|
/**
|
|
* Method for calculating the maximum of a rectangular block of cells
|
|
* @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 the rectangle.
|
|
* @return The maximum.
|
|
*/
|
|
|
|
private int max(String startCellName, String endCellName) {
|
|
int startRow=0;
|
|
int startCol=0;
|
|
|
|
int endRow=0;
|
|
int endCol;
|
|
int max;
|
|
int temp;
|
|
|
|
startRow= getRow(startCellName);
|
|
startCol= getCol(startCellName);
|
|
|
|
endRow= getRow(endCellName);
|
|
endCol= getCol(endCellName);
|
|
|
|
max=Integer.parseInt(cells[startRow][startCol].getValue());
|
|
|
|
for(int i= startRow; i<=endRow; i++) {
|
|
for(int j=startCol; j<=endCol; j++) {
|
|
|
|
temp= Integer.parseInt(cells[i][j].getValue());
|
|
if(max<temp) {
|
|
max=temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
/**
|
|
* Method for calculating the standard deviation of a rectangular block of cells
|
|
* @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 the rectangle.
|
|
* @return The standard deviation calculated.
|
|
*/
|
|
|
|
private long stDev(String startCellName, String endCellName) {
|
|
|
|
int startRow=0;
|
|
int startCol=0;
|
|
|
|
int endRow=0;
|
|
int endCol;
|
|
long temp;
|
|
int part=0;
|
|
long avg=0;;
|
|
long dev=0;
|
|
|
|
|
|
startRow= getRow(startCellName);
|
|
startCol= getCol(startCellName);
|
|
|
|
endRow= getRow(endCellName);
|
|
endCol= getCol(endCellName);
|
|
ArrayList<Long> elements = new ArrayList<>();
|
|
|
|
avg= average(startCellName, endCellName);
|
|
|
|
|
|
for(int i= startRow; i<=endRow; i++) {
|
|
for(int j=startCol; j<=endCol; j++) {
|
|
|
|
temp= Integer.parseInt(cells[i][j].getValue());
|
|
temp=temp-avg;
|
|
temp= temp*temp;
|
|
elements.add(temp);
|
|
|
|
|
|
}
|
|
}
|
|
avg=0;
|
|
|
|
for(int i=0; i<elements.size(); i++) {
|
|
avg=avg+elements.get(i);
|
|
part++;
|
|
}
|
|
avg=avg/part;
|
|
dev= (int) Math.sqrt(avg);
|
|
|
|
return dev;
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* 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 res = 0;
|
|
String cellName="";
|
|
String operand="";
|
|
long value=0;
|
|
int row;
|
|
int col;
|
|
int lock=0;
|
|
int cnt=0;
|
|
// TODO implement
|
|
|
|
|
|
while (m.find()) { // m.find() must always be used before m.group()
|
|
String s = m.group();
|
|
|
|
|
|
|
|
|
|
if (!s.isEmpty()) {
|
|
// System.out.println(s);
|
|
|
|
if(Character.isLetter(s.charAt(0))) {
|
|
cellName= s;
|
|
row=getRow(cellName);
|
|
col=getCol(cellName);
|
|
value= Integer.parseInt(cells[row][col].getValue());
|
|
lock=0;
|
|
|
|
}else if(Character.isDigit(s.charAt(0))) {
|
|
value= Integer.parseInt(s);
|
|
|
|
lock=0;
|
|
}else {
|
|
operand = s;
|
|
System.out.println(operand);
|
|
lock=1;
|
|
}
|
|
|
|
if(cnt==0)
|
|
res=value;
|
|
|
|
if(lock==0) {
|
|
switch(operand) {
|
|
|
|
case "+": res+= value;
|
|
break;
|
|
|
|
case "-": res-= value;
|
|
|
|
break;
|
|
|
|
case "*": res*= value;
|
|
break;
|
|
case "/": if(value==0) {
|
|
System.out.println("division by 0 is not possible");
|
|
}else res/= value;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
|
|
return res;
|
|
}
|
|
|
|
// -----
|
|
|
|
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) {
|
|
sb.append(System.lineSeparator());
|
|
sb.append(String.format("%2s", rc++) + ": ");
|
|
|
|
for (Cell c : r) {
|
|
sb.append(c + " | ");
|
|
}
|
|
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
|
|
/**
|
|
* Method to get the correct start cell name if it has more then 1 digit
|
|
* @param formula The formula in the cell
|
|
* @return name of the startCell
|
|
*/
|
|
|
|
|
|
public String getStartCellName(String formula) {
|
|
|
|
String startCell="";
|
|
int a=0;
|
|
|
|
for(int i=0; i<formula.length(); i++) {
|
|
if(formula.charAt(i)=='(') {
|
|
a=i;
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if(Character.isDigit(formula.charAt(a+3))) {
|
|
startCell=formula.substring(a+1, a+4);
|
|
}else startCell=formula.substring(a+1, a+3);
|
|
|
|
|
|
System.out.println(startCell);
|
|
|
|
return startCell;
|
|
|
|
}
|
|
|
|
/**
|
|
* Method to get the correct end cell name if it has more then 1 digit
|
|
* @param formula The formula in the cell
|
|
* @return name of the endCell
|
|
*/
|
|
|
|
public String getEndCellName(String formula) {
|
|
|
|
String endCell="";
|
|
int a=0;
|
|
|
|
for(int i=0; i<formula.length(); i++) {
|
|
if(formula.charAt(i)=='(') {
|
|
a=i;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if(Character.isDigit(formula.charAt(a+3))) {
|
|
if(Character.isDigit(formula.charAt(a+7))) {
|
|
endCell=formula.substring(a+5, a+8);
|
|
}else endCell=formula.substring(a+5, a+7);
|
|
}else{
|
|
if(Character.isDigit(formula.charAt(a+6))) {
|
|
endCell=formula.substring(a+4, a+7);
|
|
}else endCell=formula.substring(a+4, a+6);
|
|
|
|
}
|
|
System.out.println(endCell);
|
|
|
|
|
|
|
|
return endCell;
|
|
}
|
|
|
|
|
|
|
|
} |