]> source.dussan.org Git - poi.git/commitdiff
Make intersection formulae work in XSSF. Patch from Matt Hillsdon plus additional...
authorDavid North <dnorth@apache.org>
Wed, 19 Aug 2015 10:10:08 +0000 (10:10 +0000)
committerDavid North <dnorth@apache.org>
Wed, 19 Aug 2015 10:10:08 +0000 (10:10 +0000)
https://bz.apache.org/bugzilla/show_bug.cgi?id=52111

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1696549 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/ss/formula/FormulaParser.java
src/java/org/apache/poi/ss/formula/OperandClassTransformer.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
test-data/spreadsheet/Intersection-52111-xssf.xlsx [new file with mode: 0644]
test-data/spreadsheet/Intersection-52111.xls

index 5124a44632e9dfc062dbbc8b55e769cbbfe3fcd6..53f2dd2f9d39b72b6aeabd3fd66b9bc74bde5b46 100644 (file)
@@ -39,6 +39,7 @@ import org.apache.poi.ss.formula.ptg.FuncPtg;
 import org.apache.poi.ss.formula.ptg.FuncVarPtg;
 import org.apache.poi.ss.formula.ptg.GreaterEqualPtg;
 import org.apache.poi.ss.formula.ptg.GreaterThanPtg;
+import org.apache.poi.ss.formula.ptg.IntersectionPtg;
 import org.apache.poi.ss.formula.ptg.IntPtg;
 import org.apache.poi.ss.formula.ptg.LessEqualPtg;
 import org.apache.poi.ss.formula.ptg.LessThanPtg;
