diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 2818057da..179b0fdac 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + fixed formula parser to correctly resolve sheet-level names 46433 - support for shared formulas in XSSF 46299 - support for carriage return and line break in XWPFRun 46300 - support for line spacing in XWPFParagraph diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index d9b6face6..245f8a1a3 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + fixed formula parser to correctly resolve sheet-level names 46433 - support for shared formulas in XSSF 46299 - support for carriage return and line break in XWPFRun 46300 - support for line spacing in XWPFParagraph diff --git a/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java b/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java index 1b19f8724..f24441841 100644 --- a/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java @@ -44,7 +44,7 @@ public final class HSSFFormulaParser { * Convenience method for parsing cell formulas. see {@link #parse(String, HSSFWorkbook, int)} */ public static Ptg[] parse(String formula, HSSFWorkbook workbook) { - return FormulaParser.parse(formula, createParsingWorkbook(workbook)); + return parse(formula, workbook, FormulaType.CELL); } /** @@ -52,9 +52,23 @@ public final class HSSFFormulaParser { * @return the parsed formula tokens */ public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType) { - return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType); + return parse(formula, workbook, formulaType, -1); } + /** + * @param formula the formula to parse + * @param workbook the parent workbook + * @param formulaType a constant from {@link FormulaType} + * @param sheetIndex the 0-based index of the sheet this formula belongs to. + * The sheet index is required to resolve sheet-level names. -1 means that + * the scope of the name will be ignored and the parser will match named ranges only by name + * + * @return the parsed formula tokens + */ + public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType, int sheetIndex) { + return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType, sheetIndex); + } + /** * Static method to convert an array of {@link Ptg}s in RPN order * to a human readable string format in infix mode. diff --git a/src/java/org/apache/poi/hssf/record/CFRuleRecord.java b/src/java/org/apache/poi/hssf/record/CFRuleRecord.java index 986a0333b..cfaa991e6 100644 --- a/src/java/org/apache/poi/hssf/record/CFRuleRecord.java +++ b/src/java/org/apache/poi/hssf/record/CFRuleRecord.java @@ -23,7 +23,9 @@ import org.apache.poi.hssf.record.cf.FontFormatting; import org.apache.poi.hssf.record.cf.PatternFormatting; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.ss.formula.Formula; +import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.LittleEndianOutput; @@ -132,18 +134,18 @@ public final class CFRuleRecord extends StandardRecord { /** * Creates a new comparison operation rule */ - public static CFRuleRecord create(HSSFWorkbook workbook, String formulaText) { - Ptg[] formula1 = parseFormula(formulaText, workbook); - return new CFRuleRecord(CONDITION_TYPE_FORMULA, ComparisonOperator.NO_COMPARISON, - formula1, null); - } + public static CFRuleRecord create(HSSFSheet sheet, String formulaText) { + Ptg[] formula1 = parseFormula(formulaText, sheet); + return new CFRuleRecord(CONDITION_TYPE_FORMULA, ComparisonOperator.NO_COMPARISON, + formula1, null); + } /** * Creates a new comparison operation rule */ - public static CFRuleRecord create(HSSFWorkbook workbook, byte comparisonOperation, + public static CFRuleRecord create(HSSFSheet sheet, byte comparisonOperation, String formulaText1, String formulaText2) { - Ptg[] formula1 = parseFormula(formulaText1, workbook); - Ptg[] formula2 = parseFormula(formulaText2, workbook); + Ptg[] formula1 = parseFormula(formulaText1, sheet); + Ptg[] formula2 = parseFormula(formulaText2, sheet); return new CFRuleRecord(CONDITION_TYPE_CELL_VALUE_IS, comparisonOperation, formula1, formula2); } @@ -527,10 +529,11 @@ public final class CFRuleRecord extends StandardRecord { * * @return null if formula was null. */ - private static Ptg[] parseFormula(String formula, HSSFWorkbook workbook) { - if(formula == null) { - return null; - } - return HSSFFormulaParser.parse(formula, workbook); - } + private static Ptg[] parseFormula(String formula, HSSFSheet sheet) { + if(formula == null) { + return null; + } + int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet); + return HSSFFormulaParser.parse(formula, sheet.getWorkbook(), FormulaType.CELL, sheetIndex); + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java index 5bc478f42..68561d5a7 100644 --- a/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java +++ b/src/java/org/apache/poi/hssf/usermodel/DVConstraint.java @@ -24,7 +24,6 @@ import org.apache.poi.hssf.model.HSSFFormulaParser; import org.apache.poi.hssf.record.formula.NumberPtg; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.StringPtg; -import org.apache.poi.ss.formula.FormulaParser; import org.apache.poi.ss.formula.FormulaType; /** @@ -324,24 +323,25 @@ public class DVConstraint { /** * @return both parsed formulas (for expression 1 and 2). */ - /* package */ FormulaPair createFormulas(HSSFWorkbook workbook) { + /* package */ FormulaPair createFormulas(HSSFSheet sheet) { Ptg[] formula1; Ptg[] formula2; if (isListValidationType()) { - formula1 = createListFormula(workbook); + formula1 = createListFormula(sheet); formula2 = Ptg.EMPTY_PTG_ARRAY; } else { - formula1 = convertDoubleFormula(_formula1, _value1, workbook); - formula2 = convertDoubleFormula(_formula2, _value2, workbook); + formula1 = convertDoubleFormula(_formula1, _value1, sheet); + formula2 = convertDoubleFormula(_formula2, _value2, sheet); } return new FormulaPair(formula1, formula2); } - private Ptg[] createListFormula(HSSFWorkbook workbook) { + private Ptg[] createListFormula(HSSFSheet sheet) { if (_explicitListValues == null) { - // formula is parsed with slightly different RVA rules: (root node type must be 'reference') - return HSSFFormulaParser.parse(_formula1, workbook, FormulaType.DATAVALIDATION_LIST); + HSSFWorkbook wb = sheet.getWorkbook(); + // formula is parsed with slightly different RVA rules: (root node type must be 'reference') + return HSSFFormulaParser.parse(_formula1, wb, FormulaType.DATAVALIDATION_LIST, wb.getSheetIndex(sheet)); // To do: Excel places restrictions on the available operations within a list formula. // Some things like union and intersection are not allowed. } @@ -361,7 +361,7 @@ public class DVConstraint { * @return The parsed token array representing the formula or value specified. * Empty array if both formula and value are null */ - private static Ptg[] convertDoubleFormula(String formula, Double value, HSSFWorkbook workbook) { + private static Ptg[] convertDoubleFormula(String formula, Double value, HSSFSheet sheet) { if (formula == null) { if (value == null) { return Ptg.EMPTY_PTG_ARRAY; @@ -371,7 +371,8 @@ public class DVConstraint { if (value != null) { throw new IllegalStateException("Both formula and value cannot be present"); } - return HSSFFormulaParser.parse(formula, workbook); + HSSFWorkbook wb = sheet.getWorkbook(); + return HSSFFormulaParser.parse(formula, wb, FormulaType.CELL, wb.getSheetIndex(sheet)); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index 838a0db95..2e7431bcc 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -54,6 +54,7 @@ import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.RichTextString; +import org.apache.poi.ss.formula.FormulaType; /** * High level representation of a cell in a row of a spreadsheet. @@ -269,7 +270,7 @@ public class HSSFCell implements Cell { public short getCellNum() { return (short) getColumnIndex(); } - + public int getColumnIndex() { return record.getColumn() & 0xFFFF; } @@ -594,7 +595,8 @@ public class HSSFCell implements Cell { setCellType(CELL_TYPE_BLANK, false, row, col, styleIndex); return; } - Ptg[] ptgs = HSSFFormulaParser.parse(formula, book); + int sheetIndex = book.getSheetIndex(sheet); + Ptg[] ptgs = HSSFFormulaParser.parse(formula, book, FormulaType.CELL, sheetIndex); setCellType(CELL_TYPE_FORMULA, false, row, col, styleIndex); FormulaRecordAggregate agg = (FormulaRecordAggregate) record; FormulaRecord frec = agg.getFormulaRecord(); @@ -874,7 +876,7 @@ public class HSSFCell implements Cell { * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#getCellStyleAt(short) */ public void setCellStyle(CellStyle style) { - setCellStyle( (HSSFCellStyle)style ); + setCellStyle( (HSSFCellStyle)style ); } public void setCellStyle(HSSFCellStyle style) { // Verify it really does belong to our workbook @@ -1111,8 +1113,8 @@ public class HSSFCell implements Cell { * @param hyperlink hypelrink associated with this cell */ public void setHyperlink(Hyperlink hyperlink){ - HSSFHyperlink link = (HSSFHyperlink)hyperlink; - + HSSFHyperlink link = (HSSFHyperlink)hyperlink; + link.setFirstRow(record.getRow()); link.setLastRow(record.getRow()); link.setFirstColumn(record.getColumn()); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java index 0dfcbbfa6..288c8ac6e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFDataValidation.java @@ -219,9 +219,9 @@ public final class HSSFDataValidation { return _error_text; } - public DVRecord createDVRecord(HSSFWorkbook workbook) { + public DVRecord createDVRecord(HSSFSheet sheet) { - FormulaPair fp = _constraint.createFormulas(workbook); + FormulaPair fp = _constraint.createFormulas(sheet); return new DVRecord(_constraint.getValidationType(), _constraint.getOperator(), diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java index 9830b9f27..046a9c293 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java @@ -24,12 +24,7 @@ import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.formula.NamePtg; import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.ss.formula.EvaluationCell; -import org.apache.poi.ss.formula.EvaluationName; -import org.apache.poi.ss.formula.EvaluationSheet; -import org.apache.poi.ss.formula.EvaluationWorkbook; -import org.apache.poi.ss.formula.FormulaParsingWorkbook; -import org.apache.poi.ss.formula.FormulaRenderingWorkbook; +import org.apache.poi.ss.formula.*; /** * Internal POI use only @@ -38,118 +33,125 @@ import org.apache.poi.ss.formula.FormulaRenderingWorkbook; */ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook { - private final HSSFWorkbook _uBook; - private final Workbook _iBook; - - public static HSSFEvaluationWorkbook create(HSSFWorkbook book) { - if (book == null) { - return null; - } - return new HSSFEvaluationWorkbook(book); - } + private final HSSFWorkbook _uBook; + private final Workbook _iBook; - private HSSFEvaluationWorkbook(HSSFWorkbook book) { - _uBook = book; - _iBook = book.getWorkbook(); - } + public static HSSFEvaluationWorkbook create(HSSFWorkbook book) { + if (book == null) { + return null; + } + return new HSSFEvaluationWorkbook(book); + } - public int getExternalSheetIndex(String sheetName) { - int sheetIndex = _uBook.getSheetIndex(sheetName); - return _iBook.checkExternSheet(sheetIndex); - } - public int getExternalSheetIndex(String workbookName, String sheetName) { - return _iBook.getExternalSheetIndex(workbookName, sheetName); - } + private HSSFEvaluationWorkbook(HSSFWorkbook book) { + _uBook = book; + _iBook = book.getWorkbook(); + } - public NameXPtg getNameXPtg(String name) { - return _iBook.getNameXPtg(name); - } + public int getExternalSheetIndex(String sheetName) { + int sheetIndex = _uBook.getSheetIndex(sheetName); + return _iBook.checkExternSheet(sheetIndex); + } + public int getExternalSheetIndex(String workbookName, String sheetName) { + return _iBook.getExternalSheetIndex(workbookName, sheetName); + } - public EvaluationName getName(String name) { - for(int i=0; i < _iBook.getNumNames(); i++) { - NameRecord nr = _iBook.getNameRecord(i); - if (name.equalsIgnoreCase(nr.getNameText())) { - return new Name(nr, i); - } - } - return null; - } + public NameXPtg getNameXPtg(String name) { + return _iBook.getNameXPtg(name); + } - public int getSheetIndex(EvaluationSheet evalSheet) { - HSSFSheet sheet = ((HSSFEvaluationSheet)evalSheet).getHSSFSheet(); - return _uBook.getSheetIndex(sheet); - } - public int getSheetIndex(String sheetName) { - return _uBook.getSheetIndex(sheetName); - } + /** + * Lookup a named range by its name. + * + * @param name the name to search + * @param sheetIndex the 0-based index of the sheet this formula belongs to. + * The sheet index is required to resolve sheet-level names. -1 means workbook-global names + */ + public EvaluationName getName(String name, int sheetIndex) { + for(int i=0; i < _iBook.getNumNames(); i++) { + NameRecord nr = _iBook.getNameRecord(i); + if (nr.getSheetNumber() == sheetIndex+1 && name.equalsIgnoreCase(nr.getNameText())) { + return new Name(nr, i); + } + } + return sheetIndex == -1 ? null : getName(name, -1); + } - public String getSheetName(int sheetIndex) { - return _uBook.getSheetName(sheetIndex); - } + public int getSheetIndex(EvaluationSheet evalSheet) { + HSSFSheet sheet = ((HSSFEvaluationSheet)evalSheet).getHSSFSheet(); + return _uBook.getSheetIndex(sheet); + } + public int getSheetIndex(String sheetName) { + return _uBook.getSheetIndex(sheetName); + } - public EvaluationSheet getSheet(int sheetIndex) { - return new HSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex)); - } - public int convertFromExternSheetIndex(int externSheetIndex) { - return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex); - } + public String getSheetName(int sheetIndex) { + return _uBook.getSheetName(sheetIndex); + } - public ExternalSheet getExternalSheet(int externSheetIndex) { - return _iBook.getExternalSheet(externSheetIndex); - } + public EvaluationSheet getSheet(int sheetIndex) { + return new HSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex)); + } + public int convertFromExternSheetIndex(int externSheetIndex) { + return _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex); + } - public String resolveNameXText(NameXPtg n) { - return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex()); - } + public ExternalSheet getExternalSheet(int externSheetIndex) { + return _iBook.getExternalSheet(externSheetIndex); + } - public String getSheetNameByExternSheet(int externSheetIndex) { - return _iBook.findSheetNameFromExternSheet(externSheetIndex); - } - public String getNameText(NamePtg namePtg) { - return _iBook.getNameRecord(namePtg.getIndex()).getNameText(); - } - public EvaluationName getName(NamePtg namePtg) { - int ix = namePtg.getIndex(); - return new Name(_iBook.getNameRecord(ix), ix); - } - public Ptg[] getFormulaTokens(EvaluationCell evalCell) { - HSSFCell cell = ((HSSFEvaluationCell)evalCell).getHSSFCell(); - if (false) { - // re-parsing the formula text also works, but is a waste of time - // It is useful from time to time to run all unit tests with this code - // to make sure that all formulas POI can evaluate can also be parsed. - return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook); - } - FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord(); - return fra.getFormulaTokens(); - } + public String resolveNameXText(NameXPtg n) { + return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex()); + } - private static final class Name implements EvaluationName { + public String getSheetNameByExternSheet(int externSheetIndex) { + return _iBook.findSheetNameFromExternSheet(externSheetIndex); + } + public String getNameText(NamePtg namePtg) { + return _iBook.getNameRecord(namePtg.getIndex()).getNameText(); + } + public EvaluationName getName(NamePtg namePtg) { + int ix = namePtg.getIndex(); + return new Name(_iBook.getNameRecord(ix), ix); + } + public Ptg[] getFormulaTokens(EvaluationCell evalCell) { + HSSFCell cell = ((HSSFEvaluationCell)evalCell).getHSSFCell(); + if (false) { + // re-parsing the formula text also works, but is a waste of time + // It is useful from time to time to run all unit tests with this code + // to make sure that all formulas POI can evaluate can also be parsed. + return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet())); + } + FormulaRecordAggregate fra = (FormulaRecordAggregate) cell.getCellValueRecord(); + return fra.getFormulaTokens(); + } - private final NameRecord _nameRecord; - private final int _index; + private static final class Name implements EvaluationName { - public Name(NameRecord nameRecord, int index) { - _nameRecord = nameRecord; - _index = index; - } - public Ptg[] getNameDefinition() { - return _nameRecord.getNameDefinition(); - } - public String getNameText() { - return _nameRecord.getNameText(); - } - public boolean hasFormula() { - return _nameRecord.hasFormula(); - } - public boolean isFunctionName() { - return _nameRecord.isFunctionName(); - } - public boolean isRange() { - return _nameRecord.hasFormula(); // TODO - is this right? - } - public NamePtg createPtg() { - return new NamePtg(_index); - } - } + private final NameRecord _nameRecord; + private final int _index; + + public Name(NameRecord nameRecord, int index) { + _nameRecord = nameRecord; + _index = index; + } + public Ptg[] getNameDefinition() { + return _nameRecord.getNameDefinition(); + } + public String getNameText() { + return _nameRecord.getNameText(); + } + public boolean hasFormula() { + return _nameRecord.hasFormula(); + } + public boolean isFunctionName() { + return _nameRecord.isFunctionName(); + } + public boolean isRange() { + return _nameRecord.hasFormula(); // TODO - is this right? + } + public NamePtg createPtg() { + return new NamePtg(_index); + } + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java index 1865eaf0b..8c21ae996 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java @@ -163,7 +163,7 @@ public final class HSSFName implements Name { * @throws IllegalArgumentException if the specified reference is unparsable */ public void setRefersToFormula(String formulaText) { - Ptg[] ptgs = HSSFFormulaParser.parse(formulaText, _book, FormulaType.NAMEDRANGE); + Ptg[] ptgs = HSSFFormulaParser.parse(formulaText, _book, FormulaType.NAMEDRANGE, getSheetIndex()); _definedNameRec.setNameDefinition(ptgs); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 08abbb61f..ac8405946 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -367,7 +367,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { } DataValidityTable dvt = sheet.getOrCreateDataValidityTable(); - DVRecord dvRecord = dataValidation.createDVRecord(workbook); + DVRecord dvRecord = dataValidation.createDVRecord(this); dvt.addDataValidation(dvRecord); } @@ -1789,6 +1789,6 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { } public HSSFSheetConditionalFormatting getSheetConditionalFormatting() { - return new HSSFSheetConditionalFormatting(workbook, sheet); + return new HSSFSheetConditionalFormatting(this); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java index afaeb8031..f3c93611e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheetConditionalFormatting.java @@ -31,13 +31,13 @@ import org.apache.poi.ss.util.CellRangeAddress; */ public final class HSSFSheetConditionalFormatting { - private final HSSFWorkbook _workbook; + private final HSSFSheet _sheet; private final ConditionalFormattingTable _conditionalFormattingTable; - /* package */ HSSFSheetConditionalFormatting(HSSFWorkbook workbook, Sheet sheet) { - _workbook = workbook; - _conditionalFormattingTable = sheet.getConditionalFormattingTable(); - } + /* package */ HSSFSheetConditionalFormatting(HSSFSheet sheet) { + _sheet = sheet; + _conditionalFormattingTable = sheet.getSheet().getConditionalFormattingTable(); + } /** * A factory method allowing to create a conditional formatting rule @@ -67,8 +67,8 @@ public final class HSSFSheetConditionalFormatting { String formula1, String formula2) { - HSSFWorkbook wb = _workbook; - CFRuleRecord rr = CFRuleRecord.create(wb, comparisonOperation, formula1, formula2); + HSSFWorkbook wb = _sheet.getWorkbook(); + CFRuleRecord rr = CFRuleRecord.create(_sheet, comparisonOperation, formula1, formula2); return new HSSFConditionalFormattingRule(wb, rr); } @@ -80,8 +80,8 @@ public final class HSSFSheetConditionalFormatting { * @param formula - formula for the valued, compared with the cell */ public HSSFConditionalFormattingRule createConditionalFormattingRule(String formula) { - HSSFWorkbook wb = _workbook; - CFRuleRecord rr = CFRuleRecord.create(wb, formula); + HSSFWorkbook wb = _sheet.getWorkbook(); + CFRuleRecord rr = CFRuleRecord.create(_sheet, formula); return new HSSFConditionalFormattingRule(wb, rr); } @@ -171,7 +171,7 @@ public final class HSSFSheetConditionalFormatting { if (cf == null) { return null; } - return new HSSFConditionalFormatting(_workbook, cf); + return new HSSFConditionalFormatting(_sheet.getWorkbook(), cf); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 3b47ea31b..bb2a23bf5 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -66,6 +66,7 @@ import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; +import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -1381,7 +1382,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm sb.append("!"); sb.append(parts[i]); } - name.setNameDefinition(HSSFFormulaParser.parse(sb.toString(), this)); + name.setNameDefinition(HSSFFormulaParser.parse(sb.toString(), this, FormulaType.CELL, sheetIndex)); } /** diff --git a/src/java/org/apache/poi/ss/formula/FormulaParser.java b/src/java/org/apache/poi/ss/formula/FormulaParser.java index 4551c3585..1a3edcdd5 100644 --- a/src/java/org/apache/poi/ss/formula/FormulaParser.java +++ b/src/java/org/apache/poi/ss/formula/FormulaParser.java @@ -142,6 +142,7 @@ public final class FormulaParser { private FormulaParsingWorkbook _book; + private int _sheetIndex; /** @@ -156,11 +157,12 @@ public final class FormulaParser { * model.Workbook, then use the convenience method on * usermodel.HSSFFormulaEvaluator */ - private FormulaParser(String formula, FormulaParsingWorkbook book){ + private FormulaParser(String formula, FormulaParsingWorkbook book, int sheetIndex){ _formulaString = formula; _pointer=0; _book = book; _formulaLength = _formulaString.length(); + _sheetIndex = sheetIndex; } public static Ptg[] parse(String formula, FormulaParsingWorkbook book) { @@ -168,7 +170,24 @@ public final class FormulaParser { } public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType) { - FormulaParser fp = new FormulaParser(formula, workbook); + return parse(formula, workbook, formulaType, -1); + } + + /** + * Parse a formula into a array of tokens + * + * @param formula the formula to parse + * @param workbook the parent workbook + * @param formulaType the type of the formula, see {@link FormulaType} + * @param sheetIndex the 0-based index of the sheet this formula belongs to. + * The sheet index is required to resolve sheet-level names. -1 means that + * the scope of the name will be ignored and the parser will match names only by name + * + * @return array of parsed tokens + * @throws FormulaParseException if the formula is unparsable + */ + public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType, int sheetIndex) { + FormulaParser fp = new FormulaParser(formula, workbook, sheetIndex); fp.parse(); return fp.getRPNPtg(formulaType); } @@ -413,7 +432,7 @@ public final class FormulaParser { new FormulaParseException("Name '" + name + "' does not look like a cell reference or named range"); } - EvaluationName evalName = _book.getName(name); + EvaluationName evalName = _book.getName(name, _sheetIndex); if (evalName == null) { throw new FormulaParseException("Specified named range '" + name + "' does not exist in the current workbook."); @@ -516,7 +535,7 @@ public final class FormulaParser { // user defined function // in the token tree, the name is more or less the first argument - EvaluationName hName = _book.getName(name); + EvaluationName hName = _book.getName(name, _sheetIndex); if (hName == null) { nameToken = _book.getNameXPtg(name); diff --git a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java index e9be7d1d3..57f0f7a2c 100644 --- a/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java +++ b/src/java/org/apache/poi/ss/formula/FormulaParsingWorkbook.java @@ -30,7 +30,7 @@ public interface FormulaParsingWorkbook { /** * named range name matching is case insensitive */ - EvaluationName getName(String name); + EvaluationName getName(String name, int sheetIndex); NameXPtg getNameXPtg(String name); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index 53db06910..0d89bfee2 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -347,8 +347,9 @@ public final class XSSFCell implements Cell { throw new IllegalStateException("Shared Formula not found for group index " + idx); } String sharedFormula = sfCell.getCTCell().getF().getStringValue(); + int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet); XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(sheet.getWorkbook()); - Ptg[] ptgs = FormulaParser.parse(sharedFormula, fpb); + Ptg[] ptgs = FormulaParser.parse(sharedFormula, fpb, FormulaType.CELL, sheetIndex); Ptg[] fmla = SharedFormulaRecord.convertSharedFormulas(ptgs, getRowIndex() - sfCell.getRowIndex(), getColumnIndex() - sfCell.getColumnIndex()); return FormulaRenderer.toFormulaString(fpb, fmla); @@ -371,9 +372,10 @@ public final class XSSFCell implements Cell { return; } - XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(row.getSheet().getWorkbook()); + XSSFWorkbook wb = row.getSheet().getWorkbook(); + XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); try { - Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL); + Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, wb.getSheetIndex(getSheet())); } catch (RuntimeException e) { if (e.getClass().getName().startsWith(FormulaParser.class.getName())) { throw new IllegalArgumentException("Unparsable formula '" + formula + "'", e); 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 fdcebbcc9..f733a67bd 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationWorkbook.java @@ -20,13 +20,7 @@ package org.apache.poi.xssf.usermodel; import org.apache.poi.hssf.record.formula.NamePtg; import org.apache.poi.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.Ptg; -import org.apache.poi.ss.formula.EvaluationCell; -import org.apache.poi.ss.formula.EvaluationName; -import org.apache.poi.ss.formula.EvaluationSheet; -import org.apache.poi.ss.formula.EvaluationWorkbook; -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.*; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName; /** @@ -73,15 +67,16 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E return convertToExternalSheetIndex(sheetIndex); } - public EvaluationName getName(String name) { - for(int i=0; i < _uBook.getNumberOfNames(); i++) { - String nameText = _uBook.getNameAt(i).getNameName(); - if (name.equalsIgnoreCase(nameText)) { - return new Name(_uBook.getNameAt(i), i, this); - } - } - return null; - } + public EvaluationName getName(String name, int sheetIndex) { + for(int i=0; i < _uBook.getNumberOfNames(); i++) { + XSSFName nm = _uBook.getNameAt(i); + String nameText = nm.getNameName(); + if (name.equalsIgnoreCase(nameText) && nm.getSheetIndex() == sheetIndex) { + return new Name(_uBook.getNameAt(i), i, this); + } + } + return sheetIndex == -1 ? null : getName(name, -1); + } public int getSheetIndex(EvaluationSheet evalSheet) { XSSFSheet sheet = ((XSSFEvaluationSheet)evalSheet).getXSSFSheet(); @@ -135,7 +130,7 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E public Ptg[] getFormulaTokens(EvaluationCell evalCell) { XSSFCell cell = ((XSSFEvaluationCell)evalCell).getXSSFCell(); XSSFEvaluationWorkbook frBook = XSSFEvaluationWorkbook.create(_uBook); - return FormulaParser.parse(cell.getCellFormula(), frBook); + return FormulaParser.parse(cell.getCellFormula(), frBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet())); } private static final class Name implements EvaluationName { @@ -152,7 +147,7 @@ public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E public Ptg[] getNameDefinition() { - return FormulaParser.parse(_nameRecord.getRefersToFormula(), _fpBook); + return FormulaParser.parse(_nameRecord.getRefersToFormula(), _fpBook, FormulaType.NAMEDRANGE, _nameRecord.getSheetIndex()); } public String getNameText() { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java index 85e919bef..47fc9ba04 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFName.java @@ -199,7 +199,7 @@ public final class XSSFName implements Name { public void setRefersToFormula(String formulaText) { XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(workbook); try { - Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE); + Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex()); } catch (RuntimeException e) { if (e.getClass().getName().startsWith(FormulaParser.class.getName())) { throw new IllegalArgumentException("Unparsable formula '" + formulaText + "'", e); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java index b9035db6b..f1ebf5f65 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java @@ -121,4 +121,41 @@ public final class TestXSSFFormulaEvaluation extends TestCase { assertEquals("B5", cell.getCellFormula()); assertEquals("UniqueDocumentNumberID", evaluator.evaluate(cell).getStringValue()); } + + /** + * Test creation / evaluation of formulas with sheet-level names + */ + public void testSheetLevelFormulas(){ + XSSFWorkbook wb = new XSSFWorkbook(); + + XSSFRow row; + XSSFSheet sh1 = wb.createSheet("Sheet1"); + XSSFName nm1 = wb.createName(); + nm1.setNameName("sales_1"); + nm1.setSheetIndex(0); + nm1.setRefersToFormula("Sheet1!$A$1"); + row = sh1.createRow(0); + row.createCell(0).setCellValue(3); + row.createCell(1).setCellFormula("sales_1"); + row.createCell(2).setCellFormula("sales_1*2"); + + XSSFSheet sh2 = wb.createSheet("Sheet2"); + XSSFName nm2 = wb.createName(); + nm2.setNameName("sales_1"); + nm2.setSheetIndex(1); + nm2.setRefersToFormula("Sheet2!$A$1"); + + row = sh2.createRow(0); + row.createCell(0).setCellValue(5); + row.createCell(1).setCellFormula("sales_1"); + row.createCell(2).setCellFormula("sales_1*3"); + + XSSFFormulaEvaluator evaluator = new XSSFFormulaEvaluator(wb); + assertEquals(3.0, evaluator.evaluate(sh1.getRow(0).getCell(1)).getNumberValue()); + assertEquals(6.0, evaluator.evaluate(sh1.getRow(0).getCell(2)).getNumberValue()); + + assertEquals(5.0, evaluator.evaluate(sh2.getRow(0).getCell(1)).getNumberValue()); + assertEquals(15.0, evaluator.evaluate(sh2.getRow(0).getCell(2)).getNumberValue()); + } + } diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index c8a46a8dd..b951ae3b6 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -899,11 +899,11 @@ public final class TestFormulaParser extends TestCase { wb.setSheetName(0, "Sheet1"); cell.setCellFormula("Sheet1!B$4:Sheet1!$C1"); // explicit range ':' operator - assertEquals("Sheet1!B$4:Sheet1!$C1", cell.getCellFormula()); + assertEquals("Sheet1!B$4:Sheet1!$C1", cell.getCellFormula()); cell.setCellFormula("Sheet1!B$4:$C1"); // plain area ref assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); // note - area ref is normalised - + cell.setCellFormula("Sheet1!$C1...B$4"); // different syntax for plain area ref assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); @@ -922,7 +922,7 @@ public final class TestFormulaParser extends TestCase { assertEquals("'true'!B2", cell.getCellFormula()); } - + public void testParseExternalWorkbookReference() { HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls"); HSSFCell cell = wbA.getSheetAt(0).getRow(0).getCell(0); @@ -931,15 +931,15 @@ public final class TestFormulaParser extends TestCase { 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()); @@ -950,13 +950,13 @@ public final class TestFormulaParser extends TestCase { assertEquals(Ref3DPtg.class, ptg0.getClass()); assertEquals(expectedExternSheetIndex, ((Ref3DPtg)ptg0).getExternSheetIndex()); } - + public void testUnion() { String formula = "Sheet1!$B$2:$C$3,OFFSET(Sheet1!$E$2:$E$4,1,Sheet1!$A$1),Sheet1!$D$6"; HSSFWorkbook wb = new HSSFWorkbook(); wb.createSheet("Sheet1"); Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb)); - + Class[] expectedClasses = { // TODO - AttrPtg.class, // Excel prepends this MemFuncPtg.class, diff --git a/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java b/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java index 1e54031f9..744c692f0 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestCFRuleRecord.java @@ -28,6 +28,7 @@ import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.RefNPtg; import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.util.LittleEndian; import org.apache.poi.ss.formula.Formula; @@ -43,20 +44,21 @@ public final class TestCFRuleRecord extends TestCase public void testConstructors () { HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet(); - CFRuleRecord rule1 = CFRuleRecord.create(workbook, "7"); + CFRuleRecord rule1 = CFRuleRecord.create(sheet, "7"); assertEquals(CFRuleRecord.CONDITION_TYPE_FORMULA, rule1.getConditionType()); assertEquals(ComparisonOperator.NO_COMPARISON, rule1.getComparisonOperation()); assertNotNull(rule1.getParsedExpression1()); assertSame(Ptg.EMPTY_PTG_ARRAY, rule1.getParsedExpression2()); - CFRuleRecord rule2 = CFRuleRecord.create(workbook, ComparisonOperator.BETWEEN, "2", "5"); + CFRuleRecord rule2 = CFRuleRecord.create(sheet, ComparisonOperator.BETWEEN, "2", "5"); assertEquals(CFRuleRecord.CONDITION_TYPE_CELL_VALUE_IS, rule2.getConditionType()); assertEquals(ComparisonOperator.BETWEEN, rule2.getComparisonOperation()); assertNotNull(rule2.getParsedExpression1()); assertNotNull(rule2.getParsedExpression2()); - CFRuleRecord rule3 = CFRuleRecord.create(workbook, ComparisonOperator.EQUAL, null, null); + CFRuleRecord rule3 = CFRuleRecord.create(sheet, ComparisonOperator.EQUAL, null, null); assertEquals(CFRuleRecord.CONDITION_TYPE_CELL_VALUE_IS, rule3.getConditionType()); assertEquals(ComparisonOperator.EQUAL, rule3.getComparisonOperation()); assertSame(Ptg.EMPTY_PTG_ARRAY, rule3.getParsedExpression2()); @@ -66,7 +68,8 @@ public final class TestCFRuleRecord extends TestCase public void testCreateCFRuleRecord () { HSSFWorkbook workbook = new HSSFWorkbook(); - CFRuleRecord record = CFRuleRecord.create(workbook, "7"); + HSSFSheet sheet = workbook.createSheet(); + CFRuleRecord record = CFRuleRecord.create(sheet, "7"); testCFRuleRecord(record); // Serialize @@ -306,7 +309,8 @@ public final class TestCFRuleRecord extends TestCase public void testWrite() { HSSFWorkbook workbook = new HSSFWorkbook(); - CFRuleRecord rr = CFRuleRecord.create(workbook, ComparisonOperator.BETWEEN, "5", "10"); + HSSFSheet sheet = workbook.createSheet(); + CFRuleRecord rr = CFRuleRecord.create(sheet, ComparisonOperator.BETWEEN, "5", "10"); PatternFormatting patternFormatting = new PatternFormatting(); patternFormatting.setFillPattern(PatternFormatting.BRICKS); diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java index b3c864cdb..1e153bc45 100644 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestCFRecordsAggregate.java @@ -31,6 +31,7 @@ import org.apache.poi.hssf.record.CFRuleRecord; import org.apache.poi.hssf.record.RecordFactory; import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.util.LittleEndian; @@ -46,11 +47,13 @@ public final class TestCFRecordsAggregate extends TestCase public void testCFRecordsAggregate() { HSSFWorkbook workbook = new HSSFWorkbook(); - List recs = new ArrayList(); + HSSFSheet sheet = workbook.createSheet(); + + List recs = new ArrayList(); CFHeaderRecord header = new CFHeaderRecord(); - CFRuleRecord rule1 = CFRuleRecord.create(workbook, "7"); - CFRuleRecord rule2 = CFRuleRecord.create(workbook, ComparisonOperator.BETWEEN, "2", "5"); - CFRuleRecord rule3 = CFRuleRecord.create(workbook, ComparisonOperator.GE, "100", null); + CFRuleRecord rule1 = CFRuleRecord.create(sheet, "7"); + CFRuleRecord rule2 = CFRuleRecord.create(sheet, ComparisonOperator.BETWEEN, "2", "5"); + CFRuleRecord rule3 = CFRuleRecord.create(sheet, ComparisonOperator.GE, "100", null); header.setNumberOfConditionalFormats(3); CellRangeAddress[] cellRanges = { new CellRangeAddress(0,1,0,0), @@ -107,13 +110,14 @@ public final class TestCFRecordsAggregate extends TestCase */ public void testNRules() { HSSFWorkbook workbook = new HSSFWorkbook(); + HSSFSheet sheet = workbook.createSheet(); CellRangeAddress[] cellRanges = { new CellRangeAddress(0,1,0,0), new CellRangeAddress(0,1,2,2), }; CFRuleRecord[] rules = { - CFRuleRecord.create(workbook, "7"), - CFRuleRecord.create(workbook, ComparisonOperator.BETWEEN, "2", "5"), + CFRuleRecord.create(sheet, "7"), + CFRuleRecord.create(sheet, ComparisonOperator.BETWEEN, "2", "5"), }; CFRecordsAggregate agg = new CFRecordsAggregate(cellRanges, rules); byte[] serializedRecord = new byte[agg.getRecordSize()]; diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java index 03f47d681..c38a555ed 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java @@ -25,8 +25,12 @@ import java.util.Date; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.model.HSSFFormulaParser; +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.formula.NamePtg; import org.apache.poi.hssf.util.CellReference; import org.apache.poi.util.TempFile; +import org.apache.poi.ss.formula.FormulaType; /** * @author Andrew C. Oliver (acoliver at apache dot org) @@ -182,11 +186,11 @@ public final class TestFormulas extends TestCase { // don't know how to check correct result .. for the moment, we just verify that the file can be read. for (int x = 1; x < Short.MAX_VALUE && x > 0; x=(short)(x*2)) { - HSSFRow r = s.getRow(x); + HSSFRow r = s.getRow(x); for (int y = 1; y < 256 && y > 0; y=(short)(y+2)) { - HSSFCell c = r.getCell(y); + HSSFCell c = r.getCell(y); assertTrue("got a formula",c.getCellFormula()!=null); assertTrue("loop Formula is as expected "+x+"."+y+operator+y+"."+x+"!="+c.getCellFormula(),( @@ -535,9 +539,9 @@ public final class TestFormulas extends TestCase { public void testAbsRefs() { HSSFWorkbook wb = new HSSFWorkbook(); - HSSFSheet s = wb.createSheet(); - HSSFRow r; - HSSFCell c; + HSSFSheet s = wb.createSheet(); + HSSFRow r; + HSSFCell c; r = s.createRow(0); c = r.createCell(0); @@ -884,4 +888,51 @@ public final class TestFormulas extends TestCase { assertEquals("DZ2*2", wb.getSheetAt(0).getRow(1).getCell(128).toString()); assertEquals("B32770*2", wb.getSheetAt(0).getRow(32768).getCell(1).toString()); } + + /** + * Test creation / evaluation of formulas with sheet-level names + */ + public void testSheetLevelFormulas(){ + HSSFWorkbook wb = new HSSFWorkbook(); + + HSSFRow row; + HSSFSheet sh1 = wb.createSheet("Sheet1"); + HSSFName nm1 = wb.createName(); + nm1.setNameName("sales_1"); + nm1.setSheetIndex(0); + nm1.setRefersToFormula("Sheet1!$A$1"); + row = sh1.createRow(0); + row.createCell(0).setCellValue(3); + row.createCell(1).setCellFormula("sales_1"); + row.createCell(2).setCellFormula("sales_1*2"); + + + HSSFSheet sh2 = wb.createSheet("Sheet2"); + HSSFName nm2 = wb.createName(); + nm2.setNameName("sales_1"); + nm2.setSheetIndex(1); + nm2.setRefersToFormula("Sheet2!$A$1"); + + row = sh2.createRow(0); + row.createCell(0).setCellValue(5); + row.createCell(1).setCellFormula("sales_1"); + row.createCell(2).setCellFormula("sales_1*3"); + + //check that NamePtg refers to the correct NameRecord + Ptg[] ptgs1 = HSSFFormulaParser.parse("sales_1", wb, FormulaType.CELL, 0); + NamePtg nPtg1 = (NamePtg)ptgs1[0]; + assertSame(nm1, wb.getNameAt(nPtg1.getIndex())); + + Ptg[] ptgs2 = HSSFFormulaParser.parse("sales_1", wb, FormulaType.CELL, 1); + NamePtg nPtg2 = (NamePtg)ptgs2[0]; + assertSame(nm2, wb.getNameAt(nPtg2.getIndex())); + + //check that the formula evaluator returns the correct result + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); + assertEquals(3.0, evaluator.evaluate(sh1.getRow(0).getCell(1)).getNumberValue()); + assertEquals(6.0, evaluator.evaluate(sh1.getRow(0).getCell(2)).getNumberValue()); + + assertEquals(5.0, evaluator.evaluate(sh2.getRow(0).getCell(1)).getNumberValue()); + assertEquals(15.0, evaluator.evaluate(sh2.getRow(0).getCell(2)).getNumberValue()); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java b/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java index 4ad6b5793..a0ef4fabc 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java @@ -543,7 +543,7 @@ public final class TestNamedRange extends TestCase { HSSFWorkbook wb = new HSSFWorkbook(); wb.createSheet("CSCO"); - Ptg[] ptgs = HSSFFormulaParser.parse("CSCO!$E$71", wb, FormulaType.NAMEDRANGE); + Ptg[] ptgs = HSSFFormulaParser.parse("CSCO!$E$71", wb, FormulaType.NAMEDRANGE, 0); for (int i = 0; i < ptgs.length; i++) { assertEquals('R', ptgs[i].getRVAType()); }