bug 57840: add structured reference formula parsing tests from FormulaParser.parseStructuredReference
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xssf_structured_references@1747655 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
95df2cc417
commit
954d5f8be4
@ -614,7 +614,7 @@ public final class FormulaParser {
|
|||||||
}
|
}
|
||||||
Table tbl = _book.getTable(tableName);
|
Table tbl = _book.getTable(tableName);
|
||||||
if (tbl == null) {
|
if (tbl == null) {
|
||||||
throw new FormulaParseException("Illegal table name!");
|
throw new FormulaParseException("Illegal table name: '" + tableName + "'");
|
||||||
}
|
}
|
||||||
String sheetName = tbl.getSheetName();
|
String sheetName = tbl.getSheetName();
|
||||||
|
|
||||||
@ -623,6 +623,8 @@ public final class FormulaParser {
|
|||||||
int startRow = tbl.getStartRowIndex();
|
int startRow = tbl.getStartRowIndex();
|
||||||
int endRow = tbl.getEndRowIndex();
|
int endRow = tbl.getEndRowIndex();
|
||||||
|
|
||||||
|
// Do NOT return before done reading all the structured reference tokens from the input stream.
|
||||||
|
// Throwing exceptions is okay.
|
||||||
int savePtr0 = _pointer;
|
int savePtr0 = _pointer;
|
||||||
GetChar();
|
GetChar();
|
||||||
|
|
||||||
@ -650,9 +652,9 @@ public final class FormulaParser {
|
|||||||
} else if (specName.equals(specTotals)) {
|
} else if (specName.equals(specTotals)) {
|
||||||
isTotalsSpec = true;
|
isTotalsSpec = true;
|
||||||
} else {
|
} else {
|
||||||
throw new FormulaParseException("Unknown special qunatifier "+ specName);
|
throw new FormulaParseException("Unknown special quantifier "+ specName);
|
||||||
}
|
}
|
||||||
nSpecQuantifiers++ ;
|
nSpecQuantifiers++;
|
||||||
if (look == ','){
|
if (look == ','){
|
||||||
GetChar();
|
GetChar();
|
||||||
} else {
|
} else {
|
||||||
@ -708,7 +710,7 @@ public final class FormulaParser {
|
|||||||
} else if (name.equals(specTotals)) {
|
} else if (name.equals(specTotals)) {
|
||||||
isTotalsSpec = true;
|
isTotalsSpec = true;
|
||||||
} else {
|
} else {
|
||||||
throw new FormulaParseException("Unknown special qunatifier "+ name);
|
throw new FormulaParseException("Unknown special quantifier "+ name);
|
||||||
}
|
}
|
||||||
nSpecQuantifiers++;
|
nSpecQuantifiers++;
|
||||||
} else {
|
} else {
|
||||||
@ -718,6 +720,22 @@ public final class FormulaParser {
|
|||||||
} else {
|
} else {
|
||||||
Match(']');
|
Match(']');
|
||||||
}
|
}
|
||||||
|
// Done reading from input stream
|
||||||
|
// Ok to return now
|
||||||
|
|
||||||
|
if (isTotalsSpec && !tbl.isHasTotalsRow()) {
|
||||||
|
return new ParseNode(ErrPtg.REF_INVALID);
|
||||||
|
}
|
||||||
|
if ((isThisRow || isThisRowSpec) && (_rowIndex < startRow || endRow < _rowIndex)) {
|
||||||
|
// structured reference is trying to reference a row above or below the table with [#This Row] or [@]
|
||||||
|
if (_rowIndex >= 0) {
|
||||||
|
return new ParseNode(ErrPtg.VALUE_INVALID);
|
||||||
|
} else {
|
||||||
|
throw new FormulaParseException(
|
||||||
|
"Formula contained [#This Row] or [@] structured reference but this row < 0. " +
|
||||||
|
"Row index must be specified for row-referencing structured references.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int actualStartRow = startRow;
|
int actualStartRow = startRow;
|
||||||
int actualEndRow = endRow;
|
int actualEndRow = endRow;
|
||||||
@ -756,10 +774,11 @@ public final class FormulaParser {
|
|||||||
actualStartRow++;
|
actualStartRow++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Selecting cols
|
//Selecting cols
|
||||||
|
|
||||||
if (nColQuantifiers == 2){
|
if (nColQuantifiers == 2) {
|
||||||
if (startColumnName == null || endColumnName == null){
|
if (startColumnName == null || endColumnName == null) {
|
||||||
throw new IllegalStateException("Fatal error");
|
throw new IllegalStateException("Fatal error");
|
||||||
}
|
}
|
||||||
int startIdx = tbl.findColumnIndex(startColumnName);
|
int startIdx = tbl.findColumnIndex(startColumnName);
|
||||||
@ -770,8 +789,8 @@ public final class FormulaParser {
|
|||||||
actualStartCol = startCol+ startIdx;
|
actualStartCol = startCol+ startIdx;
|
||||||
actualEndCol = startCol + endIdx;
|
actualEndCol = startCol + endIdx;
|
||||||
|
|
||||||
} else if(nColQuantifiers == 1){
|
} else if (nColQuantifiers == 1 && !isThisRow) {
|
||||||
if (startColumnName == null){
|
if (startColumnName == null) {
|
||||||
throw new IllegalStateException("Fatal error");
|
throw new IllegalStateException("Fatal error");
|
||||||
}
|
}
|
||||||
int idx = tbl.findColumnIndex(startColumnName);
|
int idx = tbl.findColumnIndex(startColumnName);
|
||||||
@ -781,10 +800,10 @@ public final class FormulaParser {
|
|||||||
actualStartCol = startCol + idx;
|
actualStartCol = startCol + idx;
|
||||||
actualEndCol = actualStartCol;
|
actualEndCol = actualStartCol;
|
||||||
}
|
}
|
||||||
CellReference tl = new CellReference(actualStartRow, actualStartCol);
|
CellReference topLeft = new CellReference(actualStartRow, actualStartCol);
|
||||||
CellReference br = new CellReference(actualEndRow, actualEndCol);
|
CellReference bottomRight = new CellReference(actualEndRow, actualEndCol);
|
||||||
SheetIdentifier sheetIden = new SheetIdentifier( null, new NameIdentifier(sheetName, true));
|
SheetIdentifier sheetIden = new SheetIdentifier( null, new NameIdentifier(sheetName, true));
|
||||||
Ptg ptg = _book.get3DReferencePtg(new AreaReference(tl, br), sheetIden);
|
Ptg ptg = _book.get3DReferencePtg(new AreaReference(topLeft, bottomRight), sheetIden);
|
||||||
return new ParseNode(ptg);
|
return new ParseNode(ptg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -793,7 +812,7 @@ public final class FormulaParser {
|
|||||||
* Caller should save pointer.
|
* Caller should save pointer.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private String parseAsColumnQuantifier(){
|
private String parseAsColumnQuantifier() {
|
||||||
if ( look != '[') {
|
if ( look != '[') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ import static org.junit.Assert.assertNotNull;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
||||||
@ -44,6 +46,9 @@ public final class TestXSSFFormulaParser {
|
|||||||
private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla) {
|
private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla) {
|
||||||
return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1);
|
return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1);
|
||||||
}
|
}
|
||||||
|
private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla, int rowIndex) {
|
||||||
|
return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1, rowIndex);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void basicParsing() {
|
public void basicParsing() {
|
||||||
@ -137,7 +142,7 @@ public final class TestXSSFFormulaParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void formaulReferncesSameWorkbook() {
|
public void formulaReferencesSameWorkbook() {
|
||||||
// Use a test file with "other workbook" style references
|
// Use a test file with "other workbook" style references
|
||||||
// to itself
|
// to itself
|
||||||
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56737.xlsx");
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56737.xlsx");
|
||||||
@ -530,4 +535,169 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[2] instanceof AreaPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[2] instanceof AreaPtg);
|
||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[3] instanceof NameXPxg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[3] instanceof NameXPxg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseStructuredReferences() throws IOException {
|
||||||
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("StructuredReferences.xlsx");
|
||||||
|
|
||||||
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
|
Ptg[] ptgs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The following cases are tested (copied from FormulaParser.parseStructuredReference)
|
||||||
|
1 Table1[col]
|
||||||
|
2 Table1[[#Totals],[col]]
|
||||||
|
3 Table1[#Totals]
|
||||||
|
4 Table1[#All]
|
||||||
|
5 Table1[#Data]
|
||||||
|
6 Table1[#Headers]
|
||||||
|
7 Table1[#Totals]
|
||||||
|
8 Table1[#This Row]
|
||||||
|
9 Table1[[#All],[col]]
|
||||||
|
10 Table1[[#Headers],[col]]
|
||||||
|
11 Table1[[#Totals],[col]]
|
||||||
|
12 Table1[[#All],[col1]:[col2]]
|
||||||
|
13 Table1[[#Data],[col1]:[col2]]
|
||||||
|
14 Table1[[#Headers],[col1]:[col2]]
|
||||||
|
15 Table1[[#Totals],[col1]:[col2]]
|
||||||
|
16 Table1[[#Headers],[#Data],[col2]]
|
||||||
|
17 Table1[[#This Row], [col1]]
|
||||||
|
18 Table1[ [col1]:[col2] ]
|
||||||
|
*/
|
||||||
|
|
||||||
|
final String tbl = "\\_Prime.1";
|
||||||
|
final String noTotalsRowReason = ": Tables without a Totals row should return #REF! on [#Totals]";
|
||||||
|
|
||||||
|
////// Case 1: Evaluate Table1[col] with apostrophe-escaped #-signs ////////
|
||||||
|
ptgs = parse(fpb, "SUM("+tbl+"[calc='#*'#])");
|
||||||
|
assertEquals(2, ptgs.length);
|
||||||
|
|
||||||
|
// Area3DPxg [sheet=Table ! A2:A7]
|
||||||
|
assertTrue(ptgs[0] instanceof Area3DPxg);
|
||||||
|
Area3DPxg ptg0 = (Area3DPxg) ptgs[0];
|
||||||
|
assertEquals("Table", ptg0.getSheetName());
|
||||||
|
assertEquals("A2:A7", ptg0.format2DRefAsString());
|
||||||
|
// Note: structured references are evaluated and resolved to regular 3D area references.
|
||||||
|
assertEquals("Table!A2:A7", ptg0.toFormulaString());
|
||||||
|
|
||||||
|
// AttrPtg [sum ]
|
||||||
|
assertTrue(ptgs[1] instanceof AttrPtg);
|
||||||
|
AttrPtg ptg1 = (AttrPtg) ptgs[1];
|
||||||
|
assertTrue(ptg1.isSum());
|
||||||
|
|
||||||
|
////// Case 1: Evaluate "Table1[col]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[Name]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[col]", "Table!B2:B7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 2: Evaluate "Table1[[#Totals],[col]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Totals],[col]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#Totals],[col]]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
|
||||||
|
|
||||||
|
////// Case 3: Evaluate "Table1[#Totals]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[#Totals]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[#Totals]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
|
||||||
|
|
||||||
|
////// Case 4: Evaluate "Table1[#All]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[#All]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[#All]", "Table!A1:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 5: Evaluate "Table1[#Data]" (excludes Header and Data rows) ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[#Data]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[#Data]", "Table!A2:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 6: Evaluate "Table1[#Headers]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[#Headers]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[#Headers]", "Table!A1:C1", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 7: Evaluate "Table1[#Totals]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[#Totals]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[#Totals]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
|
||||||
|
|
||||||
|
////// Case 8: Evaluate "Table1[#This Row]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[#This Row]", 2);
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[#This Row]", "Table!A3:C3", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Evaluate "Table1[@]" (equivalent to "Table1[#This Row]") ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[@]", 2);
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table!A3:C3", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Evaluate "Table1[#This Row]" when rowIndex is outside Table ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[#This Row]", 10);
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[#This Row]", ErrPtg.VALUE_INVALID, ptgs[0]);
|
||||||
|
|
||||||
|
////// Evaluate "Table1[@]" when rowIndex is outside Table ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[@]", 10);
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[@]", ErrPtg.VALUE_INVALID, ptgs[0]);
|
||||||
|
|
||||||
|
////// Evaluate "Table1[[#Data],[col]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Data], [Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#Data],[col]]", "Table!C2:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
|
||||||
|
////// Case 9: Evaluate "Table1[[#All],[col]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#All], [Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#All],[col]]", "Table!C1:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 10: Evaluate "Table1[[#Headers],[col]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Headers], [Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
// also acceptable: Table1!B1
|
||||||
|
assertEquals("Table1[[#Headers],[col]]", "Table!C1:C1", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 11: Evaluate "Table1[[#Totals],[col]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Totals],[Name]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#Totals],[col]]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
|
||||||
|
|
||||||
|
////// Case 12: Evaluate "Table1[[#All],[col1]:[col2]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#All], [Name]:[Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#All],[col1]:[col2]]", "Table!B1:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 13: Evaluate "Table1[[#Data],[col]:[col2]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Data], [Name]:[Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#Data],[col]:[col2]]", "Table!B2:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 14: Evaluate "Table1[[#Headers],[col1]:[col2]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Headers], [Name]:[Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#Headers],[col1]:[col2]]", "Table!B1:C1", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 15: Evaluate "Table1[[#Totals],[col]:[col2]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Totals], [Name]:[Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#Totals],[col]:[col2]]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
|
||||||
|
|
||||||
|
////// Case 16: Evaluate "Table1[[#Headers],[#Data],[col]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#Headers],[#Data],[Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[#Headers],[#Data],[col]]", "Table!C1:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 17: Evaluate "Table1[[#This Row], [col1]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[#This Row], [Number]]", 2);
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
// also acceptable: Table!C3
|
||||||
|
assertEquals("Table1[[#This Row], [col1]]", "Table!C3:C3", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
////// Case 18: Evaluate "Table1[[col]:[col2]]" ////////
|
||||||
|
ptgs = parse(fpb, tbl+"[[Name]:[Number]]");
|
||||||
|
assertEquals(1, ptgs.length);
|
||||||
|
assertEquals("Table1[[col]:[col2]]", "Table!B2:C7", ptgs[0].toFormulaString());
|
||||||
|
|
||||||
|
wb.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user