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
import org.apache.poi.ss.formula.ptg.FuncVarPtg; | import org.apache.poi.ss.formula.ptg.FuncVarPtg; | ||||
import org.apache.poi.ss.formula.ptg.GreaterEqualPtg; | import org.apache.poi.ss.formula.ptg.GreaterEqualPtg; | ||||
import org.apache.poi.ss.formula.ptg.GreaterThanPtg; | 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.IntPtg; | ||||
import org.apache.poi.ss.formula.ptg.LessEqualPtg; | import org.apache.poi.ss.formula.ptg.LessEqualPtg; | ||||
import org.apache.poi.ss.formula.ptg.LessThanPtg; | import org.apache.poi.ss.formula.ptg.LessThanPtg; | ||||
*/ | */ | ||||
private char look; | 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 FormulaParsingWorkbook _book; | ||||
private SpreadsheetVersion _ssVersion; | private SpreadsheetVersion _ssVersion; | ||||
fp.parse(); | fp.parse(); | ||||
return fp.getRPNPtg(formulaType); | return fp.getRPNPtg(formulaType); | ||||
} | } | ||||
/** Read New Character From Input Stream */ | /** Read New Character From Input Stream */ | ||||
private void GetChar() { | 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. | // Check to see if we've walked off the end of the string. | ||||
if (_pointer > _formulaLength) { | if (_pointer > _formulaLength) { | ||||
throw new RuntimeException("too far"); | throw new RuntimeException("too far"); | ||||
// Just return if so and reset 'look' to something to keep | // Just return if so and reset 'look' to something to keep | ||||
// SkipWhitespace from spinning | // SkipWhitespace from spinning | ||||
look = (char)0; | look = (char)0; | ||||
_inIntersection = false; | |||||
} | } | ||||
_pointer++; | _pointer++; | ||||
//System.out.println("Got char: "+ look); | //System.out.println("Got char: "+ look); | ||||
return parseUnary(true); | return parseUnary(true); | ||||
case '(': | case '(': | ||||
Match('('); | Match('('); | ||||
ParseNode inside = comparisonExpression(); | |||||
ParseNode inside = unionExpression(); | |||||
Match(')'); | Match(')'); | ||||
return new ParseNode(ParenthesisPtg.instance, inside); | return new ParseNode(ParenthesisPtg.instance, inside); | ||||
case '"': | case '"': | ||||
result = new ParseNode(operator, result, other); | result = new ParseNode(operator, result, other); | ||||
} | } | ||||
} | } | ||||
private ParseNode unionExpression() { | private ParseNode unionExpression() { | ||||
ParseNode result = comparisonExpression(); | |||||
ParseNode result = intersectionExpression(); | |||||
boolean hasUnions = false; | boolean hasUnions = false; | ||||
while (true) { | while (true) { | ||||
SkipWhite(); | SkipWhite(); | ||||
case ',': | case ',': | ||||
GetChar(); | GetChar(); | ||||
hasUnions = true; | hasUnions = true; | ||||
ParseNode other = comparisonExpression(); | |||||
ParseNode other = intersectionExpression(); | |||||
result = new ParseNode(UnionPtg.instance, result, other); | result = new ParseNode(UnionPtg.instance, result, other); | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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() { | private ParseNode comparisonExpression() { | ||||
ParseNode result = concatExpression(); | ParseNode result = concatExpression(); | ||||
while (true) { | while (true) { |
import org.apache.poi.ss.formula.ptg.AttrPtg; | import org.apache.poi.ss.formula.ptg.AttrPtg; | ||||
import org.apache.poi.ss.formula.ptg.ControlPtg; | import org.apache.poi.ss.formula.ptg.ControlPtg; | ||||
import org.apache.poi.ss.formula.ptg.FuncVarPtg; | 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.MemAreaPtg; | ||||
import org.apache.poi.ss.formula.ptg.MemFuncPtg; | import org.apache.poi.ss.formula.ptg.MemFuncPtg; | ||||
import org.apache.poi.ss.formula.ptg.Ptg; | import org.apache.poi.ss.formula.ptg.Ptg; | ||||
if (token instanceof ValueOperatorPtg || token instanceof ControlPtg | if (token instanceof ValueOperatorPtg || token instanceof ControlPtg | ||||
|| token instanceof MemFuncPtg | || token instanceof MemFuncPtg | ||||
|| token instanceof MemAreaPtg | || 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 | // Value Operator Ptgs and Control are base tokens, so token will be unchanged | ||||
// but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag | // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag | ||||
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("57181.xlsm"); | XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("57181.xlsm"); | ||||
assertEquals(9, wb.getNumberOfSheets()); | 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()); | |||||
} | |||||
} | } |
assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString); | assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString); | ||||
} | } | ||||
public void testParserErrors() { | public void testParserErrors() { | ||||
parseExpectedException("1 2"); | |||||
parseExpectedException(" 12 . 345 "); | parseExpectedException(" 12 . 345 "); | ||||
parseExpectedException("1 .23 "); | parseExpectedException("1 .23 "); | ||||
); | ); | ||||
MemFuncPtg mf = (MemFuncPtg)ptgs[0]; | MemFuncPtg mf = (MemFuncPtg)ptgs[0]; | ||||
assertEquals(45, mf.getLenRefSubexpression()); | 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() { | public void testRange_bug46643() { | ||||
String formula = "Sheet1!A1:Sheet1!B3"; | String formula = "Sheet1!A1:Sheet1!B3"; | ||||
HSSFWorkbook wb = new HSSFWorkbook(); | HSSFWorkbook wb = new HSSFWorkbook(); |
public void bug52111() throws Exception { | public void bug52111() throws Exception { | ||||
Workbook wb = openSample("Intersection-52111.xls"); | Workbook wb = openSample("Intersection-52111.xls"); | ||||
Sheet s = wb.getSheetAt(0); | 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_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 | // Check we can evaluate it correctly | ||||
FormulaEvaluator eval = wb.getCreationHelper().createFormulaEvaluator(); | FormulaEvaluator eval = wb.getCreationHelper().createFormulaEvaluator(); | ||||
assertEquals("4.0", eval.evaluate(intF).formatAsString()); | |||||
assertEquals(expectedResultOrNull, eval.evaluate(intF).formatAsString()); | |||||
} | } | ||||
@Test | @Test |