improved work with UDFs and Analysis Toolpack functions, ATP functions are enabled by default and user can create / evaluate them just like built-in functions, both HSSF andf XSSF are supported

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1039870 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2010-11-28 12:03:52 +00:00
parent fa5be6e820
commit db89d09ca3
18 changed files with 547 additions and 150 deletions

View File

@ -66,9 +66,7 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
} }
public NameXPtg getNameXPtg(String name) { public NameXPtg getNameXPtg(String name) {
// TODO YK: passing UDFFinder.DEFAULT is temporary, return _iBook.getNameXPtg(name, _uBook.getUDFFinder());
// a proper design should take it from the parent HSSFWorkbook
return _iBook.getNameXPtg(name, UDFFinder.DEFAULT);
} }
/** /**
@ -147,6 +145,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord(); FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord();
return fra.getFormulaTokens(); return fra.getFormulaTokens();
} }
public UDFFinder getUDFFinder(){
return _uBook.getUDFFinder();
}
private static final class Name implements EvaluationName { private static final class Name implements EvaluationName {

View File

@ -66,6 +66,8 @@ import org.apache.poi.ss.formula.ptg.UnionPtg;
import org.apache.poi.hssf.util.CellReference; import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.ss.formula.FormulaType;
@ -148,6 +150,12 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class); private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class);
/**
* The locator of user-defined functions.
* By default includes functions from the Excel Analysis Toolpack
*/
private UDFFinder _udfFinder = UDFFinder.DEFAULT;
public static HSSFWorkbook create(InternalWorkbook book) { public static HSSFWorkbook create(InternalWorkbook book) {
return new HSSFWorkbook(book); return new HSSFWorkbook(book);
} }
@ -1672,11 +1680,33 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
} }
} }
public CreationHelper getCreationHelper() { public HSSFCreationHelper getCreationHelper() {
return new HSSFCreationHelper(this); return new HSSFCreationHelper(this);
} }
private static byte[] newUID() { private static byte[] newUID() {
return new byte[16]; return new byte[16];
} }
/**
*
* Returns the locator of user-defined functions.
* The default instance extends the built-in functions with the Analysis Tool Pack
*
* @return the locator of user-defined functions
*/
/*package*/ UDFFinder getUDFFinder(){
return _udfFinder;
}
/**
* Register a new toolpack in this workbook.
*
* @param toopack the toolpack to register
*/
public void addToolPack(UDFFinder toopack){
AggregatingUDFFinder udfs = (AggregatingUDFFinder)_udfFinder;
udfs.add(toopack);
}
} }

View File

@ -20,6 +20,7 @@ package org.apache.poi.ss.formula;
import org.apache.poi.ss.formula.ptg.NamePtg; import org.apache.poi.ss.formula.ptg.NamePtg;
import org.apache.poi.ss.formula.ptg.NameXPtg; 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.udf.UDFFinder;
/** /**
* Abstracts a workbook for the purpose of formula evaluation.<br/> * Abstracts a workbook for the purpose of formula evaluation.<br/>
@ -49,8 +50,10 @@ public interface EvaluationWorkbook {
int convertFromExternSheetIndex(int externSheetIndex); int convertFromExternSheetIndex(int externSheetIndex);
ExternalName getExternalName(int externSheetIndex, int externNameIndex); ExternalName getExternalName(int externSheetIndex, int externNameIndex);
EvaluationName getName(NamePtg namePtg); EvaluationName getName(NamePtg namePtg);
EvaluationName getName(String name, int sheetIndex);
String resolveNameXText(NameXPtg ptg); String resolveNameXText(NameXPtg ptg);
Ptg[] getFormulaTokens(EvaluationCell cell); Ptg[] getFormulaTokens(EvaluationCell cell);
UDFFinder getUDFFinder();
class ExternalSheet { class ExternalSheet {
private final String _workbookName; private final String _workbookName;

View File

@ -61,8 +61,8 @@ import org.apache.poi.ss.formula.eval.ValueEval;
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.IfFunc; import org.apache.poi.ss.formula.functions.IfFunc;
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.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
import org.apache.poi.ss.formula.eval.NotImplementedException; import org.apache.poi.ss.formula.eval.NotImplementedException;
@ -91,7 +91,7 @@ public final class WorkbookEvaluator {
private final Map<String, Integer> _sheetIndexesByName; private final Map<String, Integer> _sheetIndexesByName;
private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment; private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
private final IStabilityClassifier _stabilityClassifier; private final IStabilityClassifier _stabilityClassifier;
private final UDFFinder _udfFinder; private final AggregatingUDFFinder _udfFinder;
/** /**
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only) * @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
@ -109,7 +109,13 @@ public final class WorkbookEvaluator {
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY; _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
_workbookIx = 0; _workbookIx = 0;
_stabilityClassifier = stabilityClassifier; _stabilityClassifier = stabilityClassifier;
_udfFinder = udfFinder == null ? UDFFinder.DEFAULT : udfFinder;
AggregatingUDFFinder defaultToolkit = // workbook can be null in unit tests
workbook == null ? null : (AggregatingUDFFinder)workbook.getUDFFinder();
if(defaultToolkit != null && udfFinder != null) {
defaultToolkit.add(udfFinder);
}
_udfFinder = defaultToolkit;
} }
/** /**
@ -124,10 +130,7 @@ public final class WorkbookEvaluator {
} }
/* package */ EvaluationName getName(String name, int sheetIndex) { /* package */ EvaluationName getName(String name, int sheetIndex) {
NamePtg namePtg = null; NamePtg namePtg = _workbook.getName(name, sheetIndex).createPtg();
if(_workbook instanceof HSSFEvaluationWorkbook){
namePtg =((HSSFEvaluationWorkbook)_workbook).getName(name, sheetIndex).createPtg();
}
if(namePtg == null) { if(namePtg == null) {
return null; return null;

View File

@ -32,133 +32,150 @@ import org.apache.poi.ss.formula.eval.NotImplementedException;
*/ */
public final class AnalysisToolPak implements UDFFinder { public final class AnalysisToolPak implements UDFFinder {
public static final UDFFinder instance = new AnalysisToolPak(); public static final UDFFinder instance = new AnalysisToolPak();
private static final class NotImplemented implements FreeRefFunction { private static final class NotImplemented implements FreeRefFunction {
private final String _functionName; private final String _functionName;
public NotImplemented(String functionName) { public NotImplemented(String functionName) {
_functionName = functionName; _functionName = functionName;
} }
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
throw new NotImplementedException(_functionName); throw new NotImplementedException(_functionName);
} }
}; }
private final Map<String, FreeRefFunction> _functionsByName = createFunctionsMap(); ;
private final Map<String, FreeRefFunction> _functionsByName = createFunctionsMap();
private AnalysisToolPak() { private AnalysisToolPak() {
// enforce singleton // enforce singleton
} }
public FreeRefFunction findFunction(String name) { public FreeRefFunction findFunction(String name) {
return _functionsByName.get(name); return _functionsByName.get(name);
} }
private Map<String, FreeRefFunction> createFunctionsMap() { private Map<String, FreeRefFunction> createFunctionsMap() {
Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(100); Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(108);
r(m, "ACCRINT", null); r(m, "ACCRINT", null);
r(m, "ACCRINTM", null); r(m, "ACCRINTM", null);
r(m, "AMORDEGRC", null); r(m, "AMORDEGRC", null);
r(m, "AMORLINC", null); r(m, "AMORLINC", null);
r(m, "BESSELI", null); r(m, "AVERAGEIF", null);
r(m, "BESSELJ", null); r(m, "AVERAGEIFS", null);
r(m, "BESSELK", null); r(m, "BAHTTEXT", null);
r(m, "BESSELY", null); r(m, "BESSELI", null);
r(m, "BIN2DEC", null); r(m, "BESSELJ", null);
r(m, "BIN2HEX", null); r(m, "BESSELK", null);
r(m, "BIN2OCT", null); r(m, "BESSELY", null);
r(m, "CO MPLEX", null); r(m, "BIN2DEC", null);
r(m, "CONVERT", null); r(m, "BIN2HEX", null);
r(m, "COUPDAYBS", null); r(m, "BIN2OCT", null);
r(m, "COUPDAYS", null); r(m, "COMPLEX", null);
r(m, "COUPDAYSNC", null); r(m, "CONVERT", null);
r(m, "COUPNCD", null); r(m, "COUNTIFS", null);
r(m, "COUPNUM", null); r(m, "COUPDAYBS", null);
r(m, "COUPPCD", null); r(m, "COUPDAYS", null);
r(m, "CUMIPMT", null); r(m, "COUPDAYSNC", null);
r(m, "CUMPRINC", null); r(m, "COUPNCD", null);
r(m, "DEC2BIN", null); r(m, "COUPNUM", null);
r(m, "DEC2HEX", null); r(m, "COUPPCD", null);
r(m, "DEC2OCT", null); r(m, "CUBEKPIMEMBER", null);
r(m, "DELTA", null); r(m, "CUBEMEMBER", null);
r(m, "DISC", null); r(m, "CUBEMEMBERPROPERTY", null);
r(m, "DOLLARDE", null); r(m, "CUBERANKEDMEMBER", null);
r(m, "DOLLARFR", null); r(m, "CUBESET", null);
r(m, "DURATION", null); r(m, "CUBESETCOUNT", null);
r(m, "EDATE", null); r(m, "CUBEVALUE", null);
r(m, "EFFECT", null); r(m, "CUMIPMT", null);
r(m, "EOMONTH", null); r(m, "CUMPRINC", null);
r(m, "ERF", null); r(m, "DEC2BIN", null);
r(m, "ERFC", null); r(m, "DEC2HEX", null);
r(m, "FACTDOUBLE", null); r(m, "DEC2OCT", null);
r(m, "FVSCHEDULE", null); r(m, "DELTA", null);
r(m, "GCD", null); r(m, "DISC", null);
r(m, "GESTEP", null); r(m, "DOLLARDE", null);
r(m, "HEX2BIN", null); r(m, "DOLLARFR", null);
r(m, "HEX2DEC", null); r(m, "DURATION", null);
r(m, "HEX2OCT", null); r(m, "EDATE", null);
r(m, "IMABS", null); r(m, "EFFECT", null);
r(m, "IMAGINARY", null); r(m, "EOMONTH", null);
r(m, "IMARGUMENT", null); r(m, "ERF", null);
r(m, "IMCONJUGATE", null); r(m, "ERFC", null);
r(m, "IMCOS", null); r(m, "FACTDOUBLE", null);
r(m, "IMDIV", null); r(m, "FVSCHEDULE", null);
r(m, "IMEXP", null); r(m, "GCD", null);
r(m, "IMLN", null); r(m, "GESTEP", null);
r(m, "IMLOG10", null); r(m, "HEX2BIN", null);
r(m, "IMLOG2", null); r(m, "HEX2DEC", null);
r(m, "IMPOWER", null); r(m, "HEX2OCT", null);
r(m, "IMPRODUCT", null); r(m, "IFERROR", null);
r(m, "IMREAL", null); r(m, "IMABS", null);
r(m, "IMSIN", null); r(m, "IMAGINARY", null);
r(m, "IMSQRT", null); r(m, "IMARGUMENT", null);
r(m, "IMSUB", null); r(m, "IMCONJUGATE", null);
r(m, "IMSUM", null); r(m, "IMCOS", null);
r(m, "INTRATE", null); r(m, "IMDIV", null);
r(m, "ISEVEN", ParityFunction.IS_EVEN); r(m, "IMEXP", null);
r(m, "ISODD", ParityFunction.IS_ODD); r(m, "IMLN", null);
r(m, "LCM", null); r(m, "IMLOG10", null);
r(m, "MDURATION", null); r(m, "IMLOG2", null);
r(m, "MROUND", null); r(m, "IMPOWER", null);
r(m, "MULTINOMIAL", null); r(m, "IMPRODUCT", null);
r(m, "NETWORKDAYS", null); r(m, "IMREAL", null);
r(m, "NOMINAL", null); r(m, "IMSIN", null);
r(m, "OCT2BIN", null); r(m, "IMSQRT", null);
r(m, "OCT2DEC", null); r(m, "IMSUB", null);
r(m, "OCT2HEX", null); r(m, "IMSUM", null);
r(m, "ODDFPRICE", null); r(m, "INTRATE", null);
r(m, "ODDFYIELD", null); r(m, "ISEVEN", ParityFunction.IS_EVEN);
r(m, "ODDLPRICE", null); r(m, "ISODD", ParityFunction.IS_ODD);
r(m, "ODDLYIELD", null); r(m, "JIS", null);
r(m, "PRICE", null); r(m, "LCM", null);
r(m, "PRICEDISC", null); r(m, "MDURATION", null);
r(m, "PRICEMAT", null); r(m, "MROUND", null);
r(m, "QUOTIENT", null); r(m, "MULTINOMIAL", null);
r(m, "RANDBETWEEN", RandBetween.instance); r(m, "NETWORKDAYS", null);
r(m, "RECEIVED", null); r(m, "NOMINAL", null);
r(m, "SERIESSUM", null); r(m, "OCT2BIN", null);
r(m, "SQRTPI", null); r(m, "OCT2DEC", null);
r(m, "TBILLEQ", null); r(m, "OCT2HEX", null);
r(m, "TBILLPRICE", null); r(m, "ODDFPRICE", null);
r(m, "TBILLYIELD", null); r(m, "ODDFYIELD", null);
r(m, "WEEKNUM", null); r(m, "ODDLPRICE", null);
r(m, "WORKDAY", null); r(m, "ODDLYIELD", null);
r(m, "XIRR", null); r(m, "PRICE", null);
r(m, "XNPV", null); r(m, "PRICEDISC", null);
r(m, "YEARFRAC", YearFrac.instance); r(m, "PRICEMAT", null);
r(m, "YIELD", null); r(m, "QUOTIENT", null);
r(m, "YIELDDISC", null); r(m, "RANDBETWEEN", RandBetween.instance);
r(m, "YIELDMAT", null); r(m, "RECEIVED", null);
r(m, "RTD", null);
r(m, "SERIESSUM", null);
r(m, "SQRTPI", null);
r(m, "SUMIFS", null);
r(m, "TBILLEQ", null);
r(m, "TBILLPRICE", null);
r(m, "TBILLYIELD", null);
r(m, "WEEKNUM", null);
r(m, "WORKDAY", null);
r(m, "XIRR", null);
r(m, "XNPV", null);
r(m, "YEARFRAC", YearFrac.instance);
r(m, "YIELD", null);
r(m, "YIELDDISC", null);
r(m, "YIELDMAT", null);
return m; return m;
} }
private static void r(Map<String, FreeRefFunction> m, String functionName, FreeRefFunction pFunc) { private static void r(Map<String, FreeRefFunction> m, String functionName, FreeRefFunction pFunc) {
FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc; FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc;
m.put(functionName, func); m.put(functionName, func);
} }
} }

View File

@ -27,6 +27,7 @@ import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.EvaluationName; import org.apache.poi.ss.formula.EvaluationName;
import org.apache.poi.ss.formula.EvaluationSheet; import org.apache.poi.ss.formula.EvaluationSheet;
import org.apache.poi.ss.formula.EvaluationWorkbook; import org.apache.poi.ss.formula.EvaluationWorkbook;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
/** /**
@ -102,6 +103,10 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
return _masterBook.getName(namePtg); return _masterBook.getName(namePtg);
} }
public EvaluationName getName(String name, int sheetIndex){
return _masterBook.getName(name, sheetIndex);
}
public EvaluationSheet getSheet(int sheetIndex) { public EvaluationSheet getSheet(int sheetIndex) {
return getSharedSheet(getSheetName(sheetIndex)); return getSharedSheet(getSheetName(sheetIndex));
} }
@ -130,6 +135,10 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
return _masterBook.resolveNameXText(ptg); return _masterBook.resolveNameXText(ptg);
} }
public UDFFinder getUDFFinder(){
return _masterBook.getUDFFinder();
}
private static final class OrderedSheet implements Comparable<OrderedSheet> { private static final class OrderedSheet implements Comparable<OrderedSheet> {
private final String _sheetName; private final String _sheetName;
private final int _index; private final int _index;

View File

@ -41,7 +41,7 @@ public final class FunctionMetadataRegistry {
private final FunctionMetadata[] _functionDataByIndex; private final FunctionMetadata[] _functionDataByIndex;
private final Map<String, FunctionMetadata> _functionDataByName; private final Map<String, FunctionMetadata> _functionDataByName;
private static FunctionMetadataRegistry getInstance() { public static FunctionMetadataRegistry getInstance() {
if (_instance == null) { if (_instance == null) {
_instance = FunctionMetadataReader.createRegistry(); _instance = FunctionMetadataReader.createRegistry();
} }

View File

@ -19,17 +19,22 @@ package org.apache.poi.ss.formula.udf;
import org.apache.poi.ss.formula.functions.FreeRefFunction; import org.apache.poi.ss.formula.functions.FreeRefFunction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/** /**
* Collects add-in libraries and VB macro functions together into one UDF finder * Collects add-in libraries and VB macro functions together into one UDF finder
* *
* @author PUdalau * @author PUdalau
*/ */
public final class AggregatingUDFFinder implements UDFFinder { public class AggregatingUDFFinder implements UDFFinder {
private final UDFFinder[] _usedToolPacks; private final Collection<UDFFinder> _usedToolPacks;
public AggregatingUDFFinder(UDFFinder ... usedToolPacks) { public AggregatingUDFFinder(UDFFinder ... usedToolPacks) {
_usedToolPacks = usedToolPacks.clone(); _usedToolPacks = new ArrayList<UDFFinder>(usedToolPacks.length);
_usedToolPacks.addAll(Arrays.asList(usedToolPacks));
} }
/** /**
@ -49,4 +54,13 @@ public final class AggregatingUDFFinder implements UDFFinder {
} }
return null; return null;
} }
/**
* Add a new toolpack
*
* @param toolPack the UDF toolpack to add
*/
public void add(UDFFinder toolPack){
_usedToolPacks.add(toolPack);
}
} }

View File

@ -38,12 +38,12 @@ public final class DefaultUDFFinder implements UDFFinder {
} }
HashMap<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(nFuncs * 3 / 2); HashMap<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(nFuncs * 3 / 2);
for (int i = 0; i < functionImpls.length; i++) { for (int i = 0; i < functionImpls.length; i++) {
m.put(functionNames[i], functionImpls[i]); m.put(functionNames[i].toUpperCase(), functionImpls[i]);
} }
_functionsByName = m; _functionsByName = m;
} }
public FreeRefFunction findFunction(String name) { public FreeRefFunction findFunction(String name) {
return _functionsByName.get(name); return _functionsByName.get(name.toUpperCase());
} }
} }

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
/** /**
@ -496,4 +497,12 @@ public interface Workbook {
* @throws IllegalArgumentException if the supplied sheet index or state is invalid * @throws IllegalArgumentException if the supplied sheet index or state is invalid
*/ */
void setSheetHidden(int sheetIx, int hidden); void setSheetHidden(int sheetIx, int hidden);
/**
* Register a new toolpack in this workbook.
*
* @param toopack the toolpack to register
*/
void addToolPack(UDFFinder toopack);
} }

