2

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

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 表:

来自 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

有什么建议么?提前非常感谢!

4

3 回答 3

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 贡献指南

提交补丁后不久,POI 将包含您缺少的功能,社区将帮助维护它,以便您继续前进 :)

于 2012-04-24T21:35:48.327 回答
2

本教程是关于如何使用 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)

需要覆盖哪个以提供所需的功能。我还不清楚如何处理允许数据区域并返回区域(不是向量/数组)的函数。我将开始一个新的问题...

于 2012-04-25T08:10:27.507 回答
0

我不知道为什么,但是您必须将带有函数定义(甚至可以为空)的 VBA 代码放入工作表中 - 将函数放入模块中。

Function MINVERSE(principal As Double)
End Function
于 2012-04-24T14:16:22.567 回答