From cae73922a65ba839a084b6136176e84f2a0be524 Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Thu, 17 Sep 2009 00:00:57 +0000 Subject: [PATCH] Improvements to patch 47809 (support for UDFs) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@816016 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hssf/record/NameRecord.java | 32 ++- .../record/formula/atp/AnalysisToolPak.java | 32 ++- .../formula/eval/UserDefinedFunction.java | 51 +--- .../toolpack/MainToolPacksHandler.java | 102 -------- .../formula/udf/AggregatingUDFFinder.java | 52 ++++ .../DefaultUDFFinder.java} | 42 ++-- .../ToolPack.java => udf/UDFFinder.java} | 36 +-- .../usermodel/HSSFEvaluationWorkbook.java | 237 +++++++++--------- .../hssf/usermodel/HSSFFormulaEvaluator.java | 21 +- .../poi/hssf/usermodel/HSSFWorkbook.java | 32 --- .../poi/ss/formula/EvaluationWorkbook.java | 14 -- .../formula/OperationEvaluationContext.java | 5 + .../poi/ss/formula/WorkbookEvaluator.java | 16 +- .../eval/forked/ForkedEvaluationWorkbook.java | 6 - .../formula/eval/forked/ForkedEvaluator.java | 16 +- .../org/apache/poi/ss/usermodel/Name.java | 8 +- .../org/apache/poi/ss/usermodel/Workbook.java | 32 +-- .../usermodel/XSSFEvaluationWorkbook.java | 7 - .../xssf/usermodel/XSSFFormulaEvaluator.java | 23 +- .../poi/xssf/usermodel/XSSFWorkbook.java | 26 -- .../formula/eval/TestExternalFunction.java | 120 ++++----- .../poi/ss/formula/TestWorkbookEvaluator.java | 2 +- .../formula/WorkbookEvaluatorTestHelper.java | 6 +- 23 files changed, 367 insertions(+), 551 deletions(-) delete mode 100755 src/java/org/apache/poi/hssf/record/formula/toolpack/MainToolPacksHandler.java create mode 100755 src/java/org/apache/poi/hssf/record/formula/udf/AggregatingUDFFinder.java rename src/java/org/apache/poi/hssf/record/formula/{toolpack/DefaultToolPack.java => udf/DefaultUDFFinder.java} (56%) rename src/java/org/apache/poi/hssf/record/formula/{toolpack/ToolPack.java => udf/UDFFinder.java} (63%) diff --git a/src/java/org/apache/poi/hssf/record/NameRecord.java b/src/java/org/apache/poi/hssf/record/NameRecord.java index 6b38e36d4..9084f1236 100644 --- a/src/java/org/apache/poi/hssf/record/NameRecord.java +++ b/src/java/org/apache/poi/hssf/record/NameRecord.java @@ -33,9 +33,7 @@ import org.apache.poi.util.StringUtil; * @author Libin Roman (Vista Portal LDT. Developer) * @author Sergei Kozello (sergeikozello at mail.ru) * @author Glen Stampoultzis (glens at apache.org) - * @version 1.0-pre - * - * Modified 8/31/09 by Petr Udalau - added method setFunction(boolean) + * @author Petr Udalau - added method setFunction(boolean) */ public final class NameRecord extends StandardRecord { public final static short sid = 0x0018; @@ -135,7 +133,7 @@ public final class NameRecord extends StandardRecord { /** * For named ranges, and built-in names - * @return the 1-based sheet number. + * @return the 1-based sheet number. */ public int getSheetNumber() { @@ -239,13 +237,13 @@ public final class NameRecord extends StandardRecord { public boolean isFunctionName() { return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0; } - - /** - * Indicates that the defined name refers to a user-defined function. - * This attribute is used when there is an add-in or other code project associated with the file. - * - * @param value true indicates the name refers to a function. - */ + + /** + * Indicates that the defined name refers to a user-defined function. + * This attribute is used when there is an add-in or other code project associated with the file. + * + * @param value true indicates the name refers to a function. + */ public void setFunction(boolean function){ if (function) { field_1_option_flag |= Option.OPT_FUNCTION_NAME; @@ -351,7 +349,7 @@ public final class NameRecord extends StandardRecord { int field_8_length_description_text = field_15_description_text.length(); int field_9_length_help_topic_text = field_16_help_topic_text.length(); int field_10_length_status_bar_text = field_17_status_bar_text.length(); - + // size defined below out.writeShort(getOptionFlag()); out.writeByte(getKeyboardShortcut()); @@ -379,7 +377,7 @@ public final class NameRecord extends StandardRecord { } field_13_name_definition.serializeTokens(out); field_13_name_definition.serializeArrayConstantData(out); - + StringUtil.putCompressedUnicode( getCustomMenuText(), out); StringUtil.putCompressedUnicode( getDescriptionText(), out); StringUtil.putCompressedUnicode( getHelpTopicText(), out); @@ -388,14 +386,14 @@ public final class NameRecord extends StandardRecord { private int getNameRawSize() { if (isBuiltInName()) { return 1; - } + } int nChars = field_12_name_text.length(); if(field_11_nameIsMultibyte) { return 2 * nChars; - } + } return nChars; } - + protected int getDataSize() { return 13 // 3 shorts + 7 bytes + getNameRawSize() @@ -456,7 +454,7 @@ public final class NameRecord extends StandardRecord { } } - int nBytesAvailable = in.available() - (f7_customMenuLen + int nBytesAvailable = in.available() - (f7_customMenuLen + f8_descriptionTextLen + f9_helpTopicTextLen + f10_statusBarTextLen); field_13_name_definition = Formula.read(field_4_length_name_definition, in, nBytesAvailable); diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java b/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java index acfb30624..61f108b67 100644 --- a/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java +++ b/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java @@ -22,14 +22,17 @@ 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.hssf.record.formula.udf.UDFFinder; import org.apache.poi.ss.formula.OperationEvaluationContext; import org.apache.poi.ss.formula.eval.NotImplementedException; /** - * Modified 09/07/09 by Petr Udalau - systematized work of ToolPacks. + * @author Josh Micich + * @author Petr Udalau - systematized work of add-in libraries and user defined functions. */ -public final class AnalysisToolPak implements ToolPack { +public final class AnalysisToolPak implements UDFFinder { + + public static final UDFFinder instance = new AnalysisToolPak(); private static final class NotImplemented implements FreeRefFunction { private final String _functionName; @@ -42,13 +45,18 @@ public final class AnalysisToolPak implements ToolPack { throw new NotImplementedException(_functionName); } }; - - private Map _functionsByName = createFunctionsMap(); + + private final Map _functionsByName = createFunctionsMap(); + + + private AnalysisToolPak() { + // enforce singleton + } public FreeRefFunction findFunction(String name) { return _functionsByName.get(name); } - + private Map createFunctionsMap() { Map m = new HashMap(100); @@ -153,16 +161,4 @@ public final class AnalysisToolPak implements ToolPack { 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); - } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java b/src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java index bf390141e..223f13378 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java @@ -18,8 +18,6 @@ package org.apache.poi.hssf.record.formula.eval; 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,8 +26,7 @@ import org.apache.poi.ss.formula.eval.NotImplementedException; * AbstractFunctionPtg.field_2_fnc_index == 255) * * @author Josh Micich - * - * Modified 09/07/09 by Petr Udalau - Improved resolving of UDFs through the ToolPacks. + * @author Petr Udalau - Improved resolving of UDFs through the ToolPacks. */ final class UserDefinedFunction implements FreeRefFunction { @@ -40,60 +37,28 @@ 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"); } ValueEval nameArg = args[0]; - FreeRefFunction targetFunc; + String functionName; if (nameArg instanceof NameEval) { - targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg); + functionName = ((NameEval) nameArg).getFunctionName(); } else if (nameArg instanceof NameXEval) { - targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg); + functionName = ec.getWorkbook().resolveNameXText(((NameXEval) nameArg).getPtg()); } else { throw new RuntimeException("First argument should be a NameEval, but got (" + nameArg.getClass().getName() + ")"); } + FreeRefFunction targetFunc = ec.findUserDefinedFunction(functionName); + if (targetFunc == null) { + throw new NotImplementedException(functionName); + } int nOutGoingArgs = nIncomingArgs -1; ValueEval[] outGoingArgs = new ValueEval[nOutGoingArgs]; System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs); return targetFunc.evaluate(outGoingArgs, ec); } - - private static FreeRefFunction findExternalUserDefinedFunction(EvaluationWorkbook workbook, - NameXEval n) { - String functionName = workbook.resolveNameXText(n.getPtg()); - - if(false) { - System.out.println("received call to external user defined function (" + functionName + ")"); - } - // 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 = MainToolPacksHandler.instance().findFunction(functionName); - if (result != null) { - return result; - } - throw new NotImplementedException(functionName); - } - - 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); - } } diff --git a/src/java/org/apache/poi/hssf/record/formula/toolpack/MainToolPacksHandler.java b/src/java/org/apache/poi/hssf/record/formula/toolpack/MainToolPacksHandler.java deleted file mode 100755 index 9bb0c0788..000000000 --- a/src/java/org/apache/poi/hssf/record/formula/toolpack/MainToolPacksHandler.java +++ /dev/null @@ -1,102 +0,0 @@ -/* ==================================================================== - 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 usedToolPacks = new ArrayList(); - - 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 null 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); - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/udf/AggregatingUDFFinder.java b/src/java/org/apache/poi/hssf/record/formula/udf/AggregatingUDFFinder.java new file mode 100755 index 000000000..2dedf7b9d --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/udf/AggregatingUDFFinder.java @@ -0,0 +1,52 @@ +/* ==================================================================== + 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.udf; + +import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; + +/** + * Collects add-in libraries and VB macro functions together into one UDF finder + * + * @author PUdalau + */ +public final class AggregatingUDFFinder implements UDFFinder { + + private final UDFFinder[] _usedToolPacks; + + public AggregatingUDFFinder(UDFFinder ... usedToolPacks) { + _usedToolPacks = usedToolPacks.clone(); + } + + /** + * Returns executor by specified name. Returns null if + * function isn't contained by any registered tool pack. + * + * @param name Name of function. + * @return Function executor. null if not found + */ + public FreeRefFunction findFunction(String name) { + FreeRefFunction evaluatorForFunction; + for (UDFFinder pack : _usedToolPacks) { + evaluatorForFunction = pack.findFunction(name); + if (evaluatorForFunction != null) { + return evaluatorForFunction; + } + } + return null; + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/toolpack/DefaultToolPack.java b/src/java/org/apache/poi/hssf/record/formula/udf/DefaultUDFFinder.java similarity index 56% rename from src/java/org/apache/poi/hssf/record/formula/toolpack/DefaultToolPack.java rename to src/java/org/apache/poi/hssf/record/formula/udf/DefaultUDFFinder.java index 39d2c47ca..f87027aeb 100755 --- a/src/java/org/apache/poi/hssf/record/formula/toolpack/DefaultToolPack.java +++ b/src/java/org/apache/poi/hssf/record/formula/udf/DefaultUDFFinder.java @@ -15,7 +15,7 @@ limitations under the License. ==================================================================== */ -package org.apache.poi.hssf.record.formula.toolpack; +package org.apache.poi.hssf.record.formula.udf; import java.util.HashMap; import java.util.Map; @@ -23,29 +23,27 @@ 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. - * + * Default UDF finder - for adding your own user defined functions. + * * @author PUdalau */ -public class DefaultToolPack implements ToolPack { - private Map functionsByName = new HashMap(); +public final class DefaultUDFFinder implements UDFFinder { + private final Map _functionsByName; - public void addFunction(String name, FreeRefFunction evaluator) { - if (evaluator != null){ - functionsByName.put(name, evaluator); - } - } + public DefaultUDFFinder(String[] functionNames, FreeRefFunction[] functionImpls) { + int nFuncs = functionNames.length; + if (functionImpls.length != nFuncs) { + throw new IllegalArgumentException( + "Mismatch in number of function names and implementations"); + } + HashMap m = new HashMap(nFuncs * 3 / 2); + for (int i = 0; i < functionImpls.length; i++) { + m.put(functionNames[i], functionImpls[i]); + } + _functionsByName = m; + } - 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); - } + public FreeRefFunction findFunction(String name) { + return _functionsByName.get(name); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/toolpack/ToolPack.java b/src/java/org/apache/poi/hssf/record/formula/udf/UDFFinder.java similarity index 63% rename from src/java/org/apache/poi/hssf/record/formula/toolpack/ToolPack.java rename to src/java/org/apache/poi/hssf/record/formula/udf/UDFFinder.java index d2e58da9e..ba2882664 100755 --- a/src/java/org/apache/poi/hssf/record/formula/toolpack/ToolPack.java +++ b/src/java/org/apache/poi/hssf/record/formula/udf/UDFFinder.java @@ -15,42 +15,24 @@ limitations under the License. ==================================================================== */ -package org.apache.poi.hssf.record.formula.toolpack; +package org.apache.poi.hssf.record.formula.udf; +import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; /** - * Common interface for any new tool pack with executors for functions. - * + * Common interface for "Add-in" libraries and user defined function libraries. + * * @author PUdalau */ -public interface ToolPack { +public interface UDFFinder { + public static final UDFFinder DEFAULT = new AggregatingUDFFinder(AnalysisToolPak.instance); + /** - * Returns executor by specified name. Returns null if tool - * pack doesn't contains such function. - * + * Returns executor by specified name. Returns null if the function name is unknown. + * * @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); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java index b0f18bcda..0184d1639 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java @@ -6,7 +6,7 @@ (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 + 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, @@ -24,146 +24,145 @@ 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; +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.FormulaParsingWorkbook; +import org.apache.poi.ss.formula.FormulaRenderingWorkbook; +import org.apache.poi.ss.formula.FormulaType; /** * 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 { - private final HSSFWorkbook _uBook; - private final Workbook _iBook; + private final HSSFWorkbook _uBook; + private final Workbook _iBook; - public static HSSFEvaluationWorkbook create(HSSFWorkbook book) { - if (book == null) { - return null; - } - return new HSSFEvaluationWorkbook(book); - } + public static HSSFEvaluationWorkbook create(HSSFWorkbook book) { + if (book == null) { + return null; + } + return new HSSFEvaluationWorkbook(book); + } - private HSSFEvaluationWorkbook(HSSFWorkbook book) { - _uBook = book; - _iBook = book.getWorkbook(); - } + private HSSFEvaluationWorkbook(HSSFWorkbook book) { + _uBook = book; + _iBook = book.getWorkbook(); + } - public int getExternalSheetIndex(String sheetName) { - int sheetIndex = _uBook.getSheetIndex(sheetName); - return _iBook.checkExternSheet(sheetIndex); - } - public int getExternalSheetIndex(String workbookName, String sheetName) { - return _iBook.getExternalSheetIndex(workbookName, sheetName); - } + public int getExternalSheetIndex(String sheetName) { + int sheetIndex = _uBook.getSheetIndex(sheetName); + return _iBook.checkExternSheet(sheetIndex); + } + public int getExternalSheetIndex(String workbookName, String sheetName) { + return _iBook.getExternalSheetIndex(workbookName, sheetName); + } - public NameXPtg getNameXPtg(String name) { - return _iBook.getNameXPtg(name); - } + public NameXPtg getNameXPtg(String name) { + return _iBook.getNameXPtg(name); + } - /** - * Lookup a named range by its name. - * - * @param name the name to search - * @param sheetIndex the 0-based index of the sheet this formula belongs to. - * The sheet index is required to resolve sheet-level names. -1 means workbook-global names - */ - public EvaluationName getName(String name, int sheetIndex) { - for(int i=0; i < _iBook.getNumNames(); i++) { - NameRecord nr = _iBook.getNameRecord(i); - if (nr.getSheetNumber() == sheetIndex+1 && name.equalsIgnoreCase(nr.getNameText())) { - return new Name(nr, i); - } - } - return sheetIndex == -1 ? null : getName(name, -1); - } + /** + * Lookup a named range by its name. + * + * @param name the name to search + * @param sheetIndex the 0-based index of the sheet this formula belongs to. + * The sheet index is required to resolve sheet-level names. -1 means workbook-global names + */ + public EvaluationName getName(String name, int sheetIndex) { + for(int i=0; i < _iBook.getNumNames(); i++) { + NameRecord nr = _iBook.getNameRecord(i); + if (nr.getSheetNumber() == sheetIndex+1 && name.equalsIgnoreCase(nr.getNameText())) { + return new Name(nr, i); + } + } + return sheetIndex == -1 ? null : getName(name, -1); + } - public int getSheetIndex(EvaluationSheet evalSheet) { - HSSFSheet sheet = ((HSSFEvaluationSheet)evalSheet).getHSSFSheet(); - return _uBook.getSheetIndex(sheet); - } - public int getSheetIndex(String sheetName) { - return _uBook.getSheetIndex(sheetName); - } + public int getSheetIndex(EvaluationSheet evalSheet) { + HSSFSheet sheet = ((HSSFEvaluationSheet)evalSheet).getHSSFSheet(); + return _uBook.getSheetIndex(sheet); + } + public int getSheetIndex(String sheetName) { + return _uBook.getSheetIndex(sheetName); + } - public String getSheetName(int sheetIndex) { - return _uBook.getSheetName(sheetIndex); - } + public String getSheetName(int sheetIndex) { + return _uBook.getSheetName(sheetIndex); + } - public EvaluationSheet getSheet(int sheetIndex) { - return new HSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex)); - } - public int convertFromExternSheetIndex(int externSheetIndex) { - return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex); - } + public EvaluationSheet getSheet(int sheetIndex) { + return new HSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex)); + } + public int convertFromExternSheetIndex(int externSheetIndex) { + return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex); + } - public ExternalSheet getExternalSheet(int externSheetIndex) { - return _iBook.getExternalSheet(externSheetIndex); - } + public ExternalSheet getExternalSheet(int externSheetIndex) { + return _iBook.getExternalSheet(externSheetIndex); + } - public String resolveNameXText(NameXPtg n) { - return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex()); - } + public String resolveNameXText(NameXPtg n) { + return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex()); + } - public String getSheetNameByExternSheet(int externSheetIndex) { - return _iBook.findSheetNameFromExternSheet(externSheetIndex); - } - public String getNameText(NamePtg namePtg) { - return _iBook.getNameRecord(namePtg.getIndex()).getNameText(); - } - public EvaluationName getName(NamePtg namePtg) { - int ix = namePtg.getIndex(); - return new Name(_iBook.getNameRecord(ix), ix); - } - public Ptg[] getFormulaTokens(EvaluationCell evalCell) { - HSSFCell cell = ((HSSFEvaluationCell)evalCell).getHSSFCell(); - if (false) { - // re-parsing the formula text also works, but is a waste of time - // It is useful from time to time to run all unit tests with this code - // to make sure that all formulas POI can evaluate can also be parsed. - return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet())); - } - FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord(); - return fra.getFormulaTokens(); - } + public String getSheetNameByExternSheet(int externSheetIndex) { + return _iBook.findSheetNameFromExternSheet(externSheetIndex); + } + public String getNameText(NamePtg namePtg) { + return _iBook.getNameRecord(namePtg.getIndex()).getNameText(); + } + public EvaluationName getName(NamePtg namePtg) { + int ix = namePtg.getIndex(); + return new Name(_iBook.getNameRecord(ix), ix); + } + public Ptg[] getFormulaTokens(EvaluationCell evalCell) { + HSSFCell cell = ((HSSFEvaluationCell)evalCell).getHSSFCell(); + if (false) { + // re-parsing the formula text also works, but is a waste of time + // It is useful from time to time to run all unit tests with this code + // to make sure that all formulas POI can evaluate can also be parsed. + return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet())); + } + FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord(); + return fra.getFormulaTokens(); + } - private static final class Name implements EvaluationName { + private static final class Name implements EvaluationName { - private final NameRecord _nameRecord; - private final int _index; + private final NameRecord _nameRecord; + private final int _index; - public Name(NameRecord nameRecord, int index) { - _nameRecord = nameRecord; - _index = index; - } - public Ptg[] getNameDefinition() { - return _nameRecord.getNameDefinition(); - } - public String getNameText() { - return _nameRecord.getNameText(); - } - public boolean hasFormula() { - return _nameRecord.hasFormula(); - } - public boolean isFunctionName() { - return _nameRecord.isFunctionName(); - } - public boolean isRange() { - return _nameRecord.hasFormula(); // TODO - is this right? - } - public NamePtg createPtg() { - return new NamePtg(_index); - } - } + public Name(NameRecord nameRecord, int index) { + _nameRecord = nameRecord; + _index = index; + } + public Ptg[] getNameDefinition() { + return _nameRecord.getNameDefinition(); + } + public String getNameText() { + return _nameRecord.getNameText(); + } + public boolean hasFormula() { + return _nameRecord.hasFormula(); + } + public boolean isFunctionName() { + return _nameRecord.isFunctionName(); + } + public boolean isRange() { + return _nameRecord.hasFormula(); // TODO - is this right? + } + public NamePtg createPtg() { + return new NamePtg(_index); + } + } - public SpreadsheetVersion getSpreadsheetVersion(){ - return SpreadsheetVersion.EXCEL97; - } - - public FreeRefFunction findUserDefinedFunction(String functionName) { - return _uBook.getUserDefinedFunction(functionName); - } + public SpreadsheetVersion getSpreadsheetVersion(){ + return SpreadsheetVersion.EXCEL97; + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java index a49fb9c68..16b5a5a0d 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java @@ -24,6 +24,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.record.formula.udf.UDFFinder; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment; import org.apache.poi.ss.formula.IStabilityClassifier; import org.apache.poi.ss.formula.WorkbookEvaluator; @@ -64,9 +65,27 @@ public class HSSFFormulaEvaluator implements FormulaEvaluator { * evaluation begins. */ public HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) { - _bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook), stabilityClassifier); + this(workbook, stabilityClassifier, null); } + /** + * @param udfFinder pass null for default (AnalysisToolPak only) + */ + private HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { + _bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook), stabilityClassifier, udfFinder); + } + + /** + * @param stabilityClassifier used to optimise caching performance. Pass null + * for the (conservative) assumption that any cell may have its definition changed after + * evaluation begins. + * @param udfFinder pass null for default (AnalysisToolPak only) + */ + public static HSSFFormulaEvaluator create(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { + return new HSSFFormulaEvaluator(workbook, stabilityClassifier, udfFinder); + } + + /** * Coordinates several formula evaluators together so that formulas that involve external * references can be evaluated. diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index b9a732bc3..cb772c9b8 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -24,11 +24,9 @@ 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; @@ -64,12 +62,10 @@ 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; @@ -86,9 +82,6 @@ import org.apache.poi.util.POILogger; * @author Andrew C. Oliver (acoliver at apache dot org) * @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(","); @@ -167,9 +160,6 @@ 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 udfFunctions; /** * Creates new HSSFWorkbook from scratch (start here!) @@ -186,7 +176,6 @@ 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(); } public HSSFWorkbook(POIFSFileSystem fs) throws IOException { @@ -278,7 +267,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm _sheets = new ArrayList(INITIAL_CAPACITY); names = new ArrayList(INITIAL_CAPACITY); - udfFunctions = new HashMap(); // Grab the data from the workbook stream, however // it happens to be spelled. @@ -1627,25 +1615,6 @@ 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 getUserDefinedFunctionNames() { - return new ArrayList(udfFunctions.keySet()); - } /** * Is the workbook protected with a password (not encrypted)? @@ -1729,5 +1698,4 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm public NameXPtg getNameXPtg(String name) { return workbook.getNameXPtg(name); } - } diff --git a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java index 0d65af92d..a3b232571 100644 --- a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java @@ -20,7 +20,6 @@ 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.
@@ -28,8 +27,6 @@ import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; * 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); @@ -54,17 +51,6 @@ 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; diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java b/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java index 2ec5f5791..e1a28a6fe 100644 --- a/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java +++ b/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java @@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet; @@ -256,4 +257,8 @@ public final class OperationEvaluationContext { } return CellReference.classifyCellReference(str, ssVersion); } + + public FreeRefFunction findUserDefinedFunction(String functionName) { + return _bookEvaluator.findUserDefinedFunction(functionName); + } } diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index 806c002bb..ed8abb7df 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -59,6 +59,8 @@ import org.apache.poi.hssf.record.formula.eval.OperationEval; import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.StringEval; 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.udf.UDFFinder; import org.apache.poi.hssf.util.CellReference; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; import org.apache.poi.ss.formula.eval.NotImplementedException; @@ -87,12 +89,16 @@ public final class WorkbookEvaluator { private final Map _sheetIndexesByName; private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment; private final IStabilityClassifier _stabilityClassifier; + private final UDFFinder _udfFinder; - public WorkbookEvaluator(EvaluationWorkbook workbook, IStabilityClassifier stabilityClassifier) { - this (workbook, null, stabilityClassifier); + /** + * @param udfFinder pass null for default (AnalysisToolPak only) + */ + public WorkbookEvaluator(EvaluationWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { + this (workbook, null, stabilityClassifier, udfFinder); } /* package */ WorkbookEvaluator(EvaluationWorkbook workbook, IEvaluationListener evaluationListener, - IStabilityClassifier stabilityClassifier) { + IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { _workbook = workbook; _evaluationListener = evaluationListener; _cache = new EvaluationCache(evaluationListener); @@ -101,6 +107,7 @@ public final class WorkbookEvaluator { _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY; _workbookIx = 0; _stabilityClassifier = stabilityClassifier; + _udfFinder = udfFinder == null ? UDFFinder.DEFAULT : udfFinder; } /** @@ -516,4 +523,7 @@ public final class WorkbookEvaluator { EvaluationCell cell = sheet.getCell(rowIndex, columnIndex); return evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker); } + public FreeRefFunction findUserDefinedFunction(String functionName) { + return _udfFinder.findFunction(functionName); + } } diff --git a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java index 1525708f3..08ed7f534 100644 --- a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java @@ -36,8 +36,6 @@ 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 { @@ -144,8 +142,4 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook { return _index - o._index; } } - - public FreeRefFunction findUserDefinedFunction(String functionName) { - return _masterBook.findUserDefinedFunction(functionName); - } } diff --git a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java index e89d2a610..798560374 100644 --- a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluator.java @@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.record.formula.udf.UDFFinder; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook; @@ -47,9 +48,9 @@ public final class ForkedEvaluator { private WorkbookEvaluator _evaluator; private ForkedEvaluationWorkbook _sewb; - private ForkedEvaluator(EvaluationWorkbook masterWorkbook, IStabilityClassifier stabilityClassifier) { + private ForkedEvaluator(EvaluationWorkbook masterWorkbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { _sewb = new ForkedEvaluationWorkbook(masterWorkbook); - _evaluator = new WorkbookEvaluator(_sewb, stabilityClassifier); + _evaluator = new WorkbookEvaluator(_sewb, stabilityClassifier, udfFinder); } private static EvaluationWorkbook createEvaluationWorkbook(Workbook wb) { if (wb instanceof HSSFWorkbook) { @@ -61,8 +62,17 @@ public final class ForkedEvaluator { // } throw new IllegalArgumentException("Unexpected workbook type (" + wb.getClass().getName() + ")"); } + /** + * @deprecated (Sep 2009) (reduce overloading) use {@link #create(Workbook, IStabilityClassifier, UDFFinder)} + */ public static ForkedEvaluator create(Workbook wb, IStabilityClassifier stabilityClassifier) { - return new ForkedEvaluator(createEvaluationWorkbook(wb), stabilityClassifier); + return create(wb, stabilityClassifier, null); + } + /** + * @param udfFinder pass null for default (AnalysisToolPak only) + */ + public static ForkedEvaluator create(Workbook wb, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { + return new ForkedEvaluator(createEvaluationWorkbook(wb), stabilityClassifier, udfFinder); } /** diff --git a/src/java/org/apache/poi/ss/usermodel/Name.java b/src/java/org/apache/poi/ss/usermodel/Name.java index 0cc9d98e9..a07154e5f 100644 --- a/src/java/org/apache/poi/ss/usermodel/Name.java +++ b/src/java/org/apache/poi/ss/usermodel/Name.java @@ -49,8 +49,6 @@ package org.apache.poi.ss.usermodel; * name.setRefersToFormula("IF(Loan_Amount*Interest_Rate>0,1,0)"); * * - * - * Modified 8/31/09 by Petr Udalau - added method setFunction(boolean) */ public interface Name { @@ -61,14 +59,14 @@ public interface Name { */ String getSheetName(); - /** + /** * Gets the name of the named range * * @return named range name */ String getNameName(); - /** + /** * Sets the name of the named range * *

The following is a list of syntax rules that you need to be aware of when you create and edit names.

@@ -118,7 +116,7 @@ public interface Name { void setNameName(String name); /** - * Returns the formula that the name is defined to refer to. + * Returns the formula that the name is defined to refer to. * * @return the reference for this name, null if it has not been set yet. Never empty string * @see #setRefersToFormula(String) diff --git a/src/java/org/apache/poi/ss/usermodel/Workbook.java b/src/java/org/apache/poi/ss/usermodel/Workbook.java index cbbc2a426..99f4ddba8 100644 --- a/src/java/org/apache/poi/ss/usermodel/Workbook.java +++ b/src/java/org/apache/poi/ss/usermodel/Workbook.java @@ -21,15 +21,12 @@ 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 { @@ -189,7 +186,7 @@ public interface Workbook { * @param index of the sheet to remove (0-based) */ void removeSheetAt(int index); - + /** * Sets the repeating rows and columns for a sheet (as found in * File->PageSetup->Sheet). This is function is included in the workbook @@ -374,7 +371,7 @@ public interface Workbook { * Sets the policy on what to do when * getting missing or blank cells from a row. * - * This will then apply to all calls to + * This will then apply to all calls to * {@link Row#getCell(int)} }. See * {@link MissingCellPolicy} */ @@ -467,29 +464,4 @@ 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 getUserDefinedFunctionNames(); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java index 5e6fd9390..5264281f0 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java @@ -20,7 +20,6 @@ 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; @@ -29,8 +28,6 @@ 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 { @@ -180,8 +177,4 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E public SpreadsheetVersion getSpreadsheetVersion(){ return SpreadsheetVersion.EXCEL2007; } - - public FreeRefFunction findUserDefinedFunction(String functionName) { - return _uBook.getUserDefinedFunction(functionName); - } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFormulaEvaluator.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFormulaEvaluator.java index 8780ae23f..0130eca23 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFormulaEvaluator.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFormulaEvaluator.java @@ -24,6 +24,8 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.record.formula.udf.UDFFinder; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.formula.IStabilityClassifier; import org.apache.poi.ss.formula.WorkbookEvaluator; import org.apache.poi.ss.usermodel.Cell; @@ -47,16 +49,31 @@ public class XSSFFormulaEvaluator implements FormulaEvaluator { private WorkbookEvaluator _bookEvaluator; public XSSFFormulaEvaluator(XSSFWorkbook workbook) { - this(workbook, null); + this(workbook, null, null); } /** * @param stabilityClassifier used to optimise caching performance. Pass null - * for the (conservative) assumption that any cell may have its definition changed after + * for the (conservative) assumption that any cell may have its definition changed after * evaluation begins. + * @deprecated (Sep 2009) (reduce overloading) use {@link #create(HSSFWorkbook, IStabilityClassifier, UDFFinder)} */ public XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) { - _bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier); + _bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, null); } + private XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { + _bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, udfFinder); + } + + /** + * @param stabilityClassifier used to optimise caching performance. Pass null + * for the (conservative) assumption that any cell may have its definition changed after + * evaluation begins. + * @param udfFinder pass null for default (AnalysisToolPak only) + */ + public static XSSFFormulaEvaluator create(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) { + return new XSSFFormulaEvaluator(workbook, stabilityClassifier, udfFinder); + } + /** * Should be called whenever there are major changes (e.g. moving sheets) to input cells diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index ffd173611..67d4f69bf 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -32,7 +32,6 @@ 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; @@ -41,7 +40,6 @@ 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; @@ -65,8 +63,6 @@ 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 { private static final Pattern COMMA_PATTERN = Pattern.compile(","); @@ -133,9 +129,6 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable pictures; private static POILogger logger = POILogFactory.getLogger(XSSFWorkbook.class); - - /** Map of user defined functions, key - function name, value - instance of FreeRefFunctions */ - private Map udfFunctions = new HashMap(); /** * Create a new SpreadsheetML workbook. @@ -1352,23 +1345,4 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable getUserDefinedFunctionNames() { - return new ArrayList(udfFunctions.keySet()); - } - } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java index b2937b0fc..2cfbbfec4 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java @@ -17,97 +17,79 @@ package org.apache.poi.hssf.record.formula.eval; -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.record.formula.udf.DefaultUDFFinder; +import org.apache.poi.hssf.record.formula.udf.AggregatingUDFFinder; +import org.apache.poi.hssf.record.formula.udf.UDFFinder; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; 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.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. + * @author Petr Udalau - registering UDFs in workbook and using ToolPacks. */ public final class TestExternalFunction extends TestCase { - 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 MyFunc implements FreeRefFunction { + public MyFunc() { + // + } - 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"); - } - } - } + 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"); + } + } - /** - * 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()); - } + private static class MyFunc2 implements FreeRefFunction { + public MyFunc2() { + // + } - /** - * Creates example ToolPack which contains function "MyFunc2()". - */ - private void createExampleToolPack() { - ToolPack exampleToolPack = new DefaultToolPack(); - exampleToolPack.addFunction("myFunc2", new MyFunc2()); - MainToolPacksHandler.instance().addToolPack(exampleToolPack); - } + 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"); + } + } - /** - * 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); + /** + * Checks that an external function can get invoked from the formula + * evaluator. + */ + public void testInvoke() { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls"); + HSSFSheet sheet = wb.getSheetAt(0); - registerMyFunc(wb); - createExampleToolPack(); + /** + * register the two test UDFs in a UDF finder, to be passed to the evaluator + */ + UDFFinder udff1 = new DefaultUDFFinder(new String[] { "myFunc", }, + new FreeRefFunction[] { new MyFunc(), }); + UDFFinder udff2 = new DefaultUDFFinder(new String[] { "myFunc2", }, + new FreeRefFunction[] { new MyFunc2(), }); + UDFFinder udff = new AggregatingUDFFinder(udff1, udff2); - HSSFRow row = sheet.getRow(0); - HSSFCell myFuncCell = row.getCell(1); //=myFunc("_") - HSSFCell myFunc2Cell = row.getCell(2); //=myFunc2("_") + HSSFRow row = sheet.getRow(0); + HSSFCell myFuncCell = row.getCell(1); // =myFunc("_") - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); - try { - assertEquals("_abc", fe.evaluate(myFuncCell).getStringValue()); - assertEquals("_abc2", fe.evaluate(myFunc2Cell).getStringValue()); - } catch (Exception e) { - assertFalse(true); - } - } + HSSFCell myFunc2Cell = row.getCell(2); // =myFunc2("_") + + HSSFFormulaEvaluator fe = HSSFFormulaEvaluator.create(wb, null, udff); + assertEquals("_abc", fe.evaluate(myFuncCell).getStringValue()); + assertEquals("_abc2", fe.evaluate(myFunc2Cell).getStringValue()); + } } diff --git a/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java b/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java index 9035ecbed..41aa0ab57 100644 --- a/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java +++ b/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java @@ -44,7 +44,7 @@ public class TestWorkbookEvaluator extends TestCase { private static ValueEval evaluateFormula(Ptg[] ptgs) { OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); - return new WorkbookEvaluator(null, null).evaluateFormula(ec, ptgs); + return new WorkbookEvaluator(null, null, null).evaluateFormula(ec, ptgs); } /** diff --git a/src/testcases/org/apache/poi/ss/formula/WorkbookEvaluatorTestHelper.java b/src/testcases/org/apache/poi/ss/formula/WorkbookEvaluatorTestHelper.java index b8b5de991..673cb9500 100644 --- a/src/testcases/org/apache/poi/ss/formula/WorkbookEvaluatorTestHelper.java +++ b/src/testcases/org/apache/poi/ss/formula/WorkbookEvaluatorTestHelper.java @@ -22,7 +22,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; /** * Allows tests to execute {@link WorkbookEvaluator}s and track the internal workings. - * + * * @author Josh Micich */ public final class WorkbookEvaluatorTestHelper { @@ -30,8 +30,8 @@ public final class WorkbookEvaluatorTestHelper { private WorkbookEvaluatorTestHelper() { // no instances of this class } - + public static WorkbookEvaluator createEvaluator(HSSFWorkbook wb, EvaluationListener listener) { - return new WorkbookEvaluator(HSSFEvaluationWorkbook.create(wb), listener, null); + return new WorkbookEvaluator(HSSFEvaluationWorkbook.create(wb), listener, null, null); } }