View File

@ -0,0 +1,56 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xssf.model;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.util.Internal;
import java.util.HashMap;
/**
* A UDFFinder that can retrieve functions both by name and by fake index.
*
* @author Yegor Kozlov
*/
@Internal
public final class IndexedUDFFinder extends AggregatingUDFFinder {
private final HashMap<Integer, String> _funcMap;
public IndexedUDFFinder(UDFFinder... usedToolPacks) {
super(usedToolPacks);
_funcMap = new HashMap<Integer, String>();
}
public FreeRefFunction findFunction(String name) {
FreeRefFunction func = super.findFunction(name);
if (func != null) {
int idx = getFunctionIndex(name);
_funcMap.put(idx, name);
}
return func;
}
public String getFunctionName(int idx) {
return _funcMap.get(idx);
}
public int getFunctionIndex(String name) {
return name.hashCode();
}
}

View File

@ -17,6 +17,7 @@
package org.apache.poi.xssf.usermodel; package org.apache.poi.xssf.usermodel;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.ptg.NamePtg; import org.apache.poi.ss.formula.ptg.NamePtg;
import org.apache.poi.ss.formula.ptg.NameXPtg; import org.apache.poi.ss.formula.ptg.NameXPtg;
import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.formula.ptg.Ptg;
@ -29,8 +30,12 @@ import org.apache.poi.ss.formula.FormulaParser;
import org.apache.poi.ss.formula.FormulaParsingWorkbook; import org.apache.poi.ss.formula.FormulaParsingWorkbook;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook; import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.xssf.model.IndexedUDFFinder;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
import java.util.HashMap;
/** /**
* Internal POI use only * Internal POI use only
* *
@ -100,10 +105,18 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
} }
public NameXPtg getNameXPtg(String name) { public NameXPtg getNameXPtg(String name) {
// may require to return null to make tests pass IndexedUDFFinder udfFinder = (IndexedUDFFinder)getUDFFinder();
throw new RuntimeException("Not implemented yet"); FreeRefFunction func = udfFinder.findFunction(name);
if(func == null) return null;
else return new NameXPtg(0, udfFinder.getFunctionIndex(name));
} }
public String resolveNameXText(NameXPtg n) {
int idx = n.getNameIndex();
IndexedUDFFinder udfFinder = (IndexedUDFFinder)getUDFFinder();
return udfFinder.getFunctionName(idx);
}
public EvaluationSheet getSheet(int sheetIndex) { public EvaluationSheet getSheet(int sheetIndex) {
return new XSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex)); return new XSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
} }
@ -119,14 +132,6 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return _uBook.getSheetIndex(sheetName); return _uBook.getSheetIndex(sheetName);
} }
/**
* TODO - figure out what the hell this methods does in
* HSSF...
*/
public String resolveNameXText(NameXPtg n) {
throw new RuntimeException("method not implemented yet");
}
public String getSheetNameByExternSheet(int externSheetIndex) { public String getSheetNameByExternSheet(int externSheetIndex) {
int sheetIndex = convertFromExternalSheetIndex(externSheetIndex); int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);
return _uBook.getSheetName(sheetIndex); return _uBook.getSheetName(sheetIndex);
@ -145,6 +150,10 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return FormulaParser.parse(cell.getCellFormula(), frBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet())); return FormulaParser.parse(cell.getCellFormula(), frBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet()));
} }
public UDFFinder getUDFFinder(){
return _uBook.getUDFFinder();
}
private static final class Name implements EvaluationName { private static final class Name implements EvaluationName {
private final XSSFName _nameRecord; private final XSSFName _nameRecord;

View File

@ -46,6 +46,8 @@ import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
@ -53,11 +55,7 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.WorkbookUtil; import org.apache.poi.ss.util.WorkbookUtil;
import org.apache.poi.util.*; import org.apache.poi.util.*;
import org.apache.poi.xssf.model.CalculationChain; import org.apache.poi.xssf.model.*;
import org.apache.poi.xssf.model.MapInfo;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.model.ThemesTable;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.XmlOptions;
@ -123,6 +121,12 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
private ThemesTable theme; private ThemesTable theme;
/**
* The locator of user-defined functions.
* By default includes functions from the Excel Analysis Toolpack
*/
private IndexedUDFFinder _udfFinder = new IndexedUDFFinder(UDFFinder.DEFAULT);
/** /**
* TODO * TODO
*/ */
@ -1492,4 +1496,31 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
workbook.setWorkbookProtection(CTWorkbookProtection.Factory.newInstance()); workbook.setWorkbookProtection(CTWorkbookProtection.Factory.newInstance());
} }
} }
/**
*
* Returns the locator of user-defined functions.
* <p>
* The default instance extends the built-in functions with the Excel Analysis Tool Pack.
* To set / evaluate custom functions you need to register them as follows:
*
*
*
* </p>
* @return wrapped instance of UDFFinder that allows seeking functions both by index and name
*/
/*package*/ UDFFinder getUDFFinder() {
return _udfFinder;
}
/**
* Register a new toolpack in this workbook.
*
* @param toopack the toolpack to register
*/
public void addToolPack(UDFFinder toopack){
_udfFinder.add(toopack);
}
} }

