Bug 61859: support for evaluating comparison operators in array mode, detect array mode from formula ptgs

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1818587 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2017-12-18 15:54:50 +00:00
parent a401e74ec6
commit 0ad8c53743
9 changed files with 488 additions and 18 deletions

View File

@ -33,13 +33,8 @@ import org.apache.poi.ss.formula.eval.RefEval;
import org.apache.poi.ss.formula.eval.StringEval; import org.apache.poi.ss.formula.eval.StringEval;
import org.apache.poi.ss.formula.eval.ValueEval; import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.functions.FreeRefFunction; import org.apache.poi.ss.formula.functions.FreeRefFunction;
import org.apache.poi.ss.formula.ptg.Area3DPtg; import org.apache.poi.ss.formula.functions.Function;
import org.apache.poi.ss.formula.ptg.Area3DPxg; import org.apache.poi.ss.formula.ptg.*;
import org.apache.poi.ss.formula.ptg.NameXPtg;
import org.apache.poi.ss.formula.ptg.NameXPxg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.CellReference.NameType; import org.apache.poi.ss.util.CellReference.NameType;
@ -58,6 +53,7 @@ public final class OperationEvaluationContext {
private final EvaluationTracker _tracker; private final EvaluationTracker _tracker;
private final WorkbookEvaluator _bookEvaluator; private final WorkbookEvaluator _bookEvaluator;
private final boolean _isSingleValue; private final boolean _isSingleValue;
private final boolean _isInArrayContext;
public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum, public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
int srcColNum, EvaluationTracker tracker) { int srcColNum, EvaluationTracker tracker) {
@ -66,6 +62,11 @@ public final class OperationEvaluationContext {
public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum, public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
int srcColNum, EvaluationTracker tracker, boolean isSingleValue) { int srcColNum, EvaluationTracker tracker, boolean isSingleValue) {
this(bookEvaluator, workbook, sheetIndex, srcRowNum, srcColNum, tracker, isSingleValue, null);
}
public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
int srcColNum, EvaluationTracker tracker, boolean isSingleValue, Ptg[] ptgs) {
_bookEvaluator = bookEvaluator; _bookEvaluator = bookEvaluator;
_workbook = workbook; _workbook = workbook;
_sheetIndex = sheetIndex; _sheetIndex = sheetIndex;
@ -73,6 +74,48 @@ public final class OperationEvaluationContext {
_columnIndex = srcColNum; _columnIndex = srcColNum;
_tracker = tracker; _tracker = tracker;
_isSingleValue = isSingleValue; _isSingleValue = isSingleValue;
_isInArrayContext = isInArrayContext(ptgs);
}
/**
* Check if the given formula should be evaluated in array mode.
*
* <p>
* Normally, array formulas are recognized from their definition:
* pressing Ctrl+Shift+Enter in Excel marks the input as an array entered formula.
*</p>
* <p>
* However, in some cases Excel evaluates tokens in array mode depending on the context.
* The <code>INDEX( area, row_num, [column_num])</code> function is an example:
*
* If the array argument includes more than one row and row_num is omitted or set to 0,
* the Excel INDEX function returns an array of the entire column. Similarly, if array
* includes more than one column and the column_num argument is omitted or set to 0,
* the INDEX formula returns the entire row
* </p>
*
* @param ptgs parsed formula to analyze
* @return whether the formula should be evaluated in array mode
*/
private boolean isInArrayContext(Ptg[] ptgs){
boolean arrayMode = false;
if(ptgs != null) for(int j = ptgs.length - 1; j >= 0; j--){
if(ptgs[j] instanceof FuncVarPtg){
FuncVarPtg f = (FuncVarPtg)ptgs[j];
if(f.getName().equals("INDEX") && f.getNumberOfOperands() <= 3){
// check 2nd and 3rd arguments.
arrayMode = (ptgs[j - 1] instanceof IntPtg && ((IntPtg)ptgs[j - 1]).getValue() == 0)
|| (ptgs[j - 2] instanceof IntPtg && ((IntPtg)ptgs[j - 2]).getValue() == 0);
if(arrayMode) break;
}
}
}
return arrayMode;
}
public boolean isInArrayContext(){
return _isInArrayContext;
} }
public EvaluationWorkbook getWorkbook() { public EvaluationWorkbook getWorkbook() {
@ -478,7 +521,8 @@ public final class OperationEvaluationContext {
// Need to evaluate the reference in the context of the other book // Need to evaluate the reference in the context of the other book
OperationEvaluationContext refWorkbookContext = new OperationEvaluationContext( OperationEvaluationContext refWorkbookContext = new OperationEvaluationContext(
refWorkbookEvaluator, refWorkbookEvaluator.getWorkbook(), -1, -1, -1, _tracker); refWorkbookEvaluator, refWorkbookEvaluator.getWorkbook(), -1, -1, -1, _tracker,
true, evaluationName.getNameDefinition());
Ptg ptg = evaluationName.getNameDefinition()[0]; Ptg ptg = evaluationName.getNameDefinition()[0];
if (ptg instanceof Ref3DPtg){ if (ptg instanceof Ref3DPtg){
@ -500,4 +544,5 @@ public final class OperationEvaluationContext {
return ErrorEval.REF_INVALID; return ErrorEval.REF_INVALID;
} }
} }
} }

