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(maxInteger.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(); } }