From e92c009814e904c3cca5625e21e1877031e5001a Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Thu, 10 Jun 2010 17:07:06 +0000 Subject: [PATCH] Apply, with a few tweaks, the patch from bug #48996 - initial support for External Name References in HSSF formula evaluation git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@953395 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../org/apache/poi/hssf/dev/BiffViewer.java | 1 + .../poi/hssf/model/InternalWorkbook.java | 9 ++ .../org/apache/poi/hssf/model/LinkTable.java | 14 ++- .../poi/hssf/record/ExternalNameRecord.java | 34 ++++-- .../poi/hssf/record/formula/NameXPtg.java | 8 +- .../usermodel/HSSFEvaluationWorkbook.java | 5 + .../poi/ss/formula/EvaluationWorkbook.java | 21 ++++ .../formula/OperationEvaluationContext.java | 41 +++++++ .../poi/ss/formula/WorkbookEvaluator.java | 20 +++- .../eval/forked/ForkedEvaluationWorkbook.java | 4 + .../usermodel/XSSFEvaluationWorkbook.java | 5 + .../formula/TestExternalNameReference.java | 106 ++++++++++++++++++ .../usermodel/TestHSSFFormulaEvaluator.java | 61 ++++++++++ test-data/spreadsheet/XRefCalc.xls | Bin 0 -> 18944 bytes test-data/spreadsheet/XRefCalcData.xls | Bin 0 -> 23040 bytes 16 files changed, 318 insertions(+), 12 deletions(-) create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/TestExternalNameReference.java create mode 100644 test-data/spreadsheet/XRefCalc.xls create mode 100644 test-data/spreadsheet/XRefCalcData.xls diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 50e37663d..e0cdba7af 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 48996 - initial support for External Name References in HSSF formula evaluation 46664 - fix up Tab IDs when adding new sheets, so that print areas don't end up invalid 45269 - improve replaceText on HWPF ranges 47815 - correct documentation on what happens when you request a String from a non-string Formula cell diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java index aed5c199e..ba79aa80d 100644 --- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java +++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java @@ -162,6 +162,7 @@ public final class BiffViewer { case ExtSSTRecord.sid: return new ExtSSTRecord(in); case ExtendedFormatRecord.sid: return new ExtendedFormatRecord(in); case ExternSheetRecord.sid: return new ExternSheetRecord(in); + case ExternalNameRecord.sid: return new ExternalNameRecord(in); case FeatRecord.sid: return new FeatRecord(in); case FeatHdrRecord.sid: return new FeatHdrRecord(in); case FilePassRecord.sid: return new FilePassRecord(in); diff --git a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java index b1340f524..567b986e7 100644 --- a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java +++ b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java @@ -81,6 +81,7 @@ import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.FormulaShifter; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName; import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet; import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; @@ -1771,6 +1772,14 @@ public final class InternalWorkbook { } return new ExternalSheet(extNames[0], extNames[1]); } + public ExternalName getExternalName(int externSheetIndex, int externNameIndex) { + String nameName = linkTable.resolveNameXText(externSheetIndex, externNameIndex); + if(nameName == null) { + return null; + } + int ix = linkTable.resolveNameXIx(externSheetIndex, externNameIndex); + return new ExternalName(nameName, externNameIndex, ix); + } /** * Finds the sheet index for a particular external sheet number. diff --git a/src/java/org/apache/poi/hssf/model/LinkTable.java b/src/java/org/apache/poi/hssf/model/LinkTable.java index 73b7d02da..10a6b3063 100644 --- a/src/java/org/apache/poi/hssf/model/LinkTable.java +++ b/src/java/org/apache/poi/hssf/model/LinkTable.java @@ -124,6 +124,10 @@ final class LinkTable { public String getNameText(int definedNameIndex) { return _externalNameRecords[definedNameIndex].getText(); } + + public int getNameIx(int definedNameIndex) { + return _externalNameRecords[definedNameIndex].getIx(); + } /** * Performs case-insensitive search @@ -316,8 +320,12 @@ final class LinkTable { if (!ebr.isExternalReferences()) { return null; } + // Sheet name only applies if not a global reference int shIx = _externSheetRecord.getFirstSheetIndexFromRefIndex(extRefIndex); - String usSheetName = ebr.getSheetNames()[shIx]; + String usSheetName = null; + if(shIx >= 0) { + usSheetName = ebr.getSheetNames()[shIx]; + } return new String[] { ebr.getURL(), usSheetName, @@ -419,6 +427,10 @@ final class LinkTable { int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex); return _externalBookBlocks[extBookIndex].getNameText(definedNameIndex); } + public int resolveNameXIx(int refIndex, int definedNameIndex) { + int extBookIndex = _externSheetRecord.getExtbookIndexFromRefIndex(refIndex); + return _externalBookBlocks[extBookIndex].getNameIx(definedNameIndex); + } public NameXPtg getNameXPtg(String name) { // first find any external book block that contains the name: diff --git a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java index 6619da8c7..0156ca067 100644 --- a/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java +++ b/src/java/org/apache/poi/hssf/record/ExternalNameRecord.java @@ -41,7 +41,8 @@ public final class ExternalNameRecord extends StandardRecord { private short field_1_option_flag; - private int field_2_not_used; + private short field_2_ixals; + private short field_3_not_used; private String field_4_name; private Formula field_5_name_definition; @@ -96,6 +97,16 @@ public final class ExternalNameRecord extends StandardRecord { public String getText() { return field_4_name; } + + /** + * If this is a local name, then this is the (1 based) + * index of the name of the Sheet this refers to, as + * defined in the preceeding {@link SupBookRecord}. + * If it isn't a local name, then it must be zero. + */ + public short getIx() { + return field_2_ixals; + } protected int getDataSize(){ int result = 2 + 4; // short and int @@ -114,10 +125,11 @@ public final class ExternalNameRecord extends StandardRecord { public void serialize(LittleEndianOutput out) { out.writeShort(field_1_option_flag); - out.writeInt(field_2_not_used); + out.writeShort(field_2_ixals); + out.writeShort(field_3_not_used); - out.writeByte(field_4_name.length()); - StringUtil.writeUnicodeStringFlagAndData(out, field_4_name); + out.writeByte(field_4_name.length()); + StringUtil.writeUnicodeStringFlagAndData(out, field_4_name); if(!isOLELink() && !isStdDocumentNameIdentifier()){ if(isAutomaticLink()){ @@ -133,7 +145,8 @@ public final class ExternalNameRecord extends StandardRecord { public ExternalNameRecord(RecordInputStream in) { field_1_option_flag = in.readShort(); - field_2_not_used = in.readInt(); + field_2_ixals = in.readShort(); + field_3_not_used = in.readShort(); int numChars = in.readUByte(); field_4_name = StringUtil.readUnicodeString(in, numChars); @@ -166,10 +179,13 @@ public final class ExternalNameRecord extends StandardRecord { public String toString() { StringBuffer sb = new StringBuffer(); - sb.append(getClass().getName()).append(" [EXTERNALNAME "); - sb.append(" ").append(field_4_name); - sb.append(" ix=").append(field_2_not_used); - sb.append("]"); + sb.append("[EXTERNALNAME]\n"); + sb.append(" .ix = ").append(field_2_ixals).append("\n"); + sb.append(" .name = ").append(field_4_name).append("\n"); + if(field_5_name_definition != null) { + sb.append(" .formula = ").append(field_5_name_definition).append("\n"); + } + sb.append("[/EXTERNALNAME]\n"); return sb.toString(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java index de08763c7..5b4176f48 100644 --- a/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/NameXPtg.java @@ -73,7 +73,13 @@ public final class NameXPtg extends OperandPtg implements WorkbookDependentFormu public String toFormulaString() { throw new RuntimeException("3D references need a workbook to determine formula text"); } - + + public String toString(){ + String retValue = "NameXPtg:[sheetRefIndex:" + _sheetRefIndex + + " , nameNumber:" + _nameNumber + "]" ; + return retValue; + } + public byte getDefaultOperandClass() { return Ptg.CLASS_VALUE; } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java index 1f10a926a..2ac66d64e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java @@ -33,6 +33,7 @@ import org.apache.poi.ss.formula.FormulaParseException; import org.apache.poi.ss.formula.FormulaParsingWorkbook; import org.apache.poi.ss.formula.FormulaRenderingWorkbook; import org.apache.poi.ss.formula.FormulaType; +import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName; /** * Internal POI use only @@ -107,6 +108,10 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E public ExternalSheet getExternalSheet(int externSheetIndex) { return _iBook.getExternalSheet(externSheetIndex); } + + public ExternalName getExternalName(int externSheetIndex, int externNameIndex) { + return _iBook.getExternalName(externSheetIndex, externNameIndex); + } public String resolveNameXText(NameXPtg n) { return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex()); diff --git a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java index a3b232571..1f42aa893 100644 --- a/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java @@ -47,6 +47,7 @@ public interface EvaluationWorkbook { */ ExternalSheet getExternalSheet(int externSheetIndex); int convertFromExternSheetIndex(int externSheetIndex); + ExternalName getExternalName(int externSheetIndex, int externNameIndex); EvaluationName getName(NamePtg namePtg); String resolveNameXText(NameXPtg ptg); Ptg[] getFormulaTokens(EvaluationCell cell); @@ -66,4 +67,24 @@ public interface EvaluationWorkbook { return _sheetName; } } + class ExternalName { + private final String _nameName; + private final int _nameNumber; + private final int _ix; + + public ExternalName(String nameName, int nameNumber, int ix) { + _nameName = nameName; + _nameNumber = nameNumber; + _ix = ix; + } + public String getName() { + return _nameName; + } + public int getNumber() { + return _nameNumber; + } + public int getIx() { + return _ix; + } + } } diff --git a/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java b/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java index 147e07d7a..71db36e0c 100644 --- a/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java +++ b/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java @@ -17,10 +17,15 @@ package org.apache.poi.ss.formula; +import org.apache.poi.hssf.record.formula.Area3DPtg; +import org.apache.poi.hssf.record.formula.NameXPtg; +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.formula.Ref3DPtg; import org.apache.poi.hssf.record.formula.eval.*; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; +import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName; import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet; import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference.NameType; @@ -254,4 +259,40 @@ public final class OperationEvaluationContext { SheetRefEvaluator sre = createExternSheetRefEvaluator(extSheetIndex); return new LazyAreaEval(firstRowIndex, firstColumnIndex, lastRowIndex, lastColumnIndex, sre); } + public ValueEval getNameXEval(NameXPtg nameXPtg) { + ExternalSheet externSheet = _workbook.getExternalSheet(nameXPtg.getSheetRefIndex()); + if(externSheet == null) + return new NameXEval(nameXPtg); + String workbookName = externSheet.getWorkbookName(); + ExternalName externName = _workbook.getExternalName( + nameXPtg.getSheetRefIndex(), + nameXPtg.getNameIndex() + ); + try{ + WorkbookEvaluator refWorkbookEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName); + EvaluationName evaluationName = refWorkbookEvaluator.getName(externName.getName(),externName.getIx()-1); + if(evaluationName != null && evaluationName.hasFormula()){ + if (evaluationName.getNameDefinition().length > 1) { + throw new RuntimeException("Complex name formulas not supported yet"); + } + Ptg ptg = evaluationName.getNameDefinition()[0]; + if(ptg instanceof Ref3DPtg){ + Ref3DPtg ref3D = (Ref3DPtg)ptg; + int sheetIndex = refWorkbookEvaluator.getSheetIndexByExternIndex(ref3D.getExternSheetIndex()); + String sheetName = refWorkbookEvaluator.getSheetName(sheetIndex); + SheetRefEvaluator sre = createExternSheetRefEvaluator(workbookName, sheetName); + return new LazyRefEval(ref3D.getRow(), ref3D.getColumn(), sre); + }else if(ptg instanceof Area3DPtg){ + Area3DPtg area3D = (Area3DPtg)ptg; + int sheetIndex = refWorkbookEvaluator.getSheetIndexByExternIndex(area3D.getExternSheetIndex()); + String sheetName = refWorkbookEvaluator.getSheetName(sheetIndex); + SheetRefEvaluator sre = createExternSheetRefEvaluator(workbookName, sheetName); + return new LazyAreaEval(area3D.getFirstRow(), area3D.getFirstColumn(), area3D.getLastRow(), area3D.getLastColumn(), sre); + } + } + return ErrorEval.REF_INVALID; + }catch(WorkbookNotFoundException wnfe){ + return ErrorEval.REF_INVALID; + } + } } diff --git a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java index ad15b84e9..60d31a707 100644 --- a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java @@ -65,6 +65,7 @@ import org.apache.poi.hssf.record.formula.functions.Choose; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; import org.apache.poi.hssf.record.formula.functions.IfFunc; import org.apache.poi.hssf.record.formula.udf.UDFFinder; +import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook; import org.apache.poi.hssf.util.CellReference; import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException; import org.apache.poi.ss.formula.eval.NotImplementedException; @@ -124,6 +125,19 @@ public final class WorkbookEvaluator { /* package */ EvaluationSheet getSheet(int sheetIndex) { return _workbook.getSheet(sheetIndex); } + + /* package */ EvaluationName getName(String name, int sheetIndex) { + NamePtg namePtg = null; + if(_workbook instanceof HSSFEvaluationWorkbook){ + namePtg =((HSSFEvaluationWorkbook)_workbook).getName(name, sheetIndex).createPtg(); + } + + if(namePtg == null) { + return null; + } else { + return _workbook.getName(namePtg); + } + } private static boolean isDebugLogEnabled() { return false; @@ -223,6 +237,10 @@ public final class WorkbookEvaluator { } return result.intValue(); } + + /* package */ int getSheetIndexByExternIndex(int externSheetIndex) { + return _workbook.convertFromExternSheetIndex(externSheetIndex); + } /** @@ -524,7 +542,7 @@ public final class WorkbookEvaluator { throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'"); } if (ptg instanceof NameXPtg) { - return new NameXEval(((NameXPtg) ptg)); + return ec.getNameXEval(((NameXPtg) ptg)); } if (ptg instanceof IntPtg) { diff --git a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java index 08ed7f534..c91600676 100644 --- a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationWorkbook.java @@ -106,6 +106,10 @@ final class ForkedEvaluationWorkbook implements EvaluationWorkbook { public EvaluationSheet getSheet(int sheetIndex) { return getSharedSheet(getSheetName(sheetIndex)); } + + public ExternalName getExternalName(int externSheetIndex, int externNameIndex) { + return _masterBook.getExternalName(externSheetIndex, externNameIndex); + } public int getSheetIndex(EvaluationSheet sheet) { if (sheet instanceof ForkedEvaluationSheet) { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java index c3467fc7e..b2c71fee2 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java @@ -29,6 +29,7 @@ import org.apache.poi.ss.formula.FormulaParser; import org.apache.poi.ss.formula.FormulaParsingWorkbook; import org.apache.poi.ss.formula.FormulaRenderingWorkbook; import org.apache.poi.ss.formula.FormulaType; +import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; /** @@ -94,6 +95,10 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E public String getSheetName(int sheetIndex) { return _uBook.getSheetName(sheetIndex); } + + public ExternalName getExternalName(int externSheetIndex, int externNameIndex) { + throw new RuntimeException("Not implemented yet"); + } public NameXPtg getNameXPtg(String name) { // may require to return null to make tests pass diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestExternalNameReference.java b/src/testcases/org/apache/poi/hssf/record/formula/TestExternalNameReference.java new file mode 100644 index 000000000..6d2b871c3 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestExternalNameReference.java @@ -0,0 +1,106 @@ +/* ==================================================================== + 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; + + +import junit.framework.TestCase; + +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hssf.util.CellReference; +/** + * Tests for proper calculation of named ranges from external workbooks. + * + * + * @author Stephen Wolke (smwolke at geistig.com) + */ +public final class TestExternalNameReference extends TestCase { + double MARKUP_COST = 1.9d; + double MARKUP_COST_1 = 1.8d; + double MARKUP_COST_2 = 1.5d; + double PART_COST = 12.3d; + double NEW_QUANT = 7.0d; + double NEW_PART_COST = 15.3d; + /** + * tests NameXPtg for external cell reference by name and logic in Workbook below that + */ + public void testReadCalcSheet() { + try{ + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("XRefCalc.xls"); + assertEquals("Sheet1!$A$2", wb.getName("QUANT").getRefersToFormula()); + assertEquals("Sheet1!$B$2", wb.getName("PART").getRefersToFormula()); + assertEquals("x123",wb.getSheet("Sheet1").getRow(1).getCell(1).getStringCellValue()); + assertEquals("Sheet1!$C$2", wb.getName("UNITCOST").getRefersToFormula()); + CellReference cellRef = new CellReference(wb.getName("UNITCOST").getRefersToFormula()); + HSSFCell cell = wb.getSheet(cellRef.getSheetName()).getRow(cellRef.getRow()).getCell((int)cellRef.getCol()); + assertEquals("VLOOKUP(PART,COSTS,2,FALSE)",cell.getCellFormula()); + assertEquals("Sheet1!$D$2", wb.getName("COST").getRefersToFormula()); + cellRef = new CellReference(wb.getName("COST").getRefersToFormula()); + cell = wb.getSheet(cellRef.getSheetName()).getRow(cellRef.getRow()).getCell((int)cellRef.getCol()); + assertEquals("UNITCOST*Quant",cell.getCellFormula()); + assertEquals("Sheet1!$E$2", wb.getName("TOTALCOST").getRefersToFormula()); + cellRef = new CellReference(wb.getName("TOTALCOST").getRefersToFormula()); + cell = wb.getSheet(cellRef.getSheetName()).getRow(cellRef.getRow()).getCell((int)cellRef.getCol()); + assertEquals("Cost*Markup_Cost",cell.getCellFormula()); + }catch(Exception e){ + fail(); + } + } + + public void testReadReferencedSheet() { + try{ + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("XRefCalcData.xls"); + assertEquals("CostSheet!$A$2:$B$3", wb.getName("COSTS").getRefersToFormula()); + assertEquals("x123",wb.getSheet("CostSheet").getRow(1).getCell(0).getStringCellValue()); + assertEquals(PART_COST,wb.getSheet("CostSheet").getRow(1).getCell(1).getNumericCellValue()); + assertEquals("MarkupSheet!$B$1", wb.getName("Markup_Cost").getRefersToFormula()); + assertEquals(MARKUP_COST_1,wb.getSheet("MarkupSheet").getRow(0).getCell(1).getNumericCellValue()); + }catch(Exception e){ + fail(); + } + } + + public void testEvaluate() throws Exception { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("XRefCalc.xls"); + HSSFWorkbook wb2 = HSSFTestDataSamples.openSampleWorkbook("XRefCalcData.xls"); + CellReference cellRef = new CellReference(wb.getName("QUANT").getRefersToFormula()); + HSSFCell cell = wb.getSheet(cellRef.getSheetName()).getRow(cellRef.getRow()).getCell((int)cellRef.getCol()); + cell.setCellValue(NEW_QUANT); + cell = wb2.getSheet("CostSheet").getRow(1).getCell(1); + cell.setCellValue(NEW_PART_COST); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); + HSSFFormulaEvaluator evaluatorCost = new HSSFFormulaEvaluator(wb2); + String[] bookNames = { "XRefCalc.xls", "XRefCalcData.xls" }; + HSSFFormulaEvaluator[] evaluators = { evaluator, evaluatorCost, }; + HSSFFormulaEvaluator.setupEnvironment(bookNames, evaluators); + cellRef = new CellReference(wb.getName("UNITCOST").getRefersToFormula()); + HSSFCell uccell = wb.getSheet(cellRef.getSheetName()).getRow(cellRef.getRow()).getCell((int)cellRef.getCol()); + cellRef = new CellReference(wb.getName("COST").getRefersToFormula()); + HSSFCell ccell = wb.getSheet(cellRef.getSheetName()).getRow(cellRef.getRow()).getCell((int)cellRef.getCol()); + cellRef = new CellReference(wb.getName("TOTALCOST").getRefersToFormula()); + HSSFCell tccell = wb.getSheet(cellRef.getSheetName()).getRow(cellRef.getRow()).getCell((int)cellRef.getCol()); + evaluator.evaluateFormulaCell(uccell); + evaluator.evaluateFormulaCell(ccell); + evaluator.evaluateFormulaCell(tccell); + assertEquals(NEW_PART_COST, uccell.getNumericCellValue()); + assertEquals(NEW_PART_COST*NEW_QUANT, ccell.getNumericCellValue()); + assertEquals(NEW_PART_COST*NEW_QUANT*MARKUP_COST_2, tccell.getNumericCellValue()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java index b75fb441f..d1b1a73b0 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java @@ -215,4 +215,65 @@ public final class TestHSSFFormulaEvaluator extends TestCase { assertEquals(3, evalCount); assertEquals(2.0, ((NumberEval)ve).getNumberValue(), 0D); } + + /** + * Ensures that we can handle NameXPtgs in the formulas + * we parse. + */ + public void testXRefs() throws Exception { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("XRefCalc.xls"); + HSSFWorkbook wbData = HSSFTestDataSamples.openSampleWorkbook("XRefCalcData.xls"); + Cell cell; + + // VLookup on a name in another file + cell = wb.getSheetAt(0).getRow(1).getCell(2); + assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType()); + assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCachedFormulaResultType()); + assertEquals(12.30, cell.getNumericCellValue(), 0.0001); + // WARNING - this is wrong! + // The file name should be showing, but bug #45970 is fixed + // we seem to loose it + assertEquals("VLOOKUP(PART,COSTS,2,FALSE)", cell.getCellFormula()); + + + // Simple reference to a name in another file + cell = wb.getSheetAt(0).getRow(1).getCell(4); + assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType()); + assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCachedFormulaResultType()); + assertEquals(36.90, cell.getNumericCellValue(), 0.0001); + // WARNING - this is wrong! + // The file name should be showing, but bug #45970 is fixed + // we seem to loose it + assertEquals("Cost*Markup_Cost", cell.getCellFormula()); + + + // Evaluate the cells + HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb); + HSSFFormulaEvaluator.setupEnvironment( + new String[] { "XRefCalc.xls", "XRefCalcData.xls" }, + new HSSFFormulaEvaluator[] { + eval, + new HSSFFormulaEvaluator(wbData) + } + ); + eval.evaluateFormulaCell( + wb.getSheetAt(0).getRow(1).getCell(2) + ); + eval.evaluateFormulaCell( + wb.getSheetAt(0).getRow(1).getCell(4) + ); + + + // Re-check VLOOKUP one + cell = wb.getSheetAt(0).getRow(1).getCell(2); + assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType()); + assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCachedFormulaResultType()); + assertEquals(12.30, cell.getNumericCellValue(), 0.0001); + + // Re-check ref one + cell = wb.getSheetAt(0).getRow(1).getCell(4); + assertEquals(Cell.CELL_TYPE_FORMULA, cell.getCellType()); + assertEquals(Cell.CELL_TYPE_NUMERIC, cell.getCachedFormulaResultType()); + assertEquals(36.90, cell.getNumericCellValue(), 0.0001); + } } diff --git a/test-data/spreadsheet/XRefCalc.xls b/test-data/spreadsheet/XRefCalc.xls new file mode 100644 index 0000000000000000000000000000000000000000..7da6acfe1e47eb7a95d8b8e7cc5548a2a649cb92 GIT binary patch literal 18944 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 t4bwG#|j`_q+GK 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 literal 0 HcmV?d00001