Exemple #1
0
 // MODIFIED PROTECTION FOR PS5
 /// <summary>
 /// If changing the contents of the named cell to be the formula would cause a
 /// circular dependency, throws a CircularException, and no change is made to the spreadsheet.
 ///
 /// Otherwise, the contents of the named cell becomes formula. The method returns a
 /// list consisting of name plus the names of all other cells whose value depends,
 /// directly or indirectly, on the named cell. The order of the list should be any
 /// order such that if cells are re-evaluated in that order, their dependencies
 /// are satisfied by the time they are evaluated.
 ///
 /// For example, if name is A1, B1 contains A1*2, and C1 contains B1+A1, the
 /// list {A1, B1, C1} is returned.
 /// </summary>
 protected abstract IList <String> SetCellContents(String name, Formula formula);
Exemple #2
0
        /// <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, and no change is made to the spreadsheet.
        ///
        /// Otherwise, the contents of the named cell becomes formula.  The method returns a
        /// list 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
        /// list {A1, B1, C1} is returned.
        /// </summary>
        protected override IList <string> SetCellContents(string name, Formula formula)
        {
            //Normalizes the name of the Cell
            String normalizedName = Normalize(name);


            //Creates a new Cell passing the name and formula
            Cell c = new Cell(normalizedName, formula);

            //Sets the contents of the cell with the formula
            c.SetContents(formula);
            //Sets the value of the Cell which should be a double if the formula is valid
            //or a FormulaError if the formula is invalid
            c.SetValue(Lookup);

            //A boolean to keep track if a new Entry is made
            bool newEntry = false;
            //Gets the previous dependees of the cell before
            HashSet <string> dependees = new HashSet <string>();
            //A Cell to store the prevCell
            Cell prevCell = null;

            //If the dictionary contains the key name, then goes here
            if (dictionary.ContainsKey(normalizedName))
            {
                //Gets the prevCell
                prevCell = dictionary[normalizedName];
                //Replaces it with the new cell
                dictionary[normalizedName] = c;

                //Gets the prevDependees of the graph
                dependees = new HashSet <string>(graph.GetDependees(normalizedName));
                //Replaces the dependees with the variables of the formula
                graph.ReplaceDependees(normalizedName, formula.GetVariables());
            }
            else
            {
                //Adds the cell to the dictionary
                dictionary.Add(normalizedName, c);

                //Gets the prevDependees of the graph
                dependees = new HashSet <string>(graph.GetDependees(normalizedName));

                //Replaces the dependees with the variables of the formula
                graph.ReplaceDependees(normalizedName, formula.GetVariables());

                //Sets the newEntry boolean to true
                newEntry = true;
            }

            //Creates a new list to be returned
            List <string> recalculatedList;

            try
            {
                //Gets the orders of the cell recalculated and stores it into a list
                recalculatedList = new List <string>(GetCellsToRecalculate(normalizedName));

                //Recalculates the values of all the cells
                RecalculateValues(recalculatedList);
            }
            //Goes in here if we encounter a CircularException
            catch (CircularException ce)
            {
                //If a new Cell was added, we remove it from the dictionary
                if (newEntry == true)
                {
                    dictionary.Remove(normalizedName);
                }
                //Otherwise, we revert the value set to the value before in the Cell
                else
                {
                    dictionary[normalizedName] = prevCell;
                }
                //Replace the dependees of the name to it's original form
                graph.ReplaceDependees(normalizedName, dependees);

                //Throws a CircularException
                throw new CircularException();
            }


            //Sets changed to true
            Changed = true;

            //Returns the list of cells recalculated
            return(recalculatedList);
        }
Exemple #3
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);
        }
