]> source.dussan.org Git - poi.git/commitdiff
allow runtime registration of functions in FormulaEvaluator
authorYegor Kozlov <yegor@apache.org>
Tue, 21 Feb 2012 09:43:01 +0000 (09:43 +0000)
committerYegor Kozlov <yegor@apache.org>
Tue, 21 Feb 2012 09:43:01 +0000 (09:43 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1291677 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/ss/formula/atp/AnalysisToolPak.java
src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
src/testcases/org/apache/poi/ss/formula/TestFunctionRegistry.java [new file with mode: 0644]

index 31402b1482a83041d5de1d259ae95063423021d5..edfd430e92e211bd0495d7fe3fba4dda41a4fd4b 100644 (file)
@@ -34,6 +34,7 @@
 
     <changes>
         <release version="3.8-beta6" date="2012-??-??">
+           <action dev="poi-developers" type="add"> allow runtime registration of functions in FormulaEvaluator</action>
            <action dev="poi-developers" type="fix">52665 - When reading from a ZipFileZipEntrySource that has already been closed, give IllegalArgumentException rather than NPE</action>
            <action dev="poi-developers" type="fix">52664 - MAPIMessage may not always have name chunks when checking for 7 bit encodings</action>
            <action dev="poi-developers" type="fix">52649 - fixed namespace issue in WordToFoConverter</action>
index ce93467ad7ebf4dc7d85e325b1e2eaee71db49ed..098ad996e91bcafd5df6b8ce32bbb80d8e45845f 100644 (file)
 
 package org.apache.poi.ss.formula.atp;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.function.FunctionMetadata;
+import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.formula.functions.FreeRefFunction;
+import org.apache.poi.ss.formula.functions.Function;
+import org.apache.poi.ss.formula.functions.NotImplementedFunction;
 import org.apache.poi.ss.formula.functions.Sumifs;
 import org.apache.poi.ss.formula.udf.UDFFinder;
 import org.apache.poi.ss.formula.OperationEvaluationContext;
@@ -175,4 +180,55 @@ public final class AnalysisToolPak implements UDFFinder {
         FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc;
         m.put(functionName, func);
     }
+
+    public static boolean isATPFunction(String name){
+        AnalysisToolPak inst = (AnalysisToolPak)instance;
+        return inst._functionsByName.containsKey(name);
+    }
+
+    /**
+     * Returns an array of function names implemented by POI.
+     *
+     * @return an array of supported functions
+     * @since 3.8 beta6
+     */
+    public static String[] getSupportedFunctionNames(){
+        AnalysisToolPak inst = (AnalysisToolPak)instance;
+        ArrayList<String> lst = new ArrayList<String>();
+        for(String name : inst._functionsByName.keySet()){
+            FreeRefFunction func = inst._functionsByName.get(name);
+            if(func != null && !(func instanceof NotImplemented)){
+                lst.add(name);
+            }
+        }
+        return lst.toArray(new String[lst.size()]);
+    }
+
+    /**
+     * Register a ATP function in runtime.
+     *
+     * @param name  the function name
+     * @param func  the functoin to register
+     * @throws IllegalArgumentException if the function is unknown or already  registered.
+     * @since 3.8 beta6
+     */
+   public static void registerFunction(String name, FreeRefFunction func){
+        AnalysisToolPak inst = (AnalysisToolPak)instance;
+        if(!isATPFunction(name)) {
+            FunctionMetadata metaData = FunctionMetadataRegistry.getFunctionByName(name);
+            if(metaData != null) {
+                throw new IllegalArgumentException(name + " is a built-in Excel function. " +
+                        "Use FunctoinEval.registerFunction(String name, Function func) instead.");
+            } else {
+                throw new IllegalArgumentException(name + " is not a function from the Excel Analysis Toolpack.");
+            }
+        }
+        FreeRefFunction f = inst.findFunction(name);
+        if(f != null && !(f instanceof NotImplemented)) {
+            throw new IllegalArgumentException("POI already implememts " + name +
+                    ". You cannot override POI's implementations of Excel functions");
+        }
+
+        inst._functionsByName.put(name, func);
+    }
 }
index b3a18c1a14346cacbe19218c5e0bdf7bf76e2a01..980d8af745516f0f464c679a6d5de490778906a7 100644 (file)
 
 package org.apache.poi.ss.formula.eval;
 
+import org.apache.poi.ss.formula.atp.AnalysisToolPak;
 import org.apache.poi.ss.formula.function.FunctionMetadata;
 import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
 import org.apache.poi.ss.formula.functions.*;
 
+import java.util.ArrayList;
+
 /**
  * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  */
@@ -255,4 +258,50 @@ public final class FunctionEval {
                }
                return result;
        }
