Make intersection formulae work in XSSF. Patch from Matt Hillsdon plus additional tests.
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
This commit is contained in:
parent
badd478547
commit
5706c2ac9b
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
BIN
test-data/spreadsheet/Intersection-52111-xssf.xlsx
Normal file
BIN
test-data/spreadsheet/Intersection-52111-xssf.xlsx
Normal file
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user