mirror of https://github.com/apache/poi.git
Bug 45865 - modified Formula Parser/Evaluator to handle cross-worksheet formulas
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@699761 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
567de4d703
commit
c819babd6e
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
<!-- Don't forget to update status.xml too! -->
|
<!-- Don't forget to update status.xml too! -->
|
||||||
<release version="3.2-alpha1" date="2008-??-??">
|
<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">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">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">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
<!-- Don't forget to update changes.xml too! -->
|
<!-- Don't forget to update changes.xml too! -->
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.2-alpha1" date="2008-??-??">
|
<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">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">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">Update hssf.extractor.ExcelExtractor to optionally output blank cells too</action>
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.poi.hssf.record.ExternalNameRecord;
|
||||||
import org.apache.poi.hssf.record.NameRecord;
|
import org.apache.poi.hssf.record.NameRecord;
|
||||||
import org.apache.poi.hssf.record.Record;
|
import org.apache.poi.hssf.record.Record;
|
||||||
import org.apache.poi.hssf.record.SupBookRecord;
|
import org.apache.poi.hssf.record.SupBookRecord;
|
||||||
|
import org.apache.poi.hssf.record.UnicodeString;
|
||||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,8 +110,8 @@ final class LinkTable {
|
||||||
temp.toArray(_crnBlocks);
|
temp.toArray(_crnBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExternalBookBlock(short numberOfSheets) {
|
public ExternalBookBlock(int numberOfSheets) {
|
||||||
_externalBookRecord = SupBookRecord.createInternalReferences(numberOfSheets);
|
_externalBookRecord = SupBookRecord.createInternalReferences((short)numberOfSheets);
|
||||||
_externalNameRecords = new ExternalNameRecord[0];
|
_externalNameRecords = new ExternalNameRecord[0];
|
||||||
_crnBlocks = new CRNBlock[0];
|
_crnBlocks = new CRNBlock[0];
|
||||||
}
|
}
|
||||||
|
@ -197,7 +198,7 @@ final class LinkTable {
|
||||||
return ExternSheetRecord.combine(esrs);
|
return ExternSheetRecord.combine(esrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) {
|
public LinkTable(int numberOfSheets, WorkbookRecordList workbookRecordList) {
|
||||||
_workbookRecordList = workbookRecordList;
|
_workbookRecordList = workbookRecordList;
|
||||||
_definedNames = new ArrayList();
|
_definedNames = new ArrayList();
|
||||||
_externalBookBlocks = new ExternalBookBlock[] {
|
_externalBookBlocks = new ExternalBookBlock[] {
|
||||||
|
@ -303,8 +304,62 @@ final class LinkTable {
|
||||||
return lastName.getSheetNumber() == firstName.getSheetNumber();
|
return lastName.getSheetNumber() == firstName.getSheetNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 getIndexToSheet(int extRefIndex) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,20 +370,26 @@ final class LinkTable {
|
||||||
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
|
return _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int addSheetIndexToExternSheet(int sheetNumber) {
|
public int checkExternSheet(int sheetIndex) {
|
||||||
// TODO - what about the first parameter (extBookIndex)?
|
int thisWbIndex = -1; // this is probably always zero
|
||||||
return _externSheetRecord.addRef(0, sheetNumber, sheetNumber);
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
public short checkExternSheet(int sheetIndex) {
|
|
||||||
|
|
||||||
//Trying to find reference to this sheet
|
//Trying to find reference to this sheet
|
||||||
int i = _externSheetRecord.getRefIxForSheet(sheetIndex);
|
int i = _externSheetRecord.getRefIxForSheet(thisWbIndex, sheetIndex);
|
||||||
if (i>=0) {
|
if (i>=0) {
|
||||||
return (short)i;
|
return i;
|
||||||
}
|
}
|
||||||
//We Haven't found reference to this sheet
|
//We haven't found reference to this sheet
|
||||||
return (short)addSheetIndexToExternSheet((short) sheetIndex);
|
return _externSheetRecord.addRef(thisWbIndex, sheetIndex, sheetIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.poi.ddf.*;
|
||||||
import org.apache.poi.hssf.record.*;
|
import org.apache.poi.hssf.record.*;
|
||||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||||
import org.apache.poi.hssf.util.HSSFColor;
|
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.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
|
|
||||||
|
@ -1914,8 +1915,7 @@ public final class Workbook implements Model {
|
||||||
*/
|
*/
|
||||||
public String findSheetNameFromExternSheet(int externSheetIndex){
|
public String findSheetNameFromExternSheet(int externSheetIndex){
|
||||||
|
|
||||||
int indexToSheet = linkTable.getIndexToSheet(externSheetIndex);
|
int indexToSheet = linkTable.getIndexToInternalSheet(externSheetIndex);
|
||||||
|
|
||||||
if (indexToSheet < 0) {
|
if (indexToSheet < 0) {
|
||||||
// TODO - what does '-1' mean here?
|
// TODO - what does '-1' mean here?
|
||||||
//error check, bail out gracefully!
|
//error check, bail out gracefully!
|
||||||
|
@ -1927,6 +1927,13 @@ public final class Workbook implements Model {
|
||||||
}
|
}
|
||||||
return getSheetName(indexToSheet);
|
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.
|
* Finds the sheet index for a particular external sheet number.
|
||||||
|
@ -1944,9 +1951,14 @@ public final class Workbook implements Model {
|
||||||
* @return index to extern sheet
|
* @return index to extern sheet
|
||||||
*/
|
*/
|
||||||
public short checkExternSheet(int sheetNumber){
|
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
|
/** gets the total number of names
|
||||||
* @return number of names
|
* @return number of names
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -250,10 +250,13 @@ public class ExternSheetRecord extends Record {
|
||||||
return _list.size() - 1;
|
return _list.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRefIxForSheet(int sheetIndex) {
|
public int getRefIxForSheet(int externalBookIndex, int sheetIndex) {
|
||||||
int nItems = _list.size();
|
int nItems = _list.size();
|
||||||
for (int i = 0; i < nItems; i++) {
|
for (int i = 0; i < nItems; i++) {
|
||||||
RefSubRecord ref = getRef(i);
|
RefSubRecord ref = getRef(i);
|
||||||
|
if (ref.getExtBookIndex() != externalBookIndex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) {
|
if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,8 +221,33 @@ public final class SupBookRecord extends Record {
|
||||||
{
|
{
|
||||||
return sid;
|
return sid;
|
||||||
}
|
}
|
||||||
public UnicodeString getURL() {
|
public String getURL() {
|
||||||
return field_2_encoded_url;
|
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() {
|
public UnicodeString[] getSheetNames() {
|
||||||
return (UnicodeString[]) field_3_sheet_names.clone();
|
return (UnicodeString[]) field_3_sheet_names.clone();
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
package org.apache.poi.hssf.record.formula;
|
package org.apache.poi.hssf.record.formula;
|
||||||
|
|
||||||
import org.apache.poi.hssf.record.RecordInputStream;
|
import org.apache.poi.hssf.record.RecordInputStream;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.ss.formula.ExternSheetReferenceToken;
|
||||||
import org.apache.poi.ss.formula.WorkbookDependentFormula;
|
|
||||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||||
|
import org.apache.poi.ss.formula.WorkbookDependentFormula;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
|
||||||
* @author Jason Height (jheight at chariot dot net dot au)
|
* @author Jason Height (jheight at chariot dot net dot au)
|
||||||
* @version 1.0-pre
|
* @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;
|
public final static byte sid = 0x3b;
|
||||||
private final static int SIZE = 11; // 10 + 1 for Ptg
|
private final static int SIZE = 11; // 10 + 1 for Ptg
|
||||||
|
|
||||||
|
@ -77,8 +77,8 @@ public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFor
|
||||||
return SIZE;
|
return SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getExternSheetIndex() {
|
public int getExternSheetIndex() {
|
||||||
return (short)field_1_index_extern_sheet;
|
return field_1_index_extern_sheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExternSheetIndex(int index) {
|
public void setExternSheetIndex(int index) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.apache.poi.hssf.record.formula;
|
package org.apache.poi.hssf.record.formula;
|
||||||
|
|
||||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||||
|
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Josh Micich
|
* @author Josh Micich
|
||||||
|
@ -29,14 +30,23 @@ final class ExternSheetNameResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String prependSheetName(FormulaRenderingWorkbook book, int field_1_index_extern_sheet, String cellRefText) {
|
public static String prependSheetName(FormulaRenderingWorkbook book, int field_1_index_extern_sheet, String cellRefText) {
|
||||||
|
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 {
|
||||||
String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
|
String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
|
||||||
StringBuffer sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
|
sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
|
||||||
if (sheetName.length() < 1) {
|
if (sheetName.length() < 1) {
|
||||||
// What excel does if sheet has been deleted
|
// What excel does if sheet has been deleted
|
||||||
sb.append("#REF"); // note - '!' added just once below
|
sb.append("#REF"); // note - '!' added just once below
|
||||||
} else {
|
} else {
|
||||||
SheetNameFormatter.appendFormat(sb, sheetName);
|
SheetNameFormatter.appendFormat(sb, sheetName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
sb.append('!');
|
sb.append('!');
|
||||||
sb.append(cellRefText);
|
sb.append(cellRefText);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|
|
@ -19,8 +19,9 @@ package org.apache.poi.hssf.record.formula;
|
||||||
|
|
||||||
import org.apache.poi.hssf.record.RecordInputStream;
|
import org.apache.poi.hssf.record.RecordInputStream;
|
||||||
import org.apache.poi.hssf.util.CellReference;
|
import org.apache.poi.hssf.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.FormulaRenderingWorkbook;
|
||||||
|
import org.apache.poi.ss.formula.WorkbookDependentFormula;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
|
||||||
* @author Jason Height (jheight at chariot dot net dot au)
|
* @author Jason Height (jheight at chariot dot net dot au)
|
||||||
* @version 1.0-pre
|
* @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;
|
public final static byte sid = 0x3a;
|
||||||
|
|
||||||
private final static int SIZE = 7; // 6 + 1 for Ptg
|
private final static int SIZE = 7; // 6 + 1 for Ptg
|
||||||
|
|
|
@ -66,6 +66,22 @@ public final class SheetNameFormatter {
|
||||||
out.append(rawSheetName);
|
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) {
|
private static void appendAndEscape(StringBuffer sb, String rawSheetName) {
|
||||||
int len = rawSheetName.length();
|
int len = rawSheetName.length();
|
||||||
|
|
|
@ -39,6 +39,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||||
int sheetIndex = _uBook.getSheetIndex(sheetName);
|
int sheetIndex = _uBook.getSheetIndex(sheetName);
|
||||||
return _iBook.checkExternSheet(sheetIndex);
|
return _iBook.checkExternSheet(sheetIndex);
|
||||||
}
|
}
|
||||||
|
public int getExternalSheetIndex(String workbookName, String sheetName) {
|
||||||
|
return _iBook.getExternalSheetIndex(workbookName, sheetName);
|
||||||
|
}
|
||||||
|
|
||||||
public EvaluationName getName(int index) {
|
public EvaluationName getName(int index) {
|
||||||
return new Name(_iBook.getNameRecord(index), index);
|
return new Name(_iBook.getNameRecord(index), index);
|
||||||
|
@ -57,6 +60,9 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||||
public int getSheetIndex(HSSFSheet sheet) {
|
public int getSheetIndex(HSSFSheet sheet) {
|
||||||
return _uBook.getSheetIndex(sheet);
|
return _uBook.getSheetIndex(sheet);
|
||||||
}
|
}
|
||||||
|
public int getSheetIndex(String sheetName) {
|
||||||
|
return _uBook.getSheetIndex(sheetName);
|
||||||
|
}
|
||||||
|
|
||||||
public String getSheetName(int sheetIndex) {
|
public String getSheetName(int sheetIndex) {
|
||||||
return _uBook.getSheetName(sheetIndex);
|
return _uBook.getSheetName(sheetIndex);
|
||||||
|
@ -77,6 +83,10 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
|
||||||
return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);
|
return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExternalSheet getExternalSheet(int externSheetIndex) {
|
||||||
|
return _iBook.getExternalSheet(externSheetIndex);
|
||||||
|
}
|
||||||
|
|
||||||
public HSSFWorkbook getWorkbook() {
|
public HSSFWorkbook getWorkbook() {
|
||||||
return _uBook;
|
return _uBook;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
|
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
|
||||||
import org.apache.poi.ss.formula.WorkbookEvaluator;
|
import org.apache.poi.ss.formula.WorkbookEvaluator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +55,21 @@ public class HSSFFormulaEvaluator {
|
||||||
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));
|
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coordinates several formula evaluators together so that formulas that involve external
|
||||||
|
* references can be evaluated.
|
||||||
|
* @param workbookNames the simple file names used to identify the workbooks in formulas
|
||||||
|
* with external links (for example "MyData.xls" as used in a formula "[MyData.xls]Sheet1!A1")
|
||||||
|
* @param evaluators all evaluators for the full set of workbooks required by the formulas.
|
||||||
|
*/
|
||||||
|
public static void setupEnvironment(String[] workbookNames, HSSFFormulaEvaluator[] evaluators) {
|
||||||
|
WorkbookEvaluator[] wbEvals = new WorkbookEvaluator[evaluators.length];
|
||||||
|
for (int i = 0; i < wbEvals.length; i++) {
|
||||||
|
wbEvals[i] = evaluators[i]._bookEvaluator;
|
||||||
|
}
|
||||||
|
CollaboratingWorkbooksEnvironment.setup(workbookNames, wbEvals);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does nothing
|
* Does nothing
|
||||||
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
|
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
|
||||||
|
|
|
@ -17,25 +17,32 @@
|
||||||
|
|
||||||
package org.apache.poi.ss.formula;
|
package org.apache.poi.ss.formula;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.util.CellReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the parameters that identify the evaluation of one cell.<br/>
|
* Stores the parameters that identify the evaluation of one cell.<br/>
|
||||||
*/
|
*/
|
||||||
final class CellLocation {
|
final class CellLocation {
|
||||||
public static final CellLocation[] EMPTY_ARRAY = { };
|
public static final CellLocation[] EMPTY_ARRAY = { };
|
||||||
|
|
||||||
|
private final EvaluationWorkbook _book;
|
||||||
private final int _sheetIndex;
|
private final int _sheetIndex;
|
||||||
private final int _rowIndex;
|
private final int _rowIndex;
|
||||||
private final int _columnIndex;
|
private final int _columnIndex;
|
||||||
private final int _hashCode;
|
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) {
|
if (sheetIndex < 0) {
|
||||||
throw new IllegalArgumentException("sheetIndex must not be negative");
|
throw new IllegalArgumentException("sheetIndex must not be negative");
|
||||||
}
|
}
|
||||||
|
_book = book;
|
||||||
_sheetIndex = sheetIndex;
|
_sheetIndex = sheetIndex;
|
||||||
_rowIndex = rowIndex;
|
_rowIndex = rowIndex;
|
||||||
_columnIndex = columnIndex;
|
_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() {
|
public int getSheetIndex() {
|
||||||
return _sheetIndex;
|
return _sheetIndex;
|
||||||
|
@ -49,15 +56,18 @@ final class CellLocation {
|
||||||
|
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
CellLocation other = (CellLocation) obj;
|
CellLocation other = (CellLocation) obj;
|
||||||
if (getSheetIndex() != other.getSheetIndex()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getRowIndex() != other.getRowIndex()) {
|
if (getRowIndex() != other.getRowIndex()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getColumnIndex() != other.getColumnIndex()) {
|
if (getColumnIndex() != other.getColumnIndex()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (getSheetIndex() != other.getSheetIndex()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getBook() != other.getBook()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
@ -68,7 +78,8 @@ final class CellLocation {
|
||||||
* @return human readable string for debug purposes
|
* @return human readable string for debug purposes
|
||||||
*/
|
*/
|
||||||
public String formatAsString() {
|
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() {
|
public String toString() {
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
/* ====================================================================
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,13 +81,7 @@ final class EvaluationCache {
|
||||||
+ cellLoc.formatAsString());
|
+ cellLoc.formatAsString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_evaluationListener == null) {
|
sortCellLocationsForLogging(usedCells);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
CellCacheEntry entry = getEntry(cellLoc);
|
CellCacheEntry entry = getEntry(cellLoc);
|
||||||
CellLocation[] consumingFormulaCells = entry.getConsumingCells();
|
CellLocation[] consumingFormulaCells = entry.getConsumingCells();
|
||||||
CellLocation[] prevUsedCells = entry.getUsedCells();
|
CellLocation[] prevUsedCells = entry.getUsedCells();
|
||||||
|
@ -110,6 +104,18 @@ final class EvaluationCache {
|
||||||
recurseClearCachedFormulaResults(consumingFormulaCells, 0);
|
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,
|
private void unlinkConsumingCells(CellLocation[] prevUsedCells, CellLocation[] usedCells,
|
||||||
CellLocation cellLoc) {
|
CellLocation cellLoc) {
|
||||||
if (prevUsedCells == null) {
|
if (prevUsedCells == null) {
|
||||||
|
@ -149,6 +155,7 @@ final class EvaluationCache {
|
||||||
* @param formulaCells
|
* @param formulaCells
|
||||||
*/
|
*/
|
||||||
private void recurseClearCachedFormulaResults(CellLocation[] formulaCells, int depth) {
|
private void recurseClearCachedFormulaResults(CellLocation[] formulaCells, int depth) {
|
||||||
|
sortCellLocationsForLogging(formulaCells);
|
||||||
int nextDepth = depth+1;
|
int nextDepth = depth+1;
|
||||||
for (int i = 0; i < formulaCells.length; i++) {
|
for (int i = 0; i < formulaCells.length; i++) {
|
||||||
CellLocation fc = formulaCells[i];
|
CellLocation fc = formulaCells[i];
|
||||||
|
@ -196,6 +203,10 @@ final class EvaluationCache {
|
||||||
CellLocation clB = (CellLocation) b;
|
CellLocation clB = (CellLocation) b;
|
||||||
|
|
||||||
int cmp;
|
int cmp;
|
||||||
|
cmp = System.identityHashCode(clA.getBook()) - System.identityHashCode(clB.getBook());
|
||||||
|
if (cmp != 0) {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
cmp = clA.getSheetIndex() - clB.getSheetIndex();
|
cmp = clA.getSheetIndex() - clB.getSheetIndex();
|
||||||
if (cmp != 0) {
|
if (cmp != 0) {
|
||||||
return cmp;
|
return cmp;
|
||||||
|
|
|
@ -31,12 +31,36 @@ import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
*/
|
*/
|
||||||
public interface EvaluationWorkbook {
|
public interface EvaluationWorkbook {
|
||||||
String getSheetName(int sheetIndex);
|
String getSheetName(int sheetIndex);
|
||||||
|
/**
|
||||||
|
* @return -1 if the specified sheet is from a different book
|
||||||
|
*/
|
||||||
int getSheetIndex(HSSFSheet sheet);
|
int getSheetIndex(HSSFSheet sheet);
|
||||||
|
int getSheetIndex(String sheetName);
|
||||||
|
|
||||||
HSSFSheet getSheet(int sheetIndex);
|
HSSFSheet getSheet(int sheetIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook
|
||||||
|
*/
|
||||||
|
ExternalSheet getExternalSheet(int externSheetIndex);
|
||||||
int convertFromExternSheetIndex(int externSheetIndex);
|
int convertFromExternSheetIndex(int externSheetIndex);
|
||||||
EvaluationName getName(NamePtg namePtg);
|
EvaluationName getName(NamePtg namePtg);
|
||||||
String resolveNameXText(NameXPtg ptg);
|
String resolveNameXText(NameXPtg ptg);
|
||||||
Ptg[] getFormulaTokens(HSSFCell cell);
|
Ptg[] getFormulaTokens(HSSFCell cell);
|
||||||
|
|
||||||
|
class ExternalSheet {
|
||||||
|
private final String _workbookName;
|
||||||
|
private final String _sheetName;
|
||||||
|
|
||||||
|
public ExternalSheet(String workbookName, String sheetName) {
|
||||||
|
_workbookName = workbookName;
|
||||||
|
_sheetName = sheetName;
|
||||||
|
}
|
||||||
|
public String getWorkbookName() {
|
||||||
|
return _workbookName;
|
||||||
|
}
|
||||||
|
public String getSheetName() {
|
||||||
|
return _sheetName;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.ss.formula;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be implemented by any {@link Ptg} subclass that needs has an extern sheet index <br/>
|
||||||
|
*
|
||||||
|
* For POI internal use only
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public interface ExternSheetReferenceToken {
|
||||||
|
int getExternSheetIndex();
|
||||||
|
}
|
|
@ -257,7 +257,7 @@ public final class FormulaParser {
|
||||||
*/
|
*/
|
||||||
private Identifier parseIdentifier() {
|
private Identifier parseIdentifier() {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
if (!IsAlpha(look) && look != '\'') {
|
if (!IsAlpha(look) && look != '\'' && look != '[') {
|
||||||
throw expected("Name");
|
throw expected("Name");
|
||||||
}
|
}
|
||||||
boolean isQuoted = look == '\'';
|
boolean isQuoted = look == '\'';
|
||||||
|
@ -276,7 +276,7 @@ public final class FormulaParser {
|
||||||
} else {
|
} else {
|
||||||
// allow for any sequence of dots and identifier chars
|
// allow for any sequence of dots and identifier chars
|
||||||
// special case of two consecutive dots is best treated in the calling code
|
// special case of two consecutive dots is best treated in the calling code
|
||||||
while (IsAlNum(look) || look == '.') {
|
while (IsAlNum(look) || look == '.' || look == '[' || look == ']') {
|
||||||
sb.append(look);
|
sb.append(look);
|
||||||
GetChar();
|
GetChar();
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ public final class FormulaParser {
|
||||||
// 3-D ref
|
// 3-D ref
|
||||||
// this code assumes iden is a sheetName
|
// this code assumes iden is a sheetName
|
||||||
// TODO - handle <book name> ! <named range name>
|
// TODO - handle <book name> ! <named range name>
|
||||||
int externIdx = book.getExternalSheetIndex(iden.getName());
|
int externIdx = getExternalSheetIndex(iden.getName());
|
||||||
String secondIden = parseUnquotedIdentifier();
|
String secondIden = parseUnquotedIdentifier();
|
||||||
AreaReference areaRef = parseArea(secondIden);
|
AreaReference areaRef = parseArea(secondIden);
|
||||||
if (areaRef == null) {
|
if (areaRef == null) {
|
||||||
|
@ -418,6 +418,17 @@ public final class FormulaParser {
|
||||||
+ name + "' is not a range as expected");
|
+ 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)
|
* @param name an 'identifier' like string (i.e. contains alphanums, and dots)
|
||||||
* @return <code>null</code> if name cannot be split at a dot
|
* @return <code>null</code> if name cannot be split at a dot
|
||||||
|
@ -656,7 +667,7 @@ public final class FormulaParser {
|
||||||
Match('}');
|
Match('}');
|
||||||
return arrayNode;
|
return arrayNode;
|
||||||
}
|
}
|
||||||
if (IsAlpha(look) || look == '\''){
|
if (IsAlpha(look) || look == '\'' || look == '['){
|
||||||
return parseFunctionReferenceOrName();
|
return parseFunctionReferenceOrName();
|
||||||
}
|
}
|
||||||
// else - assume number
|
// else - assume number
|
||||||
|
|
|
@ -32,6 +32,16 @@ public interface FormulaParsingWorkbook {
|
||||||
*/
|
*/
|
||||||
EvaluationName getName(String name);
|
EvaluationName getName(String name);
|
||||||
|
|
||||||
int getExternalSheetIndex(String sheetName);
|
|
||||||
NameXPtg getNameXPtg(String name);
|
NameXPtg getNameXPtg(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the externSheet index for a sheet from this workbook
|
||||||
|
*/
|
||||||
|
int getExternalSheetIndex(String sheetName);
|
||||||
|
/**
|
||||||
|
* gets the externSheet index for a sheet from an external workbook
|
||||||
|
* @param workbookName e.g. "Budget.xls"
|
||||||
|
* @param sheetName a name of a sheet in that workbook
|
||||||
|
*/
|
||||||
|
int getExternalSheetIndex(String workbookName, String sheetName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
|
||||||
|
|
||||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||||
|
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstracts a workbook for the purpose of converting formula to text.<br/>
|
* Abstracts a workbook for the purpose of converting formula to text.<br/>
|
||||||
|
@ -29,6 +30,10 @@ import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||||
*/
|
*/
|
||||||
public interface FormulaRenderingWorkbook {
|
public interface FormulaRenderingWorkbook {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <code>null</code> if externSheetIndex refers to a sheet inside the current workbook
|
||||||
|
*/
|
||||||
|
ExternalSheet getExternalSheet(int externSheetIndex);
|
||||||
String getSheetNameByExternSheet(int externSheetIndex);
|
String getSheetNameByExternSheet(int externSheetIndex);
|
||||||
String resolveNameXText(NameXPtg nameXPtg);
|
String resolveNameXText(NameXPtg nameXPtg);
|
||||||
String getNameText(NamePtg namePtg);
|
String getNameText(NamePtg namePtg);
|
||||||
|
|
|
@ -63,6 +63,7 @@ import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
import org.apache.poi.hssf.util.CellReference;
|
import org.apache.poi.hssf.util.CellReference;
|
||||||
|
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates formula cells.<p/>
|
* Evaluates formula cells.<p/>
|
||||||
|
@ -75,13 +76,14 @@ import org.apache.poi.hssf.util.CellReference;
|
||||||
*
|
*
|
||||||
* @author Josh Micich
|
* @author Josh Micich
|
||||||
*/
|
*/
|
||||||
public class WorkbookEvaluator {
|
public final class WorkbookEvaluator {
|
||||||
|
|
||||||
private final EvaluationWorkbook _workbook;
|
private final EvaluationWorkbook _workbook;
|
||||||
private final EvaluationCache _cache;
|
private EvaluationCache _cache;
|
||||||
|
|
||||||
private final IEvaluationListener _evaluationListener;
|
private final IEvaluationListener _evaluationListener;
|
||||||
private final Map _sheetIndexesBySheet;
|
private final Map _sheetIndexesBySheet;
|
||||||
|
private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
|
||||||
|
|
||||||
public WorkbookEvaluator(EvaluationWorkbook workbook) {
|
public WorkbookEvaluator(EvaluationWorkbook workbook) {
|
||||||
this (workbook, null);
|
this (workbook, null);
|
||||||
|
@ -91,6 +93,7 @@ public class WorkbookEvaluator {
|
||||||
_evaluationListener = evaluationListener;
|
_evaluationListener = evaluationListener;
|
||||||
_cache = new EvaluationCache(evaluationListener);
|
_cache = new EvaluationCache(evaluationListener);
|
||||||
_sheetIndexesBySheet = new IdentityHashMap();
|
_sheetIndexesBySheet = new IdentityHashMap();
|
||||||
|
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,6 +111,21 @@ public class WorkbookEvaluator {
|
||||||
System.out.println(s);
|
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.
|
* Should be called whenever there are changes to input cells in the evaluated workbook.
|
||||||
|
@ -130,7 +148,7 @@ public class WorkbookEvaluator {
|
||||||
throw new IllegalArgumentException("value must not be null");
|
throw new IllegalArgumentException("value must not be null");
|
||||||
}
|
}
|
||||||
int sheetIndex = getSheetIndex(sheet);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -139,13 +157,17 @@ public class WorkbookEvaluator {
|
||||||
*/
|
*/
|
||||||
public void notifySetFormula(HSSFSheet sheet, int rowIndex, int columnIndex) {
|
public void notifySetFormula(HSSFSheet sheet, int rowIndex, int columnIndex) {
|
||||||
int sheetIndex = getSheetIndex(sheet);
|
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(HSSFSheet sheet) {
|
private int getSheetIndex(HSSFSheet sheet) {
|
||||||
Integer result = (Integer) _sheetIndexesBySheet.get(sheet);
|
Integer result = (Integer) _sheetIndexesBySheet.get(sheet);
|
||||||
if (result == null) {
|
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);
|
_sheetIndexesBySheet.put(sheet, result);
|
||||||
}
|
}
|
||||||
return result.intValue();
|
return result.intValue();
|
||||||
|
@ -153,7 +175,7 @@ public class WorkbookEvaluator {
|
||||||
|
|
||||||
public ValueEval evaluate(HSSFCell srcCell) {
|
public ValueEval evaluate(HSSFCell srcCell) {
|
||||||
int sheetIndex = getSheetIndex(srcCell.getSheet());
|
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));
|
return internalEvaluate(srcCell, cellLoc, new EvaluationTracker(_cache));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,6 +364,20 @@ public class WorkbookEvaluator {
|
||||||
}
|
}
|
||||||
return operation.evaluate(ops, srcRowNum, (short)srcColNum);
|
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
|
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
|
||||||
|
@ -350,6 +386,8 @@ public class WorkbookEvaluator {
|
||||||
* passed here!
|
* passed here!
|
||||||
*/
|
*/
|
||||||
private Eval getEvalForPtg(Ptg ptg, int sheetIndex, EvaluationTracker tracker) {
|
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) {
|
if (ptg instanceof NamePtg) {
|
||||||
// named ranges, macro functions
|
// named ranges, macro functions
|
||||||
NamePtg namePtg = (NamePtg) ptg;
|
NamePtg namePtg = (NamePtg) ptg;
|
||||||
|
@ -388,14 +426,12 @@ public class WorkbookEvaluator {
|
||||||
}
|
}
|
||||||
if (ptg instanceof Ref3DPtg) {
|
if (ptg instanceof Ref3DPtg) {
|
||||||
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
||||||
int otherSheetIndex = _workbook.convertFromExternSheetIndex(refPtg.getExternSheetIndex());
|
SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, refPtg);
|
||||||
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
|
|
||||||
return new LazyRefEval(refPtg, sre);
|
return new LazyRefEval(refPtg, sre);
|
||||||
}
|
}
|
||||||
if (ptg instanceof Area3DPtg) {
|
if (ptg instanceof Area3DPtg) {
|
||||||
Area3DPtg aptg = (Area3DPtg) ptg;
|
Area3DPtg aptg = (Area3DPtg) ptg;
|
||||||
int otherSheetIndex = _workbook.convertFromExternSheetIndex(aptg.getExternSheetIndex());
|
SheetRefEvaluator sre = createExternSheetRefEvaluator(tracker, aptg);
|
||||||
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, otherSheetIndex);
|
|
||||||
return new LazyAreaEval(aptg, sre);
|
return new LazyAreaEval(aptg, sre);
|
||||||
}
|
}
|
||||||
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, sheetIndex);
|
SheetRefEvaluator sre = new SheetRefEvaluator(this, tracker, _workbook, sheetIndex);
|
||||||
|
@ -435,7 +471,7 @@ public class WorkbookEvaluator {
|
||||||
} else {
|
} else {
|
||||||
cell = row.getCell(columnIndex);
|
cell = row.getCell(columnIndex);
|
||||||
}
|
}
|
||||||
CellLocation cellLoc = new CellLocation(sheetIndex, rowIndex, columnIndex);
|
CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex);
|
||||||
tracker.acceptDependency(cellLoc);
|
tracker.acceptDependency(cellLoc);
|
||||||
return internalEvaluate(cell, cellLoc, tracker);
|
return internalEvaluate(cell, cellLoc, tracker);
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -43,11 +43,13 @@ import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||||
import org.apache.poi.hssf.record.formula.PercentPtg;
|
import org.apache.poi.hssf.record.formula.PercentPtg;
|
||||||
import org.apache.poi.hssf.record.formula.PowerPtg;
|
import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||||
import org.apache.poi.hssf.record.formula.Ptg;
|
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.RefPtg;
|
||||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||||
import org.apache.poi.hssf.record.formula.SubtractPtg;
|
import org.apache.poi.hssf.record.formula.SubtractPtg;
|
||||||
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
||||||
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
|
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.HSSFCell;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
||||||
|
@ -913,4 +915,33 @@ public final class TestFormulaParser extends TestCase {
|
||||||
|
|
||||||
assertEquals("'true'!B2", cell.getCellFormula());
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ public final class TestSupBookRecord extends TestCase {
|
||||||
|
|
||||||
assertEquals( 34, record.getRecordSize() ); //sid+size+data
|
assertEquals( 34, record.getRecordSize() ); //sid+size+data
|
||||||
|
|
||||||
assertEquals("testURL", record.getURL().getString());
|
assertEquals("testURL", record.getURL());
|
||||||
UnicodeString[] sheetNames = record.getSheetNames();
|
UnicodeString[] sheetNames = record.getSheetNames();
|
||||||
assertEquals(2, sheetNames.length);
|
assertEquals(2, sheetNames.length);
|
||||||
assertEquals("Sheet1", sheetNames[0].getString());
|
assertEquals("Sheet1", sheetNames[0].getString());
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.poi.ss.formula;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
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.AreaErrPtg;
|
||||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||||
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
|
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
|
||||||
|
@ -29,6 +30,10 @@ 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.ErrorEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
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}.
|
* Tests {@link WorkbookEvaluator}.
|
||||||
|
@ -89,4 +94,61 @@ public class TestWorkbookEvaluator extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue