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[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;

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.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) {

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 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;
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 &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() {
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 {

View File

@ -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 <tt>AreaEval</tt>
*

View File

@ -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;
* <b>friendly_name</b> (optional) the value to display<p/>
*
* 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;
}
}

View File

@ -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() + ")");
}
AreaEval reference = (AreaEval) firstArg;
int rowIx = 0;
int columnIx = 0;
boolean colArgWasPassed = false;
int columnIx = 0;
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 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) {
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

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;
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 &lt; amolweb at ya hoo dot com &gt;
* @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.<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.
* @param rangeLookupArg
* @param srcCellRow
* @param srcCellCol
* @return
* @throws EvaluationException
* @param rangeLookupArg must not be <code>null</code>
*/
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:

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;
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
@ -28,20 +31,77 @@ import org.apache.poi.hssf.record.formula.eval.EvaluationException;
*
* @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;
}
}

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 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);
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
double result;
try {
double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
result = evaluate(d0, d1);
checkValue(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
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;
return new NumberEval(result);
}
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<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);
}
};
public static final Function DOLLAR = new OneArg() {
protected double evaluate(double d) {
return d;
static final NumberEval DOLLAR_ARG2_DEFAULT = new NumberEval(2.0);
public static final Function DOLLAR = new Var1or2ArgFunction() {
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() {
@ -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
}
double base = ds[1];
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) {
return logE;
result = logE;
} else {
result = logE / Math.log(base);
}
return logE / Math.log(base);
NumericFunction.checkValue(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
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());
}
};
}

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.
* @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;
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);
result = replaceAllOccurrences(oldStr, searchStr, newStr);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return new StringEval(result);
}
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, ValueEval arg3) {
String result;
switch (args.length) {
default:
throw new IllegalStateException("Cannot happen");
case 4:
int instanceNumber = evaluateIntArg(args[3], srcCellRow, srcCellCol);
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);
break;
case 3:
result = replaceAllOccurrences(oldStr, searchStr, newStr);
} catch (EvaluationException e) {
return e.getErrorEval();
}
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 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 &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)
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<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());
}
};
public static final Function EXACT = new TextFunction() {
public static final Function EXACT = new Fixed2ArgFunction() {
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,
ValueEval arg1) {
String s0;
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));
}
};

View File

@ -17,26 +17,24 @@
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.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;
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 retval;
return ErrorEval.VALUE_INVALID;
}
}

View File

@ -17,26 +17,24 @@
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.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;
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 retval;
return ErrorEval.VALUE_INVALID;
}
}

View File

@ -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 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) {
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 col_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 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);
return resultCol.getItem(rowIndex);
} catch (EvaluationException e) {

View File

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