Browse Source

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
tags/REL_3_8_BETA1
Yegor Kozlov 13 years ago
parent
commit
db89d09ca3

+ 4
- 3
src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java View File

@@ -66,9 +66,7 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
}

public NameXPtg getNameXPtg(String name) {
// TODO YK: passing UDFFinder.DEFAULT is temporary,
// a proper design should take it from the parent HSSFWorkbook
return _iBook.getNameXPtg(name, UDFFinder.DEFAULT);
return _iBook.getNameXPtg(name, _uBook.getUDFFinder());
}

/**
@@ -147,6 +145,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord();
return fra.getFormulaTokens();
}
public UDFFinder getUDFFinder(){
return _uBook.getUDFFinder();
}

private static final class Name implements EvaluationName {


+ 31
- 1
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java 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.poifs.filesystem.DirectoryNode;
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.Row.MissingCellPolicy;
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);

/**
* 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) {
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);
}

private static byte[] newUID() {
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);
}

}

+ 3
- 0
src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java 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.NameXPtg;
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/>
@@ -49,8 +50,10 @@ public interface EvaluationWorkbook {
int convertFromExternSheetIndex(int externSheetIndex);
ExternalName getExternalName(int externSheetIndex, int externNameIndex);
EvaluationName getName(NamePtg namePtg);
EvaluationName getName(String name, int sheetIndex);
String resolveNameXText(NameXPtg ptg);
Ptg[] getFormulaTokens(EvaluationCell cell);
UDFFinder getUDFFinder();

class ExternalSheet {
private final String _workbookName;

+ 10
- 7
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java 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.FreeRefFunction;
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.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
import org.apache.poi.ss.formula.eval.NotImplementedException;
@@ -91,7 +91,7 @@ public final class WorkbookEvaluator {
private final Map<String, Integer> _sheetIndexesByName;
private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
private final IStabilityClassifier _stabilityClassifier;
private final UDFFinder _udfFinder;
private final AggregatingUDFFinder _udfFinder;

/**
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
@@ -109,7 +109,13 @@ public final class WorkbookEvaluator {
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
_workbookIx = 0;
_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) {
NamePtg namePtg = null;
if(_workbook instanceof HSSFEvaluationWorkbook){
namePtg =((HSSFEvaluationWorkbook)_workbook).getName(name, sheetIndex).createPtg();
}
NamePtg namePtg = _workbook.getName(name, sheetIndex).createPtg();

if(namePtg == null) {
return null;

+ 146
- 129
src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java View File

@@ -32,133 +32,150 @@ import org.apache.poi.ss.formula.eval.NotImplementedException;
*/
public final class AnalysisToolPak implements UDFFinder {

public static final UDFFinder instance = new AnalysisToolPak();

private static final class NotImplemented implements FreeRefFunction {
private final String _functionName;

public NotImplemented(String functionName) {
_functionName = functionName;
}

public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
throw new NotImplementedException(_functionName);
}
};

private final Map<String, FreeRefFunction> _functionsByName = createFunctionsMap();


private AnalysisToolPak() {
// enforce singleton
}

public FreeRefFunction findFunction(String name) {
return _functionsByName.get(name);
}

