git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@880593 13f79535-47bb-0310-9956-ffa450edef68pull/1/head
@@ -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 <code>null</code> 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; | |||
} | |||
} |
@@ -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 <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(); | |||
} |
@@ -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; | |||
@@ -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 <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 { | |||
private static final Map<OperationPtg, OperationEval> _instancesByPtgClass = initialiseInstancesMap2(); | |||
private static final Map<OperationPtg, Function> _instancesByPtgClass = initialiseInstancesMap(); | |||
private OperationEvaluatorFactory() { | |||
// no instances of this class | |||
} | |||
private static Map<OperationPtg, OperationEval> initialiseInstancesMap2() { | |||
Map<OperationPtg, OperationEval> m = new HashMap<OperationPtg, OperationEval>(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, 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); | |||
private static Map<OperationPtg, Function> initialiseInstancesMap() { | |||
Map<OperationPtg, Function> m = new HashMap<OperationPtg, Function>(32); | |||
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, 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<OperationPtg, OperationEval> m, int argCount, | |||
OperationPtg ptgKey, Function instance) { | |||
private static void put(Map<OperationPtg, Function> 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() + ")"); | |||
} |
@@ -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; |
@@ -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; | |||
} |