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 ca9dd7d3e..104b7a86d 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 @@ -20,34 +20,39 @@ package org.apache.poi.hssf.record.formula.atp; import java.util.HashMap; import java.util.Map; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.ss.formula.EvaluationWorkbook; +import org.apache.poi.ss.formula.eval.NotImplementedException; public final class AnalysisToolPak { - private static final FreeRefFunction NotImplemented = new FreeRefFunction() { + private static final class NotImplemented implements FreeRefFunction { + private final String _functionName; + + public NotImplemented(String functionName) { + _functionName = functionName; + } public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) { - return ErrorEval.FUNCTION_NOT_IMPLEMENTED; + throw new NotImplementedException(_functionName); } }; - private static Map _functionsByName = createFunctionsMap(); + private static Map _functionsByName = createFunctionsMap(); private AnalysisToolPak() { // no instances of this class } public static FreeRefFunction findFunction(String name) { - return (FreeRefFunction)_functionsByName.get(name); + return _functionsByName.get(name); } - private static Map createFunctionsMap() { - Map m = new HashMap(100); + private static Map createFunctionsMap() { + Map m = new HashMap(100); r(m, "ACCRINT", null); r(m, "ACCRINTM", null); @@ -146,8 +151,8 @@ public final class AnalysisToolPak { return m; } - private static void r(Map m, String functionName, FreeRefFunction pFunc) { - FreeRefFunction func = pFunc == null ? NotImplemented : pFunc; + private static void r(Map m, String functionName, FreeRefFunction pFunc) { + FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc; m.put(functionName, func); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ErrorEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/ErrorEval.java index e8e197d20..687052566 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ErrorEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ErrorEval.java @@ -48,7 +48,6 @@ public final class ErrorEval implements ValueEval { private static final int CIRCULAR_REF_ERROR_CODE = 0xFFFFFFC4; private static final int FUNCTION_NOT_IMPLEMENTED_CODE = 0xFFFFFFE2; - public static final ErrorEval FUNCTION_NOT_IMPLEMENTED = new ErrorEval(FUNCTION_NOT_IMPLEMENTED_CODE); // Note - Excel does not seem to represent this condition with an error code public static final ErrorEval CIRCULAR_REF_ERROR = new ErrorEval(CIRCULAR_REF_ERROR_CODE); @@ -68,7 +67,6 @@ public final class ErrorEval implements ValueEval { case HSSFErrorConstants.ERROR_NA: return NA; // non-std errors (conditions modeled as errors by POI) case CIRCULAR_REF_ERROR_CODE: return CIRCULAR_REF_ERROR; - case FUNCTION_NOT_IMPLEMENTED_CODE: return FUNCTION_NOT_IMPLEMENTED; } throw new RuntimeException("Unexpected error code (" + errorCode + ")"); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java index 8e8a9799e..d4d6de6a6 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java @@ -20,6 +20,7 @@ package org.apache.poi.hssf.record.formula.eval; import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.ss.formula.EvaluationWorkbook; +import org.apache.poi.ss.formula.eval.NotImplementedException; /** * * Common entry point for all user-defined (non-built-in) functions (where @@ -40,17 +41,13 @@ final class ExternalFunction implements FreeRefFunction { Eval nameArg = args[0]; FreeRefFunction targetFunc; - try { - if (nameArg instanceof NameEval) { - targetFunc = findInternalUserDefinedFunction((NameEval) nameArg); - } else if (nameArg instanceof NameXEval) { - targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg); - } else { - throw new RuntimeException("First argument should be a NameEval, but got (" - + nameArg.getClass().getName() + ")"); - } - } catch (EvaluationException e) { - return e.getErrorEval(); + if (nameArg instanceof NameEval) { + targetFunc = findInternalUserDefinedFunction((NameEval) nameArg); + } else if (nameArg instanceof NameXEval) { + targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg); + } else { + throw new RuntimeException("First argument should be a NameEval, but got (" + + nameArg.getClass().getName() + ")"); } int nOutGoingArgs = nIncomingArgs -1; Eval[] outGoingArgs = new Eval[nOutGoingArgs]; @@ -58,8 +55,8 @@ final class ExternalFunction implements FreeRefFunction { return targetFunc.evaluate(outGoingArgs, workbook, srcCellSheet, srcCellRow, srcCellCol); } - private FreeRefFunction findExternalUserDefinedFunction(EvaluationWorkbook workbook, - NameXEval n) throws EvaluationException { + private static FreeRefFunction findExternalUserDefinedFunction(EvaluationWorkbook workbook, + NameXEval n) { String functionName = workbook.resolveNameXText(n.getPtg()); if(false) { @@ -71,10 +68,10 @@ final class ExternalFunction implements FreeRefFunction { if (result != null) { return result; } - throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); + throw new NotImplementedException(functionName); } - private FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) throws EvaluationException { + private static FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) { String functionName = functionNameEval.getFunctionName(); if(false) { @@ -82,7 +79,6 @@ final class ExternalFunction implements FreeRefFunction { } // TODO find the implementation for the user defined function - throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); + throw new NotImplementedException(functionName); } } - diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/FuncVarEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/FuncVarEval.java index d30c35359..76cfe7bb6 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/FuncVarEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/FuncVarEval.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.record.formula.eval; import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; import org.apache.poi.hssf.record.formula.functions.Function; +import org.apache.poi.ss.formula.eval.NotImplementedException; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -26,25 +27,25 @@ import org.apache.poi.hssf.record.formula.functions.Function; */ public final class FuncVarEval extends FunctionEval { - private AbstractFunctionPtg delegate; + private AbstractFunctionPtg delegate; - public FuncVarEval(AbstractFunctionPtg funcPtg) { - delegate = funcPtg; - } + public FuncVarEval(AbstractFunctionPtg funcPtg) { + delegate = funcPtg; + } - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - Function f = getFunction(); - if (f == null) { - return ErrorEval.FUNCTION_NOT_IMPLEMENTED; + public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { + Function f = getFunction(); + if (f == null) { + throw new NotImplementedException("FuncIx=" + getFunctionIndex()); } return f.evaluate(operands, srcRow, srcCol); - } + } - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } + public int getNumberOfOperands() { + return delegate.getNumberOfOperands(); + } - public short getFunctionIndex() { - return delegate.getFunctionIndex(); - } + public short getFunctionIndex() { + return delegate.getFunctionIndex(); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java index e68b8ee67..2940af686 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java @@ -43,17 +43,14 @@ public abstract class FunctionEval implements OperationEval { protected static Function[] functions = produceFunctions(); - private static Map freeRefFunctionsByIdMap; + private static Map freeRefFunctionsByIdMap; static { - Map m = new HashMap(); - addMapping(m, ID.INDIRECT, new Indirect()); - addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction()); + Map m = new HashMap(); + m.put(createFRFKey(ID.INDIRECT), new Indirect()); + m.put(createFRFKey(ID.EXTERNAL_FUNC), new ExternalFunction()); freeRefFunctionsByIdMap = m; } - private static void addMapping(Map m, int offset, FreeRefFunction frf) { - m.put(createFRFKey(offset), frf); - } private static Integer createFRFKey(int functionIndex) { return new Integer(functionIndex); } @@ -67,7 +64,7 @@ public abstract class FunctionEval implements OperationEval { return freeRefFunctionsByIdMap.containsKey(createFRFKey(getFunctionIndex())); } public FreeRefFunction getFreeRefFunction() { - return (FreeRefFunction) freeRefFunctionsByIdMap.get(createFRFKey(getFunctionIndex())); + return freeRefFunctionsByIdMap.get(createFRFKey(getFunctionIndex())); } public abstract short getFunctionIndex(); @@ -164,11 +161,11 @@ public abstract class FunctionEval implements OperationEval { retval[88] = new Setname(); // SETNAME retval[89] = new Caller(); // CALLER retval[90] = new Deref(); // DEREF - retval[91] = new NotImplementedFunction(); // WINDOWS + retval[91] = new NotImplementedFunction("WINDOWS"); retval[92] = new Series(); // SERIES - retval[93] = new NotImplementedFunction(); // DOCUMENTS + retval[93] = new NotImplementedFunction("DOCUMENTS"); retval[94] = new Activecell(); // ACTIVECELL - retval[95] = new NotImplementedFunction(); // SELECTION + retval[95] = new NotImplementedFunction("SELECTION"); retval[96] = new Result(); // RESULT retval[97] = NumericFunction.ATAN2; retval[98] = NumericFunction.ASIN; @@ -179,8 +176,8 @@ public abstract class FunctionEval implements OperationEval { retval[103] = new Links(); // LINKS retval[104] = new Input(); // INPUT retval[105] = new Isref(); // ISREF - retval[106] = new NotImplementedFunction(); // GETFORMULA - retval[107] = new NotImplementedFunction(); // GETNAME + retval[106] = new NotImplementedFunction("GETFORMULA"); + retval[107] = new NotImplementedFunction("GETNAME"); retval[108] = new Setvalue(); // SETVALUE retval[109] = NumericFunction.LOG; retval[110] = new Exec(); // EXEC @@ -196,7 +193,7 @@ public abstract class FunctionEval implements OperationEval { retval[120] = new Substitute(); // SUBSTITUTE retval[121] = new Code(); // CODE retval[122] = new Names(); // NAMES - retval[123] = new NotImplementedFunction(); // DIRECTORY + retval[123] = new NotImplementedFunction("DIRECTORY"); retval[124] = new Find(); // FIND retval[125] = new Cell(); // CELL retval[126] = new Iserr(); // ISERR @@ -205,36 +202,36 @@ public abstract class FunctionEval implements OperationEval { retval[129] = new Isblank(); // ISBLANK retval[130] = new T(); // T retval[131] = new N(); // N - retval[132] = new NotImplementedFunction(); // FOPEN - retval[133] = new NotImplementedFunction(); // FCLOSE - retval[134] = new NotImplementedFunction(); // FSIZE - retval[135] = new NotImplementedFunction(); // FREADLN - retval[136] = new NotImplementedFunction(); // FREAD - retval[137] = new NotImplementedFunction(); // FWRITELN - retval[138] = new NotImplementedFunction(); // FWRITE + retval[132] = new NotImplementedFunction("FOPEN"); + retval[133] = new NotImplementedFunction("FCLOSE"); + retval[134] = new NotImplementedFunction("FSIZE"); + retval[135] = new NotImplementedFunction("FREADLN"); + retval[136] = new NotImplementedFunction("FREAD"); + retval[137] = new NotImplementedFunction("FWRITELN"); + retval[138] = new NotImplementedFunction("FWRITE"); retval[139] = new Fpos(); // FPOS retval[140] = new Datevalue(); // DATEVALUE retval[141] = new Timevalue(); // TIMEVALUE retval[142] = new Sln(); // SLN retval[143] = new Syd(); // SYD retval[144] = new Ddb(); // DDB - retval[145] = new NotImplementedFunction(); // GETDEF + retval[145] = new NotImplementedFunction("GETDEF"); retval[146] = new Reftext(); // REFTEXT retval[147] = new Textref(); // TEXTREF retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature - retval[149] = new NotImplementedFunction(); // REGISTER + retval[149] = new NotImplementedFunction("REGISTER"); retval[150] = new Call(); // CALL - retval[151] = new NotImplementedFunction(); // ADDBAR - retval[152] = new NotImplementedFunction(); // ADDMENU - retval[153] = new NotImplementedFunction(); // ADDCOMMAND - retval[154] = new NotImplementedFunction(); // ENABLECOMMAND - retval[155] = new NotImplementedFunction(); // CHECKCOMMAND - retval[156] = new NotImplementedFunction(); // RENAMECOMMAND - retval[157] = new NotImplementedFunction(); // SHOWBAR - retval[158] = new NotImplementedFunction(); // DELETEMENU - retval[159] = new NotImplementedFunction(); // DELETECOMMAND - retval[160] = new NotImplementedFunction(); // GETCHARTITEM - retval[161] = new NotImplementedFunction(); // DIALOGBOX + retval[151] = new NotImplementedFunction("ADDBAR"); + retval[152] = new NotImplementedFunction("ADDMENU"); + retval[153] = new NotImplementedFunction("ADDCOMMAND"); + retval[154] = new NotImplementedFunction("ENABLECOMMAND"); + retval[155] = new NotImplementedFunction("CHECKCOMMAND"); + retval[156] = new NotImplementedFunction("RENAMECOMMAND"); + retval[157] = new NotImplementedFunction("SHOWBAR"); + retval[158] = new NotImplementedFunction("DELETEMENU"); + retval[159] = new NotImplementedFunction("DELETECOMMAND"); + retval[160] = new NotImplementedFunction("GETCHARTITEM"); + retval[161] = new NotImplementedFunction("DIALOGBOX"); retval[162] = new Clean(); // CLEAN retval[163] = new Mdeterm(); // MDETERM retval[164] = new Minverse(); // MINVERSE @@ -243,24 +240,24 @@ public abstract class FunctionEval implements OperationEval { retval[167] = new Ipmt(); // IPMT retval[168] = new Ppmt(); // PPMT retval[169] = new Counta(); // COUNTA - retval[170] = new NotImplementedFunction(); // CANCELKEY + retval[170] = new NotImplementedFunction("CANCELKEY"); retval[175] = new Initiate(); // INITIATE retval[176] = new Request(); // REQUEST - retval[177] = new NotImplementedFunction(); // POKE - retval[178] = new NotImplementedFunction(); // EXECUTE - retval[179] = new NotImplementedFunction(); // TERMINATE - retval[180] = new NotImplementedFunction(); // RESTART + retval[177] = new NotImplementedFunction("POKE"); + retval[178] = new NotImplementedFunction("EXECUTE"); + retval[179] = new NotImplementedFunction("TERMINATE"); + retval[180] = new NotImplementedFunction("RESTART"); retval[181] = new Help(); // HELP - retval[182] = new NotImplementedFunction(); // GETBAR + retval[182] = new NotImplementedFunction("GETBAR"); retval[183] = AggregateFunction.PRODUCT; retval[184] = NumericFunction.FACT; - retval[185] = new NotImplementedFunction(); // GETCELL - retval[186] = new NotImplementedFunction(); // GETWORKSPACE - retval[187] = new NotImplementedFunction(); // GETWINDOW - retval[188] = new NotImplementedFunction(); // GETDOCUMENT + retval[185] = new NotImplementedFunction("GETCELL"); + retval[186] = new NotImplementedFunction("GETWORKSPACE"); + retval[187] = new NotImplementedFunction("GETWINDOW"); + retval[188] = new NotImplementedFunction("GETDOCUMENT"); retval[189] = new Dproduct(); // DPRODUCT retval[190] = new Isnontext(); // ISNONTEXT - retval[191] = new NotImplementedFunction(); // GETNOTE + retval[191] = new NotImplementedFunction("GETNOTE"); retval[192] = new Note(); // NOTE retval[193] = new Stdevp(); // STDEVP retval[194] = new Varp(); // VARP @@ -269,8 +266,8 @@ public abstract class FunctionEval implements OperationEval { retval[197] = new Trunc(); // TRUNC retval[198] = new Islogical(); // ISLOGICAL retval[199] = new Dcounta(); // DCOUNTA - retval[200] = new NotImplementedFunction(); // DELETEBAR - retval[201] = new NotImplementedFunction(); // UNREGISTER + retval[200] = new NotImplementedFunction("DELETEBAR"); + retval[201] = new NotImplementedFunction("UNREGISTER"); retval[204] = new Usdollar(); // USDOLLAR retval[205] = new Findb(); // FINDB retval[206] = new Searchb(); // SEARCHB @@ -297,37 +294,37 @@ public abstract class FunctionEval implements OperationEval { retval[233] = NumericFunction.ACOSH; retval[234] = NumericFunction.ATANH; retval[235] = new Dget(); // DGET - retval[236] = new NotImplementedFunction(); // CREATEOBJECT + retval[236] = new NotImplementedFunction("CREATEOBJECT"); retval[237] = new Volatile(); // VOLATILE retval[238] = new Lasterror(); // LASTERROR - retval[239] = new NotImplementedFunction(); // CUSTOMUNDO + retval[239] = new NotImplementedFunction("CUSTOMUNDO"); retval[240] = new Customrepeat(); // CUSTOMREPEAT retval[241] = new Formulaconvert(); // FORMULACONVERT - retval[242] = new NotImplementedFunction(); // GETLINKINFO - retval[243] = new NotImplementedFunction(); // TEXTBOX + retval[242] = new NotImplementedFunction("GETLINKINFO"); + retval[243] = new NotImplementedFunction("TEXTBOX"); retval[244] = new Info(); // INFO retval[245] = new Group(); // GROUP - retval[246] = new NotImplementedFunction(); // GETOBJECT + retval[246] = new NotImplementedFunction("GETOBJECT"); retval[247] = new Db(); // DB - retval[248] = new NotImplementedFunction(); // PAUSE - retval[250] = new NotImplementedFunction(); // RESUME + retval[248] = new NotImplementedFunction("PAUSE"); + retval[250] = new NotImplementedFunction("RESUME"); retval[252] = new Frequency(); // FREQUENCY - retval[253] = new NotImplementedFunction(); // ADDTOOLBAR - retval[254] = new NotImplementedFunction(); // DELETETOOLBAR + retval[253] = new NotImplementedFunction("ADDTOOLBAR"); + retval[254] = new NotImplementedFunction("DELETETOOLBAR"); retval[ID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeREfFunction - retval[256] = new NotImplementedFunction(); // RESETTOOLBAR + retval[256] = new NotImplementedFunction("RESETTOOLBAR"); retval[257] = new Evaluate(); // EVALUATE - retval[258] = new NotImplementedFunction(); // GETTOOLBAR - retval[259] = new NotImplementedFunction(); // GETTOOL - retval[260] = new NotImplementedFunction(); // SPELLINGCHECK + retval[258] = new NotImplementedFunction("GETTOOLBAR"); + retval[259] = new NotImplementedFunction("GETTOOL"); + retval[260] = new NotImplementedFunction("SPELLINGCHECK"); retval[261] = new Errortype(); // ERRORTYPE - retval[262] = new NotImplementedFunction(); // APPTITLE - retval[263] = new NotImplementedFunction(); // WINDOWTITLE - retval[264] = new NotImplementedFunction(); // SAVETOOLBAR - retval[265] = new NotImplementedFunction(); // ENABLETOOL - retval[266] = new NotImplementedFunction(); // PRESSTOOL - retval[267] = new NotImplementedFunction(); // REGISTERID - retval[268] = new NotImplementedFunction(); // GETWORKBOOK + retval[262] = new NotImplementedFunction("APPTITLE"); + retval[263] = new NotImplementedFunction("WINDOWTITLE"); + retval[264] = new NotImplementedFunction("SAVETOOLBAR"); + retval[265] = new NotImplementedFunction("ENABLETOOL"); + retval[266] = new NotImplementedFunction("PRESSTOOL"); + retval[267] = new NotImplementedFunction("REGISTERID"); + retval[268] = new NotImplementedFunction("GETWORKBOOK"); retval[269] = AggregateFunction.AVEDEV; retval[270] = new Betadist(); // BETADIST retval[271] = new Gammaln(); // GAMMALN @@ -392,33 +389,33 @@ public abstract class FunctionEval implements OperationEval { retval[330] = new Mode(); // MODE retval[331] = new Trimmean(); // TRIMMEAN retval[332] = new Tinv(); // TINV - retval[334] = new NotImplementedFunction(); // MOVIECOMMAND - retval[335] = new NotImplementedFunction(); // GETMOVIE + retval[334] = new NotImplementedFunction("MOVIECOMMAND"); + retval[335] = new NotImplementedFunction("GETMOVIE"); retval[336] = TextFunction.CONCATENATE; retval[337] = NumericFunction.POWER; - retval[338] = new NotImplementedFunction(); // PIVOTADDDATA - retval[339] = new NotImplementedFunction(); // GETPIVOTTABLE - retval[340] = new NotImplementedFunction(); // GETPIVOTFIELD - retval[341] = new NotImplementedFunction(); // GETPIVOTITEM + retval[338] = new NotImplementedFunction("PIVOTADDDATA"); + retval[339] = new NotImplementedFunction("GETPIVOTTABLE"); + retval[340] = new NotImplementedFunction("GETPIVOTFIELD"); + retval[341] = new NotImplementedFunction("GETPIVOTITEM"); retval[342] = NumericFunction.RADIANS; retval[343] = NumericFunction.DEGREES; retval[344] = new Subtotal(); // SUBTOTAL retval[345] = new Sumif(); // SUMIF retval[346] = new Countif(); // COUNTIF retval[347] = new Countblank(); // COUNTBLANK - retval[348] = new NotImplementedFunction(); // SCENARIOGET - retval[349] = new NotImplementedFunction(); // OPTIONSLISTSGET + retval[348] = new NotImplementedFunction("SCENARIOGET"); + retval[349] = new NotImplementedFunction("OPTIONSLISTSGET"); retval[350] = new Ispmt(); // ISPMT retval[351] = new Datedif(); // DATEDIF retval[352] = new Datestring(); // DATESTRING retval[353] = new Numberstring(); // NUMBERSTRING retval[354] = new Roman(); // ROMAN - retval[355] = new NotImplementedFunction(); // OPENDIALOG - retval[356] = new NotImplementedFunction(); // SAVEDIALOG - retval[357] = new NotImplementedFunction(); // VIEWGET - retval[358] = new NotImplementedFunction(); // GETPIVOTDATA + retval[355] = new NotImplementedFunction("OPENDIALOG"); + retval[356] = new NotImplementedFunction("SAVEDIALOG"); + retval[357] = new NotImplementedFunction("VIEWGET"); + retval[358] = new NotImplementedFunction("GETPIVOTDATA"); retval[359] = new Hyperlink(); // HYPERLINK - retval[360] = new NotImplementedFunction(); // PHONETIC + retval[360] = new NotImplementedFunction("PHONETIC"); retval[361] = new Averagea(); // AVERAGEA retval[362] = MinaMaxa.MAXA; retval[363] = MinaMaxa.MINA; diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java b/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java index 567f29b2b..acac8488d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java @@ -17,10 +17,10 @@ package org.apache.poi.hssf.record.formula.functions; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.ss.formula.EvaluationWorkbook; +import org.apache.poi.ss.formula.eval.NotImplementedException; /** * Implementation for Excel function INDIRECT

@@ -42,7 +42,6 @@ public final class Indirect implements FreeRefFunction { public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) { // TODO - implement INDIRECT() - return ErrorEval.FUNCTION_NOT_IMPLEMENTED; + throw new NotImplementedException("INDIRECT"); } - } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/NotImplementedFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/NotImplementedFunction.java index 49ed84fb4..19643abf6 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/NotImplementedFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/NotImplementedFunction.java @@ -1,41 +1,44 @@ -/* -* 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. -*/ -/* - * Created on May 6, 2005 - * - */ +/* ==================================================================== + 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.functions; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; +import org.apache.poi.ss.formula.eval.NotImplementedException; /** * * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * This is the default implementation of a Function class. - * The default behaviour is to return a non-standard ErrorEval - * "ErrorEval.FUNCTION_NOT_IMPLEMENTED". This error should alert + * The default behaviour is to raise a POI internal error + * ({@link NotImplementedException}). This error should alert * the user that the formula contained a function that is not * yet implemented. */ public class NotImplementedFunction implements Function { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - return ErrorEval.FUNCTION_NOT_IMPLEMENTED; - } - + private final String _functionName; + protected NotImplementedFunction() { + _functionName = getClass().getName(); + } + public NotImplementedFunction(String name) { + _functionName = name; + } + + public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { + throw new NotImplementedException(_functionName); + } } diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index f913f924e..cb26cb247 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -63,6 +63,7 @@ import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.util.CellReference; import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet; +import org.apache.poi.ss.formula.eval.NotImplementedException; import org.apache.poi.ss.usermodel.Cell; /** @@ -220,6 +221,8 @@ public final class WorkbookEvaluator { } tracker.updateCacheResult(result); + } catch (NotImplementedException e) { + throw addExceptionInfo(e, sheetIndex, rowIndex, columnIndex); } finally { tracker.endEvaluate(cce); } @@ -236,6 +239,25 @@ public final class WorkbookEvaluator { } return cce.getValue(); } + + /** + * Adds the current cell reference to the exception for easier debugging. + * Would be nice to get the formula text as well, but that seems to require + * too much digging around and casting to get the FormulaRenderingWorkbook. + */ + private NotImplementedException addExceptionInfo(NotImplementedException inner, int sheetIndex, int rowIndex, int columnIndex) { + + try { + String sheetName = _workbook.getSheetName(sheetIndex); + CellReference cr = new CellReference(sheetName, rowIndex, columnIndex, false, false); + String msg = "Error evaluating cell " + cr.formatAsString(); + return new NotImplementedException(msg, inner); + } catch (Exception e) { + // avoid bombing out during exception handling + e.printStackTrace(); + return inner; // preserve original exception + } + } /** * Gets the value from a non-formula cell. * @param cell may be null diff --git a/src/java/org/apache/poi/ss/formula/eval/NotImplementedException.java b/src/java/org/apache/poi/ss/formula/eval/NotImplementedException.java new file mode 100644 index 000000000..84480758e --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/eval/NotImplementedException.java @@ -0,0 +1,36 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.ss.formula.eval; + +import org.apache.poi.ss.usermodel.FormulaEvaluator; + +/** + * An exception thrown by implementors of {@link FormulaEvaluator} when attempting to evaluate + * a formula which requires features that POI does not (yet) support. + * + * @author Josh Micich + */ +public final class NotImplementedException extends RuntimeException { + + public NotImplementedException(String message) { + super(message); + } + public NotImplementedException(String message, NotImplementedException cause) { + super(message, cause); + } +} diff --git a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls index fd7a61457..8bd07c2f3 100644 Binary files a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls and b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls differ 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 45e49076c..ebd0afb55 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java @@ -28,6 +28,7 @@ import org.apache.poi.hssf.usermodel.HSSFName; 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.eval.NotImplementedException; import org.apache.poi.ss.usermodel.CellValue; /** * @@ -66,11 +67,16 @@ public final class TestExternalFunction extends TestCase { assertEquals("myFunc()", actualFormula); HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); - CellValue evalResult = fe.evaluate(cell); - - // Check the return value from ExternalFunction.evaluate() + // Check out what ExternalFunction.evaluate() does: + CellValue evalResult; + try { + evalResult = fe.evaluate(cell); + } catch (NotImplementedException e) { + assertEquals("Error evaluating cell Sheet1!B1", e.getMessage()); + assertEquals("myFunc", e.getCause().getMessage()); + return; + } // TODO - make this test assert something more interesting as soon as ExternalFunction works a bit better - assertEquals(HSSFCell.CELL_TYPE_ERROR, evalResult.getCellType()); - assertEquals(ErrorEval.FUNCTION_NOT_IMPLEMENTED.getErrorCode(), evalResult.getErrorValue()); + assertNotNull(evalResult); } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java index d47723134..51340af49 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/NumericFunctionInvoker.java @@ -24,6 +24,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.NumericValueEval; import org.apache.poi.hssf.record.formula.eval.OperationEval; +import org.apache.poi.ss.formula.eval.NotImplementedException; /** * Test helper class for invoking functions with numeric results. @@ -83,12 +84,16 @@ public final class NumericFunctionInvoker { throws NumericEvalEx { Eval evalResult; // TODO - make OperationEval extend Function - if (target instanceof Function) { - Function ff = (Function) target; - evalResult = ff.evaluate(args, srcCellRow, (short)srcCellCol); - } else { - OperationEval ff = (OperationEval) target; - evalResult = ff.evaluate(args, srcCellRow, (short)srcCellCol); + try { + if (target instanceof Function) { + Function ff = (Function) target; + evalResult = ff.evaluate(args, srcCellRow, (short)srcCellCol); + } else { + OperationEval ff = (OperationEval) target; + evalResult = ff.evaluate(args, srcCellRow, (short)srcCellCol); + } + } catch (NotImplementedException e) { + throw new NumericEvalEx("Not implemented:" + e.getMessage()); } if(evalResult == null) { @@ -108,9 +113,6 @@ public final class NumericFunctionInvoker { return result.getNumberValue(); } private static String formatErrorMessage(ErrorEval ee) { - if(errorCodesAreEqual(ee, ErrorEval.FUNCTION_NOT_IMPLEMENTED)) { - return "Function not implemented"; - } if(errorCodesAreEqual(ee, ErrorEval.VALUE_INVALID)) { return "Error code: #VALUE! (invalid value)"; }