View File

@ -120,7 +120,7 @@ final class OperationEvaluatorFactory {
EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex()); EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex());
EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex()); EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex());
if (evalCell.isPartOfArrayFormulaGroup() && result instanceof ArrayFunction) if ((evalCell.isPartOfArrayFormulaGroup() || ec.isInArrayContext()) && result instanceof ArrayFunction)
return ((ArrayFunction) result).evaluateArray(args, ec.getRowIndex(), ec.getColumnIndex()); return ((ArrayFunction) result).evaluateArray(args, ec.getRowIndex(), ec.getColumnIndex());
return result.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex()); return result.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex());

View File

@ -268,11 +268,12 @@ public final class WorkbookEvaluator {
if (!tracker.startEvaluate(cce)) { if (!tracker.startEvaluate(cce)) {
return ErrorEval.CIRCULAR_REF_ERROR; return ErrorEval.CIRCULAR_REF_ERROR;
} }
OperationEvaluationContext ec = new OperationEvaluationContext(this, _workbook, sheetIndex, rowIndex, columnIndex, tracker);
try { try {
Ptg[] ptgs = _workbook.getFormulaTokens(srcCell); Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
OperationEvaluationContext ec = new OperationEvaluationContext
(this, _workbook, sheetIndex, rowIndex, columnIndex, tracker, true, ptgs);
if (evalListener == null) { if (evalListener == null) {
result = evaluateFormula(ec, ptgs); result = evaluateFormula(ec, ptgs);
} else { } else {
@ -779,15 +780,17 @@ public final class WorkbookEvaluator {
} }
int rowIndex = ref == null ? -1 : ref.getRow(); int rowIndex = ref == null ? -1 : ref.getRow();
short colIndex = ref == null ? -1 : ref.getCol(); short colIndex = ref == null ? -1 : ref.getCol();
Ptg[] ptgs = FormulaParser.parse(formula, (FormulaParsingWorkbook) getWorkbook(), FormulaType.CELL, sheetIndex, rowIndex);
final OperationEvaluationContext ec = new OperationEvaluationContext( final OperationEvaluationContext ec = new OperationEvaluationContext(
this, this,
getWorkbook(), getWorkbook(),
sheetIndex, sheetIndex,
rowIndex, rowIndex,
colIndex, colIndex,
new EvaluationTracker(_cache) new EvaluationTracker(_cache),
true,
ptgs
); );
Ptg[] ptgs = FormulaParser.parse(formula, (FormulaParsingWorkbook) getWorkbook(), FormulaType.CELL, sheetIndex, rowIndex);
return evaluateNameFormula(ptgs, ec); return evaluateNameFormula(ptgs, ec);
} }
@ -836,7 +839,7 @@ public final class WorkbookEvaluator {
adjustRegionRelativeReference(ptgs, target, region); adjustRegionRelativeReference(ptgs, target, region);
final OperationEvaluationContext ec = new OperationEvaluationContext(this, getWorkbook(), sheetIndex, target.getRow(), target.getCol(), new EvaluationTracker(_cache), formulaType.isSingleValue()); final OperationEvaluationContext ec = new OperationEvaluationContext(this, getWorkbook(), sheetIndex, target.getRow(), target.getCol(), new EvaluationTracker(_cache), formulaType.isSingleValue(), ptgs);
return evaluateNameFormula(ptgs, ec); return evaluateNameFormula(ptgs, ec);
} }

View File

@ -202,7 +202,7 @@ public final class OperandResolver {
if(!ae.isRow()) { if(!ae.isRow()) {
// multi-column, multi-row area // multi-column, multi-row area
if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) { if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) {
return ae.getAbsoluteValue(ae.getFirstRow(), ae.getFirstColumn()); return ae.getAbsoluteValue(srcCellRow, srcCellCol);
} }
throw EvaluationException.invalidValue(); throw EvaluationException.invalidValue();
} }

