fixed formula parser to correctly resolve sheet-level names

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@729028 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-12-23 16:19:07 +00:00
parent 98121b7691
commit 177533e23b
23 changed files with 343 additions and 206 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! -->
<release version="3.5-beta5" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">fixed formula parser to correctly resolve sheet-level names</action>
<action dev="POI-DEVELOPERS" type="fix">46433 - support for shared formulas in XSSF</action>
<action dev="POI-DEVELOPERS" type="add">46299 - support for carriage return and line break in XWPFRun</action>
<action dev="POI-DEVELOPERS" type="add">46300 - support for line spacing in XWPFParagraph</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.5-beta5" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">fixed formula parser to correctly resolve sheet-level names</action>
<action dev="POI-DEVELOPERS" type="fix">46433 - support for shared formulas in XSSF</action>
<action dev="POI-DEVELOPERS" type="add">46299 - support for carriage return and line break in XWPFRun</action>
<action dev="POI-DEVELOPERS" type="add">46300 - support for line spacing in XWPFParagraph</action>

View File

@ -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. <code>-1</code> 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.

View File

@ -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 <code>null</code> if <tt>formula</tt> 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);
}
}

View File

@ -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 <code>null</code>
*/
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));
}

View File

@ -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());

View File

@ -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(),

View File

@ -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. <code>-1</code> 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);
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
/**

View File

@ -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));
}
/**

View File

@ -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. <code>-1</code> 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);

View File

@ -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);

View File

@ -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);

View File

@ -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() {

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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()];

View File

@ -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());
}
}

View File

@ -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());
}