/// <summary> /// Evaluates a set of cells in the order they appear. /// </summary> /// <param name="cellNames">Set of cells to be evaluated.</param> private void EvaluateCells(HashSet<string> cellNames) { foreach (string name in cellNames) { Cell outCell = new Cell(); if (!cells.TryGetValue(name, out outCell)) { continue; } if (outCell.Content is string || outCell.Content is double) return; Formula formula = (Formula)outCell.Content; outCell.Value = formula.Evaluate(GetValue); } }
// MODIFIED PROTECTION FOR PS5 /// <summary> /// If text is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, the contents of the named cell becomes text. The method returns a /// set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, string text) { if (text == null) throw new ArgumentNullException(); CheckValidCellName(name); if (text == "") { HashSet<string> recalculate = new HashSet<string>(GetCellsToRecalculate(name)); dependencies.ReplaceDependees(name, new List<string>()); cells.Remove(name); return recalculate; } Cell outCell = new Cell(); if (cells.TryGetValue(name, out outCell)) { cells[name].Content = text; dependencies.ReplaceDependees(name, new List<string>()); } else { cells.Add(name, new Cell(name, text, text)); } Changed = true; return new HashSet<string>(GetCellsToRecalculate(name)); }
// MODIFIED PROTECTION FOR PS5 /// <summary> /// If formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, Formula formula) { if (formula == null) throw new ArgumentException(); CheckValidCellName(name); bool originalChangedState = Changed; List<string> origDeps = dependencies.GetDependees(name).ToList(); Dictionary<string, Cell> origCells = new Dictionary<string, Cell>(); for (int i = 0; i < cells.Count; i++) { origCells.Add(cells.ElementAt(i).Key, cells.ElementAt(i).Value.Clone()); } Cell getCell = new Cell(); if (cells.TryGetValue(name, out getCell)) { cells[name].Content = formula; cells[name].Value = null; } else { cells.Add(name, new Cell(name, formula, null)); } dependencies.ReplaceDependees(name, formula.GetVariables()); try { Changed = true; return new HashSet<string>(GetCellsToRecalculate(name)); } catch (CircularException) { Changed = originalChangedState; dependencies.ReplaceDependees(name, origDeps); cells = origCells; throw new CircularException(); } }
// ADDED FOR PS5 /// <summary> /// If name is null or invalid, throws an InvalidNameException. /// /// Otherwise, returns the value (as opposed to the contents) of the named cell. The return /// value should be either a string, a double, or a SpreadsheetUtilities.FormulaError. /// </summary> public override object GetCellValue(string name) { name = Normalize(name); CheckValidCellName(name); Cell c = new Cell(); if (cells.TryGetValue(name, out c)) return c.Value; return ""; }
// MODIFIED PROTECTION FOR PS5 /// <summary> /// If name is null or invalid, throws an InvalidNameException. /// /// Otherwise, the contents of the named cell becomes number. The method returns a /// set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, double number) { CheckValidCellName(name); Cell outCell = new Cell(); if (cells.TryGetValue(name, out outCell)) { cells[name].Content = number; dependencies.ReplaceDependees(name, new List<string>()); } else { cells.Add(name, new Cell(name, number, number)); } Changed = true; return new HashSet<string>(GetCellsToRecalculate(name)); }
/// <summary> /// Takes in a cell name and a formula and returns the dependents /// Calculates the value of the formula then recalculates the value of any cell that depends directly or inderectly on it. /// </summary> /// <param name="name"></param> /// <param name="formula"></param> /// <returns></returns> protected override ISet<string> SetCellContents(string name, Formula formula) { name = Normalize(name); HashSet<string> mySet = new HashSet<string>(); HashSet<string> replaceSet = new HashSet<string>(); if (Object.ReferenceEquals(formula, null)) { throw new ArgumentNullException(); } if (Object.ReferenceEquals(name, null)) { throw new InvalidNameException(); } if (!Regex.IsMatch(name, "^([a-z]|[A-Z])+\\d+$") | !IsValid(name)) { throw new InvalidNameException(); } try { foreach (string s in formula.GetVariables()) { replaceSet.Add(s); } // dependentCells.ReplaceDependees(name, mySet); dependentCells.ReplaceDependees(name, replaceSet); GetCellsToRecalculate(name); } catch (CircularException) { throw new CircularException(); } Formula myEvl = new Formula(formula.ToString(), IsValid, Normalize); myCell = new Cell(formula, myEvl.Evaluate(myLookup)); if (mySpreadsheet.ContainsKey(name)) { mySpreadsheet[name] = myCell; } else { mySpreadsheet.Add(name, myCell); } mySet.Add(name); foreach (string s in GetCellsToRecalculate(name)) { mySet.Add(s); } foreach (string s in mySet) { myCell = new Cell(GetCellContents(s), myEvaluate(s)); mySpreadsheet[s] = myCell; } Changed = true; return mySet; }
/// <summary> /// If name is invalid or null, throws InvalidNameException. /// contents of the named cell become a string. /// /// The method returns a set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> /// <param name="name"></param> /// <param name="text"></param> /// <returns></returns> protected override ISet<string> SetCellContents(string name, string text) { //Replacing cell name with new content and value. cells[name] = new Cell(name, text, text); //spreadsheet has changed Changed = true; //recalculates all dependees of name. recalculateDependees(name, (LinkedList<string>)GetCellsToRecalculate(name)); //remove all dependencies of name, since name is now a text cell and not a Formula. foreach (string el in dependencies.GetDependents(name)) { dependencies.RemoveDependency(name, el); } //grabs name and dependees(indirect and direct) of name, puts them into a HashSet and returns them. return new HashSet<string>(GetCellsToRecalculate(name)); }
/// <summary> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. (No change is made to the spreadsheet.) /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, Formula formula) { IEnumerable<String> dependee_list = dependency_graph.GetDependees(name); dependency_graph.ReplaceDependees(name, formula.GetVariables()); try { HashSet<String> all_dependees = new HashSet<String>(GetCellsToRecalculate(name)); if (cell.ContainsKey(name)) { cell[name] = new Cell(formula, LookupValue); } else { cell.Add(name, new Cell(formula, LookupValue)); } return all_dependees; } catch (CircularException) { dependency_graph.ReplaceDependees(name, dependee_list); throw new CircularException(); } }
// MODIFIED PROTECTION FOR PS5 /// <summary> /// If formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if changing the contents of the named cell to be the formula would cause a /// circular dependency, throws a CircularException. /// /// Otherwise, the contents of the named cell becomes formula. The method returns a /// Set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> protected override ISet<string> SetCellContents(string name, SpreadsheetUtilities.Formula formula) { if (formula == null) throw new ArgumentNullException("Error: Formula paramter was null."); // Checks if cell name is valid and not null if (name == null || !ValidName(name)) throw new InvalidNameException(); List<string> cellNames = new List<string>(); Cell cellValue; // Holds the cell names that will be returned HashSet<string> result = new HashSet<string>(); // Holds the contents of the original cell object o = null; if (cells.ContainsKey(name)) { o = cells[name].Content; RemoveDependency(name); } // Save the content of the cell to store in the dictionary cellValue = new Cell(formula, GetCell); cells.Add(name, cellValue); // cellNames gets a list of all of the cell names in the formula and creates a list // to add to the dependency graph cellNames = formula.GetVariables().ToList(); // Add the values associated to name to the dependency graph foreach (string n in cellNames) { dg.AddDependency(name, n); } try { //saves the direct and indirect values into tempI and then put them into my returnSet //also checks for circular dependency and recalculates the values in the cells associate with the name IEnumerable<string> tempI = GetCellsToRecalculate(name);//recalculates the values and checks for circular dependency foreach (string n in tempI) result.Add(n); } catch (CircularException c) { //method comes here if a circularexeption is found when calling the GetCellsToRecalculate method //originalCellContent is saved earlier in the code to allow us to go back to the original values to get //rid of the circular dependency if (o != null) { if (o is Formula) { Formula originalFormula = new Formula(o.ToString()); SetCellContents(name, originalFormula); } else if (o is String) { SetCellContents(name, o.ToString()); } else if (o is double) { SetCellContents(name, Convert.ToDouble(o)); } } else SetCellContents(name, ""); throw c; } return result; }
/// <summary> /// If name is null or invalid, throws an InvalidNameException. /// /// Otherwise, the contents of the named cell becomes number. The method returns a /// set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. protected override ISet<string> SetCellContents(string name, double number) { if (cell.ContainsKey(name)) { cell[name] = new Cell(number); } else { cell.Add(name, new Cell(number)); } dependency_graph.ReplaceDependees(name, new HashSet<String>()); HashSet<String> dependees = new HashSet<string>(GetCellsToRecalculate(name)); return dependees; }
/// <summary> /// If text is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, the contents of the named cell becomes text. The method returns a /// set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. protected override ISet<string> SetCellContents(string name, string text) { if (cell.ContainsKey(name)) { cell[name] = new Cell(text); } else { cell.Add(name, new Cell(text)); } string cell_content = (String)cell[name].contents; if (cell_content.Equals("")) cell.Remove(name); dependency_graph.ReplaceDependees(name, new HashSet<String>()); HashSet<String> dependees = new HashSet<string>(GetCellsToRecalculate(name)); return dependees; }
/// <summary> /// If name is null or invalid, throws an InvalidNameException. /// /// Otherwise, returns the contents (as opposed to the value) of the named cell. The return /// value should be either a string, a double, or a Formula. /// </summary> public override object GetCellContents(string name) { if (!CheckValidCellName(name)) throw new InvalidNameException(); Cell getContent = new Cell(); if (cells.TryGetValue(name, out getContent)) { return getContent.Content; } return ""; }
/// <summary> /// If name is null or invalid, throws an InvalidNameException. /// /// Otherwise, the contents of the named cell becomes number. The method returns a /// set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> public override ISet<string> SetCellContents(string name, double number) { if (!CheckValidCellName(name)) throw new InvalidNameException(); Cell outCell = new Cell(); if (cells.TryGetValue(name, out outCell)) { cells[name].Content = number; dependencies.ReplaceDependees(name, new List<string>()); } else { cells.Add(name, new Cell(name, number, number)); } return new HashSet<string>(GetCellsToRecalculate(name)); }
/// <summary> /// If the formula parameter is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// contents of the named cell become a formula. /// /// The method returns a set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> /// <param name="name"></param> /// <param name="formula"></param> /// <returns></returns> protected override ISet<string> SetCellContents(string name, Formula formula) { LinkedList<string> cellsToRecalc; cellsToRecalc = (LinkedList<string>)GetCellsToRecalculate(name); //Testing for CircularExceptions, does not affect cells so no need to backup old cell. foreach (string el in cellsToRecalc) { if (formula.GetVariables().Contains(el)) throw new CircularException(); } //Replacing cell name with new content and value. try { cells[name] = new Cell(name, formula, formula.Evaluate(lookup)); } catch (System.ArgumentException e) { cells[name] = new Cell(name, formula, new FormulaError()); } //spreadsheet has changed Changed = true; //recalculates the dependees of name. recalculateDependees(name, cellsToRecalc); //replace all dependents of name with new formulas variables. dependencies.ReplaceDependents(name, formula.GetVariables()); //grabs name and dependees(direct and indirect) of name and puts them into a HashSet and returns them. return new HashSet<string>(cellsToRecalc); }
/// <summary> /// This provides a function to be used as a lookup function for the evaluator. /// </summary> /// <param name="s">Returns a value associated with a cell</param> /// <returns></returns> private double GetValue(string s) { Cell c = new Cell(); if (!cells.TryGetValue(s, out c)) throw new ArgumentException(); if (!(c.Value is double)) throw new ArgumentException(); return (double)c.Value; }
public void CellConstructorTest() { Cell target = new Cell(22,22); Assert.AreEqual(22, target.getContents()); Assert.AreEqual(22, target.getValue()); }
/// <summary> /// Takes in a cell name and the text of a cell and returns dependents and sets cell value /// </summary> /// <param name="name"></param> /// <param name="text"></param> /// <returns></returns> protected override ISet<string> SetCellContents(string name, string text) { name = Normalize(name); HashSet<string> mySet = new HashSet<string>(); HashSet<string> replaceSet = new HashSet<string>(); myCell = new Cell(text, text); if (Object.ReferenceEquals(text, null)) { throw new ArgumentNullException(); } if (text == "") { if (mySpreadsheet.ContainsKey(name)) { mySpreadsheet[name] = myCell; } return mySet; } if (Object.ReferenceEquals(name, null)) { throw new InvalidNameException(); } if (!Regex.IsMatch(name, "^([a-z]|[A-Z])+\\d+$") | !IsValid(name)) { throw new InvalidNameException(); } if (mySpreadsheet.ContainsKey(name)) { mySpreadsheet[name] = myCell; } else { mySpreadsheet.Add(name, myCell); } mySet.Add(name); dependentCells.ReplaceDependees(name, replaceSet); foreach (string s in GetCellsToRecalculate(name)) { mySet.Add(s); } foreach (string s in mySet) { if (s == name) { mySpreadsheet[s] = myCell; } else { myCell = new Cell(GetCellContents(s), myEvaluate(s)); mySpreadsheet[s] = myCell; } } Changed = true; return mySet; }
/// <summary> /// If name is null or invalid, throws an InvalidNameException. /// /// Otherwise, returns the contents (as opposed to the value) of the named cell. The return /// value should be either a string, a double, or a Formula. /// </summary> public override object GetCellContents(string name) { name = Normalize(name); CheckValidCellName(name); Cell getContent = new Cell(); if (cells.TryGetValue(name, out getContent)) { return getContent.Content; } return ""; }
/// <summary> /// Returns the cell associated to name. If there is no cell associated with name, returns null. /// </summary> /// <param name="name"></param> /// <returns></returns> protected Cell GetCell(string name) { name = CorrectInput(name); //Checks to see if there's a cell associated with name. If not, creates an empty cell and associates to name. if (!cells.ContainsKey(name)) { cells[name] = new Cell("", ""); } //Gets the cell associated with name. return cells[name]; }
/// <summary> /// If name is invalid or null, throws InvalidNameException. /// contents of the named cell become a double. /// /// The method returns a set consisting of name plus the names of all other cells whose value depends, /// directly or indirectly, on the named cell. /// /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the /// set {A1, B1, C1} is returned. /// </summary> /// <param name="name"></param> /// <param name="number"></param> /// <returns></returns> protected override ISet<string> SetCellContents(string name, double number) { //adds new cell "name". Parsing double to correct formatting. 5.0000 == 5.0 cells[name] = new Cell(name, number, double.Parse(number.ToString())); Changed = true; //fixes all dependees of name recalculateDependees(name, (LinkedList<string>)GetCellsToRecalculate(name)); //removes all dependents of name since name isnt a formula foreach (string el in dependencies.GetDependents(name)) { dependencies.RemoveDependency(name, el); } //grabs name and dependees(direct and indirect) of name, puts them into a HashSet and returns them. return new HashSet<string>(GetCellsToRecalculate(name)); }