From: Yegor Kozlov Date: Sat, 18 Apr 2009 07:12:38 +0000 (+0000) Subject: Allow 255 arguments for excel functions in XSSF, see bugzilla 46279 X-Git-Tag: REL_3_5_BETA6~55 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=46b06af35a59c1bb1ce4ee91d32106ab124287b0;p=poi.git Allow 255 arguments for excel functions in XSSF, see bugzilla 46279 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@766251 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index f58747e7ed..087588e7e5 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 46279 - Allow 255 arguments for excel functions in XSSF 47028 - Fixed XSSFCell to preserve cell style when cell value is set to blank 47026 - Avoid NPE in XSSFCell.setCellType() when workbook does not have SST 46987 - Allow RecordFactory to handle non-zero padding at the end of the workbook stream diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 4ef245ad87..5bccbf28cd 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 46279 - Allow 255 arguments for excel functions in XSSF 47028 - Fixed XSSFCell to preserve cell style when cell value is set to blank 47026 - Avoid NPE in XSSFCell.setCellType() when workbook does not have SST 46987 - Allow RecordFactory to handle non-zero padding at the end of the workbook stream diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java index 1b78cc44df..848888e951 100644 --- a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java @@ -30,7 +30,15 @@ public final class FunctionMetadataRegistry { */ public static final String FUNCTION_NAME_IF = "IF"; - public static final short FUNCTION_INDEX_SUM = 4; + /** + * maxParams=30 in functionMetadata.txt means the maximum number arguments supported + * by the given version of Excel. Validation routines should take the actual limit (Excel 97 or 2007) + * from the SpreadsheetVersion enum. + * @see org.apache.poi.ss.formula.FormulaParser#validateNumArgs(int, FunctionMetadata) + */ + public static final short FUNCTION_MAX_PARAMS = 30; + + public static final short FUNCTION_INDEX_SUM = 4; public static final short FUNCTION_INDEX_EXTERNAL = 255; private static FunctionMetadataRegistry _instance; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java index 046a9c2937..10805658a7 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java @@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.formula.NamePtg; import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.ss.formula.*; +import org.apache.poi.ss.SpreadsheetVersion; /** * Internal POI use only @@ -154,4 +155,8 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E return new NamePtg(_index); } } + + public SpreadsheetVersion getSpreadsheetVersion(){ + return SpreadsheetVersion.EXCEL97; + } } diff --git a/src/java/org/apache/poi/ss/formula/FormulaParser.java b/src/java/org/apache/poi/ss/formula/FormulaParser.java index 8311a17c44..d36793be93 100644 --- a/src/java/org/apache/poi/ss/formula/FormulaParser.java +++ b/src/java/org/apache/poi/ss/formula/FormulaParser.java @@ -31,6 +31,7 @@ import org.apache.poi.hssf.usermodel.HSSFErrorConstants; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference.NameType; +import org.apache.poi.ss.SpreadsheetVersion; /** * This class parses a formula string into a List of tokens in RPN order. @@ -982,13 +983,20 @@ public final class FormulaParser { } msg += " but got " + numArgs + "."; throw new FormulaParseException(msg); - } - if(numArgs > fm.getMaxParams()) { + } + //the maximum number of arguments depends on the Excel version + int maxArgs = fm.getMaxParams(); + if( maxArgs == FunctionMetadataRegistry.FUNCTION_MAX_PARAMS) { + //_book can be omitted by test cases + if(_book != null) maxArgs = _book.getSpreadsheetVersion().getMaxFunctionArgs(); + } + + if(numArgs > maxArgs) { String msg = "Too many arguments to function '" + fm.getName() + "'. "; if(fm.hasFixedArgsLength()) { - msg += "Expected " + fm.getMaxParams(); + msg += "Expected " + maxArgs; } else { - msg += "At most " + fm.getMaxParams() + " were expected"; + msg += "At most " + maxArgs + " were expected"; } msg += " but got " + numArgs + "."; throw new FormulaParseException(msg); diff --git a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java index 57f0f7a2c4..2ecd69be9a 100644 --- a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java @@ -18,6 +18,7 @@ package org.apache.poi.ss.formula; import org.apache.poi.hssf.record.formula.NameXPtg; +import org.apache.poi.ss.SpreadsheetVersion; /** * Abstracts a workbook for the purpose of formula parsing.
@@ -44,4 +45,11 @@ public interface FormulaParsingWorkbook { * @param sheetName a name of a sheet in that workbook */ int getExternalSheetIndex(String workbookName, String sheetName); + + /** + * Returns an enum holding spreadhseet properties specific to an Excel version ( + * max column and row numbers, max arguments to a function, etc.) + */ + SpreadsheetVersion getSpreadsheetVersion(); + } 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 2a0e1ff4b2..6bbcf1a8c6 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -366,14 +366,8 @@ public final class XSSFCell implements Cell { } XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); - try { - Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, wb.getSheetIndex(getSheet())); - } catch (RuntimeException e) { - if (e.getClass().getName().startsWith(FormulaParser.class.getName())) { - throw new IllegalArgumentException("Unparsable formula '" + formula + "'", e); - } - throw e; - } + //validate through the FormulaParser + FormulaParser.parse(formula, fpb, FormulaType.CELL, wb.getSheetIndex(getSheet())); 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 f733a67bd1..ef2bdf89b3 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java @@ -21,6 +21,7 @@ import org.apache.poi.hssf.record.formula.NamePtg; import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.ss.formula.*; +import org.apache.poi.ss.SpreadsheetVersion; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; /** @@ -172,4 +173,8 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E return new NamePtg(_index); } } + + public SpreadsheetVersion getSpreadsheetVersion(){ + return SpreadsheetVersion.EXCEL2007; + } } 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 9f3c7f0125..ed4d364abe 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java @@ -191,14 +191,9 @@ public final class XSSFName implements Name { public void setRefersToFormula(String formulaText) { XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(workbook); - try { - FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex()); - } catch (RuntimeException e) { - if (e.getClass().getName().startsWith(FormulaParser.class.getName())) { - throw new IllegalArgumentException("Unparsable formula '" + formulaText + "'", e); - } - throw e; - } + //validate through the FormulaParser + FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex()); + ctName.setStringValue(formulaText); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java index 4cc3db4e56..4cd7c41634 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java @@ -22,19 +22,12 @@ import java.util.*; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.util.CellReference; -import org.apache.poi.ss.formula.FormulaParser; -import org.apache.poi.ss.formula.FormulaType; -import org.apache.poi.ss.formula.FormulaRenderer; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.xssf.model.CalculationChain; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.FormulaShifter; -import org.apache.poi.hssf.record.SharedFormulaRecord; import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogFactory; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; /** * High level representation of a row of a spreadsheet. diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 80d383d759..242f24f048 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -24,19 +24,12 @@ import java.util.*; import javax.xml.namespace.QName; import org.apache.poi.hssf.util.PaneInformation; -import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.FormulaShifter; -import org.apache.poi.hssf.record.SharedFormulaRecord; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; -import org.apache.poi.ss.util.AreaReference; -import org.apache.poi.ss.formula.FormulaParser; -import org.apache.poi.ss.formula.FormulaType; -import org.apache.poi.ss.formula.FormulaRenderer; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.xssf.model.CommentsTable; -import org.apache.poi.xssf.model.CalculationChain; import org.apache.poi.xssf.usermodel.helpers.ColumnHelper; import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter; import org.apache.poi.POIXMLDocumentPart; diff --git a/src/testcases/org/apache/poi/hssf/HSSFITestDataProvider.java b/src/testcases/org/apache/poi/hssf/HSSFITestDataProvider.java index 926ed3d382..d77f3a0998 100755 --- a/src/testcases/org/apache/poi/hssf/HSSFITestDataProvider.java +++ b/src/testcases/org/apache/poi/hssf/HSSFITestDataProvider.java @@ -48,7 +48,7 @@ public final class HSSFITestDataProvider implements ITestDataProvider { } public SpreadsheetVersion getSpreadsheetVersion(){ - return SpreadsheetVersion.EXCEL2007; + return SpreadsheetVersion.EXCEL97; } private HSSFITestDataProvider(){} diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java index ef6d0a14fa..adbcaad940 100755 --- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java +++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestBugzillaIssues.java @@ -19,7 +19,9 @@ package org.apache.poi.ss.usermodel; import junit.framework.TestCase; import junit.framework.AssertionFailedError; import org.apache.poi.ss.ITestDataProvider; +import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * A base class for bugzilla issues that can be described in terms of common ss interfaces. @@ -295,4 +297,42 @@ public abstract class BaseTestBugzillaIssues extends TestCase { assertEquals(d, (311+312+321+322), 0.0000001); } + + public void testMaxFunctionArguments_bug46729(){ + String[] func = {"COUNT", "AVERAGE", "MAX", "MIN", "OR", "SUBTOTAL", "SKEW"}; + + SpreadsheetVersion ssVersion = getTestDataProvider().getSpreadsheetVersion(); + Workbook wb = getTestDataProvider().createWorkbook(); + Cell cell = wb.createSheet().createRow(0).createCell(0); + + String fmla; + for (String name : func) { + + fmla = createFunction(name, 5); + cell.setCellFormula(fmla); + + fmla = createFunction(name, ssVersion.getMaxFunctionArgs()); + cell.setCellFormula(fmla); + + try { + fmla = createFunction(name, ssVersion.getMaxFunctionArgs() + 1); + cell.setCellFormula(fmla); + fail("Expected FormulaParseException"); + } catch (RuntimeException e){ + assertTrue(e.getMessage().startsWith("Too many arguments to function '"+name+"'")); + } + } + } + + private String createFunction(String name, int maxArgs){ + StringBuffer fmla = new StringBuffer(); + fmla.append(name); + fmla.append("("); + for(int i=0; i < maxArgs; i++){ + if(i > 0) fmla.append(','); + fmla.append("A1"); + } + fmla.append(")"); + return fmla.toString(); + } } \ No newline at end of file