diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaI.java b/src/java/org/apache/poi/hssf/record/formula/AreaI.java
index 477d32f677..319a9819c9 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AreaI.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AreaI.java
@@ -50,10 +50,10 @@ public interface AreaI {
public OffsetArea(int baseRow, int baseColumn, int relFirstRowIx, int relLastRowIx,
int relFirstColIx, int relLastColIx) {
- _firstRow = baseRow + relFirstRowIx;
- _lastRow = baseRow + relLastRowIx;
- _firstColumn = baseColumn + relFirstColIx;
- _lastColumn = baseColumn + relLastColIx;
+ _firstRow = baseRow + Math.min(relFirstRowIx, relLastRowIx);
+ _lastRow = baseRow + Math.max(relFirstRowIx, relLastRowIx);
+ _firstColumn = baseColumn + Math.min(relFirstColIx, relLastColIx);
+ _lastColumn = baseColumn + Math.max(relFirstColIx, relLastColIx);
}
public int getFirstColumn() {
@@ -72,5 +72,4 @@ public interface AreaI {
return _lastRow;
}
}
-
}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java b/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
index 34558ebdf8..701a7aee74 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
@@ -34,13 +34,13 @@ public final class AttrPtg extends ControlPtg {
private final static int SIZE = 4;
private byte field_1_options;
private short field_2_data;
-
+
/** only used for tAttrChoose: table of offsets to starts of args */
private final int[] _jumpTable;
/** only used for tAttrChoose: offset to the tFuncVar for CHOOSE() */
private final int _chooseFuncOffset;
-
- // flags 'volatile' and 'space', can be combined.
+
+ // flags 'volatile' and 'space', can be combined.
// OOO spec says other combinations are theoretically possible but not likely to occur.
private static final BitField semiVolatile = BitFieldFactory.getInstance(0x01);
private static final BitField optiIf = BitFieldFactory.getInstance(0x02);
@@ -49,12 +49,14 @@ public final class AttrPtg extends ControlPtg {
private static final BitField sum = BitFieldFactory.getInstance(0x10);
private static final BitField baxcel = BitFieldFactory.getInstance(0x20); // 'assignment-style formula in a macro sheet'
private static final BitField space = BitFieldFactory.getInstance(0x40);
-
+
+ public static final AttrPtg SUM = new AttrPtg(0x0010, 0, null, -1);
+
public static final class SpaceType {
private SpaceType() {
// no instances of this class
}
-
+
/** 00H = Spaces before the next token (not allowed before tParen token) */
public static final int SPACE_BEFORE = 0x00;
/** 01H = Carriage returns before the next token (not allowed before tParen token) */
@@ -75,7 +77,7 @@ public final class AttrPtg extends ControlPtg {
_jumpTable = null;
_chooseFuncOffset = -1;
}
-
+
public AttrPtg(RecordInputStream in)
{
field_1_options = in.readByte();
@@ -92,7 +94,7 @@ public final class AttrPtg extends ControlPtg {
_jumpTable = null;
_chooseFuncOffset = -1;
}
-
+
}
private AttrPtg(int options, int data, int[] jt, int chooseFuncOffset) {
field_1_options = (byte) options;
@@ -100,7 +102,7 @@ public final class AttrPtg extends ControlPtg {
_jumpTable = jt;
_chooseFuncOffset = chooseFuncOffset;
}
-
+
/**
* @param type a constant from SpaceType
* @param count the number of space characters
@@ -145,7 +147,7 @@ public final class AttrPtg extends ControlPtg {
{
return sum.isSet(getOptions());
}
-
+
public void setSum(boolean bsum) {
field_1_options=sum.setByteBoolean(field_1_options,bsum);
}
@@ -155,13 +157,13 @@ public final class AttrPtg extends ControlPtg {
}
/**
- * Flags this ptg as a goto/jump
+ * Flags this ptg as a goto/jump
* @param isGoto
*/
public void setGoto(boolean isGoto) {
field_1_options=optGoto.setByteBoolean(field_1_options, isGoto);
}
-
+
// lets hope no one uses this anymore
public boolean isBaxcel()
{
@@ -201,7 +203,7 @@ public final class AttrPtg extends ControlPtg {
} else if(isOptimizedChoose()) {
sb.append("choose nCases=").append(getData());
} else if(isGoto()) {
- sb.append("skip dist=").append(getData());
+ sb.append("skip dist=").append(getData());
} else if(isSum()) {
sb.append("sum ");
} else if(isBaxcel()) {
@@ -218,7 +220,7 @@ public final class AttrPtg extends ControlPtg {
LittleEndian.putShort(array,offset+2, field_2_data);
int[] jt = _jumpTable;
if (jt != null) {
- int joff = offset+4;
+ int joff = offset+4;
LittleEndian.putUShort(array, joff, _chooseFuncOffset);
joff+=2;
for (int i = 0; i < jt.length; i++) {
@@ -227,7 +229,7 @@ public final class AttrPtg extends ControlPtg {
}
LittleEndian.putUShort(array, joff, _chooseFuncOffset);
}
-
+
}
public int getSize()
@@ -249,7 +251,7 @@ public final class AttrPtg extends ControlPtg {
return toFormulaString() + "(" + operands[ 0 ] + ")";
}
}
-
+
public int getNumberOfOperands()
{
@@ -260,7 +262,7 @@ public final class AttrPtg extends ControlPtg {
{
return -1;
}
-
+
public String toFormulaString() {
if(semiVolatile.isSet(field_1_options)) {
return "ATTR(semiVolatile)";
diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/RangeEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/RangeEval.java
new file mode 100644
index 0000000000..9398108fdc
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/formula/eval/RangeEval.java
@@ -0,0 +1,71 @@
+/* ====================================================================
+ 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
+ */
+public final class RangeEval implements OperationEval {
+
+ public static final OperationEval instance = new RangeEval();
+
+ private RangeEval() {
+ }
+
+ public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+ if(args.length != 2) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ try {
+ RefEval reA = evaluateRef(args[0]);
+ RefEval reB = evaluateRef(args[1]);
+ return resolveRange(reA, reB);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ }
+
+ private static AreaEval resolveRange(RefEval reA, RefEval reB) {
+
+ int height = reB.getRow() - reA.getRow();
+ int width = reB.getColumn() - reA.getColumn();
+
+ return reA.offset(0, height, 0, width);
+ }
+
+ private static RefEval evaluateRef(Eval arg) throws EvaluationException {
+ if (arg instanceof RefEval) {
+ return (RefEval) arg;
+ }
+ if (arg instanceof ErrorEval) {
+ throw new EvaluationException((ErrorEval)arg);
+ }
+ throw new IllegalArgumentException("Unexpected ref arg class (" + arg.getClass().getName() + ")");
+ }
+
+ public int getNumberOfOperands() {
+ return 2;
+ }
+
+ public int getType() {
+ throw new RuntimeException("obsolete code should not be called");
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
index a9c16b090d..6811e49be2 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java
@@ -2,7 +2,9 @@ package org.apache.poi.hssf.usermodel;
import org.apache.poi.hssf.model.HSSFFormulaParser;
import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.NameRecord;
+import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.record.formula.Ptg;
@@ -94,7 +96,14 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return new Name(_iBook.getNameRecord(ix), ix);
}
public Ptg[] getFormulaTokens(HSSFCell cell) {
- return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);
+ if (false) {
+ // re-parsing the formula text also works, but is a waste of time
+ // It is useful from time to time to run all unit tests with this code
+ // to make sure that all formulas POI can evaluate can also be parsed.
+ return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);
+ }
+ FormulaRecord fr = ((FormulaRecordAggregate) cell.getCellValueRecord()).getFormulaRecord();
+ return fr.getParsedExpression();
}
private static final class Name implements EvaluationName {
diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
index eaa57c114f..09c93723f9 100755
--- a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
+++ b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
@@ -40,6 +40,7 @@ import org.apache.poi.hssf.record.formula.OperationPtg;
import org.apache.poi.hssf.record.formula.PercentPtg;
import org.apache.poi.hssf.record.formula.PowerPtg;
import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.RangePtg;
import org.apache.poi.hssf.record.formula.SubtractPtg;
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
@@ -57,6 +58,7 @@ import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
import org.apache.poi.hssf.record.formula.eval.OperationEval;
import org.apache.poi.hssf.record.formula.eval.PercentEval;
import org.apache.poi.hssf.record.formula.eval.PowerEval;
+import org.apache.poi.hssf.record.formula.eval.RangeEval;
import org.apache.poi.hssf.record.formula.eval.SubtractEval;
import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
@@ -101,6 +103,7 @@ final class OperationEvaluatorFactory {
add(m, SubtractPtg.class, SubtractEval.instance);
add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
+ add(m, RangePtg.class, RangeEval.instance);
return m;
}
diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
index 36ad65b7f1..e958649695 100644
--- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
+++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
@@ -22,12 +22,18 @@ import java.util.Map;
import java.util.Stack;
import org.apache.poi.hssf.record.formula.Area3DPtg;
+import org.apache.poi.hssf.record.formula.AreaErrPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.BoolPtg;
import org.apache.poi.hssf.record.formula.ControlPtg;
+import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
+import org.apache.poi.hssf.record.formula.DeletedRef3DPtg;
import org.apache.poi.hssf.record.formula.ErrPtg;
+import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.MemErrPtg;
+import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
@@ -35,6 +41,7 @@ import org.apache.poi.hssf.record.formula.NumberPtg;
import org.apache.poi.hssf.record.formula.OperationPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefErrorPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.StringPtg;
import org.apache.poi.hssf.record.formula.UnionPtg;
@@ -181,10 +188,10 @@ public class WorkbookEvaluator {
isPlainFormulaCell = false;
Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
if(evalListener == null) {
- result = evaluateCell(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
+ result = evaluateFormula(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
} else {
evalListener.onStartEvaluate(sheetIndex, rowIndex, columnIndex, ptgs);
- result = evaluateCell(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
+ result = evaluateFormula(sheetIndex, rowIndex, (short)columnIndex, ptgs, tracker);
evalListener.onEndEvaluate(sheetIndex, rowIndex, columnIndex, result);
}
}
@@ -225,17 +232,31 @@ public class WorkbookEvaluator {
}
throw new RuntimeException("Unexpected cell type (" + cellType + ")");
}
- private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
+ // visibility raised for testing
+ /* package */ ValueEval evaluateFormula(int sheetIndex, int srcRowNum, int srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
Stack stack = new Stack();
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
// since we don't know how to handle these yet :(
Ptg ptg = ptgs[i];
+ if (ptg instanceof AttrPtg) {
+ AttrPtg attrPtg = (AttrPtg) ptg;
+ if (attrPtg.isSum()) {
+ // Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator
+ // expects the equivalent function token
+ byte nArgs = 1; // tAttrSum always has 1 parameter
+ ptg = new FuncVarPtg("SUM", nArgs);
+ }
+ }
if (ptg instanceof ControlPtg) {
// skip Parentheses, Attr, etc
continue;
}
+ if (ptg instanceof MemFuncPtg) {
+ // can ignore, rest of tokens for this expression are in OK RPN order
+ continue;
+ }
if (ptg instanceof MemErrPtg) { continue; }
if (ptg instanceof MissingArgPtg) {
// TODO - might need to push BlankEval or MissingArgEval
@@ -289,7 +310,7 @@ public class WorkbookEvaluator {
* @return a NumberEval, StringEval, BoolEval,
* BlankEval or ErrorEval. Never null.
*/
- private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
+ private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
if (evaluationResult instanceof RefEval) {
RefEval rv = (RefEval) evaluationResult;
return rv.getInnerValueEval();
@@ -361,6 +382,10 @@ public class WorkbookEvaluator {
if (ptg instanceof ErrPtg) {
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
}
+ if (ptg instanceof AreaErrPtg ||ptg instanceof RefErrorPtg
+ || ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) {
+ return ErrorEval.REF_INVALID;
+ }
if (ptg instanceof Ref3DPtg) {
Ref3DPtg refPtg = (Ref3DPtg) ptg;
int otherSheetIndex = _workbook.convertFromExternSheetIndex(refPtg.getExternSheetIndex());
diff --git a/src/testcases/org/apache/poi/hssf/HSSFTests.java b/src/testcases/org/apache/poi/hssf/HSSFTests.java
index aa5bf46d31..d5a491099d 100644
--- a/src/testcases/org/apache/poi/hssf/HSSFTests.java
+++ b/src/testcases/org/apache/poi/hssf/HSSFTests.java
@@ -28,7 +28,7 @@ import org.apache.poi.hssf.model.AllModelTests;
import org.apache.poi.hssf.record.AllRecordTests;
import org.apache.poi.hssf.usermodel.AllUserModelTests;
import org.apache.poi.hssf.util.AllHSSFUtilTests;
-import org.apache.poi.ss.formula.TestEvaluationCache;
+import org.apache.poi.ss.formula.AllSSFormulaTests;
/**
* Test Suite for all sub-packages of org.apache.poi.hssf
@@ -53,7 +53,7 @@ public final class HSSFTests {
}
suite.addTest(new TestSuite(TestEventRecordFactory.class));
suite.addTest(new TestSuite(TestModelFactory.class));
- suite.addTest(new TestSuite(TestEvaluationCache.class));
+ suite.addTest(AllSSFormulaTests.suite());
return suite;
}
}
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 7551061942..fe37a3c84b 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
@@ -37,6 +37,7 @@ public class AllFormulaEvalTests {
result.addTestSuite(TestFormulaBugs.class);
result.addTestSuite(TestFormulasFromSpreadsheet.class);
result.addTestSuite(TestPercentEval.class);
+ result.addTestSuite(TestRangeEval.class);
result.addTestSuite(TestUnaryPlusEval.class);
return result;
}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestRangeEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestRangeEval.java
new file mode 100644
index 0000000000..e98517a732
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestRangeEval.java
@@ -0,0 +1,95 @@
+/* ====================================================================
+ 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.AreaI;
+import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
+import org.apache.poi.hssf.util.AreaReference;
+import org.apache.poi.hssf.util.CellReference;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for unary plus operator evaluator.
+ *
+ * @author Josh Micich
+ */
+public final class TestRangeEval extends TestCase {
+
+ public void testPermutations() {
+
+ confirm("B3", "D7", "B3:D7");
+ confirm("B1", "B1", "B1:B1");
+
+ confirm("B7", "D3", "B3:D7");
+ confirm("D3", "B7", "B3:D7");
+ confirm("D7", "B3", "B3:D7");
+ }
+
+ private static void confirm(String refA, String refB, String expectedAreaRef) {
+
+ Eval[] args = {
+ createRefEval(refA),
+ createRefEval(refB),
+ };
+ AreaReference ar = new AreaReference(expectedAreaRef);
+ Eval result = RangeEval.instance.evaluate(args, 0, (short)0);
+ assertTrue(result instanceof AreaEval);
+ AreaEval ae = (AreaEval) result;
+ assertEquals(ar.getFirstCell().getRow(), ae.getFirstRow());
+ assertEquals(ar.getLastCell().getRow(), ae.getLastRow());
+ assertEquals(ar.getFirstCell().getCol(), ae.getFirstColumn());
+ assertEquals(ar.getLastCell().getCol(), ae.getLastColumn());
+ }
+
+ private static Eval createRefEval(String refStr) {
+ CellReference cr = new CellReference(refStr);
+ return new MockRefEval(cr.getRow(), cr.getCol());
+
+ }
+
+ private static final class MockRefEval extends RefEvalBase {
+
+ public MockRefEval(int rowIndex, int columnIndex) {
+ super(rowIndex, columnIndex);
+ }
+ public ValueEval getInnerValueEval() {
+ throw new RuntimeException("not expected to be called during this test");
+ }
+ public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx,
+ int relLastColIx) {
+ AreaI area = new OffsetArea(getRow(), getColumn(),
+ relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+ return new MockAreaEval(area);
+ }
+ }
+
+ private static final class MockAreaEval extends AreaEvalBase {
+
+ public MockAreaEval(AreaI ptg) {
+ super(ptg);
+ }
+ public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
+ throw new RuntimeException("not expected to be called during this test");
+ }
+ public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx,
+ int relLastColIx) {
+ throw new RuntimeException("not expected to be called during this test");
+ }
+ }
+}
diff --git a/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java b/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java
new file mode 100644
index 0000000000..dd1360f769
--- /dev/null
+++ b/src/testcases/org/apache/poi/ss/formula/AllSSFormulaTests.java
@@ -0,0 +1,34 @@
+/* ====================================================================
+ 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;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+/**
+ * Test suite for org.apache.poi.ss.formula
+ *
+ * @author Josh Micich
+ */
+public final class AllSSFormulaTests {
+ public static Test suite() {
+ TestSuite result = new TestSuite(AllSSFormulaTests.class.getName());
+ result.addTestSuite(TestEvaluationCache.class);
+ result.addTestSuite(TestWorkbookEvaluator.class);
+ return result;
+ }
+}
diff --git a/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java b/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java
new file mode 100644
index 0000000000..20cc9c1787
--- /dev/null
+++ b/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java
@@ -0,0 +1,92 @@
+/* ====================================================================
+ 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;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.record.formula.AreaErrPtg;
+import org.apache.poi.hssf.record.formula.AttrPtg;
+import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
+import org.apache.poi.hssf.record.formula.DeletedRef3DPtg;
+import org.apache.poi.hssf.record.formula.IntPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.RefErrorPtg;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+/**
+ * Tests {@link WorkbookEvaluator}.
+ *
+ * @author Josh Micich
+ */
+public class TestWorkbookEvaluator extends TestCase {
+
+ /**
+ * Make sure that the evaluator can directly handle tAttrSum (instead of relying on re-parsing
+ * the whole formula which converts tAttrSum to tFuncVar("SUM") )
+ */
+ public void testAttrSum() {
+
+ Ptg[] ptgs = {
+ new IntPtg(42),
+ AttrPtg.SUM,
+ };
+
+ ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
+ assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0);
+ }
+
+ /**
+ * Make sure that the evaluator can directly handle (deleted) ref error tokens
+ * (instead of relying on re-parsing the whole formula which converts these
+ * to the error constant #REF! )
+ */
+ public void testRefErr() {
+
+ confirmRefErr(new RefErrorPtg());
+ confirmRefErr(new AreaErrPtg());
+ confirmRefErr(new DeletedRef3DPtg(0));
+ confirmRefErr(new DeletedArea3DPtg(0));
+ }
+ private static void confirmRefErr(Ptg ptg) {
+ Ptg[] ptgs = {
+ ptg,
+ };
+
+ ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
+ assertEquals(ErrorEval.REF_INVALID, result);
+ }
+
+ /**
+ * Make sure that the evaluator can directly handle tAttrSum (instead of relying on re-parsing
+ * the whole formula which converts tAttrSum to tFuncVar("SUM") )
+ */
+ public void testMemFunc() {
+
+ Ptg[] ptgs = {
+ new IntPtg(42),
+ AttrPtg.SUM,
+ };
+
+ ValueEval result = new WorkbookEvaluator(null).evaluateFormula(0, 0, 0, ptgs, null);
+ assertEquals(42, ((NumberEval)result).getNumberValue(), 0.0);
+ }
+
+
+}