Ejemplo n.º 1
0
        private bool ContainsCircularRefRec(SheetCell cell, HashSet <SheetCell> curChain)
        {
            if (curChain.Contains(cell))
            {
                return(true);
            }

            if (Refs.ContainsKey(cell) && Refs[cell].Count > 0)
            {
                curChain.Add(cell);

                foreach (var cellRef in Refs[cell])
                {
                    // create a duplicate HashSet for every new possible walk on the graph of cells.
                    // this is so that cells that reference multiple values that don't contain
                    // crefs wont cause false positives
                    if (ContainsCircularRefRec(cellRef, new HashSet <SheetCell>(curChain)))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Ejemplo n.º 2
0
        private void UpdateRefs(SheetCell cell)
        {
            if (!Refs.ContainsKey(cell))
            {
                return;
            }

            // recompute the values and update Vars dict
            foreach (var refCell in Refs[cell])
            {
                if (refCell.InvalidExp)
                {
                    continue;
                }

                var res = refCell.ETree.Eval();
                Vars[refCell.Name] = res;
                refCell.SetValue(res.ToString());
                CellPropertyChanged(refCell, new PropertyChangedEventArgs("Value"));

                // recursively update ref ref calls etc
                UpdateRefs(refCell);
            }
        }
Ejemplo n.º 3
0
        // called whenever a cell changes so that we can dispatch another event as well as
        // check to see if we need to look up the value in another cell
        private void CellChanged(object sender, PropertyChangedEventArgs e)
        {
            var cell = sender as SheetCell;

            if (e.PropertyName == "Color")
            {
                CellPropertyChanged(cell, new PropertyChangedEventArgs("Color"));
                return;
            }

            // reset expression validity when it changes
            // if its still invalid it will get set back
            // to true
            cell.InvalidExp = false;

            // handle raw numbers
            double val;

            if (double.TryParse(cell.Text, out val))
            {
                foreach (var old in cell.Deps)
                {
                    Refs[Name2Cell(old)].Remove(cell);
                }

                cell.Deps.Clear();
                Vars[cell.Name] = val;
                cell.SetValue(val.ToString());
            }
            else if (cell.Text != null && cell.Text.Length > 0 && cell.Text[0] == '=')
            {
                cell.ETree = new ExpTree(cell.Text.Substring(1), Vars);

                double res     = cell.ETree.Eval();
                var    newDeps = cell.ETree.GetExpLocals();

                // cell expression variables contain reference to the cell itself
                if (newDeps.FirstOrDefault(dep => dep == cell.Name) != null)
                {
                    cell.SetValue("!(self reference)");
                    cell.InvalidExp = true;
                }
                // one of the cell expression variables is invalid
                else if (newDeps.FirstOrDefault(dep => Name2Cell(dep) == null) != null)
                {
                    cell.SetValue("!(bad reference)");
                    cell.InvalidExp = true;
                }
                // chain of cell exp vars contains a circular reference
                else if (newDeps.ToList().FirstOrDefault(p => ContainsCircularRef(cell, Name2Cell(p))) != null)
                {
                    cell.SetValue("!(circ reference)");
                    cell.InvalidExp = true;
                }
                else
                {
                    Vars[cell.Name] = res;

                    // remove dependencies that arent needed anymore
                    foreach (var old in cell.Deps.Except(newDeps))
                    {
                        Refs[Name2Cell(old)].Remove(cell);
                    }

                    cell.Deps = newDeps;

                    // add current cell to each set corresponding to one of the current cell's
                    // dependencies
                    foreach (var dep in cell.Deps)
                    {
                        var rcell = Name2Cell(dep);

                        if (!Refs.ContainsKey(rcell))
                        {
                            Refs.Add(rcell, new HashSet <SheetCell>());
                        }

                        Refs[rcell].Add(cell);
                    }

                    cell.SetValue(res.ToString());
                }
            }
            else
            {
                // remove previous dependencies when directly setting text
                foreach (var old in cell.Deps)
                {
                    Refs[Name2Cell(old)].Remove(cell);
                }

                // changing cells that have dependencies from numerical to be strings
                // defaults the cells value to 0 so that all dependencies get updated with
                // the 0 value
                Vars[cell.Name] = 0;
                cell.SetValue(cell.Text);
            }

            // now update all dependencies
            UpdateRefs(cell);
            CellPropertyChanged((sender as SheetCell), new PropertyChangedEventArgs("Value"));
        }