Improvements to patch 47809 (support for UDFs)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@816016 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2009-09-17 00:00:57 +00:00
parent 6977f358dd
commit cae73922a6
23 changed files with 367 additions and 551 deletions

View File

@ -33,9 +33,7 @@ import org.apache.poi.util.StringUtil;
* @author Libin Roman (Vista Portal LDT. Developer) * @author Libin Roman (Vista Portal LDT. Developer)
* @author Sergei Kozello (sergeikozello at mail.ru) * @author Sergei Kozello (sergeikozello at mail.ru)
* @author Glen Stampoultzis (glens at apache.org) * @author Glen Stampoultzis (glens at apache.org)
* @version 1.0-pre * @author Petr Udalau - added method setFunction(boolean)
*
* Modified 8/31/09 by Petr Udalau - added method setFunction(boolean)
*/ */
public final class NameRecord extends StandardRecord { public final class NameRecord extends StandardRecord {
public final static short sid = 0x0018; public final static short sid = 0x0018;
@ -135,7 +133,7 @@ public final class NameRecord extends StandardRecord {
/** /**
* For named ranges, and built-in names * For named ranges, and built-in names
* @return the 1-based sheet number. * @return the 1-based sheet number.
*/ */
public int getSheetNumber() public int getSheetNumber()
{ {
@ -239,13 +237,13 @@ public final class NameRecord extends StandardRecord {
public boolean isFunctionName() { public boolean isFunctionName() {
return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0; return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0;
} }
/** /**
* Indicates that the defined name refers to a user-defined 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. * This attribute is used when there is an add-in or other code project associated with the file.
* *
* @param value <code>true</code> indicates the name refers to a function. * @param value <code>true</code> indicates the name refers to a function.
*/ */
public void setFunction(boolean function){ public void setFunction(boolean function){
if (function) { if (function) {
field_1_option_flag |= Option.OPT_FUNCTION_NAME; 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_8_length_description_text = field_15_description_text.length();
int field_9_length_help_topic_text = field_16_help_topic_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(); int field_10_length_status_bar_text = field_17_status_bar_text.length();
// size defined below // size defined below
out.writeShort(getOptionFlag()); out.writeShort(getOptionFlag());
out.writeByte(getKeyboardShortcut()); out.writeByte(getKeyboardShortcut());
@ -379,7 +377,7 @@ public final class NameRecord extends StandardRecord {
} }
field_13_name_definition.serializeTokens(out); field_13_name_definition.serializeTokens(out);
field_13_name_definition.serializeArrayConstantData(out); field_13_name_definition.serializeArrayConstantData(out);
StringUtil.putCompressedUnicode( getCustomMenuText(), out); StringUtil.putCompressedUnicode( getCustomMenuText(), out);
StringUtil.putCompressedUnicode( getDescriptionText(), out); StringUtil.putCompressedUnicode( getDescriptionText(), out);
StringUtil.putCompressedUnicode( getHelpTopicText(), out); StringUtil.putCompressedUnicode( getHelpTopicText(), out);
@ -388,14 +386,14 @@ public final class NameRecord extends StandardRecord {
private int getNameRawSize() { private int getNameRawSize() {
if (isBuiltInName()) { if (isBuiltInName()) {
return 1; return 1;
} }
int nChars = field_12_name_text.length(); int nChars = field_12_name_text.length();
if(field_11_nameIsMultibyte) { if(field_11_nameIsMultibyte) {
return 2 * nChars; return 2 * nChars;
} }
return nChars; return nChars;
} }
protected int getDataSize() { protected int getDataSize() {
return 13 // 3 shorts + 7 bytes return 13 // 3 shorts + 7 bytes
+ getNameRawSize() + 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); + f8_descriptionTextLen + f9_helpTopicTextLen + f10_statusBarTextLen);
field_13_name_definition = Formula.read(field_4_length_name_definition, in, nBytesAvailable); field_13_name_definition = Formula.read(field_4_length_name_definition, in, nBytesAvailable);

View File

@ -22,14 +22,17 @@ import java.util.Map;
import org.apache.poi.hssf.record.formula.eval.ValueEval; 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.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.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.NotImplementedException; 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 static final class NotImplemented implements FreeRefFunction {
private final String _functionName; private final String _functionName;
@ -42,13 +45,18 @@ public final class AnalysisToolPak implements ToolPack {
throw new NotImplementedException(_functionName); throw new NotImplementedException(_functionName);
} }
}; };
private Map<String, FreeRefFunction> _functionsByName = createFunctionsMap(); private final Map<String, FreeRefFunction> _functionsByName = createFunctionsMap();
private AnalysisToolPak() {
// enforce singleton
}
public FreeRefFunction findFunction(String name) { public FreeRefFunction findFunction(String name) {
return _functionsByName.get(name); return _functionsByName.get(name);
} }
private Map<String, FreeRefFunction> createFunctionsMap() { private Map<String, FreeRefFunction> createFunctionsMap() {
Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(100); Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(100);
@ -153,16 +161,4 @@ public final class AnalysisToolPak implements ToolPack {
FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc; FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc;
m.put(functionName, func); 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);
}
} }

View File

@ -18,8 +18,6 @@
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; 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.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.NotImplementedException; import org.apache.poi.ss.formula.eval.NotImplementedException;
/** /**
@ -28,8 +26,7 @@ import org.apache.poi.ss.formula.eval.NotImplementedException;
* <tt>AbstractFunctionPtg.field_2_fnc_index</tt> == 255) * <tt>AbstractFunctionPtg.field_2_fnc_index</tt> == 255)
* *
* @author Josh Micich * @author Josh Micich
* * @author Petr Udalau - Improved resolving of UDFs through the ToolPacks.
* Modified 09/07/09 by Petr Udalau - Improved resolving of UDFs through the ToolPacks.
*/ */
final class UserDefinedFunction implements FreeRefFunction { final class UserDefinedFunction implements FreeRefFunction {
@ -40,60 +37,28 @@ final class UserDefinedFunction implements FreeRefFunction {
} }
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
EvaluationWorkbook workbook = ec.getWorkbook();
int nIncomingArgs = args.length; int nIncomingArgs = args.length;
if(nIncomingArgs < 1) { if(nIncomingArgs < 1) {
throw new RuntimeException("function name argument missing"); throw new RuntimeException("function name argument missing");
} }
ValueEval nameArg = args[0]; ValueEval nameArg = args[0];
FreeRefFunction targetFunc; String functionName;
if (nameArg instanceof NameEval) { if (nameArg instanceof NameEval) {
targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg); functionName = ((NameEval) nameArg).getFunctionName();
} else if (nameArg instanceof NameXEval) { } else if (nameArg instanceof NameXEval) {
targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg); functionName = ec.getWorkbook().resolveNameXText(((NameXEval) nameArg).getPtg());
} else { } else {
throw new RuntimeException("First argument should be a NameEval, but got (" throw new RuntimeException("First argument should be a NameEval, but got ("
+ nameArg.getClass().getName() + ")"); + nameArg.getClass().getName() + ")");
} }
FreeRefFunction targetFunc = ec.findUserDefinedFunction(functionName);
if (targetFunc == null) {
throw new NotImplementedException(functionName);
}
int nOutGoingArgs = nIncomingArgs -1; int nOutGoingArgs = nIncomingArgs -1;
ValueEval[] outGoingArgs = new ValueEval[nOutGoingArgs]; ValueEval[] outGoingArgs = new ValueEval[nOutGoingArgs];
System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs); System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs);
return targetFunc.evaluate(outGoingArgs, ec); 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);
}
} }