View File

@ -17,6 +17,8 @@
package org.apache.poi.ss.formula.eval; package org.apache.poi.ss.formula.eval;
import org.apache.poi.ss.formula.CacheAreaEval;
import org.apache.poi.ss.formula.functions.ArrayFunction;
import org.apache.poi.ss.formula.functions.Fixed2ArgFunction; import org.apache.poi.ss.formula.functions.Fixed2ArgFunction;
import org.apache.poi.ss.formula.functions.Function; import org.apache.poi.ss.formula.functions.Function;
import org.apache.poi.ss.util.NumberComparer; import org.apache.poi.ss.util.NumberComparer;
@ -26,7 +28,7 @@ import org.apache.poi.ss.util.NumberComparer;
* *
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*/ */
public abstract class RelationalOperationEval extends Fixed2ArgFunction { public abstract class RelationalOperationEval extends Fixed2ArgFunction implements ArrayFunction {
/** /**
* Converts a standard compare result (-1, 0, 1) to <code>true</code> or <code>false</code> * Converts a standard compare result (-1, 0, 1) to <code>true</code> or <code>false</code>
@ -56,6 +58,7 @@ public abstract class RelationalOperationEval extends Fixed2ArgFunction {
* Blank < Positive numbers * Blank < Positive numbers
* </pre> * </pre>
*/ */
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
ValueEval vA; ValueEval vA;
@ -71,6 +74,86 @@ public abstract class RelationalOperationEval extends Fixed2ArgFunction {
return BoolEval.valueOf(result); return BoolEval.valueOf(result);
} }
public ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
ValueEval arg0 = args[0];
ValueEval arg1 = args[1];
int w1, w2, h1, h2;
int a1FirstCol = 0, a1FirstRow = 0;
if (arg0 instanceof AreaEval) {
AreaEval ae = (AreaEval)arg0;
w1 = ae.getWidth();
h1 = ae.getHeight();
a1FirstCol = ae.getFirstColumn();
a1FirstRow = ae.getFirstRow();
} else if (arg0 instanceof RefEval){
RefEval ref = (RefEval)arg0;
w1 = 1;
h1 = 1;
a1FirstCol = ref.getColumn();
a1FirstRow = ref.getRow();
} else {
w1 = 1;
h1 = 1;
}
int a2FirstCol = 0, a2FirstRow = 0;
if (arg1 instanceof AreaEval) {
AreaEval ae = (AreaEval)arg1;
w2 = ae.getWidth();
h2 = ae.getHeight();
a2FirstCol = ae.getFirstColumn();
a2FirstRow = ae.getFirstRow();
} else if (arg1 instanceof RefEval){
RefEval ref = (RefEval)arg1;
w2 = 1;
h2 = 1;
a2FirstCol = ref.getColumn();
a2FirstRow = ref.getRow();
} else {
w2 = 1;
h2 = 1;
}
int width = Math.max(w1, w2);
int height = Math.max(h1, h2);
ValueEval[] vals = new ValueEval[height * width];
int idx = 0;
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
ValueEval vA;
try {
vA = OperandResolver.getSingleValue(arg0, a1FirstRow + i, a1FirstCol + j);
} catch (EvaluationException e) {
vA = e.getErrorEval();
}
ValueEval vB;
try {
vB = OperandResolver.getSingleValue(arg1, a2FirstRow + i, a2FirstCol + j);
} catch (EvaluationException e) {
vB = e.getErrorEval();
}
if(vA instanceof ErrorEval){
vals[idx++] = vA;
} else if (vB instanceof ErrorEval) {
vals[idx++] = vB;
} else {
int cmpResult = doCompare(vA, vB);
boolean result = convertComparisonResult(cmpResult);
vals[idx++] = BoolEval.valueOf(result);
}
}
}
if (vals.length == 1) {
return vals[0];
}
return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals);
}
private static int doCompare(ValueEval va, ValueEval vb) { private static int doCompare(ValueEval va, ValueEval vb) {
// special cases when one operand is blank // special cases when one operand is blank
if (va == BlankEval.instance) { if (va == BlankEval.instance) {

View File

@ -64,7 +64,7 @@ public abstract class TwoOperandNumericOperation extends Fixed2ArgFunction imple
protected abstract double evaluate(double d0, double d1) throws EvaluationException; protected abstract double evaluate(double d0, double d1) throws EvaluationException;
private final class ArrayEval extends TwoArrayArg { private final class ArrayEval extends TwoArrayArg {
private final MutableValueCollector instance = new MutableValueCollector(false, true); private final MutableValueCollector instance = new MutableValueCollector(true, true);
protected double[] collectValues(ValueEval arg) throws EvaluationException { protected double[] collectValues(ValueEval arg) throws EvaluationException {
return instance.collectValues(arg); return instance.collectValues(arg);

View File

@ -22,11 +22,17 @@ import java.util.Arrays;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.eval.AreaEval; import org.apache.poi.ss.formula.eval.AreaEval;
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.WorkbookEvaluator; import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
/** /**
@ -154,4 +160,145 @@ public final class TestIndex extends TestCase {
assertEquals(cra.getLastColumn(), ae.getLastColumn()); assertEquals(cra.getLastColumn(), ae.getLastColumn());
return ae; return ae;
} }
public void test61859(){
Workbook wb = HSSFTestDataSamples.openSampleWorkbook("maxindextest.xls");
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Sheet example1 = wb.getSheetAt(0);
Cell ex1cell1 = example1.getRow(1).getCell(6);
assertEquals("MAX(INDEX(($B$2:$B$11=F2)*$A$2:$A$11,0))", ex1cell1.getCellFormula());
fe.evaluate(ex1cell1);
assertEquals(4.0, ex1cell1.getNumericCellValue());
Cell ex1cell2 = example1.getRow(2).getCell(6);
assertEquals("MAX(INDEX(($B$2:$B$11=F3)*$A$2:$A$11,0))", ex1cell2.getCellFormula());
fe.evaluate(ex1cell2);
assertEquals(10.0, ex1cell2.getNumericCellValue());
Cell ex1cell3 = example1.getRow(3).getCell(6);
assertEquals("MAX(INDEX(($B$2:$B$11=F4)*$A$2:$A$11,0))", ex1cell3.getCellFormula());
fe.evaluate(ex1cell3);
assertEquals(20.0, ex1cell3.getNumericCellValue());
}
/**
* If both the Row_num and Column_num arguments are used,
* INDEX returns the value in the cell at the intersection of Row_num and Column_num
*/
public void testReference2DArea(){
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet();
/**
* 1 2 3
* 4 5 6
* 7 8 9
*/
int val = 0;
for(int i = 0; i < 3; i++){
Row row = sheet.createRow(i);
for(int j = 0; j < 3; j++){
row.createCell(j).setCellValue(++val);
}
}
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
Cell c1 = sheet.getRow(0).createCell(5);
c1.setCellFormula("INDEX(A1:C3,2,2)");
Cell c2 = sheet.getRow(0).createCell(6);
c2.setCellFormula("INDEX(A1:C3,3,2)");
assertEquals(5.0, fe.evaluate(c1).getNumberValue());
assertEquals(8.0, fe.evaluate(c2).getNumberValue());
}
/**
* If Column_num is 0 (zero), INDEX returns the array of values for the entire row.
*/
public void testArrayArgument_RowLookup(){
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet();
/**
* 1 2 3
* 4 5 6
* 7 8 9
*/
int val = 0;
for(int i = 0; i < 3; i++){
Row row = sheet.createRow(i);
for(int j = 0; j < 3; j++){
row.createCell(j).setCellValue(++val);
}
}
Cell c1 = sheet.getRow(0).createCell(5);
c1.setCellFormula("SUM(INDEX(A1:C3,1,0))"); // sum of all values in the 1st row: 1 + 2 + 3 = 6
Cell c2 = sheet.getRow(0).createCell(6);
c2.setCellFormula("SUM(INDEX(A1:C3,2,0))"); // sum of all values in the 2nd row: 4 + 5 + 6 = 15
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
assertEquals(6.0, fe.evaluate(c1).getNumberValue());
assertEquals(15.0, fe.evaluate(c2).getNumberValue());
}
/**
* If Row_num is 0 (zero), INDEX returns the array of values for the entire column.
*/
public void testArrayArgument_ColumnLookup(){
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet();
/**
* 1 2 3
* 4 5 6
* 7 8 9
*/
int val = 0;
for(int i = 0; i < 3; i++){
Row row = sheet.createRow(i);
for(int j = 0; j < 3; j++){
row.createCell(j).setCellValue(++val);
}
}
Cell c1 = sheet.getRow(0).createCell(5);
c1.setCellFormula("SUM(INDEX(A1:C3,0,1))"); // sum of all values in the 1st column: 1 + 4 + 7 = 12
Cell c2 = sheet.getRow(0).createCell(6);
c2.setCellFormula("SUM(INDEX(A1:C3,0,3))"); // sum of all values in the 3rd column: 3 + 6 + 9 = 18
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
assertEquals(12.0, fe.evaluate(c1).getNumberValue());
assertEquals(18.0, fe.evaluate(c2).getNumberValue());
}
/**
* =SUM(B1:INDEX(B1:B3,2))
*
* The sum of the range starting at B1, and ending at the intersection of the 2nd row of the range B1:B3,
* which is the sum of B1:B2.
*/
public void testDynamicReference(){
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet();
/**
* 1 2 3
* 4 5 6
* 7 8 9
*/
int val = 0;
for(int i = 0; i < 3; i++){
Row row = sheet.createRow(i);
for(int j = 0; j < 3; j++){
row.createCell(j).setCellValue(++val);
}
}
Cell c1 = sheet.getRow(0).createCell(5);
c1.setCellFormula("SUM(B1:INDEX(B1:B3,2))"); // B1:INDEX(B1:B3,2) evaluates to B1:B2
FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
assertEquals(7.0, fe.evaluate(c1).getNumberValue());
}
} }

View File

@ -0,0 +1,192 @@
/* ====================================================================
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.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.CacheAreaEval;
import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.usermodel.CellValue;
import static org.junit.Assert.assertEquals;
public class TestRelationalOperations extends TestCase {
/**
* (1, 1)(1, 1) = 1
*
* evaluates to
*
* (TRUE, TRUE)(TRUE, TRUE)
*
*/
public void testEqMatrixByScalar_Numbers() {
ValueEval[] values = new ValueEval[4];
for (int i = 0; i < values.length; i++) {
values[i] = new NumberEval(1);
}
ValueEval arg1 = EvalFactory.createAreaEval("A1:B2", values);
ValueEval arg2 = EvalFactory.createRefEval("D1", new NumberEval(1));
RelationalOperationEval eq = (RelationalOperationEval)RelationalOperationEval.EqualEval;
ValueEval result = eq.evaluateArray(new ValueEval[]{ arg1, arg2}, 2, 5);
assertEquals("expected CacheAreaEval", CacheAreaEval.class, result.getClass());
CacheAreaEval ce = (CacheAreaEval)result;
assertEquals(2, ce.getWidth());
assertEquals(2, ce.getHeight());
for(int i =0; i < ce.getHeight(); i++){
for(int j = 0; j < ce.getWidth(); j++){
assertEquals(BoolEval.TRUE, ce.getRelativeValue(i, j));
}
}
}
public void testEqMatrixByScalar_String() {
ValueEval[] values = new ValueEval[4];
for (int i = 0; i < values.length; i++) {
values[i] = new StringEval("ABC");
}
ValueEval arg1 = EvalFactory.createAreaEval("A1:B2", values);
ValueEval arg2 = EvalFactory.createRefEval("D1", new StringEval("ABC"));
RelationalOperationEval eq = (RelationalOperationEval)RelationalOperationEval.EqualEval;
ValueEval result = eq.evaluateArray(new ValueEval[]{ arg1, arg2}, 2, 5);
assertEquals("expected CacheAreaEval", CacheAreaEval.class, result.getClass());
CacheAreaEval ce = (CacheAreaEval)result;
assertEquals(2, ce.getWidth());
assertEquals(2, ce.getHeight());
for(int i =0; i < ce.getHeight(); i++){
for(int j = 0; j < ce.getWidth(); j++){
assertEquals(BoolEval.TRUE, ce.getRelativeValue(i, j));
}
}
}
public void testEqMatrixBy_Row() {
ValueEval[] matrix = {
new NumberEval(-1), new NumberEval(1),
new NumberEval(-1), new NumberEval(1)
};
ValueEval[] row = {
new NumberEval(1), new NumberEval(1), new NumberEval(1)
};
ValueEval[] expected = {
BoolEval.FALSE, BoolEval.TRUE, ErrorEval.VALUE_INVALID,
BoolEval.FALSE, BoolEval.TRUE, ErrorEval.VALUE_INVALID
};
ValueEval arg1 = EvalFactory.createAreaEval("A1:B2", matrix);
ValueEval arg2 = EvalFactory.createAreaEval("A4:C4", row);
RelationalOperationEval eq = (RelationalOperationEval)RelationalOperationEval.EqualEval;
ValueEval result = eq.evaluateArray(new ValueEval[]{ arg1, arg2}, 4, 5);
assertEquals("expected CacheAreaEval", CacheAreaEval.class, result.getClass());
CacheAreaEval ce = (CacheAreaEval)result;
assertEquals(3, ce.getWidth());
assertEquals(2, ce.getHeight());
int idx = 0;
for(int i =0; i < ce.getHeight(); i++){
for(int j = 0; j < ce.getWidth(); j++){
assertEquals("[" + i + "," + j + "]", expected[idx++], ce.getRelativeValue(i, j));
}
}
}
public void testEqMatrixBy_Column() {
ValueEval[] matrix = {
new NumberEval(-1), new NumberEval(1),
new NumberEval(-1), new NumberEval(1)
};
ValueEval[] column = {
new NumberEval(1),
new NumberEval(1),
new NumberEval(1)
};
ValueEval[] expected = {
BoolEval.FALSE, BoolEval.TRUE,
BoolEval.FALSE, BoolEval.TRUE,
ErrorEval.VALUE_INVALID, ErrorEval.VALUE_INVALID
};
ValueEval arg1 = EvalFactory.createAreaEval("A1:B2", matrix);
ValueEval arg2 = EvalFactory.createAreaEval("A6:A8", column);
RelationalOperationEval eq = (RelationalOperationEval)RelationalOperationEval.EqualEval;
ValueEval result = eq.evaluateArray(new ValueEval[]{ arg1, arg2}, 4, 6);
assertEquals("expected CacheAreaEval", CacheAreaEval.class, result.getClass());
CacheAreaEval ce = (CacheAreaEval)result;
assertEquals(2, ce.getWidth());
assertEquals(3, ce.getHeight());
int idx = 0;
for(int i =0; i < ce.getHeight(); i++){
for(int j = 0; j < ce.getWidth(); j++){
assertEquals("[" + i + "," + j + "]", expected[idx++], ce.getRelativeValue(i, j));
}
}
}
public void testEqMatrixBy_Matrix() {
// A1:B2
ValueEval[] matrix1 = {
new NumberEval(-1), new NumberEval(1),
new NumberEval(-1), new NumberEval(1)
};
// A10:C12
ValueEval[] matrix2 = {
new NumberEval(1), new NumberEval(1), new NumberEval(1),
new NumberEval(1), new NumberEval(1), new NumberEval(1),
new NumberEval(1), new NumberEval(1), new NumberEval(1)
};
ValueEval[] expected = {
BoolEval.FALSE, BoolEval.TRUE, ErrorEval.VALUE_INVALID,
BoolEval.FALSE, BoolEval.TRUE, ErrorEval.VALUE_INVALID,
ErrorEval.VALUE_INVALID, ErrorEval.VALUE_INVALID, ErrorEval.VALUE_INVALID
};
ValueEval arg1 = EvalFactory.createAreaEval("A1:B2", matrix1);
ValueEval arg2 = EvalFactory.createAreaEval("A10:C12", matrix2);
RelationalOperationEval eq = (RelationalOperationEval)RelationalOperationEval.EqualEval;
ValueEval result = eq.evaluateArray(new ValueEval[]{ arg1, arg2}, 4, 6);
assertEquals("expected CacheAreaEval", CacheAreaEval.class, result.getClass());
CacheAreaEval ce = (CacheAreaEval)result;
assertEquals(3, ce.getWidth());
assertEquals(3, ce.getHeight());
int idx = 0;
for(int i =0; i < ce.getHeight(); i++){
for(int j = 0; j < ce.getWidth(); j++){
assertEquals("[" + i + "," + j + "]", expected[idx++], ce.getRelativeValue(i, j));
}
}
}
}

Binary file not shown.