예제 #1
0
        /// <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);
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        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();
            }
        }
예제 #4
0
        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);
        }
예제 #5
0
        /// <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);
        }
예제 #7
0
        /// <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);
        }