<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
<action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
<action dev="POI-DEVELOPERS" type="add">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
<action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
<action dev="POI-DEVELOPERS" type="add">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SupBookRecord;
+import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.formula.NameXPtg;
/**
temp.toArray(_crnBlocks);
}
- public ExternalBookBlock(short numberOfSheets) {
- _externalBookRecord = SupBookRecord.createInternalReferences(numberOfSheets);
+ public ExternalBookBlock(int numberOfSheets) {
+ _externalBookRecord = SupBookRecord.createInternalReferences((short)numberOfSheets);
_externalNameRecords = new ExternalNameRecord[0];
_crnBlocks = new CRNBlock[0];
}
return ExternSheetRecord.combine(esrs);
}
- public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) {
+ public LinkTable(int numberOfSheets, WorkbookRecordList workbookRecordList) {
_workbookRecordList = workbookRecordList;
_definedNames = new ArrayList();
_externalBookBlocks = new ExternalBookBlock[] {
return lastName.getSheetNumber() == firstName.getSheetNumber();
}
-
- public int getIndexToSheet(int extRefIndex) {
+ public String[] getExternalBookAndSheetName(int extRefIndex) {
+ int ebIx = _externSheetRecord.getExtbookIndexFromRefIndex(extRefIndex);
+ SupBookRecord ebr = _externalBookBlocks[ebIx].getExternalBookRecord();
+ if (!ebr.isExternalReferences()) {
+ return null;
+ }
+ int shIx = _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
+ UnicodeString usSheetName = ebr.getSheetNames()[shIx];
+ return new String[] {
+ ebr.getURL(),
+ usSheetName.getString(),
+ };
+ }
+
+ public int getExternalSheetIndex(String workbookName, String sheetName) {
+ SupBookRecord ebrTarget = null;
+ int externalBookIndex = -1;
+ for (int i=0; i<_externalBookBlocks.length; i++) {
+ SupBookRecord ebr = _externalBookBlocks[i].getExternalBookRecord();
+ if (!ebr.isExternalReferences()) {
+ continue;
+ }
+ if (workbookName.equals(ebr.getURL())) { // not sure if 'equals()' works when url has a directory
+ ebrTarget = ebr;
+ externalBookIndex = i;
+ break;
+ }
+ }
+ if (ebrTarget == null) {
+ throw new RuntimeException("No external workbook with name '" + workbookName + "'");
+ }
+ int sheetIndex = getSheetIndex(ebrTarget.getSheetNames(), sheetName);
+
+ int result = _externSheetRecord.getRefIxForSheet(externalBookIndex, sheetIndex);
+ if (result < 0) {
+ throw new RuntimeException("ExternSheetRecord does not contain combination ("
+ + externalBookIndex + ", " + sheetIndex + ")");
+ }
+ return result;
+ }
+
+ private static int getSheetIndex(UnicodeString[] sheetNames, String sheetName) {
+ for (int i = 0; i < sheetNames.length; i++) {
+ if (sheetNames[i].getString().equals(sheetName)) {
+ return i;
+ }
+
+ }
+ throw new RuntimeException("External workbook does not contain sheet '" + sheetName + "'");
+ }
+
+ /**
+ * @param extRefIndex as from a {@link Ref3DPtg} or {@link Area3DPtg}
+ * @return -1 if the reference is to an external book
+ */
+ public int getIndexToInternalSheet(int extRefIndex) {
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
}
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
}
- public int addSheetIndexToExternSheet(int sheetNumber) {
- // TODO - what about the first parameter (extBookIndex)?
- return _externSheetRecord.addRef(0, sheetNumber, sheetNumber);
- }
-
- public short checkExternSheet(int sheetIndex) {
+ public int checkExternSheet(int sheetIndex) {
+ int thisWbIndex = -1; // this is probably always zero
+ for (int i=0; i<_externalBookBlocks.length; i++) {
+ SupBookRecord ebr = _externalBookBlocks[i].getExternalBookRecord();
+ if (ebr.isInternalReferences()) {
+ thisWbIndex = i;
+ break;
+ }
+ }
+ if (thisWbIndex < 0) {
+ throw new RuntimeException("Could not find 'internal references' EXTERNALBOOK");
+ }
//Trying to find reference to this sheet
- int i = _externSheetRecord.getRefIxForSheet(sheetIndex);
+ int i = _externSheetRecord.getRefIxForSheet(thisWbIndex, sheetIndex);
if (i>=0) {
- return (short)i;
+ return i;
}
- //We Haven't found reference to this sheet
- return (short)addSheetIndexToExternSheet((short) sheetIndex);
+ //We haven't found reference to this sheet
+ return _externSheetRecord.addRef(thisWbIndex, sheetIndex, sheetIndex);
}
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
for ( int k = 0; k < nBoundSheets; k++ ) {
BoundSheetRecord bsr = retval.createBoundSheet(k);
- records.add(bsr);
- retval.boundsheets.add(bsr);
- retval.records.setBspos(records.size() - 1);
+ records.add(bsr);
+ retval.boundsheets.add(bsr);
+ retval.records.setBspos(records.size() - 1);
}
// retval.records.supbookpos = retval.records.bspos + 1;
// retval.records.namepos = retval.records.supbookpos + 2;
* @param hidden 0 for not hidden, 1 for hidden, 2 for very hidden
*/
public void setSheetHidden(int sheetnum, int hidden) {
- BoundSheetRecord bsr = getBoundSheetRec(sheetnum);
- boolean h = false;
- boolean vh = false;
- if(hidden == 0) {
- } else if(hidden == 1) {
- h = true;
- } else if(hidden == 2) {
- vh = true;
- } else {
- throw new IllegalArgumentException("Invalid hidden flag " + hidden + " given, must be 0, 1 or 2");
- }
- bsr.setHidden(h);
- bsr.setVeryHidden(vh);
+ BoundSheetRecord bsr = getBoundSheetRec(sheetnum);
+ boolean h = false;
+ boolean vh = false;
+ if(hidden == 0) {
+ } else if(hidden == 1) {
+ h = true;
+ } else if(hidden == 2) {
+ vh = true;
+ } else {
+ throw new IllegalArgumentException("Invalid hidden flag " + hidden + " given, must be 0, 1 or 2");
+ }
+ bsr.setHidden(h);
+ bsr.setVeryHidden(vh);
}
* have a Style set.
*/
public StyleRecord getStyleRecord(int xfIndex) {
- // Style records always follow after
- // the ExtendedFormat records
- boolean done = false;
- for(int i=records.getXfpos(); i<records.size() &&
- !done; i++) {
- Record r = records.get(i);
- if(r instanceof ExtendedFormatRecord) {
- } else if(r instanceof StyleRecord) {
- StyleRecord sr = (StyleRecord)r;
- if(sr.getIndex() == xfIndex) {
- return sr;
- }
- } else {
- done = true;
- }
- }
- return null;
+ // Style records always follow after
+ // the ExtendedFormat records
+ boolean done = false;
+ for(int i=records.getXfpos(); i<records.size() &&
+ !done; i++) {
+ Record r = records.get(i);
+ if(r instanceof ExtendedFormatRecord) {
+ } else if(r instanceof StyleRecord) {
+ StyleRecord sr = (StyleRecord)r;
+ if(sr.getIndex() == xfIndex) {
+ return sr;
+ }
+ } else {
+ done = true;
+ }
+ }
+ return null;
}
/**
* Creates a new StyleRecord, for the given Extended
* records collection
*/
public StyleRecord createStyleRecord(int xfIndex) {
- // Style records always follow after
- // the ExtendedFormat records
- StyleRecord newSR = new StyleRecord();
- newSR.setIndex((short)xfIndex);
-
- // Find the spot
- int addAt = -1;
- for(int i=records.getXfpos(); i<records.size() &&
- addAt == -1; i++) {
- Record r = records.get(i);
- if(r instanceof ExtendedFormatRecord ||
- r instanceof StyleRecord) {
- // Keep going
- } else {
- addAt = i;
- }
- }
- if(addAt == -1) {
- throw new IllegalStateException("No XF Records found!");
- }
- records.add(addAt, newSR);
-
- return newSR;
+ // Style records always follow after
+ // the ExtendedFormat records
+ StyleRecord newSR = new StyleRecord();
+ newSR.setIndex((short)xfIndex);
+
+ // Find the spot
+ int addAt = -1;
+ for(int i=records.getXfpos(); i<records.size() &&
+ addAt == -1; i++) {
+ Record r = records.get(i);
+ if(r instanceof ExtendedFormatRecord ||
+ r instanceof StyleRecord) {
+ // Keep going
+ } else {
+ addAt = i;
+ }
+ }
+ if(addAt == -1) {
+ throw new IllegalStateException("No XF Records found!");
+ }
+ records.add(addAt, newSR);
+
+ return newSR;
}
/**
*/
public String findSheetNameFromExternSheet(int externSheetIndex){
- int indexToSheet = linkTable.getIndexToSheet(externSheetIndex);
-
+ int indexToSheet = linkTable.getIndexToInternalSheet(externSheetIndex);
if (indexToSheet < 0) {
// TODO - what does '-1' mean here?
//error check, bail out gracefully!
}
return getSheetName(indexToSheet);
}
+ public ExternalSheet getExternalSheet(int externSheetIndex) {
+ String[] extNames = linkTable.getExternalBookAndSheetName(externSheetIndex);
+ if (extNames == null) {
+ return null;
+ }
+ return new ExternalSheet(extNames[0], extNames[1]);
+ }
/**
* Finds the sheet index for a particular external sheet number.
* @return index to extern sheet
*/
public short checkExternSheet(int sheetNumber){
- return getOrCreateLinkTable().checkExternSheet(sheetNumber);
+ return (short)getOrCreateLinkTable().checkExternSheet(sheetNumber);
}
+ public int getExternalSheetIndex(String workbookName, String sheetName) {
+ return getOrCreateLinkTable().getExternalSheetIndex(workbookName, sheetName);
+ }
+
+
/** gets the total number of names
* @return number of names
*/
return _list.size() - 1;
}
- public int getRefIxForSheet(int sheetIndex) {
+ public int getRefIxForSheet(int externalBookIndex, int sheetIndex) {
int nItems = _list.size();
for (int i = 0; i < nItems; i++) {
RefSubRecord ref = getRef(i);
+ if (ref.getExtBookIndex() != externalBookIndex) {
+ continue;
+ }
if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) {
return i;
}
{
return sid;
}
- public UnicodeString getURL() {
- return field_2_encoded_url;
+ public String getURL() {
+ String encodedUrl = field_2_encoded_url.getString();
+ switch(encodedUrl.charAt(0)) {
+ case 0: // Reference to an empty workbook name
+ return encodedUrl.substring(1); // will this just be empty string?
+ case 1: // encoded file name
+ return decodeFileName(encodedUrl);
+ case 2: // Self-referential external reference
+ return encodedUrl.substring(1);
+
+ }
+ return encodedUrl;
+ }
+ private static String decodeFileName(String encodedUrl) {
+ return encodedUrl.substring(1);
+ // TODO the following special characters may appear in the rest of the string, and need to get interpreted
+ /* see "MICROSOFT OFFICE EXCEL 97-2007 BINARY FILE FORMAT SPECIFICATION"
+ chVolume 1
+ chSameVolume 2
+ chDownDir 3
+ chUpDir 4
+ chLongVolume 5
+ chStartupDir 6
+ chAltStartupDir 7
+ chLibDir 8
+
+ */
}
public UnicodeString[] getSheetNames() {
return (UnicodeString[]) field_3_sheet_names.clone();
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
-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;
/**
* @author Jason Height (jheight at chariot dot net dot au)
* @version 1.0-pre
*/
-public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFormula {
+public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFormula, ExternSheetReferenceToken {
public final static byte sid = 0x3b;
private final static int SIZE = 11; // 10 + 1 for Ptg
return SIZE;
}
- public short getExternSheetIndex() {
- return (short)field_1_index_extern_sheet;
+ public int getExternSheetIndex() {
+ return field_1_index_extern_sheet;
}
public void setExternSheetIndex(int index) {
public OffsetArea(int baseRow, int baseColumn, int relFirstRowIx, int relLastRowIx,
int relFirstColIx, int relLastColIx) {
- _firstRow = baseRow + relFirstRowIx;
- _lastRow = baseRow + relLastRowIx;
- _firstColumn = baseColumn + relFirstColIx;
- _lastColumn = baseColumn + relLastColIx;
+ _firstRow = baseRow + Math.min(relFirstRowIx, relLastRowIx);
+ _lastRow = baseRow + Math.max(relFirstRowIx, relLastRowIx);
+ _firstColumn = baseColumn + Math.min(relFirstColIx, relLastColIx);
+ _lastColumn = baseColumn + Math.max(relFirstColIx, relLastColIx);
}
public int getFirstColumn() {
return _lastRow;
}
}
-
}
\ No newline at end of file
* @author Jason Height (jheight at chariot dot net dot au)
*/
public abstract class AreaPtgBase extends OperandPtg implements AreaI {
- /**
- * TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
- * see similar comment in ReferencePtg
- */
- protected final RuntimeException notImplemented() {
- return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
- }
+ /**
+ * TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
+ * see similar comment in ReferencePtg
+ */
+ protected final RuntimeException notImplemented() {
+ return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
+ }
- /** zero based, unsigned 16 bit */
- private int field_1_first_row;
- /** zero based, unsigned 16 bit */
- private int field_2_last_row;
- /** zero based, unsigned 8 bit */
- private int field_3_first_column;
- /** zero based, unsigned 8 bit */
- private int field_4_last_column;
-
- private final static BitField rowRelative = BitFieldFactory.getInstance(0x8000);
- private final static BitField colRelative = BitFieldFactory.getInstance(0x4000);
- private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF);
+ /** zero based, unsigned 16 bit */
+ private int field_1_first_row;
+ /** zero based, unsigned 16 bit */
+ private int field_2_last_row;
+ /** zero based, unsigned 8 bit */
+ private int field_3_first_column;
+ /** zero based, unsigned 8 bit */
+ private int field_4_last_column;
+
+ private final static BitField rowRelative = BitFieldFactory.getInstance(0x8000);
+ private final static BitField colRelative = BitFieldFactory.getInstance(0x4000);
+ private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF);
- protected AreaPtgBase() {
- // do nothing
- }
-
- protected AreaPtgBase(String arearef) {
- AreaReference ar = new AreaReference(arearef);
- CellReference firstCell = ar.getFirstCell();
- CellReference lastCell = ar.getLastCell();
- setFirstRow(firstCell.getRow());
- setFirstColumn(firstCell.getCol());
- setLastRow(lastCell.getRow());
- setLastColumn(lastCell.getCol());
- setFirstColRelative(!firstCell.isColAbsolute());
- setLastColRelative(!lastCell.isColAbsolute());
- setFirstRowRelative(!firstCell.isRowAbsolute());
- setLastRowRelative(!lastCell.isRowAbsolute());
- }
-
- protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn,
- boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
-
- checkColumnBounds(firstColumn);
- checkColumnBounds(lastColumn);
- checkRowBounds(firstRow);
- checkRowBounds(lastRow);
- setFirstRow(firstRow);
- setLastRow(lastRow);
- setFirstColumn(firstColumn);
- setLastColumn(lastColumn);
- setFirstRowRelative(firstRowRelative);
- setLastRowRelative(lastRowRelative);
- setFirstColRelative(firstColRelative);
- setLastColRelative(lastColRelative);
- }
+ protected AreaPtgBase() {
+ // do nothing
+ }
+
+ protected AreaPtgBase(String arearef) {
+ AreaReference ar = new AreaReference(arearef);
+ CellReference firstCell = ar.getFirstCell();
+ CellReference lastCell = ar.getLastCell();
+ setFirstRow(firstCell.getRow());
+ setFirstColumn(firstCell.getCol());
+ setLastRow(lastCell.getRow());
+ setLastColumn(lastCell.getCol());
+ setFirstColRelative(!firstCell.isColAbsolute());
+ setLastColRelative(!lastCell.isColAbsolute());
+ setFirstRowRelative(!firstCell.isRowAbsolute());
+ setLastRowRelative(!lastCell.isRowAbsolute());
+ }
+
+ protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn,
+ boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
+
+ checkColumnBounds(firstColumn);
+ checkColumnBounds(lastColumn);
+ checkRowBounds(firstRow);
+ checkRowBounds(lastRow);
+
+ if (lastRow > firstRow) {
+ setFirstRow(firstRow);
+ setLastRow(lastRow);
+ setFirstRowRelative(firstRowRelative);
+ setLastRowRelative(lastRowRelative);
+ } else {
+ setFirstRow(lastRow);
+ setLastRow(firstRow);
+ setFirstRowRelative(lastRowRelative);
+ setLastRowRelative(firstRowRelative);
+ }
+
+ if (lastColumn > firstColumn) {
+ setFirstColumn(firstColumn);
+ setLastColumn(lastColumn);
+ setFirstColRelative(firstColRelative);
+ setLastColRelative(lastColRelative);
+ } else {
+ setFirstColumn(lastColumn);
+ setLastColumn(firstColumn);
+ setFirstColRelative(lastColRelative);
+ setLastColRelative(firstColRelative);
+ }
+ }
- private static void checkColumnBounds(int colIx) {
- if((colIx & 0x0FF) != colIx) {
- throw new IllegalArgumentException("colIx (" + colIx + ") is out of range");
- }
- }
- private static void checkRowBounds(int rowIx) {
- if((rowIx & 0x0FFFF) != rowIx) {
- throw new IllegalArgumentException("rowIx (" + rowIx + ") is out of range");
- }
- }
+ private static void checkColumnBounds(int colIx) {
+ if((colIx & 0x0FF) != colIx) {
+ throw new IllegalArgumentException("colIx (" + colIx + ") is out of range");
+ }
+ }
+ private static void checkRowBounds(int rowIx) {
+ if((rowIx & 0x0FFFF) != rowIx) {
+ throw new IllegalArgumentException("rowIx (" + rowIx + ") is out of range");
+ }
+ }
- protected final void readCoordinates(RecordInputStream in) {
- field_1_first_row = in.readUShort();
- field_2_last_row = in.readUShort();
- field_3_first_column = in.readUShort();
- field_4_last_column = in.readUShort();
- }
- protected final void writeCoordinates(byte[] array, int offset) {
- LittleEndian.putUShort(array, offset + 0, field_1_first_row);
- LittleEndian.putUShort(array, offset + 2, field_2_last_row);
- LittleEndian.putUShort(array, offset + 4, field_3_first_column);
- LittleEndian.putUShort(array, offset + 6, field_4_last_column);
- }
+ protected final void readCoordinates(RecordInputStream in) {
+ field_1_first_row = in.readUShort();
+ field_2_last_row = in.readUShort();
+ field_3_first_column = in.readUShort();
+ field_4_last_column = in.readUShort();
+ }
+ protected final void writeCoordinates(byte[] array, int offset) {
+ LittleEndian.putUShort(array, offset + 0, field_1_first_row);
+ LittleEndian.putUShort(array, offset + 2, field_2_last_row);
+ LittleEndian.putUShort(array, offset + 4, field_3_first_column);
+ LittleEndian.putUShort(array, offset + 6, field_4_last_column);
+ }
- /**
- * @return the first row in the area
- */
- public final int getFirstRow() {
- return field_1_first_row;
- }
+ /**
+ * @return the first row in the area
+ */
+ public final int getFirstRow() {
+ return field_1_first_row;
+ }
- /**
- * sets the first row
- * @param rowIx number (0-based)
- */
- public final void setFirstRow(int rowIx) {
- checkRowBounds(rowIx);
- field_1_first_row = rowIx;
- }
+ /**
+ * sets the first row
+ * @param rowIx number (0-based)
+ */
+ public final void setFirstRow(int rowIx) {
+ checkRowBounds(rowIx);
+ field_1_first_row = rowIx;
+ }
- /**
- * @return last row in the range (x2 in x1,y1-x2,y2)
- */
- public final int getLastRow() {
- return field_2_last_row;
- }
+ /**
+ * @return last row in the range (x2 in x1,y1-x2,y2)
+ */
+ public final int getLastRow() {
+ return field_2_last_row;
+ }
- /**
- * @param rowIx last row number in the area
- */
- public final void setLastRow(int rowIx) {
- checkRowBounds(rowIx);
- field_2_last_row = rowIx;
- }
+ /**
+ * @param rowIx last row number in the area
+ */
+ public final void setLastRow(int rowIx) {
+ checkRowBounds(rowIx);
+ field_2_last_row = rowIx;
+ }
- /**
- * @return the first column number in the area.
- */
- public final int getFirstColumn() {
- return columnMask.getValue(field_3_first_column);
- }
+ /**
+ * @return the first column number in the area.
+ */
+ public final int getFirstColumn() {
+ return columnMask.getValue(field_3_first_column);
+ }
- /**
- * @return the first column number + the options bit settings unstripped
- */
- public final short getFirstColumnRaw() {
- return (short) field_3_first_column; // TODO
- }
+ /**
+ * @return the first column number + the options bit settings unstripped
+ */
+ public final short getFirstColumnRaw() {
+ return (short) field_3_first_column; // TODO
+ }
- /**
- * @return whether or not the first row is a relative reference or not.
- */
- public final boolean isFirstRowRelative() {
- return rowRelative.isSet(field_3_first_column);
- }
-
- /**
- * sets the first row to relative or not
- * @param rel is relative or not.
- */
- public final void setFirstRowRelative(boolean rel) {
- field_3_first_column=rowRelative.setBoolean(field_3_first_column,rel);
- }
+ /**
+ * @return whether or not the first row is a relative reference or not.
+ */
+ public final boolean isFirstRowRelative() {
+ return rowRelative.isSet(field_3_first_column);
+ }
+
+ /**
+ * sets the first row to relative or not
+ * @param rel is relative or not.
+ */
+ public final void setFirstRowRelative(boolean rel) {
+ field_3_first_column=rowRelative.setBoolean(field_3_first_column,rel);
+ }
- /**
- * @return isrelative first column to relative or not
- */
- public final boolean isFirstColRelative() {
- return colRelative.isSet(field_3_first_column);
- }
-
- /**
- * set whether the first column is relative
- */
- public final void setFirstColRelative(boolean rel) {
- field_3_first_column=colRelative.setBoolean(field_3_first_column,rel);
- }
+ /**
+ * @return isrelative first column to relative or not
+ */
+ public final boolean isFirstColRelative() {
+ return colRelative.isSet(field_3_first_column);
+ }
+
+ /**
+ * set whether the first column is relative
+ */
+ public final void setFirstColRelative(boolean rel) {
+ field_3_first_column=colRelative.setBoolean(field_3_first_column,rel);
+ }
- /**
- * set the first column in the area
- */
- public final void setFirstColumn(int colIx) {
- checkColumnBounds(colIx);
- field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
- }
+ /**
+ * set the first column in the area
+ */
+ public final void setFirstColumn(int colIx) {
+ checkColumnBounds(colIx);
+ field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
+ }
- /**
- * set the first column irrespective of the bitmasks
- */
- public final void setFirstColumnRaw(int column) {
- field_3_first_column = column;
- }
+ /**
+ * set the first column irrespective of the bitmasks
+ */
+ public final void setFirstColumnRaw(int column) {
+ field_3_first_column = column;
+ }
- /**
- * @return lastcolumn in the area
- */
- public final int getLastColumn() {
- return columnMask.getValue(field_4_last_column);
- }
+ /**
+ * @return lastcolumn in the area
+ */
+ public final int getLastColumn() {
+ return columnMask.getValue(field_4_last_column);
+ }
- /**
- * @return last column and bitmask (the raw field)
- */
- public final short getLastColumnRaw() {
- return (short) field_4_last_column;
- }
+ /**
+ * @return last column and bitmask (the raw field)
+ */
+ public final short getLastColumnRaw() {
+ return (short) field_4_last_column;
+ }
- /**
- * @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 <code>true</code> if the last row relative, else
- * <code>false</code>
- */
- 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 <code>true</code> if the last row relative, else
+ * <code>false</code>
+ */
+ 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;
+ }
}
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);
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) */
_jumpTable = null;
_chooseFuncOffset = -1;
}
-
+
public AttrPtg(RecordInputStream in)
{
field_1_options = in.readByte();
_jumpTable = null;
_chooseFuncOffset = -1;
}
-
+
}
private AttrPtg(int options, int data, int[] jt, int chooseFuncOffset) {
field_1_options = (byte) options;
_jumpTable = jt;
_chooseFuncOffset = chooseFuncOffset;
}
-
+
/**
* @param type a constant from <tt>SpaceType</tt>
* @param count the number of space characters
{
return sum.isSet(getOptions());
}
-
+
public void setSum(boolean bsum) {
field_1_options=sum.setByteBoolean(field_1_options,bsum);
}
}
/**
- * 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()
{
} 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()) {
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++) {
}
LittleEndian.putUShort(array, joff, _chooseFuncOffset);
}
-
+
}
public int getSize()
return toFormulaString() + "(" + operands[ 0 ] + ")";
}
}
-
+
public int getNumberOfOperands()
{
{
return -1;
}
-
+
public String toFormulaString() {
if(semiVolatile.isSet(field_1_options)) {
return "ATTR(semiVolatile)";
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
}
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);
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;
/**
* @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
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;
}
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();
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 <code>true</code> 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 '$'
return false;
}
switch(ch) {
- case '_': // underscore is ok
+ case '.': // dot is OK
+ case '_': // underscore is OK
return false;
case '\n':
case '\r':
--- /dev/null
+/* ====================================================================
+ 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");
+ }
+}
\r
import org.apache.poi.hssf.model.HSSFFormulaParser;\r
import org.apache.poi.hssf.model.Workbook;\r
+import org.apache.poi.hssf.record.FormulaRecord;\r
import org.apache.poi.hssf.record.NameRecord;\r
+import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;\r
import org.apache.poi.hssf.record.formula.NamePtg;\r
import org.apache.poi.hssf.record.formula.NameXPtg;\r
import org.apache.poi.hssf.record.formula.Ptg;\r
int sheetIndex = _uBook.getSheetIndex(sheetName);\r
return _iBook.checkExternSheet(sheetIndex);\r
}\r
+ public int getExternalSheetIndex(String workbookName, String sheetName) {\r
+ return _iBook.getExternalSheetIndex(workbookName, sheetName);\r
+ }\r
\r
public EvaluationName getName(int index) {\r
return new Name(_iBook.getNameRecord(index), index);\r
public int getSheetIndex(Sheet sheet) {\r
return _uBook.getSheetIndex(sheet);\r
}\r
+ public int getSheetIndex(String sheetName) {\r
+ return _uBook.getSheetIndex(sheetName);\r
+ }\r
\r
public String getSheetName(int sheetIndex) {\r
return _uBook.getSheetName(sheetIndex);\r
}\r
public int convertFromExternSheetIndex(int externSheetIndex) {\r
return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);\r
-}\r
+ }\r
\r
+ public ExternalSheet getExternalSheet(int externSheetIndex) {\r
+ return _iBook.getExternalSheet(externSheetIndex);\r
+ }\r
+ \r
public HSSFWorkbook getWorkbook() {\r
return _uBook;\r
}\r
return new Name(_iBook.getNameRecord(ix), ix);\r
}\r
public Ptg[] getFormulaTokens(Cell cell) {\r
- return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);\r
+ if (false) {\r
+ // re-parsing the formula text also works, but is a waste of time\r
+ // It is useful from time to time to run all unit tests with this code\r
+ // to make sure that all formulas POI can evaluate can also be parsed.\r
+ return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);\r
+ }\r
+ HSSFCell hCell = (HSSFCell) cell;\r
+ FormulaRecord fr = ((FormulaRecordAggregate) hCell.getCellValueRecord()).getFormulaRecord();\r
+ return fr.getParsedExpression();\r
}\r
\r
private static final class Name implements EvaluationName {\r
import org.apache.poi.hssf.record.formula.eval.NumberEval;\r
import org.apache.poi.hssf.record.formula.eval.StringEval;\r
import org.apache.poi.hssf.record.formula.eval.ValueEval;\r
+import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;\r
import org.apache.poi.ss.formula.WorkbookEvaluator;\r
import org.apache.poi.ss.usermodel.Cell;\r
import org.apache.poi.ss.usermodel.CellValue;\r
public HSSFFormulaEvaluator(HSSFWorkbook workbook) {\r
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));\r
}\r
+ \r
+ /**\r
+ * Coordinates several formula evaluators together so that formulas that involve external\r
+ * references can be evaluated.\r
+ * @param workbookNames the simple file names used to identify the workbooks in formulas\r
+ * with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")\r
+ * @param evaluators all evaluators for the full set of workbooks required by the formulas. \r
+ */\r
+ public static void setupEnvironment(String[] workbookNames, HSSFFormulaEvaluator[] evaluators) {\r
+ WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];\r
+ for (int i = 0; i < wbEvals.length; i++) {\r
+ wbEvals[i] = evaluators[i]._bookEvaluator;\r
+ }\r
+ CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);\r
+ }\r
\r
/**\r
* Does nothing\r
/**
* 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);
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.
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);
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);
}
package org.apache.poi.ss.formula;
+import org.apache.poi.hssf.util.CellReference;
+
/**
* Stores the parameters that identify the evaluation of one cell.<br/>
*/
final class CellLocation {
public static final CellLocation[] EMPTY_ARRAY = { };
+ private final EvaluationWorkbook _book;
private final int _sheetIndex;
private final int _rowIndex;
private final int _columnIndex;
private final int _hashCode;
- public CellLocation(int sheetIndex, int rowIndex, int columnIndex) {
+ public CellLocation(EvaluationWorkbook book, int sheetIndex, int rowIndex, int columnIndex) {
if (sheetIndex < 0) {
throw new IllegalArgumentException("sheetIndex must not be negative");
}
+ _book = book;
_sheetIndex = sheetIndex;
_rowIndex = rowIndex;
_columnIndex = columnIndex;
- _hashCode = sheetIndex + 17 * (rowIndex + 17 * columnIndex);
+ _hashCode = System.identityHashCode(book) + sheetIndex + 17 * (rowIndex + 17 * columnIndex);
+ }
+ public Object getBook() {
+ return _book;
}
public int getSheetIndex() {
return _sheetIndex;
public boolean equals(Object obj) {
CellLocation other = (CellLocation) obj;
- if (getSheetIndex() != other.getSheetIndex()) {
- return false;
- }
if (getRowIndex() != other.getRowIndex()) {
return false;
}
if (getColumnIndex() != other.getColumnIndex()) {
return false;
}
+ if (getSheetIndex() != other.getSheetIndex()) {
+ return false;
+ }
+ if (getBook() != other.getBook()) {
+ return false;
+ }
return true;
}
public int hashCode() {
* @return human readable string for debug purposes
*/
public String formatAsString() {
- return "ShIx=" + getSheetIndex() + " R=" + getRowIndex() + " C=" + getColumnIndex();
+ CellReference cr = new CellReference(_rowIndex, _columnIndex, false, false);
+ return "ShIx=" + getSheetIndex() + " " + cr.formatAsString();
}
public String toString() {
--- /dev/null
+/* ====================================================================
+ 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 java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Manages a collection of {@link WorkbookEvaluator}s, in order to support evaluation of formulas
+ * across spreadsheets.<p/>
+ *
+ * For POI internal use only
+ *
+ * @author Josh Micich
+ */
+public final class CollaboratingWorkbooksEnvironment {
+
+ public static final CollaboratingWorkbooksEnvironment EMPTY = new CollaboratingWorkbooksEnvironment();
+
+ private final Map _evaluatorsByName;
+ private final WorkbookEvaluator[] _evaluators;
+
+ private boolean _unhooked;
+ private CollaboratingWorkbooksEnvironment() {
+ _evaluatorsByName = Collections.EMPTY_MAP;
+ _evaluators = new WorkbookEvaluator[0];
+ }
+ public static void setup(String[] workbookNames, WorkbookEvaluator[] evaluators) {
+ int nItems = workbookNames.length;
+ if (evaluators.length != nItems) {
+ throw new IllegalArgumentException("Number of workbook names is " + nItems
+ + " but number of evaluators is " + evaluators.length);
+ }
+ if (nItems < 1) {
+ throw new IllegalArgumentException("Must provide at least one collaborating worbook");
+ }
+ new CollaboratingWorkbooksEnvironment(workbookNames, evaluators, nItems);
+ }
+
+ private CollaboratingWorkbooksEnvironment(String[] workbookNames, WorkbookEvaluator[] evaluators, int nItems) {
+ Map m = new HashMap(nItems * 3 / 2);
+ IdentityHashMap uniqueEvals = new IdentityHashMap(nItems * 3 / 2);
+ for(int i=0; i<nItems; i++) {
+ String wbName = workbookNames[i];
+ WorkbookEvaluator wbEval = evaluators[i];
+ if (m.containsKey(wbName)) {
+ throw new IllegalArgumentException("Duplicate workbook name '" + wbName + "'");
+ }
+ if (uniqueEvals.containsKey(wbEval)) {
+ String msg = "Attempted to register same workbook under names '"
+ + uniqueEvals.get(wbEval) + "' and '" + wbName + "'";
+ throw new IllegalArgumentException(msg);
+ }
+ uniqueEvals.put(wbEval, wbName);
+ m.put(wbName, wbEval);
+ }
+ unhookOldEnvironments(evaluators);
+ hookNewEnvironment(evaluators, this);
+ _unhooked = false;
+ _evaluators = evaluators;
+ _evaluatorsByName = m;
+ }
+
+ private static void hookNewEnvironment(WorkbookEvaluator[] evaluators, CollaboratingWorkbooksEnvironment env) {
+
+ // All evaluators will need to share the same cache.
+ // but the cache takes an optional evaluation listener.
+ int nItems = evaluators.length;
+ IEvaluationListener evalListener = evaluators[0].getEvaluationListener();
+ // make sure that all evaluators have the same listener
+ for(int i=0; i<nItems; i++) {
+ if(evalListener != evaluators[i].getEvaluationListener()) {
+ // This would be very complex to support
+ throw new RuntimeException("Workbook evaluators must all have the same evaluation listener");
+ }
+ }
+ EvaluationCache cache = new EvaluationCache(evalListener);
+
+ for(int i=0; i<nItems; i++) {
+ evaluators[i].attachToEnvironment(env, cache);
+ }
+
+ }
+ private void unhookOldEnvironments(WorkbookEvaluator[] evaluators) {
+ Set oldEnvs = new HashSet();
+ for(int i=0; i<evaluators.length; i++) {
+ oldEnvs.add(evaluators[i].getEnvironment());
+ }
+ CollaboratingWorkbooksEnvironment[] oldCWEs = new CollaboratingWorkbooksEnvironment[oldEnvs.size()];
+ oldEnvs.toArray(oldCWEs);
+ for (int i = 0; i < oldCWEs.length; i++) {
+ oldCWEs[i].unhook();
+ }
+ }
+
+ /**
+ *
+ */
+ private void unhook() {
+ if (_evaluators.length < 1) {
+ return;
+ }
+ for (int i = 0; i < _evaluators.length; i++) {
+ _evaluators[i].detachFromEnvironment();
+ }
+ _unhooked = true;
+ }
+
+ public WorkbookEvaluator getWorkbookEvaluator(String workbookName) {
+ if (_unhooked) {
+ throw new IllegalStateException("This environment has been unhooked");
+ }
+ WorkbookEvaluator result = (WorkbookEvaluator) _evaluatorsByName.get(workbookName);
+ if (result == null) {
+ StringBuffer sb = new StringBuffer(256);
+ sb.append("Could not resolve external workbook name '").append(workbookName).append("'.");
+ if (_evaluators.length < 1) {
+ sb.append(" Workbook environment has not been set up.");
+ } else {
+ sb.append(" The following workbook names are valid: (");
+ Iterator i = _evaluatorsByName.keySet().iterator();
+ int count=0;
+ while(i.hasNext()) {
+ if (count++>0) {
+ sb.append(", ");
+ }
+ sb.append("'").append(i.next()).append("'");
+ }
+ sb.append(")");
+ }
+ throw new RuntimeException(sb.toString());
+ }
+ return result;
+ }
+}
+ cellLoc.formatAsString());
}
}
- if (_evaluationListener == null) {
- // optimisation - don't bother sorting if there is no listener.
- } else {
- // for testing
- // make order of callbacks to listener more deterministic
- Arrays.sort(usedCells, CellLocationComparator);
- }
+ sortCellLocationsForLogging(usedCells);
CellCacheEntry entry = getEntry(cellLoc);
CellLocation[] consumingFormulaCells = entry.getConsumingCells();
CellLocation[] prevUsedCells = entry.getUsedCells();
recurseClearCachedFormulaResults(consumingFormulaCells, 0);
}
+ /**
+ * This method sorts the supplied cellLocs so that the order of call-backs to the evaluation
+ * listener is more deterministic
+ */
+ private void sortCellLocationsForLogging(CellLocation[] cellLocs) {
+ if (_evaluationListener == null) {
+ // optimisation - don't bother sorting if there is no listener.
+ } else {
+ Arrays.sort(cellLocs, CellLocationComparator);
+ }
+ }
+
private void unlinkConsumingCells(CellLocation[] prevUsedCells, CellLocation[] usedCells,
CellLocation cellLoc) {
if (prevUsedCells == null) {
* @param formulaCells
*/
private void recurseClearCachedFormulaResults(CellLocation[] formulaCells, int depth) {
+ sortCellLocationsForLogging(formulaCells);
int nextDepth = depth+1;
for (int i = 0; i < formulaCells.length; i++) {
CellLocation fc = formulaCells[i];
CellLocation clB = (CellLocation) b;
int cmp;
+ cmp = System.identityHashCode(clA.getBook()) - System.identityHashCode(clB.getBook());
+ if (cmp != 0) {
+ return cmp;
+ }
cmp = clA.getSheetIndex() - clB.getSheetIndex();
if (cmp != 0) {
return cmp;
*/\r
public interface EvaluationWorkbook {\r
String getSheetName(int sheetIndex);\r
+ /**\r
+ * @return -1 if the specified sheet is from a different book\r
+ */\r
int getSheetIndex(Sheet sheet);\r
+ int getSheetIndex(String sheetName);\r
\r
Sheet getSheet(int sheetIndex);\r
\r
+ /**\r
+ * @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook\r
+ */\r
+ ExternalSheet getExternalSheet(int externSheetIndex);\r
int convertFromExternSheetIndex(int externSheetIndex);\r
EvaluationName getName(NamePtg namePtg);\r
String resolveNameXText(NameXPtg ptg);\r
Ptg[] getFormulaTokens(Cell cell);\r
+ \r
+ class ExternalSheet {\r
+ private final String _workbookName;\r
+ private final String _sheetName;\r
+\r
+ public ExternalSheet(String workbookName, String sheetName) {\r
+ _workbookName = workbookName;\r
+ _sheetName = sheetName;\r
+ }\r
+ public String getWorkbookName() {\r
+ return _workbookName;\r
+ }\r
+ public String getSheetName() {\r
+ return _sheetName;\r
+ }\r
+ }\r
}\r
--- /dev/null
+/* ====================================================================
+ 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 <br/>
+ *
+ * For POI internal use only
+ *
+ * @author Josh Micich
+ */
+public interface ExternSheetReferenceToken {
+ int getExternSheetIndex();
+}
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.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.StringPtg;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.CellReference;
-import org.apache.poi.hssf.util.CellReference.NameType;
+import org.apache.poi.ss.util.CellReference.NameType;
/**
* This class parses a formula string into a List of tokens in RPN order.
* @author Josh Micich
*/
public final class FormulaParser {
+ private static final class Identifier {
+ private final String _name;
+ private final boolean _isQuoted;
+
+ public Identifier(String name, boolean isQuoted) {
+ _name = name;
+ _isQuoted = isQuoted;
+ }
+ public String getName() {
+ return _name;
+ }
+ public boolean isQuoted() {
+ return _isQuoted;
+ }
+ public String toString() {
+ StringBuffer sb = new StringBuffer(64);
+ sb.append(getClass().getName());
+ sb.append(" [");
+ if (_isQuoted) {
+ sb.append("'").append(_name).append("'");
+ } else {
+ sb.append(_name);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+ }
/**
* Specific exception thrown when a supplied formula does not parse properly.<br/>
}
/** Recognize an Alpha Character */
- private boolean IsAlpha(char c) {
+ private static boolean IsAlpha(char c) {
return Character.isLetter(c) || c == '$' || c=='_';
}
/** Recognize a Decimal Digit */
- private boolean IsDigit(char c) {
+ private static boolean IsDigit(char c) {
return Character.isDigit(c);
}
/** Recognize an Alphanumeric */
- private boolean IsAlNum(char c) {
- return (IsAlpha(c) || IsDigit(c));
+ private static boolean IsAlNum(char c) {
+ return IsAlpha(c) || IsDigit(c);
}
/** Recognize White Space */
- private boolean IsWhite( char c) {
- return (c ==' ' || c== TAB);
+ private static boolean IsWhite( char c) {
+ return c ==' ' || c== TAB;
}
/** Skip Over Leading White Space */
}
GetChar();
}
-
+ private String parseUnquotedIdentifier() {
+ Identifier iden = parseIdentifier();
+ if (iden.isQuoted()) {
+ throw expected("unquoted identifier");
+ }
+ return iden.getName();
+ }
/**
* Parses a sheet name, named range name, or simple cell reference.<br/>
* Note - identifiers in Excel can contain dots, so this method may return a String
* may return a value like "A1..B2", in which case the caller must convert it to
* an area reference like "A1:B2"
*/
- private String parseIdentifier() {
- StringBuffer Token = new StringBuffer();
- if (!IsAlpha(look) && look != '\'') {
+ private Identifier parseIdentifier() {
+ StringBuffer sb = new StringBuffer();
+ if (!IsAlpha(look) && look != '\'' && look != '[') {
throw expected("Name");
}
- if(look == '\'')
- {
+ boolean isQuoted = look == '\'';
+ if(isQuoted) {
Match('\'');
boolean done = look == '\'';
- while(!done)
- {
- Token.append(look);
+ while(!done) {
+ sb.append(look);
GetChar();
if(look == '\'')
{
done = look != '\'';
}
}
- }
- else
- {
+ } else {
// allow for any sequence of dots and identifier chars
// special case of two consecutive dots is best treated in the calling code
- while (IsAlNum(look) || look == '.') {
- Token.append(look);
+ while (IsAlNum(look) || look == '.' || look == '[' || look == ']') {
+ sb.append(look);
GetChar();
}
}
- return Token.toString();
+ return new Identifier(sb.toString(), isQuoted);
}
/** Get a Number */
}
private ParseNode parseFunctionReferenceOrName() {
- String name = parseIdentifier();
+ Identifier iden = parseIdentifier();
if (look == '('){
//This is a function
- return function(name);
+ return function(iden.getName());
}
- return new ParseNode(parseNameOrReference(name));
- }
-
- private Ptg parseNameOrReference(String name) {
-
- AreaReference areaRef = parseArea(name);
- if (areaRef != null) {
- // will happen if dots are used instead of colon
- return new AreaPtg(areaRef.formatAsString());
+ if (!iden.isQuoted()) {
+ String name = iden.getName();
+ if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
+ return new ParseNode(new BoolPtg(name.toUpperCase()));
+ }
}
+ return parseRangeExpression(iden);
+ }
- if (look == ':' || look == '.') { // this is a AreaReference
+ private ParseNode parseRangeExpression(Identifier iden) {
+ Ptg ptgA = parseNameOrCellRef(iden);
+ if (look == ':') {
GetChar();
-
- while (look == '.') { // formulas can have . or .. or ... instead of :
- GetChar();
+ Identifier iden2 = parseIdentifier();
+ Ptg ptgB = parseNameOrCellRef(iden2);
+ Ptg simplified = reduceRangeExpression(ptgA, ptgB);
+
+ if (simplified == null) {
+ ParseNode[] children = {
+ new ParseNode(ptgA),
+ new ParseNode(ptgB),
+ };
+ return new ParseNode(RangePtg.instance, children);
}
+ return new ParseNode(simplified);
+ }
+ return new ParseNode(ptgA);
+ }
+
+ /**
+ *
+ * "A1", "B3" -> "A1:B3"
+ * "sheet1!A1", "B3" -> "sheet1!A1:B3"
+ *
+ * @return <code>null</code> 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;
- String first = name;
- String second = parseIdentifier();
- return new AreaPtg(first+":"+second);
+ 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 == '!') {
- Match('!');
- String sheetName = name;
- String first = parseIdentifier();
- int externIdx = book.getExternalSheetIndex(sheetName);
- areaRef = parseArea(name);
- if (areaRef != null) {
- // will happen if dots are used instead of colon
- return new Area3DPtg(areaRef.formatAsString(), externIdx);
- }
- if (look == ':') {
- Match(':');
- String second=parseIdentifier();
- if (look == '!') {
- //The sheet name was included in both of the areas. Only really
- //need it once
- Match('!');
- String third=parseIdentifier();
-
- if (!sheetName.equals(second))
- throw new RuntimeException("Unhandled double sheet reference.");
-
- return new Area3DPtg(first+":"+third,externIdx);
- }
- return new Area3DPtg(first+":"+second,externIdx);
+ GetChar();
+ // 3-D ref
+ // this code assumes iden is a sheetName
+ // TODO - handle <book name> ! <named range name>
+ int externIdx = getExternalSheetIndex(iden.getName());
+ String secondIden = parseUnquotedIdentifier();
+ AreaReference areaRef = parseArea(secondIden);
+ if (areaRef == null) {
+ return new Ref3DPtg(secondIden, externIdx);
}
- return new Ref3DPtg(first, externIdx);
- }
- if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
- return new BoolPtg(name.toUpperCase());
+ // will happen if dots are used instead of colon
+ return new Area3DPtg(areaRef.formatAsString(), externIdx);
}
+ String name = iden.getName();
+ AreaReference areaRef = parseArea(name);
+ if (areaRef != null) {
+ // will happen if dots are used instead of colon
+ return new AreaPtg(areaRef.formatAsString());
+ }
// This can be either a cell ref or a named range
- // Try to spot which it is
+
+
int nameType = CellReference.classifyCellReference(name);
if (nameType == NameType.CELL) {
return new RefPtg(name);
}
+ if (look == ':') {
+ if (nameType == NameType.COLUMN) {
+ GetChar();
+ String secondIden = parseUnquotedIdentifier();
+ if (CellReference.classifyCellReference(secondIden) != NameType.COLUMN) {
+ throw new FormulaParseException("Expected full column after '" + name
+ + ":' but got '" + secondIden + "'");
+ }
+ return new AreaPtg(name + ":" + secondIden);
+ }
+ }
if (nameType != NameType.NAMED_RANGE) {
new FormulaParseException("Name '" + name
+ "' does not look like a cell reference or named range");
+ name + "' is not a range as expected");
}
+ private int getExternalSheetIndex(String name) {
+ if (name.charAt(0) == '[') {
+ // we have a sheet name qualified with workbook name e.g. '[MyData.xls]Sheet1'
+ int pos = name.lastIndexOf(']'); // safe because sheet names never have ']'
+ String wbName = name.substring(1, pos);
+ String sheetName = name.substring(pos+1);
+ return book.getExternalSheetIndex(wbName, sheetName);
+ }
+ return book.getExternalSheetIndex(name);
+ }
+
/**
* @param name an 'identifier' like string (i.e. contains alphanums, and dots)
* @return <code>null</code> if name cannot be split at a dot
Match('}');
return arrayNode;
}
- if (IsAlpha(look) || look == '\''){
+ if (IsAlpha(look) || look == '\'' || look == '['){
return parseFunctionReferenceOrName();
}
// else - assume number
}
private Boolean parseBooleanLiteral() {
- String iden = parseIdentifier();
+ String iden = parseUnquotedIdentifier();
if ("TRUE".equalsIgnoreCase(iden)) {
return Boolean.TRUE;
}
private int parseErrorLiteral() {
Match('#');
- String part1 = parseIdentifier().toUpperCase();
+ String part1 = parseUnquotedIdentifier().toUpperCase();
switch(part1.charAt(0)) {
case 'V':
*/\r
EvaluationName getName(String name);\r
\r
- int getExternalSheetIndex(String sheetName);\r
NameXPtg getNameXPtg(String name);\r
+\r
+ /**\r
+ * gets the externSheet index for a sheet from this workbook\r
+ */\r
+ int getExternalSheetIndex(String sheetName);\r
+ /**\r
+ * gets the externSheet index for a sheet from an external workbook\r
+ * @param workbookName e.g. "Budget.xls"\r
+ * @param sheetName a name of a sheet in that workbook\r
+ */\r
+ int getExternalSheetIndex(String workbookName, String sheetName);\r
}\r
\r
import org.apache.poi.hssf.record.formula.NamePtg;\r
import org.apache.poi.hssf.record.formula.NameXPtg;\r
+import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;\r
\r
/**\r
* Abstracts a workbook for the purpose of converting formula to text.<br/>\r
*/\r
public interface FormulaRenderingWorkbook {\r
\r
+ /**\r
+ * @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook\r
+ */\r
+ ExternalSheet getExternalSheet(int externSheetIndex);\r
String getSheetNameByExternSheet(int externSheetIndex);\r
String resolveNameXText(NameXPtg nameXPtg);\r
String getNameText(NamePtg namePtg);\r
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;
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");
}
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;
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;
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;
}
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;
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;
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;
*
* @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);
_evaluationListener = evaluationListener;
_cache = new EvaluationCache(evaluationListener);
_sheetIndexesBySheet = new IdentityHashMap();
+ _collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
}
/**
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
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);
}
/**
*/
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();
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));
}
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);
}
}
}
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
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
*/
- 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();
}
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
* 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;
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);
} 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);
}
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;
* 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;
}
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).
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
- }
-
- public CellReference(int pRow, int pCol) {
- this(pRow, pCol & 0xFFFF, false, false);
- }
- public CellReference(int pRow, short pCol) {
- this(pRow, (int)pCol, false, false);
- }
+ /**
+ * 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, 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) {
+ this(pRow, pCol, false, false);
+ }
+ public CellReference(int pRow, short pCol) {
+ this(pRow, pCol & 0xFFFF, false, false);
+ }
- public int getRow(){return _rowIndex;}
- public short getCol(){return (short) _colIndex;}
- public boolean isRowAbsolute(){return _isRowAbs;}
- public boolean isColAbsolute(){return _isColAbs;}
- /**
- * @return possibly <code>null</code> 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;
- }
+ 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;
+ }
- /**
- * 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 <code>null</code> 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);
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 <tt>NameType</tt>
- */
- 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 <tt>NameType</tt>
+ */
+ 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
* <blockquote><table border="0" cellpadding="1" cellspacing="0"
* summary="Notable cases.">
* <tr><th>Input </th>
- * <th>Result </th></tr>
+ * <th>Result </th></tr>
* <tr><td>"A", "1"</td><td>true</td></tr>
* <tr><td>"a", "111"</td><td>true</td></tr>
* <tr><td>"A", "65536"</td><td>true</td></tr>
* @return <code>true</code> 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
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; i<lastQuotePos; i++) { // Note boundaries - skip outer quotes
- char ch = reference.charAt(i);
- if(ch != SPECIAL_NAME_DELIMITER) {
- sb.append(ch);
- continue;
- }
- if(i < lastQuotePos) {
- if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) {
- // two consecutive quotes is the escape sequence for a single one
- i++; // skip this and keep parsing the special name
- sb.append(ch);
- continue;
- }
- }
- throw new RuntimeException("Bad sheet name quote escaping: (" + reference + ")");
- }
- return sb.toString();
- }
+ // 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; i<lastQuotePos; i++) { // Note boundaries - skip outer quotes
+ char ch = reference.charAt(i);
+ if(ch != SPECIAL_NAME_DELIMITER) {
+ sb.append(ch);
+ continue;
+ }
+ if(i < lastQuotePos) {
+ if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) {
+ // two consecutive quotes is the escape sequence for a single one
+ i++; // skip this and keep parsing the special name
+ sb.append(ch);
+ continue;
+ }
+ }
+ throw new RuntimeException("Bad sheet name quote escaping: (" + reference + ")");
+ }
+ return sb.toString();
+ }
- /**
- * Takes in a 0-based base-10 column and returns a ALPHA-26
- * representation.
- * eg column #3 -> D
- */
- 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;
}
return colRef;
- }
+ }
+
+ /**
+ * Example return values:
+ * <table border="0" cellpadding="1" cellspacing="0" summary="Example return values">
+ * <tr><th align='left'>Result</th><th align='left'>Comment</th></tr>
+ * <tr><td>A1</td><td>Cell reference without sheet</td></tr>
+ * <tr><td>Sheet1!A1</td><td>Standard sheet name</td></tr>
+ * <tr><td>'O''Brien''s Sales'!A1' </td><td>Sheet name with special characters</td></tr>
+ * </table>
+ * @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:
- * <table border="0" cellpadding="1" cellspacing="0" summary="Example return values">
- * <tr><th align='left'>Result</th><th align='left'>Comment</th></tr>
- * <tr><td>A1</td><td>Cell reference without sheet</td></tr>
- * <tr><td>Sheet1!A1</td><td>Standard sheet name</td></tr>
- * <tr><td>'O''Brien''s Sales'!A1' </td><td>Sheet name with special characters</td></tr>
- * </table>
- * @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
};
}
- /**
- * 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);
+ }
}
return convertToExternalSheetIndex(sheetIndex);\r
}\r
\r
- public EvaluationName getName(int index) {\r
- return new Name(_uBook.getNameAt(index), index, this);\r
- }\r
-\r
public EvaluationName getName(String name) {\r
for(int i=0; i < _uBook.getNumberOfNames(); i++) {\r
String nameText = _uBook.getNameName(i);\r
return _uBook.getSheetAt(sheetIndex);\r
}\r
\r
- /**\r
- * Doesn't do anything - returns the same index\r
- * TODO - figure out if this is a ole2 specific thing, or\r
- * if we need to do something proper here too!\r
- */\r
- public Sheet getSheetByExternSheetIndex(int externSheetIndex) {\r
- int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);\r
- return _uBook.getSheetAt(sheetIndex);\r
+ public ExternalSheet getExternalSheet(int externSheetIndex) {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
+ public int getExternalSheetIndex(String workbookName, String sheetName) {\r
+ throw new RuntimeException("not implemented yet");\r
+ }\r
+ public int getSheetIndex(String sheetName) {\r
+ return _uBook.getSheetIndex(sheetName);\r
}\r
\r
public Workbook getWorkbook() {\r
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<br/>
}
suite.addTest(new TestSuite(TestEventRecordFactory.class));
suite.addTest(new TestSuite(TestModelFactory.class));
- suite.addTest(new TestSuite(TestEvaluationCache.class));
+ suite.addTest(AllSSFormulaTests.suite());
return suite;
}
}
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;
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);
/**
* 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.<p/>
- *
+ *
* 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() {
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 ')'");
}
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.
*/
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");
FuncPtg funcPtg = (FuncPtg) ptgs[1];
assertEquals("ERROR.TYPE", funcPtg.getName());
}
-
+
public void testNamedRangeThatLooksLikeCell() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
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;
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]);
+ }
+
+ 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());
}
-}
\ No newline at end of file
+
+ 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());
+ }
+
+}
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());
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));
}
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));
}
result.addTestSuite(TestFormulaBugs.class);
result.addTestSuite(TestFormulasFromSpreadsheet.class);
result.addTestSuite(TestPercentEval.class);
+ result.addTestSuite(TestRangeEval.class);
result.addTestSuite(TestUnaryPlusEval.class);
return result;
}
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...
--- /dev/null
+/* ====================================================================
+ 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");
+ }
+ }
+}
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;
/**
* 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;
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[] {
"+", "-", "*", "/", "^", "&"
};
* 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;
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);
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;
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;
));
- 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())
)
);
-
-
}
}
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());
}
/**
* 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;
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
assertTrue("minval Formula is as expected",
formula.equals(c.getCellFormula())
);
-
- in.close();
}
/**
* 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;
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;
( ("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);
(""+x+operator+y).equals(c.getCellFormula())
)
);
-
-
}
}
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;
-
+ public static void areaFunctionTest(String function) {
- r = s.createRow(0);
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ HSSFRow r = null;
+ HSSFCell c = null;
- c = r.createCell(0);
- c.setCellFormula(function+"(A2:A3)");
+ r = s.createRow(0);
- wb.write(out);
- out.close();
- assertTrue("file exists",file.exists());
+ c = r.createCell(0);
+ c.setCellFormula(function+"(A2:A3)");
- FileInputStream in = new FileInputStream(file);
- wb = new HSSFWorkbook(in);
- s = wb.getSheetAt(0);
- r = s.getRow(0);
- c = r.getCell(0);
+ 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)")) )
- );
- in.close();
+ 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)
- throws Exception {
+ public void refArrayFunctionTest(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;
-
-
- r = s.createRow(0);
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ HSSFRow r = null;
+ HSSFCell c = null;
- c = r.createCell(0);
- c.setCellFormula(function+"(A2,A3)");
+ r = s.createRow(0);
- wb.write(out);
- out.close();
- assertTrue("file exists",file.exists());
+ c = r.createCell(0);
+ c.setCellFormula(function+"(A2,A3)");
- FileInputStream in = new FileInputStream(file);
- wb = new HSSFWorkbook(in);
- s = wb.getSheetAt(0);
- r = s.getRow(0);
- c = r.getCell(0);
+ 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()) )
- );
- in.close();
+ assertTrue("function ="+function+"(A2,A3)",
+ ( (function+"(A2,A3)").equals(c.getCellFormula()) )
+ );
}
* Writes a function then tests to see if its correct
*
*/
- public void refAreaArrayFunctionTest(String function)
- throws Exception {
-
- 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;
+ public void refAreaArrayFunctionTest(String function) {
+ 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:A4,B2:B4)");
- c=r.createCell(1);
- c.setCellFormula(function+"($A$2:$A4,B$2:B4)");
+ r = s.createRow(0);
- wb.write(out);
- out.close();
- assertTrue("file exists",file.exists());
+ c = r.createCell(0);
+ c.setCellFormula(function+"(A2:A4,B2:B4)");
+ c=r.createCell(1);
+ c.setCellFormula(function+"($A$2:$A4,B$2:B4)");
- FileInputStream in = new FileInputStream(file);
- wb = new HSSFWorkbook(in);
- s = wb.getSheetAt(0);
- r = s.getRow(0);
- c = r.getCell(0);
+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+ 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)");
+
+ 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() {
+ 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);
- r = s.createRow(0);
+ 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");
- 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.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 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 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;
- 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();
- assertTrue("file exists",file.exists());
+ r = s.createRow(0);
- 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();
- }
+ 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)");
- 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;
+ c=r.createCell(6);
+ c.setCellFormula("SUM(A2:A3)");
+ c=r.createCell(7);
+ c.setCellFormula("SUM(A2,A3)");
- r = s.createRow(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);
- 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(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);
-
- 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(A1<A2,B1,B2)");
- public void testLogicalFormulas()
- throws IOException
- {
-
- File file = TempFile.createTempFile("testLogicalFormula",".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("IF(A1<A2,B1,B2)");
+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+ s = wb.getSheetAt(0);
+ r = s.getRow(0);
+ c = r.getCell(1);
+ assertEquals("Formula in cell 1 ","IF(A1<A2,B1,B2)",c.getCellFormula());
+ }
+ public void testDateFormulas() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet("testSheet1");
+ HSSFRow r = null;
+ HSSFCell c = null;
- wb.write(out);
- out.close();
+ r = s.createRow(0 );
+ c = r.createCell(0 );
- assertTrue("file exists",file.exists());
+ HSSFCellStyle cellStyle = wb.createCellStyle();
+ cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
+ c.setCellValue(new Date());
+ c.setCellStyle(cellStyle);
- FileInputStream in = new FileInputStream(file);
- wb = new HSSFWorkbook(in);
- s = wb.getSheetAt(0);
- r = s.getRow(0);
- c = r.getCell(1);
- assertEquals("Formula in cell 1 ","IF(A1<A2,B1,B2)",c.getCellFormula());
- in.close();
- }
+ // assertEquals("Checking hour = " + hour, date.getTime().getTime(),
+ // HSSFDateUtil.getJavaDate(excelDate).getTime());
- public void testDateFormulas()
- throws IOException
- {
- File file = TempFile.createTempFile("testDateFormula",".xls");
- FileOutputStream out = new FileOutputStream(file);
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet("testSheet1");
- HSSFRow r = null;
- HSSFCell c = null;
-
- r = s.createRow(0 );
- c = r.createCell(0 );
-
- HSSFCellStyle cellStyle = wb.createCellStyle();
- cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
- c.setCellValue(new Date());
- c.setCellStyle(cellStyle);
-
- // assertEquals("Checking hour = " + hour, date.getTime().getTime(),
- // HSSFDateUtil.getJavaDate(excelDate).getTime());
-
- for (int k=1; k < 100; k++) {
- r=s.createRow(k);
- c=r.createCell(0);
- c.setCellFormula("A"+(k)+"+1");
- c.setCellStyle(cellStyle);
- }
+ for (int k=1; k < 100; k++) {
+ r=s.createRow(k);
+ c=r.createCell(0);
+ c.setCellFormula("A"+(k)+"+1");
+ c.setCellStyle(cellStyle);
+ }
- wb.write(out);
- out.close();
+ HSSFTestDataSamples.writeOutAndReadBack(wb);
+ }
- assertTrue("file exists",file.exists());
- }
+ public void testIfFormulas() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet("testSheet1");
+ HSSFRow r = null;
+ HSSFCell c = null;
+ r = s.createRow(0);
+ c=r.createCell(1); c.setCellValue(1);
+ c=r.createCell(2); c.setCellValue(2);
+ c=r.createCell(3); c.setCellFormula("MAX(A1:B1)");
+ c=r.createCell(4); c.setCellFormula("IF(A1=D1,\"A1\",\"B1\")");
+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+ s = wb.getSheetAt(0);
+ r = s.getRow(0);
+ c = r.getCell(4);
- public void testIfFormulas()
- throws IOException
- {
- File file = TempFile.createTempFile("testIfFormula",".xls");
- FileOutputStream out = new FileOutputStream(file);
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet("testSheet1");
- HSSFRow r = null;
- HSSFCell c = null;
- r = s.createRow(0);
- c=r.createCell(1); c.setCellValue(1);
- c=r.createCell(2); c.setCellValue(2);
- c=r.createCell(3); c.setCellFormula("MAX(A1:B1)");
- c=r.createCell(4); c.setCellFormula("IF(A1=D1,\"A1\",\"B1\")");
+ assertTrue("expected: IF(A1=D1,\"A1\",\"B1\") got "+c.getCellFormula(), ("IF(A1=D1,\"A1\",\"B1\")").equals(c.getCellFormula()));
- wb.write(out);
- out.close();
+ wb = openSample("IfFormulaTest.xls");
+ s = wb.getSheetAt(0);
+ r = s.getRow(3);
+ c = r.getCell(0);
+ assertTrue("expected: IF(A3=A1,\"A1\",\"A2\") got "+c.getCellFormula(), ("IF(A3=A1,\"A1\",\"A2\")").equals(c.getCellFormula()));
+ //c = r.getCell((short)1);
+ //assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
- 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(4);
-
- assertTrue("expected: IF(A1=D1,\"A1\",\"B1\") got "+c.getCellFormula(), ("IF(A1=D1,\"A1\",\"B1\")").equals(c.getCellFormula()));
- in.close();
-
- wb = openSample("IfFormulaTest.xls");
- s = wb.getSheetAt(0);
- r = s.getRow(3);
- c = r.getCell(0);
- assertTrue("expected: IF(A3=A1,\"A1\",\"A2\") got "+c.getCellFormula(), ("IF(A3=A1,\"A1\",\"A2\")").equals(c.getCellFormula()));
- //c = r.getCell((short)1);
- //assertTrue("expected: A!A1+A!B1 got: "+c.getCellFormula(), ("A!A1+A!B1").equals(c.getCellFormula()));
- in.close();
-
- File simpleIf = TempFile.createTempFile("testSimpleIfFormulaWrite",".xls");
- out = new FileOutputStream(simpleIf);
wb = new HSSFWorkbook();
s = wb.createSheet("testSheet1");
r = null;
r = s.createRow(0);
c=r.createCell(0); c.setCellFormula("IF(1=1,0,1)");
- wb.write(out);
- out.close();
- assertTrue("file exists", simpleIf.exists());
-
- assertTrue("length of simpleIf file is zero", (simpleIf.length()>0));
+ HSSFTestDataSamples.writeOutAndReadBack(wb);
- File nestedIf = TempFile.createTempFile("testNestedIfFormula",".xls");
- out = new FileOutputStream(nestedIf);
wb = new HSSFWorkbook();
s = wb.createSheet("testSheet1");
r = null;
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");
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() {
/** 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");
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);
- }
}
--- /dev/null
+/* ====================================================================
+ 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;
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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());
+ }
+}