Browse Source

Bug 61063: Add a RefListEval to handle UnionPtg and implement it for Rank(), may be missing for other functions where ArrayEval is currently handled

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1795963 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_17_BETA1
Dominik Stadler 7 years ago
parent
commit
05a6cf3263

+ 11
- 20
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java View File

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];



+ 46
- 0
src/java/org/apache/poi/ss/formula/eval/RefListEval.java View File

/* ====================================================================
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;
}
}

+ 2
- 11
src/java/org/apache/poi/ss/formula/functions/LogicalFunction.java View File



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;

+ 53
- 33
src/java/org/apache/poi/ss/formula/functions/Rank.java View File



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();

+ 19
- 0
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java View File



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();
}
} }

+ 6
- 14
src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java View File

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);

+ 3
- 10
src/testcases/org/apache/poi/ss/formula/functions/TestTFunc.java View File



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);

BIN
test-data/spreadsheet/61063.xlsx View File


Loading…
Cancel
Save