View File

@ -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<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);
}
}

View File

@ -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 <code>null</code> if
* function isn't contained by any registered tool pack.
*
* @param name Name of function.
* @return Function executor. <code>null</code> 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;
}
}

View File

@ -15,7 +15,7 @@
limitations under the License. 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.HashMap;
import java.util.Map; import java.util.Map;
@ -23,29 +23,27 @@ import java.util.Map;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
/** /**
* Default tool pack. * Default UDF finder - for adding your own user defined functions.
* If you want to add some UDF, but you don't want to create new tool pack, use this. *
*
* @author PUdalau * @author PUdalau
*/ */
public class DefaultToolPack implements ToolPack { public final class DefaultUDFFinder implements UDFFinder {
private Map<String, FreeRefFunction> functionsByName = new HashMap<String, FreeRefFunction>(); private final Map<String, FreeRefFunction> _functionsByName;
public void addFunction(String name, FreeRefFunction evaluator) { public DefaultUDFFinder(String[] functionNames, FreeRefFunction[] functionImpls) {
if (evaluator != null){ int nFuncs = functionNames.length;
functionsByName.put(name, evaluator); if (functionImpls.length != nFuncs) {
} throw new IllegalArgumentException(
} "Mismatch in number of function names and implementations");
}
HashMap<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(nFuncs * 3 / 2);
for (int i = 0; i < functionImpls.length; i++) {
m.put(functionNames[i], functionImpls[i]);
}
_functionsByName = m;
}
public boolean containsFunction(String name) { public FreeRefFunction findFunction(String name) {
return functionsByName.containsKey(name); return _functionsByName.get(name);
} }
public FreeRefFunction findFunction(String name) {
return functionsByName.get(name);
}
public void removeFunction(String name) {
functionsByName.remove(name);
}
} }

