/// <summary> /// performs the operations as described in the abstract class file. If it is called and a /// cell already exists, then the content of that cell with be changed along with the value /// </summary> /// <param name="name">name of the cell</param> /// <param name="formula">formula to be stored in the cell</param> /// <returns>all of the dependents of a cell</returns> protected override ISet <string> SetCellContents(string name, Formula formula) { addCell(name, formula, formula.Evaluate(Lookup)); dependencyGraph.ReplaceDependents(name, formula.GetVariables()); return(CellsToRecalculate(name)); }
/// <summary> /// If content is null, throws an ArgumentNullException. /// /// Otherwise, if name is null or invalid, throws an InvalidNameException. /// /// Otherwise, if content parses as a double, the contents of the named /// cell becomes that double. /// /// Otherwise, if content begins with the character '=', an attempt is made /// to parse the remainder of content into a Formula f using the Formula /// constructor with s => s.ToUpper() as the normalizer and a validator that /// checks that s is a valid cell name as defined in the AbstractSpreadsheet /// class comment. There are then three possibilities: /// /// (1) If the remainder of content cannot be parsed into a Formula, a /// Formulas.FormulaFormatException is thrown. /// /// (2) Otherwise, if changing the contents of the named cell to be f /// would cause a circular dependency, a CircularException is thrown. /// /// (3) Otherwise, the contents of the named cell becomes f. /// /// Otherwise, the contents of the named cell becomes content. /// /// If an exception is not thrown, 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> SetContentsOfCell(string name, string content) { var ReturnSet = new HashSet <string>(); if (content == null) { throw new ArgumentNullException(); } if (name == null || !IsValid.IsMatch(name)) { throw new InvalidNameException(); } //Double case. double number; if (double.TryParse(content, out number)) { foreach (string s in SetCellContents(name.ToUpper(), number)) { ReturnSet.Add(s); } } //Formula case. else if (content != string.Empty && content[0] == '=') { string text = content.Substring(1); Formula form = new Formula(text, s => s.ToUpper(), s => IsValid.IsMatch(s)); foreach (string redo in SetCellContents(name.ToUpper(), form)) { ReturnSet.Add(redo); } } //Text case. else { foreach (string s in SetCellContents(name.ToUpper(), content)) { ReturnSet.Add(s); } } //Now we have all the cells to Recalculate in ReturnSet. foreach (string recalc in ReturnSet) { foreach (Cell cell in CellList) { if (cell.Name == recalc) { //Only need to recalcuate cells whose contents are formulas. Other cells are self sufficient and values set earlier. if (cell.Contents.GetType() == typeof(Formula)) { Formula contents = (Formula)cell.Contents; try { cell.Value = contents.Evaluate(s => (double)GetCellValue(s)); } catch (InvalidCastException) { cell.Value = new FormulaError("One or more variables have an undefined value."); } catch (FormulaEvaluationException) { cell.Value = new FormulaError("Issue evaluating formula."); } } } } } return(ReturnSet); }
/// <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 cell_name, SpreadsheetUtilities.Formula formula) { IEnumerable <string> backup_variables = null; object backup_contents = null; bool had_name = false; string name = Normalize(cell_name); //if the formula passed in is null this throws an ArgumentNullException(); if (formula == null) { throw new ArgumentNullException("Formula cannot be null"); } //checks if the name is null or invalid if so throws a new InvalidNameException(); if (name == null || IsValid(name) == false) { throw new InvalidNameException(); } //checks if the cell with the name has been set up, if so this sets the contents of the cell to the formula passed in. if (spreadsheet.ContainsKey(name)) { backup_variables = dependency_graph.GetDependees(name); backup_contents = spreadsheet[name].get_contents(); spreadsheet[name].set_contents(formula); had_name = true; } //if the cell hasn't been set up this creates a new cell and sets its contents to the text passed in. else { backup_contents = " "; backup_variables = new HashSet <string> { }; spreadsheet.Add(name, new Cell(name, formula)); } IEnumerable <string> variables = formula.GetVariables(); //Gets all the variables dependency_graph.ReplaceDependees(name, variables); //Replace the dependees of the name with all the variables in the formula passed in. //creates a hash set for the return ISet. ISet <string> depends = new HashSet <string> { }; //looks through each item in the dependency graph and add them to the hash set to return. try { IEnumerable <string> recalculate_cells = GetCellsToRecalculate(name); foreach (string item in recalculate_cells) { depends.Add(item); } } catch (CircularException) { spreadsheet[name].set_contents(backup_contents); dependency_graph.ReplaceDependees(name, backup_variables); throw new CircularException(); } //sets the value of the cell IEnumerable <string> evaluate_cells = GetCellsToRecalculate(name); foreach (string cell in evaluate_cells) { Formula value_of_formula = (Formula)GetCellContents(cell); try { spreadsheet[cell].set_value(value_of_formula.Evaluate(Lookup)); } catch (ArgumentException) { } } //returns the hash set with all the dependents of name. return(depends); }
/// <summary> /// Requires that all of the variables in formula are valid cell names. /// /// 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) { // check if name is null or invalid if (!Regex.IsMatch(name, IsValid.ToString()) || ReferenceEquals(name, null) || !Regex.IsMatch(name, @"^[a-zA-Z_](?: [a-zA-Z_]|\d)*$")) { throw new InvalidNameException(); } IEnumerable <String> dependees = dg.GetDependees(name); // replace dependents of name with the formula variables dg.ReplaceDependees(name, formula.GetVariables()); //check if dependency graph produces circular dependency try { HashSet <String> hsDependees = new HashSet <String>(GetCellsToRecalculate(name)); Cell cell = new Cell(formula); cell.value = formula; // if cell does not contain name, add new key for the value. // if it does contain name, then replace key with the value if (!cells.ContainsKey(name)) { cells.Add(name, new Cell(formula)); } else { cells[name] = new Cell(formula); } foreach (string values in new HashSet <String>(GetCellsToRecalculate(name))) { if (!(cells[values].contents is Formula)) { cell.value = formula.Evaluate((Lookup)LookupValue); } else { try { Formula f = (Formula)cells[values].contents; double value = f.Evaluate((Lookup)LookupValue); cells[values].value = value; } catch (Exception e) { cells[values].value = new FormulaError(); } } } return(new HashSet <String>(GetCellsToRecalculate(name))); } // if exception is caught, keep old dependents catch (CircularException e) { dg.ReplaceDependees(name, dependees); throw new CircularException(); } }
/// <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 cell_name, string text) { string name = Normalize(cell_name); //if the text passed in is null this throws an ArgumentNullException(); if (text == null) { throw new ArgumentNullException(); } //checks if the name is null or invalid if so throws a new InvalidNameException(); if (name == null || IsValid(name) == false) { throw new InvalidNameException(); } else if (text.Length < 1) { return(new HashSet <string> { }); } //checks if the cell with the name has been set up, if so this sets the contents of the cell to the text passed in. if (spreadsheet.ContainsKey(name)) { spreadsheet[name].set_contents(text); } //if the cell hasnt been set up this creates a new cell and sets its contents to the text passed in. else { spreadsheet.Add(name, new Cell(name, text, text)); } //creates a hash set for the return ISet. HashSet <string> depends = new HashSet <string> { }; spreadsheet[name].set_value(text); //looks through each item in the dependency graph and add them to the hash set to return. foreach (string item in GetCellsToRecalculate(name)) { depends.Add(item); } List <string> dependency_fix = new List <string> { }; foreach (string value in dependency_graph.GetDependees(name)) { dependency_fix.Add(value); } foreach (string key in dependency_fix) { dependency_graph.RemoveDependency(key, name); } foreach (string cell in depends) { if (!(cell == name)) { Formula value_of_formula = (Formula)GetCellContents(cell); spreadsheet[cell].set_value(value_of_formula.Evaluate(Lookup)); } } //returns the hash set with all the dependents of name. return(depends); }
public Cell(Formula name, Func <string, double> lookup) { content = name; value = name.Evaluate(lookup); contentType = "Formula"; }
protected override ISet <string> SetCellContents(string name, Formula formula) { // Parse the formula to get the variables IEnumerable variables = formula.GetVariables(); // Get features of cell prior to removing it in case there is a circular dependency Cell beforeCircleCheck = new Cell(); Object contents = new Object(); if (spreadsheetCells.TryGetValue(name, out beforeCircleCheck)) { contents = beforeCircleCheck.cellContents; } // If the cell already exists, remove it if (spreadsheetCells.ContainsKey(name)) { spreadsheetCells.Remove(name); foreach (String variable in variables) { dependencies.RemoveDependency(name, variable); } } // Create new cell object Cell toBeAdded = new Cell(); // Set cellName equal to name toBeAdded.cellName = name; // Set cellContents eequal to formula toBeAdded.cellContents = "=" + formula; // Set cellContentsType equal to formula toBeAdded.cellContentsType = "formula"; // Add (name, cell) to spreadsheetCells dictionary spreadsheetCells.Add(name, toBeAdded); // Add to dependency Graph foreach (String variable in variables) { dependencies.AddDependency(name, variable); } // Check for circular dependencies. If one is found, restore origional cell try { GetCellsToRecalculate(name); } catch (CircularException e) { // Restore origional cell if (!(beforeCircleCheck == null)) { spreadsheetCells.Remove(name); foreach (String var in variables) { dependencies.AddDependency(name, var); } toBeAdded.cellContents = beforeCircleCheck.cellContents; spreadsheetCells.Add(name, toBeAdded); } throw e; } // Set value of cell try { toBeAdded.cellValue = formula.Evaluate((x) => (double)GetCellValue(x)); } catch (InvalidCastException) { toBeAdded.cellValue = new FormulaError(); } catch (FormulaEvaluationException) { toBeAdded.cellValue = new FormulaError(); } // Get dependencies for return method call HashSet <String> returnValues = new HashSet <String>(); // Call GetCellsToRecalculate for direct dependents IEnumerable dependents = GetCellsToRecalculate(name); foreach (String temp in dependents) { // Add direct dependents to returnValues returnValues.Add(temp); } // Call GetDependees on dependencies to get indirect dependents IEnumerable dependees = dependencies.GetDependees(name); foreach (String temp in dependees) { // Add indriect dependents to returnValues returnValues.Add(temp); } // Recalculate Cell recalculation; foreach (String depend in dependents) { if (spreadsheetCells.TryGetValue(depend, out recalculation)) { // Set value of cell try { recalculation.cellValue = formula.Evaluate((x) => (double)GetCellValue(x)); } catch (InvalidCastException) { recalculation.cellValue = new FormulaError(); } catch (FormulaEvaluationException) { recalculation.cellValue = new FormulaError(); } } } // Change changed to true since we made a change to the spreadsheet Changed = true; // return return(returnValues); }
protected override ISet <string> SetCellContents(string name, string text) { // Check to see if text is empty if (text.Equals("")) { Cell value; if (spreadsheetCells.TryGetValue(name, out value)) { value.cellContents = ""; value.cellValue = ""; // Readd cell's that need to be recomputed IEnumerable <String> recalc = GetCellsToRecalculate(name); //recalc.Skip<String>(1); Cell contents; foreach (String cellNm in recalc) { spreadsheetCells.TryGetValue(cellNm, out contents); if (contents.cellName.Equals(name)) { continue; } SetContentsOfCell(cellNm, contents.cellContents.ToString()); String toBeAddedContents = contents.cellContents.ToString(); } } spreadsheetCells.Remove(name); return(new HashSet <String>()); } // If the cell already exists, remove it if (spreadsheetCells.ContainsKey(name)) { spreadsheetCells.Remove(name); } // Create new cell object Cell toBeAdded = new Cell(); // Set cellName equal to name toBeAdded.cellName = name; // Set cellContents equal to text toBeAdded.cellContents = text; // Set cellValue equal to text toBeAdded.cellValue = text; // Set cellContentsType equal to string toBeAdded.cellContentsType = "string"; // Add (name, cell) to spreadsheetCells dictionary spreadsheetCells.Add(name, toBeAdded); // Get dependencies for return method call HashSet <String> returnValues = new HashSet <String>(); // Call GetCellsToRecalculate to get direct dependents IEnumerable dependents = GetCellsToRecalculate(name); foreach (String temp in dependents) { // Store direct dependents in returnValues returnValues.Add(temp); } // Call GetDependees on dependencies to get indirect dependents IEnumerable dependees = dependencies.GetDependees(name); foreach (String temp in dependees) { // Store indirect dependents in returnValues returnValues.Add(temp); } Cell recalculation; foreach (String depend in dependents) { if (spreadsheetCells.TryGetValue(depend, out recalculation)) { // Set value of cell try { String equalPattern = @"^="; String cellContents = recalculation.cellContents.ToString(); if (Regex.IsMatch(cellContents, equalPattern)) { String parsedContents = cellContents.Substring(1); Formula f = new Formula(parsedContents); recalculation.cellValue = f.Evaluate((x) => (double)GetCellValue(x)); //SetContentsOfCell(recalculation.cellName, recalculation.cellContents.ToString()); } } catch (InvalidCastException) { recalculation.cellValue = new FormulaError(); } catch (FormulaEvaluationException) { recalculation.cellValue = new FormulaError(); } } } // Change changed to true since we made a change to the spreadsheet Changed = true; // return return(returnValues); }
protected override ISet <string> SetCellContents(string name, double number) { // If the cell already exists, remove it if (spreadsheetCells.ContainsKey(name)) { spreadsheetCells.Remove(name); } // Create new cell Cell toBeAdded = new Cell(); // Set cellName equal to name toBeAdded.cellName = name; // Set cellContents equal to number toBeAdded.cellContents = number; // Set cellValue equal to number toBeAdded.cellValue = number; // Set cellContentsType equal to double toBeAdded.cellContentsType = "double"; // Add (name, cell) to our spreadsheetCells dictionary spreadsheetCells.Add(name, toBeAdded); // Get dependecies for return portion of method call HashSet <String> returnValues = new HashSet <String>(); // Call GetCellsToRecalculate to get direct dependents IEnumerable dependents = GetCellsToRecalculate(name); foreach (String temp in dependents) { // Add direct dependents to returnValues returnValues.Add(temp); } // Call GetDependees on dependencies to get indirect dependents IEnumerable dependees = dependencies.GetDependees(name); foreach (String temp in dependees) { // Add indirect dependents to returnValues returnValues.Add(temp); } // Recalculate Cell recalculation; foreach (String depend in dependents) { if (spreadsheetCells.TryGetValue(depend, out recalculation)) { // Set value of cell try { String equalPattern = @"^="; String cellContents = recalculation.cellContents.ToString(); if (Regex.IsMatch(cellContents, equalPattern)) { String parsedContents = cellContents.Substring(1); Formula f = new Formula(parsedContents); recalculation.cellValue = f.Evaluate((x) => (double)GetCellValue(x)); //SetContentsOfCell(recalculation.cellName, recalculation.cellContents.ToString()); } } catch (InvalidCastException) { recalculation.cellValue = new FormulaError(); } catch (FormulaEvaluationException) { recalculation.cellValue = new FormulaError(); } } } // Change changed to true since we made a change to the spreadsheet Changed = true; // Return return(returnValues); }