From 7d7e5d1515f0659e58bf5f252f872d01c666b43c Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Mon, 23 Nov 2009 00:19:11 +0000 Subject: [PATCH] updated remaining evaluator functions to implement fixed args interfaces git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@883197 13f79535-47bb-0310-9956-ffa450edef68 --- .../record/formula/eval/FunctionEval.java | 31 ++-- .../formula/functions/AggregateFunction.java | 80 +++++++--- .../hssf/record/formula/functions/And.java | 32 ---- .../formula/functions/BooleanFunction.java | 42 +++++ .../formula/functions/FinanceFunction.java | 49 +++++- .../record/formula/functions/Hlookup.java | 32 ++-- .../record/formula/functions/Hyperlink.java | 25 ++- .../hssf/record/formula/functions/Index.java | 97 +++++++----- .../record/formula/functions/IsError.java | 42 ----- .../hssf/record/formula/functions/IsNa.java | 56 ------- .../record/formula/functions/Isblank.java | 47 ------ .../hssf/record/formula/functions/Isref.java | 42 ----- .../formula/functions/LogicalFunction.java | 81 ++++++---- .../record/formula/functions/LookupUtils.java | 11 +- .../hssf/record/formula/functions/Not.java | 50 ------ .../hssf/record/formula/functions/Npv.java | 78 ++++++++-- .../formula/functions/NumericFunction.java | 146 ++++++++++++------ .../poi/hssf/record/formula/functions/Or.java | 32 ---- .../poi/hssf/record/formula/functions/Pi.java | 44 ------ .../hssf/record/formula/functions/Rand.java | 42 ----- .../record/formula/functions/Substitute.java | 49 +++--- .../formula/functions/TextFunction.java | 89 ++++++----- .../{True.java => Var1or2ArgFunction.java} | 28 ++-- .../{False.java => Var3or4ArgFunction.java} | 28 ++-- .../record/formula/functions/Vlookup.java | 31 ++-- .../formula/OperationEvaluationContext.java | 2 +- 26 files changed, 587 insertions(+), 699 deletions(-) delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/And.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/IsError.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/IsNa.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Isblank.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Isref.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Not.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Or.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Pi.java delete mode 100644 src/java/org/apache/poi/hssf/record/formula/functions/Rand.java rename src/java/org/apache/poi/hssf/record/formula/functions/{True.java => Var1or2ArgFunction.java} (69%) rename src/java/org/apache/poi/hssf/record/formula/functions/{False.java => Var3or4ArgFunction.java} (68%) 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 cfe88369e4..1fa5a38614 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 @@ -52,8 +52,8 @@ public final class FunctionEval { retval[0] = new Count(); retval[1] = new If(); - retval[2] = new IsNa(); - retval[3] = new IsError(); + retval[2] = LogicalFunction.ISNA; + retval[3] = LogicalFunction.ISERROR; retval[ID.SUM] = AggregateFunction.SUM; retval[5] = AggregateFunction.AVERAGE; retval[6] = AggregateFunction.MIN; @@ -69,7 +69,7 @@ public final class FunctionEval { retval[16] = NumericFunction.COS; retval[17] = NumericFunction.TAN; retval[18] = NumericFunction.ATAN; - retval[19] = new Pi(); + retval[19] = NumericFunction.PI; retval[20] = NumericFunction.SQRT; retval[21] = NumericFunction.EXP; retval[22] = NumericFunction.LN; @@ -84,11 +84,11 @@ public final class FunctionEval { retval[31] = TextFunction.MID; retval[32] = TextFunction.LEN; retval[33] = new Value(); - retval[34] = new True(); - retval[35] = new False(); - retval[36] = new And(); - retval[37] = new Or(); - retval[38] = new Not(); + retval[34] = BooleanFunction.TRUE; + retval[35] = BooleanFunction.FALSE; + retval[36] = BooleanFunction.AND; + retval[37] = BooleanFunction.OR; + retval[38] = BooleanFunction.NOT; retval[39] = NumericFunction.MOD; retval[56] = FinanceFunction.PV; @@ -96,7 +96,7 @@ public final class FunctionEval { retval[58] = FinanceFunction.NPER; retval[59] = FinanceFunction.PMT; - retval[63] = new Rand(); + retval[63] = NumericFunction.RAND; retval[64] = new Match(); retval[65] = DateFunc.instance; retval[66] = new Time(); @@ -108,6 +108,7 @@ public final class FunctionEval { retval[76] = new Rows(); retval[77] = new Columns(); + retval[82] = TextFunction.SEARCH; retval[ID.OFFSET] = new Offset(); retval[82] = TextFunction.SEARCH; @@ -118,7 +119,7 @@ public final class FunctionEval { retval[101] = new Hlookup(); retval[102] = new Vlookup(); - retval[105] = new Isref(); + retval[105] = LogicalFunction.ISREF; retval[109] = NumericFunction.LOG; @@ -134,9 +135,9 @@ public final class FunctionEval { retval[124] = TextFunction.FIND; - retval[127] = LogicalFunction.IsText; - retval[128] = LogicalFunction.IsNumber; - retval[129] = new Isblank(); + retval[127] = LogicalFunction.ISTEXT; + retval[128] = LogicalFunction.ISNUMBER; + retval[129] = LogicalFunction.ISBLANK; retval[130] = new T(); retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature @@ -146,9 +147,9 @@ public final class FunctionEval { retval[183] = AggregateFunction.PRODUCT; retval[184] = NumericFunction.FACT; - retval[190] = LogicalFunction.IsNonText; + retval[190] = LogicalFunction.ISNONTEXT; - retval[198] = LogicalFunction.IsLogical; + retval[198] = LogicalFunction.ISLOGICAL; retval[212] = NumericFunction.ROUNDUP; retval[213] = NumericFunction.ROUNDDOWN; diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java index 6397ea39f5..2df9dd3e57 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java @@ -19,13 +19,67 @@ 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.EvaluationException; +import org.apache.poi.hssf.record.formula.eval.NumberEval; +import org.apache.poi.hssf.record.formula.eval.OperandResolver; +import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ public abstract class AggregateFunction extends MultiOperandNumericFunction { + private static final class LargeSmall extends Fixed2ArgFunction { + private final boolean _isLarge; + protected LargeSmall(boolean isLarge) { + _isLarge = isLarge; + } + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1) { + double dn; + try { + ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex); + dn = OperandResolver.coerceValueToDouble(ve1); + } catch (EvaluationException e1) { + // all errors in the second arg translate to #VALUE! + return ErrorEval.VALUE_INVALID; + } + // weird Excel behaviour on second arg + if (dn < 1.0) { + // values between 0.0 and 1.0 result in #NUM! + return ErrorEval.NUM_ERROR; + } + // all other values are rounded up to the next integer + int k = (int) Math.ceil(dn); + + double result; + try { + double[] ds = ValueCollector.collectValues(arg0); + if (k > ds.length) { + return ErrorEval.NUM_ERROR; + } + result = _isLarge ? StatsLib.kthLargest(ds, k) : StatsLib.kthSmallest(ds, k); + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + return new NumberEval(result); + } + } + private static final class ValueCollector extends MultiOperandNumericFunction { + private static final ValueCollector instance = new ValueCollector(); + public ValueCollector() { + super(false, false); + } + public static double[] collectValues(ValueEval...operands) throws EvaluationException { + return instance.getNumberArray(operands); + } + protected double evaluate(double[] values) { + throw new IllegalStateException("should not be called"); + } + } + protected AggregateFunction() { super(false, false); } @@ -48,17 +102,7 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction { return StatsLib.devsq(values); } }; - public static final Function LARGE = new AggregateFunction() { - protected double evaluate(double[] ops) throws EvaluationException { - if (ops.length < 2) { - throw new EvaluationException(ErrorEval.NUM_ERROR); - } - double[] values = new double[ops.length-1]; - int k = (int) ops[ops.length-1]; - System.arraycopy(ops, 0, values, 0, values.length); - return StatsLib.kthLargest(values, k); - } - }; + public static final Function LARGE = new LargeSmall(true); public static final Function MAX = new AggregateFunction() { protected double evaluate(double[] values) { return values.length > 0 ? MathX.max(values) : 0; @@ -79,17 +123,7 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction { return MathX.product(values); } }; - public static final Function SMALL = new AggregateFunction() { - protected double evaluate(double[] ops) throws EvaluationException { - if (ops.length < 2) { - throw new EvaluationException(ErrorEval.NUM_ERROR); - } - double[] values = new double[ops.length-1]; - int k = (int) ops[ops.length-1]; - System.arraycopy(ops, 0, values, 0, values.length); - return StatsLib.kthSmallest(values, k); - } - }; + public static final Function SMALL = new LargeSmall(false); public static final Function STDEV = new AggregateFunction() { protected double evaluate(double[] values) throws EvaluationException { if (values.length < 1) { diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/And.java b/src/java/org/apache/poi/hssf/record/formula/functions/And.java deleted file mode 100644 index e024562865..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/And.java +++ /dev/null @@ -1,32 +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.functions; - -/** - * - */ -public final class And extends BooleanFunction { - - protected boolean getInitialResultValue() { - return true; - } - - protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) { - return cumulativeResult && currentValue; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/BooleanFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/BooleanFunction.java index 4e959a61e5..0165e501a5 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/BooleanFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/BooleanFunction.java @@ -101,4 +101,46 @@ public abstract class BooleanFunction implements Function { protected abstract boolean getInitialResultValue(); protected abstract boolean partialEvaluate(boolean cumulativeResult, boolean currentValue); + + + public static final Function AND = new BooleanFunction() { + protected boolean getInitialResultValue() { + return true; + } + protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) { + return cumulativeResult && currentValue; + } + }; + public static final Function OR = new BooleanFunction() { + protected boolean getInitialResultValue() { + return false; + } + protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) { + return cumulativeResult || currentValue; + } + }; + public static final Function FALSE = new Fixed0ArgFunction() { + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) { + return BoolEval.FALSE; + } + }; + public static final Function TRUE = new Fixed0ArgFunction() { + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) { + return BoolEval.TRUE; + } + }; + public static final Function NOT = new Fixed1ArgFunction() { + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { + boolean boolArgVal; + try { + ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); + Boolean b = OperandResolver.coerceValueToBoolean(ve, false); + boolArgVal = b == null ? false : b.booleanValue(); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + return BoolEval.valueOf(!boolArgVal); + } + }; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java index 2de9ab1350..c101f2dc9e 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java @@ -17,17 +17,58 @@ package org.apache.poi.hssf.record.formula.functions; +import org.apache.poi.hssf.record.formula.eval.BoolEval; +import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.EvaluationException; +import org.apache.poi.hssf.record.formula.eval.NumberEval; +import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * Super class for all Evals for financial function evaluation. - * */ -public abstract class FinanceFunction extends NumericFunction.MultiArg { +public abstract class FinanceFunction implements Function3Arg, Function4Arg { + private static final ValueEval DEFAULT_ARG3 = NumberEval.ZERO; + private static final ValueEval DEFAULT_ARG4 = BoolEval.FALSE; + protected FinanceFunction() { - super (3, 5); + // no instance fields + } + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2) { + return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, DEFAULT_ARG3); + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2, ValueEval arg3) { + return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, arg3, DEFAULT_ARG4); + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2, ValueEval arg3, ValueEval arg4) { + double result; + try { + double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); + double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex); + double d3 = NumericFunction.singleOperandEvaluate(arg3, srcRowIndex, srcColumnIndex); + double d4 = NumericFunction.singleOperandEvaluate(arg4, srcRowIndex, srcColumnIndex); + result = evaluate(d0, d1, d2, d3, d4 != 0.0); + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + switch (args.length) { + case 3: + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], DEFAULT_ARG3, DEFAULT_ARG4); + case 4: + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3], DEFAULT_ARG4); + case 5: + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3], args[4]); + } + return ErrorEval.VALUE_INVALID; } protected double evaluate(double[] ds) throws EvaluationException { diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java index 0a25044f33..03faa25b55 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java @@ -18,7 +18,7 @@ package org.apache.poi.hssf.record.formula.functions; 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.BoolEval; import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.ValueEval; @@ -39,27 +39,24 @@ import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector; * * @author Josh Micich */ -public final class Hlookup implements Function { +public final class Hlookup extends Var3or4ArgFunction { + private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE; - public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { - ValueEval arg3 = null; - switch(args.length) { - case 4: - arg3 = args[3]; // important: assumed array element is never null - case 3: - break; - default: - // wrong number of arguments - return ErrorEval.VALUE_INVALID; - } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2) { + return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, DEFAULT_ARG3); + } + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2, ValueEval arg3) { try { // Evaluation order: // arg0 lookup_value, arg1 table_array, arg3 range_lookup, find lookup value, arg2 row_index, fetch result - ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); - AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); - boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); + ValueEval lookupValue = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); + AreaEval tableArray = LookupUtils.resolveTableArrayArg(arg1); + boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcRowIndex, srcColumnIndex); int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup); - int rowIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol); + int rowIndex = LookupUtils.resolveRowOrColIndexArg(arg2, srcRowIndex, srcColumnIndex); ValueVector resultCol = createResultColumnVector(tableArray, rowIndex); return resultCol.getItem(colIndex); } catch (EvaluationException e) { @@ -67,7 +64,6 @@ public final class Hlookup implements Function { } } - /** * Returns one column from an AreaEval * diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Hyperlink.java b/src/java/org/apache/poi/hssf/record/formula/functions/Hyperlink.java index 703af9716c..761c5f3a63 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Hyperlink.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Hyperlink.java @@ -17,9 +17,6 @@ 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.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; @@ -36,21 +33,17 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * friendly_name (optional) the value to display

* * Returns last argument. Leaves type unchanged (does not convert to {@link StringEval}). - + * * @author Wayne Clingingsmith */ -public final class Hyperlink implements Function { +public final class Hyperlink extends Var1or2ArgFunction { - public ValueEval evaluate(ValueEval[] operands, int srcRow, int srcCol) { - int lastArgIx = operands.length - 1; - if (lastArgIx < 0 || lastArgIx > 1) { - return ErrorEval.VALUE_INVALID; - } - - try { - return OperandResolver.getSingleValue(operands[lastArgIx], srcRow, srcCol); - } catch (EvaluationException e) { - return e.getErrorEval(); - } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { + return arg0; + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { + // note - if last arg is MissingArgEval, result will be NumberEval.ZERO, + // but WorkbookEvaluator does that translation + return arg1; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java index 49e7484fa7..ad1dc9dd2b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java @@ -44,56 +44,71 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * * @author Josh Micich */ -public final class Index implements Function { +public final class Index implements Function2Arg, Function3Arg, Function4Arg { - public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { - int nArgs = args.length; - if(nArgs < 2) { - // too few arguments - return ErrorEval.VALUE_INVALID; - } - ValueEval firstArg = args[0]; - if (firstArg instanceof RefEval) { - // convert to area ref for simpler code in getValueFromArea() - firstArg = ((RefEval)firstArg).offset(0, 0, 0, 0); - } - if(!(firstArg instanceof AreaEval)) { + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { + AreaEval reference = convertFirstArg(arg0); - // else the other variation of this function takes an array as the first argument - // it seems like interface 'ArrayEval' does not even exist yet - throw new RuntimeException("Incomplete code - cannot handle first arg of type (" - + firstArg.getClass().getName() + ")"); + boolean colArgWasPassed = false; + int columnIx = 0; + try { + int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex); + return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcRowIndex, srcColumnIndex); + } catch (EvaluationException e) { + return e.getErrorEval(); } - AreaEval reference = (AreaEval) firstArg; + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2) { + AreaEval reference = convertFirstArg(arg0); - int rowIx = 0; - int columnIx = 0; - boolean colArgWasPassed = false; + boolean colArgWasPassed = true; try { - switch(nArgs) { - case 4: - throw new RuntimeException("Incomplete code" + - " - don't know how to support the 'area_num' parameter yet)"); - // Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3) - // In this example, the 3rd area would be used i.e. D2:E5, and the overall result would be E2 - // Token array might be encoded like this: MemAreaPtg, AreaPtg, AreaPtg, UnionPtg, UnionPtg, ParenthesesPtg - // The formula parser doesn't seem to support this yet. Not sure if the evaluator does either - - case 3: - columnIx = resolveIndexArg(args[2], srcCellRow, srcCellCol); - colArgWasPassed = true; - case 2: - rowIx = resolveIndexArg(args[1], srcCellRow, srcCellCol); - break; - default: - // too many arguments - return ErrorEval.VALUE_INVALID; - } - return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcCellRow, srcCellCol); + int columnIx = resolveIndexArg(arg2, srcRowIndex, srcColumnIndex); + int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex); + return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcRowIndex, srcColumnIndex); } catch (EvaluationException e) { return e.getErrorEval(); } } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2, ValueEval arg3) { + throw new RuntimeException("Incomplete code" + + " - don't know how to support the 'area_num' parameter yet)"); + // Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3) + // In this example, the 3rd area would be used i.e. D2:E5, and the overall result would be E2 + // Token array might be encoded like this: MemAreaPtg, AreaPtg, AreaPtg, UnionPtg, UnionPtg, ParenthesesPtg + // The formula parser doesn't seem to support this yet. Not sure if the evaluator does either + } + + private static AreaEval convertFirstArg(ValueEval arg0) { + ValueEval firstArg = arg0; + if (firstArg instanceof RefEval) { + // convert to area ref for simpler code in getValueFromArea() + return ((RefEval)firstArg).offset(0, 0, 0, 0); + } + if((firstArg instanceof AreaEval)) { + return (AreaEval) firstArg; + } + // else the other variation of this function takes an array as the first argument + // it seems like interface 'ArrayEval' does not even exist yet + throw new RuntimeException("Incomplete code - cannot handle first arg of type (" + + firstArg.getClass().getName() + ")"); + + } + + public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + switch (args.length) { + case 2: + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]); + case 3: + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2]); + case 4: + return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3]); + } + return ErrorEval.VALUE_INVALID; + } + /** * @param colArgWasPassed false if the INDEX argument list had just 2 items diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/IsError.java b/src/java/org/apache/poi/hssf/record/formula/functions/IsError.java deleted file mode 100644 index 654f4d7968..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/IsError.java +++ /dev/null @@ -1,42 +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.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Josh Micich - */ -public final class IsError implements Function { - - public ValueEval evaluate(ValueEval[] operands, int srcCellRow, int srcCellCol) { - if (operands.length != 1) { - return ErrorEval.VALUE_INVALID; - } - try { - OperandResolver.getSingleValue(operands[0], srcCellRow, srcCellCol); - } catch (EvaluationException e) { - return BoolEval.TRUE; - } - return BoolEval.FALSE; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/IsNa.java b/src/java/org/apache/poi/hssf/record/formula/functions/IsNa.java deleted file mode 100644 index 316a29ec4d..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/IsNa.java +++ /dev/null @@ -1,56 +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.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.ValueEval; -import org.apache.poi.ss.usermodel.ErrorConstants; - -/** - * Implementation for Excel ISNA() function.

- * - * Syntax:
- * ISNA(value)

- * - * value The value to be tested
- *
- * Returns TRUE if the specified value is '#N/A', FALSE otherwise. - * - * @author Josh Micich - */ -public final class IsNa implements Function { - - public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { - if(args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - ValueEval arg = args[0]; - - try { - OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); - } catch (EvaluationException e) { - if (e.getErrorEval().getErrorCode() == ErrorConstants.ERROR_NA) { - return BoolEval.TRUE; - } - } - return BoolEval.FALSE; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Isblank.java b/src/java/org/apache/poi/hssf/record/formula/functions/Isblank.java deleted file mode 100644 index 60132c337f..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Isblank.java +++ /dev/null @@ -1,47 +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.functions; - -import org.apache.poi.hssf.record.formula.eval.BlankEval; -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Isblank implements Function { - - public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { - if(args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - ValueEval arg = args[0]; - - ValueEval singleCellValue; - try { - singleCellValue = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); - } catch (EvaluationException e) { - return BoolEval.FALSE; - } - return BoolEval.valueOf(singleCellValue instanceof BlankEval); - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Isref.java b/src/java/org/apache/poi/hssf/record/formula/functions/Isref.java deleted file mode 100644 index 84eb80ba52..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Isref.java +++ /dev/null @@ -1,42 +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.functions; - -import org.apache.poi.hssf.record.formula.eval.AreaEval; -import org.apache.poi.hssf.record.formula.eval.BoolEval; -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.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - */ -public final class Isref implements Function { - public ValueEval evaluate(ValueEval[] operands, int srcCellRow, int srcCellCol) { - if (operands.length != 1) { - return ErrorEval.VALUE_INVALID; - } - - ValueEval eval = operands[0]; - if (eval instanceof RefEval || eval instanceof AreaEval) { - return BoolEval.TRUE; - } - - return BoolEval.FALSE; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/LogicalFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/LogicalFunction.java index 34221ec9f6..206dd41b68 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/LogicalFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/LogicalFunction.java @@ -17,6 +17,8 @@ package org.apache.poi.hssf.record.formula.functions; +import org.apache.poi.hssf.record.formula.eval.AreaEval; +import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.EvaluationException; @@ -28,30 +30,14 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > + * @author Josh Micich */ -public abstract class LogicalFunction implements Function { +public abstract class LogicalFunction extends Fixed1ArgFunction { - /** - * recursively evaluate any RefEvals TODO - use {@link OperandResolver} - */ - private static ValueEval xlateRefEval(RefEval reval) { - ValueEval retval = reval.getInnerValueEval(); - - if (retval instanceof RefEval) { - RefEval re = (RefEval) retval; - retval = xlateRefEval(re); - } - - return retval; - } - - public final ValueEval evaluate(ValueEval[] operands, int srcCellRow, int srcCellCol) { - if (operands.length != 1) { - return ErrorEval.VALUE_INVALID; - } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { ValueEval ve; try { - ve = OperandResolver.getSingleValue(operands[0], srcCellRow, srcCellCol); + ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); } catch (EvaluationException e) { if (false) { // Note - it is more usual to propagate error codes straight to the result like this: @@ -61,32 +47,73 @@ public abstract class LogicalFunction implements Function { // this will usually cause a 'FALSE' result except for ISNONTEXT() ve = e.getErrorEval(); } - if (ve instanceof RefEval) { - ve = xlateRefEval((RefEval) ve); - } return BoolEval.valueOf(evaluate(ve)); } + /** + * @param arg any {@link ValueEval}, potentially {@link BlankEval} or {@link ErrorEval}. + */ protected abstract boolean evaluate(ValueEval arg); - public static final Function IsLogical = new LogicalFunction() { + public static final Function ISLOGICAL = new LogicalFunction() { protected boolean evaluate(ValueEval arg) { return arg instanceof BoolEval; } }; - public static final Function IsNonText = new LogicalFunction() { + public static final Function ISNONTEXT = new LogicalFunction() { protected boolean evaluate(ValueEval arg) { return !(arg instanceof StringEval); } }; - public static final Function IsNumber = new LogicalFunction() { + public static final Function ISNUMBER = new LogicalFunction() { protected boolean evaluate(ValueEval arg) { return arg instanceof NumberEval; } }; - public static final Function IsText = new LogicalFunction() { + public static final Function ISTEXT = new LogicalFunction() { protected boolean evaluate(ValueEval arg) { return arg instanceof StringEval; } }; + + public static final Function ISBLANK = new LogicalFunction() { + + protected boolean evaluate(ValueEval arg) { + return arg instanceof BlankEval; + } + }; + + public static final Function ISERROR = new LogicalFunction() { + + protected boolean evaluate(ValueEval arg) { + return arg instanceof ErrorEval; + } + }; + + /** + * Implementation for Excel ISNA() function.

+ * + * Syntax:
+ * ISNA(value)

+ * + * value The value to be tested
+ *
+ * Returns TRUE if the specified value is '#N/A', FALSE otherwise. + */ + public static final Function ISNA = new LogicalFunction() { + + protected boolean evaluate(ValueEval arg) { + return arg == ErrorEval.NA; + } + }; + + public static final Function ISREF = new Fixed1ArgFunction() { + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { + if (arg0 instanceof RefEval || arg0 instanceof AreaEval) { + return BoolEval.TRUE; + } + return BoolEval.FALSE; + } + }; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java index 58e3684dfc..ae2bc9114c 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/LookupUtils.java @@ -379,17 +379,10 @@ final class LookupUtils { /** * Resolves the last (optional) parameter (range_lookup) to the VLOOKUP and HLOOKUP functions. - * @param rangeLookupArg - * @param srcCellRow - * @param srcCellCol - * @return - * @throws EvaluationException + * @param rangeLookupArg must not be null */ public static boolean resolveRangeLookupArg(ValueEval rangeLookupArg, int srcCellRow, int srcCellCol) throws EvaluationException { - if(rangeLookupArg == null) { - // range_lookup arg not provided - return true; // default is TRUE - } + ValueEval valEval = OperandResolver.getSingleValue(rangeLookupArg, srcCellRow, srcCellCol); if(valEval instanceof BlankEval) { // Tricky: diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Not.java b/src/java/org/apache/poi/hssf/record/formula/functions/Not.java deleted file mode 100644 index 792ee3a025..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Not.java +++ /dev/null @@ -1,50 +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.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - - -/** - * @author Amol S. Deshmukh < amol at apache dot org > - * The NOT boolean function. Returns negation of specified value - * (treated as a boolean). If the specified arg is a number, - * then it is true <=> 'number is non-zero' - */ -public final class Not implements Function { - - public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { - if (args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - boolean boolArgVal; - try { - ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); - Boolean b = OperandResolver.coerceValueToBoolean(ve, false); - boolArgVal = b == null ? false : b.booleanValue(); - } catch (EvaluationException e) { - return e.getErrorEval(); - } - - return BoolEval.valueOf(!boolArgVal); - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Npv.java b/src/java/org/apache/poi/hssf/record/formula/functions/Npv.java index 114aa4d02e..99955301e7 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Npv.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Npv.java @@ -17,7 +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.EvaluationException; +import org.apache.poi.hssf.record.formula.eval.NumberEval; +import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * Calculates the net present value of an investment by using a discount rate @@ -25,23 +28,80 @@ import org.apache.poi.hssf.record.formula.eval.EvaluationException; * values). Minimum 2 arguments, first arg is the rate of discount over the * length of one period others up to 254 arguments representing the payments and * income. - * + * * @author SPetrakovsky */ -public class Npv extends NumericFunction.MultiArg { +public final class Npv implements Function2Arg, Function3Arg, Function4Arg { - public Npv() { - super(2, 255); + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { + double result; + try { + double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); + result = evaluate(rate, d1); + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2) { + double result; + try { + double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); + double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex); + result = evaluate(rate, d1, d2); + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2, ValueEval arg3) { + double result; + try { + double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); + double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex); + double d3 = NumericFunction.singleOperandEvaluate(arg3, srcRowIndex, srcColumnIndex); + result = evaluate(rate, d1, d2, d3); + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); } - @Override - protected double evaluate(double[] ds) throws EvaluationException { - double rate = ds[0]; + public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + int nArgs = args.length; + if (nArgs<2) { + return ErrorEval.VALUE_INVALID; + } + int np = nArgs-1; + double[] ds = new double[np]; + double result; + try { + double rate = NumericFunction.singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex); + for (int i = 0; i < ds.length; i++) { + ds[i] = NumericFunction.singleOperandEvaluate(args[i+1], srcRowIndex, srcColumnIndex); + } + result = evaluate(rate, ds); + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + + private static double evaluate(double rate, double...ds) { double sum = 0; - for (int i = 1; i < ds.length; i++) { + for (int i = 0; i < ds.length; i++) { sum += ds[i] / Math.pow(rate + 1, i); } return sum; } - } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java index 24b0ae2692..3d32925ae3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java @@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > + * @author Josh Micich */ public abstract class NumericFunction implements Function { @@ -32,8 +33,8 @@ public abstract class NumericFunction implements Function { static final double TEN = 10.0; static final double LOG_10_TO_BASE_e = Math.log(TEN); - protected static final double singleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException { - ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + protected static final double singleOperandEvaluate(ValueEval arg, int srcRowIndex, int srcColumnIndex) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcRowIndex, srcColumnIndex); double result = OperandResolver.coerceValueToDouble(ve); checkValue(result); return result; @@ -64,10 +65,21 @@ public abstract class NumericFunction implements Function { /* -------------------------------------------------------------------------- */ // intermediate sub-classes (one-arg, two-arg and multi-arg) - public static abstract class OneArg extends NumericFunction { + public static abstract class OneArg extends Fixed1ArgFunction { protected OneArg() { // no fields to initialise } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { + double result; + try { + double d = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + result = evaluate(d); + checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } protected final double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException { if (args.length != 1) { throw new EvaluationException(ErrorEval.VALUE_INVALID); @@ -78,40 +90,26 @@ public abstract class NumericFunction implements Function { protected abstract double evaluate(double d) throws EvaluationException; } - public static abstract class TwoArg extends NumericFunction { + public static abstract class TwoArg extends Fixed2ArgFunction { protected TwoArg() { // no fields to initialise } - protected final double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException { - if (args.length != 2) { - throw new EvaluationException(ErrorEval.VALUE_INVALID); - } - double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol); - double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol); - return evaluate(d0, d1); - } - protected abstract double evaluate(double d0, double d1) throws EvaluationException; - } - public static abstract class MultiArg extends NumericFunction { - private final int _minArgs; - private final int _maxArgs; - protected MultiArg(int minArgs, int maxArgs) { - _minArgs = minArgs; - _maxArgs = maxArgs; - } - protected final double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException { - int nArgs = args.length; - if (nArgs < _minArgs || nArgs > _maxArgs) { - throw new EvaluationException(ErrorEval.VALUE_INVALID); - } - double[] ds = new double[nArgs]; - for(int i=0; i 127) { + return ErrorEval.VALUE_INVALID; + } + + + // TODO - DOLLAR() function impl is NQR + // result should be StringEval, with leading '$' and thousands separators + // current junits are asserting incorrect behaviour + return new NumberEval(val); } }; public static final Function EXP = new OneArg() { @@ -298,18 +321,53 @@ public abstract class NumericFunction implements Function { /* -------------------------------------------------------------------------- */ - public static final Function LOG = new MultiArg(1,2) { - protected double evaluate(double[] ds) { - - double logE = Math.log(ds[0]); - if (ds.length == 1) { - return logE / LOG_10_TO_BASE_e; + private static final class Log extends Var1or2ArgFunction { + public Log() { + // no instance fields + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { + double result; + try { + double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + result = Math.log(d0) / LOG_10_TO_BASE_e; + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); } - double base = ds[1]; - if (base == Math.E) { - return logE; + return new NumberEval(result); + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1) { + double result; + try { + double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex); + double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex); + double logE = Math.log(d0); + double base = d1; + if (base == Math.E) { + result = logE; + } else { + result = logE / Math.log(base); + } + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); } - return logE / Math.log(base); + return new NumberEval(result); + } + } + + public static final Function LOG = new Log(); + + static final NumberEval PI_EVAL = new NumberEval(Math.PI); + public static final Function PI = new Fixed0ArgFunction() { + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) { + return PI_EVAL; + } + }; + public static final Function RAND = new Fixed0ArgFunction() { + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) { + return new NumberEval(Math.random()); } }; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Or.java b/src/java/org/apache/poi/hssf/record/formula/functions/Or.java deleted file mode 100644 index bab77471ae..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Or.java +++ /dev/null @@ -1,32 +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.functions; - -/** - * - */ -public final class Or extends BooleanFunction { - - protected boolean getInitialResultValue() { - return false; - } - - protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) { - return cumulativeResult || currentValue; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Pi.java b/src/java/org/apache/poi/hssf/record/formula/functions/Pi.java deleted file mode 100644 index e31d6564d1..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Pi.java +++ /dev/null @@ -1,44 +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.functions; - -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.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Pi implements Function { - - private static final NumberEval PI_EVAL = new NumberEval(Math.PI); - - public ValueEval evaluate(ValueEval[] operands, int srcRow, int srcCol) { - ValueEval retval; - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 0: - retval = PI_EVAL; - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Rand.java b/src/java/org/apache/poi/hssf/record/formula/functions/Rand.java deleted file mode 100644 index e1467a6e60..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Rand.java +++ /dev/null @@ -1,42 +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.functions; - -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.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Rand implements Function { - - public ValueEval evaluate(ValueEval[] operands, int srcRow, int srcCol) { - ValueEval retval; - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 0: - retval = new NumberEval(Math.random()); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java b/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java index 0055bc6fe4..558de6ba8f 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java @@ -27,31 +27,38 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * Substitutes text in a text string with new text, some number of times. * @author Manda Wilson < wilson at c bio dot msk cc dot org > */ -public final class Substitute extends TextFunction { +public final class Substitute extends Var3or4ArgFunction { - protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) - throws EvaluationException { - if (args.length < 3 || args.length > 4) { - return ErrorEval.VALUE_INVALID; - } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2) { + String result; + try { + String oldStr = TextFunction.evaluateStringArg(arg0, srcRowIndex, srcColumnIndex); + String searchStr = TextFunction.evaluateStringArg(arg1, srcRowIndex, srcColumnIndex); + String newStr = TextFunction.evaluateStringArg(arg2, srcRowIndex, srcColumnIndex); - String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol); - String searchStr = evaluateStringArg(args[1], srcCellRow, srcCellCol); - String newStr = evaluateStringArg(args[2], srcCellRow, srcCellCol); + result = replaceAllOccurrences(oldStr, searchStr, newStr); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new StringEval(result); + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, + ValueEval arg2, ValueEval arg3) { String result; - switch (args.length) { - default: - throw new IllegalStateException("Cannot happen"); - case 4: - int instanceNumber = evaluateIntArg(args[3], srcCellRow, srcCellCol); - if (instanceNumber < 1) { - return ErrorEval.VALUE_INVALID; - } - result = replaceOneOccurrence(oldStr, searchStr, newStr, instanceNumber); - break; - case 3: - result = replaceAllOccurrences(oldStr, searchStr, newStr); + try { + String oldStr = TextFunction.evaluateStringArg(arg0, srcRowIndex, srcColumnIndex); + String searchStr = TextFunction.evaluateStringArg(arg1, srcRowIndex, srcColumnIndex); + String newStr = TextFunction.evaluateStringArg(arg2, srcRowIndex, srcColumnIndex); + + int instanceNumber = TextFunction.evaluateIntArg(arg3, srcRowIndex, srcColumnIndex); + if (instanceNumber < 1) { + return ErrorEval.VALUE_INVALID; + } + result = replaceOneOccurrence(oldStr, searchStr, newStr, instanceNumber); + } catch (EvaluationException e) { + return e.getErrorEval(); } return new StringEval(result); } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java index e624259866..6b108f2f65 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java @@ -27,7 +27,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * @author Josh Micich */ public abstract class TextFunction implements Function { @@ -54,17 +54,18 @@ public abstract class TextFunction implements Function { /* ---------------------------------------------------------------------- */ - private static abstract class SingleArgTextFunc extends TextFunction { + private static abstract class SingleArgTextFunc extends Fixed1ArgFunction { protected SingleArgTextFunc() { // no fields to initialise } - protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) - throws EvaluationException { - if (args.length != 1) { - return ErrorEval.VALUE_INVALID; + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { + String arg; + try { + arg = evaluateStringArg(arg0, srcRowIndex, srcColumnIndex); + } catch (EvaluationException e) { + return e.getErrorEval(); } - String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol); return evaluate(arg); } protected abstract ValueEval evaluate(String arg); @@ -107,17 +108,20 @@ public abstract class TextFunction implements Function { * * Author: Manda Wilson < wilson at c bio dot msk cc dot org > */ - public static final Function MID = new TextFunction() { + public static final Function MID = new Fixed3ArgFunction() { - protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) - throws EvaluationException { - if (args.length != 3) { - return ErrorEval.VALUE_INVALID; + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1, ValueEval arg2) { + String text; + int startCharNum; + int numChars; + try { + text = evaluateStringArg(arg0, srcRowIndex, srcColumnIndex); + startCharNum = evaluateIntArg(arg1, srcRowIndex, srcColumnIndex); + numChars = evaluateIntArg(arg2, srcRowIndex, srcColumnIndex); + } catch (EvaluationException e) { + return e.getErrorEval(); } - - String text = evaluateStringArg(args[0], srcCellRow, srcCellCol); - int startCharNum = evaluateIntArg(args[1], srcCellRow, srcCellCol); - int numChars = evaluateIntArg(args[2], srcCellRow, srcCellCol); int startIx = startCharNum - 1; // convert to zero-based // Note - for start_num arg, blank/zero causes error(#VALUE!), @@ -138,19 +142,25 @@ public abstract class TextFunction implements Function { } }; - private static final class LeftRight extends TextFunction { - + private static final class LeftRight extends Var1or2ArgFunction { + private static final ValueEval DEFAULT_ARG1 = new NumberEval(1.0); private final boolean _isLeft; protected LeftRight(boolean isLeft) { _isLeft = isLeft; } - protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) - throws EvaluationException { - if (args.length != 2) { - return ErrorEval.VALUE_INVALID; + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { + return evaluate(srcRowIndex, srcColumnIndex, arg0, DEFAULT_ARG1); + } + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1) { + String arg; + int index; + try { + arg = evaluateStringArg(arg0, srcRowIndex, srcColumnIndex); + index = evaluateIntArg(arg1, srcRowIndex, srcColumnIndex); + } catch (EvaluationException e) { + return e.getErrorEval(); } - String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol); - int index = evaluateIntArg(args[1], srcCellRow, srcCellCol); String result; if (_isLeft) { @@ -165,28 +175,33 @@ public abstract class TextFunction implements Function { public static final Function LEFT = new LeftRight(true); public static final Function RIGHT = new LeftRight(false); - public static final Function CONCATENATE = new TextFunction() { + public static final Function CONCATENATE = new Function() { - protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) - throws EvaluationException { - StringBuffer sb = new StringBuffer(); + public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + StringBuilder sb = new StringBuilder(); for (int i=0, iSize=args.length; i