private ErrType add_remove(SpreadsheetCell toAlter, ExpTree mainTree, bool removing) { /*Adding to and removing from the reference table occurs in this function*/ ErrType Error = ErrType.None; if (toAlter.VarList != null && toAlter.VarList.Count > 0) { string referencedBy = CellToString(toAlter); if (removing) { foreach (string referencedCell in toAlter.VarList) // Removes all variables from the old tree { if (refTable.ContainsKey(referencedCell)) { if (refTable[referencedCell].Contains(referencedBy)) refTable[referencedCell].Remove(referencedBy); // Removes the current cell from any other cells referencing hash if (refTable[referencedCell].Count < 1) // If an entry in the table has no cells referencing it, then it is removed refTable.Remove(referencedCell); } } toAlter.VarList.Clear(); // Empty that variable list (will be rebuild below) } else // Adding value to the reference this { foreach (string s in toAlter.VarList) // Updates the reference table with all of the referenced cells (variables in the expTree's context) { double CellValue = 0.0; SpreadsheetCell next = StringToCell(s); if (next != null) { if (s == CellToString(toAlter)) // SELF-REFERENCING { Error = ErrType.SelfRef; CheckErr(toAlter, Error, CellToString(toAlter)); UpdateErrorReferecedBy(toAlter, ErrType.SelfRef, CellToString(toAlter)); // Updates all cells referencing this cell that there is a value return ErrType.SelfRef; } else if (next.Value.Contains("REF")) // Won't check for already occuring errors (in referenced cell) { if (next.Value.Contains("=<BAD_REF")) // If this cell REFERENCES a cell that contains a bad_ref error { CheckErr(toAlter, ErrType.BadRef, s); UpdateErrorReferecedBy(toAlter, ErrType.BadRef, s); Error = ErrType.BadRef; } else if (next.Value.Contains("=<SELF_REF")) // If this cell REFERENCES a cell that contains a self_ref error { CheckErr(toAlter, ErrType.SelfRef, s); UpdateErrorReferecedBy(toAlter, ErrType.SelfRef, CellToString(toAlter)); Error = ErrType.SelfRef; } else if (next.Value.Contains("=<CIRC_REF")) { CheckErr(toAlter, ErrType.CircRef, s); UpdateErrorReferecedBy(toAlter, ErrType.CircRef, CellToString(toAlter)); Error = ErrType.CircRef; } } if (next.Text != "") { Double.TryParse(next.Value, out CellValue); // Gets the cell's value mainTree.SetVar(s, CellValue); // Sets the variable in the expression tree's dictionary (0 if not yet set) } if (refTable.ContainsKey(s)) // If The variable already has references, just add to its hash refTable[s].Add(referencedBy); else // Otherwise create the new variable key with a new list containing the cell that references it refTable.Add(s, new HashSet<string>() { referencedBy }); } else // If Cell parsing return null (cell not recovered), the there is a bad reference { Error = ErrType.BadRef; CheckErr(toAlter, Error, CellToString(toAlter)); UpdateErrorReferecedBy(toAlter, ErrType.BadRef, CellToString(toAlter)); return ErrType.BadRef; } } if (Error == ErrType.CircRef) return Error; if (Error != ErrType.SelfRef && CheckCircularRef(toAlter, CellToString(toAlter))) // Checks for circular references here *** { Error = ErrType.CircRef; CheckErr(toAlter, Error, CellToString(toAlter)); UpdateErrorReferecedBy(toAlter, ErrType.CircRef, CellToString(toAlter)); } } } return Error; }
private void UpdateCellValue(ref SpreadsheetCell cell) { var mainTree = new ExpTree(); // Initializes a new expression tree to build the cell's expression ErrType Error = ErrType.None; if (cell.Text != "" && cell.Text[0] != '=') // not an expression, simply a text value { cellArr[cell.RowIndex, cell.ColumnIndex] = new Cell(cell, cell.Text); add_remove(cell, mainTree, true); } else { if (cell.Text != "") mainTree.Expression = cell.Text.Substring(1).Replace(" ", ""); // Build the expression tree with the cell's text (minus the '=') :: Also ignores whitespaces else mainTree.Expression = cell.Text; add_remove(cell, mainTree, true); // Removes all variables cooresponding to the old tree cell.VarList = GetVarNames(cell.Text); // Will Return all found variables in the new expression Error = add_remove(cell, mainTree, false); if (Error != ErrType.None) // Notifies the UI that there is an error in one of the cells that the expression references { return; // Exits the function before executing anything else, error display has already been taken care of at this point } try { cellArr[cell.RowIndex, cell.ColumnIndex] = new Cell(cell, mainTree.Eval().ToString()); // Attempts to evaluate the expression, placing it into a new cell } catch (DivideByZeroException) // User tried to divide by zero { CheckErr(cell, ErrType.DivZero, CellToString(cell)); UpdateErrorReferecedBy(cell, ErrType.DivZero, CellToString(cell)); return; } catch (NullReferenceException) // Input not regonized / invalid expression { if (cell.Text == "") { cellArr[cell.RowIndex, cell.ColumnIndex] = new Cell(cell, ""); // if the cell was deleted or reset, this will set the cell to an empty value (caught by expression tree as null) } else { CheckErr(cell, ErrType.InvExp, CellToString(cell)); UpdateErrorReferecedBy(cell, ErrType.InvExp, CellToString(cell)); // Notifies UI that an invalid expression has been entered return; } } } cellArr[cell.RowIndex, cell.ColumnIndex].PropertyChanged += detect_PropertyChanged; // Reassigns the the detect_property function to the cell's delegate CellPropertyChanged(cellArr[cell.RowIndex, cell.ColumnIndex], new PropertyChangedEventArgs("Value")); // fires the event that notifies the GUI of a change UpdateReferencedBy(cell); // Updates all cells that reference this cell }