public void TestShortCircuitIfEvaluation()
        {
            // Set up a simple IF() formula that has measurable evaluation cost for its operands.
            HSSFWorkbook wb     = new HSSFWorkbook();
            ISheet       sheet  = wb.CreateSheet("Sheet1");
            IRow         row    = sheet.CreateRow(0);
            ICell        cellA1 = row.CreateCell(0);

            cellA1.CellFormula = "if(B1,C1,D1+E1+F1)";
            // populate cells B1..F1 with simple formulas instead of plain values so we can use
            // EvaluationListener to check which parts of the first formula get evaluated
            for (int i = 1; i < 6; i++)
            {
                // formulas are just literal constants "1".."5"
                row.CreateCell(i).CellFormula = i.ToString();
            }

            EvalCountListener evalListener = new EvalCountListener();
            WorkbookEvaluator evaluator    = WorkbookEvaluatorTestHelper.CreateEvaluator(wb, evalListener);
            ValueEval         ve           = evaluator.Evaluate(HSSFEvaluationTestHelper.WrapCell(cellA1));
            int evalCount = evalListener.EvalCount;

            if (evalCount == 6)
            {
                // Without short-circuit-if evaluation, evaluating cell 'A1' takes 3 extra evaluations (for D1,E1,F1)
                throw new AssertionException("Identifed bug 48195 - Formula evaluator should short-circuit IF() calculations.");
            }
            Assert.AreEqual(3, evalCount);
            Assert.AreEqual(2.0, ((NumberEval)ve).NumberValue, 0D);
        }
示例#2
0
        /**
         * If cell Contains a formula, the formula is Evaluated and returned,
         * else the CellValue simply copies the appropriate cell value from
         * the cell and also its cell type. This method should be preferred over
         * EvaluateInCell() when the call should not modify the contents of the
         * original cell.
         *
         * @param sheetName the name of the sheet Containing the cell
         * @param rowIndex zero based
         * @param columnIndex zero based
         * @return <code>null</code> if the supplied cell is <code>null</code> or blank
         */
        public ValueEval Evaluate(String sheetName, int rowIndex, int columnIndex)
        {
            IEvaluationCell cell = _sewb.GetEvaluationCell(sheetName, rowIndex, columnIndex);

            switch (cell.CellType)
            {
            case CellType.Boolean:
                return(BoolEval.ValueOf(cell.BooleanCellValue));

            case CellType.Error:
                return(ErrorEval.ValueOf(cell.ErrorCellValue));

            case CellType.Formula:
                return(_evaluator.Evaluate(cell));

            case CellType.Numeric:
                return(new NumberEval(cell.NumericCellValue));

            case CellType.String:
                return(new StringEval(cell.StringCellValue));

            case CellType.Blank:
                return(null);
            }
            throw new InvalidOperationException("Bad cell type (" + cell.CellType + ")");
        }
示例#3
0
        /**
         * Returns a CellValue wrapper around the supplied ValueEval instance.
         * @param eval
         */
        private CellValue EvaluateFormulaCellValue(ICell cell)
        {
            ValueEval eval = _bookEvaluator.Evaluate(new HSSFEvaluationCell((HSSFCell)cell));

            if (eval is NumberEval)
            {
                NumberEval ne = (NumberEval)eval;
                return(new CellValue(ne.NumberValue));
            }
            if (eval is BoolEval)
            {
                BoolEval be = (BoolEval)eval;
                return(CellValue.ValueOf(be.BooleanValue));
            }
            if (eval is StringEval)
            {
                StringEval ne = (StringEval)eval;
                return(new CellValue(ne.StringValue));
            }
            if (eval is ErrorEval)
            {
                return(CellValue.GetError(((ErrorEval)eval).ErrorCode));
            }
            throw new InvalidOperationException("Unexpected eval class (" + eval.GetType().Name + ")");
        }
示例#4
0
        /**
         * Returns a CellValue wrapper around the supplied ValueEval instance.
         */
        private CellValue EvaluateFormulaCellValue(ICell cell)
        {
            if (!(cell is XSSFCell))
            {
                throw new ArgumentException("Unexpected type of cell: " + cell.GetType() + "." +
                                            " Only XSSFCells can be Evaluated.");
            }

            ValueEval eval = _bookEvaluator.Evaluate(new XSSFEvaluationCell((XSSFCell)cell));

            if (eval is NumberEval)
            {
                NumberEval ne = (NumberEval)eval;
                return(new CellValue(ne.NumberValue));
            }
            if (eval is BoolEval)
            {
                BoolEval be = (BoolEval)eval;
                return(CellValue.ValueOf(be.BooleanValue));
            }
            if (eval is StringEval)
            {
                StringEval ne = (StringEval)eval;
                return(new CellValue(ne.StringValue));
            }
            if (eval is ErrorEval)
            {
                return(CellValue.GetError(((ErrorEval)eval).ErrorCode));
            }
            throw new Exception("Unexpected eval class (" + eval.GetType().Name + ")");
        }
