Removed OperationEval and rearranged function/operator evaluation.

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@880593 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2009-11-16 05:41:57 +00:00
parent 5630df07a4
commit 518ae59f07
6 changed files with 58 additions and 121 deletions

View File

@ -17,17 +17,15 @@
package org.apache.poi.hssf.record.formula.eval; 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.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry; import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.hssf.record.formula.functions.*; import org.apache.poi.hssf.record.formula.functions.*;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.NotImplementedException; import org.apache.poi.ss.formula.eval.NotImplementedException;
/** /**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > * @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 * Some function IDs that require special treatment
*/ */
@ -37,7 +35,7 @@ public final class FunctionEval implements OperationEval {
/** 78 */ /** 78 */
public static final int OFFSET = 78; public static final int OFFSET = 78;
/** 148 */ /** 148 */
public static final int INDIRECT = 148; public static final int INDIRECT = FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT;
/** 255 */ /** 255 */
public static final int EXTERNAL_FUNC = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL; public static final int EXTERNAL_FUNC = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL;
} }
@ -222,33 +220,21 @@ public final class FunctionEval implements OperationEval {
} }
return retval; return retval;
} }
/**
private AbstractFunctionPtg _delegate; * @return <code>null</code> if the specified functionIndex is for INDIRECT() or any external (add-in) function.
*/
public FunctionEval(AbstractFunctionPtg funcPtg) { public static Function getBasicFunction(int functionIndex) {
_delegate = funcPtg;
}
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
int fidx = _delegate.getFunctionIndex();
// check for 'free ref' functions first // check for 'free ref' functions first
switch (fidx) { switch (functionIndex) {
case FunctionID.INDIRECT: case FunctionID.INDIRECT:
return Indirect.instance.evaluate(args, ec);
case FunctionID.EXTERNAL_FUNC: case FunctionID.EXTERNAL_FUNC:
return UserDefinedFunction.instance.evaluate(args, ec); return null;
} }
// else - must be plain function // else - must be plain function
Function f = functions[fidx]; Function result = functions[functionIndex];
if (f == null) { if (result == null) {
throw new NotImplementedException("FuncIx=" + fidx); throw new NotImplementedException("FuncIx=" + functionIndex);
} }
int srcCellRow = ec.getRowIndex(); return result;
int srcCellCol = ec.getColumnIndex();
return f.evaluate(args, srcCellRow, (short) srcCellCol);
}
public int getNumberOfOperands() {
return _delegate.getNumberOfOperands();
} }
} }

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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 <code>null</code>.
* @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 <code>null</code>.
*/
ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec);
int getNumberOfOperands();
}

View File

