From: Josh Micich Date: Wed, 10 Sep 2008 19:27:24 +0000 (+0000) Subject: (Should have been submitted with 693939) Fixing error value handling for numeric... X-Git-Tag: REL_3_2_FINAL~73 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=cd0a88ea0aac5a60058910eb8b9407c7b5085034;p=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 --- diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java index fd96f1495a..9a702ee6a3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java @@ -1,98 +1,316 @@ -/* -* 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. -*/ -/* - * Created on May 14, 2005 - * - */ +/* ==================================================================== + 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.ErrorEval; 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.ValueEvalToNumericXlator; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ public abstract class NumericFunction implements Function { - - protected static final double E = Math.E; - protected static final double PI = Math.PI; - - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | 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) { - ValueEval retval; - if (eval instanceof AreaEval) { - 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; - } + static final double ZERO = 0.0; + static final double TEN = 10.0; + static final double LOG_10_TO_BASE_e = Math.log(TEN); + + protected static final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + double result = OperandResolver.coerceValueToDouble(ve); + checkValue(result); + return result; + } + + private static final void checkValue(double result) throws EvaluationException { + if (Double.isNaN(result) || Double.isInfinite(result)) { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + } + + 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 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); + } + }; }