353 lines
10 KiB
Java
353 lines
10 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.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;
|
|
}
|
|
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 Integer.parseInt(cellName.substring(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 path) throws FileNotFoundException {
|
|
Scanner file=new Scanner(new File(path));
|
|
String[] rowRead;
|
|
int row=0;
|
|
while(file.hasNextLine()) {
|
|
rowRead=file.nextLine().split(",");
|
|
for(int col=0;col<=rowRead.length-1;col++) {
|
|
put(row,col,rowRead[col]);
|
|
}
|
|
row++;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
out.print(cell.getValue());
|
|
if (cell != row[cells.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 = "";
|
|
|
|
if (formula.startsWith("SUMME("))
|
|
if(formula.length()==14)
|
|
result = "" + sum(formula.substring(6, 9), formula.substring(10, 13));
|
|
else if(formula.length()==13)
|
|
result = "" + sum(formula.substring(6, 8), formula.substring(9, 12));
|
|
else
|
|
result = "" + sum(formula.substring(6, 8), formula.substring(9, 11));
|
|
else if (formula.startsWith("PRODUKT("))
|
|
if(formula.length()==16)
|
|
result = "" + product(formula.substring(8, 11), formula.substring(12, 15));
|
|
else if(formula.length()==15)
|
|
result = "" + product(formula.substring(8, 10), formula.substring(11, 14));
|
|
else
|
|
result = "" + product(formula.substring(8, 10), formula.substring(11, 13));
|
|
else if (formula.startsWith("MITTELWERT("))
|
|
if(formula.length()==19)
|
|
result = "" + average(formula.substring(11, 14), formula.substring(15, 18));
|
|
else if(formula.length()==18)
|
|
result = "" + average(formula.substring(11, 13), formula.substring(14, 17));
|
|
else
|
|
result = "" + average(formula.substring(11, 13), formula.substring(14, 16));
|
|
else if (formula.startsWith("STABW("))
|
|
if(formula.length()==14)
|
|
result = "" + standardDeviation(formula.substring(6, 9), formula.substring(10, 13));
|
|
else if(formula.length()==13)
|
|
result = "" + standardDeviation(formula.substring(6, 8), formula.substring(9, 12));
|
|
else
|
|
result = "" + standardDeviation(formula.substring(6, 8), formula.substring(9, 11));
|
|
else if (formula.startsWith("MAX("))
|
|
if(formula.length()==12)
|
|
result = "" + max(formula.substring(4, 7), formula.substring(8, 11));
|
|
else if(formula.length()==11)
|
|
result = "" + max(formula.substring(4, 6), formula.substring(7, 10));
|
|
else
|
|
result = "" + max(formula.substring(4, 6), formula.substring(7, 9));
|
|
else if (formula.startsWith("MIN("))
|
|
if(formula.length()==12)
|
|
result = "" + min(formula.substring(4, 7), formula.substring(8, 11));
|
|
else if(formula.length()==11)
|
|
result = "" + min(formula.substring(4, 6), formula.substring(7, 10));
|
|
else
|
|
result = "" + min(formula.substring(4, 6), formula.substring(7, 9));
|
|
else if (!formula.isEmpty()) {
|
|
try {
|
|
result = "" + calculate(formula);
|
|
} catch(ArithmeticException ae) {
|
|
result = "exc.";
|
|
}
|
|
}
|
|
|
|
cells[row][col].setValue("" + result);
|
|
}
|
|
//calculations for cell blocks
|
|
private long sum(String startCellName, String endCellName) {
|
|
long sum=0;
|
|
for(int i=startCellName.charAt(0)-'A'; i<=endCellName.charAt(0)-'A'; i++) {
|
|
for(int j=Integer.parseInt(startCellName.substring(1))-1; j<=Integer.parseInt(endCellName.substring(1))-1; j++) {
|
|
if(cells[j][i].isEmpty())
|
|
continue;
|
|
sum+=Integer.parseInt(cells[j][i].getValue());
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
private long product(String startCellName, String endCellName) {
|
|
long product=1;
|
|
for(int i=startCellName.charAt(0)-'A'; i<=endCellName.charAt(0)-'A'; i++) {
|
|
for(int j=Integer.parseInt(startCellName.substring(1))-1; j<=Integer.parseInt(endCellName.substring(1))-1; j++) {
|
|
if(cells[j][i].isEmpty())
|
|
return 0;
|
|
product=product*Integer.parseInt(cells[j][i].getValue());
|
|
}
|
|
}
|
|
return product;
|
|
}
|
|
private long average(String startCellName, String endCellName) {
|
|
long average=0;
|
|
long counter=0;
|
|
for(int i=startCellName.charAt(0)-'A'; i<=endCellName.charAt(0)-'A'; i++) {
|
|
for(int j=Integer.parseInt(startCellName.substring(1))-1; j<=Integer.parseInt(endCellName.substring(1))-1; j++) {
|
|
if(cells[j][i].isEmpty())
|
|
continue;
|
|
average+=Integer.parseInt(cells[j][i].getValue());
|
|
counter++;
|
|
}
|
|
}
|
|
return (long) average/counter;
|
|
}
|
|
private long standardDeviation(String startCellName, String endCellName) {
|
|
long average=average(startCellName,endCellName);
|
|
long counter=0;
|
|
long value=0;
|
|
for(int i=startCellName.charAt(0)-'A'; i<=endCellName.charAt(0)-'A'; i++) {
|
|
for(int j=Integer.parseInt(startCellName.substring(1))-1; j<=Integer.parseInt(endCellName.substring(1))-1; j++) {
|
|
if(cells[j][i].isEmpty())
|
|
continue;
|
|
value+=Math.pow(Integer.parseInt(cells[j][i].getValue())-average,2);
|
|
counter++;
|
|
}
|
|
}
|
|
return (long) Math.sqrt(value/(counter-1));
|
|
}
|
|
private int max(String startCellName,String endCellName) {
|
|
int max=Integer.MIN_VALUE;
|
|
for(int i=startCellName.charAt(0)-'A'; i<=endCellName.charAt(0)-'A'; i++) {
|
|
for(int j=Integer.parseInt(startCellName.substring(1))-1; j<=Integer.parseInt(endCellName.substring(1))-1; j++) {
|
|
if(cells[j][i].isEmpty())
|
|
continue;
|
|
else if(max<Integer.parseInt(cells[j][i].getValue()))
|
|
max=Integer.parseInt(cells[j][i].getValue());
|
|
}
|
|
}
|
|
return max;
|
|
}
|
|
private int min(String startCellName,String endCellName) {
|
|
int min=Integer.MAX_VALUE;
|
|
for(int i=startCellName.charAt(0)-'A'; i<=endCellName.charAt(0)-'A'; i++) {
|
|
for(int j=Integer.parseInt(startCellName.substring(1))-1; j<=Integer.parseInt(endCellName.substring(1))-1; j++) {
|
|
if(cells[j][i].isEmpty())
|
|
continue;
|
|
else if(min>Integer.parseInt(cells[j][i].getValue()))
|
|
min=Integer.parseInt(cells[j][i].getValue());
|
|
}
|
|
}
|
|
return min;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
boolean first=true;
|
|
String operation="";
|
|
|
|
while (m.find()) { // m.find() must always be used before m.group()
|
|
String s = m.group();
|
|
if(!s.isEmpty()) {
|
|
if((int)s.charAt(0)>=(int)'A'&&(int)s.charAt(0)<=(int)'Z' && get(s).isEmpty())
|
|
continue;
|
|
if(first && (int)s.charAt(0)>=(int)'A'&&(int)s.charAt(0)<=(int)'Z') {
|
|
res=Integer.parseInt(get(s));
|
|
first=false;
|
|
}else if(first) {
|
|
res=Integer.parseInt(s);
|
|
first=false;
|
|
}else if(s.equals("+")){
|
|
operation="+";
|
|
}else if(s.equals("-")){
|
|
operation="-";
|
|
}else if(s.equals("*")){
|
|
operation="*";
|
|
}else if(s.equals("/")){
|
|
operation="/";
|
|
}else if((int)s.charAt(0)>=(int)'A'&&(int)s.charAt(0)<=(int)'Z'){
|
|
switch(operation) {
|
|
case("+"):
|
|
res+=Integer.parseInt(get(s));
|
|
break;
|
|
case("-"):
|
|
res-=Integer.parseInt(get(s));
|
|
break;
|
|
case("*"):
|
|
res*=Integer.parseInt(get(s));
|
|
break;
|
|
case("/"):
|
|
res/=Integer.parseInt(get(s));
|
|
break;
|
|
}
|
|
}else {
|
|
switch(operation) {
|
|
case("+"):
|
|
res+=Integer.parseInt(s);
|
|
break;
|
|
case("-"):
|
|
res-=Integer.parseInt(s);
|
|
break;
|
|
case("*"):
|
|
res*=Integer.parseInt(s);
|
|
break;
|
|
case("/"):
|
|
res/=Integer.parseInt(s);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
} |