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-ffa450edef68tags/REL_3_13_FINAL
@@ -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 |