private Map<String, FreeRefFunction> createFunctionsMap() {
Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(100);

r(m, "ACCRINT", null);
r(m, "ACCRINTM", null);
r(m, "AMORDEGRC", null);
r(m, "AMORLINC", null);
r(m, "BESSELI", null);
r(m, "BESSELJ", null);
r(m, "BESSELK", null);
r(m, "BESSELY", null);
r(m, "BIN2DEC", null);
r(m, "BIN2HEX", null);
r(m, "BIN2OCT", null);
r(m, "CO MPLEX", null);
r(m, "CONVERT", null);
r(m, "COUPDAYBS", null);
r(m, "COUPDAYS", null);
r(m, "COUPDAYSNC", null);
r(m, "COUPNCD", null);
r(m, "COUPNUM", null);
r(m, "COUPPCD", null);
r(m, "CUMIPMT", null);
r(m, "CUMPRINC", null);
r(m, "DEC2BIN", null);
r(m, "DEC2HEX", null);
r(m, "DEC2OCT", null);
r(m, "DELTA", null);
r(m, "DISC", null);
r(m, "DOLLARDE", null);
r(m, "DOLLARFR", null);
r(m, "DURATION", null);
r(m, "EDATE", null);
r(m, "EFFECT", null);
r(m, "EOMONTH", null);
r(m, "ERF", null);
r(m, "ERFC", null);
r(m, "FACTDOUBLE", null);
r(m, "FVSCHEDULE", null);
r(m, "GCD", null);
r(m, "GESTEP", null);
r(m, "HEX2BIN", null);
r(m, "HEX2DEC", null);
r(m, "HEX2OCT", null);
r(m, "IMABS", null);
r(m, "IMAGINARY", null);
r(m, "IMARGUMENT", null);
r(m, "IMCONJUGATE", null);
r(m, "IMCOS", null);
r(m, "IMDIV", null);
r(m, "IMEXP", null);
r(m, "IMLN", null);
r(m, "IMLOG10", null);
r(m, "IMLOG2", null);
r(m, "IMPOWER", null);
r(m, "IMPRODUCT", null);
r(m, "IMREAL", null);
r(m, "IMSIN", null);
r(m, "IMSQRT", null);
r(m, "IMSUB", null);
r(m, "IMSUM", null);
r(m, "INTRATE", null);
r(m, "ISEVEN", ParityFunction.IS_EVEN);
r(m, "ISODD", ParityFunction.IS_ODD);
r(m, "LCM", null);
r(m, "MDURATION", null);
r(m, "MROUND", null);
r(m, "MULTINOMIAL", null);
r(m, "NETWORKDAYS", null);
r(m, "NOMINAL", null);
r(m, "OCT2BIN", null);
r(m, "OCT2DEC", null);
r(m, "OCT2HEX", null);
r(m, "ODDFPRICE", null);
r(m, "ODDFYIELD", null);
r(m, "ODDLPRICE", null);
r(m, "ODDLYIELD", null);
r(m, "PRICE", null);
r(m, "PRICEDISC", null);
r(m, "PRICEMAT", null);
r(m, "QUOTIENT", null);
r(m, "RANDBETWEEN", RandBetween.instance);
r(m, "RECEIVED", null);
r(m, "SERIESSUM", null);
r(m, "SQRTPI", 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;
}

private static void r(Map<String, FreeRefFunction> m, String functionName, FreeRefFunction pFunc) {
FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc;
m.put(functionName, func);
}
public static final UDFFinder instance = new AnalysisToolPak();

private static final class NotImplemented implements FreeRefFunction {
private final String _functionName;

public NotImplemented(String functionName) {
_functionName = functionName;
}

public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
throw new NotImplementedException(_functionName);
}
}

;

private final Map<String, FreeRefFunction> _functionsByName = createFunctionsMap();


private AnalysisToolPak() {
// enforce singleton
}

public FreeRefFunction findFunction(String name) {
return _functionsByName.get(name);
}

private Map<String, FreeRefFunction> createFunctionsMap() {
Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(108);

r(m, "ACCRINT", null);
r(m, "ACCRINTM", null);
r(m, "AMORDEGRC", null);
r(m, "AMORLINC", null);
r(m, "AVERAGEIF", null);
r(m, "AVERAGEIFS", null);
r(m, "BAHTTEXT", null);
r(m, "BESSELI", null);
r(m, "BESSELJ", null);
r(m, "BESSELK", null);
r(m, "BESSELY", null);
r(m, "BIN2DEC", null);
r(m, "BIN2HEX", null);
r(m, "BIN2OCT", null);
r(m, "COMPLEX", null);
r(m, "CONVERT", null);
r(m, "COUNTIFS", null);
r(m, "COUPDAYBS", null);
r(m, "COUPDAYS", null);
r(m, "COUPDAYSNC", null);
r(m, "COUPNCD", null);
r(m, "COUPNUM", null);
r(m, "COUPPCD", null);
r(m, "CUBEKPIMEMBER", null);
r(m, "CUBEMEMBER", null);
r(m, "CUBEMEMBERPROPERTY", null);
r(m, "CUBERANKEDMEMBER", null);
r(m, "CUBESET", null);
r(m, "CUBESETCOUNT", null);
r(m, "CUBEVALUE", null);
r(m, "CUMIPMT", null);
r(m, "CUMPRINC", null);
r(m, "DEC2BIN", null);
r(m, "DEC2HEX", null);
r(m, "DEC2OCT", null);
r(m, "DELTA", null);
r(m, "DISC", null);
r(m, "DOLLARDE", null);
r(m, "DOLLARFR", null);
r(m, "DURATION", null);
r(m, "EDATE", null);
r(m, "EFFECT", null);
r(m, "EOMONTH", null);
r(m, "ERF", null);
r(m, "ERFC", null);
r(m, "FACTDOUBLE", null);
r(m, "FVSCHEDULE", null);
r(m, "GCD", null);
r(m, "GESTEP", null);
r(m, "HEX2BIN", null);
r(m, "HEX2DEC", null);
r(m, "HEX2OCT", null);
r(m, "IFERROR", null);
r(m, "IMABS", null);
r(m, "IMAGINARY", null);
r(m, "IMARGUMENT", null);
r(m, "IMCONJUGATE", null);
r(m, "IMCOS", null);
r(m, "IMDIV", null);
r(m, "IMEXP", null);
r(m, "IMLN", null);
r(m, "IMLOG10", null);
r(m, "IMLOG2", null);
r(m, "IMPOWER", null);
r(m, "IMPRODUCT", null);
r(m, "IMREAL", null);
r(m, "IMSIN", null);
r(m, "IMSQRT", null);
r(m, "IMSUB", null);
r(m, "IMSUM", null);
r(m, "INTRATE", null);
r(m, "ISEVEN", ParityFunction.IS_EVEN);
r(m, "ISODD", ParityFunction.IS_ODD);
r(m, "JIS", null);
r(m, "LCM", null);
r(m, "MDURATION", null);
r(m, "MROUND", null);
r(m, "MULTINOMIAL", null);
r(m, "NETWORKDAYS", null);
r(m, "NOMINAL", null);
r(m, "OCT2BIN", null);
r(m, "OCT2DEC", null);
r(m, "OCT2HEX", null);
r(m, "ODDFPRICE", null);
r(m, "ODDFYIELD", null);
r(m, "ODDLPRICE", null);
r(m, "ODDLYIELD", null);
r(m, "PRICE", null);
r(m, "PRICEDISC", null);
r(m, "PRICEMAT", null);
r(m, "QUOTIENT", null);
r(m, "RANDBETWEEN", RandBetween.instance);
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;
}