+
+    /**
+     * Register a new function in runtime.
+     *
+     * @param name  the function name
+     * @param func  the functoin to register
+     * @throws IllegalArgumentException if the function is unknown or already  registered.
+     * @since 3.8 beta6
+     */
+    public static void registerFunction(String name, Function func){
+        FunctionMetadata metaData = FunctionMetadataRegistry.getFunctionByName(name);
+        if(metaData == null) {
+            if(AnalysisToolPak.isATPFunction(name)) {
+                throw new IllegalArgumentException(name + " is a function from the Excel Analysis Toolpack. " +
+                        "Use AnalysisToolpack.registerFunction(String name, FreeRefFunction func) instead.");
+            } else {
+                throw new IllegalArgumentException("Unknown function: " + name);
+            }
+        }
+
+        int idx = metaData.getIndex();
+        if(functions[idx] instanceof NotImplementedFunction) {
+            functions[idx] = func;
+        } else {
+            throw new IllegalArgumentException("POI already implememts " + name +
+                    ". You cannot override POI's implementations of Excel functions");
+        }
+    }
+
+    /**
+     * Returns an array of function names implemented by POI.
+     *
+     * @return an array of supported functions
+     * @since 3.8 beta6
+     */
+    public static String[] getSupportedFunctionNames(){
+        ArrayList<String>  lst = new ArrayList<String>();
+        for(int i = 0; i < functions.length; i++){
+            Function func = functions[i];
+            if(func != null && !(func instanceof NotImplementedFunction)){
+                FunctionMetadata metaData = FunctionMetadataRegistry.getFunctionByIndex(i);
+                lst.add(metaData.getName());
+            }
+        }
+        return lst.toArray(new String[lst.size()]);
+    }
 }
diff --git a/src/testcases/org/apache/poi/ss/formula/TestFunctionRegistry.java b/src/testcases/org/apache/poi/ss/formula/TestFunctionRegistry.java
new file mode 100644 (file)
index 0000000..87f07ce
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *  ====================================================================
+ *    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.hssf.usermodel.*;
+import org.apache.poi.ss.formula.atp.AnalysisToolPak;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.FunctionEval;
+import org.apache.poi.ss.formula.eval.NotImplementedException;
+import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.functions.FreeRefFunction;
+import org.apache.poi.ss.formula.functions.Function;
+import org.apache.poi.ss.usermodel.CellValue;
+
+/**
+ *
+ * @author Yegor Kozlov
+ */
+public class TestFunctionRegistry extends TestCase {
+
+       public void testRegisterInRuntime() {
+               HSSFWorkbook wb = new HSSFWorkbook();
+               HSSFSheet sheet = wb.createSheet("Sheet1");
+               HSSFRow row = sheet.createRow(0);
+        HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
+
+               HSSFCell cellA = row.createCell(0);
+               cellA.setCellFormula("FISHER(A5)");
+               CellValue cv;
+               try {
+                       cv = fe.evaluate(cellA);
+            fail("expectecd exception");
+               } catch (NotImplementedException e) {
+                       ;
+               }
+
+        FunctionEval.registerFunction("FISHER", new Function() {
+            public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
+                return ErrorEval.NA;
+            }
+        });
+
+        cv = fe.evaluate(cellA);
+        assertEquals(ErrorEval.NA.getErrorCode(), cv.getErrorValue());
+
+        HSSFCell cellB = row.createCell(1);
+        cellB.setCellFormula("CUBEMEMBERPROPERTY(A5)");
+        try {
+            cv = fe.evaluate(cellB);
+            fail("expectecd exception");
+        } catch (NotImplementedException e) {
+            ;
+        }
+
+        AnalysisToolPak.registerFunction("CUBEMEMBERPROPERTY", new FreeRefFunction() {
+            public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
+                return ErrorEval.NUM_ERROR;
+            }
+        });
+
+        cv = fe.evaluate(cellB);
+        assertEquals(ErrorEval.NUM_ERROR.getErrorCode(), cv.getErrorValue());
+       }
+
+    public void testExceptions() {
+        Function func = new Function() {
+            public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
+                return ErrorEval.NA;
+            }
+        };
+        try {
+            FunctionEval.registerFunction("SUM", func);
+            fail("expectecd exception");
+        } catch (IllegalArgumentException e){
+            assertEquals("POI already implememts SUM" +
+                    ". You cannot override POI's implementations of Excel functions", e.getMessage());
+        }
+        try {
+            FunctionEval.registerFunction("SUMXXX", func);
+            fail("expectecd exception");
+        } catch (IllegalArgumentException e){
+            assertEquals("Unknown function: SUMXXX", e.getMessage());
+        }
+        try {
+            FunctionEval.registerFunction("ISODD", func);
+            fail("expectecd exception");
+        } catch (IllegalArgumentException e){
+            assertEquals("ISODD is a function from the Excel Analysis Toolpack. " +
+                    "Use AnalysisToolpack.registerFunction(String name, FreeRefFunction func) instead.", e.getMessage());
+        }
+
+        FreeRefFunction atpFunc = new FreeRefFunction() {
+            public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
+                return ErrorEval.NUM_ERROR;
+            }
+        };
+        try {
+            AnalysisToolPak.registerFunction("ISODD", atpFunc);
+            fail("expectecd exception");
+        } catch (IllegalArgumentException e){
+            assertEquals("POI already implememts ISODD" +
+                    ". You cannot override POI's implementations of Excel functions", e.getMessage());
+        }
+        try {
+            AnalysisToolPak.registerFunction("ISODDXXX", atpFunc);
+            fail("expectecd exception");
+        } catch (IllegalArgumentException e){
+            assertEquals("ISODDXXX is not a function from the Excel Analysis Toolpack.", e.getMessage());
+        }
+        try {
+            AnalysisToolPak.registerFunction("SUM", atpFunc);
+            fail("expectecd exception");
+        } catch (IllegalArgumentException e){
+            assertEquals("SUM is a built-in Excel function. " +
+                    "Use FunctoinEval.registerFunction(String name, Function func) instead.", e.getMessage());
+        }
+    }
+}