mirror of https://github.com/apache/poi.git
(Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy.
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@693941 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ea1c2e1863
commit
cd0a88ea0a
|
@ -1,98 +1,316 @@
|
||||||
/*
|
/* ====================================================================
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
* this work for additional information regarding copyright ownership.
|
this work for additional information regarding copyright ownership.
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
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 not use this file except in compliance with
|
||||||
* the License. You may obtain a copy of the License at
|
the License. You may obtain a copy of the License at
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
limitations under the License.
|
||||||
*/
|
==================================================================== */
|
||||||
/*
|
|
||||||
* Created on May 14, 2005
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
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.ErrorEval;
|
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.Eval;
|
||||||
|
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;
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public abstract class NumericFunction implements Function {
|
public abstract class NumericFunction implements Function {
|
||||||
|
|
||||||
protected static final double E = Math.E;
|
static final double ZERO = 0.0;
|
||||||
protected static final double PI = Math.PI;
|
static final double TEN = 10.0;
|
||||||
|
static final double LOG_10_TO_BASE_e = Math.log(TEN);
|
||||||
|
|
||||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
protected static final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||||
new ValueEvalToNumericXlator((short) (
|
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
double result = OperandResolver.coerceValueToDouble(ve);
|
||||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
checkValue(result);
|
||||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
return result;
|
||||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
|
||||||
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
|
|
||||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
|
||||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
|
||||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
|
||||||
));
|
|
||||||
|
|
||||||
private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this is the default impl of the factory(ish) method getXlator.
|
|
||||||
* Subclasses can override this method
|
|
||||||
* if they desire to return a different ValueEvalToNumericXlator instance
|
|
||||||
* than the default.
|
|
||||||
*/
|
|
||||||
protected ValueEvalToNumericXlator getXlator() {
|
|
||||||
return DEFAULT_NUM_XLATOR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
|
private static final void checkValue(double result) throws EvaluationException {
|
||||||
ValueEval retval;
|
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
||||||
if (eval instanceof AreaEval) {
|
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||||
AreaEval ae = (AreaEval) eval;
|
|
||||||
if (ae.contains(srcRow, srcCol)) { // circular ref!
|
|
||||||
retval = ErrorEval.CIRCULAR_REF_ERROR;
|
|
||||||
}
|
}
|
||||||
else if (ae.isRow()) {
|
|
||||||
if (ae.containsColumn(srcCol)) {
|
|
||||||
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
|
|
||||||
ve = getXlator().attemptXlateToNumeric(ve);
|
|
||||||
retval = getXlator().attemptXlateToNumeric(ve);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
retval = ErrorEval.VALUE_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ae.isColumn()) {
|
|
||||||
if (ae.containsRow(srcRow)) {
|
|
||||||
ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
|
|
||||||
retval = getXlator().attemptXlateToNumeric(ve);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
retval = ErrorEval.VALUE_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
retval = ErrorEval.VALUE_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
retval = getXlator().attemptXlateToNumeric((ValueEval) eval);
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||||
|
double result;
|
||||||
|
try {
|
||||||
|
result = eval(args, srcCellRow, srcCellCol);
|
||||||
|
checkValue(result);
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
return new NumberEval(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException;
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
// intermediate sub-classes (one-arg, two-arg and multi-arg
|
||||||
|
|
||||||
|
|
||||||
|
public static abstract class OneArg extends NumericFunction {
|
||||||
|
protected OneArg() {
|
||||||
|
// no fields to initialise
|
||||||
|
}
|
||||||
|
protected final double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||||
|
if (args.length != 1) {
|
||||||
|
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
double d = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
|
||||||
|
return evaluate(d);
|
||||||
|
}
|
||||||
|
protected abstract double evaluate(double d) throws EvaluationException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class TwoArg extends NumericFunction {
|
||||||
|
protected TwoArg() {
|
||||||
|
// no fields to initialise
|
||||||
|
}
|
||||||
|
protected final double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||||
|
if (args.length != 2) {
|
||||||
|
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
|
||||||
|
double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol);
|
||||||
|
return evaluate(d0, d1);
|
||||||
|
}
|
||||||
|
protected abstract double evaluate(double d0, double d1) throws EvaluationException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class MultiArg extends NumericFunction {
|
||||||
|
private final int _minArgs;
|
||||||
|
private final int _maxArgs;
|
||||||
|
protected MultiArg(int minArgs, int maxArgs) {
|
||||||
|
_minArgs = minArgs;
|
||||||
|
_maxArgs = maxArgs;
|
||||||
|
}
|
||||||
|
protected final double eval(Eval[] args, int srcCellRow, short 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public static final Function ABS = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.abs(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ACOS = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.acos(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ACOSH = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.acosh(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ASIN = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.asin(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ASINH = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.asinh(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ATAN = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.atan(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ATANH = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.atanh(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function COS = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.cos(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function COSH = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.cosh(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function DEGREES = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.toDegrees(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function DOLLAR = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function EXP = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.pow(Math.E, d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function FACT = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.factorial((int)d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function INT = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.round(d-0.5);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function LN = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.log(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function LOG10 = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.log(d) / LOG_10_TO_BASE_e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function RADIANS = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.toRadians(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function SIGN = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.sign(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function SIN = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.sin(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function SINH = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.sinh(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function SQRT = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.sqrt(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final Function TAN = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return Math.tan(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function TANH = new OneArg() {
|
||||||
|
protected double evaluate(double d) {
|
||||||
|
return MathX.tanh(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
public static final Function ATAN2 = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||||
|
if (d0 == ZERO && d1 == ZERO) {
|
||||||
|
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||||
|
}
|
||||||
|
return Math.atan2(d1, d0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function CEILING = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) {
|
||||||
|
return MathX.ceiling(d0, d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function COMBIN = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||||
|
if (d0 > Integer.MAX_VALUE || d1 > Integer.MAX_VALUE) {
|
||||||
|
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||||
|
}
|
||||||
|
return MathX.nChooseK((int) d0, (int) d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function FLOOR = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||||
|
if (d1 == ZERO) {
|
||||||
|
if (d0 == ZERO) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||||
|
}
|
||||||
|
return MathX.floor(d0, d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function MOD = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||||
|
if (d1 == ZERO) {
|
||||||
|
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||||
|
}
|
||||||
|
return MathX.mod(d0, d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function POWER = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) {
|
||||||
|
return Math.pow(d0, d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ROUND = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) {
|
||||||
|
return MathX.round(d0, (int)d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ROUNDDOWN = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) {
|
||||||
|
return MathX.roundDown(d0, (int)d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function ROUNDUP = new TwoArg() {
|
||||||
|
protected double evaluate(double d0, double d1) {
|
||||||
|
return MathX.roundUp(d0, (int)d1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
double base = ds[1];
|
||||||
|
if (base == Math.E) {
|
||||||
|
return logE;
|
||||||
|
}
|
||||||
|
return logE / Math.log(base);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue