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
This commit is contained in:
Josh Micich 2009-11-23 00:19:11 +00:00
parent 24717007c8
commit 1f22e9c945
26 changed files with 591 additions and 703 deletions

View File

@ -52,8 +52,8 @@ public final class FunctionEval {
retval[0] = new Count(); retval[0] = new Count();
retval[1] = new If(); retval[1] = new If();
retval[2] = new IsNa(); retval[2] = LogicalFunction.ISNA;
retval[3] = new IsError(); retval[3] = LogicalFunction.ISERROR;
retval[ID.SUM] = AggregateFunction.SUM; retval[ID.SUM] = AggregateFunction.SUM;
retval[5] = AggregateFunction.AVERAGE; retval[5] = AggregateFunction.AVERAGE;
retval[6] = AggregateFunction.MIN; retval[6] = AggregateFunction.MIN;
@ -69,7 +69,7 @@ public final class FunctionEval {
retval[16] = NumericFunction.COS; retval[16] = NumericFunction.COS;
retval[17] = NumericFunction.TAN; retval[17] = NumericFunction.TAN;
retval[18] = NumericFunction.ATAN; retval[18] = NumericFunction.ATAN;
retval[19] = new Pi(); retval[19] = NumericFunction.PI;
retval[20] = NumericFunction.SQRT; retval[20] = NumericFunction.SQRT;
retval[21] = NumericFunction.EXP; retval[21] = NumericFunction.EXP;
retval[22] = NumericFunction.LN; retval[22] = NumericFunction.LN;
@ -84,11 +84,11 @@ public final class FunctionEval {
retval[31] = TextFunction.MID; retval[31] = TextFunction.MID;
retval[32] = TextFunction.LEN; retval[32] = TextFunction.LEN;
retval[33] = new Value(); retval[33] = new Value();
retval[34] = new True(); retval[34] = BooleanFunction.TRUE;
retval[35] = new False(); retval[35] = BooleanFunction.FALSE;
retval[36] = new And(); retval[36] = BooleanFunction.AND;
retval[37] = new Or(); retval[37] = BooleanFunction.OR;
retval[38] = new Not(); retval[38] = BooleanFunction.NOT;
retval[39] = NumericFunction.MOD; retval[39] = NumericFunction.MOD;
retval[56] = FinanceFunction.PV; retval[56] = FinanceFunction.PV;
@ -96,7 +96,7 @@ public final class FunctionEval {
retval[58] = FinanceFunction.NPER; retval[58] = FinanceFunction.NPER;
retval[59] = FinanceFunction.PMT; retval[59] = FinanceFunction.PMT;
retval[63] = new Rand(); retval[63] = NumericFunction.RAND;
retval[64] = new Match(); retval[64] = new Match();
retval[65] = DateFunc.instance; retval[65] = DateFunc.instance;
retval[66] = new Time(); retval[66] = new Time();
@ -108,6 +108,7 @@ public final class FunctionEval {
retval[76] = new Rows(); retval[76] = new Rows();
retval[77] = new Columns(); retval[77] = new Columns();
retval[82] = TextFunction.SEARCH;
retval[ID.OFFSET] = new Offset(); retval[ID.OFFSET] = new Offset();
retval[82] = TextFunction.SEARCH; retval[82] = TextFunction.SEARCH;
@ -118,7 +119,7 @@ public final class FunctionEval {
retval[101] = new Hlookup(); retval[101] = new Hlookup();
retval[102] = new Vlookup(); retval[102] = new Vlookup();
retval[105] = new Isref(); retval[105] = LogicalFunction.ISREF;
retval[109] = NumericFunction.LOG; retval[109] = NumericFunction.LOG;
@ -134,9 +135,9 @@ public final class FunctionEval {
retval[124] = TextFunction.FIND; retval[124] = TextFunction.FIND;
retval[127] = LogicalFunction.IsText; retval[127] = LogicalFunction.ISTEXT;
retval[128] = LogicalFunction.IsNumber; retval[128] = LogicalFunction.ISNUMBER;
retval[129] = new Isblank(); retval[129] = LogicalFunction.ISBLANK;
retval[130] = new T(); retval[130] = new T();
retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature
@ -146,9 +147,9 @@ public final class FunctionEval {
retval[183] = AggregateFunction.PRODUCT; retval[183] = AggregateFunction.PRODUCT;
retval[184] = NumericFunction.FACT; 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[212] = NumericFunction.ROUNDUP;
retval[213] = NumericFunction.ROUNDDOWN; retval[213] = NumericFunction.ROUNDDOWN;

View File

@ -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.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException; 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 > * @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/ */
public abstract class AggregateFunction extends MultiOperandNumericFunction { 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() { protected AggregateFunction() {
super(false, false); super(false, false);
} }
@ -48,17 +102,7 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction {
return StatsLib.devsq(values); return StatsLib.devsq(values);
} }
}; };
public static final Function LARGE = new AggregateFunction() { public static final Function LARGE = new LargeSmall(true);
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 MAX = new AggregateFunction() { public static final Function MAX = new AggregateFunction() {
protected double evaluate(double[] values) { protected double evaluate(double[] values) {
return values.length > 0 ? MathX.max(values) : 0; return values.length > 0 ? MathX.max(values) : 0;
@ -79,17 +123,7 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction {
return MathX.product(values); return MathX.product(values);
} }
}; };
public static final Function SMALL = new AggregateFunction() { public static final Function SMALL = new LargeSmall(false);
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 STDEV = new AggregateFunction() { public static final Function STDEV = new AggregateFunction() {
protected double evaluate(double[] values) throws EvaluationException { protected double evaluate(double[] values) throws EvaluationException {
if (values.length < 1) { if (values.length < 1) {

View File

@ -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;
}
}

View File

@ -101,4 +101,46 @@ public abstract class BooleanFunction implements Function {
protected abstract boolean getInitialResultValue(); protected abstract boolean getInitialResultValue();
protected abstract boolean partialEvaluate(boolean cumulativeResult, boolean currentValue); 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);
}
};
} }

View File

@ -17,17 +17,58 @@
package org.apache.poi.hssf.record.formula.functions; 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.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* 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() { 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 { protected double evaluate(double[] ds) throws EvaluationException {

View File

@ -18,7 +18,7 @@
package org.apache.poi.hssf.record.formula.functions; 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.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.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval; 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 * @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) { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
ValueEval arg3 = null; ValueEval arg2) {
switch(args.length) { return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, DEFAULT_ARG3);
case 4: }
arg3 = args[3]; // important: assumed array element is never null
case 3: public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
break; ValueEval arg2, ValueEval arg3) {
default:
// wrong number of arguments
return ErrorEval.VALUE_INVALID;
}
try { try {
// Evaluation order: // Evaluation order:
// arg0 lookup_value, arg1 table_array, arg3 range_lookup, find lookup value, arg2 row_index, fetch result // 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); ValueEval lookupValue = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); AreaEval tableArray = LookupUtils.resolveTableArrayArg(arg1);
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcRowIndex, srcColumnIndex);
int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup); 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); ValueVector resultCol = createResultColumnVector(tableArray, rowIndex);
return resultCol.getItem(colIndex); return resultCol.getItem(colIndex);
} catch (EvaluationException e) { } catch (EvaluationException e) {
@ -67,7 +64,6 @@ public final class Hlookup implements Function {
} }
} }
/** /**
* Returns one column from an <tt>AreaEval</tt> * Returns one column from an <tt>AreaEval</tt>
* *

View File

@ -17,9 +17,6 @@
package org.apache.poi.hssf.record.formula.functions; 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.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
@ -36,21 +33,17 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
* <b>friendly_name</b> (optional) the value to display<p/> * <b>friendly_name</b> (optional) the value to display<p/>
* *
* Returns last argument. Leaves type unchanged (does not convert to {@link StringEval}). * Returns last argument. Leaves type unchanged (does not convert to {@link StringEval}).
*
* @author Wayne Clingingsmith * @author Wayne Clingingsmith
*/ */
public final class Hyperlink implements Function { public final class Hyperlink extends Var1or2ArgFunction {
public ValueEval evaluate(ValueEval[] operands, int srcRow, int srcCol) { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
int lastArgIx = operands.length - 1; return arg0;
if (lastArgIx < 0 || lastArgIx > 1) { }
return ErrorEval.VALUE_INVALID; 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
try { return arg1;
return OperandResolver.getSingleValue(operands[lastArgIx], srcRow, srcCol);
} catch (EvaluationException e) {
return e.getErrorEval();
}
} }
} }

View File

@ -44,56 +44,71 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
* *
* @author Josh Micich * @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) { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
int nArgs = args.length; AreaEval reference = convertFirstArg(arg0);
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)) {
// 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() + ")");
}
AreaEval reference = (AreaEval) firstArg;
int rowIx = 0;
int columnIx = 0;
boolean colArgWasPassed = false; boolean colArgWasPassed = false;
int columnIx = 0;
try { try {
switch(nArgs) { int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex);
case 4: return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcRowIndex, srcColumnIndex);
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);
} catch (EvaluationException e) { } catch (EvaluationException e) {
return e.getErrorEval(); return e.getErrorEval();
} }
} }
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
ValueEval arg2) {
AreaEval reference = convertFirstArg(arg0);
boolean colArgWasPassed = true;
try {
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 <code>false</code> if the INDEX argument list had just 2 items * @param colArgWasPassed <code>false</code> if the INDEX argument list had just 2 items

View File

@ -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;
}
}