View File

@ -15,42 +15,24 @@
limitations under the License. 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; 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 * @author PUdalau
*/ */
public interface ToolPack { public interface UDFFinder {
public static final UDFFinder DEFAULT = new AggregatingUDFFinder(AnalysisToolPak.instance);
/** /**
* Returns executor by specified name. Returns <code>null</code> if tool * Returns executor by specified name. Returns <code>null</code> if the function name is unknown.
* pack doesn't contains such function. *
*
* @param name Name of function. * @param name Name of function.
* @return Function executor. * @return Function executor.
*/ */
FreeRefFunction findFunction(String name); 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);
} }

View File

@ -6,7 +6,7 @@
(the "License"); you may not use this file except in compliance with (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at 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 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, 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.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.record.formula.Ptg; 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.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 * Internal POI use only
* *
* @author Josh Micich * @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 { public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {
private final HSSFWorkbook _uBook; private final HSSFWorkbook _uBook;
private final Workbook _iBook; private final Workbook _iBook;
public static HSSFEvaluationWorkbook create(HSSFWorkbook book) { public static HSSFEvaluationWorkbook create(HSSFWorkbook book) {
if (book == null) { if (book == null) {
return null; return null;
} }
return new HSSFEvaluationWorkbook(book); return new HSSFEvaluationWorkbook(book);
} }
private HSSFEvaluationWorkbook(HSSFWorkbook book) { private HSSFEvaluationWorkbook(HSSFWorkbook book) {
_uBook = book; _uBook = book;
_iBook = book.getWorkbook(); _iBook = book.getWorkbook();
} }
public int getExternalSheetIndex(String sheetName) { public int getExternalSheetIndex(String sheetName) {
int sheetIndex = _uBook.getSheetIndex(sheetName); int sheetIndex = _uBook.getSheetIndex(sheetName);
return _iBook.checkExternSheet(sheetIndex); return _iBook.checkExternSheet(sheetIndex);
} }
public int getExternalSheetIndex(String workbookName, String sheetName) { public int getExternalSheetIndex(String workbookName, String sheetName) {
return _iBook.getExternalSheetIndex(workbookName, sheetName); return _iBook.getExternalSheetIndex(workbookName, sheetName);
} }
public NameXPtg getNameXPtg(String name) { public NameXPtg getNameXPtg(String name) {
return _iBook.getNameXPtg(name); return _iBook.getNameXPtg(name);
} }
/** /**
* Lookup a named range by its name. * Lookup a named range by its name.
* *
* @param name the name to search * @param name the name to search
* @param sheetIndex the 0-based index of the sheet this formula belongs to. * @param sheetIndex the 0-based index of the sheet this formula belongs to.
* The sheet index is required to resolve sheet-level names. <code>-1</code> means workbook-global names * The sheet index is required to resolve sheet-level names. <code>-1</code> means workbook-global names
*/ */
public EvaluationName getName(String name, int sheetIndex) { public EvaluationName getName(String name, int sheetIndex) {
for(int i=0; i < _iBook.getNumNames(); i++) { for(int i=0; i < _iBook.getNumNames(); i++) {
NameRecord nr = _iBook.getNameRecord(i); NameRecord nr = _iBook.getNameRecord(i);
if (nr.getSheetNumber() == sheetIndex+1 && name.equalsIgnoreCase(nr.getNameText())) { if (nr.getSheetNumber() == sheetIndex+1 && name.equalsIgnoreCase(nr.getNameText())) {
return new Name(nr, i); return new Name(nr, i);
} }
} }
return sheetIndex == -1 ? null : getName(name, -1); return sheetIndex == -1 ? null : getName(name, -1);
} }
public int getSheetIndex(EvaluationSheet evalSheet) { public int getSheetIndex(EvaluationSheet evalSheet) {
HSSFSheet sheet = ((HSSFEvaluationSheet)evalSheet).getHSSFSheet(); HSSFSheet sheet = ((HSSFEvaluationSheet)evalSheet).getHSSFSheet();
return _uBook.getSheetIndex(sheet); return _uBook.getSheetIndex(sheet);
} }
public int getSheetIndex(String sheetName) { public int getSheetIndex(String sheetName) {
return _uBook.getSheetIndex(sheetName); return _uBook.getSheetIndex(sheetName);
} }
public String getSheetName(int sheetIndex) { public String getSheetName(int sheetIndex) {
return _uBook.getSheetName(sheetIndex); return _uBook.getSheetName(sheetIndex);
} }
public EvaluationSheet getSheet(int sheetIndex) { public EvaluationSheet getSheet(int sheetIndex) {
return new HSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex)); return new HSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
} }
public int convertFromExternSheetIndex(int externSheetIndex) { public int convertFromExternSheetIndex(int externSheetIndex) {
return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex); return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);
} }
public ExternalSheet getExternalSheet(int externSheetIndex) { public ExternalSheet getExternalSheet(int externSheetIndex) {
return _iBook.getExternalSheet(externSheetIndex); return _iBook.getExternalSheet(externSheetIndex);
} }
public String resolveNameXText(NameXPtg n) { public String resolveNameXText(NameXPtg n) {
return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex()); return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex());
} }
public String getSheetNameByExternSheet(int externSheetIndex) { public String getSheetNameByExternSheet(int externSheetIndex) {
return _iBook.findSheetNameFromExternSheet(externSheetIndex); return _iBook.findSheetNameFromExternSheet(externSheetIndex);
} }
public String getNameText(NamePtg namePtg) { public String getNameText(NamePtg namePtg) {
return _iBook.getNameRecord(namePtg.getIndex()).getNameText(); return _iBook.getNameRecord(namePtg.getIndex()).getNameText();
} }
public EvaluationName getName(NamePtg namePtg) { public EvaluationName getName(NamePtg namePtg) {
int ix = namePtg.getIndex(); int ix = namePtg.getIndex();
return new Name(_iBook.getNameRecord(ix), ix); return new Name(_iBook.getNameRecord(ix), ix);
} }
public Ptg[] getFormulaTokens(EvaluationCell evalCell) { public Ptg[] getFormulaTokens(EvaluationCell evalCell) {
HSSFCell cell = ((HSSFEvaluationCell)evalCell).getHSSFCell(); HSSFCell cell = ((HSSFEvaluationCell)evalCell).getHSSFCell();
if (false) { if (false) {
// re-parsing the formula text also works, but is a waste of time // 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 // 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. // 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())); return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet()));
} }
FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord(); FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord();
return fra.getFormulaTokens(); return fra.getFormulaTokens();
} }
private static final class Name implements EvaluationName { private static final class Name implements EvaluationName {
private final NameRecord _nameRecord; private final NameRecord _nameRecord;
private final int _index; private final int _index;
public Name(NameRecord nameRecord, int index) { public Name(NameRecord nameRecord, int index) {
_nameRecord = nameRecord; _nameRecord = nameRecord;
_index = index; _index = index;
} }
public Ptg[] getNameDefinition() { public Ptg[] getNameDefinition() {
return _nameRecord.getNameDefinition(); return _nameRecord.getNameDefinition();
} }
public String getNameText() { public String getNameText() {
return _nameRecord.getNameText(); return _nameRecord.getNameText();
} }
public boolean hasFormula() { public boolean hasFormula() {
return _nameRecord.hasFormula(); return _nameRecord.hasFormula();
} }
public boolean isFunctionName() { public boolean isFunctionName() {
return _nameRecord.isFunctionName(); return _nameRecord.isFunctionName();
} }
public boolean isRange() { public boolean isRange() {
return _nameRecord.hasFormula(); // TODO - is this right? return _nameRecord.hasFormula(); // TODO - is this right?
} }
public NamePtg createPtg() { public NamePtg createPtg() {
return new NamePtg(_index); return new NamePtg(_index);
} }
} }
public SpreadsheetVersion getSpreadsheetVersion(){ public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL97; return SpreadsheetVersion.EXCEL97;
} }
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _uBook.getUserDefinedFunction(functionName);
}
} }

