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 000000000..7da6acfe1 Binary files /dev/null and b/test-data/spreadsheet/XRefCalc.xls differ diff --git a/test-data/spreadsheet/XRefCalcData.xls b/test-data/spreadsheet/XRefCalcData.xls new file mode 100644 index 000000000..677de9839 Binary files /dev/null and b/test-data/spreadsheet/XRefCalcData.xls differ