forked from hummel/PR1-Spreadsheet
430 lines
13 KiB
Java
430 lines
13 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.regex.Matcher;
|
||
import java.util.regex.Pattern;
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
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) {
|
||
|
||
int rowWithMax99= Math.min(rows, 99);
|
||
int colWithMax26= Math.min(cols, 26);
|
||
|
||
cells = new Cell[rowWithMax99][colWithMax26];
|
||
|
||
for (int r = 0; r < rowWithMax99; r++)
|
||
for (int c = 0; c < colWithMax26; c++)
|
||
cells[r][c] = new Cell();
|
||
}
|
||
/**
|
||
* Sets a Spreadsheet of size rows * cols.
|
||
* @param rows number of rows
|
||
* @param cols number of columns
|
||
*/
|
||
public void changeSize(int rows, int cols) {
|
||
cells = new Cell[0][0];
|
||
|
||
int rowWithMax99= Math.min(rows, 99);
|
||
int colWithMax26= Math.min(cols, 26);
|
||
|
||
cells = new Cell[rowWithMax99][colWithMax26];
|
||
|
||
for (int r = 0; r < rowWithMax99; r++)
|
||
for (int c = 0; c < colWithMax26; c++)
|
||
cells[r][c] = new Cell();
|
||
}
|
||
|
||
// -----
|
||
/*
|
||
* @param str A String with multiple numbers.
|
||
* @return true if there are ONLY numbers in this sting.
|
||
*/
|
||
private boolean isNumber(String str) {
|
||
return str.matches("[0-9]+");
|
||
}
|
||
private boolean isCar(String str) {//Brum
|
||
return str.matches("[-\\+\\*/]+");
|
||
}
|
||
// 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));
|
||
}
|
||
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);
|
||
}
|
||
public int getCol(String cellName) {
|
||
return cellName.charAt(0) - 'A';
|
||
}
|
||
public int getRow(String cellName) {
|
||
if (cellName.length()==3) {
|
||
int Row = 0;
|
||
Row +=((cellName.charAt(1)-'0')*10);
|
||
Row +=(cellName.charAt(2)-'0');
|
||
return Row-1;
|
||
}else {
|
||
return cellName.charAt(1) - '1';
|
||
}
|
||
}
|
||
// -----
|
||
// business logic
|
||
|
||
/**
|
||
* A method for reading in data from a CSV file.
|
||
* @param path The file to read.
|
||
* @param startCellName 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, String startCellName) throws FileNotFoundException {
|
||
Scanner csvIn = new Scanner(new File(path));
|
||
int index= 0;
|
||
|
||
while(csvIn.hasNextLine()){
|
||
String line =csvIn.nextLine();
|
||
String all="";
|
||
int filled=0;
|
||
for(int i = 0;i<line.length();i++) {
|
||
if(line.charAt(i)==',') {
|
||
if(!all.isBlank()) {
|
||
put(index,i-filled,all);
|
||
all="";
|
||
}
|
||
}else {
|
||
filled++;
|
||
all+=line.charAt(i);
|
||
}
|
||
|
||
}
|
||
index++;
|
||
}
|
||
|
||
csvIn.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(PrintWriter out) throws FileNotFoundException {
|
||
|
||
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 = "";
|
||
int offset = 0;
|
||
int offsetEnd = 0;
|
||
int diff=0;
|
||
for(int i = 0; i<formula.length();i++) {
|
||
if(formula.charAt(i)=='(') {
|
||
offset=i+1;
|
||
}else if(formula.charAt(i)==')') {
|
||
offsetEnd=i;
|
||
}
|
||
else if(formula.charAt(i)==':') {
|
||
diff=i;
|
||
}
|
||
}
|
||
if (formula.startsWith("SUMME(")) { // e.g. SUMME(A3:A8)
|
||
result += sum(formula.substring(offset, diff), formula.substring(diff+1,offsetEnd));
|
||
}else if (formula.startsWith("PRODUKT(")) { // e.g. PRODUKT(A3:B9)
|
||
result += prod(formula.substring(offset, diff), formula.substring(diff+1, offsetEnd));
|
||
}else if (formula.startsWith("MID(")) { // e.g. MITTELWERT(A3:A5)
|
||
result += mid(formula.substring(offset, diff), formula.substring(diff+1, offsetEnd));
|
||
}else if (formula.startsWith("STABW(")) { // e.g. STABW(C6:D8) -> Standardabweichung
|
||
result += stabw(formula.substring(offset, diff), formula.substring(diff+1, offsetEnd));
|
||
}else if (formula.startsWith("MIN(")) {// e.g. MIN(C13:H13) -> kleinster Wert
|
||
result += min(formula.substring(offset, diff), formula.substring(diff+1, offsetEnd));
|
||
}else if (formula.startsWith("MAX(")) { // e.g. MAX(A1:A10) -> gr<67><72>ter Wert
|
||
result += max(formula.substring(offset, diff), formula.substring(diff+1, offsetEnd));
|
||
}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.
|
||
*/
|
||
public long sum(String startCellName, String endCellName) {
|
||
long res = 0;
|
||
for(int j = getRow(startCellName); j<= (getRow(endCellName));j++){
|
||
for(int i = getCol(startCellName); i<= (getCol(endCellName));i++) {
|
||
if(!cells[j][i].getValue().isBlank()) {
|
||
res += Long.parseLong(cells[j][i].getValue());
|
||
}
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
/**
|
||
* 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 of the rectangle.
|
||
* @return The product calculated.
|
||
*/
|
||
public long prod(String startCellName, String endCellName) {
|
||
long res = 1;
|
||
boolean anyNumbers= false;
|
||
for(int j = getRow(startCellName); j<= (getRow(endCellName));j++){
|
||
for(int i = getCol(startCellName); i<= (getCol(endCellName));i++) {
|
||
if(!cells[j][i].getValue().isBlank()) {
|
||
res*= Long.parseLong(cells[j][i].getValue());
|
||
anyNumbers = true;
|
||
}
|
||
}
|
||
}
|
||
if(anyNumbers)
|
||
return res;
|
||
else
|
||
return 0;
|
||
}
|
||
/**
|
||
* Method for calculating the Mid Value 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 Mid Value calculated.
|
||
*/
|
||
public long mid(String startCellName, String endCellName) {
|
||
long res = 0;
|
||
int Numbers= 0;
|
||
for(int j = getRow(startCellName); j<= (getRow(endCellName));j++){
|
||
for(int i = getCol(startCellName); i<= (getCol(endCellName));i++) {
|
||
if(!cells[j][i].getValue().isBlank()) {
|
||
res+= Long.parseLong(cells[j][i].getValue());
|
||
Numbers++;
|
||
}
|
||
}
|
||
}
|
||
res/=Numbers;
|
||
return res;
|
||
}
|
||
/**
|
||
* Method for calculating the Standard deviation 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 Standard deviation calculated.
|
||
*/
|
||
public long stabw(String startCellName, String endCellName) {
|
||
long res=0;
|
||
long midSum=0;
|
||
//mid(startCellName,endCellName)
|
||
//mid ist nicht verwendbar weil wir die anzahl der Nummern sowieso brauchen
|
||
int numbers= 0;
|
||
for(int j = getRow(startCellName); j<= (getRow(endCellName));j++){
|
||
for(int i = getCol(startCellName); i<= (getCol(endCellName));i++) {
|
||
if(!cells[j][i].getValue().isBlank()) {
|
||
midSum+= Long.parseLong(cells[j][i].getValue());
|
||
numbers++;
|
||
}
|
||
}
|
||
}
|
||
double midVal = (double) midSum / numbers;
|
||
|
||
double sum = 0.0;
|
||
for (int j = getRow(startCellName); j <= getRow(endCellName); j++) {
|
||
for (int i = getCol(startCellName); i <= getCol(endCellName); i++) {
|
||
if (!cells[j][i].getValue().isBlank()) {
|
||
long value = Long.parseLong(cells[j][i].getValue());
|
||
sum += Math.pow(value - midVal, 2);
|
||
}
|
||
}
|
||
}
|
||
double stabw = Math.sqrt(sum / numbers);
|
||
return (long) stabw;
|
||
}
|
||
/**
|
||
* Method for calculating the minimum value 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 minimum value calculated.
|
||
*/
|
||
public long min(String startCellName, String endCellName) {
|
||
long res = 0;
|
||
List<Long> args = new ArrayList<>();
|
||
for(int j = getRow(startCellName); j<= (getRow(endCellName));j++){
|
||
for(int i = getCol(startCellName); i<= (getCol(endCellName));i++) {
|
||
if(!cells[j][i].getValue().isBlank()) {
|
||
args.add(Long.parseLong(cells[j][i].getValue()));
|
||
}
|
||
}
|
||
}
|
||
if(args.size()>0) {
|
||
res=args.get(0);
|
||
}
|
||
for(int i = 0; i<args.size();i++) {
|
||
if(res>args.get(i)) {
|
||
res=args.get(i);
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
/**
|
||
* Method for calculating the maximum value 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 maximum value calculated.
|
||
*/
|
||
public long max(String startCellName, String endCellName) {
|
||
long res = 0;
|
||
List<Long> args = new ArrayList<>();
|
||
for(int j = getRow(startCellName); j<= (getRow(endCellName));j++){
|
||
for(int i = getCol(startCellName); i<= (getCol(endCellName));i++) {
|
||
if(!cells[j][i].getValue().isBlank()) {
|
||
args.add(Long.parseLong(cells[j][i].getValue()));
|
||
}
|
||
}
|
||
}
|
||
if(args.size()>0) {
|
||
res=args.get(0);
|
||
}
|
||
for(int i = 0; i<args.size();i++) {
|
||
if(res<args.get(i)) {
|
||
res=args.get(i);
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
/**
|
||
* 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);
|
||
|
||
List<String> args = new ArrayList<>();
|
||
|
||
while (m.find()) {
|
||
if(!m.group().isEmpty()) {
|
||
if(isNumber(m.group())||isCar(m.group())) {
|
||
args.add(m.group());
|
||
}else {
|
||
if(cells[getRow(m.group())][getCol(m.group())].getValue().isEmpty())
|
||
args.add("0");
|
||
else
|
||
args.add(m.group());
|
||
}
|
||
}
|
||
}
|
||
|
||
for(int i = 2; i<args.size();) {
|
||
String val = "";
|
||
switch(args.get(i-1)) {
|
||
case "+":
|
||
val =""+ (Integer.parseInt(args.get(i-2))+Integer.parseInt(args.get(i)));
|
||
args.set(i,val);
|
||
args.remove(i-1);
|
||
args.remove(i-2);
|
||
break;
|
||
case "-":
|
||
val =""+ (Integer.parseInt(args.get(i-2))-Integer.parseInt(args.get(i)));
|
||
args.set(i,val);
|
||
args.remove(i-1);
|
||
args.remove(i-2);
|
||
break;
|
||
case "*" :
|
||
val =""+ (Integer.parseInt(args.get(i-2))*Integer.parseInt(args.get(i)));
|
||
args.set(i,val);
|
||
args.remove(i-1);
|
||
args.remove(i-2);
|
||
break;
|
||
case "/":
|
||
val =""+ (Integer.parseInt(args.get(i-2))/Integer.parseInt(args.get(i)));
|
||
args.set(i,val);
|
||
args.remove(i-1);
|
||
args.remove(i-2);
|
||
break;
|
||
}
|
||
}
|
||
return Long.parseLong(args.get(0));
|
||
}
|
||
|
||
// -----
|
||
|
||
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();
|
||
}
|
||
|
||
} |