2012-04-24 470 views
2

我将Apache POIhow to register custom functions网站上的教程阅读到FormulaEvaluator中,我想用它来定义POI不提供支持的函数MINVERSE。所以,首先我创建了一个定义MINVERSE的类(仅用于测试目的,我定义MINVERSE总是返回值10)。因此,这里是MINVERSE.java:Apache POI - 如何注册一个函数

package simpleboxapi; 

import org.apache.poi.ss.formula.OperationEvaluationContext; 
import org.apache.poi.ss.formula.eval.NumberEval; 
import org.apache.poi.ss.formula.eval.ValueEval; 
import org.apache.poi.ss.formula.functions.FreeRefFunction; 

public class MINVERSE implements FreeRefFunction{ 

    @Override 
    public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { 
     return new NumberEval(10); 
    } 
} 

后来我试过的东西真的很简单:我创建了下面的Excel表:

​​

A1是一个给定的常量和A2是A2 = MINVERSE(A1 )

这里是我的主类代码:

package simpleboxapi; 

import java.io.*; 
import org.apache.poi.hssf.util.CellReference; 
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 
import org.apache.poi.ss.formula.functions.FreeRefFunction; 
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder; 
import org.apache.poi.ss.formula.udf.DefaultUDFFinder; 
import org.apache.poi.ss.formula.udf.UDFFinder; 
import org.apache.poi.ss.usermodel.*; 


public class SimpleBoxAPI { 

    static String fileName = "workbook.xls"; 
    static Workbook wb; 

    private static double updateInputVal(String cell, double val) throws IOException, InvalidFormatException{ 
     InputStream inp = new FileInputStream(fileName); 
     wb = WorkbookFactory.create(inp); 
     CellReference crInput = new CellReference(cell); 
     Sheet sheet = wb.getSheetAt(0); 
     Row rowInput = sheet.getRow(crInput.getRow()); 
     Cell cellInput = rowInput.getCell(crInput.getCol()); 
     cellInput.setCellValue(val); 
     FileOutputStream fileOut = new FileOutputStream(fileName); 
     wb.write(fileOut); 
     fileOut.close(); 
     double cellContents = cellInput.getNumericCellValue(); 
     inp.close(); 
     return cellContents; 
    } 


    private static void registerMINVERSE(){ 
     String[] functionNames = {"MINVERSE"}; 
     FreeRefFunction[] functionImpls = {new MINVERSE()}; 
     UDFFinder udfs = new DefaultUDFFinder(functionNames, functionImpls); 
     UDFFinder udfToolpack = new AggregatingUDFFinder(udfs); 
     wb.addToolPack(udfToolpack); 
    } 


    public static void main(String[] args) throws Exception { 

     double updatedValue = updateInputVal("A1",55); 
     System.out.println(updatedValue); 
     registerMINVERSE(); 

     FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); 
     CellReference cr = new CellReference("A2"); 
     Sheet sheet = wb.getSheetAt(0); 
     Row row = sheet.getRow(cr.getRow()); 
     Cell cell = row.getCell(cr.getCol()); 
     System.out.println(evaluator.evaluate(cell).getNumberValue()); 
    } 
} 

然而,每当我尝试执行它时,我得到以下错误:

org.apache.poi.ss.formula.eval.NotImplementedException: Error evaluating cell 'new sheet'!A2 
    at org.apache.poi.ss.formula.WorkbookEvaluator.addExceptionInfo(WorkbookEvaluator.java:356) 
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:297) 
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:229) 
    at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateFormulaCellValue(HSSFFormulaEvaluator.java:354) 
    at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluate(HSSFFormulaEvaluator.java:185) 
    at simpleboxapi.SimpleBoxAPI.main(SimpleBoxAPI.java:56) 
Caused by: org.apache.poi.ss.formula.eval.NotImplementedException: MINVERSE 
    at org.apache.poi.ss.formula.functions.NotImplementedFunction.evaluate(NotImplementedFunction.java:42) 
    at org.apache.poi.ss.formula.OperationEvaluatorFactory.evaluate(OperationEvaluatorFactory.java:132) 
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateFormula(WorkbookEvaluator.java:491) 
    at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:287) 
    ... 4 more 

有什么建议吗?提前感谢!

+0

是MINVERSE你的自定义功能,抑或是简单地从当前的设定被Apache POI实现那些缺少一个标准的Excel函数? – Gagravarr 2012-04-24 15:07:20

+0

@Gagravarr MINVERSE是Excel的标准功能。它返回矩阵的逆。我可以在Java中实现它,并将进行计算的类注册到评估程序,但是由于某种原因,我不能解释,因为我在我的问题中解释了这个问题。 – 2012-04-24 15:20:14

回答

0

我不是为什么,但你必须把VBA代码与函数定义(甚至可以是空的)放在工作表中 - 将函数放在模块中。

Function MINVERSE(principal As Double) 
End Function 
+0

你的意思是写一些VBA? – 2012-04-24 14:45:36

3

您一直在看的自定义函数教程只适用于真正的自定义函数。它不会让你覆盖没有POI实现的内置Excel函数

如果你看看org/apache/poi/ss/formula/function/functionMetadata.txt,你会看到以文件格式定义的内置Excel函数列表。该列表中的任何内容都不能被重写为自定义函数,因为它们以文件格式以不同方式存储。 (当然,对于.xls文件,.xlsx有点不同)。在查看该文件时,请记下您的函数的ID。

如果你的公式函数是一个内置函数,那么你应该看看FunctionEval。您可以使用getNotSupportedFunctionNames()或者只是查看代码以查看函数是否已实现。 (该阵列由功能ID,你从functionMetadata.txt了索引)

如果你的功能没有实现,你需要抓住POI源代码,并:

  • 中添加你的函数实现的地方
  • 列表用正确的ID在FunctionEval功能
  • 测试(可以使用POI公式测试帮助)
  • 提交它作为一个补丁!见POI contribution guidelines的细节

不久你的补丁被提交后,再POI将包括丢失的功能,和社区将有助于保持它,你就赢了前进:)

+0

嗨加格拉瓦尔。谢谢你的提示。这就是我最初想的,但我发现如何以简单的方式做到这一点。查看我的答案以获取详细信息。 – 2012-04-25 08:00:31

+0

我仍然建议我的方式,因为它会让您在POI中进行更改,以便其他人也可以从中受益! (此外,您不必继续维护代码) – Gagravarr 2012-04-25 10:03:17

1

This tutorial是有关如何覆盖自定义函数使用Java类在Excel端定义。为了覆盖POI不提供支持的现有Excel函数,您需要将其注册到FunctionEval。这很简单:

FunctionEval.registerFunction("MINVERSE", new Minverse()); 

(但由于缺乏适当的文档而变得困难)。 Minverse类应该实现接口org.apache.poi.ss.formula.functions.Function或扩展相同包的某个抽象类。

Function定义了方法:

public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) 

哪一个需要重写,以提供期望的功能。我不清楚如何处理接受数据领域的功能,并返回区域(而不是向量/数组)。我将开始一个新的问题......