View File

@ -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.<p/>
*
* <b>Syntax</b>:<br/>
* <b>ISNA</b>(<b>value</b>)<p/>
*
* <b>value</b> The value to be tested<br/>
* <br/>
* Returns <tt>TRUE</tt> if the specified value is '#N/A', <tt>FALSE</tt> 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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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);
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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;
}
}

View File

@ -17,6 +17,8 @@
package org.apache.poi.hssf.record.formula.functions; 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.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval; 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.EvaluationException;
@ -28,30 +30,14 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* @author Josh Micich
*/ */
public abstract class LogicalFunction implements Function { public abstract class LogicalFunction extends Fixed1ArgFunction {
/** public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
* 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;
}
ValueEval ve; ValueEval ve;
try { try {
ve = OperandResolver.getSingleValue(operands[0], srcCellRow, srcCellCol); ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
} catch (EvaluationException e) { } catch (EvaluationException e) {
if (false) { if (false) {
// Note - it is more usual to propagate error codes straight to the result like this: // 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() // this will usually cause a 'FALSE' result except for ISNONTEXT()
ve = e.getErrorEval(); ve = e.getErrorEval();
} }
if (ve instanceof RefEval) {
ve = xlateRefEval((RefEval) ve);
}
return BoolEval.valueOf(evaluate(ve)); return BoolEval.valueOf(evaluate(ve));
} }
/**
* @param arg any {@link ValueEval}, potentially {@link BlankEval} or {@link ErrorEval}.
*/
protected abstract boolean evaluate(ValueEval arg); 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) { protected boolean evaluate(ValueEval arg) {
return arg instanceof BoolEval; return arg instanceof BoolEval;
} }
}; };
public static final Function IsNonText = new LogicalFunction() { public static final Function ISNONTEXT = new LogicalFunction() {
protected boolean evaluate(ValueEval arg) { protected boolean evaluate(ValueEval arg) {
return !(arg instanceof StringEval); return !(arg instanceof StringEval);
} }
}; };
public static final Function IsNumber = new LogicalFunction() { public static final Function ISNUMBER = new LogicalFunction() {
protected boolean evaluate(ValueEval arg) { protected boolean evaluate(ValueEval arg) {
return arg instanceof NumberEval; return arg instanceof NumberEval;
} }
}; };
public static final Function IsText = new LogicalFunction() { public static final Function ISTEXT = new LogicalFunction() {
protected boolean evaluate(ValueEval arg) { protected boolean evaluate(ValueEval arg) {
return arg instanceof StringEval; 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.<p/>
*
* <b>Syntax</b>:<br/>
* <b>ISNA</b>(<b>value</b>)<p/>
*
* <b>value</b> The value to be tested<br/>
* <br/>
* Returns <tt>TRUE</tt> if the specified value is '#N/A', <tt>FALSE</tt> 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;
}
};
} }

View File

@ -379,17 +379,10 @@ final class LookupUtils {
/** /**
* Resolves the last (optional) parameter (<b>range_lookup</b>) to the VLOOKUP and HLOOKUP functions. * Resolves the last (optional) parameter (<b>range_lookup</b>) to the VLOOKUP and HLOOKUP functions.
* @param rangeLookupArg * @param rangeLookupArg must not be <code>null</code>
* @param srcCellRow
* @param srcCellCol
* @return
* @throws EvaluationException
*/ */
public static boolean resolveRangeLookupArg(ValueEval rangeLookupArg, int srcCellRow, int srcCellCol) throws EvaluationException { 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); ValueEval valEval = OperandResolver.getSingleValue(rangeLookupArg, srcCellRow, srcCellCol);
if(valEval instanceof BlankEval) { if(valEval instanceof BlankEval) {
// Tricky: // Tricky:

View File

@ -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 &lt; amol at apache dot org &gt;
* 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);
}
}

View File

@ -17,7 +17,10 @@
package org.apache.poi.hssf.record.formula.functions; 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.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 * Calculates the net present value of an investment by using a discount rate
@ -28,20 +31,77 @@ import org.apache.poi.hssf.record.formula.eval.EvaluationException;
* *
* @author SPetrakovsky * @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 public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
protected double evaluate(double[] ds) throws EvaluationException { int nArgs = args.length;
double rate = ds[0]; 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; 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); sum += ds[i] / Math.pow(rate + 1, i);
} }
return sum; return sum;
} }
} }

