/// <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> /// 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) { if (!IsValidCellName(name, IsValid)) { throw new InvalidNameException(); } if (!Cells.ContainsKey(name)) { Cells.Add(name, new Cell()); } // Set consisting of variables in formula ISet <string> variables = formula.GetVariables(); object oldVal = Cells[name.ToUpper()].GetContent(); bool hadFormula = Cells[name.ToUpper()].hasFormula; Cells[name.ToUpper()].SetContent(formula); // Remove dependess that aren't there in the formula if (Graph.HasDependees(name)) { foreach (string dependee in Graph.GetDependees(name).ToList()) { if (!variables.Contains(dependee)) { Graph.RemoveDependency(dependee, name); } } } // Add dependees that don't already exist foreach (string variable in variables) { Graph.AddDependency(variable, name); } ISet <string> changedSet = new HashSet <string>(); changedSet = GetAllRelatedDependents(name); // changedSet.Add(name); // if (Graph.HasDependents(name)) // { // foreach (string dependent in Graph.GetDependents(name)) // { // changedSet.Add(dependent); // } // } // To check for Circular Dependency try { GetCellsToRecalculate(changedSet); } catch (CircularException e) { Cells[name.ToUpper()].SetContent(oldVal); Cells[name.ToUpper()].hasFormula = hadFormula; throw e; } return(changedSet); }
protected override IList <string> SetCellContents(string name, Formula formula) { foreach (string var in formula.GetVariables()) { if (!IsValid(var)) { throw new FormulaFormatException("invalid variable " + var); } } // Try Catch in case of Circular Exception try { // Adding dependencies for the variables within formula to name foreach (string var in formula.GetVariables()) { depGraph.AddDependency(var, name); } // Our list to return as well as check for CircularException List <string> toReturn = GetCellsToRecalculate(name).ToList(); // Adding as a new Cell and grabbing it's value. object value; try { value = formula.Evaluate(lookup); } catch (ArgumentException) { value = new FormulaError(); } if (!(Cells.ContainsKey(name))) { Cells.Add(name, new Cell(name, formula, value)); } // Overwriting the Cell to a formula else { Cells[name] = new Cell(name, formula, value); } // Recalculate dependents RecalculateDependents(name); Changed = true; return(toReturn); } // Resetting dependencies SS is unchanged. catch (CircularException) { foreach (string var in formula.GetVariables()) { depGraph.RemoveDependency(var, name); } // Then we throw the Exception. throw new CircularException(); } }
protected override ISet <String> SetCellContents(String name, Formula formula) { //EXCEPTION HANDELING\\ if (System.String.IsNullOrWhiteSpace(name)) { throw new InvalidNameException(); } if (!isValid(name)) { throw new InvalidNameException(); } if (formula == null) { throw new ArgumentNullException(); } this.temp = new HashSet <string>(); foreach (string s in formula.GetVariables()) //GET ALL THE VARIABLES FROM THE FORMULA AND ADD THEM TO A SET { if (!isValid(s)) { throw new FormulaFormatException("One or more variables names do not follow the proper syntax expected"); } this.temp.Add(s); } foreach (string s in this.temp) //TRACK DEPENDENCIES { if (ssDep.HasDependents(s)) { foreach (string ss in ssDep.GetDependents(s)) { if (ss == s || ss == name) { throw new CircularException(); } } } ssDep.AddDependency(name, s); } Cell v; if (Cells.TryGetValue(name, out v)) //Get the right cell { if (v.Content is Formula) { Formula check = (Formula)v.Content; foreach (string s in check.GetVariables()) { ssDep.RemoveDependency(name, s); //Remove old dependencies } } } ISet <string> set = Set(name, formula); foreach (string s in set) //Recalculate Cells that depend on new cells content { calculateV(s); } return(set); }
/// <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); }
/// <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) { // checkif everything is legal if (formula.Equals(null)) { throw new ArgumentNullException(); } if (name == null || !legalName.IsMatch(name) || (constructorUse != 1 && !isvalName.IsMatch(name))) { throw new InvalidNameException(); } // if the sheet have the name already if (sheet.ContainsKey(name)) { // delete it and add it sheet.Remove(name); cell mycell = new cell(name, formula, new FormulaError()); RecalculteCells(); sheet.Add(name, mycell); // if in the formula contains some cell's name // get the related names foreach (string cellsName in formula.GetVariables()) { if (legalName.IsMatch(cellsName)) { dependency.AddDependency(name, cellsName); } } } // if the sheet doesn't exsit the cell else { // add the cell cell mycell = new cell(name, formula, new FormulaError()); RecalculteCells(); sheet.Add(name, mycell); // get the dependency of the cells foreach (string cellsName in formula.GetVariables()) { if (legalName.IsMatch(cellsName)) { dependency.AddDependency(name, cellsName); } } } HashSet <string> relatedCells = new HashSet <string>(); // save the dependency to the set and return it foreach (string cellName in GetCellsToRecalculate(name)) { relatedCells.Add(cellName); } Changed = true; return(relatedCells); }
/// <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) { name = name.ToUpper(); // check if passed in formula contains a dependent of the named cell // to prevent a ciruclar dependency //foreach(string token in GetDirectDependents(name)) //{ // if(formula.ToString().Contains(token)) // { // throw new CircularException(); // } //} HashSet <string> set = new HashSet <string>(); // A backup spreadsheet for revert in case a CircularException is thrown Dictionary <string, Cell> tempDict = new Dictionary <string, Cell>(); // A backup DependencyGraph for revert in case a CircularException is thrown DependencyGraph tempGraph = new DependencyGraph(graph); //create a copy for the spreadsheet foreach (KeyValuePair <string, Cell> key in cellList) { tempDict.Add(key.Key, key.Value); } // Replace current cell's content for update graph.ReplaceDependees(name, new List <string>()); // Create dependency relationship between cell and valid variables generated by Formula foreach (string var in formula.GetVariables()) { CheckParameters(var); graph.AddDependency(var, name); } // Generate a new cell if such named cell doesn't exists yet, or simply replace the existing // cell with new information GenerateCellInfo(name, formula); set.Add(name); // This try catch block is used to detect possible Circular dependency by calling GetCellsToRecalculate() try { foreach (string var in GetCellsToRecalculate(name)) { set.Add(var); } } catch (CircularException e) { // If a CircularException is caught, revert the entire operation. cellList = tempDict; graph = tempGraph; Changed = false; throw e; } // Update current named cell's dependee's content, value and dependency UpdateCellInfo(set, name); Changed = true; return(set); }
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); }