private static void r(Map<String, FreeRefFunction> m, String functionName, FreeRefFunction pFunc) {
FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc;
m.put(functionName, func);
}
}

+ 9
- 0
src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java 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.EvaluationSheet;
import org.apache.poi.ss.formula.EvaluationWorkbook;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Workbook;

/**
@@ -102,6 +103,10 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
return _masterBook.getName(namePtg);
}

public EvaluationName getName(String name, int sheetIndex){
return _masterBook.getName(name, sheetIndex);
}

public EvaluationSheet getSheet(int sheetIndex) {
return getSharedSheet(getSheetName(sheetIndex));
}
@@ -130,6 +135,10 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
return _masterBook.resolveNameXText(ptg);
}

public UDFFinder getUDFFinder(){
return _masterBook.getUDFFinder();
}

private static final class OrderedSheet implements Comparable<OrderedSheet> {
private final String _sheetName;
private final int _index;

+ 1
- 1
src/java/org/apache/poi/ss/formula/function/FunctionMetadataRegistry.java View File

@@ -41,7 +41,7 @@ public final class FunctionMetadataRegistry {
private final FunctionMetadata[] _functionDataByIndex;
private final Map<String, FunctionMetadata> _functionDataByName;

private static FunctionMetadataRegistry getInstance() {
public static FunctionMetadataRegistry getInstance() {
if (_instance == null) {
_instance = FunctionMetadataReader.createRegistry();
}

+ 17
- 3
src/java/org/apache/poi/ss/formula/udf/AggregatingUDFFinder.java View File

@@ -19,17 +19,22 @@ package org.apache.poi.ss.formula.udf;

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
*
* @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) {
_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;
}

/**
* Add a new toolpack
*
* @param toolPack the UDF toolpack to add
*/
public void add(UDFFinder toolPack){
_usedToolPacks.add(toolPack);
}
}

+ 2
- 2
src/java/org/apache/poi/ss/formula/udf/DefaultUDFFinder.java View File

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

public FreeRefFunction findFunction(String name) {
return _functionsByName.get(name);
return _functionsByName.get(name.toUpperCase());
}
}

+ 9
- 0
src/java/org/apache/poi/ss/usermodel/Workbook.java View File

@@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import org.apache.poi.ss.formula.udf.UDFFinder;
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
*/
void setSheetHidden(int sheetIx, int hidden);

/**
* Register a new toolpack in this workbook.
*
* @param toopack the toolpack to register
*/
void addToolPack(UDFFinder toopack);

}

+ 56
- 0
src/ooxml/java/org/apache/poi/xssf/model/IndexedUDFFinder.java 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();
}
}

+ 19
- 10
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java View File

@@ -17,6 +17,7 @@

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.NameXPtg;
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.FormulaRenderingWorkbook;
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 java.util.HashMap;

/**
* Internal POI use only
*
@@ -100,10 +105,18 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
}

public NameXPtg getNameXPtg(String name) {
// may require to return null to make tests pass
throw new RuntimeException("Not implemented yet");
IndexedUDFFinder udfFinder = (IndexedUDFFinder)getUDFFinder();
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) {
return new XSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
}
@@ -119,14 +132,6 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
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) {
int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);
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()));
}

public UDFFinder getUDFFinder(){
return _uBook.getUDFFinder();
}

private static final class Name implements EvaluationName {

private final XSSFName _nameRecord;

+ 36
- 5
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java 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.PackagingURIHelper;
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.Sheet;
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.WorkbookUtil;
import org.apache.poi.util.*;
import org.apache.poi.xssf.model.CalculationChain;
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.poi.xssf.model.*;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
@@ -123,6 +121,12 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X

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
*/
@@ -1492,4 +1496,31 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
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);
}


}

+ 35
- 0
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFExternalFunctions.java 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");
}
}

+ 36
- 0
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFExternalFunctions.java 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");
}

}

+ 144
- 0
src/testcases/org/apache/poi/ss/formula/BaseTestExternalFunctions.java 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));

}

}

BIN
test-data/spreadsheet/atp.xls View File


BIN
test-data/spreadsheet/atp.xlsx View File


Loading…
Cancel
Save