@@ -100,6 +101,12 @@ public final class FormulaParser {
         */
        private char look;
 
+    /**
+     * Tracks whether the run of whitespace preceeding "look" could be an
+     * intersection operator.  See GetChar.
+     */
+       private boolean _inIntersection = false;
+
        private FormulaParsingWorkbook _book;
        private SpreadsheetVersion _ssVersion;
 
@@ -145,9 +152,20 @@ public final class FormulaParser {
                fp.parse();
                return fp.getRPNPtg(formulaType);
        }
-
+       
        /** Read New Character From Input Stream */
        private void GetChar() {
+               // The intersection operator is a space.  We track whether the run of 
+               // whitespace preceeding "look" counts as an intersection operator.  
+               if (IsWhite(look)) {
+                       if (look == ' ') {
+                               _inIntersection = true;
+                       }
+               }
+               else {
+                       _inIntersection = false;
+               }
+               
                // Check to see if we've walked off the end of the string.
                if (_pointer > _formulaLength) {
                        throw new RuntimeException("too far");
@@ -158,6 +176,7 @@ public final class FormulaParser {
                        // Just return if so and reset 'look' to something to keep
                        // SkipWhitespace from spinning
                        look = (char)0;
+                       _inIntersection = false;
                }
                _pointer++;
                //System.out.println("Got char: "+ look);
@@ -1108,7 +1127,7 @@ public final class FormulaParser {
                                return parseUnary(true);
                        case '(':
                                Match('(');
-                               ParseNode inside = comparisonExpression();
+                               ParseNode inside = unionExpression();
                                Match(')');
                                return new ParseNode(ParenthesisPtg.instance, inside);
                        case '"':
@@ -1447,8 +1466,9 @@ public final class FormulaParser {
                        result = new ParseNode(operator, result, other);
                }
        }
+
        private ParseNode unionExpression() {
-               ParseNode result = comparisonExpression();
+               ParseNode result = intersectionExpression();
                boolean hasUnions = false;
                while (true) {
                        SkipWhite();
@@ -1456,7 +1476,7 @@ public final class FormulaParser {
                                case ',':
                                        GetChar();
                                        hasUnions = true;
-                                       ParseNode other = comparisonExpression();
+                                       ParseNode other = intersectionExpression();
                                        result = new ParseNode(UnionPtg.instance, result, other);
                                        continue;
                        }
@@ -1467,6 +1487,25 @@ public final class FormulaParser {
                }
        }
 
+   private ParseNode intersectionExpression() {
+               ParseNode result = comparisonExpression();
+               boolean hasIntersections = false;
+               while (true) {
+                       SkipWhite();
+                       if (_inIntersection) {
+                               // Don't getChar() as the space has already been eaten and recorded by SkipWhite().
+                               hasIntersections = true;
+                               ParseNode other = comparisonExpression();
+                               result = new ParseNode(IntersectionPtg.instance, result, other);
+                               continue;
+                       }
+                       if (hasIntersections) {
+                               return augmentWithMemPtg(result);
+                       }
+                       return result;
+               }
+       }
+       
        private ParseNode comparisonExpression() {
                ParseNode result = concatExpression();
                while (true) {
index 05b7d56d714b56d3049988f43943bcceac56e35a..ea3a488761ade354aefaa09a6f9e8328917ba1e1 100644 (file)
@@ -21,6 +21,7 @@ import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
 import org.apache.poi.ss.formula.ptg.AttrPtg;
 import org.apache.poi.ss.formula.ptg.ControlPtg;
 import org.apache.poi.ss.formula.ptg.FuncVarPtg;
+import org.apache.poi.ss.formula.ptg.IntersectionPtg;
 import org.apache.poi.ss.formula.ptg.MemAreaPtg;
 import org.apache.poi.ss.formula.ptg.MemFuncPtg;
 import org.apache.poi.ss.formula.ptg.Ptg;
@@ -117,7 +118,8 @@ final class OperandClassTransformer {
                if (token instanceof ValueOperatorPtg || token instanceof ControlPtg
                                || token instanceof MemFuncPtg
                                || token instanceof MemAreaPtg
-                               || token instanceof UnionPtg) {
+                               || token instanceof UnionPtg
+                               || token instanceof IntersectionPtg) {
                        // Value Operator Ptgs and Control are base tokens, so token will be unchanged
                        // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
 
index 1409d422749d7e60dc0aa8c58d9e3c6d4d7d5147..00aa4b2f7506dfd28476faf4e5d78f6fa0134e7b 100644 (file)
@@ -2501,4 +2501,30 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
         XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("57181.xlsm");
         assertEquals(9, wb.getNumberOfSheets());
     }
+    
+    @Test
+    public void bug52111() throws Exception {
+        Workbook wb = XSSFTestDataSamples.openSampleWorkbook("Intersection-52111-xssf.xlsx");
+        Sheet s = wb.getSheetAt(0);
+        assertFormula(wb, s.getRow(2).getCell(0), "(C2:D3 D3:E4)", "4.0");
+        assertFormula(wb, s.getRow(6).getCell(0), "Tabelle2!E:E Tabelle2!11:11", "5.0");
+        assertFormula(wb, s.getRow(8).getCell(0), "Tabelle2!E:F Tabelle2!11:12", null);
+    }
+    
+    private void assertFormula(Workbook wb, Cell intF, String expectedFormula, String expectedResultOrNull) {
+        assertEquals(Cell.CELL_TYPE_FORMULA, intF.getCellType());
+        if (null == expectedResultOrNull) {
+            assertEquals(Cell.CELL_TYPE_ERROR, intF.getCachedFormulaResultType());
+            expectedResultOrNull = "#VALUE!";
+        }
+        else {
+            assertEquals(Cell.CELL_TYPE_NUMERIC, intF.getCachedFormulaResultType());
+        }
+        
+        assertEquals(expectedFormula, intF.getCellFormula());
+
+        // Check we can evaluate it correctly
+        FormulaEvaluator eval = wb.getCreationHelper().createFormulaEvaluator();
+        assertEquals(expectedResultOrNull, eval.evaluate(intF).formatAsString());
+    }
 }
index ce017f7ed25d24c46e88d9ca66ecf2b0dbc95a95..09bf5a9b6a1f2d2522abb323eeb92dba553fa1ef 100644 (file)
@@ -702,7 +702,6 @@ public final class TestFormulaParser extends TestCase {
                assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
        }
        public void testParserErrors() {
-               parseExpectedException("1 2");
                parseExpectedException(" 12 . 345  ");
                parseExpectedException("1 .23  ");
 
@@ -1060,8 +1059,65 @@ public final class TestFormulaParser extends TestCase {
                );
                MemFuncPtg mf = (MemFuncPtg)ptgs[0];
                assertEquals(45, mf.getLenRefSubexpression());
+
+        // We don't check the type of the operands.
+        confirmTokenClasses("1,2", MemAreaPtg.class, IntPtg.class, IntPtg.class, UnionPtg.class);
        }
 
+       public void testIntersection() {
+       String formula = "Sheet1!$B$2:$C$3 OFFSET(Sheet1!$E$2:$E$4, 1,Sheet1!$A$1) Sheet1!$D$6";
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("Sheet1");
+        Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb), FormulaType.CELL, -1);
+
+        confirmTokenClasses(ptgs,
+                // TODO - AttrPtg.class, // Excel prepends this
+                MemFuncPtg.class,
+                Area3DPtg.class,
+                Area3DPtg.class,
+                IntPtg.class,
+                Ref3DPtg.class,
+                FuncVarPtg.class,
+                IntersectionPtg.class,
+                Ref3DPtg.class,
+                IntersectionPtg.class
+        );
+        MemFuncPtg mf = (MemFuncPtg)ptgs[0];
+        assertEquals(45, mf.getLenRefSubexpression());
+
+        // This used to be an error but now parses.  Union has the same behaviour.
+        confirmTokenClasses("1 2", MemAreaPtg.class, IntPtg.class, IntPtg.class, IntersectionPtg.class);
+       }
+       
+       public void testComparisonInParen() {
+           confirmTokenClasses("(A1 > B2)", 
+            RefPtg.class, 
+            RefPtg.class, 
+            GreaterThanPtg.class, 
+            ParenthesisPtg.class
+        );
+       }
+       
+       public void testUnionInParen() {
+           confirmTokenClasses("(A1:B2,B2:C3)", 
+          MemAreaPtg.class, 
+          AreaPtg.class, 
+          AreaPtg.class, 
+          UnionPtg.class, 
+          ParenthesisPtg.class
+        );
+       }
+
+    public void testIntersectionInParen() {
+        confirmTokenClasses("(A1:B2 B2:C3)", 
+            MemAreaPtg.class, 
+            AreaPtg.class, 
+            AreaPtg.class, 
+            IntersectionPtg.class, 
+            ParenthesisPtg.class
+        );
+    }
+    
        public void testRange_bug46643() {
                String formula = "Sheet1!A1:Sheet1!B3";
                HSSFWorkbook wb = new HSSFWorkbook();
index af6e8bb0364e01b30912e61c865dd250a712886b..c1e14bc674ed6679ae18e9d949a5e5c991c8489a 100644 (file)
@@ -2664,17 +2664,26 @@ public final class TestBugs extends BaseTestBugzillaIssues {
     public void bug52111() throws Exception {
         Workbook wb = openSample("Intersection-52111.xls");
         Sheet s = wb.getSheetAt(0);
-
-        // Check we can read it correctly
-        Cell intF = s.getRow(2).getCell(0);
+        assertFormula(wb, s.getRow(2).getCell(0), "(C2:D3 D3:E4)", "4.0");
+        assertFormula(wb, s.getRow(6).getCell(0), "Tabelle2!E:E Tabelle2!$A11:$IV11", "5.0");
+        assertFormula(wb, s.getRow(8).getCell(0), "Tabelle2!E:F Tabelle2!$A11:$IV12", null);
+    }
+    
+    private void assertFormula(Workbook wb, Cell intF, String expectedFormula, String expectedResultOrNull) {
         assertEquals(Cell.CELL_TYPE_FORMULA, intF.getCellType());
-        assertEquals(Cell.CELL_TYPE_NUMERIC, intF.getCachedFormulaResultType());
-
-        assertEquals("(C2:D3 D3:E4)", intF.getCellFormula());
+        if (null == expectedResultOrNull) {
+            assertEquals(Cell.CELL_TYPE_ERROR, intF.getCachedFormulaResultType());
+            expectedResultOrNull = "#VALUE!";
+        }
+        else {
+            assertEquals(Cell.CELL_TYPE_NUMERIC, intF.getCachedFormulaResultType());
+        }
+        
+        assertEquals(expectedFormula, intF.getCellFormula());
 
         // Check we can evaluate it correctly
         FormulaEvaluator eval = wb.getCreationHelper().createFormulaEvaluator();
-        assertEquals("4.0", eval.evaluate(intF).formatAsString());
+        assertEquals(expectedResultOrNull, eval.evaluate(intF).formatAsString());
     }
 
     @Test
diff --git a/test-data/spreadsheet/Intersection-52111-xssf.xlsx b/test-data/spreadsheet/Intersection-52111-xssf.xlsx
new file mode 100644 (file)
index 0000000..1ff0583
Binary files /dev/null and b/test-data/spreadsheet/Intersection-52111-xssf.xlsx differ
index 835130e8b716c5bf6f160b6d9d5211e825be7c6e..dd33ffe5abe7e7edf22c7369a60cece3d25e913c 100644 (file)
Binary files a/test-data/spreadsheet/Intersection-52111.xls and b/test-data/spreadsheet/Intersection-52111.xls differ