bug 57840: merge changes from ^/poi/branches/xssf_structured_references to ^/poi/trunk
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1747657 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
commit
12d7fb30a4
@ -38,6 +38,7 @@ import org.apache.poi.ss.formula.ptg.NameXPtg;
|
|||||||
import org.apache.poi.ss.formula.ptg.Ptg;
|
import org.apache.poi.ss.formula.ptg.Ptg;
|
||||||
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
|
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
|
||||||
import org.apache.poi.ss.formula.udf.UDFFinder;
|
import org.apache.poi.ss.formula.udf.UDFFinder;
|
||||||
|
import org.apache.poi.ss.usermodel.Table;
|
||||||
import org.apache.poi.ss.util.AreaReference;
|
import org.apache.poi.ss.util.AreaReference;
|
||||||
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.ss.util.CellReference;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
@ -264,7 +265,16 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
|||||||
return extIx;
|
return extIx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SpreadsheetVersion getSpreadsheetVersion(){
|
public SpreadsheetVersion getSpreadsheetVersion(){
|
||||||
return SpreadsheetVersion.EXCEL97;
|
return SpreadsheetVersion.EXCEL97;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException: data tables are not supported in Excel 97-2003 format
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Table getTable(String name) {
|
||||||
|
throw new IllegalStateException("XSSF-style tables are not supported for HSSF");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import org.apache.poi.ss.formula.function.FunctionMetadata;
|
|||||||
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
||||||
import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
|
import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
|
||||||
import org.apache.poi.ss.formula.ptg.AddPtg;
|
import org.apache.poi.ss.formula.ptg.AddPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.Area3DPxg;
|
||||||
import org.apache.poi.ss.formula.ptg.AreaPtg;
|
import org.apache.poi.ss.formula.ptg.AreaPtg;
|
||||||
import org.apache.poi.ss.formula.ptg.ArrayPtg;
|
import org.apache.poi.ss.formula.ptg.ArrayPtg;
|
||||||
import org.apache.poi.ss.formula.ptg.AttrPtg;
|
import org.apache.poi.ss.formula.ptg.AttrPtg;
|
||||||
@ -69,6 +70,7 @@ import org.apache.poi.ss.formula.ptg.UnionPtg;
|
|||||||
import org.apache.poi.ss.formula.ptg.ValueOperatorPtg;
|
import org.apache.poi.ss.formula.ptg.ValueOperatorPtg;
|
||||||
import org.apache.poi.ss.usermodel.FormulaError;
|
import org.apache.poi.ss.usermodel.FormulaError;
|
||||||
import org.apache.poi.ss.usermodel.Name;
|
import org.apache.poi.ss.usermodel.Name;
|
||||||
|
import org.apache.poi.ss.usermodel.Table;
|
||||||
import org.apache.poi.ss.util.AreaReference;
|
import org.apache.poi.ss.util.AreaReference;
|
||||||
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.ss.util.CellReference;
|
||||||
import org.apache.poi.ss.util.CellReference.NameType;
|
import org.apache.poi.ss.util.CellReference.NameType;
|
||||||
@ -117,6 +119,7 @@ public final class FormulaParser {
|
|||||||
private final SpreadsheetVersion _ssVersion;
|
private final SpreadsheetVersion _ssVersion;
|
||||||
|
|
||||||
private final int _sheetIndex;
|
private final int _sheetIndex;
|
||||||
|
private final int _rowIndex; // 0-based
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,18 +134,45 @@ public final class FormulaParser {
|
|||||||
* model.Workbook, then use the convenience method on
|
* model.Workbook, then use the convenience method on
|
||||||
* usermodel.HSSFFormulaEvaluator
|
* usermodel.HSSFFormulaEvaluator
|
||||||
*/
|
*/
|
||||||
private FormulaParser(String formula, FormulaParsingWorkbook book, int sheetIndex){
|
private FormulaParser(String formula, FormulaParsingWorkbook book, int sheetIndex, int rowIndex){
|
||||||
_formulaString = formula;
|
_formulaString = formula;
|
||||||
_pointer=0;
|
_pointer=0;
|
||||||
_book = book;
|
_book = book;
|
||||||
_ssVersion = book == null ? SpreadsheetVersion.EXCEL97 : book.getSpreadsheetVersion();
|
_ssVersion = book == null ? SpreadsheetVersion.EXCEL97 : book.getSpreadsheetVersion();
|
||||||
_formulaLength = _formulaString.length();
|
_formulaLength = _formulaString.length();
|
||||||
_sheetIndex = sheetIndex;
|
_sheetIndex = sheetIndex;
|
||||||
|
_rowIndex = rowIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a formula into a array of tokens
|
* Parse a formula into an array of tokens
|
||||||
* Side effect: creates name (Workbook.createName) if formula contains unrecognized names (names are likely UDFs)
|
* Side effect: creates name ({@link org.apache.poi.ss.usermodel.Workbook#createName})
|
||||||
|
* if formula contains unrecognized names (names are likely UDFs)
|
||||||
|
*
|
||||||
|
* @param formula the formula to parse
|
||||||
|
* @param workbook the parent workbook
|
||||||
|
* @param formulaType the type of the formula, see {@link FormulaType}
|
||||||
|
* @param sheetIndex the 0-based index of the sheet this formula belongs to.
|
||||||
|
* The sheet index is required to resolve sheet-level names. <code>-1</code> means that
|
||||||
|
* the scope of the name will be ignored and the parser will match names only by name
|
||||||
|
* @param rowIndex - the related cell's row index in 0-based form (-1 if the formula is not cell related)
|
||||||
|
* used to handle structured references that have the "#This Row" quantifier.
|
||||||
|
* Use rowIndex=-1 or {@link #parseStructuredReference(String, FormulaParsingWorkbook, int, int) if formula
|
||||||
|
* does not contain structured references.
|
||||||
|
*
|
||||||
|
* @return array of parsed tokens
|
||||||
|
* @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
|
||||||
|
*/
|
||||||
|
public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType, int sheetIndex, int rowIndex) {
|
||||||
|
FormulaParser fp = new FormulaParser(formula, workbook, sheetIndex, rowIndex);
|
||||||
|
fp.parse();
|
||||||
|
return fp.getRPNPtg(formulaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a formula into an array of tokens
|
||||||
|
* Side effect: creates name ({@link org.apache.poi.ss.usermodel.Workbook#createName})
|
||||||
|
* if formula contains unrecognized names (names are likely UDFs)
|
||||||
*
|
*
|
||||||
* @param formula the formula to parse
|
* @param formula the formula to parse
|
||||||
* @param workbook the parent workbook
|
* @param workbook the parent workbook
|
||||||
@ -155,9 +185,25 @@ public final class FormulaParser {
|
|||||||
* @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
|
* @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
|
||||||
*/
|
*/
|
||||||
public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType, int sheetIndex) {
|
public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType, int sheetIndex) {
|
||||||
FormulaParser fp = new FormulaParser(formula, workbook, sheetIndex);
|
return parse(formula, workbook, formulaType, sheetIndex, -1);
|
||||||
fp.parse();
|
}
|
||||||
return fp.getRPNPtg(formulaType);
|
|
||||||
|
/**
|
||||||
|
* Parse a structured reference. Converts the structured
|
||||||
|
* reference to the area that represent it.
|
||||||
|
*
|
||||||
|
* @param tableText - The structured reference text
|
||||||
|
* @param workbook - the parent workbook
|
||||||
|
* @param rowIndex - the 0-based cell's row index ( used to handle "#This Row" quantifiers )
|
||||||
|
* @return the area that being represented by the structured reference.
|
||||||
|
*/
|
||||||
|
public static Area3DPxg parseStructuredReference(String tableText, FormulaParsingWorkbook workbook, int rowIndex) {
|
||||||
|
final int sheetIndex = -1; //don't care?
|
||||||
|
Ptg[] arr = FormulaParser.parse(tableText, workbook, FormulaType.CELL, sheetIndex, rowIndex);
|
||||||
|
if (arr.length != 1 || !(arr[0] instanceof Area3DPxg) ) {
|
||||||
|
throw new IllegalStateException("Illegal structured reference");
|
||||||
|
}
|
||||||
|
return (Area3DPxg) arr[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read New Character From Input Stream */
|
/** Read New Character From Input Stream */
|
||||||
@ -529,6 +575,285 @@ public final class FormulaParser {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private final static String specHeaders = "Headers";
|
||||||
|
private final static String specAll = "All";
|
||||||
|
private final static String specData = "Data";
|
||||||
|
private final static String specTotals = "Totals";
|
||||||
|
private final static String specThisRow = "This Row";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a structured reference, returns it as area reference.
|
||||||
|
* Examples:
|
||||||
|
* <pre>
|
||||||
|
* Table1[col]
|
||||||
|
* Table1[[#Totals],[col]]
|
||||||
|
* Table1[#Totals]
|
||||||
|
* Table1[#All]
|
||||||
|
* Table1[#Data]
|
||||||
|
* Table1[#Headers]
|
||||||
|
* Table1[#Totals]
|
||||||
|
* Table1[#This Row]
|
||||||
|
* Table1[[#All],[col]]
|
||||||
|
* Table1[[#Headers],[col]]
|
||||||
|
* Table1[[#Totals],[col]]
|
||||||
|
* Table1[[#All],[col1]:[col2]]
|
||||||
|
* Table1[[#Data],[col1]:[col2]]
|
||||||
|
* Table1[[#Headers],[col1]:[col2]]
|
||||||
|
* Table1[[#Totals],[col1]:[col2]]
|
||||||
|
* Table1[[#Headers],[#Data],[col2]]
|
||||||
|
* Table1[[#This Row], [col1]]
|
||||||
|
* Table1[ [col1]:[col2] ]
|
||||||
|
* </pre>
|
||||||
|
* @param tableName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private ParseNode parseStructuredReference(String tableName) {
|
||||||
|
|
||||||
|
if ( ! (_ssVersion.equals(SpreadsheetVersion.EXCEL2007)) ) {
|
||||||
|
throw new FormulaParseException("Structured references work only on XSSF (Excel 2007+)!");
|
||||||
|
}
|
||||||
|
Table tbl = _book.getTable(tableName);
|
||||||
|
if (tbl == null) {
|
||||||
|
throw new FormulaParseException("Illegal table name: '" + tableName + "'");
|
||||||
|
}
|
||||||
|
String sheetName = tbl.getSheetName();
|
||||||
|
|
||||||
|
int startCol = tbl.getStartColIndex();
|
||||||
|
int endCol = tbl.getEndColIndex();
|
||||||
|
int startRow = tbl.getStartRowIndex();
|
||||||
|
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;
|
||||||
|
GetChar();
|
||||||
|
|
||||||
|
boolean isTotalsSpec = false;
|
||||||
|
boolean isThisRowSpec = false;
|
||||||
|
boolean isDataSpec = false;
|
||||||
|
boolean isHeadersSpec = false;
|
||||||
|
boolean isAllSpec = false;
|
||||||
|
int nSpecQuantifiers = 0; // The number of special quantifiers
|
||||||
|
while (true) {
|
||||||
|
int savePtr1 = _pointer;
|
||||||
|
String specName = parseAsSpecialQuantifier();
|
||||||
|
if (specName == null) {
|
||||||
|
resetPointer(savePtr1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (specName.equals(specAll)) {
|
||||||
|
isAllSpec = true;
|
||||||
|
} else if (specName.equals(specData)) {
|
||||||
|
isDataSpec = true;
|
||||||
|
} else if (specName.equals(specHeaders)) {
|
||||||
|
isHeadersSpec = true;
|
||||||
|
} else if (specName.equals(specThisRow)) {
|
||||||
|
isThisRowSpec = true;
|
||||||
|
} else if (specName.equals(specTotals)) {
|
||||||
|
isTotalsSpec = true;
|
||||||
|
} else {
|
||||||
|
throw new FormulaParseException("Unknown special quantifier "+ specName);
|
||||||
|
}
|
||||||
|
nSpecQuantifiers++;
|
||||||
|
if (look == ','){
|
||||||
|
GetChar();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean isThisRow = false;
|
||||||
|
SkipWhite();
|
||||||
|
if (look == '@') {
|
||||||
|
isThisRow = true;
|
||||||
|
GetChar();
|
||||||
|
}
|
||||||
|
// parse column quantifier
|
||||||
|
String startColumnName = null;
|
||||||
|
String endColumnName = null;
|
||||||
|
int nColQuantifiers = 0;
|
||||||
|
int savePtr1 = _pointer;
|
||||||
|
startColumnName = parseAsColumnQuantifier();
|
||||||
|
if (startColumnName == null) {
|
||||||
|
resetPointer(savePtr1);
|
||||||
|
} else {
|
||||||
|
nColQuantifiers++;
|
||||||
|
if (look == ','){
|
||||||
|
throw new FormulaParseException("The formula "+ _formulaString + "is illegal: you should not use ',' with column quantifiers");
|
||||||
|
} else if (look == ':') {
|
||||||
|
GetChar();
|
||||||
|
endColumnName = parseAsColumnQuantifier();
|
||||||
|
nColQuantifiers++;
|
||||||
|
if (endColumnName == null) {
|
||||||
|
throw new FormulaParseException("The formula "+ _formulaString + "is illegal: the string after ':' must be column quantifier");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nColQuantifiers == 0 && nSpecQuantifiers == 0){
|
||||||
|
resetPointer(savePtr0);
|
||||||
|
savePtr0 = _pointer;
|
||||||
|
startColumnName = parseAsColumnQuantifier();
|
||||||
|
if (startColumnName != null) {
|
||||||
|
nColQuantifiers++;
|
||||||
|
} else {
|
||||||
|
resetPointer(savePtr0);
|
||||||
|
String name = parseAsSpecialQuantifier();
|
||||||
|
if (name!=null) {
|
||||||
|
if (name.equals(specAll)) {
|
||||||
|
isAllSpec = true;
|
||||||
|
} else if (name.equals(specData)) {
|
||||||
|
isDataSpec = true;
|
||||||
|
} else if (name.equals(specHeaders)) {
|
||||||
|
isHeadersSpec = true;
|
||||||
|
} else if (name.equals(specThisRow)) {
|
||||||
|
isThisRowSpec = true;
|
||||||
|
} else if (name.equals(specTotals)) {
|
||||||
|
isTotalsSpec = true;
|
||||||
|
} else {
|
||||||
|
throw new FormulaParseException("Unknown special quantifier "+ name);
|
||||||
|
}
|
||||||
|
nSpecQuantifiers++;
|
||||||
|
} else {
|
||||||
|
throw new FormulaParseException("The formula "+ _formulaString + " is illegal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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 actualEndRow = endRow;
|
||||||
|
int actualStartCol = startCol;
|
||||||
|
int actualEndCol = endCol;
|
||||||
|
if (nSpecQuantifiers > 0) {
|
||||||
|
//Selecting rows
|
||||||
|
if (nSpecQuantifiers == 1 && isAllSpec) {
|
||||||
|
//do nothing
|
||||||
|
} else if (isDataSpec && isHeadersSpec) {
|
||||||
|
if (tbl.isHasTotalsRow()) {
|
||||||
|
actualEndRow = endRow - 1;
|
||||||
|
}
|
||||||
|
} else if (isDataSpec && isTotalsSpec) {
|
||||||
|
actualStartRow = startRow + 1;
|
||||||
|
} else if (nSpecQuantifiers == 1 && isDataSpec) {
|
||||||
|
actualStartRow = startRow + 1;
|
||||||
|
if (tbl.isHasTotalsRow()) {
|
||||||
|
actualEndRow = endRow - 1;
|
||||||
|
}
|
||||||
|
} else if (nSpecQuantifiers == 1 && isHeadersSpec) {
|
||||||
|
actualEndRow = actualStartRow;
|
||||||
|
} else if (nSpecQuantifiers == 1 && isTotalsSpec) {
|
||||||
|
actualStartRow = actualEndRow;
|
||||||
|
} else if ((nSpecQuantifiers == 1 && isThisRowSpec) || isThisRow) {
|
||||||
|
actualStartRow = _rowIndex; //The rowNum is 0 based
|
||||||
|
actualEndRow = _rowIndex;
|
||||||
|
} else {
|
||||||
|
throw new FormulaParseException("The formula "+ _formulaString + " is illegal");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isThisRow) { // there is a @
|
||||||
|
actualStartRow = _rowIndex; //The rowNum is 0 based
|
||||||
|
actualEndRow = _rowIndex;
|
||||||
|
} else { // Really no special quantifiers
|
||||||
|
actualStartRow++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Selecting cols
|
||||||
|
|
||||||
|
if (nColQuantifiers == 2) {
|
||||||
|
if (startColumnName == null || endColumnName == null) {
|
||||||
|
throw new IllegalStateException("Fatal error");
|
||||||
|
}
|
||||||
|
int startIdx = tbl.findColumnIndex(startColumnName);
|
||||||
|
int endIdx = tbl.findColumnIndex(endColumnName);
|
||||||
|
if (startIdx == -1 || endIdx == -1) {
|
||||||
|
throw new FormulaParseException("One of the columns "+ startColumnName +", "+ endColumnName +" doesn't exist in table "+ tbl.getName());
|
||||||
|
}
|
||||||
|
actualStartCol = startCol+ startIdx;
|
||||||
|
actualEndCol = startCol + endIdx;
|
||||||
|
|
||||||
|
} else if (nColQuantifiers == 1 && !isThisRow) {
|
||||||
|
if (startColumnName == null) {
|
||||||
|
throw new IllegalStateException("Fatal error");
|
||||||
|
}
|
||||||
|
int idx = tbl.findColumnIndex(startColumnName);
|
||||||
|
if (idx == -1) {
|
||||||
|
throw new FormulaParseException("The column "+ startColumnName + " doesn't exist in table "+ tbl.getName());
|
||||||
|
}
|
||||||
|
actualStartCol = startCol + idx;
|
||||||
|
actualEndCol = actualStartCol;
|
||||||
|
}
|
||||||
|
CellReference topLeft = new CellReference(actualStartRow, actualStartCol);
|
||||||
|
CellReference bottomRight = new CellReference(actualEndRow, actualEndCol);
|
||||||
|
SheetIdentifier sheetIden = new SheetIdentifier( null, new NameIdentifier(sheetName, true));
|
||||||
|
Ptg ptg = _book.get3DReferencePtg(new AreaReference(topLeft, bottomRight), sheetIden);
|
||||||
|
return new ParseNode(ptg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to parse the next as column - can contain whitespace
|
||||||
|
* Caller should save pointer.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String parseAsColumnQuantifier() {
|
||||||
|
if ( look != '[') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
GetChar();
|
||||||
|
String name = "";
|
||||||
|
if (look == '#') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (look == '@') {
|
||||||
|
GetChar();
|
||||||
|
}
|
||||||
|
while (look!=']') {
|
||||||
|
name += look;
|
||||||
|
GetChar();
|
||||||
|
}
|
||||||
|
Match(']');
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Tries to parse the next as special quantifier
|
||||||
|
* Caller should save pointer.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String parseAsSpecialQuantifier(){
|
||||||
|
if ( look != '[') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
GetChar();
|
||||||
|
if( look != '#') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
GetChar();
|
||||||
|
String name = parseAsName();
|
||||||
|
if ( name.equals("This")) {
|
||||||
|
name = name + ' ' + parseAsName();
|
||||||
|
}
|
||||||
|
Match(']');
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses simple factors that are not primitive ranges or range components
|
* Parses simple factors that are not primitive ranges or range components
|
||||||
* i.e. '!', ':'(and equiv '...') do not appear
|
* i.e. '!', ':'(and equiv '...') do not appear
|
||||||
@ -558,6 +883,9 @@ public final class FormulaParser {
|
|||||||
if (look == '(') {
|
if (look == '(') {
|
||||||
return function(name);
|
return function(name);
|
||||||
}
|
}
|
||||||
|
if(look == '['){
|
||||||
|
return parseStructuredReference(name);
|
||||||
|
}
|
||||||
if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
|
if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
|
||||||
return new ParseNode(BoolPtg.valueOf(name.equalsIgnoreCase("TRUE")));
|
return new ParseNode(BoolPtg.valueOf(name.equalsIgnoreCase("TRUE")));
|
||||||
}
|
}
|
||||||
@ -581,9 +909,9 @@ public final class FormulaParser {
|
|||||||
private String parseAsName() {
|
private String parseAsName() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
// defined names may begin with a letter or underscore
|
// defined names may begin with a letter or underscore or backslash
|
||||||
if (!Character.isLetter(look) && look != '_') {
|
if (!Character.isLetter(look) && look != '_' && look != '\\') {
|
||||||
throw expected("number, string, or defined name");
|
throw expected("number, string, defined name, or data table");
|
||||||
}
|
}
|
||||||
while (isValidDefinedNameChar(look)) {
|
while (isValidDefinedNameChar(look)) {
|
||||||
sb.append(look);
|
sb.append(look);
|
||||||
@ -1175,7 +1503,9 @@ public final class FormulaParser {
|
|||||||
Match('}');
|
Match('}');
|
||||||
return arrayNode;
|
return arrayNode;
|
||||||
}
|
}
|
||||||
if (IsAlpha(look) || Character.isDigit(look) || look == '\'' || look == '['){
|
// named ranges and tables can start with underscore or backslash
|
||||||
|
// see https://support.office.com/en-us/article/Define-and-use-names-in-formulas-4d0f13ac-53b7-422e-afd2-abd7ff379c64?ui=en-US&rs=en-US&ad=US#bmsyntax_rules_for_names
|
||||||
|
if (IsAlpha(look) || Character.isDigit(look) || look == '\'' || look == '[' || look == '_' || look == '\\' ) {
|
||||||
return parseRangeExpression();
|
return parseRangeExpression();
|
||||||
}
|
}
|
||||||
if (look == '.') {
|
if (look == '.') {
|
||||||
|
@ -20,6 +20,7 @@ package org.apache.poi.ss.formula;
|
|||||||
import org.apache.poi.ss.SpreadsheetVersion;
|
import org.apache.poi.ss.SpreadsheetVersion;
|
||||||
import org.apache.poi.ss.formula.ptg.Ptg;
|
import org.apache.poi.ss.formula.ptg.Ptg;
|
||||||
import org.apache.poi.ss.usermodel.Name;
|
import org.apache.poi.ss.usermodel.Name;
|
||||||
|
import org.apache.poi.ss.usermodel.Table;
|
||||||
import org.apache.poi.ss.util.AreaReference;
|
import org.apache.poi.ss.util.AreaReference;
|
||||||
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.ss.util.CellReference;
|
||||||
|
|
||||||
@ -41,6 +42,11 @@ public interface FormulaParsingWorkbook {
|
|||||||
*/
|
*/
|
||||||
Name createName();
|
Name createName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSSF Only - gets a table that exists in the worksheet
|
||||||
|
*/
|
||||||
|
Table getTable(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an external name (named range, function, user-defined function) Ptg
|
* Return an external name (named range, function, user-defined function) Ptg
|
||||||
*/
|
*/
|
||||||
|
@ -407,6 +407,9 @@ public final class OperationEvaluationContext {
|
|||||||
return new FunctionNameEval(name);
|
return new FunctionNameEval(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public int getSheetIndex() {
|
||||||
|
return _sheetIndex;
|
||||||
|
}
|
||||||
|
|
||||||
private ValueEval getExternalNameXEval(ExternalName externName, String workbookName) {
|
private ValueEval getExternalNameXEval(ExternalName externName, String workbookName) {
|
||||||
try {
|
try {
|
||||||
|
@ -26,20 +26,60 @@ import java.util.TreeSet;
|
|||||||
|
|
||||||
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
|
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
|
||||||
import org.apache.poi.ss.formula.atp.AnalysisToolPak;
|
import org.apache.poi.ss.formula.atp.AnalysisToolPak;
|
||||||
import org.apache.poi.ss.formula.eval.*;
|
import org.apache.poi.ss.formula.eval.BlankEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.BoolEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||||
|
import org.apache.poi.ss.formula.eval.ExternalNameEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.FunctionEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.FunctionNameEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.MissingArgEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
||||||
|
import org.apache.poi.ss.formula.eval.NumberEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.OperandResolver;
|
||||||
|
import org.apache.poi.ss.formula.eval.StringEval;
|
||||||
|
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||||
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
||||||
import org.apache.poi.ss.formula.functions.Choose;
|
import org.apache.poi.ss.formula.functions.Choose;
|
||||||
import org.apache.poi.ss.formula.functions.FreeRefFunction;
|
import org.apache.poi.ss.formula.functions.FreeRefFunction;
|
||||||
import org.apache.poi.ss.formula.functions.Function;
|
import org.apache.poi.ss.formula.functions.Function;
|
||||||
import org.apache.poi.ss.formula.functions.IfFunc;
|
import org.apache.poi.ss.formula.functions.IfFunc;
|
||||||
import org.apache.poi.ss.formula.ptg.*;
|
import org.apache.poi.ss.formula.ptg.Area3DPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.Area3DPxg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.AreaErrPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.AreaPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.AttrPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.BoolPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.ControlPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.DeletedArea3DPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.DeletedRef3DPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.ErrPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.ExpPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.FuncVarPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.IntPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.MemAreaPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.MemErrPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.MemFuncPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.MissingArgPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.NamePtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.NameXPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.NameXPxg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.NumberPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.OperationPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.Ptg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.RefErrorPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.RefPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.StringPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.UnionPtg;
|
||||||
|
import org.apache.poi.ss.formula.ptg.UnknownPtg;
|
||||||
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
|
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
|
||||||
import org.apache.poi.ss.formula.udf.UDFFinder;
|
import org.apache.poi.ss.formula.udf.UDFFinder;
|
||||||
import org.apache.poi.ss.usermodel.Cell;
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.ss.util.CellReference;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates formula cells.<p/>
|
* Evaluates formula cells.<p/>
|
||||||
*
|
*
|
||||||
|
@ -17,13 +17,18 @@
|
|||||||
|
|
||||||
package org.apache.poi.ss.formula.functions;
|
package org.apache.poi.ss.formula.functions;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.formula.FormulaParseException;
|
||||||
|
import org.apache.poi.ss.formula.FormulaParser;
|
||||||
|
import org.apache.poi.ss.formula.FormulaParsingWorkbook;
|
||||||
|
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||||
import org.apache.poi.ss.formula.eval.BlankEval;
|
import org.apache.poi.ss.formula.eval.BlankEval;
|
||||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||||
import org.apache.poi.ss.formula.eval.EvaluationException;
|
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||||
import org.apache.poi.ss.formula.eval.MissingArgEval;
|
import org.apache.poi.ss.formula.eval.MissingArgEval;
|
||||||
import org.apache.poi.ss.formula.eval.OperandResolver;
|
import org.apache.poi.ss.formula.eval.OperandResolver;
|
||||||
import org.apache.poi.ss.formula.eval.ValueEval;
|
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
import org.apache.poi.ss.formula.ptg.Area3DPxg;
|
||||||
|
import org.apache.poi.ss.usermodel.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation for Excel function INDIRECT<p/>
|
* Implementation for Excel function INDIRECT<p/>
|
||||||
@ -88,8 +93,9 @@ public final class Indirect implements FreeRefFunction {
|
|||||||
return OperandResolver.coerceValueToBoolean(ve, false).booleanValue();
|
return OperandResolver.coerceValueToBoolean(ve, false).booleanValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ValueEval evaluateIndirect(OperationEvaluationContext ec, String text,
|
private static ValueEval evaluateIndirect(final OperationEvaluationContext ec, String text,
|
||||||
boolean isA1style) {
|
boolean isA1style) {
|
||||||
|
|
||||||
// Search backwards for '!' because sheet names can contain '!'
|
// Search backwards for '!' because sheet names can contain '!'
|
||||||
int plingPos = text.lastIndexOf('!');
|
int plingPos = text.lastIndexOf('!');
|
||||||
|
|
||||||
@ -110,9 +116,19 @@ public final class Indirect implements FreeRefFunction {
|
|||||||
refText = text.substring(plingPos + 1);
|
refText = text.substring(plingPos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Table.isStructuredReference.matcher(refText).matches()) {
|
||||||
|
// The argument is structured reference
|
||||||
|
Area3DPxg areaPtg = null;
|
||||||
|
try {
|
||||||
|
areaPtg = FormulaParser.parseStructuredReference(refText, (FormulaParsingWorkbook) ec.getWorkbook(), ec.getRowIndex());
|
||||||
|
} catch (FormulaParseException e) {
|
||||||
|
return ErrorEval.REF_INVALID;
|
||||||
|
}
|
||||||
|
return ec.getArea3DEval(areaPtg);
|
||||||
|
} else {
|
||||||
|
// The argument is regular reference
|
||||||
String refStrPart1;
|
String refStrPart1;
|
||||||
String refStrPart2;
|
String refStrPart2;
|
||||||
|
|
||||||
int colonPos = refText.indexOf(':');
|
int colonPos = refText.indexOf(':');
|
||||||
if (colonPos < 0) {
|
if (colonPos < 0) {
|
||||||
refStrPart1 = refText.trim();
|
refStrPart1 = refText.trim();
|
||||||
@ -123,6 +139,7 @@ public final class Indirect implements FreeRefFunction {
|
|||||||
}
|
}
|
||||||
return ec.getDynamicReference(workbookName, sheetName, refStrPart1, refStrPart2, isA1style);
|
return ec.getDynamicReference(workbookName, sheetName, refStrPart1, refStrPart2, isA1style);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array of length 2: {workbookName, sheetName,}. Second element will always be
|
* @return array of length 2: {workbookName, sheetName,}. Second element will always be
|
||||||
|
@ -28,7 +28,7 @@ import org.apache.poi.xssf.usermodel.BaseXSSFEvaluationWorkbook;
|
|||||||
* SXSSF wrapper around the SXSSF and XSSF workbooks
|
* SXSSF wrapper around the SXSSF and XSSF workbooks
|
||||||
*/
|
*/
|
||||||
public final class SXSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook {
|
public final class SXSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook {
|
||||||
private SXSSFWorkbook _uBook;
|
private final SXSSFWorkbook _uBook;
|
||||||
|
|
||||||
public static SXSSFEvaluationWorkbook create(SXSSFWorkbook book) {
|
public static SXSSFEvaluationWorkbook create(SXSSFWorkbook book) {
|
||||||
if (book == null) {
|
if (book == null) {
|
||||||
@ -42,15 +42,18 @@ public final class SXSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook {
|
|||||||
_uBook = book;
|
_uBook = book;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getSheetIndex(EvaluationSheet evalSheet) {
|
public int getSheetIndex(EvaluationSheet evalSheet) {
|
||||||
SXSSFSheet sheet = ((SXSSFEvaluationSheet)evalSheet).getSXSSFSheet();
|
SXSSFSheet sheet = ((SXSSFEvaluationSheet)evalSheet).getSXSSFSheet();
|
||||||
return _uBook.getSheetIndex(sheet);
|
return _uBook.getSheetIndex(sheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public EvaluationSheet getSheet(int sheetIndex) {
|
public EvaluationSheet getSheet(int sheetIndex) {
|
||||||
return new SXSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
|
return new SXSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Ptg[] getFormulaTokens(EvaluationCell evalCell) {
|
public Ptg[] getFormulaTokens(EvaluationCell evalCell) {
|
||||||
SXSSFCell cell = ((SXSSFEvaluationCell)evalCell).getSXSSFCell();
|
SXSSFCell cell = ((SXSSFEvaluationCell)evalCell).getSXSSFCell();
|
||||||
SXSSFEvaluationWorkbook frBook = SXSSFEvaluationWorkbook.create(_uBook);
|
SXSSFEvaluationWorkbook frBook = SXSSFEvaluationWorkbook.create(_uBook);
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
|
|
||||||
package org.apache.poi.xssf.usermodel;
|
package org.apache.poi.xssf.usermodel;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.poi.ss.SpreadsheetVersion;
|
import org.apache.poi.ss.SpreadsheetVersion;
|
||||||
import org.apache.poi.ss.formula.EvaluationName;
|
import org.apache.poi.ss.formula.EvaluationName;
|
||||||
@ -36,6 +39,8 @@ import org.apache.poi.ss.formula.ptg.Ptg;
|
|||||||
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
|
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
|
||||||
import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
|
import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
|
||||||
import org.apache.poi.ss.formula.udf.UDFFinder;
|
import org.apache.poi.ss.formula.udf.UDFFinder;
|
||||||
|
import org.apache.poi.ss.usermodel.Table;
|
||||||
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
import org.apache.poi.ss.util.AreaReference;
|
import org.apache.poi.ss.util.AreaReference;
|
||||||
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.ss.util.CellReference;
|
||||||
import org.apache.poi.util.NotImplemented;
|
import org.apache.poi.util.NotImplemented;
|
||||||
@ -310,6 +315,55 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
|
|||||||
return _uBook.createName();
|
return _uBook.createName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String caseInsensitive(String s) {
|
||||||
|
return s.toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: data tables are stored at the workbook level in XSSF, but are bound to a single sheet.
|
||||||
|
* The current code structure has them hanging off XSSFSheet, but formulas reference them
|
||||||
|
* only by name (names are global, and case insensitive).
|
||||||
|
* This map stores names as lower case for case-insensitive lookups.
|
||||||
|
*
|
||||||
|
* FIXME: Caching tables by name here for fast formula lookup means the map is out of date if
|
||||||
|
* a table is renamed or added/removed to a sheet after the map is created.
|
||||||
|
*
|
||||||
|
* Perhaps tables can be managed similar to PivotTable references above?
|
||||||
|
*/
|
||||||
|
private Map<String, XSSFTable> _tableCache = null;
|
||||||
|
private Map<String, XSSFTable> getTableCache() {
|
||||||
|
if ( _tableCache != null ) {
|
||||||
|
return _tableCache;
|
||||||
|
}
|
||||||
|
// FIXME: use org.apache.commons.collections.map.CaseInsensitiveMap
|
||||||
|
_tableCache = new HashMap<String, XSSFTable>();
|
||||||
|
|
||||||
|
for (Sheet sheet : _uBook) {
|
||||||
|
for (XSSFTable tbl : ((XSSFSheet)sheet).getTables()) {
|
||||||
|
String lname = caseInsensitive(tbl.getName());
|
||||||
|
_tableCache.put(lname, tbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _tableCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data table with the given name (case insensitive).
|
||||||
|
* Tables are cached for performance (formula evaluation looks them up by name repeatedly).
|
||||||
|
* After the first table lookup, adding or removing a table from the document structure will cause trouble.
|
||||||
|
* This is meant to be used on documents whose structure is essentially static at the point formulas are evaluated.
|
||||||
|
*
|
||||||
|
* @param name the data table name (case-insensitive)
|
||||||
|
* @return The Data table in the workbook named <tt>name</tt>, or <tt>null</tt> if no table is named <tt>name</tt>.
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public XSSFTable getTable(String name) {
|
||||||
|
if (name == null) return null;
|
||||||
|
String lname = caseInsensitive(name);
|
||||||
|
return getTableCache().get(lname);
|
||||||
|
}
|
||||||
|
|
||||||
public UDFFinder getUDFFinder(){
|
public UDFFinder getUDFFinder(){
|
||||||
return _uBook.getUDFFinder();
|
return _uBook.getUDFFinder();
|
||||||
}
|
}
|
||||||
|
@ -506,7 +506,7 @@ public final class XSSFCell implements Cell {
|
|||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(sheet.getWorkbook());
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(sheet.getWorkbook());
|
||||||
SharedFormula sf = new SharedFormula(SpreadsheetVersion.EXCEL2007);
|
SharedFormula sf = new SharedFormula(SpreadsheetVersion.EXCEL2007);
|
||||||
|
|
||||||
Ptg[] ptgs = FormulaParser.parse(sharedFormula, fpb, FormulaType.CELL, sheetIndex);
|
Ptg[] ptgs = FormulaParser.parse(sharedFormula, fpb, FormulaType.CELL, sheetIndex, getRowIndex());
|
||||||
Ptg[] fmla = sf.convertSharedFormulas(ptgs,
|
Ptg[] fmla = sf.convertSharedFormulas(ptgs,
|
||||||
getRowIndex() - ref.getFirstRow(), getColumnIndex() - ref.getFirstColumn());
|
getRowIndex() - ref.getFirstRow(), getColumnIndex() - ref.getFirstColumn());
|
||||||
return FormulaRenderer.toFormulaString(fpb, fmla);
|
return FormulaRenderer.toFormulaString(fpb, fmla);
|
||||||
@ -550,7 +550,7 @@ public final class XSSFCell implements Cell {
|
|||||||
|
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
//validate through the FormulaParser
|
//validate through the FormulaParser
|
||||||
FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()));
|
FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()), getRowIndex());
|
||||||
|
|
||||||
CTCellFormula f = CTCellFormula.Factory.newInstance();
|
CTCellFormula f = CTCellFormula.Factory.newInstance();
|
||||||
f.setStringValue(formula);
|
f.setStringValue(formula);
|
||||||
|
@ -38,18 +38,21 @@ public final class XSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook {
|
|||||||
super(book);
|
super(book);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getSheetIndex(EvaluationSheet evalSheet) {
|
public int getSheetIndex(EvaluationSheet evalSheet) {
|
||||||
XSSFSheet sheet = ((XSSFEvaluationSheet)evalSheet).getXSSFSheet();
|
XSSFSheet sheet = ((XSSFEvaluationSheet)evalSheet).getXSSFSheet();
|
||||||
return _uBook.getSheetIndex(sheet);
|
return _uBook.getSheetIndex(sheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public EvaluationSheet getSheet(int sheetIndex) {
|
public EvaluationSheet getSheet(int sheetIndex) {
|
||||||
return new XSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
|
return new XSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Ptg[] getFormulaTokens(EvaluationCell evalCell) {
|
public Ptg[] getFormulaTokens(EvaluationCell evalCell) {
|
||||||
XSSFCell cell = ((XSSFEvaluationCell)evalCell).getXSSFCell();
|
XSSFCell cell = ((XSSFEvaluationCell)evalCell).getXSSFCell();
|
||||||
XSSFEvaluationWorkbook frBook = XSSFEvaluationWorkbook.create(_uBook);
|
XSSFEvaluationWorkbook frBook = XSSFEvaluationWorkbook.create(_uBook);
|
||||||
return FormulaParser.parse(cell.getCellFormula(), frBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet()));
|
return FormulaParser.parse(cell.getCellFormula(), frBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet()), cell.getRowIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ public final class XSSFName implements Name {
|
|||||||
public void setRefersToFormula(String formulaText) {
|
public void setRefersToFormula(String formulaText) {
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(_workbook);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(_workbook);
|
||||||
//validate through the FormulaParser
|
//validate through the FormulaParser
|
||||||
FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex());
|
FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex(), -1);
|
||||||
|
|
||||||
_ctName.setStringValue(formulaText);
|
_ctName.setStringValue(formulaText);
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ public final class XSSFName implements Name {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(_workbook);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(_workbook);
|
||||||
Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex());
|
Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex(), -1);
|
||||||
return Ptg.doesFormulaReferToDeletedCell(ptgs);
|
return Ptg.doesFormulaReferToDeletedCell(ptgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,13 +24,17 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.apache.poi.POIXMLDocumentPart;
|
import org.apache.poi.POIXMLDocumentPart;
|
||||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||||
|
import org.apache.poi.ss.usermodel.Table;
|
||||||
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.ss.util.CellReference;
|
||||||
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
|
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
|
||||||
|
import org.apache.poi.util.StringUtil;
|
||||||
import org.apache.xmlbeans.XmlException;
|
import org.apache.xmlbeans.XmlException;
|
||||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable;
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable;
|
||||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;
|
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;
|
||||||
@ -48,10 +52,12 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.TableDocument;
|
|||||||
*
|
*
|
||||||
* @author Roberto Manicardi
|
* @author Roberto Manicardi
|
||||||
*/
|
*/
|
||||||
public class XSSFTable extends POIXMLDocumentPart {
|
public class XSSFTable extends POIXMLDocumentPart implements Table {
|
||||||
|
|
||||||
private CTTable ctTable;
|
private CTTable ctTable;
|
||||||
private List<XSSFXmlColumnPr> xmlColumnPr;
|
private List<XSSFXmlColumnPr> xmlColumnPr;
|
||||||
|
private CTTableColumn[] ctColumns;
|
||||||
|
private HashMap<String, Integer> columnMap;
|
||||||
private CellReference startCellReference;
|
private CellReference startCellReference;
|
||||||
private CellReference endCellReference;
|
private CellReference endCellReference;
|
||||||
private String commonXPath;
|
private String commonXPath;
|
||||||
@ -117,32 +123,41 @@ public class XSSFTable extends POIXMLDocumentPart {
|
|||||||
* @return true if the Table element contain mappings
|
* @return true if the Table element contain mappings
|
||||||
*/
|
*/
|
||||||
public boolean mapsTo(long id){
|
public boolean mapsTo(long id){
|
||||||
boolean maps =false;
|
|
||||||
|
|
||||||
List<XSSFXmlColumnPr> pointers = getXmlColumnPrs();
|
List<XSSFXmlColumnPr> pointers = getXmlColumnPrs();
|
||||||
|
|
||||||
for (XSSFXmlColumnPr pointer: pointers) {
|
for (XSSFXmlColumnPr pointer: pointers) {
|
||||||
if (pointer.getMapId()==id) {
|
if (pointer.getMapId()==id) {
|
||||||
maps=true;
|
return true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return maps;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* caches table columns for performance.
|
||||||
|
* Updated via updateHeaders
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
private CTTableColumn[] getTableColumns() {
|
||||||
|
if (ctColumns == null) {
|
||||||
|
ctColumns = ctTable.getTableColumns().getTableColumnArray();
|
||||||
|
}
|
||||||
|
return ctColumns;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Calculates the xpath of the root element for the table. This will be the common part
|
* Calculates the xpath of the root element for the table. This will be the common part
|
||||||
* of all the mapping's xpaths
|
* of all the mapping's xpaths
|
||||||
|
* Note: this function caches the result for performance. To flush the cache {@link #updateHeaders()} must be called.
|
||||||
*
|
*
|
||||||
* @return the xpath of the table's root element
|
* @return the xpath of the table's root element
|
||||||
*/
|
*/
|
||||||
public String getCommonXpath() {
|
public String getCommonXpath() {
|
||||||
if (commonXPath == null) {
|
if (commonXPath == null) {
|
||||||
String[] commonTokens = {};
|
String[] commonTokens = {};
|
||||||
for (CTTableColumn column :ctTable.getTableColumns().getTableColumnArray()) {
|
for (CTTableColumn column : getTableColumns()) {
|
||||||
if (column.getXmlColumnPr()!=null) {
|
if (column.getXmlColumnPr()!=null) {
|
||||||
String xpath = column.getXmlColumnPr().getXpath();
|
String xpath = column.getXmlColumnPr().getXpath();
|
||||||
String[] tokens = xpath.split("/");
|
String[] tokens = xpath.split("/");
|
||||||
@ -166,21 +181,24 @@ public class XSSFTable extends POIXMLDocumentPart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commonXPath = "";
|
commonTokens[0] = "";
|
||||||
for (int i = 1 ; i< commonTokens.length;i++) {
|
commonXPath = StringUtil.join(commonTokens, "/");
|
||||||
commonXPath +="/"+commonTokens[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return commonXPath;
|
return commonXPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note this list is static - once read, it does not notice later changes to the underlying column structures
|
||||||
|
* To clear the cache, call {@link #updateHeaders}
|
||||||
|
* @return List of XSSFXmlColumnPr
|
||||||
|
*/
|
||||||
public List<XSSFXmlColumnPr> getXmlColumnPrs() {
|
public List<XSSFXmlColumnPr> getXmlColumnPrs() {
|
||||||
|
|
||||||
if (xmlColumnPr==null) {
|
if (xmlColumnPr==null) {
|
||||||
xmlColumnPr = new ArrayList<XSSFXmlColumnPr>();
|
xmlColumnPr = new ArrayList<XSSFXmlColumnPr>();
|
||||||
for (CTTableColumn column:ctTable.getTableColumns().getTableColumnArray()) {
|
for (CTTableColumn column: getTableColumns()) {
|
||||||
if (column.getXmlColumnPr()!=null) {
|
if (column.getXmlColumnPr()!=null) {
|
||||||
XSSFXmlColumnPr columnPr = new XSSFXmlColumnPr(this,column,column.getXmlColumnPr());
|
XSSFXmlColumnPr columnPr = new XSSFXmlColumnPr(this,column,column.getXmlColumnPr());
|
||||||
xmlColumnPr.add(columnPr);
|
xmlColumnPr.add(columnPr);
|
||||||
@ -301,6 +319,83 @@ public class XSSFTable extends POIXMLDocumentPart {
|
|||||||
}
|
}
|
||||||
cellnum++;
|
cellnum++;
|
||||||
}
|
}
|
||||||
|
ctColumns = null;
|
||||||
|
columnMap = null;
|
||||||
|
xmlColumnPr = null;
|
||||||
|
commonXPath = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String caseInsensitive(String s) {
|
||||||
|
return s.toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the relative column index of a column in this table having the header name <code>column</code>.
|
||||||
|
* The column index is relative to the left-most column in the table, 0-indexed.
|
||||||
|
* Returns <code>-1</code> if <code>column</code> is not a header name in table.
|
||||||
|
*
|
||||||
|
* Note: this function caches column names for performance. To flush the cache (because columns
|
||||||
|
* have been moved or column headers have been changed), {@link #updateHeaders()} must be called.
|
||||||
|
*
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public int findColumnIndex(String column) {
|
||||||
|
if (columnMap == null) {
|
||||||
|
// FIXME: replace with org.apache.commons.collections.map.CaseInsensitiveMap
|
||||||
|
int count = getTableColumns().length;
|
||||||
|
columnMap = new HashMap<String, Integer>(count);
|
||||||
|
|
||||||
|
for (int i=0; i < count; i++) {
|
||||||
|
String columnName = getTableColumns()[i].getName();
|
||||||
|
columnMap.put(caseInsensitive(columnName), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Table column names with special characters need a single quote escape
|
||||||
|
// but the escape is not present in the column definition
|
||||||
|
Integer idx = columnMap.get(caseInsensitive(column.replace("'", "")));
|
||||||
|
return idx == null ? -1 : idx.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public String getSheetName() {
|
||||||
|
return getXSSFSheet().getSheetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public boolean isHasTotalsRow() {
|
||||||
|
return ctTable.getTotalsRowShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public int getStartColIndex() {
|
||||||
|
return getStartCellReference().getCol();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public int getStartRowIndex() {
|
||||||
|
return getStartCellReference().getRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public int getEndColIndex() {
|
||||||
|
return getEndCellReference().getCol();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public int getEndRowIndex() {
|
||||||
|
return getEndCellReference().getRow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -2261,4 +2262,24 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
|||||||
public SpreadsheetVersion getSpreadsheetVersion() {
|
public SpreadsheetVersion getSpreadsheetVersion() {
|
||||||
return SpreadsheetVersion.EXCEL2007;
|
return SpreadsheetVersion.EXCEL2007;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data table with the given name (case insensitive).
|
||||||
|
*
|
||||||
|
* @param name the data table name (case-insensitive)
|
||||||
|
* @return The Data table in the workbook named <tt>name</tt>, or <tt>null</tt> if no table is named <tt>name</tt>.
|
||||||
|
* @since 3.15 beta 2
|
||||||
|
*/
|
||||||
|
public XSSFTable getTable(String name) {
|
||||||
|
if (name != null && sheets != null) {
|
||||||
|
for (XSSFSheet sheet : sheets) {
|
||||||
|
for (XSSFTable tbl : sheet.getTables()) {
|
||||||
|
if (name.equalsIgnoreCase(tbl.getName())) {
|
||||||
|
return tbl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ public final class XSSFFormulaUtils {
|
|||||||
String formula = f.getStringValue();
|
String formula = f.getStringValue();
|
||||||
if (formula != null && formula.length() > 0) {
|
if (formula != null && formula.length() > 0) {
|
||||||
int sheetIndex = _wb.getSheetIndex(cell.getSheet());
|
int sheetIndex = _wb.getSheetIndex(cell.getSheet());
|
||||||
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.CELL, sheetIndex);
|
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.CELL, sheetIndex, cell.getRowIndex());
|
||||||
for (Ptg ptg : ptgs) {
|
for (Ptg ptg : ptgs) {
|
||||||
updatePtg(ptg, oldName, newName);
|
updatePtg(ptg, oldName, newName);
|
||||||
}
|
}
|
||||||
@ -113,7 +113,8 @@ public final class XSSFFormulaUtils {
|
|||||||
String formula = name.getRefersToFormula();
|
String formula = name.getRefersToFormula();
|
||||||
if (formula != null) {
|
if (formula != null) {
|
||||||
int sheetIndex = name.getSheetIndex();
|
int sheetIndex = name.getSheetIndex();
|
||||||
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.NAMEDRANGE, sheetIndex);
|
int rowIndex = -1; //don't care
|
||||||
|
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.NAMEDRANGE, sheetIndex, rowIndex);
|
||||||
for (Ptg ptg : ptgs) {
|
for (Ptg ptg : ptgs) {
|
||||||
updatePtg(ptg, oldName, newName);
|
updatePtg(ptg, oldName, newName);
|
||||||
}
|
}
|
||||||
|
@ -134,8 +134,9 @@ public final class XSSFRowShifter {
|
|||||||
XSSFName name = wb.getNameAt(i);
|
XSSFName name = wb.getNameAt(i);
|
||||||
String formula = name.getRefersToFormula();
|
String formula = name.getRefersToFormula();
|
||||||
int sheetIndex = name.getSheetIndex();
|
int sheetIndex = name.getSheetIndex();
|
||||||
|
final int rowIndex = -1; //don't care, named ranges are not allowed to include structured references
|
||||||
|
|
||||||
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.NAMEDRANGE, sheetIndex);
|
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.NAMEDRANGE, sheetIndex, rowIndex);
|
||||||
if (shifter.adjustFormula(ptgs, sheetIndex)) {
|
if (shifter.adjustFormula(ptgs, sheetIndex)) {
|
||||||
String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
|
String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
|
||||||
name.setRefersToFormula(shiftedFmla);
|
name.setRefersToFormula(shiftedFmla);
|
||||||
@ -218,10 +219,11 @@ public final class XSSFRowShifter {
|
|||||||
XSSFSheet sheet = row.getSheet();
|
XSSFSheet sheet = row.getSheet();
|
||||||
XSSFWorkbook wb = sheet.getWorkbook();
|
XSSFWorkbook wb = sheet.getWorkbook();
|
||||||
int sheetIndex = wb.getSheetIndex(sheet);
|
int sheetIndex = wb.getSheetIndex(sheet);
|
||||||
|
final int rowIndex = row.getRowNum();
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex);
|
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex, rowIndex);
|
||||||
String shiftedFmla = null;
|
String shiftedFmla = null;
|
||||||
if (shifter.adjustFormula(ptgs, sheetIndex)) {
|
if (shifter.adjustFormula(ptgs, sheetIndex)) {
|
||||||
shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
|
shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
|
||||||
@ -238,6 +240,7 @@ public final class XSSFRowShifter {
|
|||||||
public void updateConditionalFormatting(FormulaShifter shifter) {
|
public void updateConditionalFormatting(FormulaShifter shifter) {
|
||||||
XSSFWorkbook wb = sheet.getWorkbook();
|
XSSFWorkbook wb = sheet.getWorkbook();
|
||||||
int sheetIndex = wb.getSheetIndex(sheet);
|
int sheetIndex = wb.getSheetIndex(sheet);
|
||||||
|
final int rowIndex = -1; //don't care, structured references not allowed in conditional formatting
|
||||||
|
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
CTWorksheet ctWorksheet = sheet.getCTWorksheet();
|
CTWorksheet ctWorksheet = sheet.getCTWorksheet();
|
||||||
@ -283,7 +286,7 @@ public final class XSSFRowShifter {
|
|||||||
String[] formulaArray = cfRule.getFormulaArray();
|
String[] formulaArray = cfRule.getFormulaArray();
|
||||||
for (int i = 0; i < formulaArray.length; i++) {
|
for (int i = 0; i < formulaArray.length; i++) {
|
||||||
String formula = formulaArray[i];
|
String formula = formulaArray[i];
|
||||||
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex);
|
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex, rowIndex);
|
||||||
if (shifter.adjustFormula(ptgs, sheetIndex)) {
|
if (shifter.adjustFormula(ptgs, sheetIndex)) {
|
||||||
String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
|
String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
|
||||||
cfRule.setFormulaArray(i, shiftedFmla);
|
cfRule.setFormulaArray(i, shiftedFmla);
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.poi.ss.formula;
|
package org.apache.poi.ss.formula;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
|
import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
|
||||||
@ -28,7 +33,7 @@ import org.apache.poi.xssf.XSSFTestDataSamples;
|
|||||||
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
|
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test {@link FormulaParser}'s handling of row numbers at the edge of the
|
* Test {@link FormulaParser}'s handling of row numbers at the edge of the
|
||||||
@ -36,8 +41,9 @@ import junit.framework.TestCase;
|
|||||||
*
|
*
|
||||||
* @author David North
|
* @author David North
|
||||||
*/
|
*/
|
||||||
public class TestFormulaParser extends TestCase {
|
public class TestFormulaParser {
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testHSSFFailsForOver65536() {
|
public void testHSSFFailsForOver65536() {
|
||||||
FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(new HSSFWorkbook());
|
FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(new HSSFWorkbook());
|
||||||
try {
|
try {
|
||||||
@ -49,16 +55,19 @@ public class TestFormulaParser extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testHSSFPassCase() {
|
public void testHSSFPassCase() {
|
||||||
FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(new HSSFWorkbook());
|
FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(new HSSFWorkbook());
|
||||||
FormulaParser.parse("Sheet1!1:65536", workbook, FormulaType.CELL, 0);
|
FormulaParser.parse("Sheet1!1:65536", workbook, FormulaType.CELL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testXSSFWorksForOver65536() {
|
public void testXSSFWorksForOver65536() {
|
||||||
FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(new XSSFWorkbook());
|
FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(new XSSFWorkbook());
|
||||||
FormulaParser.parse("Sheet1!1:65537", workbook, FormulaType.CELL, 0);
|
FormulaParser.parse("Sheet1!1:65537", workbook, FormulaType.CELL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testXSSFFailCase() {
|
public void testXSSFFailCase() {
|
||||||
FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(new XSSFWorkbook());
|
FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(new XSSFWorkbook());
|
||||||
try {
|
try {
|
||||||
@ -71,6 +80,7 @@ public class TestFormulaParser extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copied from org.apache.poi.hssf.model.TestFormulaParser
|
// copied from org.apache.poi.hssf.model.TestFormulaParser
|
||||||
|
@Test
|
||||||
public void testMacroFunction() throws Exception {
|
public void testMacroFunction() throws Exception {
|
||||||
// testNames.xlsm contains a VB function called 'myFunc'
|
// testNames.xlsm contains a VB function called 'myFunc'
|
||||||
final String testFile = "testNames.xlsm";
|
final String testFile = "testNames.xlsm";
|
||||||
@ -126,6 +136,7 @@ public class TestFormulaParser extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testParserErrors() throws Exception {
|
public void testParserErrors() throws Exception {
|
||||||
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("testNames.xlsm");
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("testNames.xlsm");
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.apache.poi.ss.formula;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
|
import org.apache.poi.ss.usermodel.CellValue;
|
||||||
|
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||||
|
import org.apache.poi.ss.usermodel.Table;
|
||||||
|
import org.apache.poi.xssf.XSSFTestDataSamples;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests Excel Table expressions (structured references)
|
||||||
|
* @see <a href="https://support.office.com/en-us/article/Using-structured-references-with-Excel-tables-F5ED2452-2337-4F71-BED3-C8AE6D2B276E">
|
||||||
|
* Excel Structured Reference Syntax
|
||||||
|
* </a>
|
||||||
|
*/
|
||||||
|
public class TestStructuredReferences {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the regular expression used in INDIRECT() evaluation to recognize structured references
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTableExpressionSyntax() {
|
||||||
|
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("abc[col1]").matches());
|
||||||
|
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("_abc[col1]").matches());
|
||||||
|
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("_[col1]").matches());
|
||||||
|
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[col1]").matches());
|
||||||
|
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[col1]").matches());
|
||||||
|
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[#This Row]").matches());
|
||||||
|
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[ [col1], [col2] ]").matches());
|
||||||
|
|
||||||
|
// can't have a space between the table name and open bracket
|
||||||
|
assertFalse("Invalid structured reference syntax didn't fail expression", Table.isStructuredReference.matcher("\\abc [ [col1], [col2] ]").matches());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTableFormulas() throws Exception {
|
||||||
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("StructuredReferences.xlsx");
|
||||||
|
try {
|
||||||
|
|
||||||
|
final FormulaEvaluator eval = new XSSFFormulaEvaluator(wb);
|
||||||
|
confirm(eval, wb.getSheet("Table").getRow(5).getCell(0), 49);
|
||||||
|
confirm(eval, wb.getSheet("Formulas").getRow(0).getCell(0), 209);
|
||||||
|
} finally {
|
||||||
|
wb.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void confirm(FormulaEvaluator fe, Cell cell, double expectedResult) {
|
||||||
|
fe.clearAllCachedResultValues();
|
||||||
|
CellValue cv = fe.evaluate(cell);
|
||||||
|
if (cv.getCellType() != Cell.CELL_TYPE_NUMERIC) {
|
||||||
|
fail("expected numeric cell type but got " + cv.formatAsString());
|
||||||
|
}
|
||||||
|
assertEquals(expectedResult, cv.getNumberValue(), 0.0);
|
||||||
|
}
|
||||||
|
}
|
@ -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,9 +46,12 @@ 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() throws IOException {
|
||||||
XSSFWorkbook wb = new XSSFWorkbook();
|
XSSFWorkbook wb = new XSSFWorkbook();
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
Ptg[] ptgs;
|
Ptg[] ptgs;
|
||||||
@ -118,10 +123,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertEquals(AttrPtg.class, ptgs[1].getClass());
|
assertEquals(AttrPtg.class, ptgs[1].getClass());
|
||||||
assertEquals("Sheet1!A1:B3", ptgs[0].toFormulaString());
|
assertEquals("Sheet1!A1:B3", ptgs[0].toFormulaString());
|
||||||
assertEquals("SUM", ptgs[1].toFormulaString());
|
assertEquals("SUM", ptgs[1].toFormulaString());
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void builtInFormulas() {
|
public void builtInFormulas() throws IOException {
|
||||||
XSSFWorkbook wb = new XSSFWorkbook();
|
XSSFWorkbook wb = new XSSFWorkbook();
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
Ptg[] ptgs;
|
Ptg[] ptgs;
|
||||||
@ -134,10 +141,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertEquals(2, ptgs.length);
|
assertEquals(2, ptgs.length);
|
||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof IntPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof IntPtg);
|
||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof FuncPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof FuncPtg);
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void formaulReferncesSameWorkbook() {
|
public void formulaReferencesSameWorkbook() throws IOException {
|
||||||
// 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");
|
||||||
@ -153,10 +162,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertEquals(null, ((NameXPxg)ptgs[0]).getSheetName());
|
assertEquals(null, ((NameXPxg)ptgs[0]).getSheetName());
|
||||||
assertEquals("NR_Global_B2",((NameXPxg)ptgs[0]).getNameName());
|
assertEquals("NR_Global_B2",((NameXPxg)ptgs[0]).getNameName());
|
||||||
assertEquals("[0]!NR_Global_B2",((NameXPxg)ptgs[0]).toFormulaString());
|
assertEquals("[0]!NR_Global_B2",((NameXPxg)ptgs[0]).toFormulaString());
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void formulaReferencesOtherSheets() {
|
public void formulaReferencesOtherSheets() throws IOException {
|
||||||
// Use a test file with the named ranges in place
|
// Use a test file with the named ranges in place
|
||||||
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56737.xlsx");
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56737.xlsx");
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
@ -193,10 +204,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertEquals(1, ptgs.length);
|
assertEquals(1, ptgs.length);
|
||||||
assertEquals(NamePtg.class, ptgs[0].getClass());
|
assertEquals(NamePtg.class, ptgs[0].getClass());
|
||||||
assertEquals("NR_Global_B2",((NamePtg)ptgs[0]).toFormulaString(fpb));
|
assertEquals("NR_Global_B2",((NamePtg)ptgs[0]).toFormulaString(fpb));
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void formulaReferencesOtherWorkbook() {
|
public void formulaReferencesOtherWorkbook() throws IOException {
|
||||||
// Use a test file with the external linked table in place
|
// Use a test file with the external linked table in place
|
||||||
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx");
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx");
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
@ -228,6 +241,8 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertEquals(null, ((NameXPxg)ptgs[0]).getSheetName());
|
assertEquals(null, ((NameXPxg)ptgs[0]).getSheetName());
|
||||||
assertEquals("NR_Global_B2",((NameXPxg)ptgs[0]).getNameName());
|
assertEquals("NR_Global_B2",((NameXPxg)ptgs[0]).getNameName());
|
||||||
assertEquals("[1]!NR_Global_B2",((NameXPxg)ptgs[0]).toFormulaString());
|
assertEquals("[1]!NR_Global_B2",((NameXPxg)ptgs[0]).toFormulaString());
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,7 +256,7 @@ public final class TestXSSFFormulaParser {
|
|||||||
* (but not evaluate - that's elsewhere in the test suite)
|
* (but not evaluate - that's elsewhere in the test suite)
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void multiSheetReferencesHSSFandXSSF() throws Exception {
|
public void multiSheetReferencesHSSFandXSSF() throws IOException {
|
||||||
Workbook[] wbs = new Workbook[] {
|
Workbook[] wbs = new Workbook[] {
|
||||||
HSSFTestDataSamples.openSampleWorkbook("55906-MultiSheetRefs.xls"),
|
HSSFTestDataSamples.openSampleWorkbook("55906-MultiSheetRefs.xls"),
|
||||||
XSSFTestDataSamples.openSampleWorkbook("55906-MultiSheetRefs.xlsx")
|
XSSFTestDataSamples.openSampleWorkbook("55906-MultiSheetRefs.xlsx")
|
||||||
@ -363,6 +378,8 @@ public final class TestXSSFFormulaParser {
|
|||||||
newF = s1.getRow(0).createCell(11, Cell.CELL_TYPE_FORMULA);
|
newF = s1.getRow(0).createCell(11, Cell.CELL_TYPE_FORMULA);
|
||||||
newF.setCellFormula("MIN(Sheet1:Sheet2!A1:B2)");
|
newF.setCellFormula("MIN(Sheet1:Sheet2!A1:B2)");
|
||||||
assertEquals("MIN(Sheet1:Sheet2!A1:B2)", newF.getCellFormula());
|
assertEquals("MIN(Sheet1:Sheet2!A1:B2)", newF.getCellFormula());
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +391,7 @@ public final class TestXSSFFormulaParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test58648Single() {
|
public void test58648Single() throws IOException {
|
||||||
XSSFWorkbook wb = new XSSFWorkbook();
|
XSSFWorkbook wb = new XSSFWorkbook();
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
Ptg[] ptgs;
|
Ptg[] ptgs;
|
||||||
@ -384,10 +401,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
2, ptgs.length);
|
2, ptgs.length);
|
||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof RefPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof RefPtg);
|
||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof ParenthesisPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof ParenthesisPtg);
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test58648Basic() {
|
public void test58648Basic() throws IOException {
|
||||||
XSSFWorkbook wb = new XSSFWorkbook();
|
XSSFWorkbook wb = new XSSFWorkbook();
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
Ptg[] ptgs;
|
Ptg[] ptgs;
|
||||||
@ -431,10 +450,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof RefPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof RefPtg);
|
||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof ParenthesisPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof ParenthesisPtg);
|
||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[2] instanceof ParenthesisPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[2] instanceof ParenthesisPtg);
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test58648FormulaParsing() {
|
public void test58648FormulaParsing() throws IOException {
|
||||||
Workbook wb = XSSFTestDataSamples.openSampleWorkbook("58648.xlsx");
|
Workbook wb = XSSFTestDataSamples.openSampleWorkbook("58648.xlsx");
|
||||||
|
|
||||||
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
|
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
|
||||||
@ -460,10 +481,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
Cell cell = sheet.getRow(1).getCell(4);
|
Cell cell = sheet.getRow(1).getCell(4);
|
||||||
|
|
||||||
assertEquals(5d, cell.getNumericCellValue(), 0d);
|
assertEquals(5d, cell.getNumericCellValue(), 0d);
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWhitespaceInFormula() {
|
public void testWhitespaceInFormula() throws IOException {
|
||||||
XSSFWorkbook wb = new XSSFWorkbook();
|
XSSFWorkbook wb = new XSSFWorkbook();
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
Ptg[] ptgs;
|
Ptg[] ptgs;
|
||||||
@ -505,10 +528,12 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof AreaPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof AreaPtg);
|
||||||
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 IntersectionPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[3] instanceof IntersectionPtg);
|
||||||
|
|
||||||
|
wb.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWhitespaceInComplexFormula() {
|
public void testWhitespaceInComplexFormula() throws IOException {
|
||||||
XSSFWorkbook wb = new XSSFWorkbook();
|
XSSFWorkbook wb = new XSSFWorkbook();
|
||||||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
|
||||||
Ptg[] ptgs;
|
Ptg[] ptgs;
|
||||||
@ -529,5 +554,172 @@ public final class TestXSSFFormulaParser {
|
|||||||
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof RefPtg);
|
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof RefPtg);
|
||||||
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);
|
||||||
|
|
||||||
|
wb.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1137,7 +1137,7 @@ public final class TestXSSFSheet extends BaseTestXSheet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See bug #50829
|
* See bug #50829 test data tables
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void tables() throws IOException {
|
public void tables() throws IOException {
|
||||||
|
@ -1107,4 +1107,36 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook {
|
|||||||
assertTrue("Had: " + e.getCause(), e.getCause() instanceof IOException);
|
assertTrue("Had: " + e.getCause(), e.getCause() instanceof IOException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See bug #57840 test data tables
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getTable() throws IOException {
|
||||||
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithTable.xlsx");
|
||||||
|
XSSFTable table1 = wb.getTable("Tabella1");
|
||||||
|
assertNotNull("Tabella1 was not found in workbook", table1);
|
||||||
|
assertEquals("Table name", "Tabella1", table1.getName());
|
||||||
|
assertEquals("Sheet name", "Foglio1", table1.getSheetName());
|
||||||
|
|
||||||
|
// Table lookup should be case-insensitive
|
||||||
|
assertSame("Case insensitive table name lookup", table1, wb.getTable("TABELLA1"));
|
||||||
|
|
||||||
|
// If workbook does not contain any data tables matching the provided name, getTable should return null
|
||||||
|
assertNull("Null table name should not throw NPE", wb.getTable(null));
|
||||||
|
assertNull("Should not be able to find non-existent table", wb.getTable("Foglio1"));
|
||||||
|
|
||||||
|
// If a table is added after getTable is called it should still be reachable by XSSFWorkbook.getTable
|
||||||
|
// This test makes sure that if any caching is done that getTable never uses a stale cache
|
||||||
|
XSSFTable table2 = wb.getSheet("Foglio2").createTable();
|
||||||
|
table2.setName("Table2");
|
||||||
|
assertSame("Did not find Table2", table2, wb.getTable("Table2"));
|
||||||
|
|
||||||
|
// If table name is modified after getTable is called, the table can only be found by its new name
|
||||||
|
// This test makes sure that if any caching is done that getTable never uses a stale cache
|
||||||
|
table1.setName("Table1");
|
||||||
|
assertSame("Did not find Tabella1 renamed to Table1", table1, wb.getTable("TABLE1"));
|
||||||
|
|
||||||
|
wb.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import org.junit.runners.Suite;
|
|||||||
@Suite.SuiteClasses({
|
@Suite.SuiteClasses({
|
||||||
TestDrawingManager.class,
|
TestDrawingManager.class,
|
||||||
TestDrawingManager2.class,
|
TestDrawingManager2.class,
|
||||||
TestFormulaParser.class,
|
//TestFormulaParser.class, //converted to junit4
|
||||||
TestFormulaParserEval.class,
|
TestFormulaParserEval.class,
|
||||||
TestFormulaParserIf.class,
|
TestFormulaParserIf.class,
|
||||||
TestLinkTable.class,
|
TestLinkTable.class,
|
||||||
|
@ -1285,7 +1285,7 @@ public final class TestFormulaParser {
|
|||||||
String formula = "Sheet1!A1:Sheet1!B3";
|
String formula = "Sheet1!A1:Sheet1!B3";
|
||||||
HSSFWorkbook wb = new HSSFWorkbook();
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
wb.createSheet("Sheet1");
|
wb.createSheet("Sheet1");
|
||||||
Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb), FormulaType.CELL, -1);
|
Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb), FormulaType.CELL, -1, -1);
|
||||||
|
|
||||||
if (ptgs.length == 3) {
|
if (ptgs.length == 3) {
|
||||||
confirmTokenClasses(ptgs, Ref3DPtg.class, Ref3DPtg.class, RangePtg.class);
|
confirmTokenClasses(ptgs, Ref3DPtg.class, Ref3DPtg.class, RangePtg.class);
|
||||||
@ -1486,8 +1486,8 @@ public final class TestFormulaParser {
|
|||||||
|
|
||||||
confirmParseError(wb, "A1:ROUND(B1,1)", "The RHS of the range operator ':' at position 3 is not a proper reference.");
|
confirmParseError(wb, "A1:ROUND(B1,1)", "The RHS of the range operator ':' at position 3 is not a proper reference.");
|
||||||
|
|
||||||
confirmParseError(wb, "Sheet1!!!", "Parse error near char 7 '!' in specified formula 'Sheet1!!!'. Expected number, string, or defined name");
|
confirmParseError(wb, "Sheet1!!!", "Parse error near char 7 '!' in specified formula 'Sheet1!!!'. Expected number, string, defined name, or data table");
|
||||||
confirmParseError(wb, "Sheet1!.Name", "Parse error near char 7 '.' in specified formula 'Sheet1!.Name'. Expected number, string, or defined name");
|
confirmParseError(wb, "Sheet1!.Name", "Parse error near char 7 '.' in specified formula 'Sheet1!.Name'. Expected number, string, defined name, or data table");
|
||||||
confirmParseError(wb, "Sheet1!Sheet1", "Specified name 'Sheet1' for sheet Sheet1 not found");
|
confirmParseError(wb, "Sheet1!Sheet1", "Specified name 'Sheet1' for sheet Sheet1 not found");
|
||||||
confirmParseError(wb, "Sheet1!F:Sheet1!G", "'Sheet1!F' is not a proper reference.");
|
confirmParseError(wb, "Sheet1!F:Sheet1!G", "'Sheet1!F' is not a proper reference.");
|
||||||
confirmParseError(wb, "Sheet1!F..foobar", "Complete area reference expected after sheet name at index 11.");
|
confirmParseError(wb, "Sheet1!F..foobar", "Complete area reference expected after sheet name at index 11.");
|
||||||
|
Loading…
Reference in New Issue
Block a user