/// <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> public override ISet <string> SetCellContents(string name, Formula formula) { if (string.IsNullOrWhiteSpace(name) || !IsValidName(name)) { throw new InvalidNameException(); } if (formula == null) { throw new ArgumentNullException(nameof(formula), "Formula is null"); } if (!_cells.ContainsKey(name)) { _cells.Add(name, new Cell(name)); } var referencedCellsEnumeration = formula.GetVariables(); var referencedCells = referencedCellsEnumeration as string[] ?? referencedCellsEnumeration.ToArray(); var cell = _cells[name]; cell.Content = formula; cell.Value = formula.Evaluate(_resolveVariables); _depenencyManager.ReplaceDependents(name, referencedCells); foreach (var dep in referencedCells) { //cell.DepenencyManager.AddDependency(dep, name); if (_cells.ContainsKey(dep)) { _depenencyManager.AddDependency(name, dep); } } 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> /// If the 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> private ISet <string> SetContents(string name, object content) { // Make sure the content is not null if (content == null) { throw new ArgumentNullException("Make sure the formula is not null."); } if (name == null || !Cell.IsValidName(name)) { throw new InvalidNameException(); } // Every time we set the contents, remove all of the dependents and dependees for this name. cellRelations.ReplaceDependents(name, new List <string>()); // Set the value to the content. Value will change if content is a formula. object value = content; // Now, handle formula-specific actions if (content is Formula) { // First make sure that adding this new content would not create a circular dependency. Formula contentAsFormula = (Formula)content; checkForCircularDependency(name, contentAsFormula); // Add in all of the dependencies for this cell List <string> variablesInFormula = contentAsFormula.GetVariables().ToList(); foreach (string v in variablesInFormula) { cellRelations.AddDependency(name, v); } // Get the value and content prepped for cell creation value = contentAsFormula.Evaluate(lookup); content = contentAsFormula.ToString(); } // If the cell already exists, just change the content and value. if (cells.ContainsKey(name)) { // If they are setting the content to "", remove the cell. if (value.ToString().Equals("")) { cells.Remove(name); } else { cells[name].content = content; cells[name].value = value; } } // Otherwise, create a new cell object with the proper name, content, and value // This will also check that the name of the cell is valid or not. else { Cell cellForName = new Cell(name, content, value); cells.Add(name, cellForName); } // Get all of the dependees for the named cell and return them. HashSet <string> dependsOnNamedCell = new HashSet <string>() { name }; List <string> namedCellDependees = GetCellsToRecalculate(name).ToList(); foreach (string dep in namedCellDependees) { dependsOnNamedCell.Add(dep); } return(dependsOnNamedCell); }
/// <summary> /// 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> /// <param name="name"></param> /// <param name="formula"></param> /// <returns></returns> protected override ISet <string> SetCellContents(string name, Formula formula) { var ReturnSet = new HashSet <string>(); var NewDependents = new List <string>(); var OldDependees = new List <string>(); var VariableValues = new Dictionary <string, double>(); bool found = false; bool InGraph = false; //Looks through CellList for the cell called name. foreach (Cell cell in CellList) { if (cell.Name == name) { foreach (string var in formula.GetVariables()) { NewDependents.Add(var); } foreach (string DependName in GetCellsToRecalculate(name)) { if (NewDependents.Contains(DependName)) { throw new CircularException(); // Changed b/c circular exception doesn't take an argument } ReturnSet.Add(DependName); } foreach (string s in Graph.GetDependents(name)) { InGraph = true; } if (InGraph != true) { foreach (string t in NewDependents) { Graph.AddDependency(name, t); } } else { Graph.ReplaceDependents(name, NewDependents); } cell.Contents = formula; found = true; break; } } //If the name wasnt in the list already we can add it as a new cell. if (found == false) { Cell newcell = new Cell(name, formula, new FormulaError("One or more variables have an undefined value.")); CellList.Add(newcell); foreach (string var in formula.GetVariables()) { if (var == name) { throw new CircularException(); } else { Graph.AddDependency(name, var); } } foreach (string var in GetCellsToRecalculate(name)) { ReturnSet.Add(var); } } Changed = true; return(ReturnSet); }