View File

@ -0,0 +1,35 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xssf.usermodel;
import org.apache.poi.ss.formula.BaseTestExternalFunctions;
import org.apache.poi.xssf.XSSFITestDataProvider;
/**
* Tests setting and evaluating user-defined functions in HSSF
*/
public final class TestXSSFExternalFunctions extends BaseTestExternalFunctions {
public TestXSSFExternalFunctions() {
super(XSSFITestDataProvider.instance);
}
public void testATP(){
baseTestInvokeATP("atp.xlsx");
}
}

View File

@ -0,0 +1,36 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.usermodel;
import org.apache.poi.hssf.HSSFITestDataProvider;
import org.apache.poi.ss.formula.BaseTestExternalFunctions;
/**
* Tests setting and evaluating user-defined functions in HSSF
*/
public final class TestHSSFExternalFunctions extends BaseTestExternalFunctions {
public TestHSSFExternalFunctions() {
super(HSSFITestDataProvider.instance);
}
public void testATP(){
baseTestInvokeATP("atp.xls");
}
}

View File

@ -0,0 +1,144 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.formula;
import junit.framework.TestCase;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.StringEval;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.udf.DefaultUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Test setting / evaluating of Analysis Toolpack and user-defined functions
*
* @author Yegor Kozlov
*/
public class BaseTestExternalFunctions extends TestCase {
// define two custom user-defined functions
private static class MyFunc implements FreeRefFunction {
public MyFunc() {
//
}
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
if (args.length != 1 || !(args[0] instanceof StringEval)) {
return ErrorEval.VALUE_INVALID;
}
StringEval input = (StringEval) args[0];
return new StringEval(input.getStringValue() + "abc");
}
}
private static class MyFunc2 implements FreeRefFunction {
public MyFunc2() {
//
}
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
if (args.length != 1 || !(args[0] instanceof StringEval)) {
return ErrorEval.VALUE_INVALID;
}
StringEval input = (StringEval) args[0];
return new StringEval(input.getStringValue() + "abc2");
}
}
/**
* register the two test UDFs in a UDF finder, to be passed to the workbook
*/
private static UDFFinder customToolpack = new DefaultUDFFinder(
new String[] { "myFunc", "myFunc2"},
new FreeRefFunction[] { new MyFunc(), new MyFunc2()}
);
protected final ITestDataProvider _testDataProvider;
/**
* @param testDataProvider an object that provides test data in HSSF / XSSF specific way
*/
protected BaseTestExternalFunctions(ITestDataProvider testDataProvider) {
_testDataProvider = testDataProvider;
}
public void testExternalFunctions() {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sh = wb.createSheet();
Cell cell1 = sh.createRow(0).createCell(0);
cell1.setCellFormula("ISODD(1)+ISEVEN(2)"); // functions from the Excel Analysis Toolpack
assertEquals("ISODD(1)+ISEVEN(2)", cell1.getCellFormula());
Cell cell2 = sh.createRow(1).createCell(0);
try {
cell2.setCellFormula("MYFUNC(\"B1\")");
fail("Should fail because MYFUNC is an unknown function");
} catch (FormulaParseException e){
; //expected
}
wb.addToolPack(customToolpack);
cell2.setCellFormula("MYFUNC(\"B1\")");
assertEquals("MYFUNC(\"B1\")", cell2.getCellFormula());
Cell cell3 = sh.createRow(2).createCell(0);
cell3.setCellFormula("MYFUNC2(\"C1\")&\"-\"&A2"); //where A2 is defined above
assertEquals("MYFUNC2(\"C1\")&\"-\"&A2", cell3.getCellFormula());
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
assertEquals(2.0, evaluator.evaluate(cell1).getNumberValue());
assertEquals("B1abc", evaluator.evaluate(cell2).getStringValue());
assertEquals("C1abc2-B1abc", evaluator.evaluate(cell3).getStringValue());
}
/**
* test invoking saved ATP functions
*
* @param testFile either atp.xls or atp.xlsx
*/
public void baseTestInvokeATP(String testFile){
Workbook wb = _testDataProvider.openSampleWorkbook(testFile);
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
Sheet sh = wb.getSheetAt(0);
// these two are not imlemented in r
assertEquals("DELTA(1.3,1.5)", sh.getRow(0).getCell(1).getCellFormula());
assertEquals("COMPLEX(2,4)", sh.getRow(1).getCell(1).getCellFormula());
Cell cell2 = sh.getRow(2).getCell(1);
assertEquals("ISODD(2)", cell2.getCellFormula());
assertEquals(false, evaluator.evaluate(cell2).getBooleanValue());
assertEquals(Cell.CELL_TYPE_BOOLEAN, evaluator.evaluateFormulaCell(cell2));
Cell cell3 = sh.getRow(3).getCell(1);
assertEquals("ISEVEN(2)", cell3.getCellFormula());
assertEquals(true, evaluator.evaluate(cell3).getBooleanValue());
assertEquals(Cell.CELL_TYPE_BOOLEAN, evaluator.evaluateFormulaCell(cell3));
}
}

Binary file not shown.

Binary file not shown.