View File

@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* @author Josh Micich
*/ */
public abstract class NumericFunction implements Function { 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 TEN = 10.0;
static final double LOG_10_TO_BASE_e = Math.log(TEN); static final double LOG_10_TO_BASE_e = Math.log(TEN);
protected static final double singleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException { protected static final double singleOperandEvaluate(ValueEval arg, int srcRowIndex, int srcColumnIndex) throws EvaluationException {
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); ValueEval ve = OperandResolver.getSingleValue(arg, srcRowIndex, srcColumnIndex);
double result = OperandResolver.coerceValueToDouble(ve); double result = OperandResolver.coerceValueToDouble(ve);
checkValue(result); checkValue(result);
return result; return result;
@ -64,10 +65,21 @@ public abstract class NumericFunction implements Function {
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
// intermediate sub-classes (one-arg, two-arg and multi-arg) // 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() { protected OneArg() {
// no fields to initialise // 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 { protected final double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException {
if (args.length != 1) { if (args.length != 1) {
throw new EvaluationException(ErrorEval.VALUE_INVALID); throw new EvaluationException(ErrorEval.VALUE_INVALID);
@ -78,40 +90,26 @@ public abstract class NumericFunction implements Function {
protected abstract double evaluate(double d) throws EvaluationException; protected abstract double evaluate(double d) throws EvaluationException;
} }
public static abstract class TwoArg extends NumericFunction { public static abstract class TwoArg extends Fixed2ArgFunction {
protected TwoArg() { protected TwoArg() {
// no fields to initialise // 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; public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
private final int _maxArgs; double result;
protected MultiArg(int minArgs, int maxArgs) { try {
_minArgs = minArgs; double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
_maxArgs = maxArgs; double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
} result = evaluate(d0, d1);
protected final double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException { checkValue(result);
int nArgs = args.length; } catch (EvaluationException e) {
if (nArgs < _minArgs || nArgs > _maxArgs) { return e.getErrorEval();
throw new EvaluationException(ErrorEval.VALUE_INVALID);
} }
double[] ds = new double[nArgs]; return new NumberEval(result);
for(int i=0; i<nArgs; i++) {
ds[i] = singleOperandEvaluate(args[i], srcCellRow, srcCellCol);
}
return evaluate(ds);
} }
protected abstract double evaluate(double[] ds) throws EvaluationException;
protected abstract double evaluate(double d0, double d1) throws EvaluationException;
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
@ -166,9 +164,34 @@ public abstract class NumericFunction implements Function {
return Math.toDegrees(d); return Math.toDegrees(d);
} }
}; };
public static final Function DOLLAR = new OneArg() { static final NumberEval DOLLAR_ARG2_DEFAULT = new NumberEval(2.0);
protected double evaluate(double d) { public static final Function DOLLAR = new Var1or2ArgFunction() {
return d; public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
return evaluate(srcRowIndex, srcColumnIndex, arg0, DOLLAR_ARG2_DEFAULT);
}
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
ValueEval arg1) {
double val;
double d1;
try {
val = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
} catch (EvaluationException e) {
return e.getErrorEval();
}
// second arg converts to int by truncating toward zero
int nPlaces = (int)d1;
if (nPlaces > 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() { 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) { private static final class Log extends Var1or2ArgFunction {
protected double evaluate(double[] ds) { 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();
}
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 new NumberEval(result);
}
}
double logE = Math.log(ds[0]); public static final Function LOG = new Log();
if (ds.length == 1) {
return logE / LOG_10_TO_BASE_e; static final NumberEval PI_EVAL = new NumberEval(Math.PI);
} public static final Function PI = new Fixed0ArgFunction() {
double base = ds[1]; public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
if (base == Math.E) { return PI_EVAL;
return logE; }
} };
return logE / Math.log(base); public static final Function RAND = new Fixed0ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
return new NumberEval(Math.random());
} }
}; };
} }

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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;
}
}

View File

@ -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. * Substitutes text in a text string with new text, some number of times.
* @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt; * @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
*/ */
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;
}
String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol);
String searchStr = evaluateStringArg(args[1], srcCellRow, srcCellCol);
String newStr = evaluateStringArg(args[2], srcCellRow, srcCellCol);
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
ValueEval arg2) {
String result; String result;
switch (args.length) { try {
default: String oldStr = TextFunction.evaluateStringArg(arg0, srcRowIndex, srcColumnIndex);
throw new IllegalStateException("Cannot happen"); String searchStr = TextFunction.evaluateStringArg(arg1, srcRowIndex, srcColumnIndex);
case 4: String newStr = TextFunction.evaluateStringArg(arg2, srcRowIndex, srcColumnIndex);
int instanceNumber = evaluateIntArg(args[3], srcCellRow, srcCellCol);
if (instanceNumber < 1) { result = replaceAllOccurrences(oldStr, searchStr, newStr);
return ErrorEval.VALUE_INVALID; } catch (EvaluationException e) {
} return e.getErrorEval();
result = replaceOneOccurrence(oldStr, searchStr, newStr, instanceNumber); }
break; return new StringEval(result);
case 3: }
result = replaceAllOccurrences(oldStr, searchStr, newStr);
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
ValueEval arg2, ValueEval arg3) {
String result;
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); return new StringEval(result);
} }

View File

@ -27,7 +27,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* * @author Josh Micich
*/ */
public abstract class TextFunction implements Function { 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() { protected SingleArgTextFunc() {
// no fields to initialise // no fields to initialise
} }
protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
throws EvaluationException { String arg;
if (args.length != 1) { try {
return ErrorEval.VALUE_INVALID; arg = evaluateStringArg(arg0, srcRowIndex, srcColumnIndex);
} catch (EvaluationException e) {
return e.getErrorEval();
} }
String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol);
return evaluate(arg); return evaluate(arg);
} }
protected abstract ValueEval evaluate(String arg); protected abstract ValueEval evaluate(String arg);
@ -107,17 +108,20 @@ public abstract class TextFunction implements Function {
* *
* Author: Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt; * Author: Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
*/ */
public static final Function MID = new TextFunction() { public static final Function MID = new Fixed3ArgFunction() {
protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
throws EvaluationException { ValueEval arg1, ValueEval arg2) {
if (args.length != 3) { String text;
return ErrorEval.VALUE_INVALID; 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 int startIx = startCharNum - 1; // convert to zero-based
// Note - for start_num arg, blank/zero causes error(#VALUE!), // 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; private final boolean _isLeft;
protected LeftRight(boolean isLeft) { protected LeftRight(boolean isLeft) {
_isLeft = isLeft; _isLeft = isLeft;
} }
protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
throws EvaluationException { return evaluate(srcRowIndex, srcColumnIndex, arg0, DEFAULT_ARG1);
if (args.length != 2) { }
return ErrorEval.VALUE_INVALID; 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; String result;
if (_isLeft) { if (_isLeft) {
@ -165,28 +175,33 @@ public abstract class TextFunction implements Function {
public static final Function LEFT = new LeftRight(true); public static final Function LEFT = new LeftRight(true);
public static final Function RIGHT = new LeftRight(false); 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) public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
throws EvaluationException { StringBuilder sb = new StringBuilder();
StringBuffer sb = new StringBuffer();
for (int i=0, iSize=args.length; i<iSize; i++) { for (int i=0, iSize=args.length; i<iSize; i++) {
sb.append(evaluateStringArg(args[i], srcCellRow, srcCellCol)); try {
sb.append(evaluateStringArg(args[i], srcRowIndex, srcColumnIndex));
} catch (EvaluationException e) {
return e.getErrorEval();
}
} }
return new StringEval(sb.toString()); return new StringEval(sb.toString());
} }
}; };
public static final Function EXACT = new TextFunction() { public static final Function EXACT = new Fixed2ArgFunction() {
protected ValueEval evaluateFunc(ValueEval[] args, int srcCellRow, int srcCellCol) public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
throws EvaluationException { ValueEval arg1) {
if (args.length != 2) { String s0;
return ErrorEval.VALUE_INVALID; String s1;
try {
s0 = evaluateStringArg(arg0, srcRowIndex, srcColumnIndex);
s1 = evaluateStringArg(arg1, srcRowIndex, srcColumnIndex);
} catch (EvaluationException e) {
return e.getErrorEval();
} }
String s0 = evaluateStringArg(args[0], srcCellRow, srcCellCol);
String s1 = evaluateStringArg(args[1], srcCellRow, srcCellCol);
return BoolEval.valueOf(s0.equals(s1)); return BoolEval.valueOf(s0.equals(s1));
} }
}; };

View File

@ -17,26 +17,24 @@
package org.apache.poi.hssf.record.formula.functions; 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.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * Convenience base class for any function which must take two or three
* arguments
* *
* @author Josh Micich
*/ */
public final class True implements Function { abstract class Var1or2ArgFunction implements Function1Arg, Function2Arg {
public ValueEval evaluate(ValueEval[] operands, int srcRow, int srcCol) {
ValueEval retval;
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 0:
retval = BoolEval.TRUE;
}
return retval;
}
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
switch (args.length) {
case 1:
return evaluate(srcRowIndex, srcColumnIndex, args[0]);
case 2:
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]);
}
return ErrorEval.VALUE_INVALID;
}
} }

