(real) fix for bug #44691. Modified Pmt.java to allow for 3 arguments. Added TestPmt junit.
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@641996 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0d7c6a7b78
commit
4e35b2475d
@ -14,61 +14,78 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
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.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.NumericValueEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
public class Pmt extends FinanceFunction {
|
||||
/**
|
||||
* Implementation for the PMT() Excel function.<p/>
|
||||
*
|
||||
* <b>Syntax:</b><br/>
|
||||
* <b>PMT</b>(<b>rate</b>, <b>nper</b>, <b>pv</b>, fv, type)<p/>
|
||||
*
|
||||
* Returns the constant repayment amount required for a loan assuming a constant interest rate.<p/>
|
||||
*
|
||||
* <b>rate</b> the loan interest rate.<br/>
|
||||
* <b>nper</b> the number of loan repayments.<br/>
|
||||
* <b>pv</b> the present value of the future payments (or principle).<br/>
|
||||
* <b>fv</b> the future value (default zero) surplus cash at the end of the loan lifetime.<br/>
|
||||
* <b>type</b> whether payments are due at the beginning(1) or end(0 - default) of each payment period.<br/>
|
||||
*
|
||||
*/
|
||||
public final class Pmt extends FinanceFunction {
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
double rate = 0, fv = 0, nper = 0, pv = 0, d = 0;
|
||||
boolean type = false;
|
||||
ValueEval retval = null;
|
||||
ValueEval ve = null;
|
||||
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
case 5:
|
||||
ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol);
|
||||
if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; }
|
||||
type = ((BoolEval) ve).getBooleanValue();
|
||||
case 4:
|
||||
ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue();
|
||||
else { retval = ErrorEval.VALUE_INVALID; break; }
|
||||
|
||||
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) nper = ((NumericValueEval) ve).getNumberValue();
|
||||
else { retval = ErrorEval.VALUE_INVALID; break; }
|
||||
|
||||
ve = singleOperandEvaluate(operands[2], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) pv = ((NumericValueEval) ve).getNumberValue();
|
||||
else { retval = ErrorEval.VALUE_INVALID; break; }
|
||||
|
||||
ve = singleOperandEvaluate(operands[3], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) fv = ((NumericValueEval) ve).getNumberValue();
|
||||
else { retval = ErrorEval.VALUE_INVALID; break; }
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
d = FinanceLib.pmt(rate, nper, pv, fv, type);
|
||||
retval = (Double.isNaN(d))
|
||||
? (ValueEval) ErrorEval.VALUE_INVALID
|
||||
: (Double.isInfinite(d))
|
||||
? (ValueEval) ErrorEval.NUM_ERROR
|
||||
: new NumberEval(d);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
|
||||
if(args.length < 3 || args.length > 5) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
// evaluate first three (always present) args
|
||||
double rate = evalArg(args[0], srcRow, srcCol);
|
||||
double nper = evalArg(args[1], srcRow, srcCol);
|
||||
double pv = evalArg(args[2], srcRow, srcCol);
|
||||
double fv = 0;
|
||||
boolean arePaymentsAtPeriodBeginning = false;
|
||||
|
||||
switch (args.length) {
|
||||
case 5:
|
||||
ValueEval ve = singleOperandNumericAsBoolean(args[4], srcRow, srcCol);
|
||||
if (ve instanceof ErrorEval) {
|
||||
return ve;
|
||||
}
|
||||
arePaymentsAtPeriodBeginning = ((BoolEval) ve).getBooleanValue();
|
||||
case 4:
|
||||
fv = evalArg(args[3], srcRow, srcCol);
|
||||
}
|
||||
double d = FinanceLib.pmt(rate, nper, pv, fv, arePaymentsAtPeriodBeginning);
|
||||
if (Double.isNaN(d)) {
|
||||
return (ValueEval) ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
if (Double.isInfinite(d)) {
|
||||
return (ValueEval) ErrorEval.NUM_ERROR;
|
||||
}
|
||||
return new NumberEval(d);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
private double evalArg(Eval arg, int srcRow, short srcCol) throws EvaluationException {
|
||||
ValueEval ve = singleOperandEvaluate(arg, srcRow, srcCol);
|
||||
if(ve instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) ve);
|
||||
}
|
||||
if (ve instanceof NumericValueEval) {
|
||||
return ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
@ -41,6 +40,7 @@ public final class AllIndividualFunctionEvaluationTests {
|
||||
result.addTestSuite(TestMid.class);
|
||||
result.addTestSuite(TestMathX.class);
|
||||
result.addTestSuite(TestMatch.class);
|
||||
result.addTestSuite(TestPmt.class);
|
||||
result.addTestSuite(TestOffset.class);
|
||||
result.addTestSuite(TestRowCol.class);
|
||||
result.addTestSuite(TestSumproduct.class);
|
||||
@ -50,5 +50,4 @@ public final class AllIndividualFunctionEvaluationTests {
|
||||
result.addTestSuite(TestXYNumericFunction.class);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,87 @@
|
||||
/* ====================================================================
|
||||
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 junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
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.NumberEval;
|
||||
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class TestPmt extends TestCase {
|
||||
|
||||
private static void confirm(double expected, NumberEval ne) {
|
||||
// only asserting accuracy to 4 fractional digits
|
||||
assertEquals(expected, ne.getNumberValue(), 0.00005);
|
||||
}
|
||||
private static Eval invoke(Eval[] args) {
|
||||
return new Pmt().evaluate(args, -1, (short)-1);
|
||||
}
|
||||
/**
|
||||
* Invocation when not expecting an error result
|
||||
*/
|
||||
private static NumberEval invokeNormal(Eval[] args) {
|
||||
Eval ev = invoke(args);
|
||||
if(ev instanceof ErrorEval) {
|
||||
throw new AssertionFailedError("Normal evaluation failed with error code: "
|
||||
+ ev.toString());
|
||||
}
|
||||
return (NumberEval) ev;
|
||||
}
|
||||
|
||||
private static void confirm(double expected, double rate, double nper, double pv, double fv, boolean isBeginning) {
|
||||
Eval[] args = {
|
||||
new NumberEval(rate),
|
||||
new NumberEval(nper),
|
||||
new NumberEval(pv),
|
||||
new NumberEval(fv),
|
||||
new NumberEval(isBeginning ? 1 : 0),
|
||||
};
|
||||
confirm(expected, invokeNormal(args));
|
||||
}
|
||||
|
||||
|
||||
public void testBasic() {
|
||||
confirm(-1037.0321, (0.08/12), 10, 10000, 0, false);
|
||||
confirm(-1030.1643, (0.08/12), 10, 10000, 0, true);
|
||||
}
|
||||
|
||||
public void test3args() {
|
||||
|
||||
Eval[] args = {
|
||||
new NumberEval(0.005),
|
||||
new NumberEval(24),
|
||||
new NumberEval(1000),
|
||||
};
|
||||
Eval ev = invoke(args);
|
||||
if(ev instanceof ErrorEval) {
|
||||
ErrorEval err = (ErrorEval) ev;
|
||||
if(err.getErrorCode() == HSSFErrorConstants.ERROR_VALUE) {
|
||||
throw new AssertionFailedError("Identified bug 44691");
|
||||
}
|
||||
}
|
||||
|
||||
confirm(-44.3206, invokeNormal(args));
|
||||
}
|
||||
}
|
@ -1,70 +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.usermodel;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
|
||||
/**
|
||||
* The PMT formula seems to be giving some grief
|
||||
*/
|
||||
public final class TestBug44691 extends TestCase {
|
||||
String dirname;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
dirname = System.getProperty("HSSF.testdata.path");
|
||||
}
|
||||
|
||||
public void DISABLEDtestBug44691() throws Exception {
|
||||
HSSFWorkbook outWorkbook = new HSSFWorkbook();
|
||||
HSSFSheet outPMTSheet = outWorkbook.createSheet("PMT Sheet");
|
||||
HSSFRow row = outPMTSheet.createRow((short) 0);
|
||||
HSSFCell cell = row.createCell((short) 0);
|
||||
cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
|
||||
cell.setCellFormula("PMT(0.09/12,48,-10000)");
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
outWorkbook.write(baos);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
|
||||
HSSFWorkbook inWorkbook = new HSSFWorkbook(bais);
|
||||
|
||||
HSSFSheet inPMTSheet = inWorkbook.getSheet("PMT Sheet");
|
||||
HSSFFormulaEvaluator evaluator = new
|
||||
HSSFFormulaEvaluator(inPMTSheet, inWorkbook);
|
||||
CellReference cellReference = new CellReference("A1");
|
||||
HSSFRow inRow = inPMTSheet.getRow(cellReference.getRow());
|
||||
HSSFCell inCell = inRow.getCell(cellReference.getCol());
|
||||
|
||||
assertEquals("PMT(0.09/12,48,-10000)",
|
||||
inCell.getCellFormula());
|
||||
assertEquals(HSSFCell.CELL_TYPE_FORMULA, inCell.getCellType());
|
||||
|
||||
evaluator.setCurrentRow(inRow);
|
||||
HSSFFormulaEvaluator.CellValue inCellValue =
|
||||
evaluator.evaluate(inCell);
|
||||
|
||||
assertEquals(0, inCellValue.getErrorValue());
|
||||
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, inCellValue.getCellType());
|
||||
assertEquals(248.85, inCellValue.getNumberValue(), 0.0001);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user