Refactored OperandResolver coerce functions to convert BlankEval to 0.0
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@693289 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f722d1e209
commit
906bceb891
@ -61,9 +61,6 @@ final class ParityFunction implements FreeRefFunction {
|
||||
private static int evaluateArgParity(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
|
||||
if (ve == BlankEval.INSTANCE) {
|
||||
return 0;
|
||||
}
|
||||
double d = OperandResolver.coerceValueToDouble(ve);
|
||||
if (d < 0) {
|
||||
d = -d;
|
||||
|
@ -97,9 +97,6 @@ final class YearFrac implements FreeRefFunction {
|
||||
Calendar date = parseDate(strVal);
|
||||
return HSSFDateUtil.getExcelDate(date, false);
|
||||
}
|
||||
if (ve instanceof BlankEval) {
|
||||
return 0.0;
|
||||
}
|
||||
return OperandResolver.coerceValueToDouble(ve);
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,8 @@ public final class OperandResolver {
|
||||
|
||||
/**
|
||||
* Applies some conversion rules if the supplied value is not already an integer.<br/>
|
||||
* Value is first coerced to a <tt>double</tt> ( See <tt>coerceValueToDouble()</tt> ).<p/>
|
||||
* Value is first coerced to a <tt>double</tt> ( See <tt>coerceValueToDouble()</tt> ).
|
||||
* Note - <tt>BlankEval</tt> is converted to <code>0</code>.<p/>
|
||||
*
|
||||
* Excel typically converts doubles to integers by truncating toward negative infinity.<br/>
|
||||
* The equivalent java code is:<br/>
|
||||
@ -181,6 +182,9 @@ public final class OperandResolver {
|
||||
*
|
||||
*/
|
||||
public static int coerceValueToInt(ValueEval ev) throws EvaluationException {
|
||||
if (ev == BlankEval.INSTANCE) {
|
||||
return 0;
|
||||
}
|
||||
double d = coerceValueToDouble(ev);
|
||||
// Note - the standard java type conversion from double to int truncates toward zero.
|
||||
// but Math.floor() truncates toward negative infinity
|
||||
@ -189,16 +193,20 @@ public final class OperandResolver {
|
||||
|
||||
/**
|
||||
* Applies some conversion rules if the supplied value is not already a number.
|
||||
* Note - <tt>BlankEval</tt> is not supported and must be handled by the caller.
|
||||
* @param ev must be a <tt>NumberEval</tt>, <tt>StringEval</tt> or <tt>BoolEval</tt>
|
||||
* Note - <tt>BlankEval</tt> is converted to {@link NumberEval#ZERO}.
|
||||
* @param ev must be a {@link NumberEval}, {@link StringEval}, {@link BoolEval} or
|
||||
* {@link BlankEval}
|
||||
* @return actual, parsed or interpreted double value (respectively).
|
||||
* @throws EvaluationException(#VALUE!) only if a StringEval is supplied and cannot be parsed
|
||||
* as a double (See <tt>parseDouble()</tt> for allowable formats).
|
||||
* @throws RuntimeException if the supplied parameter is not <tt>NumberEval</tt>,
|
||||
* <tt>StringEval</tt> or <tt>BoolEval</tt>
|
||||
* @throws RuntimeException if the supplied parameter is not {@link NumberEval},
|
||||
* {@link StringEval}, {@link BoolEval} or {@link BlankEval}
|
||||
*/
|
||||
public static double coerceValueToDouble(ValueEval ev) throws EvaluationException {
|
||||
|
||||
if (ev == BlankEval.INSTANCE) {
|
||||
return 0.0;
|
||||
}
|
||||
if (ev instanceof NumericValueEval) {
|
||||
// this also handles booleans
|
||||
return ((NumericValueEval)ev).getNumberValue();
|
||||
|
@ -33,12 +33,9 @@ public final class PercentEval implements OperationEval {
|
||||
if (args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double d0;
|
||||
double d0;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol);
|
||||
if (ve instanceof BlankEval) {
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
d0 = OperandResolver.coerceValueToDouble(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
@ -50,7 +47,7 @@ public final class PercentEval implements OperationEval {
|
||||
return 1;
|
||||
}
|
||||
public final int getType() {
|
||||
// TODO - remove
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
// TODO - remove
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
}
|
||||
|
@ -23,18 +23,15 @@ package org.apache.poi.hssf.record.formula.eval;
|
||||
abstract class TwoOperandNumericOperation implements OperationEval {
|
||||
|
||||
public final int getType() {
|
||||
// TODO - remove
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
protected final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
if (ve instanceof BlankEval) {
|
||||
return 0.0;
|
||||
}
|
||||
return OperandResolver.coerceValueToDouble(ve);
|
||||
}
|
||||
|
||||
public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
// TODO - remove
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
protected final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
return OperandResolver.coerceValueToDouble(ve);
|
||||
}
|
||||
|
||||
public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
|
||||
@ -46,8 +43,8 @@ abstract class TwoOperandNumericOperation implements OperationEval {
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
protected abstract double evaluate(double d0, double d1) throws EvaluationException;
|
||||
public final int getNumberOfOperands() {
|
||||
return 2;
|
||||
|
@ -33,12 +33,9 @@ public final class UnaryMinusEval implements OperationEval {
|
||||
if (args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double d;
|
||||
double d;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol);
|
||||
if (ve instanceof BlankEval) {
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
d = OperandResolver.coerceValueToDouble(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
@ -50,7 +47,7 @@ public final class UnaryMinusEval implements OperationEval {
|
||||
return 1;
|
||||
}
|
||||
public final int getType() {
|
||||
// TODO - remove
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
// TODO - remove
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
}
|
||||
|
@ -24,21 +24,18 @@ package org.apache.poi.hssf.record.formula.eval;
|
||||
*/
|
||||
public final class UnaryPlusEval implements OperationEval {
|
||||
|
||||
public static final OperationEval instance = new UnaryPlusEval();
|
||||
|
||||
private UnaryPlusEval() {
|
||||
}
|
||||
public static final OperationEval instance = new UnaryPlusEval();
|
||||
|
||||
private UnaryPlusEval() {
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
if(args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double d;
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
if(args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double d;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
if(ve instanceof BlankEval) {
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
if(ve instanceof StringEval) {
|
||||
// Note - asymmetric with UnaryMinus
|
||||
// -"hello" evaluates to #VALUE!
|
||||
@ -49,14 +46,14 @@ public final class UnaryPlusEval implements OperationEval {
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(+d);
|
||||
}
|
||||
return new NumberEval(+d);
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
return 1;
|
||||
}
|
||||
public int getNumberOfOperands() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
public int getType() {
|
||||
throw new RuntimeException("obsolete code should not be called");
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
@ -58,12 +57,7 @@ public final class CalendarFieldFunction implements Function {
|
||||
int val;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(operands[0], srcCellRow, srcCellCol);
|
||||
|
||||
if (ve == BlankEval.INSTANCE) {
|
||||
val = 0;
|
||||
} else {
|
||||
val = OperandResolver.coerceValueToInt(ve);
|
||||
}
|
||||
val = OperandResolver.coerceValueToInt(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
@ -339,23 +339,19 @@ final class LookupUtils {
|
||||
throw EvaluationException.invalidRef();
|
||||
}
|
||||
int oneBasedIndex;
|
||||
if(veRowColIndexArg instanceof BlankEval) {
|
||||
oneBasedIndex = 0;
|
||||
} else {
|
||||
if(veRowColIndexArg instanceof StringEval) {
|
||||
StringEval se = (StringEval) veRowColIndexArg;
|
||||
String strVal = se.getStringValue();
|
||||
Double dVal = OperandResolver.parseDouble(strVal);
|
||||
if(dVal == null) {
|
||||
// String does not resolve to a number. Raise #REF! error.
|
||||
throw EvaluationException.invalidRef();
|
||||
// This includes text booleans "TRUE" and "FALSE". They are not valid.
|
||||
}
|
||||
// else - numeric value parses OK
|
||||
if(veRowColIndexArg instanceof StringEval) {
|
||||
StringEval se = (StringEval) veRowColIndexArg;
|
||||
String strVal = se.getStringValue();
|
||||
Double dVal = OperandResolver.parseDouble(strVal);
|
||||
if(dVal == null) {
|
||||
// String does not resolve to a number. Raise #REF! error.
|
||||
throw EvaluationException.invalidRef();
|
||||
// This includes text booleans "TRUE" and "FALSE". They are not valid.
|
||||
}
|
||||
// actual BoolEval values get interpreted as FALSE->0 and TRUE->1
|
||||
oneBasedIndex = OperandResolver.coerceValueToInt(veRowColIndexArg);
|
||||
// else - numeric value parses OK
|
||||
}
|
||||
// actual BoolEval values get interpreted as FALSE->0 and TRUE->1
|
||||
oneBasedIndex = OperandResolver.coerceValueToInt(veRowColIndexArg);
|
||||
if (oneBasedIndex < 1) {
|
||||
// note this is asymmetric with the errors when the index is too large (#REF!)
|
||||
throw EvaluationException.invalidValue();
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
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.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
@ -81,12 +80,8 @@ public class Mid implements Function {
|
||||
|
||||
private static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
if (ev instanceof BlankEval) {
|
||||
// Note - for start_num arg, blank causes error(#VALUE!),
|
||||
// but for num_chars causes empty string to be returned.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note - for start_num arg, blank/zero causes error(#VALUE!),
|
||||
// but for num_chars causes empty string to be returned.
|
||||
return OperandResolver.coerceValueToInt(ev);
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
@ -77,8 +77,9 @@ public final class TestExternalFunctionFormulas extends TestCase {
|
||||
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
|
||||
confirmCellEval(sheet, 0, 0, fe, "YEARFRAC(B1,C1)", 29.0/90.0);
|
||||
confirmCellEval(sheet, 1, 0, fe, "YEARFRAC(B2,C2)", 0.0);
|
||||
confirmCellEval(sheet, 2, 0, fe, "IF(ISEVEN(3),1.2,1.6)", 1.6);
|
||||
confirmCellEval(sheet, 3, 0, fe, "IF(ISODD(3),1.2,1.6)", 1.2);
|
||||
confirmCellEval(sheet, 2, 0, fe, "YEARFRAC(B3,C3,D3)", 0.0);
|
||||
confirmCellEval(sheet, 3, 0, fe, "IF(ISEVEN(3),1.2,1.6)", 1.6);
|
||||
confirmCellEval(sheet, 4, 0, fe, "IF(ISODD(3),1.2,1.6)", 1.2);
|
||||
}
|
||||
|
||||
private static void confirmCellEval(HSSFSheet sheet, int rowIx, int colIx,
|
||||
|
Loading…
Reference in New Issue
Block a user