示例#5
0
        /**
         * If cell Contains a formula, the formula is Evaluated and returned,
         * else the CellValue simply copies the appropriate cell value from
         * the cell and also its cell type. This method should be preferred over
         * EvaluateInCell() when the call should not modify the contents of the
         * original cell.
         *
         * @param sheetName the name of the sheet Containing the cell
         * @param rowIndex zero based
         * @param columnIndex zero based
         * @return <code>null</code> if the supplied cell is <code>null</code> or blank
         */
        public ValueEval Evaluate(String sheetName, int rowIndex, int columnIndex)
        {
            IEvaluationCell cell = _sewb.GetEvaluationCell(sheetName, rowIndex, columnIndex);

            switch (cell.CellType)
            {
            case CellType.BOOLEAN:
                return(BoolEval.ValueOf(cell.BooleanCellValue));

            case CellType.ERROR:
                return(ErrorEval.ValueOf(cell.ErrorCellValue));

            case CellType.FORMULA:
                return(_evaluator.Evaluate(cell));

            case CellType.NUMERIC:
                return(new NumberEval(cell.NumericCellValue));

            case CellType.STRING:
                return(new StringEval(cell.StringCellValue));

            case CellType.BLANK:
                return(null);
            }
            throw new InvalidOperationException("Bad cell type (" + cell.CellType + ")");
        }
示例#6
0
        public void TestSlowEvaluate45376()
        {
            /*
             * Note - to observe behaviour without caching, disable the call to
             * updateValue() from FormulaCellCacheEntry.updateFormulaResult().
             */

            // Firstly set up a sequence of formula cells where each depends on the  previous multiple
            // times.  Without caching, each subsequent cell take about 4 times longer to Evaluate.
            HSSFWorkbook wb = new HSSFWorkbook();

            NPOI.SS.UserModel.ISheet sheet = wb.CreateSheet("Sheet1");
            IRow row = sheet.CreateRow(0);

            for (int i = 1; i < 10; i++)
            {
                ICell  cell     = row.CreateCell(i);
                char   prevCol  = (char)('A' + i - 1);
                String prevCell = prevCol + "1";
                // this formula is inspired by the offending formula of the attachment for bug 45376
                String formula = "IF(DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1)<=$D$3," +
                                 "DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1),NA())";
                cell.CellFormula = (formula);
            }
            row.CreateCell(0).SetCellValue(new DateTime(2000, 1, 1, 0, 0, 0));

            // Choose cell A9, so that the Assert.Failing Test case doesn't take too long to execute.
            ICell             cell1        = row.GetCell(8);
            EvalListener      evalListener = new EvalListener();
            WorkbookEvaluator evaluator    = WorkbookEvaluatorTestHelper.CreateEvaluator(wb, evalListener);
            ValueEval         ve           = evaluator.Evaluate(HSSFEvaluationTestHelper.WrapCell(cell1));
            int evalCount = evalListener.GetCountCacheMisses();

            if (evalCount > 10)
            {
                // Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes
                // much time (~3 sec on Core 2 Duo 2.2GHz)
                Console.Error.WriteLine("Cell A9 took " + evalCount + " intermediate evaluations");
                throw new AssertionException("Identifed bug 45376 - Formula evaluator should cache values");
            }
            // With caching, the evaluationCount is 8 which is a big improvement
            // Note - these expected values may change if the WorkbookEvaluator is
            // ever optimised to short circuit 'if' functions.
            Assert.AreEqual(8, evalCount);

            // The cache hits would be 24 if fully evaluating all arguments of the
            // "IF()" functions (Each of the 8 formulas has 4 refs to formula cells
            // which result in 1 cache miss and 3 cache hits). However with the
            // short-circuit-if optimisation, 2 of the cell refs get skipped
            // reducing this metric 8.
            Assert.AreEqual(8, evalListener.GetCountCacheHits());

            // confirm the evaluation result too
            Assert.AreEqual(ErrorEval.NA, ve);

            wb.Close();
        }
示例#7
0
 public ValueEval EvaluateCell(String cellRefText)
 {
     return(_evaluator.Evaluate(WrapCell(GetOrCreateCell(cellRefText))));
 }