git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1795963 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_17_BETA1
import org.apache.poi.ss.SpreadsheetVersion; | import org.apache.poi.ss.SpreadsheetVersion; | ||||
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; | import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; | ||||
import org.apache.poi.ss.formula.atp.AnalysisToolPak; | import org.apache.poi.ss.formula.atp.AnalysisToolPak; | ||||
import org.apache.poi.ss.formula.eval.BlankEval; | |||||
import org.apache.poi.ss.formula.eval.BoolEval; | |||||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||||
import org.apache.poi.ss.formula.eval.EvaluationException; | |||||
import org.apache.poi.ss.formula.eval.ExternalNameEval; | |||||
import org.apache.poi.ss.formula.eval.FunctionEval; | |||||
import org.apache.poi.ss.formula.eval.FunctionNameEval; | |||||
import org.apache.poi.ss.formula.eval.MissingArgEval; | |||||
import org.apache.poi.ss.formula.eval.NotImplementedException; | |||||
import org.apache.poi.ss.formula.eval.NumberEval; | |||||
import org.apache.poi.ss.formula.eval.OperandResolver; | |||||
import org.apache.poi.ss.formula.eval.StringEval; | |||||
import org.apache.poi.ss.formula.eval.ValueEval; | |||||
import org.apache.poi.ss.formula.eval.*; | |||||
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry; | import org.apache.poi.ss.formula.function.FunctionMetadataRegistry; | ||||
import org.apache.poi.ss.formula.functions.Choose; | import org.apache.poi.ss.formula.functions.Choose; | ||||
import org.apache.poi.ss.formula.functions.FreeRefFunction; | import org.apache.poi.ss.formula.functions.FreeRefFunction; | ||||
private final IStabilityClassifier _stabilityClassifier; | private final IStabilityClassifier _stabilityClassifier; | ||||
private final AggregatingUDFFinder _udfFinder; | private final AggregatingUDFFinder _udfFinder; | ||||
private boolean _ignoreMissingWorkbooks = false; | |||||
private boolean _ignoreMissingWorkbooks; | |||||
/** | /** | ||||
* whether print detailed messages about the next formula evaluation | * whether print detailed messages about the next formula evaluation | ||||
*/ | */ | ||||
private boolean dbgEvaluationOutputForNextEval = false; | |||||
private boolean dbgEvaluationOutputForNextEval; | |||||
// special logger for formula evaluation output (because of possibly very large output) | // special logger for formula evaluation output (because of possibly very large output) | ||||
private final POILogger EVAL_LOG = POILogFactory.getLogger("POI.FormulaEval"); | private final POILogger EVAL_LOG = POILogFactory.getLogger("POI.FormulaEval"); | ||||
Stack<ValueEval> stack = new Stack<ValueEval>(); | Stack<ValueEval> stack = new Stack<ValueEval>(); | ||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) { | for (int i = 0, iSize = ptgs.length; i < iSize; i++) { | ||||
// since we don't know how to handle these yet :( | // since we don't know how to handle these yet :( | ||||
Ptg ptg = ptgs[i]; | Ptg ptg = ptgs[i]; | ||||
if (dbgEvaluationOutputIndent > 0) { | if (dbgEvaluationOutputIndent > 0) { | ||||
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg); | |||||
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg + ", stack: " + stack); | |||||
} | } | ||||
if (ptg instanceof AttrPtg) { | if (ptg instanceof AttrPtg) { | ||||
AttrPtg attrPtg = (AttrPtg) ptg; | AttrPtg attrPtg = (AttrPtg) ptg; | ||||
continue; | continue; | ||||
} | } | ||||
if (ptg instanceof UnionPtg) { | |||||
ValueEval v2 = stack.pop(); | |||||
ValueEval v1 = stack.pop(); | |||||
stack.push(new RefListEval(v1, v2)); | |||||
continue; | |||||
} | |||||
ValueEval opResult; | ValueEval opResult; | ||||
if (ptg instanceof OperationPtg) { | if (ptg instanceof OperationPtg) { | ||||
OperationPtg optg = (OperationPtg) ptg; | OperationPtg optg = (OperationPtg) ptg; | ||||
if (optg instanceof UnionPtg) { continue; } | |||||
int numops = optg.getNumberOfOperands(); | int numops = optg.getNumberOfOperands(); | ||||
ValueEval[] ops = new ValueEval[numops]; | ValueEval[] ops = new ValueEval[numops]; | ||||
/* ==================================================================== | |||||
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.ss.formula.eval; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | |||||
* Handling of a list of values, e.g. the 2nd argument in RANK(A1,(B1,B2,B3),1) | |||||
*/ | |||||
public class RefListEval implements ValueEval { | |||||
private final List<ValueEval> list = new ArrayList<ValueEval>(); | |||||
public RefListEval(ValueEval v1, ValueEval v2) { | |||||
add(v1); | |||||
add(v2); | |||||
} | |||||
private void add(ValueEval v) { | |||||
// flatten multiple nested RefListEval | |||||
if(v instanceof RefListEval) { | |||||
list.addAll(((RefListEval)v).list); | |||||
} else { | |||||
list.add(v); | |||||
} | |||||
} | |||||
public List<ValueEval> getList() { | |||||
return list; | |||||
} | |||||
} |
package org.apache.poi.ss.formula.functions; | package org.apache.poi.ss.formula.functions; | ||||
import org.apache.poi.ss.formula.eval.AreaEval; | |||||
import org.apache.poi.ss.formula.eval.BlankEval; | |||||
import org.apache.poi.ss.formula.eval.BoolEval; | |||||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||||
import org.apache.poi.ss.formula.eval.EvaluationException; | |||||
import org.apache.poi.ss.formula.eval.NumberEval; | |||||
import org.apache.poi.ss.formula.eval.OperandResolver; | |||||
import org.apache.poi.ss.formula.eval.RefEval; | |||||
import org.apache.poi.ss.formula.eval.StringEval; | |||||
import org.apache.poi.ss.formula.eval.ValueEval; | |||||
import org.apache.poi.ss.formula.eval.*; | |||||
/** | /** | ||||
* Implementation of the various ISxxx Logical Functions, which | * Implementation of the various ISxxx Logical Functions, which | ||||
public static final Function ISREF = new Fixed1ArgFunction() { | public static final Function ISREF = new Fixed1ArgFunction() { | ||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) { | ||||
if (arg0 instanceof RefEval || arg0 instanceof AreaEval) { | |||||
if (arg0 instanceof RefEval || arg0 instanceof AreaEval || arg0 instanceof RefListEval) { | |||||
return BoolEval.TRUE; | return BoolEval.TRUE; | ||||
} | } | ||||
return BoolEval.FALSE; | return BoolEval.FALSE; |
package org.apache.poi.ss.formula.functions; | package org.apache.poi.ss.formula.functions; | ||||
import org.apache.poi.ss.formula.eval.AreaEval; | |||||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||||
import org.apache.poi.ss.formula.eval.EvaluationException; | |||||
import org.apache.poi.ss.formula.eval.NumberEval; | |||||
import org.apache.poi.ss.formula.eval.OperandResolver; | |||||
import org.apache.poi.ss.formula.eval.RefEval; | |||||
import org.apache.poi.ss.formula.eval.ValueEval; | |||||
import org.apache.poi.ss.formula.eval.*; | |||||
/** | /** | ||||
public class Rank extends Var2or3ArgFunction { | public class Rank extends Var2or3ArgFunction { | ||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { | ||||
AreaEval aeRange; | |||||
double result; | |||||
try { | try { | ||||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | ||||
result = OperandResolver.coerceValueToDouble(ve); | |||||
double result = OperandResolver.coerceValueToDouble(ve); | |||||
if (Double.isNaN(result) || Double.isInfinite(result)) { | if (Double.isNaN(result) || Double.isInfinite(result)) { | ||||
throw new EvaluationException(ErrorEval.NUM_ERROR); | throw new EvaluationException(ErrorEval.NUM_ERROR); | ||||
} | } | ||||
aeRange = convertRangeArg(arg1); | |||||
if(arg1 instanceof RefListEval) { | |||||
return eval(result, ((RefListEval)arg1), true); | |||||
} | |||||
final AreaEval aeRange = convertRangeArg(arg1); | |||||
return eval(result, aeRange, true); | |||||
} catch (EvaluationException e) { | } catch (EvaluationException e) { | ||||
return e.getErrorEval(); | return e.getErrorEval(); | ||||
} | } | ||||
return eval(srcRowIndex, srcColumnIndex, result, aeRange, true); | |||||
} | } | ||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) { | public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) { | ||||
AreaEval aeRange; | |||||
double result; | |||||
boolean order=false; | |||||
try { | try { | ||||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex); | ||||
result = OperandResolver.coerceValueToDouble(ve); | |||||
final double result = OperandResolver.coerceValueToDouble(ve); | |||||
if (Double.isNaN(result) || Double.isInfinite(result)) { | if (Double.isNaN(result) || Double.isInfinite(result)) { | ||||
throw new EvaluationException(ErrorEval.NUM_ERROR); | throw new EvaluationException(ErrorEval.NUM_ERROR); | ||||
} | } | ||||
aeRange = convertRangeArg(arg1); | |||||
ve = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex); | |||||
int order_value = OperandResolver.coerceValueToInt(ve); | |||||
if(order_value==0){ | |||||
order=true; | |||||
}else if(order_value==1){ | |||||
order=false; | |||||
}else throw new EvaluationException(ErrorEval.NUM_ERROR); | |||||
ve = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex); | |||||
int order_value = OperandResolver.coerceValueToInt(ve); | |||||
final boolean order; | |||||
if(order_value==0) { | |||||
order = true; | |||||
} else if(order_value==1) { | |||||
order = false; | |||||
} else { | |||||
throw new EvaluationException(ErrorEval.NUM_ERROR); | |||||
} | |||||
if(arg1 instanceof RefListEval) { | |||||
return eval(result, ((RefListEval)arg1), order); | |||||
} | |||||
final AreaEval aeRange = convertRangeArg(arg1); | |||||
return eval(result, aeRange, order); | |||||
} catch (EvaluationException e) { | } catch (EvaluationException e) { | ||||
return e.getErrorEval(); | return e.getErrorEval(); | ||||
} | } | ||||
return eval(srcRowIndex, srcColumnIndex, result, aeRange, order); | |||||
} | } | ||||
private static ValueEval eval(int srcRowIndex, int srcColumnIndex, double arg0, AreaEval aeRange, boolean descending_order) { | |||||
private static ValueEval eval(double arg0, AreaEval aeRange, boolean descending_order) { | |||||
int rank = 1; | int rank = 1; | ||||
int height=aeRange.getHeight(); | int height=aeRange.getHeight(); | ||||
int width= aeRange.getWidth(); | int width= aeRange.getWidth(); | ||||
} | } | ||||
return new NumberEval(rank); | return new NumberEval(rank); | ||||
} | } | ||||
private static Double getValue(AreaEval aeRange, int relRowIndex, int relColIndex) { | |||||
private static ValueEval eval(double arg0, RefListEval aeRange, boolean descending_order) { | |||||
int rank = 1; | |||||
for(ValueEval ve : aeRange.getList()) { | |||||
if (ve instanceof RefEval) { | |||||
ve = ((RefEval) ve).getInnerValueEval(((RefEval) ve).getFirstSheetIndex()); | |||||
} | |||||
final Double value; | |||||
if (ve instanceof NumberEval) { | |||||
value = ((NumberEval)ve).getNumberValue(); | |||||
} else { | |||||
continue; | |||||
} | |||||
if(descending_order && value>arg0 || !descending_order && value<arg0){ | |||||
rank++; | |||||
} | |||||
} | |||||
return new NumberEval(rank); | |||||
} | |||||
private static Double getValue(AreaEval aeRange, int relRowIndex, int relColIndex) { | |||||
ValueEval addend = aeRange.getRelativeValue(relRowIndex, relColIndex); | ValueEval addend = aeRange.getRelativeValue(relRowIndex, relColIndex); | ||||
if (addend instanceof NumberEval) { | if (addend instanceof NumberEval) { | ||||
return ((NumberEval)addend).getNumberValue(); | return ((NumberEval)addend).getNumberValue(); |
wb.close(); | wb.close(); | ||||
} | } | ||||
@Test | |||||
public void bug61063() throws Exception { | |||||
Workbook wb = XSSFTestDataSamples.openSampleWorkbook("61063.xlsx"); | |||||
FormulaEvaluator eval = wb.getCreationHelper().createFormulaEvaluator(); | |||||
Sheet s = wb.getSheetAt(0); | |||||
Row r = s.getRow(3); | |||||
Cell c = r.getCell(0); | |||||
assertEquals(CellType.FORMULA, c.getCellTypeEnum()); | |||||
System.out.println(c.getCellFormula()); | |||||
eval.setDebugEvaluationOutputForNextEval(true); | |||||
CellValue cv = eval.evaluate(c); | |||||
assertNotNull(cv); | |||||
assertEquals("Had: " + cv, 2.0, cv.getNumberValue(), 0.00001); | |||||
wb.close(); | |||||
} | |||||
} | } |
import org.apache.poi.ss.formula.eval.MissingArgEval; | import org.apache.poi.ss.formula.eval.MissingArgEval; | ||||
import org.apache.poi.ss.formula.eval.NumberEval; | import org.apache.poi.ss.formula.eval.NumberEval; | ||||
import org.apache.poi.ss.formula.eval.ValueEval; | import org.apache.poi.ss.formula.eval.ValueEval; | ||||
import org.apache.poi.ss.formula.ptg.AreaErrPtg; | |||||
import org.apache.poi.ss.formula.ptg.AttrPtg; | |||||
import org.apache.poi.ss.formula.ptg.DeletedArea3DPtg; | |||||
import org.apache.poi.ss.formula.ptg.DeletedRef3DPtg; | |||||
import org.apache.poi.ss.formula.ptg.IntPtg; | |||||
import org.apache.poi.ss.formula.ptg.Ptg; | |||||
import org.apache.poi.ss.formula.ptg.RefErrorPtg; | |||||
import org.apache.poi.ss.formula.ptg.*; | |||||
import org.apache.poi.ss.usermodel.Cell; | import org.apache.poi.ss.usermodel.Cell; | ||||
import org.apache.poi.ss.usermodel.CellType; | import org.apache.poi.ss.usermodel.CellType; | ||||
import org.apache.poi.ss.usermodel.CellValue; | import org.apache.poi.ss.usermodel.CellValue; | ||||
*/ | */ | ||||
@Test | @Test | ||||
public void testMemFunc() { | public void testMemFunc() { | ||||
Ptg[] ptgs = { | Ptg[] ptgs = { | ||||
new IntPtg(42), | new IntPtg(42), | ||||
AttrPtg.SUM, | AttrPtg.SUM, | ||||
assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0); | assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0); | ||||
} | } | ||||
@Test | @Test | ||||
public void testEvaluateMultipleWorkbooks() { | public void testEvaluateMultipleWorkbooks() { | ||||
HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls"); | HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls"); | ||||
/** | /** | ||||
* Functions like IF, INDIRECT, INDEX, OFFSET etc can return AreaEvals which | * Functions like IF, INDIRECT, INDEX, OFFSET etc can return AreaEvals which | ||||
* should be dereferenced by the evaluator | * should be dereferenced by the evaluator | ||||
* @throws IOException | |||||
*/ | */ | ||||
@Test | @Test | ||||
public void testResultOutsideRange() throws IOException { | public void testResultOutsideRange() throws IOException { | ||||
/** | /** | ||||
* formulas with defined names. | * formulas with defined names. | ||||
* @throws IOException | |||||
*/ | */ | ||||
@Test | @Test | ||||
public void testNamesInFormulas() throws IOException { | public void testNamesInFormulas() throws IOException { | ||||
final String formula, final CellType cellType, final String expectedFormula, final double expectedValue) { | final String formula, final CellType cellType, final String expectedFormula, final double expectedValue) { | ||||
testIFEqualsFormulaEvaluation_evaluate(formula, cellType, expectedFormula, expectedValue); | testIFEqualsFormulaEvaluation_evaluate(formula, cellType, expectedFormula, expectedValue); | ||||
testIFEqualsFormulaEvaluation_evaluateFormulaCell(formula, cellType, expectedFormula, expectedValue); | testIFEqualsFormulaEvaluation_evaluateFormulaCell(formula, cellType, expectedFormula, expectedValue); | ||||
testIFEqualsFormulaEvaluation_evaluateInCell(formula, cellType, expectedFormula, expectedValue); | |||||
testIFEqualsFormulaEvaluation_evaluateInCell(formula, cellType, expectedValue); | |||||
testIFEqualsFormulaEvaluation_evaluateAll(formula, cellType, expectedFormula, expectedValue); | testIFEqualsFormulaEvaluation_evaluateAll(formula, cellType, expectedFormula, expectedValue); | ||||
testIFEqualsFormulaEvaluation_evaluateAllFormulaCells(formula, cellType, expectedFormula, expectedValue); | testIFEqualsFormulaEvaluation_evaluateAllFormulaCells(formula, cellType, expectedFormula, expectedValue); | ||||
} | } | ||||
} | } | ||||
private void testIFEqualsFormulaEvaluation_evaluateInCell( | private void testIFEqualsFormulaEvaluation_evaluateInCell( | ||||
String formula, CellType cellType, String expectedFormula, double expectedResult) { | |||||
String formula, CellType cellType, double expectedResult) { | |||||
Workbook wb = testIFEqualsFormulaEvaluation_setup(formula, cellType); | Workbook wb = testIFEqualsFormulaEvaluation_setup(formula, cellType); | ||||
Cell D1 = wb.getSheet("IFEquals").getRow(0).getCell(3); | Cell D1 = wb.getSheet("IFEquals").getRow(0).getCell(3); | ||||
try { | try { | ||||
D1.getCellFormula(); | D1.getCellFormula(); | ||||
fail("cell formula should be overwritten with formula result"); | fail("cell formula should be overwritten with formula result"); | ||||
} catch (final IllegalStateException expected) { } | |||||
} catch (final IllegalStateException expected) { | |||||
// expected here | |||||
} | |||||
assertEquals(CellType.NUMERIC, D1.getCellTypeEnum()); | assertEquals(CellType.NUMERIC, D1.getCellTypeEnum()); | ||||
assertEquals(expectedResult, D1.getNumericCellValue(), EPSILON); | assertEquals(expectedResult, D1.getNumericCellValue(), EPSILON); | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import org.apache.poi.ss.formula.eval.AreaEval; | |||||
import org.apache.poi.ss.formula.eval.BlankEval; | |||||
import org.apache.poi.ss.formula.eval.BoolEval; | |||||
import org.apache.poi.ss.formula.eval.ErrorEval; | |||||
import org.apache.poi.ss.formula.eval.NumberEval; | |||||
import org.apache.poi.ss.formula.eval.StringEval; | |||||
import org.apache.poi.ss.formula.eval.ValueEval; | |||||
import org.apache.poi.ss.formula.eval.*; | |||||
/** | /** | ||||
* Test cases for Excel function T() | * Test cases for Excel function T() | ||||
assertNotNull("result may never be null", result); | assertNotNull("result may never be null", result); | ||||
return result; | return result; | ||||
} | } | ||||
/** | /** | ||||
* Simulates call: T(A1) | * Simulates call: T(A1) | ||||
* where cell A1 has the specified innerValue | * where cell A1 has the specified innerValue | ||||
} | } | ||||
public void testTextValues() { | public void testTextValues() { | ||||
confirmText("abc"); | confirmText("abc"); | ||||
confirmText(""); | confirmText(""); | ||||
confirmText(" "); | confirmText(" "); | ||||
}; | }; | ||||
AreaEval ae = EvalFactory.createAreaEval("C10:D11", areaValues); | AreaEval ae = EvalFactory.createAreaEval("C10:D11", areaValues); | ||||
ValueEval ve; | |||||
ve = invokeT(ae); | |||||
ValueEval ve = invokeT(ae); | |||||
confirmString(ve, "abc"); | confirmString(ve, "abc"); | ||||
areaValues[0] = new NumberEval(5.0); | areaValues[0] = new NumberEval(5.0); |