Browse Source

improved work with user-defined functions, see Bugzilla 47809

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@815928 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_5-FINAL
Yegor Kozlov 14 years ago
parent
commit
759683dcc0

+ 1
- 0
src/documentation/content/xdocs/status.xml View File

@@ -33,6 +33,7 @@

<changes>
<release version="3.5-beta7" date="2009-??-??">
<action dev="POI-DEVELOPERS" type="add">47809 - Improved work with user-defined functions</action>
<action dev="POI-DEVELOPERS" type="fix">47581 - fixed XSSFSheet.setColumnWidth to produce XML compatible with Mac Excel 2008</action>
<action dev="POI-DEVELOPERS" type="fix">47734 - removed unnecessary svn:executable flag from files in SVN trunk</action>
<action dev="POI-DEVELOPERS" type="fix">47543 - added javadoc how to avoid Excel crash when creating too many HSSFRichTextString cells</action>

+ 22
- 10
src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java View File

@@ -22,10 +22,14 @@ import java.util.Map;

import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.hssf.record.formula.toolpack.ToolPack;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.NotImplementedException;

public final class AnalysisToolPak {
/**
* Modified 09/07/09 by Petr Udalau - systematized work of ToolPacks.
*/
public final class AnalysisToolPak implements ToolPack {

private static final class NotImplemented implements FreeRefFunction {
private final String _functionName;
@@ -38,18 +42,14 @@ public final class AnalysisToolPak {
throw new NotImplementedException(_functionName);
}
};
private Map<String, FreeRefFunction> _functionsByName = createFunctionsMap();

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

private AnalysisToolPak() {
// no instances of this class
}

public static FreeRefFunction findFunction(String name) {
public FreeRefFunction findFunction(String name) {
return _functionsByName.get(name);
}
private static Map<String, FreeRefFunction> createFunctionsMap() {
private Map<String, FreeRefFunction> createFunctionsMap() {
Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(100);

r(m, "ACCRINT", null);
@@ -153,4 +153,16 @@ public final class AnalysisToolPak {
FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc;
m.put(functionName, func);
}

public void addFunction(String name, FreeRefFunction evaluator) {
r(_functionsByName, name, evaluator);
}

public boolean containsFunction(String name) {
return _functionsByName.containsKey(name);
}

public void removeFunction(String name) {
_functionsByName.remove(name);
}
}

+ 17
- 7
src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java View File

@@ -17,8 +17,8 @@

package org.apache.poi.hssf.record.formula.eval;

import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.hssf.record.formula.toolpack.MainToolPacksHandler;
import org.apache.poi.ss.formula.EvaluationWorkbook;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.NotImplementedException;
@@ -28,6 +28,8 @@ import org.apache.poi.ss.formula.eval.NotImplementedException;
* <tt>AbstractFunctionPtg.field_2_fnc_index</tt> == 255)
*
* @author Josh Micich
*
* Modified 09/07/09 by Petr Udalau - Improved resolving of UDFs through the ToolPacks.
*/
final class UserDefinedFunction implements FreeRefFunction {

@@ -38,7 +40,7 @@ final class UserDefinedFunction implements FreeRefFunction {
}

public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
EvaluationWorkbook workbook = ec.getWorkbook();
int nIncomingArgs = args.length;
if(nIncomingArgs < 1) {
throw new RuntimeException("function name argument missing");
@@ -47,9 +49,9 @@ final class UserDefinedFunction implements FreeRefFunction {
ValueEval nameArg = args[0];
FreeRefFunction targetFunc;
if (nameArg instanceof NameEval) {
targetFunc = findInternalUserDefinedFunction((NameEval) nameArg);
targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg);
} else if (nameArg instanceof NameXEval) {
targetFunc = findExternalUserDefinedFunction(ec.getWorkbook(), (NameXEval) nameArg);
targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg);
} else {
throw new RuntimeException("First argument should be a NameEval, but got ("
+ nameArg.getClass().getName() + ")");
@@ -67,21 +69,29 @@ final class UserDefinedFunction implements FreeRefFunction {
if(false) {
System.out.println("received call to external user defined function (" + functionName + ")");
}
// currently only looking for functions from the 'Analysis TookPak' e.g. "YEARFRAC" or "ISEVEN"
// currently only looking for functions from the 'Analysis TookPak'(contained in MainToolPacksHandler) e.g. "YEARFRAC" or "ISEVEN"
// not sure how much this logic would need to change to support other or multiple add-ins.
FreeRefFunction result = AnalysisToolPak.findFunction(functionName);
FreeRefFunction result = MainToolPacksHandler.instance().findFunction(functionName);
if (result != null) {
return result;
}
throw new NotImplementedException(functionName);
}

private static FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) {
private static FreeRefFunction findInternalUserDefinedFunction(EvaluationWorkbook workbook,
NameEval functionNameEval) {

String functionName = functionNameEval.getFunctionName();
if(false) {
System.out.println("received call to internal user defined function (" + functionName + ")");
}
FreeRefFunction functionEvaluator = workbook.findUserDefinedFunction(functionName);
if (functionEvaluator == null) {
functionEvaluator = MainToolPacksHandler.instance().findFunction(functionName);
}
if (functionEvaluator != null) {
return functionEvaluator;
}
// TODO find the implementation for the user defined function

throw new NotImplementedException(functionName);

+ 51
- 0
src/java/org/apache/poi/hssf/record/formula/toolpack/DefaultToolPack.java View File

@@ -0,0 +1,51 @@
/* ====================================================================
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.record.formula.toolpack;

import java.util.HashMap;
import java.util.Map;

import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;

/**
* Default tool pack.
* If you want to add some UDF, but you don't want to create new tool pack, use this.
*
* @author PUdalau
*/
public class DefaultToolPack implements ToolPack {
private Map<String, FreeRefFunction> functionsByName = new HashMap<String, FreeRefFunction>();

public void addFunction(String name, FreeRefFunction evaluator) {
if (evaluator != null){
functionsByName.put(name, evaluator);
}
}

public boolean containsFunction(String name) {
return functionsByName.containsKey(name);
}

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

public void removeFunction(String name) {
functionsByName.remove(name);
}
}

+ 102
- 0
src/java/org/apache/poi/hssf/record/formula/toolpack/MainToolPacksHandler.java View File

@@ -0,0 +1,102 @@
/* ====================================================================
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.record.formula.toolpack;

import java.util.ArrayList;
import java.util.List;

import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;

/**
* Contains all tool packs. Processing of UDF is through this class.
*
* @author PUdalau
*/
public class MainToolPacksHandler{

private DefaultToolPack defaultToolPack;

private List<ToolPack> usedToolPacks = new ArrayList<ToolPack>();

private static MainToolPacksHandler instance;

/**
* @return Unique instance of handler.
*/
public static MainToolPacksHandler instance() {
if (instance == null) {
instance = new MainToolPacksHandler();
}
return instance;
}

/**
* @return Default tool pack(which is obligatory exists in handler).
*/
public DefaultToolPack getDefaultToolPack() {
return defaultToolPack;
}

private MainToolPacksHandler() {
defaultToolPack = new DefaultToolPack();
usedToolPacks.add(defaultToolPack);
usedToolPacks.add(new AnalysisToolPak());
}

/**
* Checks if such function exists in any registered tool pack.
* @param name Name of function.
* @return true if some tool pack contains such function.
*/
public boolean containsFunction(String name) {
for (ToolPack pack : usedToolPacks) {
if (pack.containsFunction(name)) {
return true;
}
}
return false;
}

/**
* Returns executor by specified name. Returns <code>null</code> if
* function isn't contained by any registered tool pack.
*
* @param name
* Name of function.
* @return Function executor.
*/
public FreeRefFunction findFunction(String name) {
FreeRefFunction evaluatorForFunction;
for (ToolPack pack : usedToolPacks) {
evaluatorForFunction = pack.findFunction(name);
if (evaluatorForFunction != null) {
return evaluatorForFunction;
}
}
return null;
}

/**
* Registers new tool pack in handler.
* @param pack Tool pack to add.
*/
public void addToolPack(ToolPack pack) {
usedToolPacks.add(pack);
}
}

+ 56
- 0
src/java/org/apache/poi/hssf/record/formula/toolpack/ToolPack.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.hssf.record.formula.toolpack;

import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;

/**
* Common interface for any new tool pack with executors for functions.
*
* @author PUdalau
*/
public interface ToolPack {
/**
* Returns executor by specified name. Returns <code>null</code> if tool
* pack doesn't contains such function.
*
* @param name Name of function.
* @return Function executor.
*/
FreeRefFunction findFunction(String name);

/**
* Add new function with executor.
* @param name Name of function.
* @param evaluator Function executor.
*/
void addFunction(String name, FreeRefFunction evaluator);

/**
* Returns executor by specified name if it exists.
* @param name Name of function.
*/
void removeFunction(String name);

/**
* Checks if such function exists in tool pack.
* @param name Name of function.
* @return true if tool pack contains such function.
*/
boolean containsFunction(String name);
}

+ 7
- 0
src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java View File

@@ -24,6 +24,7 @@ import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
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.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.*;
import org.apache.poi.ss.SpreadsheetVersion;

@@ -31,6 +32,8 @@ import org.apache.poi.ss.SpreadsheetVersion;
* Internal POI use only
*
* @author Josh Micich
*
* Modified 09/07/09 by Petr Udalau - added methods for searching for UDFs of this Workbook.
*/
public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {

@@ -159,4 +162,8 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL97;
}
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _uBook.getUserDefinedFunction(functionName);
}
}

+ 31
- 2
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java View File

@@ -24,9 +24,11 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.poi.POIDocument;
@@ -62,15 +64,18 @@ import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.SheetNameFormatter;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
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.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;


/**
* High level representation of a workbook. This is the first object most users
* will construct whether they are reading or writing a workbook. It is also the
@@ -82,6 +87,8 @@ import org.apache.poi.util.POILogger;
* @author Glen Stampoultzis (glens at apache.org)
* @author Shawn Laubach (slaubach at apache dot org)
*
*
* Modified 09/07/09 by Petr Udalau - added methods for work with UDFs of this Workbook.
*/
public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
@@ -160,8 +167,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm


private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class);


/** Map of user defined functions, key - function name, value - instance of FreeRefFunctions */
private Map<String, FreeRefFunction> udfFunctions;

/**
* Creates new HSSFWorkbook from scratch (start here!)
@@ -178,6 +186,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
workbook = book;
_sheets = new ArrayList( INITIAL_CAPACITY );
names = new ArrayList( INITIAL_CAPACITY );
udfFunctions = new HashMap<String, FreeRefFunction>();
}

public HSSFWorkbook(POIFSFileSystem fs) throws IOException {
@@ -269,6 +278,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm

_sheets = new ArrayList(INITIAL_CAPACITY);
names = new ArrayList(INITIAL_CAPACITY);
udfFunctions = new HashMap<String, FreeRefFunction>();

// Grab the data from the workbook stream, however
// it happens to be spelled.
@@ -1617,6 +1627,25 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
}
}
}
public FreeRefFunction getUserDefinedFunction(String functionName) {
return udfFunctions.get(functionName);
}
public void registerUserDefinedFunction(String functionName, FreeRefFunction freeRefFunction) {
Name udfDeclaration = getName(functionName);
if (udfDeclaration == null) {
udfDeclaration = createName();
}
udfDeclaration.setNameName(functionName);
udfDeclaration.setFunction(true);
udfFunctions.put(functionName, freeRefFunction);
}

public List<String> getUserDefinedFunctionNames() {
return new ArrayList<String>(udfFunctions.keySet());
}

/**
* Is the workbook protected with a password (not encrypted)?

+ 14
- 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.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;

/**
* Abstracts a workbook for the purpose of formula evaluation.<br/>
@@ -27,6 +28,8 @@ import org.apache.poi.hssf.record.formula.Ptg;
* For POI internal use only
*
* @author Josh Micich
*
* Modified 09/07/09 by Petr Udalau - added methods for searching for UDFs of this Workbook.
*/
public interface EvaluationWorkbook {
String getSheetName(int sheetIndex);
@@ -51,6 +54,17 @@ public interface EvaluationWorkbook {
String resolveNameXText(NameXPtg ptg);
Ptg[] getFormulaTokens(EvaluationCell cell);

/**
* Find and return user defined function (UDF) contained by workbook with
* specified name.
*
* @param functionName UDF name
* @return instance of FreeRefFunction or null if no UDF with the specified
* name exists.
*/
FreeRefFunction findUserDefinedFunction(String functionName);


class ExternalSheet {
private final String _workbookName;
private final String _sheetName;

+ 7
- 0
src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java View File

@@ -23,6 +23,7 @@ import java.util.Map;
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.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.EvaluationCell;
import org.apache.poi.ss.formula.EvaluationName;
import org.apache.poi.ss.formula.EvaluationSheet;
@@ -35,6 +36,8 @@ import org.apache.poi.ss.usermodel.Workbook;
* updated after a call to {@link #getOrCreateUpdatableCell(String, int, int)}.
*
* @author Josh Micich
*
* Modified 09/07/09 by Petr Udalau - added methods for searching for UDFs of this Workbook.
*/
final class ForkedEvaluationWorkbook implements EvaluationWorkbook {

@@ -141,4 +144,8 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
return _index - o._index;
}
}

public FreeRefFunction findUserDefinedFunction(String functionName) {
return _masterBook.findUserDefinedFunction(functionName);
}
}

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

@@ -21,12 +21,15 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;

/**
* High level representation of a Excel workbook. This is the first object most users
* will construct whether they are reading or writing a workbook. It is also the
* top level object for creating new sheets/etc.
*
* Modified 09/07/09 by Petr Udalau - added methods for work with UDFs of this Workbook.
*/
public interface Workbook {

@@ -464,4 +467,29 @@ public interface Workbook {
* @param hidden 0 for not hidden, 1 for hidden, 2 for very hidden
*/
void setSheetHidden(int sheetIx, int hidden);
/**
* Find and return user defined function (UDF) with specified name.
*
* @param functionName
* UDF name
* @return instance of FreeRefFunction or null if no UDF with the specified
* name exists.
*/
FreeRefFunction getUserDefinedFunction(String functionName);
/**
* Add user defined function (UDF) to workbook
*
* @param name
* @param function
*/
void registerUserDefinedFunction(String name, FreeRefFunction function);

/**
* Returns user defined functions (UDF) names
*
* @return list of UDF names
*/
List<String> getUserDefinedFunctionNames();
}

+ 7
- 0
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java View File

@@ -20,6 +20,7 @@ package org.apache.poi.xssf.usermodel;
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.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.*;
import org.apache.poi.ss.SpreadsheetVersion;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
@@ -28,6 +29,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
* Internal POI use only
*
* @author Josh Micich
*
* Modified 09/07/09 by Petr Udalau - added methods for searching for UDFs of this Workbook.
*/
public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {

@@ -177,4 +180,8 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL2007;
}

public FreeRefFunction findUserDefinedFunction(String functionName) {
return _uBook.getUserDefinedFunction(functionName);
}
}

+ 25
- 0
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java View File

@@ -32,6 +32,7 @@ import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.POIXMLProperties;
import org.apache.poi.hssf.record.formula.SheetNameFormatter;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
@@ -40,6 +41,7 @@ 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.usermodel.Name;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
@@ -63,6 +65,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
* High level representation of a SpreadsheetML workbook. This is the first object most users
* will construct whether they are reading or writing a workbook. It is also the
* top level object for creating new sheets/etc.
*
* Modified 09/07/09 by Petr Udalau - added methods for work with UDFs of this Workbook.
*/
public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<XSSFSheet> {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
@@ -129,6 +133,9 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
private List<XSSFPictureData> pictures;

private static POILogger logger = POILogFactory.getLogger(XSSFWorkbook.class);
/** Map of user defined functions, key - function name, value - instance of FreeRefFunctions */
private Map<String, FreeRefFunction> udfFunctions = new HashMap<String, FreeRefFunction>();

/**
* Create a new SpreadsheetML workbook.
@@ -1346,4 +1353,22 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
return mapInfo;
}

public FreeRefFunction getUserDefinedFunction(String functionName) {
return udfFunctions.get(functionName);
}
public void registerUserDefinedFunction(String functionName, FreeRefFunction freeRefFunction) {
Name udfDeclaration = getName(functionName);
if (udfDeclaration == null) {
udfDeclaration = createName();
}
udfDeclaration.setNameName(functionName);
udfDeclaration.setFunction(true);
udfFunctions.put(functionName, freeRefFunction);
}
public List<String> getUserDefinedFunctionNames() {
return new ArrayList<String>(udfFunctions.keySet());
}

}

+ 77
- 46
src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java View File

@@ -22,61 +22,92 @@ import java.io.IOException;
import junit.framework.TestCase;

import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.hssf.record.formula.toolpack.DefaultToolPack;
import org.apache.poi.hssf.record.formula.toolpack.MainToolPacksHandler;
import org.apache.poi.hssf.record.formula.toolpack.ToolPack;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.usermodel.Workbook;

/**
*
* @author Josh Micich
*
* Modified 09/14/09 by Petr Udalau - Test of registering UDFs in workbook and
* using ToolPacks.
*/
public final class TestExternalFunction extends TestCase {

/**
* Checks that an external function can get invoked from the formula evaluator.
* @throws IOException

*/
public void testInvoke() {
HSSFWorkbook wb;
HSSFSheet sheet;
HSSFCell cell;
if (false) {
// TODO - this code won't work until we can create user-defined functions directly with POI
wb = new HSSFWorkbook();
sheet = wb.createSheet();
wb.setSheetName(0, "Sheet1");
HSSFName hssfName = wb.createName();
hssfName.setNameName("myFunc");
} else {
// This sample spreadsheet already has a VB function called 'myFunc'
wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
sheet = wb.getSheetAt(0);
HSSFRow row = sheet.createRow(0);
cell = row.createCell(1);
}
cell.setCellFormula("myFunc()");
String actualFormula=cell.getCellFormula();
assertEquals("myFunc()", actualFormula);
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
// Check out what ExternalFunction.evaluate() does:
CellValue evalResult;
try {
evalResult = fe.evaluate(cell);
} catch (NotImplementedException e) {
assertEquals("Error evaluating cell Sheet1!B1", e.getMessage());
assertEquals("myFunc", e.getCause().getMessage());
return;
}
// TODO - make this test assert something more interesting as soon as ExternalFunction works a bit better
assertNotNull(evalResult);
}
private static class MyFunc implements FreeRefFunction {
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
if (args.length != 1 || !(args[0] instanceof StringEval)) {
return ErrorEval.VALUE_INVALID;
} else {
StringEval input = (StringEval) args[0];
return new StringEval(input.getStringValue() + "abc");
}
}
}

private static class MyFunc2 implements FreeRefFunction {
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
if (args.length != 1 || !(args[0] instanceof StringEval)) {
return ErrorEval.VALUE_INVALID;
} else {
StringEval input = (StringEval) args[0];
return new StringEval(input.getStringValue() + "abc2");
}
}
}

/**
* Creates and registers user-defined function "MyFunc()" directly with POI.
* This is VB function defined in "testNames.xls". In future there must be
* some parser of VBA scripts which will register UDFs.
*/
private void registerMyFunc(Workbook workbook) {
workbook.registerUserDefinedFunction("myFunc", new MyFunc());
}

/**
* Creates example ToolPack which contains function "MyFunc2()".
*/
private void createExampleToolPack() {
ToolPack exampleToolPack = new DefaultToolPack();
exampleToolPack.addFunction("myFunc2", new MyFunc2());
MainToolPacksHandler.instance().addToolPack(exampleToolPack);
}

/**
* Checks that an external function can get invoked from the formula
* evaluator.
*
* @throws IOException
*
*/
public void testInvoke() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
HSSFSheet sheet = wb.getSheetAt(0);

registerMyFunc(wb);
createExampleToolPack();

HSSFRow row = sheet.getRow(0);
HSSFCell myFuncCell = row.getCell(1); //=myFunc("_")

HSSFCell myFunc2Cell = row.getCell(2); //=myFunc2("_")

HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
try {
assertEquals("_abc", fe.evaluate(myFuncCell).getStringValue());
assertEquals("_abc2", fe.evaluate(myFunc2Cell).getStringValue());
} catch (Exception e) {
assertFalse(true);
}
}
}

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


Loading…
Cancel
Save