Exemple #4
0
        /// <summary>
        /// /// Creates a Spreadsheet that is a duplicate of the spreadsheet saved in source.
        ///
        /// See the AbstractSpreadsheet.Save method and Spreadsheet.xsd for the file format
        /// specification.
        ///
        /// If there's a problem reading source, throws an IOException.
        ///
        /// Else if the contents of source are not consistent with the schema in Spreadsheet.xsd,
        /// throws a SpreadsheetReadException.
        ///
        /// Else if the IsValid string contained in source is not a valid C# regular expression, throws
        /// a SpreadsheetReadException.  (If the exception is not thrown, this regex is referred to
        /// below as oldIsValid.)
        ///
        /// Else if there is a duplicate cell name in the source, throws a SpreadsheetReadException.
        /// (Two cell names are duplicates if they are identical after being converted to upper case.)
        ///
        /// Else if there is an invalid cell name or an invalid formula in the source, throws a
        /// SpreadsheetReadException.  (Use oldIsValid in place of IsValid in the definition of
        /// cell name validity.)
        ///
        /// Else if there is an invalid cell name or an invalid formula in the source, throws a
        /// SpreadsheetVersionException.  (Use newIsValid in place of IsValid in the definition of
        /// cell name validity.)
        ///
        /// Else if there's a formula that causes a circular dependency, throws a SpreadsheetReadException.
        ///
        /// Else, create a Spreadsheet that is a duplicate of the one encoded in source except that
        /// the new Spreadsheet's IsValid regular expression should be newIsValid.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="newIsValid"></param>
        public Spreadsheet(TextReader source, Regex newIsValid)
        {
            Changed = false;

            // Create an XmlSchemaSet object.
            XmlSchemaSet sc = new XmlSchemaSet();
            string       name;
            string       contents;

            // a regex placeholder for regex value embedded inside of source file
            Regex oldIsValid = null;
            // a regex to check cell name
            Regex formatCheck = new Regex(@"^[a-zA-Z][a-zA-Z]*[1-9][0-9]*$");


            // NOTE: To read states3.xsd this way, it must be stored in the same folder with the
            // executable.  To arrange this, I set the "Copy to Output Directory" propery of states3.xsd to
            // "Copy If Newer", which will copy states3.xsd as part of each build (if it has changed
            // since the last build).
            sc.Add(null, "Spreadsheet.xsd");

            // Configure validation
            XmlReaderSettings settings = new XmlReaderSettings
            {
                ValidationType = ValidationType.Schema,
                Schemas        = sc
            };

            // if an error occurs while reading the source, ValidationCallback will be triggered
            settings.ValidationEventHandler += ValidationCallback;

            using (XmlReader reader = XmlReader.Create(source, settings))
            {
                while (reader.Read())
                {
                    if (reader.IsStartElement())
                    {
                        // check if a spreadsheet tag exists
                        if (reader.Name.Equals("spreadsheet"))
                        {
                            // check if the regex embedded is a valid C# regex
                            if (CheckRegex(reader["IsValid"]))
                            {
                                oldIsValid = new Regex(reader["IsValid"]);
                            }
                            else
                            {
                                throw new SpreadsheetReadException("Invalid C# regular expression");
                            }
                        }

                        // begin to assign name and contents into current Spreadsheet
                        if (reader.Name.Equals("cell"))
                        {
                            name     = reader["name"];
                            contents = reader["contents"];

                            if (cellList.Keys.Contains(name.ToUpper()))
                            {
                                throw new SpreadsheetReadException("A duplicated named cell detected");
                            }

                            // check validity for name using oldIsValid
                            if (!formatCheck.IsMatch(name) || !oldIsValid.IsMatch(name))
                            {
                                throw new SpreadsheetReadException("Invalid cell name in the source.");
                            }

                            // check validity for name using newIsValid
                            if (!formatCheck.IsMatch(name) || !newIsValid.IsMatch(name))
                            {
                                throw new SpreadsheetVersionException("Invalid cell name in the source.");
                            }

                            // if contents contains a formula, do the following checks
                            if (contents.StartsWith("="))
                            {
                                string temp = contents.Remove(0, 1);

                                try
                                {
                                    // check validity for formula using oldIsValid by imposing extra regex
                                    Formula f = new Formula(temp, s => s.ToUpper(), s => oldIsValid.IsMatch(s));
                                }
                                catch
                                {
                                    throw new SpreadsheetReadException("Invalid formula in the source.");
                                }

                                try
                                {
                                    // check validity for formula using newIsValid by imposing extra regex
                                    Formula f = new Formula(temp, s => s.ToUpper(), s => newIsValid.IsMatch(s));
                                }
                                catch
                                {
                                    throw new SpreadsheetVersionException("Invalid formula in the source.");
                                }
                            }

                            // if all data are valid, prepare to set cell contents
                            IsValid = newIsValid;
                            SetContentsOfCell(name, contents);
                        }
                    }
                    // reset temporary strings
                    name     = "";
                    contents = "";
                }
            }
        }
        public override ISet <string> SetContentsOfCell(string name, string content)
        {
            // Check to see if content is null
            if (content == null)
            {
                throw new ArgumentNullException();
            }

            // Check to see if name is null
            if (name == null)
            {
                throw new InvalidNameException();
            }

            // Normalize name
            String normalName = name.ToUpper();

            // Check to see if name is valid
            if (NameIsValid(normalName))
            {
                // If content parses as a double
                double parsedContent;
                if (double.TryParse(content, out parsedContent))
                {
                    return(SetCellContents(normalName, parsedContent));
                }

                // If content begins with an = sign
                String equalPattern = @"^=";
                if (Regex.IsMatch(content, equalPattern))
                {
                    String contentToBeParsed = content.Substring(1).ToUpper();

                    // Try to parse into formula
                    Formula parsedFormula;
                    try
                    {
                        parsedFormula = new Formula(contentToBeParsed);
                    }
                    catch (FormulaFormatException e)
                    {
                        throw e;
                    }

                    // Try to set contents of name to be formula, throw CircularException if needed
                    try
                    {
                        return(SetCellContents(normalName, parsedFormula));
                    }
                    catch (CircularException e)
                    {
                        throw e;
                    }
                }

                // Content should just be a string since it isn't a formula or double
                else
                {
                    return(SetCellContents(normalName, content));
                }
            }
            else
            {
                throw new InvalidNameException();
            }
        }
        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);
        }
Exemple #9
0
 /// <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 abstract ISet <String> SetCellContents(String name, Formula formula);