true
if the last row relative, else
- * false
- */
- public final void setLastRowRelative(boolean rel) {
- field_4_last_column=rowRelative.setBoolean(field_4_last_column,rel);
- }
+ /**
+ * @return last row relative or not
+ */
+ public final boolean isLastRowRelative() {
+ return rowRelative.isSet(field_4_last_column);
+ }
+
+ /**
+ * set whether the last row is relative or not
+ * @param rel true
if the last row relative, else
+ * false
+ */
+ public final void setLastRowRelative(boolean rel) {
+ field_4_last_column=rowRelative.setBoolean(field_4_last_column,rel);
+ }
- /**
- * @return lastcol relative or not
- */
- public final boolean isLastColRelative() {
- return colRelative.isSet(field_4_last_column);
- }
-
- /**
- * set whether the last column should be relative or not
- */
- public final void setLastColRelative(boolean rel) {
- field_4_last_column=colRelative.setBoolean(field_4_last_column,rel);
- }
-
- /**
- * set the last column in the area
- */
- public final void setLastColumn(int colIx) {
- checkColumnBounds(colIx);
- field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
- }
+ /**
+ * @return lastcol relative or not
+ */
+ public final boolean isLastColRelative() {
+ return colRelative.isSet(field_4_last_column);
+ }
+
+ /**
+ * set whether the last column should be relative or not
+ */
+ public final void setLastColRelative(boolean rel) {
+ field_4_last_column=colRelative.setBoolean(field_4_last_column,rel);
+ }
+
+ /**
+ * set the last column in the area
+ */
+ public final void setLastColumn(int colIx) {
+ checkColumnBounds(colIx);
+ field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
+ }
- /**
- * set the last column irrespective of the bitmasks
- */
- public final void setLastColumnRaw(short column) {
- field_4_last_column = column;
- }
- protected final String formatReferenceAsString() {
- CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
- CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());
-
- if(AreaReference.isWholeColumnReference(topLeft, botRight)) {
- return (new AreaReference(topLeft, botRight)).formatAsString();
- }
- return topLeft.formatAsString() + ":" + botRight.formatAsString();
- }
-
- public String toFormulaString() {
- return formatReferenceAsString();
- }
+ /**
+ * set the last column irrespective of the bitmasks
+ */
+ public final void setLastColumnRaw(short column) {
+ field_4_last_column = column;
+ }
+ protected final String formatReferenceAsString() {
+ CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
+ CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());
+
+ if(AreaReference.isWholeColumnReference(topLeft, botRight)) {
+ return (new AreaReference(topLeft, botRight)).formatAsString();
+ }
+ return topLeft.formatAsString() + ":" + botRight.formatAsString();
+ }
+
+ public String toFormulaString() {
+ return formatReferenceAsString();
+ }
- public byte getDefaultOperandClass() {
- return Ptg.CLASS_REF;
- }
+ public byte getDefaultOperandClass() {
+ return Ptg.CLASS_REF;
+ }
}
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 34558ebdf..701a7aee7 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/ExternSheetNameResolver.java b/src/java/org/apache/poi/hssf/record/formula/ExternSheetNameResolver.java
index 412c110d3..dadbcb870 100644
--- a/src/java/org/apache/poi/hssf/record/formula/ExternSheetNameResolver.java
+++ b/src/java/org/apache/poi/hssf/record/formula/ExternSheetNameResolver.java
@@ -18,6 +18,7 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
/**
* @author Josh Micich
@@ -29,13 +30,22 @@ final class ExternSheetNameResolver {
}
public static String prependSheetName(FormulaRenderingWorkbook book, int field_1_index_extern_sheet, String cellRefText) {
- String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
- StringBuffer sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
- if (sheetName.length() < 1) {
- // What excel does if sheet has been deleted
- sb.append("#REF"); // note - '!' added just once below
+ ExternalSheet externalSheet = book.getExternalSheet(field_1_index_extern_sheet);
+ StringBuffer sb;
+ if (externalSheet != null) {
+ String wbName = externalSheet.getWorkbookName();
+ String sheetName = externalSheet.getSheetName();
+ sb = new StringBuffer(wbName.length() + sheetName.length() + cellRefText.length() + 4);
+ SheetNameFormatter.appendFormat(sb, wbName, sheetName);
} else {
- SheetNameFormatter.appendFormat(sb, sheetName);
+ String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
+ sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
+ if (sheetName.length() < 1) {
+ // What excel does if sheet has been deleted
+ sb.append("#REF"); // note - '!' added just once below
+ } else {
+ SheetNameFormatter.appendFormat(sb, sheetName);
+ }
}
sb.append('!');
sb.append(cellRefText);
diff --git a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
index 261e98ce8..053057925 100644
--- a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
@@ -19,8 +19,9 @@ package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.ss.formula.WorkbookDependentFormula;
+import org.apache.poi.ss.formula.ExternSheetReferenceToken;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
+import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.util.LittleEndian;
/**
@@ -31,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
* @author Jason Height (jheight at chariot dot net dot au)
* @version 1.0-pre
*/
-public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula {
+public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula, ExternSheetReferenceToken {
public final static byte sid = 0x3a;
private final static int SIZE = 7; // 6 + 1 for Ptg
@@ -75,11 +76,11 @@ public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormu
return SIZE;
}
- public int getExternSheetIndex(){
+ public int getExternSheetIndex() {
return field_1_index_extern_sheet;
}
- public void setExternSheetIndex(int index){
+ public void setExternSheetIndex(int index) {
field_1_index_extern_sheet = index;
}
diff --git a/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java b/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
index ace857da1..fd822c398 100755
--- a/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
+++ b/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
@@ -66,6 +66,22 @@ public final class SheetNameFormatter {
out.append(rawSheetName);
}
}
+ public static void appendFormat(StringBuffer out, String workbookName, String rawSheetName) {
+ boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName);
+ if(needsQuotes) {
+ out.append(DELIMITER);
+ out.append('[');
+ appendAndEscape(out, workbookName.replace('[', '(').replace(']', ')'));
+ out.append(']');
+ appendAndEscape(out, rawSheetName);
+ out.append(DELIMITER);
+ } else {
+ out.append('[');
+ out.append(workbookName);
+ out.append(']');
+ out.append(rawSheetName);
+ }
+ }
private static void appendAndEscape(StringBuffer sb, String rawSheetName) {
int len = rawSheetName.length();
@@ -101,13 +117,27 @@ public final class SheetNameFormatter {
return true;
}
}
+ if (nameLooksLikeBooleanLiteral(rawSheetName)) {
+ return true;
+ }
+ // Error constant literals all contain '#' and other special characters
+ // so they don't get this far
return false;
}
+ private static boolean nameLooksLikeBooleanLiteral(String rawSheetName) {
+ switch(rawSheetName.charAt(0)) {
+ case 'T': case 't':
+ return "TRUE".equalsIgnoreCase(rawSheetName);
+ case 'F': case 'f':
+ return "FALSE".equalsIgnoreCase(rawSheetName);
+ }
+ return false;
+ }
/**
* @return true
if the presence of the specified character in a sheet name would
* require the sheet name to be delimited in formulas. This includes every non-alphanumeric
- * character besides underscore '_'.
+ * character besides underscore '_' and dot '.'.
*/
/* package */ static boolean isSpecialChar(char ch) {
// note - Character.isJavaIdentifierPart() would allow dollars '$'
@@ -115,7 +145,8 @@ public final class SheetNameFormatter {
return false;
}
switch(ch) {
- case '_': // underscore is ok
+ case '.': // dot is OK
+ case '_': // underscore is OK
return false;
case '\n':
case '\r':
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 000000000..9398108fd
--- /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 d82a9d114..0b6a95449 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;
@@ -39,6 +41,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
int sheetIndex = _uBook.getSheetIndex(sheetName);
return _iBook.checkExternSheet(sheetIndex);
}
+ public int getExternalSheetIndex(String workbookName, String sheetName) {
+ return _iBook.getExternalSheetIndex(workbookName, sheetName);
+ }
public EvaluationName getName(int index) {
return new Name(_iBook.getNameRecord(index), index);
@@ -57,6 +62,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
public int getSheetIndex(Sheet sheet) {
return _uBook.getSheetIndex(sheet);
}
+ public int getSheetIndex(String sheetName) {
+ return _uBook.getSheetIndex(sheetName);
+ }
public String getSheetName(int sheetIndex) {
return _uBook.getSheetName(sheetIndex);
@@ -75,8 +83,12 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
}
public int convertFromExternSheetIndex(int externSheetIndex) {
return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);
-}
+ }
+ public ExternalSheet getExternalSheet(int externSheetIndex) {
+ return _iBook.getExternalSheet(externSheetIndex);
+ }
+
public HSSFWorkbook getWorkbook() {
return _uBook;
}
@@ -96,7 +108,15 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return new Name(_iBook.getNameRecord(ix), ix);
}
public Ptg[] getFormulaTokens(Cell 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);
+ }
+ HSSFCell hCell = (HSSFCell) cell;
+ FormulaRecord fr = ((FormulaRecordAggregate) hCell.getCellValueRecord()).getFormulaRecord();
+ return fr.getParsedExpression();
}
private static final class Name implements EvaluationName {
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
index d7a320738..2f30d15c8 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
@@ -25,6 +25,7 @@ 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.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
@@ -56,6 +57,21 @@ public class HSSFFormulaEvaluator /* almost implements FormulaEvaluator */ {
public HSSFFormulaEvaluator(HSSFWorkbook workbook) {
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));
}
+
+ /**
+ * Coordinates several formula evaluators together so that formulas that involve external
+ * references can be evaluated.
+ * @param workbookNames the simple file names used to identify the workbooks in formulas
+ * with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")
+ * @param evaluators all evaluators for the full set of workbooks required by the formulas.
+ */
+ public static void setupEnvironment(String[] workbookNames, HSSFFormulaEvaluator[] evaluators) {
+ WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];
+ for (int i = 0; i < wbEvals.length; i++) {
+ wbEvals[i] = evaluators[i]._bookEvaluator;
+ }
+ CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);
+ }
/**
* Does nothing
diff --git a/src/java/org/apache/poi/hssf/util/AreaReference.java b/src/java/org/apache/poi/hssf/util/AreaReference.java
index d6575642e..6a0b0e2d3 100644
--- a/src/java/org/apache/poi/hssf/util/AreaReference.java
+++ b/src/java/org/apache/poi/hssf/util/AreaReference.java
@@ -30,6 +30,7 @@ public final class AreaReference extends org.apache.poi.ss.util.AreaReference {
/**
* Creates an area ref from a pair of Cell References.
+ * Also normalises such that the top-left
*/
public AreaReference(CellReference topLeft, CellReference botRight) {
super(topLeft, botRight);
diff --git a/src/java/org/apache/poi/hssf/util/CellReference.java b/src/java/org/apache/poi/hssf/util/CellReference.java
index 0ddb0764e..2e31b2a21 100644
--- a/src/java/org/apache/poi/hssf/util/CellReference.java
+++ b/src/java/org/apache/poi/hssf/util/CellReference.java
@@ -18,22 +18,13 @@
package org.apache.poi.hssf.util;
/**
- * Common convertion functions between Excel style A1, C27 style
+ * Common conversion functions between Excel style A1, C27 style
* cell references, and POI usermodel style row=0, column=0
* style references.
* @author Avik Sengupta
* @author Dennis Doubleday (patch to seperateRowColumns())
*/
public final class CellReference extends org.apache.poi.ss.util.CellReference {
- /**
- * Used to classify identifiers found in formulas as cell references or not.
- */
- public static final class NameType {
- public static final int CELL = 1;
- public static final int NAMED_RANGE = 2;
- public static final int BAD_CELL_OR_NAMED_RANGE = -1;
- }
-
/**
* Create an cell ref from a string representation. Sheet names containing special characters should be
* delimited and escaped as per normal syntax rules for formulas.
@@ -45,9 +36,6 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
public CellReference(int pRow, int pCol) {
super(pRow, pCol, true, true);
}
- public CellReference(int pRow, short pCol) {
- super(pRow, (int)pCol, true, true);
- }
public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
super(null, pRow, pCol, pAbsRow, pAbsCol);
@@ -55,10 +43,6 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
super(pSheetName, pRow, pCol, pAbsRow, pAbsCol);
}
-
- protected void appendCellReference(StringBuffer sb) {
- super.appendCellReference(sb);
- }
protected static String convertNumToColString(int col) {
return org.apache.poi.ss.util.CellReference.convertNumToColString(col);
}
diff --git a/src/java/org/apache/poi/ss/formula/CellLocation.java b/src/java/org/apache/poi/ss/formula/CellLocation.java
index d6bb9b7bf..6857c4bc1 100644
--- a/src/java/org/apache/poi/ss/formula/CellLocation.java
+++ b/src/java/org/apache/poi/ss/formula/CellLocation.java
@@ -17,25 +17,32 @@
package org.apache.poi.ss.formula;
+import org.apache.poi.hssf.util.CellReference;
+
/**
* Stores the parameters that identify the evaluation of one cell.null
if externSheetIndex refers to a sheet inside the current workbook
+ */
+ ExternalSheet getExternalSheet(int externSheetIndex);
int convertFromExternSheetIndex(int externSheetIndex);
EvaluationName getName(NamePtg namePtg);
String resolveNameXText(NameXPtg ptg);
Ptg[] getFormulaTokens(Cell cell);
+
+ class ExternalSheet {
+ private final String _workbookName;
+ private final String _sheetName;
+
+ public ExternalSheet(String workbookName, String sheetName) {
+ _workbookName = workbookName;
+ _sheetName = sheetName;
+ }
+ public String getWorkbookName() {
+ return _workbookName;
+ }
+ public String getSheetName() {
+ return _sheetName;
+ }
+ }
}
diff --git a/src/java/org/apache/poi/ss/formula/ExternSheetReferenceToken.java b/src/java/org/apache/poi/ss/formula/ExternSheetReferenceToken.java
new file mode 100644
index 000000000..09262a132
--- /dev/null
+++ b/src/java/org/apache/poi/ss/formula/ExternSheetReferenceToken.java
@@ -0,0 +1,29 @@
+/* ====================================================================
+ 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;
+
+/**
+ * Should be implemented by any {@link Ptg} subclass that needs has an extern sheet index null
if the range expression cannot / shouldn't be reduced.
+ */
+ private static Ptg reduceRangeExpression(Ptg ptgA, Ptg ptgB) {
+ if (!(ptgB instanceof RefPtg)) {
+ // only when second ref is simple 2-D ref can the range
+ // expression be converted to an area ref
+ return null;
+ }
+ RefPtg refB = (RefPtg) ptgB;
+ if (ptgA instanceof RefPtg) {
+ RefPtg refA = (RefPtg) ptgA;
+ return new AreaPtg(refA.getRow(), refB.getRow(), refA.getColumn(), refB.getColumn(),
+ refA.isRowRelative(), refB.isRowRelative(), refA.isColRelative(), refB.isColRelative());
+ }
+ if (ptgA instanceof Ref3DPtg) {
+ Ref3DPtg refA = (Ref3DPtg) ptgA;
+ return new Area3DPtg(refA.getRow(), refB.getRow(), refA.getColumn(), refB.getColumn(),
+ refA.isRowRelative(), refB.isRowRelative(), refA.isColRelative(), refB.isColRelative(),
+ refA.getExternSheetIndex());
+ }
+ // Note - other operand types (like AreaPtg) which probably can't evaluate
+ // do not cause validation errors at parse time
+ return null;
+ }
+
+ private Ptg parseNameOrCellRef(Identifier iden) {
+
+ if (look == '!') {
+ GetChar();
+ // 3-D ref
+ // this code assumes iden is a sheetName
+ // TODO - handle null
if name cannot be split at a dot
@@ -585,7 +667,7 @@ public final class FormulaParser {
Match('}');
return arrayNode;
}
- if (IsAlpha(look) || look == '\''){
+ if (IsAlpha(look) || look == '\'' || look == '['){
return parseFunctionReferenceOrName();
}
// else - assume number
@@ -662,7 +744,7 @@ public final class FormulaParser {
}
private Boolean parseBooleanLiteral() {
- String iden = parseIdentifier();
+ String iden = parseUnquotedIdentifier();
if ("TRUE".equalsIgnoreCase(iden)) {
return Boolean.TRUE;
}
@@ -720,7 +802,7 @@ public final class FormulaParser {
private int parseErrorLiteral() {
Match('#');
- String part1 = parseIdentifier().toUpperCase();
+ String part1 = parseUnquotedIdentifier().toUpperCase();
switch(part1.charAt(0)) {
case 'V':
diff --git a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java
index 69431c2c2..e9be7d1d3 100644
--- a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java
+++ b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java
@@ -32,6 +32,16 @@ public interface FormulaParsingWorkbook {
*/
EvaluationName getName(String name);
- int getExternalSheetIndex(String sheetName);
NameXPtg getNameXPtg(String name);
+
+ /**
+ * gets the externSheet index for a sheet from this workbook
+ */
+ int getExternalSheetIndex(String sheetName);
+ /**
+ * gets the externSheet index for a sheet from an external workbook
+ * @param workbookName e.g. "Budget.xls"
+ * @param sheetName a name of a sheet in that workbook
+ */
+ int getExternalSheetIndex(String workbookName, String sheetName);
}
diff --git a/src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java b/src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java
index ac95f4da0..c9b95f6b1 100644
--- a/src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java
+++ b/src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java
@@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
/**
* Abstracts a workbook for the purpose of converting formula to text.null
if externSheetIndex refers to a sheet inside the current workbook
+ */
+ ExternalSheet getExternalSheet(int externSheetIndex);
String getSheetNameByExternSheet(int externSheetIndex);
String resolveNameXText(NameXPtg nameXPtg);
String getNameText(NamePtg namePtg);
diff --git a/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java b/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java
index 79087e4d6..91226bd53 100644
--- a/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java
+++ b/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java
@@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.ControlPtg;
+import org.apache.poi.hssf.record.formula.RangePtg;
import org.apache.poi.hssf.record.formula.ValueOperatorPtg;
import org.apache.poi.hssf.record.formula.Ptg;
@@ -115,6 +116,10 @@ final class OperandClassTransformer {
return;
}
if (children.length > 0) {
+ if (token == RangePtg.instance) {
+ // TODO is any token transformation required under the various ref operators?
+ return;
+ }
throw new IllegalStateException("Node should not have any children");
}
diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java b/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
index eaa57c114..09c93723f 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 b41e2c2c3..9c02ca9f9 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;
@@ -53,6 +60,7 @@ import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
@@ -68,13 +76,14 @@ import org.apache.poi.ss.usermodel.Sheet;
*
* @author Josh Micich
*/
-public class WorkbookEvaluator {
+public final class WorkbookEvaluator {
private final EvaluationWorkbook _workbook;
- private final EvaluationCache _cache;
+ private EvaluationCache _cache;
private final IEvaluationListener _evaluationListener;
private final Map _sheetIndexesBySheet;
+ private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
public WorkbookEvaluator(EvaluationWorkbook workbook) {
this (workbook, null);
@@ -84,6 +93,7 @@ public class WorkbookEvaluator {
_evaluationListener = evaluationListener;
_cache = new EvaluationCache(evaluationListener);
_sheetIndexesBySheet = new IdentityHashMap();
+ _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
}
/**
@@ -101,7 +111,22 @@ public class WorkbookEvaluator {
System.out.println(s);
}
}
+ /* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache) {
+ _collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
+ _cache = cache;
+ }
+ /* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
+ return _collaboratingWorkbookEnvironment;
+ }
+ /* package */ void detachFromEnvironment() {
+ _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
+ _cache = new EvaluationCache(_evaluationListener);
+ }
+ /* package */ IEvaluationListener getEvaluationListener() {
+ return _evaluationListener;
+ }
+
/**
* Should be called whenever there are changes to input cells in the evaluated workbook.
* Failure to call this method after changing cell values will cause incorrect behaviour
@@ -123,7 +148,7 @@ public class WorkbookEvaluator {
throw new IllegalArgumentException("value must not be null");
}
int sheetIndex = getSheetIndex(sheet);
- _cache.setValue(new CellLocation(sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
+ _cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
}
/**
@@ -132,13 +157,17 @@ public class WorkbookEvaluator {
*/
public void notifySetFormula(Sheet sheet, int rowIndex, int columnIndex) {
int sheetIndex = getSheetIndex(sheet);
- _cache.setValue(new CellLocation(sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
+ _cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
}
private int getSheetIndex(Sheet sheet) {
Integer result = (Integer) _sheetIndexesBySheet.get(sheet);
if (result == null) {
- result = new Integer(_workbook.getSheetIndex(sheet));
+ int sheetIndex = _workbook.getSheetIndex(sheet);
+ if (sheetIndex < 0) {
+ throw new RuntimeException("Specified sheet from a different book");
+ }
+ result = new Integer(sheetIndex);
_sheetIndexesBySheet.put(sheet, result);
}
return result.intValue();
@@ -146,7 +175,7 @@ public class WorkbookEvaluator {
public ValueEval evaluate(Cell srcCell) {
int sheetIndex = getSheetIndex(srcCell.getSheet());
- CellLocation cellLoc = new CellLocation(sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
+ CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
return internalEvaluate(srcCell, cellLoc, new EvaluationTracker(_cache));
}
@@ -181,10 +210,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 +254,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 +332,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();
@@ -321,6 +364,20 @@ public class WorkbookEvaluator {
}
return operation.evaluate(ops, srcRowNum, (short)srcColNum);
}
+ private SheetRefEvaluator createExternSheetRefEvaluator(EvaluationTracker tracker,
+ ExternSheetReferenceToken ptg) {
+ int externSheetIndex = ptg.getExternSheetIndex();
+ ExternalSheet externalSheet = _workbook.getExternalSheet(externSheetIndex);
+ if (externalSheet != null) {
+ WorkbookEvaluator otherEvaluator = _collaboratingWorkbookEnvironment.getWorkbookEvaluator(externalSheet.getWorkbookName());
+ EvaluationWorkbook otherBook = otherEvaluator._workbook;
+ int otherSheetIndex = otherBook.getSheetIndex(externalSheet.getSheetName());
+ return new SheetRefEvaluator(otherEvaluator, tracker, otherBook, otherSheetIndex);
+ }
+ int otherSheetIndex = _workbook.convertFromExternSheetIndex(externSheetIndex);
+ return new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
+
+ }
/**
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
@@ -329,6 +386,8 @@ public class WorkbookEvaluator {
* passed here!
*/
private Eval getEvalForPtg(Ptg ptg, int sheetIndex, EvaluationTracker tracker) {
+ // consider converting all these (ptg instanceof XxxPtg) expressions to (ptg.getClass() == XxxPtg.class)
+
if (ptg instanceof NamePtg) {
// named ranges, macro functions
NamePtg namePtg = (NamePtg) ptg;
@@ -361,16 +420,18 @@ 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());
- SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
+ SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, refPtg);
return new LazyRefEval(refPtg, sre);
}
if (ptg instanceof Area3DPtg) {
Area3DPtg aptg = (Area3DPtg) ptg;
- int otherSheetIndex = _workbook.convertFromExternSheetIndex(aptg.getExternSheetIndex());
- SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
+ SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, aptg);
return new LazyAreaEval(aptg, sre);
}
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, sheetIndex);
@@ -410,7 +471,7 @@ public class WorkbookEvaluator {
} else {
cell = row.getCell(columnIndex);
}
- CellLocation cellLoc = new CellLocation(sheetIndex, rowIndex, columnIndex);
+ CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex);
tracker.acceptDependency(cellLoc);
return internalEvaluate(cell, cellLoc, tracker);
}
diff --git a/src/java/org/apache/poi/ss/util/AreaReference.java b/src/java/org/apache/poi/ss/util/AreaReference.java
index ae2c9192f..30d92ebcf 100644
--- a/src/java/org/apache/poi/ss/util/AreaReference.java
+++ b/src/java/org/apache/poi/ss/util/AreaReference.java
@@ -90,7 +90,7 @@ public class AreaReference {
for(int i=refPart.length()-1; i>=0; i--) {
int ch = refPart.charAt(i);
if (ch == '$' && i==0) {
- continue;
+ continue;
}
if (ch < 'A' || ch > 'Z') {
return false;
@@ -103,8 +103,45 @@ public class AreaReference {
* Creates an area ref from a pair of Cell References.
*/
public AreaReference(CellReference topLeft, CellReference botRight) {
- _firstCell = topLeft;
- _lastCell = botRight;
+ boolean swapRows = topLeft.getRow() > botRight.getRow();
+ boolean swapCols = topLeft.getCol() > botRight.getCol();
+ if (swapRows || swapCols) {
+ int firstRow;
+ int lastRow;
+ int firstColumn;
+ int lastColumn;
+ boolean firstRowAbs;
+ boolean lastRowAbs;
+ boolean firstColAbs;
+ boolean lastColAbs;
+ if (swapRows) {
+ firstRow = botRight.getRow();
+ firstRowAbs = botRight.isRowAbsolute();
+ lastRow = topLeft.getRow();
+ lastRowAbs = topLeft.isRowAbsolute();
+ } else {
+ firstRow = topLeft.getRow();
+ firstRowAbs = topLeft.isRowAbsolute();
+ lastRow = botRight.getRow();
+ lastRowAbs = botRight.isRowAbsolute();
+ }
+ if (swapCols) {
+ firstColumn = botRight.getCol();
+ firstColAbs = botRight.isColAbsolute();
+ lastColumn = topLeft.getCol();
+ lastColAbs = topLeft.isColAbsolute();
+ } else {
+ firstColumn = topLeft.getCol();
+ firstColAbs = topLeft.isColAbsolute();
+ lastColumn = botRight.getCol();
+ lastColAbs = botRight.isColAbsolute();
+ }
+ _firstCell = new CellReference(firstRow, firstColumn, firstRowAbs, firstColAbs);
+ _lastCell = new CellReference(lastRow, lastColumn, lastRowAbs, lastColAbs);
+ } else {
+ _firstCell = topLeft;
+ _lastCell = botRight;
+ }
_isSingleCell = false;
}
diff --git a/src/java/org/apache/poi/ss/util/CellReference.java b/src/java/org/apache/poi/ss/util/CellReference.java
index e5005db5d..23b4c806e 100644
--- a/src/java/org/apache/poi/ss/util/CellReference.java
+++ b/src/java/org/apache/poi/ss/util/CellReference.java
@@ -34,21 +34,28 @@ public class CellReference {
public static final class NameType {
public static final int CELL = 1;
public static final int NAMED_RANGE = 2;
+ public static final int COLUMN = 3;
public static final int BAD_CELL_OR_NAMED_RANGE = -1;
}
- /** The character ($) that signifies a row or column value is absolute instead of relative */
- private static final char ABSOLUTE_REFERENCE_MARKER = '$';
- /** The character (!) that separates sheet names from cell references */
- private static final char SHEET_NAME_DELIMITER = '!';
- /** The character (') used to quote sheet names when they contain special characters */
- private static final char SPECIAL_NAME_DELIMITER = '\'';
-
- /**
- * Matches a run of letters followed by a run of digits. The run of letters is group 1 and the
- * run of digits is group 2. Each group may optionally be prefixed with a single '$'.
- */
+ /** The character ($) that signifies a row or column value is absolute instead of relative */
+ private static final char ABSOLUTE_REFERENCE_MARKER = '$';
+ /** The character (!) that separates sheet names from cell references */
+ private static final char SHEET_NAME_DELIMITER = '!';
+ /** The character (') used to quote sheet names when they contain special characters */
+ private static final char SPECIAL_NAME_DELIMITER = '\'';
+
+ /**
+ * Matches a run of one or more letters followed by a run of one or more digits.
+ * The run of letters is group 1 and the run of digits is group 2.
+ * Each group may optionally be prefixed with a single '$'.
+ */
private static final Pattern CELL_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)\\$?([0-9]+)");
+ /**
+ * Matches a run of one or more letters. The run of letters is group 1.
+ * The text may optionally be prefixed with a single '$'.
+ */
+ private static final Pattern COLUMN_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)");
/**
* Named range names must start with a letter or underscore. Subsequent characters may include
* digits or dot. (They can even end in dot).
@@ -65,96 +72,96 @@ public class CellReference {
private final boolean _isRowAbs;
private final boolean _isColAbs;
- /**
- * Create an cell ref from a string representation. Sheet names containing special characters should be
- * delimited and escaped as per normal syntax rules for formulas.
- */
- public CellReference(String cellRef) {
- String[] parts = separateRefParts(cellRef);
- _sheetName = parts[0];
- String colRef = parts[1];
- if (colRef.length() < 1) {
- throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
- }
- _isColAbs = colRef.charAt(0) == '$';
- if (_isColAbs) {
- colRef=colRef.substring(1);
- }
- _colIndex = convertColStringToIndex(colRef);
-
- String rowRef=parts[2];
- if (rowRef.length() < 1) {
- throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
- }
- _isRowAbs = rowRef.charAt(0) == '$';
- if (_isRowAbs) {
- rowRef=rowRef.substring(1);
- }
- _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based
- }
+ /**
+ * Create an cell ref from a string representation. Sheet names containing special characters should be
+ * delimited and escaped as per normal syntax rules for formulas.
+ */
+ public CellReference(String cellRef) {
+ String[] parts = separateRefParts(cellRef);
+ _sheetName = parts[0];
+ String colRef = parts[1];
+ if (colRef.length() < 1) {
+ throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
+ }
+ _isColAbs = colRef.charAt(0) == '$';
+ if (_isColAbs) {
+ colRef=colRef.substring(1);
+ }
+ _colIndex = convertColStringToIndex(colRef);
+
+ String rowRef=parts[2];
+ if (rowRef.length() < 1) {
+ throw new IllegalArgumentException("Invalid Formula cell reference: '"+cellRef+"'");
+ }
+ _isRowAbs = rowRef.charAt(0) == '$';
+ if (_isRowAbs) {
+ rowRef=rowRef.substring(1);
+ }
+ _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based
+ }
- public CellReference(int pRow, int pCol) {
- this(pRow, pCol & 0xFFFF, false, false);
- }
- public CellReference(int pRow, short pCol) {
- this(pRow, (int)pCol, false, false);
- }
+ public CellReference(int pRow, int pCol) {
+ this(pRow, pCol, false, false);
+ }
+ public CellReference(int pRow, short pCol) {
+ this(pRow, pCol & 0xFFFF, false, false);
+ }
- public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
- this(null, pRow, pCol, pAbsRow, pAbsCol);
- }
- public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
- // TODO - "-1" is a special value being temporarily used for whole row and whole column area references.
- // so these checks are currently N.Q.R.
- if(pRow < -1) {
- throw new IllegalArgumentException("row index may not be negative");
- }
- if(pCol < -1) {
- throw new IllegalArgumentException("column index may not be negative");
- }
- _sheetName = pSheetName;
- _rowIndex=pRow;
- _colIndex=pCol;
- _isRowAbs = pAbsRow;
- _isColAbs=pAbsCol;
- }
+ public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
+ this(null, pRow, pCol, pAbsRow, pAbsCol);
+ }
+ public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
+ // TODO - "-1" is a special value being temporarily used for whole row and whole column area references.
+ // so these checks are currently N.Q.R.
+ if(pRow < -1) {
+ throw new IllegalArgumentException("row index may not be negative");
+ }
+ if(pCol < -1) {
+ throw new IllegalArgumentException("column index may not be negative");
+ }
+ _sheetName = pSheetName;
+ _rowIndex=pRow;
+ _colIndex=pCol;
+ _isRowAbs = pAbsRow;
+ _isColAbs=pAbsCol;
+ }
- public int getRow(){return _rowIndex;}
- public short getCol(){return (short) _colIndex;}
- public boolean isRowAbsolute(){return _isRowAbs;}
- public boolean isColAbsolute(){return _isColAbs;}
- /**
- * @return possibly null
if this is a 2D reference. Special characters are not
- * escaped or delimited
- */
- public String getSheetName(){
- return _sheetName;
- }
-
- public static boolean isPartAbsolute(String part) {
- return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER;
- }
-
- /**
- * takes in a column reference portion of a CellRef and converts it from
- * ALPHA-26 number format to 0-based base 10.
- * 'A' -> 0
- * 'Z' -> 25
- * 'AA' -> 26
- * 'IV' -> 255
- * @return zero based column index
- */
- protected static int convertColStringToIndex(String ref) {
- int pos = 0;
- int retval=0;
+ public int getRow(){return _rowIndex;}
+ public short getCol(){return (short) _colIndex;}
+ public boolean isRowAbsolute(){return _isRowAbs;}
+ public boolean isColAbsolute(){return _isColAbs;}
+ /**
+ * @return possibly null
if this is a 2D reference. Special characters are not
+ * escaped or delimited
+ */
+ public String getSheetName(){
+ return _sheetName;
+ }
+
+ public static boolean isPartAbsolute(String part) {
+ return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER;
+ }
+ /**
+ * takes in a column reference portion of a CellRef and converts it from
+ * ALPHA-26 number format to 0-based base 10.
+ * 'A' -> 0
+ * 'Z' -> 25
+ * 'AA' -> 26
+ * 'IV' -> 255
+ * @return zero based column index
+ */
+ public static int convertColStringToIndex(String ref) {
+
+ int pos = 0;
+ int retval=0;
for (int k = ref.length()-1; k >= 0; k--) {
char thechar = ref.charAt(k);
- if (thechar == ABSOLUTE_REFERENCE_MARKER) {
- if (k != 0) {
- throw new IllegalArgumentException("Bad col ref format '" + ref + "'");
- }
- break;
- }
+ if (thechar == ABSOLUTE_REFERENCE_MARKER) {
+ if (k != 0) {
+ throw new IllegalArgumentException("Bad col ref format '" + ref + "'");
+ }
+ break;
+ }
// Character.getNumericValue() returns the values
// 10-35 for the letter A-Z
int shift = (int)Math.pow(26, pos);
@@ -162,64 +169,70 @@ public class CellReference {
pos++;
}
return retval-1;
- }
+ }
- /**
- * Classifies an identifier as either a simple (2D) cell reference or a named range name
- * @return one of the values from NameType
- */
- public static int classifyCellReference(String str) {
- int len = str.length();
- if (len < 1) {
- throw new IllegalArgumentException("Empty string not allowed");
- }
- char firstChar = str.charAt(0);
- switch (firstChar) {
- case ABSOLUTE_REFERENCE_MARKER:
- case '.':
- case '_':
- break;
- default:
- if (!Character.isLetter(firstChar)) {
- throw new IllegalArgumentException("Invalid first char (" + firstChar
- + ") of cell reference or named range. Letter expected");
- }
- }
- if (!Character.isDigit(str.charAt(len-1))) {
- // no digits at end of str
- return validateNamedRangeName(str);
- }
- Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str);
- if (!cellRefPatternMatcher.matches()) {
- return validateNamedRangeName(str);
- }
- String lettersGroup = cellRefPatternMatcher.group(1);
- String digitsGroup = cellRefPatternMatcher.group(2);
- if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) {
- // valid cell reference
- return NameType.CELL;
- }
- // If str looks like a cell reference, but is out of (row/col) range, it is a valid
- // named range name
- // This behaviour is a little weird. For example, "IW123" is a valid named range name
- // because the column "IW" is beyond the maximum "IV". Note - this behaviour is version
- // dependent. In Excel 2007, "IW123" is not a valid named range name.
- if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) {
- // Of course, named range names cannot have '$'
- return NameType.BAD_CELL_OR_NAMED_RANGE;
- }
- return NameType.NAMED_RANGE;
- }
+ /**
+ * Classifies an identifier as either a simple (2D) cell reference or a named range name
+ * @return one of the values from NameType
+ */
+ public static int classifyCellReference(String str) {
+ int len = str.length();
+ if (len < 1) {
+ throw new IllegalArgumentException("Empty string not allowed");
+ }
+ char firstChar = str.charAt(0);
+ switch (firstChar) {
+ case ABSOLUTE_REFERENCE_MARKER:
+ case '.':
+ case '_':
+ break;
+ default:
+ if (!Character.isLetter(firstChar)) {
+ throw new IllegalArgumentException("Invalid first char (" + firstChar
+ + ") of cell reference or named range. Letter expected");
+ }
+ }
+ if (!Character.isDigit(str.charAt(len-1))) {
+ // no digits at end of str
+ return validateNamedRangeName(str);
+ }
+ Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str);
+ if (!cellRefPatternMatcher.matches()) {
+ return validateNamedRangeName(str);
+ }
+ String lettersGroup = cellRefPatternMatcher.group(1);
+ String digitsGroup = cellRefPatternMatcher.group(2);
+ if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) {
+ // valid cell reference
+ return NameType.CELL;
+ }
+ // If str looks like a cell reference, but is out of (row/col) range, it is a valid
+ // named range name
+ // This behaviour is a little weird. For example, "IW123" is a valid named range name
+ // because the column "IW" is beyond the maximum "IV". Note - this behaviour is version
+ // dependent. In BIFF12, "IW123" is not a valid named range name, but in BIFF8 it is.
+ if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) {
+ // Of course, named range names cannot have '$'
+ return NameType.BAD_CELL_OR_NAMED_RANGE;
+ }
+ return NameType.NAMED_RANGE;
+ }
- private static int validateNamedRangeName(String str) {
+ private static int validateNamedRangeName(String str) {
+ Matcher colMatcher = COLUMN_REF_PATTERN.matcher(str);
+ if (colMatcher.matches()) {
+ String colStr = colMatcher.group(1);
+ if (isColumnWithnRange(colStr)) {
+ return NameType.COLUMN;
+ }
+ }
if (!NAMED_RANGE_NAME_PATTERN.matcher(str).matches()) {
return NameType.BAD_CELL_OR_NAMED_RANGE;
}
return NameType.NAMED_RANGE;
-
}
-
-
+
+
/**
* Used to decide whether a name of the form "[A-Z]*[0-9]*" that appears in a formula can be
* interpreted as a cell reference. Names of that form can be also used for sheets and/or
@@ -240,7 +253,7 @@ public class CellReference {
* *
+ * Input - *Result Result ** "A", "1" true * "a", "111" true @@ -257,23 +270,13 @@ public class CellReference { * @return "A", "65536" true true
if the row and col parameters are within range of a BIFF8 spreadsheet. */ public static boolean cellReferenceIsWithinRange(String colStr, String rowStr) { - int numberOfLetters = colStr.length(); - if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) { - // "Sheet1" case etc - return false; // that was easy + if (!isColumnWithnRange(colStr)) { + return false; } int nDigits = rowStr.length(); if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) { return false; } - if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) { - if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) { - return false; - } - } else { - // apparent column name has less chars than max - // no need to check range - } if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) { // ASCII comparison is valid if digit count is same @@ -288,87 +291,104 @@ public class CellReference { return true; } + private static boolean isColumnWithnRange(String colStr) { + int numberOfLetters = colStr.length(); + if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) { + // "Sheet1" case etc + return false; // that was easy + } + if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) { + if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) { + return false; + } + } else { + // apparent column name has less chars than max + // no need to check range + } + return true; + } + /** - * Separates the row from the columns and returns an array of three Strings. The first element - * is the sheet name. Only the first element may be null. The second element in is the column - * name still in ALPHA-26 number format. The third element is the row. - */ - private static String[] separateRefParts(String reference) { - - int plingPos = reference.lastIndexOf(SHEET_NAME_DELIMITER); - String sheetName = parseSheetName(reference, plingPos); - int start = plingPos+1; + * Separates the row from the columns and returns an array of three Strings. The first element + * is the sheet name. Only the first element may be null. The second element in is the column + * name still in ALPHA-26 number format. The third element is the row. + */ + private static String[] separateRefParts(String reference) { + + int plingPos = reference.lastIndexOf(SHEET_NAME_DELIMITER); + String sheetName = parseSheetName(reference, plingPos); + int start = plingPos+1; - int length = reference.length(); + int length = reference.length(); - int loc = start; - // skip initial dollars - if (reference.charAt(loc)==ABSOLUTE_REFERENCE_MARKER) { - loc++; - } - // step over column name chars until first digit (or dollars) for row number. - for (; loc < length; loc++) { - char ch = reference.charAt(loc); - if (Character.isDigit(ch) || ch == ABSOLUTE_REFERENCE_MARKER) { - break; - } - } - return new String[] { - sheetName, - reference.substring(start,loc), - reference.substring(loc), - }; - } + int loc = start; + // skip initial dollars + if (reference.charAt(loc)==ABSOLUTE_REFERENCE_MARKER) { + loc++; + } + // step over column name chars until first digit (or dollars) for row number. + for (; loc < length; loc++) { + char ch = reference.charAt(loc); + if (Character.isDigit(ch) || ch == ABSOLUTE_REFERENCE_MARKER) { + break; + } + } + return new String[] { + sheetName, + reference.substring(start,loc), + reference.substring(loc), + }; + } - private static String parseSheetName(String reference, int indexOfSheetNameDelimiter) { - if(indexOfSheetNameDelimiter < 0) { - return null; - } - - boolean isQuoted = reference.charAt(0) == SPECIAL_NAME_DELIMITER; - if(!isQuoted) { - return reference.substring(0, indexOfSheetNameDelimiter); - } - int lastQuotePos = indexOfSheetNameDelimiter-1; - if(reference.charAt(lastQuotePos) != SPECIAL_NAME_DELIMITER) { - throw new RuntimeException("Mismatched quotes: (" + reference + ")"); - } + private static String parseSheetName(String reference, int indexOfSheetNameDelimiter) { + if(indexOfSheetNameDelimiter < 0) { + return null; + } + + boolean isQuoted = reference.charAt(0) == SPECIAL_NAME_DELIMITER; + if(!isQuoted) { + return reference.substring(0, indexOfSheetNameDelimiter); + } + int lastQuotePos = indexOfSheetNameDelimiter-1; + if(reference.charAt(lastQuotePos) != SPECIAL_NAME_DELIMITER) { + throw new RuntimeException("Mismatched quotes: (" + reference + ")"); + } - // TODO - refactor cell reference parsing logic to one place. - // Current known incarnations: - // FormulaParser.GetName() - // CellReference.parseSheetName() (here) - // AreaReference.separateAreaRefs() - // SheetNameFormatter.format() (inverse) - - StringBuffer sb = new StringBuffer(indexOfSheetNameDelimiter); - - for(int i=1; iD - */ - protected static String convertNumToColString(int col) { + /** + * Takes in a 0-based base-10 column and returns a ALPHA-26 + * representation. + * eg column #3 -> D + */ + protected static String convertNumToColString(int col) { // Excel counts column A as the 1st column, we // treat it as the 0th one int excelColNum = col + 1; @@ -387,36 +407,36 @@ public class CellReference { } return colRef; - } + } + + /** + * Example return values: + * + *
+ * @return the text representation of this cell reference as it would appear in a formula. + */ + public String formatAsString() { + StringBuffer sb = new StringBuffer(32); + if(_sheetName != null) { + SheetNameFormatter.appendFormat(sb, _sheetName); + sb.append(SHEET_NAME_DELIMITER); + } + appendCellReference(sb); + return sb.toString(); + } + + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(formatAsString()); + sb.append("]"); + return sb.toString(); + } - /** - * Example return values: - *+ * Result Comment + * A1 Cell reference without sheet + * Sheet1!A1 Standard sheet name + * 'O''Brien''s Sales'!A1' Sheet name with special characters - *
- * @return the text representation of this cell reference as it would appear in a formula. - */ - public String formatAsString() { - StringBuffer sb = new StringBuffer(32); - if(_sheetName != null) { - SheetNameFormatter.appendFormat(sb, _sheetName); - sb.append(SHEET_NAME_DELIMITER); - } - appendCellReference(sb); - return sb.toString(); - } - - public String toString() { - StringBuffer sb = new StringBuffer(64); - sb.append(getClass().getName()).append(" ["); - sb.append(formatAsString()); - sb.append("]"); - return sb.toString(); - } - /** * Returns the three parts of the cell reference, the * Sheet name (or null if none supplied), the 1 based @@ -433,18 +453,18 @@ public class CellReference { }; } - /** - * Appends cell reference with '$' markers for absolute values as required. - * Sheet name is not included. - */ - protected void appendCellReference(StringBuffer sb) { - if(_isColAbs) { - sb.append(ABSOLUTE_REFERENCE_MARKER); - } - sb.append( convertNumToColString(_colIndex)); - if(_isRowAbs) { - sb.append(ABSOLUTE_REFERENCE_MARKER); - } - sb.append(_rowIndex+1); - } + /** + * Appends cell reference with '$' markers for absolute values as required. + * Sheet name is not included. + */ + /* package */ void appendCellReference(StringBuffer sb) { + if(_isColAbs) { + sb.append(ABSOLUTE_REFERENCE_MARKER); + } + sb.append( convertNumToColString(_colIndex)); + if(_isRowAbs) { + sb.append(ABSOLUTE_REFERENCE_MARKER); + } + sb.append(_rowIndex+1); + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java index 7c03ebe7f..ddadf96eb 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java @@ -57,10 +57,6 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E return convertToExternalSheetIndex(sheetIndex); } - public EvaluationName getName(int index) { - return new Name(_uBook.getNameAt(index), index, this); - } - public EvaluationName getName(String name) { for(int i=0; i < _uBook.getNumberOfNames(); i++) { String nameText = _uBook.getNameName(i); @@ -88,14 +84,15 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E return _uBook.getSheetAt(sheetIndex); } - /** - * Doesn't do anything - returns the same index - * TODO - figure out if this is a ole2 specific thing, or - * if we need to do something proper here too! - */ - public Sheet getSheetByExternSheetIndex(int externSheetIndex) { - int sheetIndex = convertFromExternalSheetIndex(externSheetIndex); - return _uBook.getSheetAt(sheetIndex); + public ExternalSheet getExternalSheet(int externSheetIndex) { + // TODO Auto-generated method stub + return null; + } + public int getExternalSheetIndex(String workbookName, String sheetName) { + throw new RuntimeException("not implemented yet"); + } + public int getSheetIndex(String sheetName) { + return _uBook.getSheetIndex(sheetName); } public Workbook getWorkbook() { diff --git a/src/testcases/org/apache/poi/hssf/HSSFTests.java b/src/testcases/org/apache/poi/hssf/HSSFTests.java index aa5bf46d3..d5a491099 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- * Result Comment - * A1 Cell reference without sheet - * Sheet1!A1 Standard sheet name - * 'O''Brien''s Sales'!A1' Sheet name with special characters
@@ -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/data/multibookFormulaA.xls b/src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls new file mode 100644 index 000000000..b844fc1db Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/multibookFormulaA.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls b/src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls new file mode 100644 index 000000000..3e4708d6c Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/multibookFormulaB.xls differ diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index 016fa8398..53061a228 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -43,11 +43,13 @@ import org.apache.poi.hssf.record.formula.NumberPtg; 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.Ref3DPtg; import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.record.formula.StringPtg; import org.apache.poi.hssf.record.formula.SubtractPtg; import org.apache.poi.hssf.record.formula.UnaryMinusPtg; import org.apache.poi.hssf.record.formula.UnaryPlusPtg; +import org.apache.poi.hssf.usermodel.FormulaExtractor; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFErrorConstants; import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook; @@ -553,14 +555,14 @@ public final class TestFormulaParser extends TestCase { Class[] expClss; - expClss = new Class[] { - RefPtg.class, + expClss = new Class[] { + RefPtg.class, AttrPtg.class, // tAttrIf - MissingArgPtg.class, + MissingArgPtg.class, AttrPtg.class, // tAttrSkip RefPtg.class, AttrPtg.class, // tAttrSkip - FuncVarPtg.class, + FuncVarPtg.class, }; confirmTokenClasses("if(A1, ,C1)", expClss); @@ -735,10 +737,10 @@ public final class TestFormulaParser extends TestCase { /** * Make sure that POI uses the right Func Ptg when encoding formulas. Functions with variable * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg. - * + * * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions. In many cases - * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases - * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead. + * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases + * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead. */ public void testFuncPtgSelection() { @@ -777,7 +779,7 @@ public final class TestFormulaParser extends TestCase { parseFormula("round(3.14;2)"); throw new AssertionFailedError("Didn't get parse exception as expected"); } catch (RuntimeException e) { - FormulaParserTestHelper.confirmParseException(e, + FormulaParserTestHelper.confirmParseException(e, "Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'"); } @@ -785,11 +787,11 @@ public final class TestFormulaParser extends TestCase { parseFormula(" =2+2"); throw new AssertionFailedError("Didn't get parse exception as expected"); } catch (RuntimeException e) { - FormulaParserTestHelper.confirmParseException(e, + FormulaParserTestHelper.confirmParseException(e, "The specified formula ' =2+2' starts with an equals sign which is not allowed."); } } - + /** * this function name has a dot in it. */ @@ -798,8 +800,8 @@ public final class TestFormulaParser extends TestCase { Ptg[] ptgs; try { ptgs = parseFormula("error.type(A1)"); - - + + } catch (IllegalArgumentException e) { if (e.getMessage().equals("Invalid Formula cell reference: 'error'")) { throw new AssertionFailedError("Identified bug 45334"); @@ -811,7 +813,7 @@ public final class TestFormulaParser extends TestCase { FuncPtg funcPtg = (FuncPtg) ptgs[1]; assertEquals("ERROR.TYPE", funcPtg.getName()); } - + public void testNamedRangeThatLooksLikeCell() { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet sheet = wb.createSheet("Sheet1"); @@ -838,35 +840,35 @@ public final class TestFormulaParser extends TestCase { cell.setCellFormula("count(pf1)"); throw new AssertionFailedError("Expected formula parse execption"); } catch (RuntimeException e) { - FormulaParserTestHelper.confirmParseException(e, + FormulaParserTestHelper.confirmParseException(e, "Specified named range 'pf1' does not exist in the current workbook."); } cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range } - + public void testParseAreaRefHighRow_bug45358() { Ptg[] ptgs; AreaI aptg; - + HSSFWorkbook book = new HSSFWorkbook(); book.createSheet("Sheet1"); - + ptgs = HSSFFormulaParser.parse("Sheet1!A10:A40000", book); aptg = (AreaI) ptgs[0]; if (aptg.getLastRow() == -25537) { throw new AssertionFailedError("Identified bug 45358"); } assertEquals(39999, aptg.getLastRow()); - + ptgs = HSSFFormulaParser.parse("Sheet1!A10:A65536", book); aptg = (AreaI) ptgs[0]; assertEquals(65535, aptg.getLastRow()); - + // plain area refs should be ok too ptgs = parseFormula("A10:A65536"); aptg = (AreaI) ptgs[0]; assertEquals(65535, aptg.getLastRow()); - + } public void testParseArray() { Ptg[] ptgs; @@ -875,11 +877,71 @@ public final class TestFormulaParser extends TestCase { Ptg ptg0 = ptgs[0]; assertEquals(ArrayPtg.class, ptg0.getClass()); assertEquals("{1.0,2.0,2.0,#REF!;FALSE,3.0,3.0,2.0}", ptg0.toFormulaString()); - + ArrayPtg aptg = (ArrayPtg) ptg0; Object[][] values = aptg.getTokenArrayValues(); assertEquals(ErrorConstant.valueOf(HSSFErrorConstants.ERROR_REF), values[0][3]); assertEquals(Boolean.FALSE, values[1][0]); - } -} \ No newline at end of file + + public void testRangeOperator() { + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + HSSFCell cell = sheet.createRow(0).createCell(0); + + wb.setSheetName(0, "Sheet1"); + cell.setCellFormula("Sheet1!B$4:Sheet1!$C1"); // explicit range ':' operator + assertEquals("Sheet1!B$4:Sheet1!$C1", cell.getCellFormula()); + + cell.setCellFormula("Sheet1!B$4:$C1"); // plain area ref + assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); // note - area ref is normalised + + cell.setCellFormula("Sheet1!$C1...B$4"); // different syntax for plain area ref + assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); + + // with funny sheet name + wb.setSheetName(0, "A1...A2"); + cell.setCellFormula("A1...A2!B1"); + assertEquals("A1...A2!B1", cell.getCellFormula()); + } + + public void testBooleanNamedSheet() { + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet("true"); + HSSFCell cell = sheet.createRow(0).createCell(0); + cell.setCellFormula("'true'!B2"); + + assertEquals("'true'!B2", cell.getCellFormula()); + } + + public void testParseExternalWorkbookReference() { + HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls"); + HSSFCell cell = wbA.getSheetAt(0).getRow(0).getCell(0); + + // make sure formula in sample is as expected + assertEquals("[multibookFormulaB.xls]BSheet1!B1", cell.getCellFormula()); + Ptg[] expectedPtgs = FormulaExtractor.getPtgs(cell); + confirmSingle3DRef(expectedPtgs, 1); + + // now try (re-)parsing the formula + Ptg[] actualPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]BSheet1!B1", wbA); + confirmSingle3DRef(actualPtgs, 1); // externalSheetIndex 1 -> BSheet1 + + // try parsing a formula pointing to a different external sheet + Ptg[] otherPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]AnotherSheet!B1", wbA); + confirmSingle3DRef(otherPtgs, 0); // externalSheetIndex 0 -> AnotherSheet + + // try setting the same formula in a cell + cell.setCellFormula("[multibookFormulaB.xls]AnotherSheet!B1"); + assertEquals("[multibookFormulaB.xls]AnotherSheet!B1", cell.getCellFormula()); + } + private static void confirmSingle3DRef(Ptg[] ptgs, int expectedExternSheetIndex) { + assertEquals(1, ptgs.length); + Ptg ptg0 = ptgs[0]; + assertEquals(Ref3DPtg.class, ptg0.getClass()); + assertEquals(expectedExternSheetIndex, ((Ref3DPtg)ptg0).getExternSheetIndex()); + } + +} diff --git a/src/testcases/org/apache/poi/hssf/record/TestSupBookRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSupBookRecord.java index 3727989ef..e2a61dde7 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestSupBookRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestSupBookRecord.java @@ -78,7 +78,7 @@ public final class TestSupBookRecord extends TestCase { assertEquals( 34, record.getRecordSize() ); //sid+size+data - assertEquals("testURL", record.getURL().getString()); + assertEquals("testURL", record.getURL()); UnicodeString[] sheetNames = record.getSheetNames(); assertEquals(2, sheetNames.length); assertEquals("Sheet1", sheetNames[0].getString()); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java b/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java index 369c09583..b7c1664ee 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java @@ -20,16 +20,12 @@ package org.apache.poi.hssf.record.formula; import junit.framework.TestCase; /** - * Tests for SheetNameFormatter + * Tests for {@link SheetNameFormatter} * * @author Josh Micich */ public final class TestSheetNameFormatter extends TestCase { - public TestSheetNameFormatter(String testName) { - super(testName); - } - private static void confirmFormat(String rawSheetName, String expectedSheetNameEncoding) { assertEquals(expectedSheetNameEncoding, SheetNameFormatter.format(rawSheetName)); } @@ -55,6 +51,16 @@ public final class TestSheetNameFormatter extends TestCase { confirmFormat("TAXRETURN19980415", "TAXRETURN19980415"); } + public void testBooleanLiterals() { + confirmFormat("TRUE", "'TRUE'"); + confirmFormat("FALSE", "'FALSE'"); + confirmFormat("True", "'True'"); + confirmFormat("fAlse", "'fAlse'"); + + confirmFormat("Yes", "Yes"); + confirmFormat("No", "No"); + } + private static void confirmCellNameMatch(String rawSheetName, boolean expected) { assertEquals(expected, SheetNameFormatter.nameLooksLikePlainCellReference(rawSheetName)); } 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 755106194..fe37a3c84 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/TestFormulaBugs.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java index 45b0f3a65..1b97905ed 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java @@ -146,7 +146,7 @@ public final class TestFormulaBugs extends TestCase { throw new AssertionFailedError("Identified bug 42448"); } - assertEquals("SUMPRODUCT(A!C7:C67,B8:B68)/B69", cell.getCellFormula()); + assertEquals("SUMPRODUCT(A!C7:A!C67,B8:B68)/B69", cell.getCellFormula()); // might as well evaluate the sucker... 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 000000000..e98517a73 --- /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/hssf/usermodel/TestFormulas.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java index 397c1e52c..03f47d681 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java @@ -18,7 +18,6 @@ package org.apache.poi.hssf.usermodel; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Date; @@ -42,12 +41,8 @@ public final class TestFormulas extends TestCase { /** * Add 1+1 -- WHoohoo! */ + public void testBasicAddIntegers() { - public void testBasicAddIntegers() - throws Exception { - - File file = TempFile.createTempFile("testFormula",".xls"); - FileOutputStream out = new FileOutputStream(file); HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet(); HSSFRow r = null; @@ -58,95 +53,78 @@ public final class TestFormulas extends TestCase { c = r.createCell(1); c.setCellFormula(1 + "+" + 1); - wb.write(out); - out.close(); - - FileInputStream in = new FileInputStream(file); - wb = new HSSFWorkbook(in); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); s = wb.getSheetAt(0); r = s.getRow(1); c = r.getCell(1); assertTrue("Formula is as expected",("1+1".equals(c.getCellFormula()))); - in.close(); } /** * Add various integers */ - - public void testAddIntegers() - throws Exception { + public void testAddIntegers() { binomialOperator("+"); } /** * Multiply various integers */ - - public void testMultplyIntegers() - throws Exception { + public void testMultplyIntegers() { binomialOperator("*"); } /** * Subtract various integers */ - public void testSubtractIntegers() - throws Exception { + public void testSubtractIntegers() { binomialOperator("-"); } /** * Subtract various integers */ - public void testDivideIntegers() - throws Exception { + public void testDivideIntegers() { binomialOperator("/"); } /** * Exponentialize various integers; */ - public void testPowerIntegers() - throws Exception { + public void testPowerIntegers() { binomialOperator("^"); } /** - * Concatinate two numbers 1&2 = 12 + * Concatenate two numbers 1&2 = 12 */ - public void testConcatIntegers() - throws Exception { + public void testConcatIntegers() { binomialOperator("&"); } /** * tests 1*2+3*4 */ - public void testOrderOfOperationsMultiply() - throws Exception { + public void testOrderOfOperationsMultiply() { orderTest("1*2+3*4"); } /** * tests 1*2+3^4 */ - public void testOrderOfOperationsPower() - throws Exception { + public void testOrderOfOperationsPower() { orderTest("1*2+3^4"); } /** * Tests that parenthesis are obeyed */ - public void testParenthesis() - throws Exception { + public void testParenthesis() { orderTest("(1*3)+2+(1+2)*(3^4)^5"); } - public void testReferencesOpr() - throws Exception { + public void testReferencesOpr() { String[] operation = new String[] { "+", "-", "*", "/", "^", "&" }; @@ -159,16 +137,12 @@ public final class TestFormulas extends TestCase { * Tests creating a file with floating point in a formula. * */ - public void testFloat() - throws Exception { + public void testFloat() { floatTest("*"); floatTest("/"); } - private void floatTest(String operator) - throws Exception { - File file = TempFile.createTempFile("testFormulaFloat",".xls"); - FileOutputStream out = new FileOutputStream(file); + private static void floatTest(String operator) { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet(); HSSFRow r = null; @@ -180,10 +154,10 @@ public final class TestFormulas extends TestCase { c = r.createCell(1); c.setCellFormula(""+Float.MIN_VALUE + operator + Float.MIN_VALUE); - for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2) ) { + for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2) ) { r = s.createRow(x); - for (short y = 1; y < 256 && y > 0; y= (short) (y +2)) { + for (int y = 1; y < 256 && y > 0; y= (short) (y +2)) { c = r.createCell(y); c.setCellFormula("" + x+"."+y + operator + y +"."+x); @@ -196,69 +170,48 @@ public final class TestFormulas extends TestCase { c = r.createCell(0); c.setCellFormula("" + Float.MAX_VALUE + operator + Float.MAX_VALUE); } - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); - out=null;wb=null; //otherwise we get out of memory error! - floatVerify(operator,file); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + floatVerify(operator, wb); } - private void floatVerify(String operator, File file) - throws Exception { + private static void floatVerify(String operator, HSSFWorkbook wb) { - FileInputStream in = new FileInputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(in); HSSFSheet s = wb.getSheetAt(0); - HSSFRow r = null; - HSSFCell c = null; - // dont know how to check correct result .. for the moment, we just verify that the file can be read. + // don't know how to check correct result .. for the moment, we just verify that the file can be read. - for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { - r = s.getRow(x); + for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { + HSSFRow r = s.getRow(x); - for (short y = 1; y < 256 && y > 0; y=(short)(y+2)) { + for (int y = 1; y < 256 && y > 0; y=(short)(y+2)) { - c = r.getCell(y); + HSSFCell c = r.getCell(y); assertTrue("got a formula",c.getCellFormula()!=null); assertTrue("loop Formula is as expected "+x+"."+y+operator+y+"."+x+"!="+c.getCellFormula(),( (""+x+"."+y+operator+y+"."+x).equals(c.getCellFormula()) )); - } } - - in.close(); - assertTrue("file exists",file.exists()); } - public void testAreaSum() - throws Exception { + public void testAreaSum() { areaFunctionTest("SUM"); } - public void testAreaAverage() - throws Exception { + public void testAreaAverage() { areaFunctionTest("AVERAGE"); } - public void testRefArraySum() - throws Exception { + public void testRefArraySum() { refArrayFunctionTest("SUM"); } - public void testAreaArraySum() - throws Exception { + public void testAreaArraySum() { refAreaArrayFunctionTest("SUM"); } - - - private void operationRefTest(String operator) - throws Exception { - File file = TempFile.createTempFile("testFormula",".xls"); - FileOutputStream out = new FileOutputStream(file); + private static void operationRefTest(String operator) { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet(); HSSFRow r = null; @@ -317,21 +270,16 @@ public final class TestFormulas extends TestCase { c.setCellFormula("" + "B1" + operator + "IV255"); } - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); - operationalRefVerify(operator,file); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + operationalRefVerify(operator, wb); } /** * Opens the sheet we wrote out by binomialOperator and makes sure the formulas * all match what we expect (x operator y) */ - private void operationalRefVerify(String operator, File file) - throws Exception { + private static void operationalRefVerify(String operator, HSSFWorkbook wb) { - FileInputStream in = new FileInputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(in); HSSFSheet s = wb.getSheetAt(0); HSSFRow r = null; HSSFCell c = null; @@ -345,46 +293,43 @@ public final class TestFormulas extends TestCase { )); - for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { + for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { r = s.getRow(x); - for (short y = 1; y < 256 && y > 0; y++) { + for (int y = 1; y < 256 && y > 0; y++) { - String ref=null; - String ref2=null; - short refx1=0; - short refy1=0; - short refx2=0; - short refy2=0; + int refx1; + int refy1; + int refx2; + int refy2; if (x +50 < Short.MAX_VALUE) { - refx1=(short)(x+50); - refx2=(short)(x+46); + refx1=x+50; + refx2=x+46; } else { - refx1=(short)(x-4); - refx2=(short)(x-3); + refx1=x-4; + refx2=x-3; } if (y+50 < 255) { - refy1=(short)(y+50); - refy2=(short)(y+49); + refy1=y+50; + refy2=y+49; } else { - refy1=(short)(y-4); - refy2=(short)(y-3); + refy1=y-4; + refy2=y-3; } c = r.getCell(y); CellReference cr= new CellReference(refx1, refy1, false, false); + String ref=cr.formatAsString(); ref=cr.formatAsString(); cr=new CellReference(refx2,refy2, false, false); - ref2=cr.formatAsString(); + String ref2=cr.formatAsString(); assertTrue("loop Formula is as expected "+ref+operator+ref2+"!="+c.getCellFormula(),( (""+ref+operator+ref2).equals(c.getCellFormula()) ) ); - - } } @@ -392,13 +337,7 @@ public final class TestFormulas extends TestCase { r = s.getRow(0); c = r.getCell(0); - assertTrue("maxval Formula is as expected",( - ("B1"+operator+"IV255").equals(c.getCellFormula()) - ) - ); - - in.close(); - assertTrue("file exists",file.exists()); + assertEquals("B1"+operator+"IV255", c.getCellFormula()); } @@ -406,10 +345,7 @@ public final class TestFormulas extends TestCase { /** * tests order wrting out == order writing in for a given formula */ - private void orderTest(String formula) - throws Exception { - File file = TempFile.createTempFile("testFormula",".xls"); - FileOutputStream out = new FileOutputStream(file); + private static void orderTest(String formula) { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet(); HSSFRow r = null; @@ -420,12 +356,7 @@ public final class TestFormulas extends TestCase { c = r.createCell(1); c.setCellFormula(formula); - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); - - FileInputStream in = new FileInputStream(file); - wb = new HSSFWorkbook(in); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); s = wb.getSheetAt(0); //get our minimum values @@ -434,8 +365,6 @@ public final class TestFormulas extends TestCase { assertTrue("minval Formula is as expected", formula.equals(c.getCellFormula()) ); - - in.close(); } /** @@ -443,10 +372,7 @@ public final class TestFormulas extends TestCase { * huge set of x operator y formulas. Next we call binomialVerify and verify * that they are all how we expect. */ - private void binomialOperator(String operator) - throws Exception { - File file = TempFile.createTempFile("testFormula",".xls"); - FileOutputStream out = new FileOutputStream(file); + private static void binomialOperator(String operator) { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet(); HSSFRow r = null; @@ -474,23 +400,15 @@ public final class TestFormulas extends TestCase { c = r.createCell(0); c.setCellFormula("" + Short.MAX_VALUE + operator + Short.MAX_VALUE); } - - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); - - binomialVerify(operator,file); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + binomialVerify(operator, wb); } /** * Opens the sheet we wrote out by binomialOperator and makes sure the formulas * all match what we expect (x operator y) */ - private void binomialVerify(String operator, File file) - throws Exception { - - FileInputStream in = new FileInputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(in); + private static void binomialVerify(String operator, HSSFWorkbook wb) { HSSFSheet s = wb.getSheetAt(0); HSSFRow r = null; HSSFCell c = null; @@ -502,10 +420,10 @@ public final class TestFormulas extends TestCase { ( ("1"+operator+"1").equals(c.getCellFormula()) )); - for (short x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { + for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { r = s.getRow(x); - for (short y = 1; y < 256 && y > 0; y++) { + for (int y = 1; y < 256 && y > 0; y++) { c = r.getCell(y); @@ -513,8 +431,6 @@ public final class TestFormulas extends TestCase { (""+x+operator+y).equals(c.getCellFormula()) ) ); - - } } @@ -522,90 +438,62 @@ public final class TestFormulas extends TestCase { r = s.getRow(0); c = r.getCell(0); - assertTrue("maxval Formula is as expected",( (""+Short.MAX_VALUE+operator+Short.MAX_VALUE).equals(c.getCellFormula()) ) ); - - in.close(); - assertTrue("file exists",file.exists()); - } - - - - /** - * Writes a function then tests to see if its correct - * - */ - public void areaFunctionTest(String function) - throws Exception { - - File file = TempFile.createTempFile("testFormulaAreaFunction"+function,".xls"); - FileOutputStream out = new FileOutputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - HSSFRow r = null; - HSSFCell c = null; - - - r = s.createRow(0); - - c = r.createCell(0); - c.setCellFormula(function+"(A2:A3)"); - - - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); - - FileInputStream in = new FileInputStream(file); - wb = new HSSFWorkbook(in); - s = wb.getSheetAt(0); - r = s.getRow(0); - c = r.getCell(0); - - assertTrue("function ="+function+"(A2:A3)", - ( (function+"(A2:A3)").equals((function+"(A2:A3)")) ) - ); - in.close(); } /** * Writes a function then tests to see if its correct - * */ - public void refArrayFunctionTest(String function) - throws Exception { + public static void areaFunctionTest(String function) { - File file = TempFile.createTempFile("testFormulaArrayFunction"+function,".xls"); - FileOutputStream out = new FileOutputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - HSSFRow r = null; - HSSFCell c = null; + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + HSSFRow r = null; + HSSFCell c = null; - r = s.createRow(0); + r = s.createRow(0); - c = r.createCell(0); - c.setCellFormula(function+"(A2,A3)"); + c = r.createCell(0); + c.setCellFormula(function+"(A2:A3)"); + + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + s = wb.getSheetAt(0); + r = s.getRow(0); + c = r.getCell(0); + + assertTrue("function ="+function+"(A2:A3)", + ( (function+"(A2:A3)").equals((function+"(A2:A3)")) ) + ); + } + + /** + * Writes a function then tests to see if its correct + */ + public void refArrayFunctionTest(String function) { + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + HSSFRow r = null; + HSSFCell c = null; - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); + r = s.createRow(0); - FileInputStream in = new FileInputStream(file); - wb = new HSSFWorkbook(in); - s = wb.getSheetAt(0); - r = s.getRow(0); - c = r.getCell(0); + c = r.createCell(0); + c.setCellFormula(function+"(A2,A3)"); - assertTrue("function ="+function+"(A2,A3)", - ( (function+"(A2,A3)").equals(c.getCellFormula()) ) - ); - in.close(); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + s = wb.getSheetAt(0); + r = s.getRow(0); + c = r.getCell(0); + + assertTrue("function ="+function+"(A2,A3)", + ( (function+"(A2,A3)").equals(c.getCellFormula()) ) + ); } @@ -613,294 +501,224 @@ public final class TestFormulas extends TestCase { * Writes a function then tests to see if its correct * */ - public void refAreaArrayFunctionTest(String function) - throws Exception { + public void refAreaArrayFunctionTest(String function) { - File file = TempFile.createTempFile("testFormulaAreaArrayFunction"+function,".xls"); - FileOutputStream out = new FileOutputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - HSSFRow r = null; - HSSFCell c = null; + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + HSSFRow r = null; + HSSFCell c = null; - r = s.createRow(0); + r = s.createRow(0); - c = r.createCell(0); - c.setCellFormula(function+"(A2:A4,B2:B4)"); - c=r.createCell(1); - c.setCellFormula(function+"($A$2:$A4,B$2:B4)"); + c = r.createCell(0); + c.setCellFormula(function+"(A2:A4,B2:B4)"); + c=r.createCell(1); + c.setCellFormula(function+"($A$2:$A4,B$2:B4)"); - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + s = wb.getSheetAt(0); + r = s.getRow(0); + c = r.getCell(0); - FileInputStream in = new FileInputStream(file); - wb = new HSSFWorkbook(in); - s = wb.getSheetAt(0); - r = s.getRow(0); - c = r.getCell(0); + assertTrue("function ="+function+"(A2:A4,B2:B4)", + ( (function+"(A2:A4,B2:B4)").equals(c.getCellFormula()) ) + ); - assertTrue("function ="+function+"(A2:A4,B2:B4)", - ( (function+"(A2:A4,B2:B4)").equals(c.getCellFormula()) ) - ); - - c=r.getCell(1); - assertTrue("function ="+function+"($A$2:$A4,B$2:B4)", - ( (function+"($A$2:$A4,B$2:B4)").equals(c.getCellFormula()) ) - ); - in.close(); + c=r.getCell(1); + assertTrue("function ="+function+"($A$2:$A4,B$2:B4)", + ( (function+"($A$2:$A4,B$2:B4)").equals(c.getCellFormula()) ) + ); } - public void testAbsRefs() throws Exception { - File file = TempFile.createTempFile("testFormulaAbsRef",".xls"); - FileOutputStream out = new FileOutputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - HSSFRow r = null; - HSSFCell c = null; + public void testAbsRefs() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + HSSFRow r; + HSSFCell c; + r = s.createRow(0); + c = r.createCell(0); + c.setCellFormula("A3+A2"); + c=r.createCell(1); + c.setCellFormula("$A3+$A2"); + c=r.createCell(2); + c.setCellFormula("A$3+A$2"); + c=r.createCell(3); + c.setCellFormula("$A$3+$A$2"); + c=r.createCell(4); + c.setCellFormula("SUM($A$3,$A$2)"); - r = s.createRow(0); - - c = r.createCell(0); - c.setCellFormula("A3+A2"); - c=r.createCell(1); - c.setCellFormula("$A3+$A2"); - c=r.createCell(2); - c.setCellFormula("A$3+A$2"); - c=r.createCell(3); - c.setCellFormula("$A$3+$A$2"); - c=r.createCell(4); - c.setCellFormula("SUM($A$3,$A$2)"); - - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); - - FileInputStream in = new FileInputStream(file); - wb = new HSSFWorkbook(in); - s = wb.getSheetAt(0); - r = s.getRow(0); - c = r.getCell(0); - assertTrue("A3+A2", ("A3+A2").equals(c.getCellFormula())); - c = r.getCell(1); - assertTrue("$A3+$A2", ("$A3+$A2").equals(c.getCellFormula())); - c = r.getCell(2); - assertTrue("A$3+A$2", ("A$3+A$2").equals(c.getCellFormula())); - c = r.getCell(3); - assertTrue("$A$3+$A$2", ("$A$3+$A$2").equals(c.getCellFormula())); - c = r.getCell(4); - assertTrue("SUM($A$3,$A$2)", ("SUM($A$3,$A$2)").equals(c.getCellFormula())); - in.close(); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); + s = wb.getSheetAt(0); + r = s.getRow(0); + c = r.getCell(0); + assertTrue("A3+A2", ("A3+A2").equals(c.getCellFormula())); + c = r.getCell(1); + assertTrue("$A3+$A2", ("$A3+$A2").equals(c.getCellFormula())); + c = r.getCell(2); + assertTrue("A$3+A$2", ("A$3+A$2").equals(c.getCellFormula())); + c = r.getCell(3); + assertTrue("$A$3+$A$2", ("$A$3+$A$2").equals(c.getCellFormula())); + c = r.getCell(4); + assertTrue("SUM($A$3,$A$2)", ("SUM($A$3,$A$2)").equals(c.getCellFormula())); } - public void testSheetFunctions() - throws IOException - { - File file = TempFile.createTempFile("testSheetFormula",".xls"); - FileOutputStream out = new FileOutputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet("A"); - HSSFRow r = null; - HSSFCell c = null; - r = s.createRow(0); - c = r.createCell(0);c.setCellValue(1); - c = r.createCell(1);c.setCellValue(2); + public void testSheetFunctions() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet("A"); + HSSFRow r = null; + HSSFCell c = null; + r = s.createRow(0); + c = r.createCell(0);c.setCellValue(1); + c = r.createCell(1);c.setCellValue(2); - s = wb.createSheet("B"); - r = s.createRow(0); - c=r.createCell(0); c.setCellFormula("AVERAGE(A!A1:B1)"); - c=r.createCell(1); c.setCellFormula("A!A1+A!B1"); - c=r.createCell(2); c.setCellFormula("A!$A$1+A!$B1"); - wb.write(out); - out.close(); + s = wb.createSheet("B"); + r = s.createRow(0); + c=r.createCell(0); c.setCellFormula("AVERAGE(A!A1:B1)"); + c=r.createCell(1); c.setCellFormula("A!A1+A!B1"); + c=r.createCell(2); c.setCellFormula("A!$A$1+A!$B1"); - assertTrue("file exists",file.exists()); + wb = HSSFTestDataSamples.writeOutAndReadBack(wb); - FileInputStream in = new FileInputStream(file); - wb = new HSSFWorkbook(in); - s = wb.getSheet("B"); - r = s.getRow(0); - c = r.getCell(0); - assertTrue("expected: AVERAGE(A!A1:B1) got: "+c.getCellFormula(), ("AVERAGE(A!A1:B1)").equals(c.getCellFormula())); - c = r.getCell(1); - assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula())); - in.close(); + s = wb.getSheet("B"); + r = s.getRow(0); + c = r.getCell(0); + assertTrue("expected: AVERAGE(A!A1:B1) got: "+c.getCellFormula(), ("AVERAGE(A!A1:B1)").equals(c.getCellFormula())); + c = r.getCell(1); + assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula())); } public void testRVAoperands() throws Exception { - File file = TempFile.createTempFile("testFormulaRVA",".xls"); - FileOutputStream out = new FileOutputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - HSSFRow r = null; - HSSFCell c = null; + File file = TempFile.createTempFile("testFormulaRVA",".xls"); + FileOutputStream out = new FileOutputStream(file); + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet(); + HSSFRow r = null; + HSSFCell c = null; - r = s.createRow(0); + r = s.createRow(0); - c = r.createCell(0); - c.setCellFormula("A3+A2"); - c=r.createCell(1); - c.setCellFormula("AVERAGE(A3,A2)"); - c=r.createCell(2); - c.setCellFormula("ROW(A3)"); - c=r.createCell(3); - c.setCellFormula("AVERAGE(A2:A3)"); - c=r.createCell(4); - c.setCellFormula("POWER(A2,A3)"); - c=r.createCell(5); - c.setCellFormula("SIN(A2)"); + c = r.createCell(0); + c.setCellFormula("A3+A2"); + c=r.createCell(1); + c.setCellFormula("AVERAGE(A3,A2)"); + c=r.createCell(2); + c.setCellFormula("ROW(A3)"); + c=r.createCell(3); + c.setCellFormula("AVERAGE(A2:A3)"); + c=r.createCell(4); + c.setCellFormula("POWER(A2,A3)"); + c=r.createCell(5); + c.setCellFormula("SIN(A2)"); - c=r.createCell(6); - c.setCellFormula("SUM(A2:A3)"); + c=r.createCell(6); + c.setCellFormula("SUM(A2:A3)"); - c=r.createCell(7); - c.setCellFormula("SUM(A2,A3)"); + c=r.createCell(7); + c.setCellFormula("SUM(A2,A3)"); - r = s.createRow(1);c=r.createCell(0); c.setCellValue(2.0); - r = s.createRow(2);c=r.createCell(0); c.setCellValue(3.0); + r = s.createRow(1);c=r.createCell(0); c.setCellValue(2.0); + r = s.createRow(2);c=r.createCell(0); c.setCellValue(3.0); - wb.write(out); - out.close(); - assertTrue("file exists",file.exists()); + wb.write(out); + out.close(); + assertTrue("file exists",file.exists()); } - public void testStringFormulas() - throws IOException - { - File file = TempFile.createTempFile("testStringFormula",".xls"); - FileOutputStream out = new FileOutputStream(file); - HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet("A"); - HSSFRow r = null; - HSSFCell c = null; - r = s.createRow(0); - c=r.createCell(1); c.setCellFormula("UPPER(\"abc\")"); - c=r.createCell(2); c.setCellFormula("LOWER(\"ABC\")"); - c=r.createCell(3); c.setCellFormula("CONCATENATE(\" my \",\" name \")"); + public void testStringFormulas() { + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet("A"); + HSSFRow r = null; + HSSFCell c = null; + r = s.createRow(0); + c=r.createCell(1); c.setCellFormula("UPPER(\"abc\")"); + c=r.createCell(2); c.setCellFormula("LOWER(\"ABC\")"); + c=r.createCell(3); c.setCellFormula("CONCATENATE(\" my \",\" name \")"); - wb.write(out); - out.close(); + HSSFTestDataSamples.writeOutAndReadBack(wb); - wb = openSample("StringFormulas.xls"); - s = wb.getSheetAt(0); - r = s.getRow(0); - c = r.getCell(0); - assertTrue("expected: UPPER(\"xyz\") got "+c.getCellFormula(), ("UPPER(\"xyz\")").equals(c.getCellFormula())); - //c = r.getCell((short)1); - //assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula())); + wb = openSample("StringFormulas.xls"); + s = wb.getSheetAt(0); + r = s.getRow(0); + c = r.getCell(0); + assertEquals("UPPER(\"xyz\")", c.getCellFormula()); + } + + public void testLogicalFormulas() { + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet s = wb.createSheet("A"); + HSSFRow r = null; + HSSFCell c = null; + r = s.createRow(0); + c=r.createCell(1); c.setCellFormula("IF(A10)); - - File nestedIf = TempFile.createTempFile("testNestedIfFormula",".xls"); - out = new FileOutputStream(nestedIf); wb = new HSSFWorkbook(); s = wb.createSheet("testSheet1"); r = null; @@ -939,17 +751,10 @@ public final class TestFormulas extends TestCase { formulaCell.setCellFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))"); - - wb.write(out); - out.close(); - assertTrue("file exists", nestedIf.exists()); - - assertTrue("length of nestedIf file is zero", (nestedIf.length()>0)); + HSSFTestDataSamples.writeOutAndReadBack(wb); } - public void testSumIf() - throws IOException - { + public void testSumIf() { String function ="SUMIF(A1:A5,\">4000\",B1:B5)"; HSSFWorkbook wb = openSample("sumifformula.xls"); @@ -960,40 +765,34 @@ public final class TestFormulas extends TestCase { assertEquals(function, c.getCellFormula()); - File file = TempFile.createTempFile("testSumIfFormula",".xls"); - FileOutputStream out = new FileOutputStream(file); wb = new HSSFWorkbook(); s = wb.createSheet(); r = s.createRow(0); - c=r.createCell(0); c.setCellValue((double)1000); - c=r.createCell(1); c.setCellValue((double)1); + c=r.createCell(0); c.setCellValue(1000); + c=r.createCell(1); c.setCellValue(1); r = s.createRow(1); - c=r.createCell(0); c.setCellValue((double)2000); - c=r.createCell(1); c.setCellValue((double)2); + c=r.createCell(0); c.setCellValue(2000); + c=r.createCell(1); c.setCellValue(2); r = s.createRow(2); - c=r.createCell(0); c.setCellValue((double)3000); - c=r.createCell(1); c.setCellValue((double)3); + c=r.createCell(0); c.setCellValue(3000); + c=r.createCell(1); c.setCellValue(3); r = s.createRow(3); - c=r.createCell(0); c.setCellValue((double)4000); - c=r.createCell(1); c.setCellValue((double)4); + c=r.createCell(0); c.setCellValue(4000); + c=r.createCell(1); c.setCellValue(4); r = s.createRow(4); - c=r.createCell(0); c.setCellValue((double)5000); - c=r.createCell(1); c.setCellValue((double)5); + c=r.createCell(0); c.setCellValue(5000); + c=r.createCell(1); c.setCellValue(5); r = s.getRow(0); c=r.createCell(2); c.setCellFormula(function); - wb.write(out); - out.close(); - - assertTrue("sumif file doesnt exists", (file.exists())); - assertTrue("sumif == 0 bytes", file.length() > 0); + HSSFTestDataSamples.writeOutAndReadBack(wb); } public void testSquareMacro() { @@ -1064,7 +863,7 @@ public final class TestFormulas extends TestCase { /** Unknown Ptg 3D*/ public void test27272_2() throws Exception { HSSFWorkbook wb = openSample("27272_2.xls"); - assertEquals("Reference for named range ", "'LOAD.POD_HISTORIES'!#REF!",wb.getNameAt(0).getReference()); + assertEquals("Reference for named range ", "LOAD.POD_HISTORIES!#REF!",wb.getNameAt(0).getReference()); File outF = File.createTempFile("bug27272_2",".xls"); wb.write(new FileOutputStream(outF)); System.out.println("Open "+outF.getAbsolutePath()+" in Excel"); @@ -1085,8 +884,4 @@ public final class TestFormulas extends TestCase { assertEquals("DZ2*2", wb.getSheetAt(0).getRow(1).getCell(128).toString()); assertEquals("B32770*2", wb.getSheetAt(0).getRow(32768).getCell(1).toString()); } - - public static void main(String [] args) { - junit.textui.TestRunner.run(TestFormulas.class); - } } 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 000000000..dd1360f76 --- /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 000000000..0d3e93ee7 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java @@ -0,0 +1,154 @@ +/* ==================================================================== + 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.HSSFTestDataSamples; +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; +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; + +/** + * 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); + } + + + public void testEvaluateMultipleWorkbooks() { + HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls"); + HSSFWorkbook wbB = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaB.xls"); + + HSSFFormulaEvaluator evaluatorA = new HSSFFormulaEvaluator(wbA); + HSSFFormulaEvaluator evaluatorB = new HSSFFormulaEvaluator(wbB); + + // Hook up the workbook evaluators to enable evaluation of formulas across books + String[] bookNames = { "multibookFormulaA.xls", "multibookFormulaB.xls", }; + HSSFFormulaEvaluator[] evaluators = { evaluatorA, evaluatorB, }; + HSSFFormulaEvaluator.setupEnvironment(bookNames, evaluators); + + HSSFCell cell; + + HSSFSheet aSheet1 = wbA.getSheetAt(0); + HSSFSheet bSheet1 = wbB.getSheetAt(0); + + // Simple case - single link from wbA to wbB + confirmFormula(wbA, 0, 0, 0, "[multibookFormulaB.xls]BSheet1!B1"); + cell = aSheet1.getRow(0).getCell(0); + confirmEvaluation(35, evaluatorA, cell); + + + // more complex case - back link into wbA + // [wbA]ASheet1!A2 references (among other things) [wbB]BSheet1!B2 + confirmFormula(wbA, 0, 1, 0, "[multibookFormulaB.xls]BSheet1!$B$2+2*A3"); + // [wbB]BSheet1!B2 references (among other things) [wbA]AnotherSheet!A1:B2 + confirmFormula(wbB, 0, 1, 1, "SUM([multibookFormulaA.xls]AnotherSheet!$A$1:$B$2)+B3"); + + cell = aSheet1.getRow(1).getCell(0); + confirmEvaluation(264, evaluatorA, cell); + + // change [wbB]BSheet1!B3 (from 50 to 60) + bSheet1.getRow(2).getCell(1).setCellValue(60); + evaluatorB.setCachedPlainValue(bSheet1, 2, 1, new NumberEval(60)); + confirmEvaluation(274, evaluatorA, cell); + + // change [wbA]ASheet1!A3 (from 100 to 80) + aSheet1.getRow(2).getCell(0).setCellValue(80); + evaluatorA.setCachedPlainValue(aSheet1, 2, 0, new NumberEval(80)); + confirmEvaluation(234, evaluatorA, cell); + + // change [wbA]AnotherSheet!A1 (from 2 to 3) + wbA.getSheetAt(1).getRow(0).getCell(0).setCellValue(3); + evaluatorA.setCachedPlainValue(wbA.getSheetAt(1), 0, 0, new NumberEval(3)); + confirmEvaluation(235, evaluatorA, cell); + } + + private static void confirmEvaluation(double expectedValue, HSSFFormulaEvaluator fe, HSSFCell cell) { + assertEquals(expectedValue, fe.evaluate(cell).getNumberValue(), 0.0); + } + + private static void confirmFormula(HSSFWorkbook wb, int sheetIndex, int rowIndex, int columnIndex, + String expectedFormula) { + HSSFCell cell = wb.getSheetAt(sheetIndex).getRow(rowIndex).getCell(columnIndex); + assertEquals(expectedFormula, cell.getCellFormula()); + } +}