]> source.dussan.org Git - poi.git/commitdiff
Fix for bug 46948 - Range operator can now take area-refs for operands
authorJosh Micich <josh@apache.org>
Wed, 1 Apr 2009 19:41:12 +0000 (19:41 +0000)
committerJosh Micich <josh@apache.org>
Wed, 1 Apr 2009 19:41:12 +0000 (19:41 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@761023 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/record/formula/eval/RangeEval.java
src/testcases/org/apache/poi/hssf/record/formula/eval/TestRangeEval.java

index 52f96225aab6b6d2c3259dce6a70e98938d99e93..c9f1eab99c0cf4626466fb63f2faaa1be5d39e3c 100644 (file)
@@ -37,6 +37,7 @@
 
                <!-- Don't forget to update status.xml too! -->
         <release version="3.5-beta6" date="2009-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">46948 - Fixed evaluation of range operator to allow for area-ref operands</action>
            <action dev="POI-DEVELOPERS" type="fix">46918 - Fixed ExtendedPivotTableViewFieldsRecord(SXVDEX) to allow shorter format</action>
            <action dev="POI-DEVELOPERS" type="fix">46898 - Fixed formula evaluator to not cache intermediate circular-reference error results</action>
            <action dev="POI-DEVELOPERS" type="fix">46917 - Fixed PageItemRecord(SXPI) to allow multiple field infos</action>
index e6cc63338a96bb42d49cc182e5222057847a58e0..65923e12c2ba9e17d2d8f308d63a5a549dd130eb 100644 (file)
@@ -34,6 +34,7 @@
        <!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.5-beta6" date="2009-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">46948 - Fixed evaluation of range operator to allow for area-ref operands</action>
            <action dev="POI-DEVELOPERS" type="fix">46918 - Fixed ExtendedPivotTableViewFieldsRecord(SXVDEX) to allow shorter format</action>
            <action dev="POI-DEVELOPERS" type="fix">46898 - Fixed formula evaluator to not cache intermediate circular-reference error results</action>
            <action dev="POI-DEVELOPERS" type="fix">46917 - Fixed PageItemRecord(SXPI) to allow multiple field infos</action>
index 938241c281450bacc77949c31327319b457b9cae..07b143a6d6328e069410fbd2f7bd57ac6e12611d 100644 (file)
@@ -36,25 +36,36 @@ public final class RangeEval implements OperationEval {
                }
 
                try {
-                       RefEval reA = evaluateRef(args[0]);
-                       RefEval reB = evaluateRef(args[1]);
+                       AreaEval reA = evaluateRef(args[0]);
+                       AreaEval reB = evaluateRef(args[1]);
                        return resolveRange(reA, reB);
                } catch (EvaluationException e) {
                        return e.getErrorEval();
                }
        }
 