View File

@ -17,26 +17,24 @@
package org.apache.poi.hssf.record.formula.functions; 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.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * Convenience base class for any function which must take three or four
* arguments
* *
* @author Josh Micich
*/ */
public final class False implements Function { abstract class Var3or4ArgFunction implements Function3Arg, Function4Arg {
public ValueEval evaluate(ValueEval[] operands, int srcRow, int srcCol) {
ValueEval retval;
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 0:
retval = BoolEval.FALSE;
}
return retval;
}
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
switch (args.length) {
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;
}
} }

View File

@ -18,7 +18,7 @@
package org.apache.poi.hssf.record.formula.functions; 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.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.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval; 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 * @author Josh Micich
*/ */
public final class Vlookup implements Function { public final class Vlookup extends Var3or4ArgFunction {
private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE;
public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
ValueEval arg3 = null; ValueEval arg2) {
switch(args.length) { return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, DEFAULT_ARG3);
case 4: }
arg3 = args[3]; // important: assumed array element is never null
case 3: public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
break; ValueEval arg2, ValueEval arg3) {
default:
// wrong number of arguments
return ErrorEval.VALUE_INVALID;
}
try { try {
// Evaluation order: // Evaluation order:
// arg0 lookup_value, arg1 table_array, arg3 range_lookup, find lookup value, arg2 col_index, fetch result // arg0 lookup_value, arg1 table_array, arg3 range_lookup, find lookup value, arg2 col_index, fetch result
ValueEval lookupValue = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); ValueEval lookupValue = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); AreaEval tableArray = LookupUtils.resolveTableArrayArg(arg1);
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcRowIndex, srcColumnIndex);
int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup); int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup);
int colIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol); int colIndex = LookupUtils.resolveRowOrColIndexArg(arg2, srcRowIndex, srcColumnIndex);
ValueVector resultCol = createResultColumnVector(tableArray, colIndex); ValueVector resultCol = createResultColumnVector(tableArray, colIndex);
return resultCol.getItem(rowIndex); return resultCol.getItem(rowIndex);
} catch (EvaluationException e) { } catch (EvaluationException e) {

View File

@ -37,7 +37,7 @@ import org.apache.poi.ss.util.CellReference.NameType;
* @author Josh Micich * @author Josh Micich
*/ */
public final class OperationEvaluationContext { public final class OperationEvaluationContext {
public static final FreeRefFunction UDF = UserDefinedFunction.instance;
private final EvaluationWorkbook _workbook; private final EvaluationWorkbook _workbook;
private final int _sheetIndex; private final int _sheetIndex;
private final int _rowIndex; private final int _rowIndex;