@ -31,6 +31,7 @@ public final class FunctionMetadataRegistry {
public static final String FUNCTION_NAME_IF = "IF"; public static final String FUNCTION_NAME_IF = "IF";
public static final short FUNCTION_INDEX_SUM = 4; public static final short FUNCTION_INDEX_SUM = 4;
public static final short FUNCTION_INDEX_INDIRECT = 148;
public static final short FUNCTION_INDEX_EXTERNAL = 255; public static final short FUNCTION_INDEX_EXTERNAL = 255;
private static FunctionMetadataRegistry _instance; private static FunctionMetadataRegistry _instance;

View File

@ -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.ConcatEval;
import org.apache.poi.hssf.record.formula.eval.FunctionEval; 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.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.PercentEval;
import org.apache.poi.hssf.record.formula.eval.RangeEval; import org.apache.poi.hssf.record.formula.eval.RangeEval;
import org.apache.poi.hssf.record.formula.eval.RelationalOperationEval; 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.UnaryMinusEval;
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval; 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.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.Function;
import org.apache.poi.hssf.record.formula.functions.Indirect;
/** /**
* This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt> * This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
@ -62,85 +63,73 @@ import org.apache.poi.hssf.record.formula.functions.Function;
*/ */
final class OperationEvaluatorFactory { final class OperationEvaluatorFactory {
private static final Map<OperationPtg, OperationEval> _instancesByPtgClass = initialiseInstancesMap2(); private static final Map<OperationPtg, Function> _instancesByPtgClass = initialiseInstancesMap();
private OperationEvaluatorFactory() { private OperationEvaluatorFactory() {
// no instances of this class // no instances of this class
} }
private static Map<OperationPtg, OperationEval> initialiseInstancesMap2() { private static Map<OperationPtg, Function> initialiseInstancesMap() {
Map<OperationPtg, OperationEval> m = new HashMap<OperationPtg, OperationEval>(32); Map<OperationPtg, Function> m = new HashMap<OperationPtg, Function>(32);
put(m, 2, EqualPtg.instance, RelationalOperationEval.EqualEval); put(m, EqualPtg.instance, RelationalOperationEval.EqualEval);
put(m, 2, GreaterEqualPtg.instance, RelationalOperationEval.GreaterEqualEval); put(m, GreaterEqualPtg.instance, RelationalOperationEval.GreaterEqualEval);
put(m, 2, GreaterThanPtg.instance, RelationalOperationEval.GreaterThanEval); put(m, GreaterThanPtg.instance, RelationalOperationEval.GreaterThanEval);
put(m, 2, LessEqualPtg.instance, RelationalOperationEval.LessEqualEval); put(m, LessEqualPtg.instance, RelationalOperationEval.LessEqualEval);
put(m, 2, LessThanPtg.instance, RelationalOperationEval.LessThanEval); put(m, LessThanPtg.instance, RelationalOperationEval.LessThanEval);
put(m, 2, NotEqualPtg.instance, RelationalOperationEval.NotEqualEval); put(m, NotEqualPtg.instance, RelationalOperationEval.NotEqualEval);
put(m, 2, ConcatPtg.instance, ConcatEval.instance); put(m, ConcatPtg.instance, ConcatEval.instance);
put(m, 2, AddPtg.instance, TwoOperandNumericOperation.AddEval); put(m, AddPtg.instance, TwoOperandNumericOperation.AddEval);
put(m, 2, DividePtg.instance, TwoOperandNumericOperation.DivideEval); put(m, DividePtg.instance, TwoOperandNumericOperation.DivideEval);
put(m, 2, MultiplyPtg.instance, TwoOperandNumericOperation.MultiplyEval); put(m, MultiplyPtg.instance, TwoOperandNumericOperation.MultiplyEval);
put(m, 1, PercentPtg.instance, PercentEval.instance); put(m, PercentPtg.instance, PercentEval.instance);
put(m, 2, PowerPtg.instance, TwoOperandNumericOperation.PowerEval); put(m, PowerPtg.instance, TwoOperandNumericOperation.PowerEval);
put(m, 2, SubtractPtg.instance, TwoOperandNumericOperation.SubtractEval); put(m, SubtractPtg.instance, TwoOperandNumericOperation.SubtractEval);
put(m, 1, UnaryMinusPtg.instance, UnaryMinusEval.instance); put(m, UnaryMinusPtg.instance, UnaryMinusEval.instance);
put(m, 1, UnaryPlusPtg.instance, UnaryPlusEval.instance); put(m, UnaryPlusPtg.instance, UnaryPlusEval.instance);
put(m, 2, RangePtg.instance, RangeEval.instance); put(m, RangePtg.instance, RangeEval.instance);
put(m, 2, IntersectionPtg.instance, IntersectionEval.instance); put(m, IntersectionPtg.instance, IntersectionEval.instance);
return m; return m;
} }
private static void put(Map<OperationPtg, OperationEval> m, int argCount, private static void put(Map<OperationPtg, Function> m, OperationPtg ptgKey,
OperationPtg ptgKey, Function instance) { Function instance) {
// make sure ptg has single private constructor because map lookups assume singleton keys // make sure ptg has single private constructor because map lookups assume singleton keys
Constructor[] cc = ptgKey.getClass().getDeclaredConstructors(); Constructor[] cc = ptgKey.getClass().getDeclaredConstructors();
if (cc.length > 1 || !Modifier.isPrivate(cc[0].getModifiers())) { if (cc.length > 1 || !Modifier.isPrivate(cc[0].getModifiers())) {
throw new RuntimeException("Failed to verify instance (" throw new RuntimeException("Failed to verify instance ("
+ ptgKey.getClass().getName() + ") is a singleton."); + ptgKey.getClass().getName() + ") is a singleton.");
} }
m.put(ptgKey, new OperationFunctionEval(instance, argCount)); m.put(ptgKey, instance);
}
/**
* 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;
}
} }
/** /**
* returns the OperationEval concrete impl instance corresponding * returns the OperationEval concrete impl instance corresponding
* to the supplied operationPtg * to the supplied operationPtg
*/ */
public static OperationEval create(OperationPtg ptg) { public static ValueEval evaluate(OperationPtg ptg, ValueEval[] args,
OperationEvaluationContext ec) {
if(ptg == null) { if(ptg == null) {
throw new IllegalArgumentException("ptg must not be null"); throw new IllegalArgumentException("ptg must not be null");
} }
OperationEval result = _instancesByPtgClass.get(ptg); Function result = _instancesByPtgClass.get(ptg);
if (result != null) { if (result != null) {
return result; return result.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex());
} }
if (ptg instanceof AbstractFunctionPtg) { 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() + ")"); throw new RuntimeException("Unexpected operation ptg class (" + ptg.getClass().getName() + ")");
} }

View File

@ -15,8 +15,11 @@
limitations under the License. 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.hssf.record.formula.functions.FreeRefFunction;
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;

View File

@ -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.NameEval;
import org.apache.poi.hssf.record.formula.eval.NameXEval; 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.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.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;
@ -425,9 +424,8 @@ public final class WorkbookEvaluator {
if (optg instanceof UnionPtg) { continue; } if (optg instanceof UnionPtg) { continue; }
OperationEval operation = OperationEvaluatorFactory.create(optg);
int numops = operation.getNumberOfOperands(); int numops = optg.getNumberOfOperands();
ValueEval[] ops = new ValueEval[numops]; ValueEval[] ops = new ValueEval[numops];
// storing the ops in reverse order since they are popping // storing the ops in reverse order since they are popping
@ -436,7 +434,7 @@ public final class WorkbookEvaluator {
ops[j] = p; ops[j] = p;
} }
// logDebug("invoke " + operation + " (nAgs=" + numops + ")"); // logDebug("invoke " + operation + " (nAgs=" + numops + ")");
opResult = operation.evaluate(ops, ec); opResult = OperationEvaluatorFactory.evaluate(optg, ops, ec);
if (opResult == MissingArgEval.instance) { if (opResult == MissingArgEval.instance) {
opResult = BlankEval.INSTANCE; opResult = BlankEval.INSTANCE;
} }