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 dc5d6c5fe..f5a941d67 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 @@ -17,17 +17,15 @@ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; import org.apache.poi.hssf.record.formula.function.FunctionMetadata; import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; import org.apache.poi.hssf.record.formula.functions.*; -import org.apache.poi.ss.formula.OperationEvaluationContext; import org.apache.poi.ss.formula.eval.NotImplementedException; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > */ -public final class FunctionEval implements OperationEval { +public final class FunctionEval { /** * Some function IDs that require special treatment */ @@ -37,7 +35,7 @@ public final class FunctionEval implements OperationEval { /** 78 */ public static final int OFFSET = 78; /** 148 */ - public static final int INDIRECT = 148; + public static final int INDIRECT = FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT; /** 255 */ public static final int EXTERNAL_FUNC = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL; } @@ -222,33 +220,21 @@ public final class FunctionEval implements OperationEval { } return retval; } - - private AbstractFunctionPtg _delegate; - - public FunctionEval(AbstractFunctionPtg funcPtg) { - _delegate = funcPtg; - } - - public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { - int fidx = _delegate.getFunctionIndex(); + /** + * @return null if the specified functionIndex is for INDIRECT() or any external (add-in) function. + */ + public static Function getBasicFunction(int functionIndex) { // check for 'free ref' functions first - switch (fidx) { + switch (functionIndex) { case FunctionID.INDIRECT: - return Indirect.instance.evaluate(args, ec); case FunctionID.EXTERNAL_FUNC: - return UserDefinedFunction.instance.evaluate(args, ec); + return null; } // else - must be plain function - Function f = functions[fidx]; - if (f == null) { - throw new NotImplementedException("FuncIx=" + fidx); + Function result = functions[functionIndex]; + if (result == null) { + throw new NotImplementedException("FuncIx=" + functionIndex); } - int srcCellRow = ec.getRowIndex(); - int srcCellCol = ec.getColumnIndex(); - return f.evaluate(args, srcCellRow, (short) srcCellCol); - } - - public int getNumberOfOperands() { - return _delegate.getNumberOfOperands(); + return result; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/OperationEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/OperationEval.java deleted file mode 100644 index 481caa0b6..000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/OperationEval.java +++ /dev/null @@ -1,40 +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.eval; - -import org.apache.poi.ss.formula.OperationEvaluationContext; - -/** - * Common interface for implementations of Excel formula operations. - * - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public interface OperationEval { - - /** - * @param args the evaluated operation arguments. Elements of this array typically implement - * {@link ValueEval}. Empty values are represented with {@link BlankEval} or {@link - * MissingArgEval}, never null. - * @param ec used to identify the current cell under evaluation, and potentially to - * dynamically create references - * @return The evaluated result, possibly an {@link ErrorEval}, never null. - */ - ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec); - int getNumberOfOperands(); -} diff --git a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java index 6fb0aabd3..e14366f78 100644 --- a/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java +++ b/src/java/org/apache/poi/hssf/record/formula/function/FunctionMetadataRegistry.java @@ -31,6 +31,7 @@ public final class FunctionMetadataRegistry { public static final String FUNCTION_NAME_IF = "IF"; public static final short FUNCTION_INDEX_SUM = 4; + public static final short FUNCTION_INDEX_INDIRECT = 148; public static final short FUNCTION_INDEX_EXTERNAL = 255; private static FunctionMetadataRegistry _instance; diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java index e416a7ee8..87cef4f9c 100644 --- a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java +++ b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java @@ -44,7 +44,6 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg; import org.apache.poi.hssf.record.formula.eval.ConcatEval; import org.apache.poi.hssf.record.formula.eval.FunctionEval; import org.apache.poi.hssf.record.formula.eval.IntersectionEval; -import org.apache.poi.hssf.record.formula.eval.OperationEval; import org.apache.poi.hssf.record.formula.eval.PercentEval; import org.apache.poi.hssf.record.formula.eval.RangeEval; import org.apache.poi.hssf.record.formula.eval.RelationalOperationEval; @@ -52,7 +51,9 @@ import org.apache.poi.hssf.record.formula.eval.TwoOperandNumericOperation; import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval; import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; import org.apache.poi.hssf.record.formula.functions.Function; +import org.apache.poi.hssf.record.formula.functions.Indirect; /** * This class creates OperationEval instances to help evaluate OperationPtg @@ -62,85 +63,73 @@ import org.apache.poi.hssf.record.formula.functions.Function; */ final class OperationEvaluatorFactory { - private static final Map _instancesByPtgClass = initialiseInstancesMap2(); + private static final Map _instancesByPtgClass = initialiseInstancesMap(); private OperationEvaluatorFactory() { // no instances of this class } - private static Map initialiseInstancesMap2() { - Map m = new HashMap(32); + private static Map initialiseInstancesMap() { + Map m = new HashMap(32); - put(m, 2, EqualPtg.instance, RelationalOperationEval.EqualEval); - put(m, 2, GreaterEqualPtg.instance, RelationalOperationEval.GreaterEqualEval); - put(m, 2, GreaterThanPtg.instance, RelationalOperationEval.GreaterThanEval); - put(m, 2, LessEqualPtg.instance, RelationalOperationEval.LessEqualEval); - put(m, 2, LessThanPtg.instance, RelationalOperationEval.LessThanEval); - put(m, 2, NotEqualPtg.instance, RelationalOperationEval.NotEqualEval); + put(m, EqualPtg.instance, RelationalOperationEval.EqualEval); + put(m, GreaterEqualPtg.instance, RelationalOperationEval.GreaterEqualEval); + put(m, GreaterThanPtg.instance, RelationalOperationEval.GreaterThanEval); + put(m, LessEqualPtg.instance, RelationalOperationEval.LessEqualEval); + put(m, LessThanPtg.instance, RelationalOperationEval.LessThanEval); + put(m, NotEqualPtg.instance, RelationalOperationEval.NotEqualEval); - put(m, 2, ConcatPtg.instance, ConcatEval.instance); - put(m, 2, AddPtg.instance, TwoOperandNumericOperation.AddEval); - put(m, 2, DividePtg.instance, TwoOperandNumericOperation.DivideEval); - put(m, 2, MultiplyPtg.instance, TwoOperandNumericOperation.MultiplyEval); - put(m, 1, PercentPtg.instance, PercentEval.instance); - put(m, 2, PowerPtg.instance, TwoOperandNumericOperation.PowerEval); - put(m, 2, SubtractPtg.instance, TwoOperandNumericOperation.SubtractEval); - put(m, 1, UnaryMinusPtg.instance, UnaryMinusEval.instance); - put(m, 1, UnaryPlusPtg.instance, UnaryPlusEval.instance); - put(m, 2, RangePtg.instance, RangeEval.instance); - put(m, 2, IntersectionPtg.instance, IntersectionEval.instance); + put(m, ConcatPtg.instance, ConcatEval.instance); + put(m, AddPtg.instance, TwoOperandNumericOperation.AddEval); + put(m, DividePtg.instance, TwoOperandNumericOperation.DivideEval); + put(m, MultiplyPtg.instance, TwoOperandNumericOperation.MultiplyEval); + put(m, PercentPtg.instance, PercentEval.instance); + put(m, PowerPtg.instance, TwoOperandNumericOperation.PowerEval); + put(m, SubtractPtg.instance, TwoOperandNumericOperation.SubtractEval); + put(m, UnaryMinusPtg.instance, UnaryMinusEval.instance); + put(m, UnaryPlusPtg.instance, UnaryPlusEval.instance); + put(m, RangePtg.instance, RangeEval.instance); + put(m, IntersectionPtg.instance, IntersectionEval.instance); return m; } - private static void put(Map m, int argCount, - OperationPtg ptgKey, Function instance) { + private static void put(Map m, OperationPtg ptgKey, + Function instance) { // make sure ptg has single private constructor because map lookups assume singleton keys Constructor[] cc = ptgKey.getClass().getDeclaredConstructors(); if (cc.length > 1 || !Modifier.isPrivate(cc[0].getModifiers())) { throw new RuntimeException("Failed to verify instance (" + ptgKey.getClass().getName() + ") is a singleton."); } - m.put(ptgKey, new OperationFunctionEval(instance, argCount)); - } - - /** - * Simple adapter from {@link OperationEval} to {@link Function} - */ - private static final class OperationFunctionEval implements OperationEval { - - private final Function _function; - private final int _numberOfOperands; - - public OperationFunctionEval(Function function, int argCount) { - _function = function; - _numberOfOperands = argCount; - } - - public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) { - return _function.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex()); - } - - public int getNumberOfOperands() { - return _numberOfOperands; - } + m.put(ptgKey, instance); } /** * returns the OperationEval concrete impl instance corresponding * to the supplied operationPtg */ - public static OperationEval create(OperationPtg ptg) { + public static ValueEval evaluate(OperationPtg ptg, ValueEval[] args, + OperationEvaluationContext ec) { if(ptg == null) { throw new IllegalArgumentException("ptg must not be null"); } - OperationEval result = _instancesByPtgClass.get(ptg); + Function result = _instancesByPtgClass.get(ptg); if (result != null) { - return result; + return result.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex()); } if (ptg instanceof AbstractFunctionPtg) { - return new FunctionEval((AbstractFunctionPtg)ptg); + AbstractFunctionPtg fptg = (AbstractFunctionPtg)ptg; + int functionIndex = fptg.getFunctionIndex(); + switch (functionIndex) { + case FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT: + return Indirect.instance.evaluate(args, ec); + case FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL: + return UserDefinedFunction.instance.evaluate(args, ec); + } + + return FunctionEval.getBasicFunction(functionIndex).evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex()); } throw new RuntimeException("Unexpected operation ptg class (" + ptg.getClass().getName() + ")"); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java b/src/java/org/apache/poi/ss/formula/UserDefinedFunction.java similarity index 92% rename from src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java rename to src/java/org/apache/poi/ss/formula/UserDefinedFunction.java index 223f13378..7bf612a47 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/UserDefinedFunction.java +++ b/src/java/org/apache/poi/ss/formula/UserDefinedFunction.java @@ -15,8 +15,11 @@ limitations under the License. ==================================================================== */ -package org.apache.poi.hssf.record.formula.eval; +package org.apache.poi.ss.formula; +import org.apache.poi.hssf.record.formula.eval.NameEval; +import org.apache.poi.hssf.record.formula.eval.NameXEval; +import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.ss.formula.OperationEvaluationContext; import org.apache.poi.ss.formula.eval.NotImplementedException; diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index 18d8c11d4..8fc1f3eb9 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -57,7 +57,6 @@ import org.apache.poi.hssf.record.formula.eval.MissingArgEval; import org.apache.poi.hssf.record.formula.eval.NameEval; import org.apache.poi.hssf.record.formula.eval.NameXEval; import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.OperationEval; import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; @@ -425,9 +424,8 @@ public final class WorkbookEvaluator { if (optg instanceof UnionPtg) { continue; } - OperationEval operation = OperationEvaluatorFactory.create(optg); - int numops = operation.getNumberOfOperands(); + int numops = optg.getNumberOfOperands(); ValueEval[] ops = new ValueEval[numops]; // storing the ops in reverse order since they are popping @@ -436,7 +434,7 @@ public final class WorkbookEvaluator { ops[j] = p; } // logDebug("invoke " + operation + " (nAgs=" + numops + ")"); - opResult = operation.evaluate(ops, ec); + opResult = OperationEvaluatorFactory.evaluate(optg, ops, ec); if (opResult == MissingArgEval.instance) { opResult = BlankEval.INSTANCE; }