-       private static AreaEval resolveRange(RefEval reA, RefEval reB) {
+       /**
+        * @return simple rectangular {@link AreaEval} which fully encloses both areas 
+        * <tt>aeA</tt> and <tt>aeB</tt>
+        */
+       private static AreaEval resolveRange(AreaEval aeA, AreaEval aeB) {
+               int aeAfr = aeA.getFirstRow();
+               int aeAfc = aeA.getFirstColumn();
                
-               int height = reB.getRow() - reA.getRow();
-               int width = reB.getColumn() - reA.getColumn();
+               int top = Math.min(aeAfr, aeB.getFirstRow());
+               int bottom = Math.max(aeA.getLastRow(), aeB.getLastRow());
+               int left = Math.min(aeAfc, aeB.getFirstColumn());
+               int right = Math.max(aeA.getLastColumn(), aeB.getLastColumn());
                
-               return reA.offset(0, height, 0, width);
+               return aeA.offset(top-aeAfr, bottom-aeAfr, left-aeAfc, right-aeAfc);
        }
 
-       private static RefEval evaluateRef(Eval arg) throws EvaluationException {
+       private static AreaEval evaluateRef(Eval arg) throws EvaluationException {
+               if (arg instanceof AreaEval) {
+                       return (AreaEval) arg;
+               }
                if (arg instanceof RefEval) {
-                       return (RefEval) arg;
+                       return ((RefEval) arg).offset(0, 0, 0, 0);
                }
                if (arg instanceof ErrorEval) {
                        throw new EvaluationException((ErrorEval)arg);
index e98517a732b06570969b41d7b2b6747240a672ea..d30a7b1d388934028881b6dd94dd478f61c3d09c 100644 (file)
 
 package org.apache.poi.hssf.record.formula.eval;
 
+import java.lang.reflect.Field;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.formula.AreaI;
+import org.apache.poi.hssf.record.formula.AttrPtg;
+import org.apache.poi.hssf.record.formula.FuncVarPtg;
+import org.apache.poi.hssf.record.formula.IntPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.RangePtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
 import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.hssf.util.AreaReference;
 import org.apache.poi.hssf.util.CellReference;
-
-import junit.framework.TestCase;
+import org.apache.poi.ss.formula.FormulaParser;
+import org.apache.poi.ss.usermodel.CellValue;
 
 /**
  * Test for unary plus operator evaluator.
@@ -30,22 +46,22 @@ import junit.framework.TestCase;
  * @author Josh Micich
  */
 public final class TestRangeEval extends TestCase {
-       
+
        public void testPermutations() {
-               
+
                confirm("B3", "D7", "B3:D7");
                confirm("B1", "B1", "B1:B1");
-               
+
                confirm("B7", "D3", "B3:D7");
                confirm("D3", "B7", "B3:D7");
                confirm("D7", "B3", "B3:D7");
        }
 
        private static void confirm(String refA, String refB, String expectedAreaRef) {
-               
+
                Eval[] args = {
-                       createRefEval(refA),    
-                       createRefEval(refB),    
+                       createRefEval(refA),
+                       createRefEval(refB),
                };
                AreaReference ar = new AreaReference(expectedAreaRef);
                Eval result = RangeEval.instance.evaluate(args, 0, (short)0);
@@ -60,7 +76,7 @@ public final class TestRangeEval extends TestCase {
        private static Eval createRefEval(String refStr) {
                CellReference cr = new CellReference(refStr);
                return new MockRefEval(cr.getRow(), cr.getCol());
-               
+
        }
 
        private static final class MockRefEval extends RefEvalBase {
@@ -89,7 +105,91 @@ public final class TestRangeEval extends TestCase {
                }
                public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx,
                                int relLastColIx) {
-                       throw new RuntimeException("not expected to be called during this test");
+                       AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
+                                       relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+
+                       return new MockAreaEval(area);
+               }
+       }
+
+       public void testRangeUsingOffsetFunc_bug46948() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFRow row = wb.createSheet("Sheet1").createRow(0);
+               HSSFCell cellA1 = row.createCell(0);
+               HSSFCell cellB1 = row.createCell(1);
+               row.createCell(2).setCellValue(5.0); // C1
+               row.createCell(3).setCellValue(7.0); // D1
+               row.createCell(4).setCellValue(9.0); // E1
+
+
+               try {
+                       cellA1.setCellFormula("SUM(C1:OFFSET(C1,0,B1))");
+               } catch (RuntimeException e) {
+                       // TODO fix formula parser to handle ':' as a proper operator
+                       if (!e.getClass().getName().startsWith(FormulaParser.class.getName())) {
+                               throw e;
+                       }
+                       // FormulaParseException is expected until the parser is fixed up
+                       // Poke the formula in directly:
+                       pokeInOffsetFormula(cellA1);
+               }
+
+
+               cellB1.setCellValue(1.0); // range will be C1:D1
+
+               HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
+               CellValue cv;
+               try {
+                       cv = fe.evaluate(cellA1);
+               } catch (IllegalArgumentException e) {
+                       if (e.getMessage().equals("Unexpected ref arg class (org.apache.poi.ss.formula.LazyAreaEval)")) {
+                               throw new AssertionFailedError("Identified bug 46948");
+                       }
+                       throw e;
+               }
+
+               assertEquals(12.0, cv.getNumberValue(), 0.0);
+
+               cellB1.setCellValue(2.0); // range will be C1:E1
+               fe.notifyUpdateCell(cellB1);
+               cv = fe.evaluate(cellA1);
+               assertEquals(21.0, cv.getNumberValue(), 0.0);
+
+               cellB1.setCellValue(0.0); // range will be C1:C1
+               fe.notifyUpdateCell(cellB1);
+               cv = fe.evaluate(cellA1);
+               assertEquals(5.0, cv.getNumberValue(), 0.0);
+       }
+
+       /**
+        * Directly sets the formula "SUM(C1:OFFSET(C1,0,B1))" in the specified cell.
+        * This hack can be removed when the formula parser can handle functions as
+        * operands to the range (:) operator.
+        *
+        */
+       private static void pokeInOffsetFormula(HSSFCell cell) {
+               cell.setCellFormula("1");
+               FormulaRecordAggregate fr;
+               try {
+                       Field field = HSSFCell.class.getDeclaredField("_record");
+                       field.setAccessible(true);
+                       fr = (FormulaRecordAggregate) field.get(cell);
+               } catch (IllegalArgumentException e) {
+                       throw new RuntimeException(e);
+               } catch (IllegalAccessException e) {
+                       throw new RuntimeException(e);
+               } catch (NoSuchFieldException e) {
+                       throw new RuntimeException(e);
                }
+               Ptg[] ptgs = {
+                               new RefPtg("C1"),
+                               new RefPtg("C1"),
+                               new IntPtg(0),
+                               new RefPtg("B1"),
+                               new FuncVarPtg("OFFSET", (byte)3),
+                               RangePtg.instance,
+                               AttrPtg.SUM,
+                       };
+               fr.setParsedExpression(ptgs);
        }
 }