From d2fd5808e6f82a3a4bc503cb8a3b2d08ee8424ee Mon Sep 17 00:00:00 2001 From: Josh Micich Date: Fri, 5 Sep 2008 18:22:30 +0000 Subject: [PATCH] Minor fixes for numeric operators - junit added. Some refactoring. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@692506 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hssf/record/formula/eval/AddEval.java | 97 ++++----------- .../hssf/record/formula/eval/DivideEval.java | 111 ++++-------------- .../record/formula/eval/MultiplyEval.java | 102 ++++------------ .../formula/eval/NumericOperationEval.java | 66 ----------- .../hssf/record/formula/eval/PercentEval.java | 51 +++----- .../hssf/record/formula/eval/PowerEval.java | 103 ++++------------ .../record/formula/eval/SubtractEval.java | 106 ++++------------- .../eval/TwoOperandNumericOperation.java | 55 +++++++++ .../record/formula/eval/UnaryMinusEval.java | 105 +++++++---------- .../record/formula/eval/UnaryPlusEval.java | 14 +-- .../eval/ValueEvalToNumericXlator.java | 37 +++--- .../usermodel/OperationEvaluatorFactory.java | 17 +-- .../formula/eval/AllFormulaEvalTests.java | 1 + .../record/formula/eval/TestDivideEval.java | 62 ++++++++++ .../record/formula/eval/TestPercentEval.java | 10 +- .../formula/eval/TestUnaryPlusEval.java | 34 +++--- 16 files changed, 348 insertions(+), 623 deletions(-) delete mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java create mode 100644 src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java index 6562263d5..bf1b42421 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/AddEval.java @@ -1,27 +1,22 @@ -/* -* 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 8, 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.eval; -import org.apache.poi.hssf.record.formula.AddPtg; -import org.apache.poi.hssf.record.formula.Ptg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -37,58 +32,14 @@ import org.apache.poi.hssf.record.formula.Ptg; *
  • 1+A1 = 2 if A1 contains TRUE or =TRUE *
  • 1+A1 = #VALUE! if A1 contains "TRUE" or ="TRUE" */ -public class AddEval extends NumericOperationEval { +public final class AddEval extends TwoOperandNumericOperation { - private AddPtg delegate; - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + public static final OperationEval instance = new AddEval(); - public AddEval(Ptg ptg) { - delegate = (AddPtg) ptg; - } + private AddEval() { + } - public ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - - double d = 0; - for (int i = 0; i < 2; i++) { - ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol); - if(ve instanceof ErrorEval) { - return ve; - } - if (ve instanceof NumericValueEval) { - d += ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - } - if(Double.isNaN(d) || Double.isInfinite(d)) { - return ErrorEval.NUM_ERROR; - } - return new NumberEval(d); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return d0 + d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java index 021168ad7..ba0b07e7e 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/DivideEval.java @@ -1,95 +1,36 @@ -/* -* 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. -*/ +/* ==================================================================== + 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.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.DividePtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class DivideEval extends NumericOperationEval { +public final class DivideEval extends TwoOperandNumericOperation { - private DividePtg delegate; + public static final OperationEval instance = new DivideEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private DivideEval() { + } - public DivideEval(Ptg ptg) { - delegate = (DividePtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - Eval retval = null; - double d0 = 0; - double d1 = 0; - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - - if (retval == null) { // no error yet - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - retval = (d1 == 0) - ? ErrorEval.DIV_ZERO - : (Double.isNaN(d0) || Double.isNaN(d1)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : new NumberEval(d0 / d1); - } - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) throws EvaluationException { + if (d1 == 0.0) { + throw new EvaluationException(ErrorEval.DIV_ZERO); + } + return d0 / d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java index 22d87b7e4..83b829c71 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/MultiplyEval.java @@ -1,89 +1,33 @@ -/* -* 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. -*/ +/* ==================================================================== + 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.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.MultiplyPtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class MultiplyEval extends NumericOperationEval { +public final class MultiplyEval extends TwoOperandNumericOperation { - private MultiplyPtg delegate; + public static final OperationEval instance = new MultiplyEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private MultiplyEval() { + } - public MultiplyEval(Ptg ptg) { - delegate = (MultiplyPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - - double d0 = 0; - double d1 = 0; - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - if (Double.isNaN(d0) || Double.isNaN(d1)) { - return ErrorEval.NUM_ERROR; - } - return new NumberEval(d0 * d1); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return d0 * d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java deleted file mode 100644 index 6bc1c95dc..000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/NumericOperationEval.java +++ /dev/null @@ -1,66 +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. -*/ -/* - * Created on May 14, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public abstract class NumericOperationEval implements OperationEval { - - protected abstract ValueEvalToNumericXlator getXlator(); - - 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; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java index c698a4e50..d03e44745 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/PercentEval.java @@ -17,55 +17,40 @@ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.PercentPtg; -import org.apache.poi.hssf.record.formula.Ptg; /** * Implementation of Excel formula token '%'.

    * @author Josh Micich */ -public final class PercentEval extends NumericOperationEval { +public final class PercentEval implements OperationEval { - private PercentPtg _delegate; + public static final OperationEval instance = new PercentEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = new ValueEvalToNumericXlator( - (short) (ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED)); - - public PercentEval(Ptg ptg) { - _delegate = (PercentPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; + private PercentEval() { } public Eval evaluate(Eval[] args, int srcRow, short srcCol) { if (args.length != 1) { return ErrorEval.VALUE_INVALID; } - - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - double d0 = ((NumericValueEval) ve).getNumberValue(); - return new NumberEval(d0 / 100); + 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(); } - - if (ve instanceof BlankEval) { - return NumberEval.ZERO; - } - if (ve instanceof ErrorEval) { - return ve; - } - return ErrorEval.VALUE_INVALID; + return new NumberEval(d0 / 100); } public int getNumberOfOperands() { - return _delegate.getNumberOfOperands(); - } - - public int getType() { - return _delegate.getType(); + return 1; } + public final int getType() { + // TODO - remove + throw new RuntimeException("obsolete code should not be called"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java index 651c5d2aa..685332a05 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/PowerEval.java @@ -1,90 +1,33 @@ -/* -* 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. -*/ +/* ==================================================================== + 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.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.PowerPtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class PowerEval extends NumericOperationEval { +public final class PowerEval extends TwoOperandNumericOperation { - private PowerPtg delegate; + public static final OperationEval instance = new PowerEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private PowerEval() { + } - public PowerEval(Ptg ptg) { - delegate = (PowerPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - double d0 = 0; - double d1 = 0; - - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - return ErrorEval.VALUE_INVALID; - } - - double p = Math.pow(d0, d1); - if (Double.isNaN(p)) { - return ErrorEval.VALUE_INVALID; - } - return new NumberEval(p); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return Math.pow(d0, d1); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java index 85a384529..cbd6dc5ee 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/SubtractEval.java @@ -1,93 +1,33 @@ -/* -* 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. -*/ +/* ==================================================================== + 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.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.SubtractPtg; - /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ -public final class SubtractEval extends NumericOperationEval { +public final class SubtractEval extends TwoOperandNumericOperation { - private SubtractPtg delegate; + public static final OperationEval instance = new SubtractEval(); - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + private SubtractEval() { + } - public SubtractEval(Ptg ptg) { - delegate = (SubtractPtg) ptg; - } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 2) { - return ErrorEval.VALUE_INVALID; - } - Eval retval = null; - double d0 = 0; - double d1 = 0; - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d0 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - - if (retval == null) { // no error yet - ve = singleOperandEvaluate(args[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d1 = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - retval = (Double.isNaN(d0) || Double.isNaN(d1)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : new NumberEval(d0 - d1); - } - return retval; - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } + protected double evaluate(double d0, double d1) { + return d0 - d1; + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java b/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java new file mode 100644 index 000000000..665ba4b46 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/eval/TwoOperandNumericOperation.java @@ -0,0 +1,55 @@ +/* ==================================================================== + 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.eval; + +/** + * @author Josh Micich + */ +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) { + double result; + try { + double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol); + double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol); + result = evaluate(d0, d1); + if (Double.isNaN(result) || Double.isInfinite(result)) { + return ErrorEval.NUM_ERROR; + } + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + protected abstract double evaluate(double d0, double d1) throws EvaluationException; + public final int getNumberOfOperands() { + return 2; + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java index ef6f533ea..8174429e0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryMinusEval.java @@ -1,75 +1,56 @@ -/* -* 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. -*/ +/* ==================================================================== + 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.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.UnaryMinusPtg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public final class UnaryMinusEval extends NumericOperationEval { +public final class UnaryMinusEval implements OperationEval { - private UnaryMinusPtg delegate; - private static final ValueEvalToNumericXlator NUM_XLATOR = - new ValueEvalToNumericXlator((short) - ( ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - )); + public static final OperationEval instance = new UnaryMinusEval(); + private UnaryMinusEval() { + } - public UnaryMinusEval(Ptg ptg) { - this.delegate = (UnaryMinusPtg) ptg; + public Eval evaluate(Eval[] args, int srcRow, short srcCol) { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + 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(); + } + return new NumberEval(-d); + } + + public int getNumberOfOperands() { + return 1; + } + public final int getType() { + // TODO - remove + throw new RuntimeException("obsolete code should not be called"); } - - protected ValueEvalToNumericXlator getXlator() { - return NUM_XLATOR; - } - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - if(args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - double d = 0; - - ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - d = ((NumericValueEval) ve).getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else if (ve instanceof ErrorEval) { - return ve; - } - - return new NumberEval(-d); - } - - public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); - } - - public int getType() { - return delegate.getType(); - } - } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java index edcc7bee7..66c5f6801 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/UnaryPlusEval.java @@ -17,8 +17,6 @@ package org.apache.poi.hssf.record.formula.eval; -import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.hssf.record.formula.UnaryPlusPtg; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -26,13 +24,9 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg; */ public final class UnaryPlusEval implements OperationEval { - private UnaryPlusPtg delegate; + public static final OperationEval instance = new UnaryPlusEval(); - /** - * called by reflection - */ - public UnaryPlusEval(Ptg ptg) { - this.delegate = (UnaryPlusPtg) ptg; + private UnaryPlusEval() { } public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { @@ -59,10 +53,10 @@ public final class UnaryPlusEval implements OperationEval { } public int getNumberOfOperands() { - return delegate.getNumberOfOperands(); + return 1; } public int getType() { - return delegate.getType(); + throw new RuntimeException("obsolete code should not be called"); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java b/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java index 1abcf34d2..46aa0ddf6 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java @@ -55,49 +55,40 @@ public final class ValueEvalToNumericXlator { * @param eval */ public ValueEval attemptXlateToNumeric(ValueEval eval) { - ValueEval retval = null; if (eval == null) { - retval = BlankEval.INSTANCE; + throw new IllegalArgumentException("eval must not be null"); } // most common case - least worries :) - else if (eval instanceof NumberEval) { - retval = eval; + if (eval instanceof NumberEval) { + return eval; } - // booleval - else if (eval instanceof BoolEval) { - retval = ((flags & BOOL_IS_PARSED) > 0) + if (eval instanceof BoolEval) { + return ((flags & BOOL_IS_PARSED) > 0) ? (NumericValueEval) eval : xlateBlankEval(BLANK_IS_PARSED); } - // stringeval - else if (eval instanceof StringEval) { - retval = xlateStringEval((StringEval) eval); // TODO: recursive call needed + if (eval instanceof StringEval) { + return xlateStringEval((StringEval) eval); // TODO: recursive call needed } - // refeval - else if (eval instanceof RefEval) { - retval = xlateRefEval((RefEval) eval); + if (eval instanceof RefEval) { + return xlateRefEval((RefEval) eval); } - // erroreval - else if (eval instanceof ErrorEval) { - retval = eval; + if (eval instanceof ErrorEval) { + return eval; } - else if (eval instanceof BlankEval) { - retval = xlateBlankEval(BLANK_IS_PARSED); + if (eval instanceof BlankEval) { + return xlateBlankEval(BLANK_IS_PARSED); } // probably AreaEval? then not acceptable. - else { - throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass()); - } - - return retval; + throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass()); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java b/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java index f7d3c9749..21dc06759 100755 --- a/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java +++ b/src/java/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java @@ -79,17 +79,9 @@ final class OperationEvaluatorFactory { private static Map initialiseConstructorsMap() { Map m = new HashMap(32); - add(m, AddPtg.class, AddEval.class); add(m, ConcatPtg.class, ConcatEval.class); - add(m, DividePtg.class, DivideEval.class); add(m, FuncPtg.class, FuncVarEval.class); add(m, FuncVarPtg.class, FuncVarEval.class); - add(m, MultiplyPtg.class, MultiplyEval.class); - add(m, PercentPtg.class, PercentEval.class); - add(m, PowerPtg.class, PowerEval.class); - add(m, SubtractPtg.class, SubtractEval.class); - add(m, UnaryMinusPtg.class, UnaryMinusEval.class); - add(m, UnaryPlusPtg.class, UnaryPlusEval.class); return m; } private static Map initialiseInstancesMap() { @@ -100,6 +92,15 @@ final class OperationEvaluatorFactory { add(m, LessEqualPtg.class, LessEqualEval.instance); add(m, LessThanPtg.class, LessThanEval.instance); add(m, NotEqualPtg.class, NotEqualEval.instance); + + add(m, AddPtg.class, AddEval.instance); + add(m, DividePtg.class, DivideEval.instance); + add(m, MultiplyPtg.class, MultiplyEval.instance); + add(m, PercentPtg.class, PercentEval.instance); + add(m, PowerPtg.class, PowerEval.instance); + add(m, SubtractPtg.class, SubtractEval.instance); + add(m, UnaryMinusPtg.class, UnaryMinusEval.instance); + add(m, UnaryPlusPtg.class, UnaryPlusEval.instance); return m; } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java index 3809a86a6..755106194 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java @@ -31,6 +31,7 @@ public class AllFormulaEvalTests { TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName()); result.addTestSuite(TestAreaEval.class); result.addTestSuite(TestCircularReferences.class); + result.addTestSuite(TestDivideEval.class); result.addTestSuite(TestEqualEval.class); result.addTestSuite(TestExternalFunction.class); result.addTestSuite(TestFormulaBugs.class); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java new file mode 100644 index 000000000..71bf03e50 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestDivideEval.java @@ -0,0 +1,62 @@ +/* ==================================================================== + 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.eval; + +import junit.framework.TestCase; + +import org.apache.poi.hssf.record.formula.functions.EvalFactory; +import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; + +/** + * Test for divide operator evaluator. + * + * @author Josh Micich + */ +public final class TestDivideEval extends TestCase { + + private static void confirm(ValueEval arg0, ValueEval arg1, double expectedResult) { + Eval[] args = { + arg0, arg1, + }; + + double result = NumericFunctionInvoker.invoke(DivideEval.instance, args, 0, 0); + + assertEquals(expectedResult, result, 0); + } + + public void testBasic() { + confirm(new NumberEval(5), new NumberEval(2), 2.5); + confirm(new NumberEval(3), new NumberEval(16), 0.1875); + confirm(new NumberEval(-150), new NumberEval(-15), 10.0); + confirm(new StringEval("0.2"), new NumberEval(0.05), 4.0); + confirm(BoolEval.TRUE, new StringEval("-0.2"), -5.0); + } + + public void test1x1Area() { + AreaEval ae0 = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), }); + AreaEval ae1 = EvalFactory.createAreaEval("C2:C2", new ValueEval[] { new NumberEval(10), }); + confirm(ae0, ae1, 5); + } + public void testDivZero() { + Eval[] args = { + new NumberEval(5), NumberEval.ZERO, + }; + Eval result = DivideEval.instance.evaluate(args, 0, (short) 0); + assertEquals(ErrorEval.DIV_ZERO, result); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java index 656c7d25a..310fce68a 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java @@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula.eval; import junit.framework.AssertionFailedError; import junit.framework.TestCase; -import org.apache.poi.hssf.record.formula.PercentPtg; +import org.apache.poi.hssf.record.formula.functions.EvalFactory; import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; @@ -41,8 +41,8 @@ public final class TestPercentEval extends TestCase { arg, }; - PercentEval opEval = new PercentEval(PercentPtg.instance); - double result = NumericFunctionInvoker.invoke(opEval, args, -1, (short)-1); + OperationEval opEval = PercentEval.instance; + double result = NumericFunctionInvoker.invoke(opEval, args, 0, 0); assertEquals(expectedResult, result, 0); } @@ -55,6 +55,10 @@ public final class TestPercentEval extends TestCase { confirm(BoolEval.TRUE, 0.01); } + public void test1x1Area() { + AreaEval ae = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), }); + confirm(ae, 0.5); + } public void testInSpreadSheet() { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet("Sheet1"); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java index 33c38a6c1..726633c40 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestUnaryPlusEval.java @@ -1,27 +1,25 @@ -/* -* 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. -*/ +/* ==================================================================== + 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.eval; import junit.framework.TestCase; import org.apache.poi.hssf.record.formula.AreaPtg; -import org.apache.poi.hssf.record.formula.UnaryPlusPtg; import org.apache.poi.hssf.record.formula.functions.EvalFactory; import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; @@ -53,7 +51,7 @@ public final class TestUnaryPlusEval extends TestCase { EvalFactory.createAreaEval(areaPtg, values), }; - double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20); + double result = NumericFunctionInvoker.invoke(UnaryPlusEval.instance, args, 10, (short)20); assertEquals(35, result, 0); }