View File

@ -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.NumberEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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.eval.ValueEval;
import org.apache.poi.hssf.record.formula.udf.UDFFinder;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
import org.apache.poi.ss.formula.IStabilityClassifier; import org.apache.poi.ss.formula.IStabilityClassifier;
import org.apache.poi.ss.formula.WorkbookEvaluator; import org.apache.poi.ss.formula.WorkbookEvaluator;
@ -64,9 +65,27 @@ public class HSSFFormulaEvaluator implements FormulaEvaluator {
* evaluation begins. * evaluation begins.
*/ */
public HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) { public HSSFFormulaEvaluator(HSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) {
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook), stabilityClassifier); this(workbook, stabilityClassifier, null);
} }
/**
* @param udfFinder pass <code>null</code> 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 <code>null</code>
* for the (conservative) assumption that any cell may have its definition changed after
* evaluation begins.
* @param udfFinder pass <code>null</code> 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 * Coordinates several formula evaluators together so that formulas that involve external
* references can be evaluated. * references can be evaluated.

View File

@ -24,11 +24,9 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.poi.POIDocument; 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.Ref3DPtg;
import org.apache.poi.hssf.record.formula.SheetNameFormatter; import org.apache.poi.hssf.record.formula.SheetNameFormatter;
import org.apache.poi.hssf.record.formula.UnionPtg; 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.hssf.util.CellReference;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.CreationHelper; 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.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.ss.formula.FormulaType;
import org.apache.poi.util.POILogFactory; 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 Andrew C. Oliver (acoliver at apache dot org)
* @author Glen Stampoultzis (glens at apache.org) * @author Glen Stampoultzis (glens at apache.org)
* @author Shawn Laubach (slaubach at apache dot 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 { public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook {
private static final Pattern COMMA_PATTERN = Pattern.compile(","); 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); 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!) * Creates new HSSFWorkbook from scratch (start here!)
@ -186,7 +176,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
workbook = book; workbook = book;
_sheets = new ArrayList( INITIAL_CAPACITY ); _sheets = new ArrayList( INITIAL_CAPACITY );
names = new ArrayList( INITIAL_CAPACITY ); names = new ArrayList( INITIAL_CAPACITY );
udfFunctions = new HashMap<String, FreeRefFunction>();
} }
public HSSFWorkbook(POIFSFileSystem fs) throws IOException { 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); _sheets = new ArrayList(INITIAL_CAPACITY);
names = new ArrayList(INITIAL_CAPACITY); names = new ArrayList(INITIAL_CAPACITY);
udfFunctions = new HashMap<String, FreeRefFunction>();
// Grab the data from the workbook stream, however // Grab the data from the workbook stream, however
// it happens to be spelled. // 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<String> getUserDefinedFunctionNames() {
return new ArrayList<String>(udfFunctions.keySet());
}
/** /**
* Is the workbook protected with a password (not encrypted)? * 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) { public NameXPtg getNameXPtg(String name) {
return workbook.getNameXPtg(name); return workbook.getNameXPtg(name);
} }
} }

View File

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

View File

@ -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.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; 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.eval.ValueEval;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet; import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
@ -256,4 +257,8 @@ public final class OperationEvaluationContext {
} }
return CellReference.classifyCellReference(str, ssVersion); return CellReference.classifyCellReference(str, ssVersion);
} }
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _bookEvaluator.findUserDefinedFunction(functionName);
}
} }

View File

@ -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.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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.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.hssf.util.CellReference;
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
import org.apache.poi.ss.formula.eval.NotImplementedException; import org.apache.poi.ss.formula.eval.NotImplementedException;
@ -87,12 +89,16 @@ public final class WorkbookEvaluator {
private final Map<String, Integer> _sheetIndexesByName; private final Map<String, Integer> _sheetIndexesByName;
private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment; private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
private final IStabilityClassifier _stabilityClassifier; private final IStabilityClassifier _stabilityClassifier;
private final UDFFinder _udfFinder;
public WorkbookEvaluator(EvaluationWorkbook workbook, IStabilityClassifier stabilityClassifier) { /**
this (workbook, null, stabilityClassifier); * @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
*/
public WorkbookEvaluator(EvaluationWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
this (workbook, null, stabilityClassifier, udfFinder);
} }
/* package */ WorkbookEvaluator(EvaluationWorkbook workbook, IEvaluationListener evaluationListener, /* package */ WorkbookEvaluator(EvaluationWorkbook workbook, IEvaluationListener evaluationListener,
IStabilityClassifier stabilityClassifier) { IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
_workbook = workbook; _workbook = workbook;
_evaluationListener = evaluationListener; _evaluationListener = evaluationListener;
_cache = new EvaluationCache(evaluationListener); _cache = new EvaluationCache(evaluationListener);
@ -101,6 +107,7 @@ public final class WorkbookEvaluator {
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY; _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
_workbookIx = 0; _workbookIx = 0;
_stabilityClassifier = stabilityClassifier; _stabilityClassifier = stabilityClassifier;
_udfFinder = udfFinder == null ? UDFFinder.DEFAULT : udfFinder;
} }
/** /**
@ -516,4 +523,7 @@ public final class WorkbookEvaluator {
EvaluationCell cell = sheet.getCell(rowIndex, columnIndex); EvaluationCell cell = sheet.getCell(rowIndex, columnIndex);
return evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker); return evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker);
} }
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _udfFinder.findFunction(functionName);
}
} }

View File

@ -36,8 +36,6 @@ import org.apache.poi.ss.usermodel.Workbook;
* updated after a call to {@link #getOrCreateUpdatableCell(String, int, int)}. * updated after a call to {@link #getOrCreateUpdatableCell(String, int, int)}.
* *
* @author Josh Micich * @author Josh Micich
*
* Modified 09/07/09 by Petr Udalau - added methods for searching for UDFs of this Workbook.
*/ */
final class ForkedEvaluationWorkbook implements EvaluationWorkbook { final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
@ -144,8 +142,4 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook {
return _index - o._index; return _index - o._index;
} }
} }
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _masterBook.findUserDefinedFunction(functionName);
}
} }

View File

@ -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.NumberEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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.eval.ValueEval;
import org.apache.poi.hssf.record.formula.udf.UDFFinder;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook; import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
@ -47,9 +48,9 @@ public final class ForkedEvaluator {
private WorkbookEvaluator _evaluator; private WorkbookEvaluator _evaluator;
private ForkedEvaluationWorkbook _sewb; private ForkedEvaluationWorkbook _sewb;
private ForkedEvaluator(EvaluationWorkbook masterWorkbook, IStabilityClassifier stabilityClassifier) { private ForkedEvaluator(EvaluationWorkbook masterWorkbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
_sewb = new ForkedEvaluationWorkbook(masterWorkbook); _sewb = new ForkedEvaluationWorkbook(masterWorkbook);
_evaluator = new WorkbookEvaluator(_sewb, stabilityClassifier); _evaluator = new WorkbookEvaluator(_sewb, stabilityClassifier, udfFinder);
} }
private static EvaluationWorkbook createEvaluationWorkbook(Workbook wb) { private static EvaluationWorkbook createEvaluationWorkbook(Workbook wb) {
if (wb instanceof HSSFWorkbook) { if (wb instanceof HSSFWorkbook) {
@ -61,8 +62,17 @@ public final class ForkedEvaluator {
// } // }
throw new IllegalArgumentException("Unexpected workbook type (" + wb.getClass().getName() + ")"); 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) { public static ForkedEvaluator create(Workbook wb, IStabilityClassifier stabilityClassifier) {
return new ForkedEvaluator(createEvaluationWorkbook(wb), stabilityClassifier); return create(wb, stabilityClassifier, null);
}
/**
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
*/
public static ForkedEvaluator create(Workbook wb, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
return new ForkedEvaluator(createEvaluationWorkbook(wb), stabilityClassifier, udfFinder);
} }
/** /**

View File

@ -49,8 +49,6 @@ package org.apache.poi.ss.usermodel;
* name.setRefersToFormula("IF(Loan_Amount*Interest_Rate>0,1,0)"); * name.setRefersToFormula("IF(Loan_Amount*Interest_Rate>0,1,0)");
* *
* </blockquote></pre> * </blockquote></pre>
*
* Modified 8/31/09 by Petr Udalau - added method setFunction(boolean)
*/ */
public interface Name { public interface Name {
@ -61,14 +59,14 @@ public interface Name {
*/ */
String getSheetName(); String getSheetName();
/** /**
* Gets the name of the named range * Gets the name of the named range
* *
* @return named range name * @return named range name
*/ */
String getNameName(); String getNameName();
/** /**
* Sets the name of the named range * Sets the name of the named range
* *
* <p>The following is a list of syntax rules that you need to be aware of when you create and edit names.</p> * <p>The following is a list of syntax rules that you need to be aware of when you create and edit names.</p>
@ -118,7 +116,7 @@ public interface Name {
void setNameName(String 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, <code>null</code> if it has not been set yet. Never empty string * @return the reference for this name, <code>null</code> if it has not been set yet. Never empty string
* @see #setRefersToFormula(String) * @see #setRefersToFormula(String)

View File

@ -21,15 +21,12 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
/** /**
* High level representation of a Excel workbook. This is the first object most users * 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 * will construct whether they are reading or writing a workbook. It is also the
* top level object for creating new sheets/etc. * 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 { public interface Workbook {
@ -189,7 +186,7 @@ public interface Workbook {
* @param index of the sheet to remove (0-based) * @param index of the sheet to remove (0-based)
*/ */
void removeSheetAt(int index); void removeSheetAt(int index);
/** /**
* Sets the repeating rows and columns for a sheet (as found in * Sets the repeating rows and columns for a sheet (as found in
* File->PageSetup->Sheet). This is function is included in the workbook * 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 * Sets the policy on what to do when
* getting missing or blank cells from a row. * 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 Row#getCell(int)} }. See
* {@link MissingCellPolicy} * {@link MissingCellPolicy}
*/ */
@ -467,29 +464,4 @@ public interface Workbook {
* @param hidden 0 for not hidden, 1 for hidden, 2 for very hidden * @param hidden 0 for not hidden, 1 for hidden, 2 for very hidden
*/ */
void setSheetHidden(int sheetIx, int 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();
} }

View File

@ -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.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.record.formula.Ptg; 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.formula.*;
import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.SpreadsheetVersion;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
@ -29,8 +28,6 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
* Internal POI use only * Internal POI use only
* *
* @author Josh Micich * @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 { public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {
@ -180,8 +177,4 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
public SpreadsheetVersion getSpreadsheetVersion(){ public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL2007; return SpreadsheetVersion.EXCEL2007;
} }
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _uBook.getUserDefinedFunction(functionName);
}
} }

View File

@ -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.NumberEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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.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.IStabilityClassifier;
import org.apache.poi.ss.formula.WorkbookEvaluator; import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
@ -47,16 +49,31 @@ public class XSSFFormulaEvaluator implements FormulaEvaluator {
private WorkbookEvaluator _bookEvaluator; private WorkbookEvaluator _bookEvaluator;
public XSSFFormulaEvaluator(XSSFWorkbook workbook) { public XSSFFormulaEvaluator(XSSFWorkbook workbook) {
this(workbook, null); this(workbook, null, null);
} }
/** /**
* @param stabilityClassifier used to optimise caching performance. Pass <code>null</code> * @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
* 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. * evaluation begins.
* @deprecated (Sep 2009) (reduce overloading) use {@link #create(HSSFWorkbook, IStabilityClassifier, UDFFinder)}
*/ */
public XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) { 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 <code>null</code>
* for the (conservative) assumption that any cell may have its definition changed after
* evaluation begins.
* @param udfFinder pass <code>null</code> 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 * Should be called whenever there are major changes (e.g. moving sheets) to input cells

View File

@ -32,7 +32,6 @@ import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException; import org.apache.poi.POIXMLException;
import org.apache.poi.POIXMLProperties; import org.apache.poi.POIXMLProperties;
import org.apache.poi.hssf.record.formula.SheetNameFormatter; 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.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart; 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.PackageRelationshipTypes;
import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.TargetMode; 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.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; 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 * 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 * will construct whether they are reading or writing a workbook. It is also the
* top level object for creating new sheets/etc. * 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> { public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<XSSFSheet> {
private static final Pattern COMMA_PATTERN = Pattern.compile(","); private static final Pattern COMMA_PATTERN = Pattern.compile(",");
@ -133,9 +129,6 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
private List<XSSFPictureData> pictures; private List<XSSFPictureData> pictures;
private static POILogger logger = POILogFactory.getLogger(XSSFWorkbook.class); 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. * Create a new SpreadsheetML workbook.
@ -1352,23 +1345,4 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
public MapInfo getMapInfo(){ public MapInfo getMapInfo(){
return mapInfo; 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());
}
} }

View File

@ -17,97 +17,79 @@
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; 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.udf.DefaultUDFFinder;
import org.apache.poi.hssf.record.formula.toolpack.MainToolPacksHandler; import org.apache.poi.hssf.record.formula.udf.AggregatingUDFFinder;
import org.apache.poi.hssf.record.formula.toolpack.ToolPack; import org.apache.poi.hssf.record.formula.udf.UDFFinder;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.OperationEvaluationContext; import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.usermodel.Workbook;
/** /**
*
* @author Josh Micich * @author Josh Micich
* * @author Petr Udalau - registering UDFs in workbook and using ToolPacks.
* Modified 09/14/09 by Petr Udalau - Test of registering UDFs in workbook and
* using ToolPacks.
*/ */
public final class TestExternalFunction extends TestCase { public final class TestExternalFunction extends TestCase {
private static class MyFunc implements FreeRefFunction { private static class MyFunc implements FreeRefFunction {
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { public MyFunc() {
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) {
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { if (args.length != 1 || !(args[0] instanceof StringEval)) {
if (args.length != 1 || !(args[0] instanceof StringEval)) { return ErrorEval.VALUE_INVALID;
return ErrorEval.VALUE_INVALID; }
} else { StringEval input = (StringEval) args[0];
StringEval input = (StringEval) args[0]; return new StringEval(input.getStringValue() + "abc");
return new StringEval(input.getStringValue() + "abc2"); }
} }
}
}
/** private static class MyFunc2 implements FreeRefFunction {
* Creates and registers user-defined function "MyFunc()" directly with POI. public MyFunc2() {
* 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());
}
/** public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
* Creates example ToolPack which contains function "MyFunc2()". if (args.length != 1 || !(args[0] instanceof StringEval)) {
*/ return ErrorEval.VALUE_INVALID;
private void createExampleToolPack() { }
ToolPack exampleToolPack = new DefaultToolPack(); StringEval input = (StringEval) args[0];
exampleToolPack.addFunction("myFunc2", new MyFunc2()); return new StringEval(input.getStringValue() + "abc2");
MainToolPacksHandler.instance().addToolPack(exampleToolPack); }
} }
/** /**
* Checks that an external function can get invoked from the formula * Checks that an external function can get invoked from the formula
* evaluator. * evaluator.
* */
* @throws IOException public void testInvoke() {
* HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
*/ HSSFSheet sheet = wb.getSheetAt(0);
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); HSSFCell myFunc2Cell = row.getCell(2); // =myFunc2("_")
try {
assertEquals("_abc", fe.evaluate(myFuncCell).getStringValue()); HSSFFormulaEvaluator fe = HSSFFormulaEvaluator.create(wb, null, udff);
assertEquals("_abc2", fe.evaluate(myFunc2Cell).getStringValue()); assertEquals("_abc", fe.evaluate(myFuncCell).getStringValue());
} catch (Exception e) { assertEquals("_abc2", fe.evaluate(myFunc2Cell).getStringValue());
assertFalse(true); }
}
}
} }

View File

@ -44,7 +44,7 @@ public class TestWorkbookEvaluator extends TestCase {
private static ValueEval evaluateFormula(Ptg[] ptgs) { private static ValueEval evaluateFormula(Ptg[] ptgs) {
OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null); 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);
} }
/** /**

View File

@ -22,7 +22,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/** /**
* Allows tests to execute {@link WorkbookEvaluator}s and track the internal workings. * Allows tests to execute {@link WorkbookEvaluator}s and track the internal workings.
* *
* @author Josh Micich * @author Josh Micich
*/ */
public final class WorkbookEvaluatorTestHelper { public final class WorkbookEvaluatorTestHelper {
@ -30,8 +30,8 @@ public final class WorkbookEvaluatorTestHelper {
private WorkbookEvaluatorTestHelper() { private WorkbookEvaluatorTestHelper() {
// no instances of this class // no instances of this class
} }
public static WorkbookEvaluator createEvaluator(HSSFWorkbook wb, EvaluationListener listener) { 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);
} }
} }