diff --git a/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java b/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java
index 1f295d0f33..e5f2afd250 100644
--- a/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java
+++ b/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java
@@ -67,7 +67,7 @@ public final class HSSFFormulaParser {
* @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
*/
public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType, int sheetIndex) throws FormulaParseException {
- return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType, sheetIndex);
+ return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType, sheetIndex, -1);
}
/**
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
index dbf85b869b..1d9459a71a 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
@@ -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.Ref3DPtg;
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.CellReference;
import org.apache.poi.util.POILogFactory;
@@ -267,4 +268,8 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL97;
}
+
+ public Table getTable(String name) {
+ throw new IllegalStateException("XSSF-style tables are not supported for HSSF");
+ }
}
diff --git a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java
index b0a3c76060..45e6351f53 100644
--- a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java
+++ b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java
@@ -68,6 +68,7 @@ public interface EvaluationWorkbook {
*/
ExternalName getExternalName(String nameName, String sheetName, int externalWorkbookNumber);
+
EvaluationName getName(NamePtg namePtg);
EvaluationName getName(String name, int sheetIndex);
String resolveNameXText(NameXPtg ptg);
diff --git a/src/java/org/apache/poi/ss/formula/FormulaParser.java b/src/java/org/apache/poi/ss/formula/FormulaParser.java
index 3820043d1f..b2e7acf730 100644
--- a/src/java/org/apache/poi/ss/formula/FormulaParser.java
+++ b/src/java/org/apache/poi/ss/formula/FormulaParser.java
@@ -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.ptg.AbstractFunctionPtg;
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.ArrayPtg;
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.usermodel.FormulaError;
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.CellReference;
import org.apache.poi.ss.util.CellReference.NameType;
@@ -117,6 +119,7 @@ public final class FormulaParser {
private final SpreadsheetVersion _ssVersion;
private final int _sheetIndex;
+ private final int _rowIndex; // 0-based
/**
@@ -131,13 +134,14 @@ public final class FormulaParser {
* model.Workbook, then use the convenience method on
* usermodel.HSSFFormulaEvaluator
*/
- private FormulaParser(String formula, FormulaParsingWorkbook book, int sheetIndex){
+ private FormulaParser(String formula, FormulaParsingWorkbook book, int sheetIndex, int rowIndex){
_formulaString = formula;
_pointer=0;
_book = book;
_ssVersion = book == null ? SpreadsheetVersion.EXCEL97 : book.getSpreadsheetVersion();
_formulaLength = _formulaString.length();
_sheetIndex = sheetIndex;
+ _rowIndex = rowIndex;
}
/**
@@ -148,18 +152,41 @@ public final class FormulaParser {
* @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.
+ * @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.
* The sheet index is required to resolve sheet-level names. -1 means that
* the scope of the name will be ignored and the parser will match names only by name
*
* @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) {
- FormulaParser fp = new FormulaParser(formula, workbook, sheetIndex);
+ 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);
}
+ public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType, int sheetIndex) {
+ return parse(formula, workbook, formulaType, sheetIndex, -1);
+ }
+
+ /**
+ * 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) {
+ Ptg[] arr = FormulaParser.parse(tableText, workbook, 0, 0, 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 */
private void GetChar() {
// The intersection operator is a space. We track whether the run of
@@ -528,6 +555,266 @@ 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:
+ *
+ * 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] ] + *+ * @param tableName + * @return + */ + private ParseNode parseStructuredReference(String tableName){ + + if ( ! (_ssVersion.equals(SpreadsheetVersion.EXCEL2007)) ) { + throw new FormulaParseException("Strctured references work only on XSSF (Excel 2007)!"); + } + Table tbl = _book.getTable(tableName); + if (tbl == null) { + throw new FormulaParseException("Illegal table name!"); + } + String sheetName = tbl.getSheetName(); + + int startCol = tbl.getStartColIndex(); + int endCol = tbl.getEndColIndex(); + int startRow = tbl.getStartRowIndex(); + int endRow = tbl.getEndRowIndex(); + + 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 qunatifier "+ 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 qunatifier "+ name); + } + nSpecQuantifiers++; + } else { + throw new FormulaParseException("The formula "+ _formulaString + " is illegal"); + } + } + } else { + Match(']'); + } + + 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){ + 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 tl = new CellReference(actualStartRow, actualStartCol); + CellReference br = new CellReference(actualEndRow, actualEndCol); + SheetIdentifier sheetIden = new SheetIdentifier( null, new NameIdentifier(sheetName, true)); + Ptg ptg = _book.get3DReferencePtg(new AreaReference(tl, br), 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 @@ -558,6 +845,11 @@ public final class FormulaParser { if (look == '(') { return function(name); } + //TODO Livshen's code + if(look == '['){ + return parseStructuredReference(name); + } + //TODO End of Livshen's code if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) { return new ParseNode(BoolPtg.valueOf(name.equalsIgnoreCase("TRUE"))); } @@ -581,9 +873,9 @@ public final class FormulaParser { private String parseAsName() { StringBuilder sb = new StringBuilder(); - // defined names may begin with a letter or underscore - if (!Character.isLetter(look) && look != '_') { - throw expected("number, string, or defined name"); + // defined names may begin with a letter or underscore or backslash + if (!Character.isLetter(look) && look != '_' && look != '\\') { + throw expected("number, string, defined name, or table"); } while (isValidDefinedNameChar(look)) { sb.append(look); @@ -1175,7 +1467,9 @@ public final class FormulaParser { Match('}'); 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(); } if (look == '.') { diff --git a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java index fe907fbb1d..0fe93eec7f 100644 --- a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java @@ -20,6 +20,7 @@ package org.apache.poi.ss.formula; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.ptg.Ptg; 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.CellReference; @@ -41,6 +42,11 @@ public interface FormulaParsingWorkbook { */ 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 */ diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java b/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java index cc590772de..277fbfc159 100644 --- a/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java +++ b/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java @@ -407,6 +407,9 @@ public final class OperationEvaluationContext { return new FunctionNameEval(name); } } + public int getSheetIndex() { + return _sheetIndex; + } private ValueEval getExternalNameXEval(ExternalName externName, String workbookName) { try { diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index f4b30c4f0e..84099bf2bb 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -26,20 +26,60 @@ import java.util.TreeSet; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; 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.functions.Choose; import org.apache.poi.ss.formula.functions.FreeRefFunction; import org.apache.poi.ss.formula.functions.Function; 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.UDFFinder; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; - /** * Evaluates formula cells. * @@ -259,6 +299,15 @@ public final class WorkbookEvaluator { try { Ptg[] ptgs = _workbook.getFormulaTokens(srcCell); +// System.out.println("====="); +// XSSFCell c = ((XSSFEvaluationCell)srcCell).getXSSFCell(); +// System.out.println("Formula is "+ c); +// System.out.println("The cell is " + c.getSheet().getSheetName()+"!"+c.getReference()); +// System.out.println("Evaluation tokens : "); // TODO Dlivshen remove +// for (Ptg ptg : ptgs) { // TODO Dlivshen remove +// System.out.println(ptg); // TODO Dlivshen remove +// } // TODO Dlivshen remove +// System.out.println("======"); // TODO Dlivshen remove if (evalListener == null) { result = evaluateFormula(ec, ptgs); } else { @@ -270,9 +319,9 @@ public final class WorkbookEvaluator { tracker.updateCacheResult(result); } catch (NotImplementedException e) { - throw addExceptionInfo(e, sheetIndex, rowIndex, columnIndex); + throw addExceptionInfo(e, sheetIndex, rowIndex, columnIndex); } catch (RuntimeException re) { - if (re.getCause() instanceof WorkbookNotFoundException && _ignoreMissingWorkbooks) { + if (re.getCause() instanceof WorkbookNotFoundException && _ignoreMissingWorkbooks) { logInfo(re.getCause().getMessage() + " - Continuing with cached value!"); switch(srcCell.getCachedFormulaResultType()) { case Cell.CELL_TYPE_NUMERIC: diff --git a/src/java/org/apache/poi/ss/formula/functions/Indirect.java b/src/java/org/apache/poi/ss/formula/functions/Indirect.java index 7774af506d..7366c798cc 100644 --- a/src/java/org/apache/poi/ss/formula/functions/Indirect.java +++ b/src/java/org/apache/poi/ss/formula/functions/Indirect.java @@ -17,13 +17,18 @@ 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.ErrorEval; import org.apache.poi.ss.formula.eval.EvaluationException; import org.apache.poi.ss.formula.eval.MissingArgEval; import org.apache.poi.ss.formula.eval.OperandResolver; 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 @@ -42,7 +47,7 @@ import org.apache.poi.ss.formula.OperationEvaluationContext; */ public final class Indirect implements FreeRefFunction { - public static final FreeRefFunction instance = new Indirect(); + public static final FreeRefFunction instance = new Indirect(); private Indirect() { // enforce singleton @@ -88,8 +93,12 @@ public final class Indirect implements FreeRefFunction { 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) { + + ec.getRowIndex(); + ec.getColumnIndex(); + // Search backwards for '!' because sheet names can contain '!' int plingPos = text.lastIndexOf('!'); @@ -112,16 +121,25 @@ public final class Indirect implements FreeRefFunction { String refStrPart1; String refStrPart2; - - int colonPos = refText.indexOf(':'); - if (colonPos < 0) { - refStrPart1 = refText.trim(); - refStrPart2 = null; - } else { - refStrPart1 = refText.substring(0, colonPos).trim(); - refStrPart2 = refText.substring(colonPos + 1).trim(); - } - return ec.getDynamicReference(workbookName, sheetName, refStrPart1, refStrPart2, isA1style); + 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 argumnet is regular reference + int colonPos = refText.indexOf(':'); + if (colonPos < 0) { + refStrPart1 = refText.trim(); + refStrPart2 = null; + } else { + refStrPart1 = refText.substring(0, colonPos).trim(); + refStrPart2 = refText.substring(colonPos + 1).trim(); + } + return ec.getDynamicReference(workbookName, sheetName, refStrPart1, refStrPart2, isA1style); + } } /** diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java index 4a6882ac42..25d80a3721 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFEvaluationWorkbook.java @@ -36,6 +36,7 @@ import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.Ref3DPxg; import org.apache.poi.ss.formula.udf.IndexedUDFFinder; 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.CellReference; import org.apache.poi.util.NotImplemented; @@ -310,6 +311,10 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork return _uBook.createName(); } + public Table getTable(String name) { + return _uBook.getTable(name); + } + public UDFFinder getUDFFinder(){ return _uBook.getUDFFinder(); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index 29aaa867e2..f542e0f172 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -506,7 +506,7 @@ public final class XSSFCell implements Cell { XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(sheet.getWorkbook()); 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, getRowIndex() - ref.getFirstRow(), getColumnIndex() - ref.getFirstColumn()); return FormulaRenderer.toFormulaString(fpb, fmla); @@ -550,7 +550,7 @@ public final class XSSFCell implements Cell { XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); //validate through the FormulaParser - FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet())); + FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()), -1); CTCellFormula f = CTCellFormula.Factory.newInstance(); f.setStringValue(formula); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java index 70fc720671..b3d3343ec5 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java @@ -50,6 +50,6 @@ public final class XSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook { public Ptg[] getFormulaTokens(EvaluationCell evalCell) { XSSFCell cell = ((XSSFEvaluationCell)evalCell).getXSSFCell(); 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()); } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java index ecbec23b5a..be68c30500 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java @@ -192,7 +192,7 @@ public final class XSSFName implements Name { public void setRefersToFormula(String formulaText) { XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(_workbook); //validate through the FormulaParser - FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex()); + FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex(), -1); _ctName.setStringValue(formulaText); } @@ -203,7 +203,7 @@ public final class XSSFName implements Name { return false; } 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); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java index c8a932bb98..72e3d341ad 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFTable.java @@ -24,11 +24,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; 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.xssf.usermodel.helpers.XSSFXmlColumnPr; import org.apache.xmlbeans.XmlException; @@ -48,10 +50,12 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.TableDocument; * * @author Roberto Manicardi */ -public class XSSFTable extends POIXMLDocumentPart { +public class XSSFTable extends POIXMLDocumentPart implements Table { private CTTable ctTable; private List