bug 57840: merge changes from ^/poi/branches/xssf_structured_references to ^/poi/trunk

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1747657 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Javen O'Neal 2016-06-10 07:41:09 +00:00
commit 12d7fb30a4
27 changed files with 5751 additions and 4867 deletions

View File

@ -31,53 +31,53 @@ import org.apache.poi.ss.formula.FormulaType;
*/
public final class HSSFFormulaParser {
private static FormulaParsingWorkbook createParsingWorkbook(HSSFWorkbook book) {
return HSSFEvaluationWorkbook.create(book);
}
private static FormulaParsingWorkbook createParsingWorkbook(HSSFWorkbook book) {
return HSSFEvaluationWorkbook.create(book);
}
private HSSFFormulaParser() {
// no instances of this class
}
private HSSFFormulaParser() {
// no instances of this class
}
/**
* Convenience method for parsing cell formulas. see {@link #parse(String, HSSFWorkbook, int, int)}
*/
public static Ptg[] parse(String formula, HSSFWorkbook workbook) throws FormulaParseException {
return parse(formula, workbook, FormulaType.CELL);
}
/**
* Convenience method for parsing cell formulas. see {@link #parse(String, HSSFWorkbook, int, int)}
*/
public static Ptg[] parse(String formula, HSSFWorkbook workbook) throws FormulaParseException {
return parse(formula, workbook, FormulaType.CELL);
}
/**
* @param formulaType a constant from {@link FormulaType}
* @return the parsed formula tokens
/**
* @param formulaType a constant from {@link FormulaType}
* @return the parsed formula tokens
* @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
*/
public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType) throws FormulaParseException {
return parse(formula, workbook, formulaType, -1);
}
*/
public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType) throws FormulaParseException {
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
/**
* @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
* @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
*/
public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType, int sheetIndex) throws FormulaParseException {
return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType, sheetIndex);
}
*/
public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType, int sheetIndex) throws FormulaParseException {
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.
* @param book used for defined names and 3D references
* @param ptgs must not be <code>null</code>
* @return a human readable String
*/
public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) {
return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs);
}
/**
* Static method to convert an array of {@link Ptg}s in RPN order
* to a human readable string format in infix mode.
* @param book used for defined names and 3D references
* @param ptgs must not be <code>null</code>
* @return a human readable String
*/
public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) {
return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs);
}
}

View File

@ -6,7 +6,7 @@
(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
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,
@ -38,6 +38,7 @@ import org.apache.poi.ss.formula.ptg.NameXPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Table;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.POILogFactory;
@ -264,7 +265,16 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, E
return extIx;
}
@Override
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL97;
}
/**
* @throws IllegalStateException: data tables are not supported in Excel 97-2003 format
*/
@Override
public Table getTable(String name) {
throw new IllegalStateException("XSSF-style tables are not supported for HSSF");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@ package org.apache.poi.ss.formula;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.usermodel.Name;
import org.apache.poi.ss.usermodel.Table;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
@ -31,46 +32,51 @@ import org.apache.poi.ss.util.CellReference;
* @author Josh Micich
*/
public interface FormulaParsingWorkbook {
/**
* named range name matching is case insensitive
*/
EvaluationName getName(String name, int sheetIndex);
/**
* Return the underlying workbook
*/
Name createName();
/**
* named range name matching is case insensitive
*/
EvaluationName getName(String name, int sheetIndex);
/**
* Return the underlying workbook
*/
Name createName();
/**
* Return an external name (named range, function, user-defined function) Ptg
*/
Ptg getNameXPtg(String name, SheetIdentifier sheet);
/**
* Produce the appropriate Ptg for a 3d cell reference
*/
Ptg get3DReferencePtg(CellReference cell, SheetIdentifier sheet);
/**
* XSSF Only - gets a table that exists in the worksheet
*/
Table getTable(String name);
/**
* Return an external name (named range, function, user-defined function) Ptg
*/
Ptg getNameXPtg(String name, SheetIdentifier sheet);
/**
* Produce the appropriate Ptg for a 3d cell reference
*/
Ptg get3DReferencePtg(CellReference cell, SheetIdentifier sheet);
/**
* Produce the appropriate Ptg for a 3d area reference
*/
Ptg get3DReferencePtg(AreaReference area, SheetIdentifier sheet);
/**
* gets the externSheet index for a sheet from this workbook
*/
int getExternalSheetIndex(String sheetName);
/**
* gets the externSheet index for a sheet from an external workbook
* @param workbookName e.g. "Budget.xls"
* @param sheetName a name of a sheet in that workbook
*/
int getExternalSheetIndex(String workbookName, String sheetName);
/**
* gets the externSheet index for a sheet from this workbook
*/
int getExternalSheetIndex(String sheetName);
/**
* gets the externSheet index for a sheet from an external workbook
* @param workbookName e.g. "Budget.xls"
* @param sheetName a name of a sheet in that workbook
*/
int getExternalSheetIndex(String workbookName, String sheetName);
/**
* Returns an enum holding spreadhseet properties specific to an Excel version (
* max column and row numbers, max arguments to a function, etc.)
*/
SpreadsheetVersion getSpreadsheetVersion();
/**
* Returns an enum holding spreadhseet properties specific to an Excel version (
* max column and row numbers, max arguments to a function, etc.)
*/
SpreadsheetVersion getSpreadsheetVersion();
}

View File

@ -46,199 +46,199 @@ import org.apache.poi.ss.util.CellReference.NameType;
* For POI internal use only
*/
public final class OperationEvaluationContext {
public static final FreeRefFunction UDF = UserDefinedFunction.instance;
private final EvaluationWorkbook _workbook;
private final int _sheetIndex;
private final int _rowIndex;
private final int _columnIndex;
private final EvaluationTracker _tracker;
private final WorkbookEvaluator _bookEvaluator;
public static final FreeRefFunction UDF = UserDefinedFunction.instance;
private final EvaluationWorkbook _workbook;
private final int _sheetIndex;
private final int _rowIndex;
private final int _columnIndex;
private final EvaluationTracker _tracker;
private final WorkbookEvaluator _bookEvaluator;
public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
int srcColNum, EvaluationTracker tracker) {
_bookEvaluator = bookEvaluator;
_workbook = workbook;
_sheetIndex = sheetIndex;
_rowIndex = srcRowNum;
_columnIndex = srcColNum;
_tracker = tracker;
}
public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
int srcColNum, EvaluationTracker tracker) {
_bookEvaluator = bookEvaluator;
_workbook = workbook;
_sheetIndex = sheetIndex;
_rowIndex = srcRowNum;
_columnIndex = srcColNum;
_tracker = tracker;
}
public EvaluationWorkbook getWorkbook() {
return _workbook;
}
public EvaluationWorkbook getWorkbook() {
return _workbook;
}
public int getRowIndex() {
return _rowIndex;
}
public int getRowIndex() {
return _rowIndex;
}
public int getColumnIndex() {
return _columnIndex;
}
public int getColumnIndex() {
return _columnIndex;
}
SheetRangeEvaluator createExternSheetRefEvaluator(ExternSheetReferenceToken ptg) {
return createExternSheetRefEvaluator(ptg.getExternSheetIndex());
}
SheetRangeEvaluator createExternSheetRefEvaluator(String firstSheetName, String lastSheetName, int externalWorkbookNumber) {
SheetRangeEvaluator createExternSheetRefEvaluator(ExternSheetReferenceToken ptg) {
return createExternSheetRefEvaluator(ptg.getExternSheetIndex());
}
SheetRangeEvaluator createExternSheetRefEvaluator(String firstSheetName, String lastSheetName, int externalWorkbookNumber) {
ExternalSheet externalSheet = _workbook.getExternalSheet(firstSheetName, lastSheetName, externalWorkbookNumber);
return createExternSheetRefEvaluator(externalSheet);
}
SheetRangeEvaluator createExternSheetRefEvaluator(int externSheetIndex) {
ExternalSheet externalSheet = _workbook.getExternalSheet(externSheetIndex);
SheetRangeEvaluator createExternSheetRefEvaluator(int externSheetIndex) {
ExternalSheet externalSheet = _workbook.getExternalSheet(externSheetIndex);
return createExternSheetRefEvaluator(externalSheet);
}
SheetRangeEvaluator createExternSheetRefEvaluator(ExternalSheet externalSheet) {
WorkbookEvaluator targetEvaluator;
int otherFirstSheetIndex;
int otherLastSheetIndex = -1;
if (externalSheet == null || externalSheet.getWorkbookName() == null) {
// sheet is in same workbook
targetEvaluator = _bookEvaluator;
if(externalSheet == null) {
otherFirstSheetIndex = 0;
} else {
otherFirstSheetIndex = _workbook.getSheetIndex(externalSheet.getSheetName());
}
if (externalSheet instanceof ExternalSheetRange) {
String lastSheetName = ((ExternalSheetRange)externalSheet).getLastSheetName();
otherLastSheetIndex = _workbook.getSheetIndex(lastSheetName);
}
} else {
// look up sheet by name from external workbook
String workbookName = externalSheet.getWorkbookName();
try {
targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
} catch (WorkbookNotFoundException e) {
throw new RuntimeException(e.getMessage(), e);
}
otherFirstSheetIndex = targetEvaluator.getSheetIndex(externalSheet.getSheetName());
}
SheetRangeEvaluator createExternSheetRefEvaluator(ExternalSheet externalSheet) {
WorkbookEvaluator targetEvaluator;
int otherFirstSheetIndex;
int otherLastSheetIndex = -1;
if (externalSheet == null || externalSheet.getWorkbookName() == null) {
// sheet is in same workbook
targetEvaluator = _bookEvaluator;
if(externalSheet == null) {
otherFirstSheetIndex = 0;
} else {
otherFirstSheetIndex = _workbook.getSheetIndex(externalSheet.getSheetName());
}
if (externalSheet instanceof ExternalSheetRange) {
String lastSheetName = ((ExternalSheetRange)externalSheet).getLastSheetName();
otherLastSheetIndex = _workbook.getSheetIndex(lastSheetName);
}
} else {
// look up sheet by name from external workbook
String workbookName = externalSheet.getWorkbookName();
try {
targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
} catch (WorkbookNotFoundException e) {
throw new RuntimeException(e.getMessage(), e);
}
otherFirstSheetIndex = targetEvaluator.getSheetIndex(externalSheet.getSheetName());
if (externalSheet instanceof ExternalSheetRange) {
String lastSheetName = ((ExternalSheetRange)externalSheet).getLastSheetName();
otherLastSheetIndex = targetEvaluator.getSheetIndex(lastSheetName);
}
if (otherFirstSheetIndex < 0) {
throw new RuntimeException("Invalid sheet name '" + externalSheet.getSheetName()
+ "' in bool '" + workbookName + "'.");
}
}
if (otherLastSheetIndex == -1) {
// Reference to just one sheet
otherLastSheetIndex = otherFirstSheetIndex;
}
SheetRefEvaluator[] evals = new SheetRefEvaluator[otherLastSheetIndex-otherFirstSheetIndex+1];
for (int i=0; i<evals.length; i++) {
int otherSheetIndex = i+otherFirstSheetIndex;
evals[i] = new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex);
}
return new SheetRangeEvaluator(otherFirstSheetIndex, otherLastSheetIndex, evals);
}
if (otherFirstSheetIndex < 0) {
throw new RuntimeException("Invalid sheet name '" + externalSheet.getSheetName()
+ "' in bool '" + workbookName + "'.");
}
}
if (otherLastSheetIndex == -1) {
// Reference to just one sheet
otherLastSheetIndex = otherFirstSheetIndex;
}
SheetRefEvaluator[] evals = new SheetRefEvaluator[otherLastSheetIndex-otherFirstSheetIndex+1];
for (int i=0; i<evals.length; i++) {
int otherSheetIndex = i+otherFirstSheetIndex;
evals[i] = new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex);
}
return new SheetRangeEvaluator(otherFirstSheetIndex, otherLastSheetIndex, evals);
}
/**
* @return <code>null</code> if either workbook or sheet is not found
*/
private SheetRefEvaluator createExternSheetRefEvaluator(String workbookName, String sheetName) {
WorkbookEvaluator targetEvaluator;
if (workbookName == null) {
targetEvaluator = _bookEvaluator;
} else {
if (sheetName == null) {
throw new IllegalArgumentException("sheetName must not be null if workbookName is provided");
}
try {
targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
} catch (WorkbookNotFoundException e) {
return null;
}
}
int otherSheetIndex = sheetName == null ? _sheetIndex : targetEvaluator.getSheetIndex(sheetName);
if (otherSheetIndex < 0) {
return null;
}
return new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex);
}
/**
* @return <code>null</code> if either workbook or sheet is not found
*/
private SheetRefEvaluator createExternSheetRefEvaluator(String workbookName, String sheetName) {
WorkbookEvaluator targetEvaluator;
if (workbookName == null) {
targetEvaluator = _bookEvaluator;
} else {
if (sheetName == null) {
throw new IllegalArgumentException("sheetName must not be null if workbookName is provided");
}
try {
targetEvaluator = _bookEvaluator.getOtherWorkbookEvaluator(workbookName);
} catch (WorkbookNotFoundException e) {
return null;
}
}
int otherSheetIndex = sheetName == null ? _sheetIndex : targetEvaluator.getSheetIndex(sheetName);
if (otherSheetIndex < 0) {
return null;
}
return new SheetRefEvaluator(targetEvaluator, _tracker, otherSheetIndex);
}
public SheetRangeEvaluator getRefEvaluatorForCurrentSheet() {
SheetRefEvaluator sre = new SheetRefEvaluator(_bookEvaluator, _tracker, _sheetIndex);
return new SheetRangeEvaluator(_sheetIndex, sre);
}
public SheetRangeEvaluator getRefEvaluatorForCurrentSheet() {
SheetRefEvaluator sre = new SheetRefEvaluator(_bookEvaluator, _tracker, _sheetIndex);
return new SheetRangeEvaluator(_sheetIndex, sre);
}
/**
* Resolves a cell or area reference dynamically.
* @param workbookName the name of the workbook containing the reference. If <code>null</code>
* the current workbook is assumed. Note - to evaluate formulas which use multiple workbooks,
* a {@link CollaboratingWorkbooksEnvironment} must be set up.
* @param sheetName the name of the sheet containing the reference. May be <code>null</code>
* (when <tt>workbookName</tt> is also null) in which case the current workbook and sheet is
* assumed.
* @param refStrPart1 the single cell reference or first part of the area reference. Must not
* be <code>null</code>.
* @param refStrPart2 the second part of the area reference. For single cell references this
* parameter must be <code>null</code>
* @param isA1Style specifies the format for <tt>refStrPart1</tt> and <tt>refStrPart2</tt>.
* Pass <code>true</code> for 'A1' style and <code>false</code> for 'R1C1' style.
* TODO - currently POI only supports 'A1' reference style
* @return a {@link RefEval} or {@link AreaEval}
*/
public ValueEval getDynamicReference(String workbookName, String sheetName, String refStrPart1,
String refStrPart2, boolean isA1Style) {
if (!isA1Style) {
throw new RuntimeException("R1C1 style not supported yet");
}
SheetRefEvaluator se = createExternSheetRefEvaluator(workbookName, sheetName);
if (se == null) {
return ErrorEval.REF_INVALID;
}
SheetRangeEvaluator sre = new SheetRangeEvaluator(_sheetIndex, se);
// ugly typecast - TODO - make spreadsheet version more easily accessible
SpreadsheetVersion ssVersion = ((FormulaParsingWorkbook)_workbook).getSpreadsheetVersion();
/**
* Resolves a cell or area reference dynamically.
* @param workbookName the name of the workbook containing the reference. If <code>null</code>
* the current workbook is assumed. Note - to evaluate formulas which use multiple workbooks,
* a {@link CollaboratingWorkbooksEnvironment} must be set up.
* @param sheetName the name of the sheet containing the reference. May be <code>null</code>
* (when <tt>workbookName</tt> is also null) in which case the current workbook and sheet is
* assumed.
* @param refStrPart1 the single cell reference or first part of the area reference. Must not
* be <code>null</code>.
* @param refStrPart2 the second part of the area reference. For single cell references this
* parameter must be <code>null</code>
* @param isA1Style specifies the format for <tt>refStrPart1</tt> and <tt>refStrPart2</tt>.
* Pass <code>true</code> for 'A1' style and <code>false</code> for 'R1C1' style.
* TODO - currently POI only supports 'A1' reference style
* @return a {@link RefEval} or {@link AreaEval}
*/
public ValueEval getDynamicReference(String workbookName, String sheetName, String refStrPart1,
String refStrPart2, boolean isA1Style) {
if (!isA1Style) {
throw new RuntimeException("R1C1 style not supported yet");
}
SheetRefEvaluator se = createExternSheetRefEvaluator(workbookName, sheetName);
if (se == null) {
return ErrorEval.REF_INVALID;
}
SheetRangeEvaluator sre = new SheetRangeEvaluator(_sheetIndex, se);
// ugly typecast - TODO - make spreadsheet version more easily accessible
SpreadsheetVersion ssVersion = ((FormulaParsingWorkbook)_workbook).getSpreadsheetVersion();
NameType part1refType = classifyCellReference(refStrPart1, ssVersion);
switch (part1refType) {
case BAD_CELL_OR_NAMED_RANGE:
return ErrorEval.REF_INVALID;
case NAMED_RANGE:
NameType part1refType = classifyCellReference(refStrPart1, ssVersion);
switch (part1refType) {
case BAD_CELL_OR_NAMED_RANGE:
return ErrorEval.REF_INVALID;
case NAMED_RANGE:
EvaluationName nm = ((FormulaParsingWorkbook)_workbook).getName(refStrPart1, _sheetIndex);
if(!nm.isRange()){
throw new RuntimeException("Specified name '" + refStrPart1 + "' is not a range as expected.");
}
return _bookEvaluator.evaluateNameFormula(nm.getNameDefinition(), this);
}
if (refStrPart2 == null) {
// no ':'
switch (part1refType) {
case COLUMN:
case ROW:
return ErrorEval.REF_INVALID;
case CELL:
CellReference cr = new CellReference(refStrPart1);
return new LazyRefEval(cr.getRow(), cr.getCol(), sre);
}
throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
}
NameType part2refType = classifyCellReference(refStrPart1, ssVersion);
switch (part2refType) {
case BAD_CELL_OR_NAMED_RANGE:
return ErrorEval.REF_INVALID;
case NAMED_RANGE:
throw new RuntimeException("Cannot evaluate '" + refStrPart1
+ "'. Indirect evaluation of defined names not supported yet");
}
}
if (refStrPart2 == null) {
// no ':'
switch (part1refType) {
case COLUMN:
case ROW:
return ErrorEval.REF_INVALID;
case CELL:
CellReference cr = new CellReference(refStrPart1);
return new LazyRefEval(cr.getRow(), cr.getCol(), sre);
}
throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
}
NameType part2refType = classifyCellReference(refStrPart1, ssVersion);
switch (part2refType) {
case BAD_CELL_OR_NAMED_RANGE:
return ErrorEval.REF_INVALID;
case NAMED_RANGE:
throw new RuntimeException("Cannot evaluate '" + refStrPart1
+ "'. Indirect evaluation of defined names not supported yet");
}
if (part2refType != part1refType) {
// LHS and RHS of ':' must be compatible
return ErrorEval.REF_INVALID;
}
int firstRow, firstCol, lastRow, lastCol;
switch (part1refType) {
case COLUMN:
if (part2refType != part1refType) {
// LHS and RHS of ':' must be compatible
return ErrorEval.REF_INVALID;
}
int firstRow, firstCol, lastRow, lastCol;
switch (part1refType) {
case COLUMN:
firstRow =0;
if (part2refType.equals(NameType.COLUMN))
{
@ -252,7 +252,7 @@ public final class OperationEvaluationContext {
lastCol = parseColRef(refStrPart2);
}
break;
case ROW:
case ROW:
// support of cell range in the form of integer:integer
firstCol = 0;
if (part2refType.equals(NameType.ROW))
@ -265,61 +265,61 @@ public final class OperationEvaluationContext {
firstRow = parseRowRef(refStrPart1);
lastRow = parseRowRef(refStrPart2);
}
break;
case CELL:
CellReference cr;
cr = new CellReference(refStrPart1);
firstRow = cr.getRow();
firstCol = cr.getCol();
cr = new CellReference(refStrPart2);
lastRow = cr.getRow();
lastCol = cr.getCol();
break;
default:
throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
}
return new LazyAreaEval(firstRow, firstCol, lastRow, lastCol, sre);
}
break;
case CELL:
CellReference cr;
cr = new CellReference(refStrPart1);
firstRow = cr.getRow();
firstCol = cr.getCol();
cr = new CellReference(refStrPart2);
lastRow = cr.getRow();
lastCol = cr.getCol();
break;
default:
throw new IllegalStateException("Unexpected reference classification of '" + refStrPart1 + "'.");
}
return new LazyAreaEval(firstRow, firstCol, lastRow, lastCol, sre);
}
private static int parseRowRef(String refStrPart) {
return CellReference.convertColStringToIndex(refStrPart);
}
private static int parseRowRef(String refStrPart) {
return CellReference.convertColStringToIndex(refStrPart);
}
private static int parseColRef(String refStrPart) {
return Integer.parseInt(refStrPart) - 1;
}
private static int parseColRef(String refStrPart) {
return Integer.parseInt(refStrPart) - 1;
}
private static NameType classifyCellReference(String str, SpreadsheetVersion ssVersion) {
int len = str.length();
if (len < 1) {
return CellReference.NameType.BAD_CELL_OR_NAMED_RANGE;
}
return CellReference.classifyCellReference(str, ssVersion);
}
private static NameType classifyCellReference(String str, SpreadsheetVersion ssVersion) {
int len = str.length();
if (len < 1) {
return CellReference.NameType.BAD_CELL_OR_NAMED_RANGE;
}
return CellReference.classifyCellReference(str, ssVersion);
}
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _bookEvaluator.findUserDefinedFunction(functionName);
}
public FreeRefFunction findUserDefinedFunction(String functionName) {
return _bookEvaluator.findUserDefinedFunction(functionName);
}
public ValueEval getRefEval(int rowIndex, int columnIndex) {
SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
return new LazyRefEval(rowIndex, columnIndex, sre);
}
public ValueEval getRef3DEval(Ref3DPtg rptg) {
SheetRangeEvaluator sre = createExternSheetRefEvaluator(rptg.getExternSheetIndex());
return new LazyRefEval(rptg.getRow(), rptg.getColumn(), sre);
}
public ValueEval getRefEval(int rowIndex, int columnIndex) {
SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
return new LazyRefEval(rowIndex, columnIndex, sre);
}
public ValueEval getRef3DEval(Ref3DPtg rptg) {
SheetRangeEvaluator sre = createExternSheetRefEvaluator(rptg.getExternSheetIndex());
return new LazyRefEval(rptg.getRow(), rptg.getColumn(), sre);
}
public ValueEval getRef3DEval(Ref3DPxg rptg) {
SheetRangeEvaluator sre = createExternSheetRefEvaluator(
rptg.getSheetName(), rptg.getLastSheetName(), rptg.getExternalWorkbookNumber());
return new LazyRefEval(rptg.getRow(), rptg.getColumn(), sre);
}
public ValueEval getAreaEval(int firstRowIndex, int firstColumnIndex,
int lastRowIndex, int lastColumnIndex) {
SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
return new LazyAreaEval(firstRowIndex, firstColumnIndex, lastRowIndex, lastColumnIndex, sre);
}
public ValueEval getAreaEval(int firstRowIndex, int firstColumnIndex,
int lastRowIndex, int lastColumnIndex) {
SheetRangeEvaluator sre = getRefEvaluatorForCurrentSheet();
return new LazyAreaEval(firstRowIndex, firstColumnIndex, lastRowIndex, lastColumnIndex, sre);
}
public ValueEval getArea3DEval(Area3DPtg aptg) {
SheetRangeEvaluator sre = createExternSheetRefEvaluator(aptg.getExternSheetIndex());
return new LazyAreaEval(aptg.getFirstRow(), aptg.getFirstColumn(),
@ -348,8 +348,8 @@ public final class OperationEvaluationContext {
);
return getExternalNameXEval(externName, workbookName);
}
public ValueEval getNameXEval(NameXPxg nameXPxg) {
ExternalSheet externSheet = _workbook.getExternalSheet(nameXPxg.getSheetName(), null, nameXPxg.getExternalWorkbookNumber());
public ValueEval getNameXEval(NameXPxg nameXPxg) {
ExternalSheet externSheet = _workbook.getExternalSheet(nameXPxg.getSheetName(), null, nameXPxg.getExternalWorkbookNumber());
if(externSheet == null || externSheet.getWorkbookName() == null) {
// External reference to our own workbook's name
return getLocalNameXEval(nameXPxg);
@ -363,8 +363,8 @@ public final class OperationEvaluationContext {
nameXPxg.getExternalWorkbookNumber()
);
return getExternalNameXEval(externName, workbookName);
}
}
private ValueEval getLocalNameXEval(NameXPxg nameXPxg) {
// Look up the sheet, if present
int sIdx = -1;
@ -383,7 +383,7 @@ public final class OperationEvaluationContext {
return new FunctionNameEval(name);
}
}
private ValueEval getLocalNameXEval(NameXPtg nameXPtg) {
private ValueEval getLocalNameXEval(NameXPtg nameXPtg) {
String name = _workbook.resolveNameXText(nameXPtg);
// Try to parse it as a name
@ -406,8 +406,11 @@ public final class OperationEvaluationContext {
// Must be an external function
return new FunctionNameEval(name);
}
}
}
public int getSheetIndex() {
return _sheetIndex;
}
private ValueEval getExternalNameXEval(ExternalName externName, String workbookName) {
try {
// Fetch the workbook this refers to, and the name as defined with that

File diff suppressed because it is too large Load Diff

View File

@ -17,13 +17,18 @@
package org.apache.poi.ss.formula.functions;
import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.formula.FormulaParser;
import org.apache.poi.ss.formula.FormulaParsingWorkbook;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.formula.eval.BlankEval;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.formula.eval.EvaluationException;
import org.apache.poi.ss.formula.eval.MissingArgEval;
import org.apache.poi.ss.formula.eval.OperandResolver;
import org.apache.poi.ss.formula.eval.ValueEval;
import org.apache.poi.ss.formula.OperationEvaluationContext;
import org.apache.poi.ss.formula.ptg.Area3DPxg;
import org.apache.poi.ss.usermodel.Table;
/**
* Implementation for Excel function INDIRECT<p/>
@ -42,198 +47,210 @@ import org.apache.poi.ss.formula.OperationEvaluationContext;
*/
public final class Indirect implements FreeRefFunction {
public static final FreeRefFunction instance = new Indirect();
public static final FreeRefFunction instance = new Indirect();
private Indirect() {
// enforce singleton
}
private Indirect() {
// enforce singleton
}
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
if (args.length < 1) {
return ErrorEval.VALUE_INVALID;
}
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
if (args.length < 1) {
return ErrorEval.VALUE_INVALID;
}
boolean isA1style;
String text;
try {
ValueEval ve = OperandResolver.getSingleValue(args[0], ec.getRowIndex(), ec
.getColumnIndex());
text = OperandResolver.coerceValueToString(ve);
switch (args.length) {
case 1:
isA1style = true;
break;
case 2:
isA1style = evaluateBooleanArg(args[1], ec);
break;
default:
return ErrorEval.VALUE_INVALID;
}
} catch (EvaluationException e) {
return e.getErrorEval();
}
boolean isA1style;
String text;
try {
ValueEval ve = OperandResolver.getSingleValue(args[0], ec.getRowIndex(), ec
.getColumnIndex());
text = OperandResolver.coerceValueToString(ve);
switch (args.length) {
case 1:
isA1style = true;
break;
case 2:
isA1style = evaluateBooleanArg(args[1], ec);
break;
default:
return ErrorEval.VALUE_INVALID;
}
} catch (EvaluationException e) {
return e.getErrorEval();
}
return evaluateIndirect(ec, text, isA1style);
}
return evaluateIndirect(ec, text, isA1style);
}
private static boolean evaluateBooleanArg(ValueEval arg, OperationEvaluationContext ec)
throws EvaluationException {
ValueEval ve = OperandResolver.getSingleValue(arg, ec.getRowIndex(), ec.getColumnIndex());
private static boolean evaluateBooleanArg(ValueEval arg, OperationEvaluationContext ec)
throws EvaluationException {
ValueEval ve = OperandResolver.getSingleValue(arg, ec.getRowIndex(), ec.getColumnIndex());
if (ve == BlankEval.instance || ve == MissingArgEval.instance) {
return false;
}
// numeric quantities follow standard boolean conversion rules
// for strings, only "TRUE" and "FALSE" (case insensitive) are valid
return OperandResolver.coerceValueToBoolean(ve, false).booleanValue();
}
if (ve == BlankEval.instance || ve == MissingArgEval.instance) {
return false;
}
// numeric quantities follow standard boolean conversion rules
// for strings, only "TRUE" and "FALSE" (case insensitive) are valid
return OperandResolver.coerceValueToBoolean(ve, false).booleanValue();
}
private static ValueEval evaluateIndirect(OperationEvaluationContext ec, String text,
boolean isA1style) {
// Search backwards for '!' because sheet names can contain '!'
int plingPos = text.lastIndexOf('!');
private static ValueEval evaluateIndirect(final OperationEvaluationContext ec, String text,
boolean isA1style) {
// Search backwards for '!' because sheet names can contain '!'
int plingPos = text.lastIndexOf('!');
String workbookName;
String sheetName;
String refText; // whitespace around this gets trimmed OK
if (plingPos < 0) {
workbookName = null;
sheetName = null;
refText = text;
} else {
String[] parts = parseWorkbookAndSheetName(text.subSequence(0, plingPos));
if (parts == null) {
return ErrorEval.REF_INVALID;
}
workbookName = parts[0];
sheetName = parts[1];
refText = text.substring(plingPos + 1);
}
String workbookName;
String sheetName;
String refText; // whitespace around this gets trimmed OK
if (plingPos < 0) {
workbookName = null;
sheetName = null;
refText = text;
} else {
String[] parts = parseWorkbookAndSheetName(text.subSequence(0, plingPos));
if (parts == null) {
return ErrorEval.REF_INVALID;
}
workbookName = parts[0];
sheetName = parts[1];
refText = text.substring(plingPos + 1);
}
String refStrPart1;
String refStrPart2;
if (Table.isStructuredReference.matcher(refText).matches()) {
// The argument is structured reference
Area3DPxg areaPtg = null;
try {
areaPtg = FormulaParser.parseStructuredReference(refText, (FormulaParsingWorkbook) ec.getWorkbook(), ec.getRowIndex());
} catch (FormulaParseException e) {
return ErrorEval.REF_INVALID;
}
return ec.getArea3DEval(areaPtg);
} else {
// The argument is regular reference
String refStrPart1;
String refStrPart2;
int colonPos = refText.indexOf(':');
if (colonPos < 0) {
refStrPart1 = refText.trim();
refStrPart2 = null;
} else {
refStrPart1 = refText.substring(0, colonPos).trim();
refStrPart2 = refText.substring(colonPos + 1).trim();
}
return ec.getDynamicReference(workbookName, sheetName, refStrPart1, refStrPart2, isA1style);
}
}
int colonPos = refText.indexOf(':');
if (colonPos < 0) {
refStrPart1 = refText.trim();
refStrPart2 = null;
} else {
refStrPart1 = refText.substring(0, colonPos).trim();
refStrPart2 = refText.substring(colonPos + 1).trim();
}
return ec.getDynamicReference(workbookName, sheetName, refStrPart1, refStrPart2, isA1style);
}
/**
* @return array of length 2: {workbookName, sheetName,}. Second element will always be
* present. First element may be null if sheetName is unqualified.
* Returns <code>null</code> if text cannot be parsed.
*/
private static String[] parseWorkbookAndSheetName(CharSequence text) {
int lastIx = text.length() - 1;
if (lastIx < 0) {
return null;
}
if (canTrim(text)) {
return null;
}
char firstChar = text.charAt(0);
if (Character.isWhitespace(firstChar)) {
return null;
}
if (firstChar == '\'') {
// workbookName or sheetName needs quoting
// quotes go around both
if (text.charAt(lastIx) != '\'') {
return null;
}
firstChar = text.charAt(1);
if (Character.isWhitespace(firstChar)) {
return null;
}
String wbName;
int sheetStartPos;
if (firstChar == '[') {
int rbPos = text.toString().lastIndexOf(']');
if (rbPos < 0) {
return null;
}
wbName = unescapeString(text.subSequence(2, rbPos));
if (wbName == null || canTrim(wbName)) {
return null;
}
sheetStartPos = rbPos + 1;
} else {
wbName = null;
sheetStartPos = 1;
}
/**
* @return array of length 2: {workbookName, sheetName,}. Second element will always be
* present. First element may be null if sheetName is unqualified.
* Returns <code>null</code> if text cannot be parsed.
*/
private static String[] parseWorkbookAndSheetName(CharSequence text) {
int lastIx = text.length() - 1;
if (lastIx < 0) {
return null;
}
if (canTrim(text)) {
return null;
}
char firstChar = text.charAt(0);
if (Character.isWhitespace(firstChar)) {
return null;
}
if (firstChar == '\'') {
// workbookName or sheetName needs quoting
// quotes go around both
if (text.charAt(lastIx) != '\'') {
return null;
}
firstChar = text.charAt(1);
if (Character.isWhitespace(firstChar)) {
return null;
}
String wbName;
int sheetStartPos;
if (firstChar == '[') {
int rbPos = text.toString().lastIndexOf(']');
if (rbPos < 0) {
return null;
}
wbName = unescapeString(text.subSequence(2, rbPos));
if (wbName == null || canTrim(wbName)) {
return null;
}
sheetStartPos = rbPos + 1;
} else {
wbName = null;
sheetStartPos = 1;
}
// else - just sheet name
String sheetName = unescapeString(text.subSequence(sheetStartPos, lastIx));
if (sheetName == null) { // note - when quoted, sheetName can
// start/end with whitespace
return null;
}
return new String[] { wbName, sheetName, };
}
// else - just sheet name
String sheetName = unescapeString(text.subSequence(sheetStartPos, lastIx));
if (sheetName == null) { // note - when quoted, sheetName can
// start/end with whitespace
return null;
}
return new String[] { wbName, sheetName, };
}
if (firstChar == '[') {
int rbPos = text.toString().lastIndexOf(']');
if (rbPos < 0) {
return null;
}
CharSequence wbName = text.subSequence(1, rbPos);
if (canTrim(wbName)) {
return null;
}
CharSequence sheetName = text.subSequence(rbPos + 1, text.length());
if (canTrim(sheetName)) {
return null;
}
return new String[] { wbName.toString(), sheetName.toString(), };
}
// else - just sheet name
return new String[] { null, text.toString(), };
}
if (firstChar == '[') {
int rbPos = text.toString().lastIndexOf(']');
if (rbPos < 0) {
return null;
}
CharSequence wbName = text.subSequence(1, rbPos);
if (canTrim(wbName)) {
return null;
}
CharSequence sheetName = text.subSequence(rbPos + 1, text.length());
if (canTrim(sheetName)) {
return null;
}
return new String[] { wbName.toString(), sheetName.toString(), };
}
// else - just sheet name
return new String[] { null, text.toString(), };
}
/**
* @return <code>null</code> if there is a syntax error in any escape sequence
* (the typical syntax error is a single quote character not followed by another).
*/
private static String unescapeString(CharSequence text) {
int len = text.length();
StringBuilder sb = new StringBuilder(len);
int i = 0;
while (i < len) {
char ch = text.charAt(i);
if (ch == '\'') {
// every quote must be followed by another
i++;
if (i >= len) {
return null;
}
ch = text.charAt(i);
if (ch != '\'') {
return null;
}
}
sb.append(ch);
i++;
}
return sb.toString();
}
/**
* @return <code>null</code> if there is a syntax error in any escape sequence
* (the typical syntax error is a single quote character not followed by another).
*/
private static String unescapeString(CharSequence text) {
int len = text.length();
StringBuilder sb = new StringBuilder(len);
int i = 0;
while (i < len) {
char ch = text.charAt(i);
if (ch == '\'') {
// every quote must be followed by another
i++;
if (i >= len) {
return null;
}
ch = text.charAt(i);
if (ch != '\'') {
return null;
}
}
sb.append(ch);
i++;
}
return sb.toString();
}
private static boolean canTrim(CharSequence text) {
int lastIx = text.length() - 1;
if (lastIx < 0) {
return false;
}
if (Character.isWhitespace(text.charAt(0))) {
return true;
}
if (Character.isWhitespace(text.charAt(lastIx))) {
return true;
}
return false;
}
private static boolean canTrim(CharSequence text) {
int lastIx = text.length() - 1;
if (lastIx < 0) {
return false;
}
if (Character.isWhitespace(text.charAt(0))) {
return true;
}
if (Character.isWhitespace(text.charAt(lastIx))) {
return true;
}
return false;
}
}

View File

@ -36,9 +36,9 @@ public final class Area3DPxg extends AreaPtgBase implements Pxg3D {
private String firstSheetName;
private String lastSheetName;
public Area3DPxg(int externalWorkbookNumber, SheetIdentifier sheetName, String arearef) {
this(externalWorkbookNumber, sheetName, new AreaReference(arearef));
}
public Area3DPxg(int externalWorkbookNumber, SheetIdentifier sheetName, String arearef) {
this(externalWorkbookNumber, sheetName, new AreaReference(arearef));
}
public Area3DPxg(int externalWorkbookNumber, SheetIdentifier sheetName, AreaReference arearef) {
super(arearef);
this.externalWorkbookNumber = externalWorkbookNumber;
@ -57,8 +57,8 @@ public final class Area3DPxg extends AreaPtgBase implements Pxg3D {
this(-1, sheetName, arearef);
}
@Override
public String toString() {
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName());
sb.append(" [");
@ -76,8 +76,8 @@ public final class Area3DPxg extends AreaPtgBase implements Pxg3D {
sb.append(formatReferenceAsString());
sb.append("]");
return sb.toString();
}
}
public int getExternalWorkbookNumber() {
return externalWorkbookNumber;
}

View File

@ -31,272 +31,272 @@ import org.apache.poi.util.LittleEndianOutput;
* @author Jason Height (jheight at chariot dot net dot au)
*/
public abstract class AreaPtgBase extends OperandPtg implements AreaI {
/**
* TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
* see similar comment in ReferencePtg
*/
protected final RuntimeException notImplemented() {
return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
}
/**
* TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas.
* see similar comment in ReferencePtg
*/
protected final RuntimeException notImplemented() {
return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
}
/** zero based, unsigned 16 bit */
private int field_1_first_row;
/** zero based, unsigned 16 bit */
private int field_2_last_row;
/** zero based, unsigned 8 bit */
private int field_3_first_column; //BitFields: (first row relative, first col relative, first column number)
/** zero based, unsigned 8 bit */
private int field_4_last_column; //BitFields: (last row relative, last col relative, last column number)
/** zero based, unsigned 16 bit */
private int field_1_first_row;
/** zero based, unsigned 16 bit */
private int field_2_last_row;
/** zero based, unsigned 8 bit */
private int field_3_first_column; //BitFields: (first row relative, first col relative, first column number)
/** zero based, unsigned 8 bit */
private int field_4_last_column; //BitFields: (last row relative, last col relative, last column number)
private final static BitField rowRelative = BitFieldFactory.getInstance(0x8000);
private final static BitField colRelative = BitFieldFactory.getInstance(0x4000);
private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF);
private final static BitField rowRelative = BitFieldFactory.getInstance(0x8000);
private final static BitField colRelative = BitFieldFactory.getInstance(0x4000);
private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF);
protected AreaPtgBase() {
// do nothing
}
protected AreaPtgBase() {
// do nothing
}
protected AreaPtgBase(AreaReference ar) {
CellReference firstCell = ar.getFirstCell();
CellReference lastCell = ar.getLastCell();
setFirstRow(firstCell.getRow());
setFirstColumn(firstCell.getCol() == -1 ? 0 : firstCell.getCol());
setLastRow(lastCell.getRow());
setLastColumn(lastCell.getCol() == -1 ? 0xFF : lastCell.getCol());
setFirstColRelative(!firstCell.isColAbsolute());
setLastColRelative(!lastCell.isColAbsolute());
setFirstRowRelative(!firstCell.isRowAbsolute());
setLastRowRelative(!lastCell.isRowAbsolute());
}
protected AreaPtgBase(AreaReference ar) {
CellReference firstCell = ar.getFirstCell();
CellReference lastCell = ar.getLastCell();
setFirstRow(firstCell.getRow());
setFirstColumn(firstCell.getCol() == -1 ? 0 : firstCell.getCol());
setLastRow(lastCell.getRow());
setLastColumn(lastCell.getCol() == -1 ? 0xFF : lastCell.getCol());
setFirstColRelative(!firstCell.isColAbsolute());
setLastColRelative(!lastCell.isColAbsolute());
setFirstRowRelative(!firstCell.isRowAbsolute());
setLastRowRelative(!lastCell.isRowAbsolute());
}
protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn,
boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
protected AreaPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn,
boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
if (lastRow >= firstRow) {
setFirstRow(firstRow);
setLastRow(lastRow);
setFirstRowRelative(firstRowRelative);
setLastRowRelative(lastRowRelative);
} else {
setFirstRow(lastRow);
setLastRow(firstRow);
setFirstRowRelative(lastRowRelative);
setLastRowRelative(firstRowRelative);
}
if (lastRow >= firstRow) {
setFirstRow(firstRow);
setLastRow(lastRow);
setFirstRowRelative(firstRowRelative);
setLastRowRelative(lastRowRelative);
} else {
setFirstRow(lastRow);
setLastRow(firstRow);
setFirstRowRelative(lastRowRelative);
setLastRowRelative(firstRowRelative);
}
if (lastColumn >= firstColumn) {
setFirstColumn(firstColumn);
setLastColumn(lastColumn);
setFirstColRelative(firstColRelative);
setLastColRelative(lastColRelative);
} else {
setFirstColumn(lastColumn);
setLastColumn(firstColumn);
setFirstColRelative(lastColRelative);
setLastColRelative(firstColRelative);
}
}
/**
* Sort the first and last row and columns in-place to the preferred (top left:bottom right) order
* Note: Sort only occurs when an instance is constructed or when this method is called.
*
* <p>For example, <code>$E5:B$10</code> becomes <code>B5:$E$10</code></p>
*/
public void sortTopLeftToBottomRight() {
if (getFirstRow() > getLastRow()) {
//swap first row and last row numbers and relativity
//Note: cannot just swap the fields because row relativity is stored in fields 3 and 4
final int firstRow = getFirstRow();
final boolean firstRowRel = isFirstRowRelative();
setFirstRow(getLastRow());
setFirstRowRelative(isLastRowRelative());
setLastRow(firstRow);
setLastRowRelative(firstRowRel);
}
if (getFirstColumn() > getLastColumn()) {
//swap first column and last column numbers and relativity
//Note: cannot just swap the fields because row relativity is stored in fields 3 and 4
final int firstCol = getFirstColumn();
final boolean firstColRel = isFirstColRelative();
setFirstColumn(getLastColumn());
setFirstColRelative(isLastColRelative());
setLastColumn(firstCol);
setLastColRelative(firstColRel);
}
}
if (lastColumn >= firstColumn) {
setFirstColumn(firstColumn);
setLastColumn(lastColumn);
setFirstColRelative(firstColRelative);
setLastColRelative(lastColRelative);
} else {
setFirstColumn(lastColumn);
setLastColumn(firstColumn);
setFirstColRelative(lastColRelative);
setLastColRelative(firstColRelative);
}
}
/**
* Sort the first and last row and columns in-place to the preferred (top left:bottom right) order
* Note: Sort only occurs when an instance is constructed or when this method is called.
*
* <p>For example, <code>$E5:B$10</code> becomes <code>B5:$E$10</code></p>
*/
public void sortTopLeftToBottomRight() {
if (getFirstRow() > getLastRow()) {
//swap first row and last row numbers and relativity
//Note: cannot just swap the fields because row relativity is stored in fields 3 and 4
final int firstRow = getFirstRow();
final boolean firstRowRel = isFirstRowRelative();
setFirstRow(getLastRow());
setFirstRowRelative(isLastRowRelative());
setLastRow(firstRow);
setLastRowRelative(firstRowRel);
}
if (getFirstColumn() > getLastColumn()) {
//swap first column and last column numbers and relativity
//Note: cannot just swap the fields because row relativity is stored in fields 3 and 4
final int firstCol = getFirstColumn();
final boolean firstColRel = isFirstColRelative();
setFirstColumn(getLastColumn());
setFirstColRelative(isLastColRelative());
setLastColumn(firstCol);
setLastColRelative(firstColRel);
}
}
protected final void readCoordinates(LittleEndianInput in) {
field_1_first_row = in.readUShort();
field_2_last_row = in.readUShort();
field_3_first_column = in.readUShort();
field_4_last_column = in.readUShort();
}
protected final void writeCoordinates(LittleEndianOutput out) {
out.writeShort(field_1_first_row);
out.writeShort(field_2_last_row);
out.writeShort(field_3_first_column);
out.writeShort(field_4_last_column);
}
protected final void readCoordinates(LittleEndianInput in) {
field_1_first_row = in.readUShort();
field_2_last_row = in.readUShort();
field_3_first_column = in.readUShort();
field_4_last_column = in.readUShort();
}
protected final void writeCoordinates(LittleEndianOutput out) {
out.writeShort(field_1_first_row);
out.writeShort(field_2_last_row);
out.writeShort(field_3_first_column);
out.writeShort(field_4_last_column);
}
/**
* @return the first row in the area
*/
public final int getFirstRow() {
return field_1_first_row;
}
/**
* @return the first row in the area
*/
public final int getFirstRow() {
return field_1_first_row;
}
/**
* sets the first row
* @param rowIx number (0-based)
*/
public final void setFirstRow(int rowIx) {
field_1_first_row = rowIx;
}
/**
* sets the first row
* @param rowIx number (0-based)
*/
public final void setFirstRow(int rowIx) {
field_1_first_row = rowIx;
}
/**
* @return last row in the range (x2 in x1,y1-x2,y2)
*/
public final int getLastRow() {
return field_2_last_row;
}
/**
* @return last row in the range (x2 in x1,y1-x2,y2)
*/
public final int getLastRow() {
return field_2_last_row;
}
/**
* @param rowIx last row number in the area
*/
public final void setLastRow(int rowIx) {
field_2_last_row = rowIx;
}
/**
* @param rowIx last row number in the area
*/
public final void setLastRow(int rowIx) {
field_2_last_row = rowIx;
}
/**
* @return the first column number in the area.
*/
public final int getFirstColumn() {
return columnMask.getValue(field_3_first_column);
}
/**
* @return the first column number in the area.
*/
public final int getFirstColumn() {
return columnMask.getValue(field_3_first_column);
}
/**
* @return the first column number + the options bit settings unstripped
*/
public final short getFirstColumnRaw() {
return (short) field_3_first_column; // TODO
}
/**
* @return the first column number + the options bit settings unstripped
*/
public final short getFirstColumnRaw() {
return (short) field_3_first_column; // TODO
}
/**
* @return whether or not the first row is a relative reference or not.
*/
public final boolean isFirstRowRelative() {
return rowRelative.isSet(field_3_first_column);
}
/**
* @return whether or not the first row is a relative reference or not.
*/
public final boolean isFirstRowRelative() {
return rowRelative.isSet(field_3_first_column);
}
/**
* sets the first row to relative or not
* @param rel is relative or not.
*/
public final void setFirstRowRelative(boolean rel) {
field_3_first_column=rowRelative.setBoolean(field_3_first_column,rel);
}
/**
* sets the first row to relative or not
* @param rel is relative or not.
*/
public final void setFirstRowRelative(boolean rel) {
field_3_first_column=rowRelative.setBoolean(field_3_first_column,rel);
}
/**
* @return isrelative first column to relative or not
*/
public final boolean isFirstColRelative() {
return colRelative.isSet(field_3_first_column);
}
/**
* @return isrelative first column to relative or not
*/
public final boolean isFirstColRelative() {
return colRelative.isSet(field_3_first_column);
}
/**
* set whether the first column is relative
*/
public final void setFirstColRelative(boolean rel) {
field_3_first_column=colRelative.setBoolean(field_3_first_column,rel);
}
/**
* set whether the first column is relative
*/
public final void setFirstColRelative(boolean rel) {
field_3_first_column=colRelative.setBoolean(field_3_first_column,rel);
}
/**
* set the first column in the area
*/
public final void setFirstColumn(int colIx) {
field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
}
/**
* set the first column in the area
*/
public final void setFirstColumn(int colIx) {
field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
}
/**
* set the first column irrespective of the bitmasks
*/
public final void setFirstColumnRaw(int column) {
field_3_first_column = column;
}
/**
* set the first column irrespective of the bitmasks
*/
public final void setFirstColumnRaw(int column) {
field_3_first_column = column;
}
/**
* @return lastcolumn in the area
*/
public final int getLastColumn() {
return columnMask.getValue(field_4_last_column);
}
/**
* @return lastcolumn in the area
*/
public final int getLastColumn() {
return columnMask.getValue(field_4_last_column);
}
/**
* @return last column and bitmask (the raw field)
*/
public final short getLastColumnRaw() {
return (short) field_4_last_column;
}
/**
* @return last column and bitmask (the raw field)
*/
public final short getLastColumnRaw() {
return (short) field_4_last_column;
}
/**
* @return last row relative or not
*/
public final boolean isLastRowRelative() {
return rowRelative.isSet(field_4_last_column);
}
/**
* @return last row relative or not
*/
public final boolean isLastRowRelative() {
return rowRelative.isSet(field_4_last_column);
}
/**
* set whether the last row is relative or not
* @param rel <code>true</code> if the last row relative, else
* <code>false</code>
*/
public final void setLastRowRelative(boolean rel) {
field_4_last_column=rowRelative.setBoolean(field_4_last_column,rel);
}
/**
* set whether the last row is relative or not
* @param rel <code>true</code> if the last row relative, else
* <code>false</code>
*/
public final void setLastRowRelative(boolean rel) {
field_4_last_column=rowRelative.setBoolean(field_4_last_column,rel);
}
/**
* @return lastcol relative or not
*/
public final boolean isLastColRelative() {
return colRelative.isSet(field_4_last_column);
}
/**
* @return lastcol relative or not
*/
public final boolean isLastColRelative() {
return colRelative.isSet(field_4_last_column);
}
/**
* set whether the last column should be relative or not
*/
public final void setLastColRelative(boolean rel) {
field_4_last_column=colRelative.setBoolean(field_4_last_column,rel);
}
/**
* set whether the last column should be relative or not
*/
public final void setLastColRelative(boolean rel) {
field_4_last_column=colRelative.setBoolean(field_4_last_column,rel);
}
/**
* set the last column in the area
*/
public final void setLastColumn(int colIx) {
field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
}
/**
* set the last column in the area
*/
public final void setLastColumn(int colIx) {
field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
}
/**
* set the last column irrespective of the bitmasks
*/
public final void setLastColumnRaw(short column) {
field_4_last_column = column;
}
protected final String formatReferenceAsString() {
CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());
/**
* set the last column irrespective of the bitmasks
*/
public final void setLastColumnRaw(short column) {
field_4_last_column = column;
}
protected final String formatReferenceAsString() {
CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());
if(AreaReference.isWholeColumnReference(SpreadsheetVersion.EXCEL97, topLeft, botRight)) {
return (new AreaReference(topLeft, botRight)).formatAsString();
}
return topLeft.formatAsString() + ":" + botRight.formatAsString();
}
if(AreaReference.isWholeColumnReference(SpreadsheetVersion.EXCEL97, topLeft, botRight)) {
return (new AreaReference(topLeft, botRight)).formatAsString();
}
return topLeft.formatAsString() + ":" + botRight.formatAsString();
}
public String toFormulaString() {
return formatReferenceAsString();
}
public String toFormulaString() {
return formatReferenceAsString();
}
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
}
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
}
}

View File

@ -28,7 +28,7 @@ import org.apache.poi.xssf.usermodel.BaseXSSFEvaluationWorkbook;
* SXSSF wrapper around the SXSSF and XSSF workbooks
*/
public final class SXSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook {
private SXSSFWorkbook _uBook;
private final SXSSFWorkbook _uBook;
public static SXSSFEvaluationWorkbook create(SXSSFWorkbook book) {
if (book == null) {
@ -41,16 +41,19 @@ public final class SXSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook {
super(book.getXSSFWorkbook());
_uBook = book;
}
@Override
public int getSheetIndex(EvaluationSheet evalSheet) {
SXSSFSheet sheet = ((SXSSFEvaluationSheet)evalSheet).getSXSSFSheet();
return _uBook.getSheetIndex(sheet);
}
@Override
public EvaluationSheet getSheet(int sheetIndex) {
return new SXSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
}
@Override
public Ptg[] getFormulaTokens(EvaluationCell evalCell) {
SXSSFCell cell = ((SXSSFEvaluationCell)evalCell).getSXSSFCell();
SXSSFEvaluationWorkbook frBook = SXSSFEvaluationWorkbook.create(_uBook);

View File

@ -17,7 +17,10 @@
package org.apache.poi.xssf.usermodel;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.EvaluationName;
@ -36,6 +39,8 @@ import org.apache.poi.ss.formula.ptg.Ptg;
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Table;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.NotImplemented;
@ -46,31 +51,31 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
* Internal POI use only - parent of XSSF and SXSSF evaluation workbooks
*/
public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {
protected final XSSFWorkbook _uBook;
protected final XSSFWorkbook _uBook;
protected BaseXSSFEvaluationWorkbook(XSSFWorkbook book) {
_uBook = book;
}
protected BaseXSSFEvaluationWorkbook(XSSFWorkbook book) {
_uBook = book;
}
private int convertFromExternalSheetIndex(int externSheetIndex) {
return externSheetIndex;
}
/**
* XSSF doesn't use external sheet indexes, so when asked treat
* it just as a local index
*/
public int convertFromExternSheetIndex(int externSheetIndex) {
return externSheetIndex;
}
/**
* @return the external sheet index of the sheet with the given internal
* index. Used by some of the more obscure formula and named range things.
* Fairly easy on XSSF (we think...) since the internal and external
* indices are the same
*/
private int convertToExternalSheetIndex(int sheetIndex) {
return sheetIndex;
}
private int convertFromExternalSheetIndex(int externSheetIndex) {
return externSheetIndex;
}
/**
* XSSF doesn't use external sheet indexes, so when asked treat
* it just as a local index
*/
public int convertFromExternSheetIndex(int externSheetIndex) {
return externSheetIndex;
}
/**
* @return the external sheet index of the sheet with the given internal
* index. Used by some of the more obscure formula and named range things.
* Fairly easy on XSSF (we think...) since the internal and external
* indices are the same
*/
private int convertToExternalSheetIndex(int sheetIndex) {
return sheetIndex;
}
public int getExternalSheetIndex(String sheetName) {
int sheetIndex = _uBook.getSheetIndex(sheetName);
@ -131,37 +136,37 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
}
}
/**
* Return EvaluationName wrapper around the matching XSSFName (named range)
* @param name case-aware but case-insensitive named range in workbook
* @param sheetIndex index of sheet if named range scope is limited to one sheet
* if named range scope is global to the workbook, sheetIndex is -1.
* @return If name is a named range in the workbook, returns
* EvaluationName corresponding to that named range
* Returns null if there is no named range with the same name and scope in the workbook
*/
public EvaluationName getName(String name, int sheetIndex) {
for (int i = 0; i < _uBook.getNumberOfNames(); i++) {
XSSFName nm = _uBook.getNameAt(i);
String nameText = nm.getNameName();
int nameSheetindex = nm.getSheetIndex();
if (name.equalsIgnoreCase(nameText) &&
(nameSheetindex == -1 || nameSheetindex == sheetIndex)) {
return new Name(nm, i, this);
}
}
return sheetIndex == -1 ? null : getName(name, -1);
}
/**
* Return EvaluationName wrapper around the matching XSSFName (named range)
* @param name case-aware but case-insensitive named range in workbook
* @param sheetIndex index of sheet if named range scope is limited to one sheet
* if named range scope is global to the workbook, sheetIndex is -1.
* @return If name is a named range in the workbook, returns
* EvaluationName corresponding to that named range
* Returns null if there is no named range with the same name and scope in the workbook
*/
public EvaluationName getName(String name, int sheetIndex) {
for (int i = 0; i < _uBook.getNumberOfNames(); i++) {
XSSFName nm = _uBook.getNameAt(i);
String nameText = nm.getNameName();
int nameSheetindex = nm.getSheetIndex();
if (name.equalsIgnoreCase(nameText) &&
(nameSheetindex == -1 || nameSheetindex == sheetIndex)) {
return new Name(nm, i, this);
}
}
return sheetIndex == -1 ? null : getName(name, -1);
}
public String getSheetName(int sheetIndex) {
return _uBook.getSheetName(sheetIndex);
}
public ExternalName getExternalName(int externSheetIndex, int externNameIndex) {
public String getSheetName(int sheetIndex) {
return _uBook.getSheetName(sheetIndex);
}
public ExternalName getExternalName(int externSheetIndex, int externNameIndex) {
throw new IllegalStateException("HSSF-style external references are not supported for XSSF");
}
}
public ExternalName getExternalName(String nameName, String sheetName, int externalWorkbookNumber) {
public ExternalName getExternalName(String nameName, String sheetName, int externalWorkbookNumber) {
if (externalWorkbookNumber > 0) {
// External reference - reference is 1 based, link table is 0 based
int linkNumber = externalWorkbookNumber - 1;
@ -186,7 +191,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
int nameIdx = _uBook.getNameIndex(nameName);
return new ExternalName(nameName, nameIdx, 0); // TODO Is this right?
}
}
/**
@ -194,7 +199,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
*/
@Override
public NameXPxg getNameXPtg(String name, SheetIdentifier sheet) {
// First, try to find it as a User Defined Function
// First, try to find it as a User Defined Function
IndexedUDFFinder udfFinder = (IndexedUDFFinder)getUDFFinder();
FreeRefFunction func = udfFinder.findFunction(name);
if (func != null) {
@ -223,7 +228,7 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
} else {
return new NameXPxg(sheetName, name);
}
}
}
public Ptg get3DReferencePtg(CellReference cell, SheetIdentifier sheet) {
if (sheet._bookName != null) {
int bookIndex = resolveBookIndex(sheet._bookName);
@ -259,102 +264,151 @@ public abstract class BaseXSSFEvaluationWorkbook implements FormulaRenderingWork
return name;
}
public ExternalSheet getExternalSheet(int externSheetIndex) {
throw new IllegalStateException("HSSF-style external references are not supported for XSSF");
}
public ExternalSheet getExternalSheet(String firstSheetName, String lastSheetName, int externalWorkbookNumber) {
String workbookName;
if (externalWorkbookNumber > 0) {
// External reference - reference is 1 based, link table is 0 based
int linkNumber = externalWorkbookNumber - 1;
ExternalLinksTable linkTable = _uBook.getExternalLinksTable().get(linkNumber);
workbookName = linkTable.getLinkedFileName();
} else {
// Internal reference
workbookName = null;
}
if (lastSheetName == null || firstSheetName.equals(lastSheetName)) {
return new ExternalSheet(workbookName, firstSheetName);
} else {
return new ExternalSheetRange(workbookName, firstSheetName, lastSheetName);
}
public ExternalSheet getExternalSheet(int externSheetIndex) {
throw new IllegalStateException("HSSF-style external references are not supported for XSSF");
}
public ExternalSheet getExternalSheet(String firstSheetName, String lastSheetName, int externalWorkbookNumber) {
String workbookName;
if (externalWorkbookNumber > 0) {
// External reference - reference is 1 based, link table is 0 based
int linkNumber = externalWorkbookNumber - 1;
ExternalLinksTable linkTable = _uBook.getExternalLinksTable().get(linkNumber);
workbookName = linkTable.getLinkedFileName();
} else {
// Internal reference
workbookName = null;
}
if (lastSheetName == null || firstSheetName.equals(lastSheetName)) {
return new ExternalSheet(workbookName, firstSheetName);
} else {
return new ExternalSheetRange(workbookName, firstSheetName, lastSheetName);
}
}
@NotImplemented
public int getExternalSheetIndex(String workbookName, String sheetName) {
throw new RuntimeException("not implemented yet");
}
public int getSheetIndex(String sheetName) {
return _uBook.getSheetIndex(sheetName);
}
throw new RuntimeException("not implemented yet");
}
public int getSheetIndex(String sheetName) {
return _uBook.getSheetIndex(sheetName);
}
public String getSheetFirstNameByExternSheet(int externSheetIndex) {
int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);
return _uBook.getSheetName(sheetIndex);
}
public String getSheetFirstNameByExternSheet(int externSheetIndex) {
int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);
return _uBook.getSheetName(sheetIndex);
}
public String getSheetLastNameByExternSheet(int externSheetIndex) {
// XSSF does multi-sheet references differently, so this is the same as the first
return getSheetFirstNameByExternSheet(externSheetIndex);
}
public String getNameText(NamePtg namePtg) {
return _uBook.getNameAt(namePtg.getIndex()).getNameName();
}
public EvaluationName getName(NamePtg namePtg) {
int ix = namePtg.getIndex();
return new Name(_uBook.getNameAt(ix), ix, this);
}
@Override
public XSSFName createName() {
return _uBook.createName();
}
public String getNameText(NamePtg namePtg) {
return _uBook.getNameAt(namePtg.getIndex()).getNameName();
}
public EvaluationName getName(NamePtg namePtg) {
int ix = namePtg.getIndex();
return new Name(_uBook.getNameAt(ix), ix, this);
}
@Override
public XSSFName createName() {
return _uBook.createName();
}
private static String caseInsensitive(String s) {
return s.toUpperCase(Locale.ROOT);
}
/*
* TODO: data tables are stored at the workbook level in XSSF, but are bound to a single sheet.
* The current code structure has them hanging off XSSFSheet, but formulas reference them
* only by name (names are global, and case insensitive).
* This map stores names as lower case for case-insensitive lookups.
*
* FIXME: Caching tables by name here for fast formula lookup means the map is out of date if
* a table is renamed or added/removed to a sheet after the map is created.
*
* Perhaps tables can be managed similar to PivotTable references above?
*/
private Map<String, XSSFTable> _tableCache = null;
private Map<String, XSSFTable> getTableCache() {
if ( _tableCache != null ) {
return _tableCache;
}
// FIXME: use org.apache.commons.collections.map.CaseInsensitiveMap
_tableCache = new HashMap<String, XSSFTable>();
for (Sheet sheet : _uBook) {
for (XSSFTable tbl : ((XSSFSheet)sheet).getTables()) {
String lname = caseInsensitive(tbl.getName());
_tableCache.put(lname, tbl);
}
}
return _tableCache;
}
/**
* Returns the data table with the given name (case insensitive).
* Tables are cached for performance (formula evaluation looks them up by name repeatedly).
* After the first table lookup, adding or removing a table from the document structure will cause trouble.
* This is meant to be used on documents whose structure is essentially static at the point formulas are evaluated.
*
* @param name the data table name (case-insensitive)
* @return The Data table in the workbook named <tt>name</tt>, or <tt>null</tt> if no table is named <tt>name</tt>.
* @since 3.15 beta 2
*/
@Override
public XSSFTable getTable(String name) {
if (name == null) return null;
String lname = caseInsensitive(name);
return getTableCache().get(lname);
}
public UDFFinder getUDFFinder(){
return _uBook.getUDFFinder();
}
private static final class Name implements EvaluationName {
private static final class Name implements EvaluationName {
private final XSSFName _nameRecord;
private final int _index;
private final FormulaParsingWorkbook _fpBook;
private final XSSFName _nameRecord;
private final int _index;
private final FormulaParsingWorkbook _fpBook;
public Name(XSSFName name, int index, FormulaParsingWorkbook fpBook) {
_nameRecord = name;
_index = index;
_fpBook = fpBook;
}
public Name(XSSFName name, int index, FormulaParsingWorkbook fpBook) {
_nameRecord = name;
_index = index;
_fpBook = fpBook;
}
public Ptg[] getNameDefinition() {
public Ptg[] getNameDefinition() {
return FormulaParser.parse(_nameRecord.getRefersToFormula(), _fpBook, FormulaType.NAMEDRANGE, _nameRecord.getSheetIndex());
}
return FormulaParser.parse(_nameRecord.getRefersToFormula(), _fpBook, FormulaType.NAMEDRANGE, _nameRecord.getSheetIndex());
}
public String getNameText() {
return _nameRecord.getNameName();
}
public String getNameText() {
return _nameRecord.getNameName();
}
public boolean hasFormula() {
// TODO - no idea if this is right
CTDefinedName ctn = _nameRecord.getCTName();
String strVal = ctn.getStringValue();
return !ctn.getFunction() && strVal != null && strVal.length() > 0;
}
public boolean hasFormula() {
// TODO - no idea if this is right
CTDefinedName ctn = _nameRecord.getCTName();
String strVal = ctn.getStringValue();
return !ctn.getFunction() && strVal != null && strVal.length() > 0;
}
public boolean isFunctionName() {
return _nameRecord.isFunctionName();
}
public boolean isFunctionName() {
return _nameRecord.isFunctionName();
}
public boolean isRange() {
return hasFormula(); // TODO - is this right?
}
public NamePtg createPtg() {
return new NamePtg(_index);
}
}
public boolean isRange() {
return hasFormula(); // TODO - is this right?
}
public NamePtg createPtg() {
return new NamePtg(_index);
}
}
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL2007;
}
public SpreadsheetVersion getSpreadsheetVersion(){
return SpreadsheetVersion.EXCEL2007;
}
}

View File

@ -406,9 +406,9 @@ public final class XSSFCell implements Cell {
if (cachedValueType != expectedTypeCode) {
throw typeMismatch(expectedTypeCode, cachedValueType, true);
}
}
}
/**
/**
* Set a string value for the cell.
*
* @param str value to set the cell to. For formulas we'll set the formula
@ -506,7 +506,7 @@ public final class XSSFCell implements Cell {
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(sheet.getWorkbook());
SharedFormula sf = new SharedFormula(SpreadsheetVersion.EXCEL2007);
Ptg[] ptgs = FormulaParser.parse(sharedFormula, fpb, FormulaType.CELL, sheetIndex);
Ptg[] ptgs = FormulaParser.parse(sharedFormula, fpb, FormulaType.CELL, sheetIndex, getRowIndex());
Ptg[] fmla = sf.convertSharedFormulas(ptgs,
getRowIndex() - ref.getFirstRow(), getColumnIndex() - ref.getFirstColumn());
return FormulaRenderer.toFormulaString(fpb, fmla);
@ -550,7 +550,7 @@ public final class XSSFCell implements Cell {
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
//validate through the FormulaParser
FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()));
FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()), getRowIndex());
CTCellFormula f = CTCellFormula.Factory.newInstance();
f.setStringValue(formula);
@ -925,8 +925,8 @@ public final class XSSFCell implements Cell {
throw new IllegalArgumentException("Illegal cell type: " + cellType);
}
if (cellType != CELL_TYPE_FORMULA && _cell.isSetF()) {
_cell.unsetF();
}
_cell.unsetF();
}
}
/**

View File

@ -27,29 +27,32 @@ import org.apache.poi.ss.formula.ptg.Ptg;
* Internal POI use only
*/
public final class XSSFEvaluationWorkbook extends BaseXSSFEvaluationWorkbook {
public static XSSFEvaluationWorkbook create(XSSFWorkbook book) {
if (book == null) {
return null;
}
return new XSSFEvaluationWorkbook(book);
}
public static XSSFEvaluationWorkbook create(XSSFWorkbook book) {
if (book == null) {
return null;
}
return new XSSFEvaluationWorkbook(book);
}
private XSSFEvaluationWorkbook(XSSFWorkbook book) {
super(book);
}
private XSSFEvaluationWorkbook(XSSFWorkbook book) {
super(book);
}
public int getSheetIndex(EvaluationSheet evalSheet) {
XSSFSheet sheet = ((XSSFEvaluationSheet)evalSheet).getXSSFSheet();
return _uBook.getSheetIndex(sheet);
}
@Override
public int getSheetIndex(EvaluationSheet evalSheet) {
XSSFSheet sheet = ((XSSFEvaluationSheet)evalSheet).getXSSFSheet();
return _uBook.getSheetIndex(sheet);
}
public EvaluationSheet getSheet(int sheetIndex) {
return new XSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
}
@Override
public EvaluationSheet getSheet(int sheetIndex) {
return new XSSFEvaluationSheet(_uBook.getSheetAt(sheetIndex));
}
@Override
public Ptg[] getFormulaTokens(EvaluationCell evalCell) {
XSSFCell cell = ((XSSFEvaluationCell)evalCell).getXSSFCell();
XSSFEvaluationWorkbook frBook = XSSFEvaluationWorkbook.create(_uBook);
return FormulaParser.parse(cell.getCellFormula(), frBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet()));
return FormulaParser.parse(cell.getCellFormula(), frBook, FormulaType.CELL, _uBook.getSheetIndex(cell.getSheet()), cell.getRowIndex());
}
}

View File

@ -192,7 +192,7 @@ public final class XSSFName implements Name {
public void setRefersToFormula(String formulaText) {
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(_workbook);
//validate through the FormulaParser
FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex());
FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex(), -1);
_ctName.setStringValue(formulaText);
}
@ -203,7 +203,7 @@ public final class XSSFName implements Name {
return false;
}
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(_workbook);
Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex());
Ptg[] ptgs = FormulaParser.parse(formulaText, fpb, FormulaType.NAMEDRANGE, getSheetIndex(), -1);
return Ptg.doesFormulaReferToDeletedCell(ptgs);
}

View File

@ -24,13 +24,17 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.ss.usermodel.Table;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.helpers.XSSFXmlColumnPr;
import org.apache.poi.util.StringUtil;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;
@ -48,10 +52,12 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.TableDocument;
*
* @author Roberto Manicardi
*/
public class XSSFTable extends POIXMLDocumentPart {
public class XSSFTable extends POIXMLDocumentPart implements Table {
private CTTable ctTable;
private List<XSSFXmlColumnPr> xmlColumnPr;
private CTTableColumn[] ctColumns;
private HashMap<String, Integer> columnMap;
private CellReference startCellReference;
private CellReference endCellReference;
private String commonXPath;
@ -107,7 +113,7 @@ public class XSSFTable extends POIXMLDocumentPart {
out.close();
}
public CTTable getCTTable(){
public CTTable getCTTable() {
return ctTable;
}
@ -117,32 +123,41 @@ public class XSSFTable extends POIXMLDocumentPart {
* @return true if the Table element contain mappings
*/
public boolean mapsTo(long id){
boolean maps =false;
List<XSSFXmlColumnPr> pointers = getXmlColumnPrs();
for (XSSFXmlColumnPr pointer: pointers) {
if (pointer.getMapId()==id) {
maps=true;
break;
return true;
}
}
return maps;
return false;
}
/**
* caches table columns for performance.
* Updated via updateHeaders
* @since 3.15 beta 2
*/
private CTTableColumn[] getTableColumns() {
if (ctColumns == null) {
ctColumns = ctTable.getTableColumns().getTableColumnArray();
}
return ctColumns;
}
/**
*
* Calculates the xpath of the root element for the table. This will be the common part
* of all the mapping's xpaths
* Note: this function caches the result for performance. To flush the cache {@link #updateHeaders()} must be called.
*
* @return the xpath of the table's root element
*/
public String getCommonXpath() {
if (commonXPath == null) {
String[] commonTokens = {};
for (CTTableColumn column :ctTable.getTableColumns().getTableColumnArray()) {
for (CTTableColumn column : getTableColumns()) {
if (column.getXmlColumnPr()!=null) {
String xpath = column.getXmlColumnPr().getXpath();
String[] tokens = xpath.split("/");
@ -166,21 +181,24 @@ public class XSSFTable extends POIXMLDocumentPart {
}
}
commonXPath = "";
for (int i = 1 ; i< commonTokens.length;i++) {
commonXPath +="/"+commonTokens[i];
}
commonTokens[0] = "";
commonXPath = StringUtil.join(commonTokens, "/");
}
return commonXPath;
}
/**
* Note this list is static - once read, it does not notice later changes to the underlying column structures
* To clear the cache, call {@link #updateHeaders}
* @return List of XSSFXmlColumnPr
*/
public List<XSSFXmlColumnPr> getXmlColumnPrs() {
if (xmlColumnPr==null) {
xmlColumnPr = new ArrayList<XSSFXmlColumnPr>();
for (CTTableColumn column:ctTable.getTableColumns().getTableColumnArray()) {
for (CTTableColumn column: getTableColumns()) {
if (column.getXmlColumnPr()!=null) {
XSSFXmlColumnPr columnPr = new XSSFXmlColumnPr(this,column,column.getXmlColumnPr());
xmlColumnPr.add(columnPr);
@ -283,7 +301,7 @@ public class XSSFTable extends POIXMLDocumentPart {
* Headers <em>must</em> be in sync, otherwise Excel will display a
* "Found unreadable content" message on startup.
*/
public void updateHeaders(){
public void updateHeaders() {
XSSFSheet sheet = (XSSFSheet)getParent();
CellReference ref = getStartCellReference();
if(ref == null) return;
@ -301,6 +319,83 @@ public class XSSFTable extends POIXMLDocumentPart {
}
cellnum++;
}
ctColumns = null;
columnMap = null;
xmlColumnPr = null;
commonXPath = null;
}
}
private static String caseInsensitive(String s) {
return s.toUpperCase(Locale.ROOT);
}
/**
* Gets the relative column index of a column in this table having the header name <code>column</code>.
* The column index is relative to the left-most column in the table, 0-indexed.
* Returns <code>-1</code> if <code>column</code> is not a header name in table.
*
* Note: this function caches column names for performance. To flush the cache (because columns
* have been moved or column headers have been changed), {@link #updateHeaders()} must be called.
*
* @since 3.15 beta 2
*/
public int findColumnIndex(String column) {
if (columnMap == null) {
// FIXME: replace with org.apache.commons.collections.map.CaseInsensitiveMap
int count = getTableColumns().length;
columnMap = new HashMap<String, Integer>(count);
for (int i=0; i < count; i++) {
String columnName = getTableColumns()[i].getName();
columnMap.put(caseInsensitive(columnName), i);
}
}
// Table column names with special characters need a single quote escape
// but the escape is not present in the column definition
Integer idx = columnMap.get(caseInsensitive(column.replace("'", "")));
return idx == null ? -1 : idx.intValue();
}
/**
* @since 3.15 beta 2
*/
public String getSheetName() {
return getXSSFSheet().getSheetName();
}
/**
* @since 3.15 beta 2
*/
public boolean isHasTotalsRow() {
return ctTable.getTotalsRowShown();
}
/**
* @since 3.15 beta 2
*/
public int getStartColIndex() {
return getStartCellReference().getCol();
}
/**
* @since 3.15 beta 2
*/
public int getStartRowIndex() {
return getStartCellReference().getRow();
}
/**
* @since 3.15 beta 2
*/
public int getEndColIndex() {
return getEndCellReference().getCol();
}
/**
* @since 3.15 beta 2
*/
public int getEndRowIndex() {
return getEndCellReference().getRow();
}
}

View File

@ -33,6 +33,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;
@ -1761,7 +1762,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
* Get the document's embedded files.
*/
@Override
public List<PackagePart> getAllEmbedds() throws OpenXML4JException {
public List<PackagePart> getAllEmbedds() throws OpenXML4JException {
List<PackagePart> embedds = new LinkedList<PackagePart>();
for(XSSFSheet sheet : sheets){
@ -1928,7 +1929,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
*/
@Internal
public MapInfo getMapInfo(){
return mapInfo;
return mapInfo;
}
/**
@ -1945,92 +1946,92 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
throw new RuntimeException("Not Implemented - see bug #57184");
}
/**
* Specifies a boolean value that indicates whether structure of workbook is locked. <br/>
* A value true indicates the structure of the workbook is locked. Worksheets in the workbook can't be moved,
* deleted, hidden, unhidden, or renamed, and new worksheets can't be inserted.<br/>
* A value of false indicates the structure of the workbook is not locked.<br/>
*
* @return true if structure of workbook is locked
*/
public boolean isStructureLocked() {
return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockStructure();
}
/**
* Specifies a boolean value that indicates whether structure of workbook is locked. <br/>
* A value true indicates the structure of the workbook is locked. Worksheets in the workbook can't be moved,
* deleted, hidden, unhidden, or renamed, and new worksheets can't be inserted.<br/>
* A value of false indicates the structure of the workbook is not locked.<br/>
*
* @return true if structure of workbook is locked
*/
public boolean isStructureLocked() {
return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockStructure();
}
/**
* Specifies a boolean value that indicates whether the windows that comprise the workbook are locked. <br/>
* A value of true indicates the workbook windows are locked. Windows are the same size and position each time the
* workbook is opened.<br/>
* A value of false indicates the workbook windows are not locked.
*
* @return true if windows that comprise the workbook are locked
*/
public boolean isWindowsLocked() {
return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockWindows();
}
/**
* Specifies a boolean value that indicates whether the windows that comprise the workbook are locked. <br/>
* A value of true indicates the workbook windows are locked. Windows are the same size and position each time the
* workbook is opened.<br/>
* A value of false indicates the workbook windows are not locked.
*
* @return true if windows that comprise the workbook are locked
*/
public boolean isWindowsLocked() {
return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockWindows();
}
/**
* Specifies a boolean value that indicates whether the workbook is locked for revisions.
*
* @return true if the workbook is locked for revisions.
*/
public boolean isRevisionLocked() {
return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockRevision();
}
/**
* Specifies a boolean value that indicates whether the workbook is locked for revisions.
*
* @return true if the workbook is locked for revisions.
*/
public boolean isRevisionLocked() {
return workbookProtectionPresent() && workbook.getWorkbookProtection().getLockRevision();
}
/**
* Locks the structure of workbook.
*/
public void lockStructure() {
safeGetWorkbookProtection().setLockStructure(true);
}
/**
* Locks the structure of workbook.
*/
public void lockStructure() {
safeGetWorkbookProtection().setLockStructure(true);
}
/**
* Unlocks the structure of workbook.
*/
public void unLockStructure() {
safeGetWorkbookProtection().setLockStructure(false);
}
/**
* Unlocks the structure of workbook.
*/
public void unLockStructure() {
safeGetWorkbookProtection().setLockStructure(false);
}
/**
* Locks the windows that comprise the workbook.
*/
public void lockWindows() {
safeGetWorkbookProtection().setLockWindows(true);
}
/**
* Locks the windows that comprise the workbook.
*/
public void lockWindows() {
safeGetWorkbookProtection().setLockWindows(true);
}
/**
* Unlocks the windows that comprise the workbook.
*/
public void unLockWindows() {
safeGetWorkbookProtection().setLockWindows(false);
}
/**
* Unlocks the windows that comprise the workbook.
*/
public void unLockWindows() {
safeGetWorkbookProtection().setLockWindows(false);
}
/**
* Locks the workbook for revisions.
*/
public void lockRevision() {
safeGetWorkbookProtection().setLockRevision(true);
}
/**
* Locks the workbook for revisions.
*/
public void lockRevision() {
safeGetWorkbookProtection().setLockRevision(true);
}
/**
* Unlocks the workbook for revisions.
*/
public void unLockRevision() {
safeGetWorkbookProtection().setLockRevision(false);
}
/**
* Unlocks the workbook for revisions.
*/
public void unLockRevision() {
safeGetWorkbookProtection().setLockRevision(false);
}
/**
* Sets the workbook password.
*
* @param password if null, the password will be removed
* @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier)
* otherwise the given algorithm is used for calculating the hash password (Excel 2013)
*/
public void setWorkbookPassword(String password, HashAlgorithm hashAlgo) {
/**
* Sets the workbook password.
*
* @param password if null, the password will be removed
* @param hashAlgo if null, the password will be set as XOR password (Excel 2010 and earlier)
* otherwise the given algorithm is used for calculating the hash password (Excel 2013)
*/
public void setWorkbookPassword(String password, HashAlgorithm hashAlgo) {
if (password == null && !workbookProtectionPresent()) return;
setPassword(safeGetWorkbookProtection(), password, hashAlgo, "workbook");
}
}
/**
* Validate the password against the stored hash, the hashing method will be determined
@ -2073,9 +2074,9 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
}
}
private boolean workbookProtectionPresent() {
return workbook.isSetWorkbookProtection();
}
private boolean workbookProtectionPresent() {
return workbook.isSetWorkbookProtection();
}
private CTWorkbookProtection safeGetWorkbookProtection() {
if (!workbookProtectionPresent()){
@ -2083,7 +2084,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
}
return workbook.getWorkbookProtection();
}
/**
*
* Returns the locator of user-defined functions.
@ -2261,4 +2262,24 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
public SpreadsheetVersion getSpreadsheetVersion() {
return SpreadsheetVersion.EXCEL2007;
}
/**
* Returns the data table with the given name (case insensitive).
*
* @param name the data table name (case-insensitive)
* @return The Data table in the workbook named <tt>name</tt>, or <tt>null</tt> if no table is named <tt>name</tt>.
* @since 3.15 beta 2
*/
public XSSFTable getTable(String name) {
if (name != null && sheets != null) {
for (XSSFSheet sheet : sheets) {
for (XSSFTable tbl : sheet.getTables()) {
if (name.equalsIgnoreCase(tbl.getName())) {
return tbl;
}
}
}
}
return null;
}
}

View File

@ -94,7 +94,7 @@ public final class XSSFFormulaUtils {
String formula = f.getStringValue();
if (formula != null && formula.length() > 0) {
int sheetIndex = _wb.getSheetIndex(cell.getSheet());
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.CELL, sheetIndex);
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.CELL, sheetIndex, cell.getRowIndex());
for (Ptg ptg : ptgs) {
updatePtg(ptg, oldName, newName);
}
@ -113,7 +113,8 @@ public final class XSSFFormulaUtils {
String formula = name.getRefersToFormula();
if (formula != null) {
int sheetIndex = name.getSheetIndex();
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.NAMEDRANGE, sheetIndex);
int rowIndex = -1; //don't care
Ptg[] ptgs = FormulaParser.parse(formula, _fpwb, FormulaType.NAMEDRANGE, sheetIndex, rowIndex);
for (Ptg ptg : ptgs) {
updatePtg(ptg, oldName, newName);
}

View File

@ -134,8 +134,9 @@ public final class XSSFRowShifter {
XSSFName name = wb.getNameAt(i);
String formula = name.getRefersToFormula();
int sheetIndex = name.getSheetIndex();
final int rowIndex = -1; //don't care, named ranges are not allowed to include structured references
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.NAMEDRANGE, sheetIndex);
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.NAMEDRANGE, sheetIndex, rowIndex);
if (shifter.adjustFormula(ptgs, sheetIndex)) {
String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
name.setRefersToFormula(shiftedFmla);
@ -218,10 +219,11 @@ public final class XSSFRowShifter {
XSSFSheet sheet = row.getSheet();
XSSFWorkbook wb = sheet.getWorkbook();
int sheetIndex = wb.getSheetIndex(sheet);
final int rowIndex = row.getRowNum();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
try {
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex);
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex, rowIndex);
String shiftedFmla = null;
if (shifter.adjustFormula(ptgs, sheetIndex)) {
shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
@ -238,6 +240,7 @@ public final class XSSFRowShifter {
public void updateConditionalFormatting(FormulaShifter shifter) {
XSSFWorkbook wb = sheet.getWorkbook();
int sheetIndex = wb.getSheetIndex(sheet);
final int rowIndex = -1; //don't care, structured references not allowed in conditional formatting
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
CTWorksheet ctWorksheet = sheet.getCTWorksheet();
@ -283,7 +286,7 @@ public final class XSSFRowShifter {
String[] formulaArray = cfRule.getFormulaArray();
for (int i = 0; i < formulaArray.length; i++) {
String formula = formulaArray[i];
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex);
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex, rowIndex);
if (shifter.adjustFormula(ptgs, sheetIndex)) {
String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
cfRule.setFormulaArray(i, shiftedFmla);

View File

@ -18,6 +18,11 @@
*/
package org.apache.poi.ss.formula;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
@ -28,7 +33,7 @@ import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import junit.framework.TestCase;
import org.junit.Test;
/**
* Test {@link FormulaParser}'s handling of row numbers at the edge of the
@ -36,8 +41,9 @@ import junit.framework.TestCase;
*
* @author David North
*/
public class TestFormulaParser extends TestCase {
public class TestFormulaParser {
@Test
public void testHSSFFailsForOver65536() {
FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(new HSSFWorkbook());
try {
@ -49,16 +55,19 @@ public class TestFormulaParser extends TestCase {
}
}
@Test
public void testHSSFPassCase() {
FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(new HSSFWorkbook());
FormulaParser.parse("Sheet1!1:65536", workbook, FormulaType.CELL, 0);
}
@Test
public void testXSSFWorksForOver65536() {
FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(new XSSFWorkbook());
FormulaParser.parse("Sheet1!1:65537", workbook, FormulaType.CELL, 0);
}
@Test
public void testXSSFFailCase() {
FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(new XSSFWorkbook());
try {
@ -71,6 +80,7 @@ public class TestFormulaParser extends TestCase {
}
// copied from org.apache.poi.hssf.model.TestFormulaParser
@Test
public void testMacroFunction() throws Exception {
// testNames.xlsm contains a VB function called 'myFunc'
final String testFile = "testNames.xlsm";
@ -126,6 +136,7 @@ public class TestFormulaParser extends TestCase {
}
}
@Test
public void testParserErrors() throws Exception {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("testNames.xlsm");
try {

View File

@ -0,0 +1,63 @@
package org.apache.poi.ss.formula;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Table;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;
/**
* Tests Excel Table expressions (structured references)
* @see <a href="https://support.office.com/en-us/article/Using-structured-references-with-Excel-tables-F5ED2452-2337-4F71-BED3-C8AE6D2B276E">
* Excel Structured Reference Syntax
* </a>
*/
public class TestStructuredReferences {
/**
* Test the regular expression used in INDIRECT() evaluation to recognize structured references
*/
@Test
public void testTableExpressionSyntax() {
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("abc[col1]").matches());
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("_abc[col1]").matches());
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("_[col1]").matches());
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[col1]").matches());
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[col1]").matches());
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[#This Row]").matches());
assertTrue("Valid structured reference syntax didn't match expression", Table.isStructuredReference.matcher("\\[ [col1], [col2] ]").matches());
// can't have a space between the table name and open bracket
assertFalse("Invalid structured reference syntax didn't fail expression", Table.isStructuredReference.matcher("\\abc [ [col1], [col2] ]").matches());
}
@Test
public void testTableFormulas() throws Exception {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("StructuredReferences.xlsx");
try {
final FormulaEvaluator eval = new XSSFFormulaEvaluator(wb);
confirm(eval, wb.getSheet("Table").getRow(5).getCell(0), 49);
confirm(eval, wb.getSheet("Formulas").getRow(0).getCell(0), 209);
} finally {
wb.close();
}
}
private static void confirm(FormulaEvaluator fe, Cell cell, double expectedResult) {
fe.clearAllCachedResultValues();
CellValue cv = fe.evaluate(cell);
if (cv.getCellType() != Cell.CELL_TYPE_NUMERIC) {
fail("expected numeric cell type but got " + cv.formatAsString());
}
assertEquals(expectedResult, cv.getNumberValue(), 0.0);
}
}

View File

@ -22,6 +22,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
@ -41,12 +43,15 @@ import org.junit.Test;
import java.util.Arrays;
public final class TestXSSFFormulaParser {
private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla) {
return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1);
}
private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla) {
return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1);
}
private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla, int rowIndex) {
return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1, rowIndex);
}
@Test
public void basicParsing() {
@Test
public void basicParsing() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
Ptg[] ptgs;
@ -118,10 +123,12 @@ public final class TestXSSFFormulaParser {
assertEquals(AttrPtg.class, ptgs[1].getClass());
assertEquals("Sheet1!A1:B3", ptgs[0].toFormulaString());
assertEquals("SUM", ptgs[1].toFormulaString());
wb.close();
}
@Test
public void builtInFormulas() {
@Test
public void builtInFormulas() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
Ptg[] ptgs;
@ -134,10 +141,12 @@ public final class TestXSSFFormulaParser {
assertEquals(2, ptgs.length);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof IntPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof FuncPtg);
wb.close();
}
@Test
public void formaulReferncesSameWorkbook() {
public void formulaReferencesSameWorkbook() throws IOException {
// Use a test file with "other workbook" style references
// to itself
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56737.xlsx");
@ -153,10 +162,12 @@ public final class TestXSSFFormulaParser {
assertEquals(null, ((NameXPxg)ptgs[0]).getSheetName());
assertEquals("NR_Global_B2",((NameXPxg)ptgs[0]).getNameName());
assertEquals("[0]!NR_Global_B2",((NameXPxg)ptgs[0]).toFormulaString());
wb.close();
}
@Test
public void formulaReferencesOtherSheets() {
@Test
public void formulaReferencesOtherSheets() throws IOException {
// Use a test file with the named ranges in place
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56737.xlsx");
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
@ -193,10 +204,12 @@ public final class TestXSSFFormulaParser {
assertEquals(1, ptgs.length);
assertEquals(NamePtg.class, ptgs[0].getClass());
assertEquals("NR_Global_B2",((NamePtg)ptgs[0]).toFormulaString(fpb));
wb.close();
}
@Test
public void formulaReferencesOtherWorkbook() {
public void formulaReferencesOtherWorkbook() throws IOException {
// Use a test file with the external linked table in place
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("ref-56737.xlsx");
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
@ -228,6 +241,8 @@ public final class TestXSSFFormulaParser {
assertEquals(null, ((NameXPxg)ptgs[0]).getSheetName());
assertEquals("NR_Global_B2",((NameXPxg)ptgs[0]).getNameName());
assertEquals("[1]!NR_Global_B2",((NameXPxg)ptgs[0]).toFormulaString());
wb.close();
}
/**
@ -241,7 +256,7 @@ public final class TestXSSFFormulaParser {
* (but not evaluate - that's elsewhere in the test suite)
*/
@Test
public void multiSheetReferencesHSSFandXSSF() throws Exception {
public void multiSheetReferencesHSSFandXSSF() throws IOException {
Workbook[] wbs = new Workbook[] {
HSSFTestDataSamples.openSampleWorkbook("55906-MultiSheetRefs.xls"),
XSSFTestDataSamples.openSampleWorkbook("55906-MultiSheetRefs.xlsx")
@ -363,6 +378,8 @@ public final class TestXSSFFormulaParser {
newF = s1.getRow(0).createCell(11, Cell.CELL_TYPE_FORMULA);
newF.setCellFormula("MIN(Sheet1:Sheet2!A1:B2)");
assertEquals("MIN(Sheet1:Sheet2!A1:B2)", newF.getCellFormula());
wb.close();
}
}
@ -374,7 +391,7 @@ public final class TestXSSFFormulaParser {
}
@Test
public void test58648Single() {
public void test58648Single() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
Ptg[] ptgs;
@ -384,10 +401,12 @@ public final class TestXSSFFormulaParser {
2, ptgs.length);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof RefPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof ParenthesisPtg);
wb.close();
}
@Test
public void test58648Basic() {
public void test58648Basic() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
Ptg[] ptgs;
@ -431,10 +450,12 @@ public final class TestXSSFFormulaParser {
assertTrue("Had " + Arrays.toString(ptgs), ptgs[0] instanceof RefPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof ParenthesisPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[2] instanceof ParenthesisPtg);
wb.close();
}
@Test
public void test58648FormulaParsing() {
public void test58648FormulaParsing() throws IOException {
Workbook wb = XSSFTestDataSamples.openSampleWorkbook("58648.xlsx");
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
@ -460,10 +481,12 @@ public final class TestXSSFFormulaParser {
Cell cell = sheet.getRow(1).getCell(4);
assertEquals(5d, cell.getNumericCellValue(), 0d);
wb.close();
}
@Test
public void testWhitespaceInFormula() {
public void testWhitespaceInFormula() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
Ptg[] ptgs;
@ -505,10 +528,12 @@ public final class TestXSSFFormulaParser {
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof AreaPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[2] instanceof AreaPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[3] instanceof IntersectionPtg);
wb.close();
}
@Test
public void testWhitespaceInComplexFormula() {
public void testWhitespaceInComplexFormula() throws IOException {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
Ptg[] ptgs;
@ -529,5 +554,172 @@ public final class TestXSSFFormulaParser {
assertTrue("Had " + Arrays.toString(ptgs), ptgs[1] instanceof RefPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[2] instanceof AreaPtg);
assertTrue("Had " + Arrays.toString(ptgs), ptgs[3] instanceof NameXPxg);
wb.close();
}
@Test
public void parseStructuredReferences() throws IOException {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("StructuredReferences.xlsx");
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
Ptg[] ptgs;
/*
The following cases are tested (copied from FormulaParser.parseStructuredReference)
1 Table1[col]
2 Table1[[#Totals],[col]]
3 Table1[#Totals]
4 Table1[#All]
5 Table1[#Data]
6 Table1[#Headers]
7 Table1[#Totals]
8 Table1[#This Row]
9 Table1[[#All],[col]]
10 Table1[[#Headers],[col]]
11 Table1[[#Totals],[col]]
12 Table1[[#All],[col1]:[col2]]
13 Table1[[#Data],[col1]:[col2]]
14 Table1[[#Headers],[col1]:[col2]]
15 Table1[[#Totals],[col1]:[col2]]
16 Table1[[#Headers],[#Data],[col2]]
17 Table1[[#This Row], [col1]]
18 Table1[ [col1]:[col2] ]
*/
final String tbl = "\\_Prime.1";
final String noTotalsRowReason = ": Tables without a Totals row should return #REF! on [#Totals]";
////// Case 1: Evaluate Table1[col] with apostrophe-escaped #-signs ////////
ptgs = parse(fpb, "SUM("+tbl+"[calc='#*'#])");
assertEquals(2, ptgs.length);
// Area3DPxg [sheet=Table ! A2:A7]
assertTrue(ptgs[0] instanceof Area3DPxg);
Area3DPxg ptg0 = (Area3DPxg) ptgs[0];
assertEquals("Table", ptg0.getSheetName());
assertEquals("A2:A7", ptg0.format2DRefAsString());
// Note: structured references are evaluated and resolved to regular 3D area references.
assertEquals("Table!A2:A7", ptg0.toFormulaString());
// AttrPtg [sum ]
assertTrue(ptgs[1] instanceof AttrPtg);
AttrPtg ptg1 = (AttrPtg) ptgs[1];
assertTrue(ptg1.isSum());
////// Case 1: Evaluate "Table1[col]" ////////
ptgs = parse(fpb, tbl+"[Name]");
assertEquals(1, ptgs.length);
assertEquals("Table1[col]", "Table!B2:B7", ptgs[0].toFormulaString());
////// Case 2: Evaluate "Table1[[#Totals],[col]]" ////////
ptgs = parse(fpb, tbl+"[[#Totals],[col]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#Totals],[col]]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
////// Case 3: Evaluate "Table1[#Totals]" ////////
ptgs = parse(fpb, tbl+"[#Totals]");
assertEquals(1, ptgs.length);
assertEquals("Table1[#Totals]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
////// Case 4: Evaluate "Table1[#All]" ////////
ptgs = parse(fpb, tbl+"[#All]");
assertEquals(1, ptgs.length);
assertEquals("Table1[#All]", "Table!A1:C7", ptgs[0].toFormulaString());
////// Case 5: Evaluate "Table1[#Data]" (excludes Header and Data rows) ////////
ptgs = parse(fpb, tbl+"[#Data]");
assertEquals(1, ptgs.length);
assertEquals("Table1[#Data]", "Table!A2:C7", ptgs[0].toFormulaString());
////// Case 6: Evaluate "Table1[#Headers]" ////////
ptgs = parse(fpb, tbl+"[#Headers]");
assertEquals(1, ptgs.length);
assertEquals("Table1[#Headers]", "Table!A1:C1", ptgs[0].toFormulaString());
////// Case 7: Evaluate "Table1[#Totals]" ////////
ptgs = parse(fpb, tbl+"[#Totals]");
assertEquals(1, ptgs.length);
assertEquals("Table1[#Totals]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
////// Case 8: Evaluate "Table1[#This Row]" ////////
ptgs = parse(fpb, tbl+"[#This Row]", 2);
assertEquals(1, ptgs.length);
assertEquals("Table1[#This Row]", "Table!A3:C3", ptgs[0].toFormulaString());
////// Evaluate "Table1[@]" (equivalent to "Table1[#This Row]") ////////
ptgs = parse(fpb, tbl+"[@]", 2);
assertEquals(1, ptgs.length);
assertEquals("Table!A3:C3", ptgs[0].toFormulaString());
////// Evaluate "Table1[#This Row]" when rowIndex is outside Table ////////
ptgs = parse(fpb, tbl+"[#This Row]", 10);
assertEquals(1, ptgs.length);
assertEquals("Table1[#This Row]", ErrPtg.VALUE_INVALID, ptgs[0]);
////// Evaluate "Table1[@]" when rowIndex is outside Table ////////
ptgs = parse(fpb, tbl+"[@]", 10);
assertEquals(1, ptgs.length);
assertEquals("Table1[@]", ErrPtg.VALUE_INVALID, ptgs[0]);
////// Evaluate "Table1[[#Data],[col]]" ////////
ptgs = parse(fpb, tbl+"[[#Data], [Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#Data],[col]]", "Table!C2:C7", ptgs[0].toFormulaString());
////// Case 9: Evaluate "Table1[[#All],[col]]" ////////
ptgs = parse(fpb, tbl+"[[#All], [Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#All],[col]]", "Table!C1:C7", ptgs[0].toFormulaString());
////// Case 10: Evaluate "Table1[[#Headers],[col]]" ////////
ptgs = parse(fpb, tbl+"[[#Headers], [Number]]");
assertEquals(1, ptgs.length);
// also acceptable: Table1!B1
assertEquals("Table1[[#Headers],[col]]", "Table!C1:C1", ptgs[0].toFormulaString());
////// Case 11: Evaluate "Table1[[#Totals],[col]]" ////////
ptgs = parse(fpb, tbl+"[[#Totals],[Name]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#Totals],[col]]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
////// Case 12: Evaluate "Table1[[#All],[col1]:[col2]]" ////////
ptgs = parse(fpb, tbl+"[[#All], [Name]:[Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#All],[col1]:[col2]]", "Table!B1:C7", ptgs[0].toFormulaString());
////// Case 13: Evaluate "Table1[[#Data],[col]:[col2]]" ////////
ptgs = parse(fpb, tbl+"[[#Data], [Name]:[Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#Data],[col]:[col2]]", "Table!B2:C7", ptgs[0].toFormulaString());
////// Case 14: Evaluate "Table1[[#Headers],[col1]:[col2]]" ////////
ptgs = parse(fpb, tbl+"[[#Headers], [Name]:[Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#Headers],[col1]:[col2]]", "Table!B1:C1", ptgs[0].toFormulaString());
////// Case 15: Evaluate "Table1[[#Totals],[col]:[col2]]" ////////
ptgs = parse(fpb, tbl+"[[#Totals], [Name]:[Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#Totals],[col]:[col2]]" + noTotalsRowReason, ErrPtg.REF_INVALID, ptgs[0]);
////// Case 16: Evaluate "Table1[[#Headers],[#Data],[col]]" ////////
ptgs = parse(fpb, tbl+"[[#Headers],[#Data],[Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[#Headers],[#Data],[col]]", "Table!C1:C7", ptgs[0].toFormulaString());
////// Case 17: Evaluate "Table1[[#This Row], [col1]]" ////////
ptgs = parse(fpb, tbl+"[[#This Row], [Number]]", 2);
assertEquals(1, ptgs.length);
// also acceptable: Table!C3
assertEquals("Table1[[#This Row], [col1]]", "Table!C3:C3", ptgs[0].toFormulaString());
////// Case 18: Evaluate "Table1[[col]:[col2]]" ////////
ptgs = parse(fpb, tbl+"[[Name]:[Number]]");
assertEquals(1, ptgs.length);
assertEquals("Table1[[col]:[col2]]", "Table!B2:C7", ptgs[0].toFormulaString());
wb.close();
}
}

View File

@ -1137,7 +1137,7 @@ public final class TestXSSFSheet extends BaseTestXSheet {
}
/**
* See bug #50829
* See bug #50829 test data tables
*/
@Test
public void tables() throws IOException {

View File

@ -27,7 +27,7 @@ import org.junit.runners.Suite;
@Suite.SuiteClasses({
TestDrawingManager.class,
TestDrawingManager2.class,
TestFormulaParser.class,
//TestFormulaParser.class, //converted to junit4
TestFormulaParserEval.class,
TestFormulaParserIf.class,
TestLinkTable.class,

File diff suppressed because it is too large Load Diff

View File

@ -38,67 +38,67 @@ import org.apache.poi.util.LittleEndianInput;
*/
public final class TestSharedFormulaRecord extends TestCase {
/**
* A sample spreadsheet known to have one sheet with 4 shared formula ranges
*/
private static final String SHARED_FORMULA_TEST_XLS = "SharedFormulaTest.xls";
/**
* Binary data for an encoded formula. Taken from attachment 22062 (bugzilla 45123/45421).
* The shared formula is in Sheet1!C6:C21, with text "SUMPRODUCT(--(End_Acct=$C6),--(End_Bal))"
* This data is found at offset 0x1A4A (within the shared formula record).
* The critical thing about this formula is that it contains shared formula tokens (tRefN*,
* tAreaN*) with operand class 'array'.
*/
private static final byte[] SHARED_FORMULA_WITH_REF_ARRAYS_DATA = {
0x1A, 0x00,
0x63, 0x02, 0x00, 0x00, 0x00,
0x6C, 0x00, 0x00, 0x02, (byte)0x80, // tRefNA
0x0B,
0x15,
0x13,
0x13,
0x63, 0x03, 0x00, 0x00, 0x00,
0x15,
0x13,
0x13,
0x42, 0x02, (byte)0xE4, 0x00,
};
/**
* A sample spreadsheet known to have one sheet with 4 shared formula ranges
*/
private static final String SHARED_FORMULA_TEST_XLS = "SharedFormulaTest.xls";
/**
* Binary data for an encoded formula. Taken from attachment 22062 (bugzilla 45123/45421).
* The shared formula is in Sheet1!C6:C21, with text "SUMPRODUCT(--(End_Acct=$C6),--(End_Bal))"
* This data is found at offset 0x1A4A (within the shared formula record).
* The critical thing about this formula is that it contains shared formula tokens (tRefN*,
* tAreaN*) with operand class 'array'.
*/
private static final byte[] SHARED_FORMULA_WITH_REF_ARRAYS_DATA = {
0x1A, 0x00,
0x63, 0x02, 0x00, 0x00, 0x00,
0x6C, 0x00, 0x00, 0x02, (byte)0x80, // tRefNA
0x0B,
0x15,
0x13,
0x13,
0x63, 0x03, 0x00, 0x00, 0x00,
0x15,
0x13,
0x13,
0x42, 0x02, (byte)0xE4, 0x00,
};
/**
* The method <tt>SharedFormulaRecord.convertSharedFormulas()</tt> converts formulas from
* 'shared formula' to 'single cell formula' format. It is important that token operand
* classes are preserved during this transformation, because Excel may not tolerate the
* incorrect encoding. The formula here is one such example (Excel displays #VALUE!).
*/
public void testConvertSharedFormulasOperandClasses_bug45123() {
/**
* The method <tt>SharedFormulaRecord.convertSharedFormulas()</tt> converts formulas from
* 'shared formula' to 'single cell formula' format. It is important that token operand
* classes are preserved during this transformation, because Excel may not tolerate the
* incorrect encoding. The formula here is one such example (Excel displays #VALUE!).
*/
public void testConvertSharedFormulasOperandClasses_bug45123() {
LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(SHARED_FORMULA_WITH_REF_ARRAYS_DATA);
int encodedLen = in.readUShort();
Ptg[] sharedFormula = Ptg.readTokens(encodedLen, in);
LittleEndianInput in = TestcaseRecordInputStream.createLittleEndian(SHARED_FORMULA_WITH_REF_ARRAYS_DATA);
int encodedLen = in.readUShort();
Ptg[] sharedFormula = Ptg.readTokens(encodedLen, in);
SharedFormula sf = new SharedFormula(SpreadsheetVersion.EXCEL97);
Ptg[] convertedFormula = sf.convertSharedFormulas(sharedFormula, 100, 200);
Ptg[] convertedFormula = sf.convertSharedFormulas(sharedFormula, 100, 200);
RefPtg refPtg = (RefPtg) convertedFormula[1];
assertEquals("$C101", refPtg.toFormulaString());
if (refPtg.getPtgClass() == Ptg.CLASS_REF) {
throw new AssertionFailedError("Identified bug 45123");
}
RefPtg refPtg = (RefPtg) convertedFormula[1];
assertEquals("$C101", refPtg.toFormulaString());
if (refPtg.getPtgClass() == Ptg.CLASS_REF) {
throw new AssertionFailedError("Identified bug 45123");
}
confirmOperandClasses(sharedFormula, convertedFormula);
}
confirmOperandClasses(sharedFormula, convertedFormula);
}
private static void confirmOperandClasses(Ptg[] originalPtgs, Ptg[] convertedPtgs) {
assertEquals(originalPtgs.length, convertedPtgs.length);
for (int i = 0; i < convertedPtgs.length; i++) {
Ptg originalPtg = originalPtgs[i];
Ptg convertedPtg = convertedPtgs[i];
if (originalPtg.getPtgClass() != convertedPtg.getPtgClass()) {
throw new ComparisonFailure("Different operand class for token[" + i + "]",
String.valueOf(originalPtg.getPtgClass()), String.valueOf(convertedPtg.getPtgClass()));
}
}
}
private static void confirmOperandClasses(Ptg[] originalPtgs, Ptg[] convertedPtgs) {
assertEquals(originalPtgs.length, convertedPtgs.length);
for (int i = 0; i < convertedPtgs.length; i++) {
Ptg originalPtg = originalPtgs[i];
Ptg convertedPtg = convertedPtgs[i];
if (originalPtg.getPtgClass() != convertedPtg.getPtgClass()) {
throw new ComparisonFailure("Different operand class for token[" + i + "]",
String.valueOf(originalPtg.getPtgClass()), String.valueOf(convertedPtg.getPtgClass()));
}
}
}
public void testConvertSharedFormulas() {
HSSFWorkbook wb = new HSSFWorkbook();
@ -138,111 +138,111 @@ public final class TestSharedFormulaRecord extends TestCase {
}
/**
* Make sure that POI preserves {@link SharedFormulaRecord}s
*/
public void testPreserveOnReserialize() {
HSSFWorkbook wb;
HSSFSheet sheet;
HSSFCell cellB32769;
HSSFCell cellC32769;
* Make sure that POI preserves {@link SharedFormulaRecord}s
*/
public void testPreserveOnReserialize() {
HSSFWorkbook wb;
HSSFSheet sheet;
HSSFCell cellB32769;
HSSFCell cellC32769;
// Reading directly from XLS file
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
cellB32769 = sheet.getRow(32768).getCell(1);
cellC32769 = sheet.getRow(32768).getCell(2);
// check reading of formulas which are shared (two cells from a 1R x 8C range)
assertEquals("B32770*2", cellB32769.getCellFormula());
assertEquals("C32770*2", cellC32769.getCellFormula());
confirmCellEvaluation(wb, cellB32769, 4);
confirmCellEvaluation(wb, cellC32769, 6);
// Confirm this example really does have SharedFormulas.
// there are 3 others besides the one at A32769:H32769
assertEquals(4, countSharedFormulas(sheet));
// Reading directly from XLS file
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
cellB32769 = sheet.getRow(32768).getCell(1);
cellC32769 = sheet.getRow(32768).getCell(2);
// check reading of formulas which are shared (two cells from a 1R x 8C range)
assertEquals("B32770*2", cellB32769.getCellFormula());
assertEquals("C32770*2", cellC32769.getCellFormula());
confirmCellEvaluation(wb, cellB32769, 4);
confirmCellEvaluation(wb, cellC32769, 6);
// Confirm this example really does have SharedFormulas.
// there are 3 others besides the one at A32769:H32769
assertEquals(4, countSharedFormulas(sheet));
// Re-serialize and check again
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
sheet = wb.getSheetAt(0);
cellB32769 = sheet.getRow(32768).getCell(1);
cellC32769 = sheet.getRow(32768).getCell(2);
assertEquals("B32770*2", cellB32769.getCellFormula());
confirmCellEvaluation(wb, cellB32769, 4);
assertEquals(4, countSharedFormulas(sheet));
}
// Re-serialize and check again
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
sheet = wb.getSheetAt(0);
cellB32769 = sheet.getRow(32768).getCell(1);
cellC32769 = sheet.getRow(32768).getCell(2);
assertEquals("B32770*2", cellB32769.getCellFormula());
confirmCellEvaluation(wb, cellB32769, 4);
assertEquals(4, countSharedFormulas(sheet));
}
public void testUnshareFormulaDueToChangeFormula() {
HSSFWorkbook wb;
HSSFSheet sheet;
HSSFCell cellB32769;
HSSFCell cellC32769;
public void testUnshareFormulaDueToChangeFormula() {
HSSFWorkbook wb;
HSSFSheet sheet;
HSSFCell cellB32769;
HSSFCell cellC32769;
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
cellB32769 = sheet.getRow(32768).getCell(1);
cellC32769 = sheet.getRow(32768).getCell(2);
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
cellB32769 = sheet.getRow(32768).getCell(1);
cellC32769 = sheet.getRow(32768).getCell(2);
// Updating cell formula, causing it to become unshared
cellB32769.setCellFormula("1+1");
confirmCellEvaluation(wb, cellB32769, 2);
// currently (Oct 2008) POI handles this by exploding the whole shared formula group
assertEquals(3, countSharedFormulas(sheet)); // one less now
// check that nearby cell of the same group still has the same formula
assertEquals("C32770*2", cellC32769.getCellFormula());
confirmCellEvaluation(wb, cellC32769, 6);
}
public void testUnshareFormulaDueToDelete() {
HSSFWorkbook wb;
HSSFSheet sheet;
HSSFCell cell;
final int ROW_IX = 2;
// Updating cell formula, causing it to become unshared
cellB32769.setCellFormula("1+1");
confirmCellEvaluation(wb, cellB32769, 2);
// currently (Oct 2008) POI handles this by exploding the whole shared formula group
assertEquals(3, countSharedFormulas(sheet)); // one less now
// check that nearby cell of the same group still has the same formula
assertEquals("C32770*2", cellC32769.getCellFormula());
confirmCellEvaluation(wb, cellC32769, 6);
}
public void testUnshareFormulaDueToDelete() {
HSSFWorkbook wb;
HSSFSheet sheet;
HSSFCell cell;
final int ROW_IX = 2;
// changing shared formula cell to blank
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
// changing shared formula cell to blank
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
assertEquals("A$1*2", sheet.getRow(ROW_IX).getCell(1).getCellFormula());
cell = sheet.getRow(ROW_IX).getCell(1);
cell.setCellType(HSSFCell.CELL_TYPE_BLANK);
assertEquals(3, countSharedFormulas(sheet));
assertEquals("A$1*2", sheet.getRow(ROW_IX).getCell(1).getCellFormula());
cell = sheet.getRow(ROW_IX).getCell(1);
cell.setCellType(HSSFCell.CELL_TYPE_BLANK);
assertEquals(3, countSharedFormulas(sheet));
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
sheet = wb.getSheetAt(0);
assertEquals("A$1*2", sheet.getRow(ROW_IX+1).getCell(1).getCellFormula());
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
sheet = wb.getSheetAt(0);
assertEquals("A$1*2", sheet.getRow(ROW_IX+1).getCell(1).getCellFormula());
// deleting shared formula cell
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
// deleting shared formula cell
wb = HSSFTestDataSamples.openSampleWorkbook(SHARED_FORMULA_TEST_XLS);
sheet = wb.getSheetAt(0);
assertEquals("A$1*2", sheet.getRow(ROW_IX).getCell(1).getCellFormula());
cell = sheet.getRow(ROW_IX).getCell(1);
sheet.getRow(ROW_IX).removeCell(cell);
assertEquals(3, countSharedFormulas(sheet));
assertEquals("A$1*2", sheet.getRow(ROW_IX).getCell(1).getCellFormula());
cell = sheet.getRow(ROW_IX).getCell(1);
sheet.getRow(ROW_IX).removeCell(cell);
assertEquals(3, countSharedFormulas(sheet));
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
sheet = wb.getSheetAt(0);
assertEquals("A$1*2", sheet.getRow(ROW_IX+1).getCell(1).getCellFormula());
}
wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
sheet = wb.getSheetAt(0);
assertEquals("A$1*2", sheet.getRow(ROW_IX+1).getCell(1).getCellFormula());
}
private static void confirmCellEvaluation(HSSFWorkbook wb, HSSFCell cell, double expectedValue) {
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
CellValue cv = fe.evaluate(cell);
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(expectedValue, cv.getNumberValue(), 0.0);
}
private static void confirmCellEvaluation(HSSFWorkbook wb, HSSFCell cell, double expectedValue) {
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
CellValue cv = fe.evaluate(cell);
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(expectedValue, cv.getNumberValue(), 0.0);
}
/**
* @return the number of {@link SharedFormulaRecord}s encoded for the specified sheet
*/
private static int countSharedFormulas(HSSFSheet sheet) {
Record[] records = RecordInspector.getRecords(sheet, 0);
int count = 0;
for (int i = 0; i < records.length; i++) {
Record rec = records[i];
if(rec instanceof SharedFormulaRecord) {
count++;
}
}
return count;
}
/**
* @return the number of {@link SharedFormulaRecord}s encoded for the specified sheet
*/
private static int countSharedFormulas(HSSFSheet sheet) {
Record[] records = RecordInspector.getRecords(sheet, 0);
int count = 0;
for (int i = 0; i < records.length; i++) {
Record rec = records[i];
if(rec instanceof SharedFormulaRecord) {
count++;
}
}
return count;
}
}

View File

@ -36,32 +36,32 @@ import org.junit.Test;
* Tests for the INDIRECT() function.</p>
*/
public final class TestIndirect {
// convenient access to namespace
// private static final ErrorEval EE = null;
// convenient access to namespace
// private static final ErrorEval EE = null;
private static void createDataRow(HSSFSheet sheet, int rowIndex, double... vals) {
HSSFRow row = sheet.createRow(rowIndex);
for (int i = 0; i < vals.length; i++) {
row.createCell(i).setCellValue(vals[i]);
}
}
private static void createDataRow(HSSFSheet sheet, int rowIndex, double... vals) {
HSSFRow row = sheet.createRow(rowIndex);
for (int i = 0; i < vals.length; i++) {
row.createCell(i).setCellValue(vals[i]);
}
}
private static HSSFWorkbook createWBA() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("Sheet1");
HSSFSheet sheet2 = wb.createSheet("Sheet2");
HSSFSheet sheet3 = wb.createSheet("John's sales");
private static HSSFWorkbook createWBA() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("Sheet1");
HSSFSheet sheet2 = wb.createSheet("Sheet2");
HSSFSheet sheet3 = wb.createSheet("John's sales");
createDataRow(sheet1, 0, 11, 12, 13, 14);
createDataRow(sheet1, 1, 21, 22, 23, 24);
createDataRow(sheet1, 2, 31, 32, 33, 34);
createDataRow(sheet1, 0, 11, 12, 13, 14);
createDataRow(sheet1, 1, 21, 22, 23, 24);
createDataRow(sheet1, 2, 31, 32, 33, 34);
createDataRow(sheet2, 0, 50, 55, 60, 65);
createDataRow(sheet2, 1, 51, 56, 61, 66);
createDataRow(sheet2, 2, 52, 57, 62, 67);
createDataRow(sheet2, 0, 50, 55, 60, 65);
createDataRow(sheet2, 1, 51, 56, 61, 66);
createDataRow(sheet2, 2, 52, 57, 62, 67);
createDataRow(sheet3, 0, 30, 31, 32);
createDataRow(sheet3, 1, 33, 34, 35);
createDataRow(sheet3, 0, 30, 31, 32);
createDataRow(sheet3, 1, 33, 34, 35);
HSSFName name1 = wb.createName();
name1.setNameName("sales1");
@ -75,131 +75,131 @@ public final class TestIndirect {
row.createCell(0).setCellValue("sales1"); //A4
row.createCell(1).setCellValue("sales2"); //B4
return wb;
}
return wb;
}
private static HSSFWorkbook createWBB() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("Sheet1");
HSSFSheet sheet2 = wb.createSheet("Sheet2");
HSSFSheet sheet3 = wb.createSheet("## Look here!");
private static HSSFWorkbook createWBB() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("Sheet1");
HSSFSheet sheet2 = wb.createSheet("Sheet2");
HSSFSheet sheet3 = wb.createSheet("## Look here!");
createDataRow(sheet1, 0, 400, 440, 480, 520);
createDataRow(sheet1, 1, 420, 460, 500, 540);
createDataRow(sheet1, 0, 400, 440, 480, 520);
createDataRow(sheet1, 1, 420, 460, 500, 540);
createDataRow(sheet2, 0, 50, 55, 60, 65);
createDataRow(sheet2, 1, 51, 56, 61, 66);
createDataRow(sheet2, 0, 50, 55, 60, 65);
createDataRow(sheet2, 1, 51, 56, 61, 66);
createDataRow(sheet3, 0, 42);
createDataRow(sheet3, 0, 42);
return wb;
}
return wb;
}
@Test
public void testBasic() throws Exception {
@Test
public void testBasic() throws Exception {
HSSFWorkbook wbA = createWBA();
HSSFCell c = wbA.getSheetAt(0).createRow(5).createCell(2);
HSSFFormulaEvaluator feA = new HSSFFormulaEvaluator(wbA);
HSSFWorkbook wbA = createWBA();
HSSFCell c = wbA.getSheetAt(0).createRow(5).createCell(2);
HSSFFormulaEvaluator feA = new HSSFFormulaEvaluator(wbA);
// non-error cases
confirm(feA, c, "INDIRECT(\"C2\")", 23);
confirm(feA, c, "INDIRECT(\"$C2\")", 23);
confirm(feA, c, "INDIRECT(\"C$2\")", 23);
confirm(feA, c, "SUM(INDIRECT(\"Sheet2!B1:C3\"))", 351); // area ref
confirm(feA, c, "SUM(INDIRECT(\"Sheet2! B1 : C3 \"))", 351); // spaces in area ref
confirm(feA, c, "SUM(INDIRECT(\"'John''s sales'!A1:C1\"))", 93); // special chars in sheet name
confirm(feA, c, "INDIRECT(\"'Sheet1'!B3\")", 32); // redundant sheet name quotes
confirm(feA, c, "INDIRECT(\"sHeet1!B3\")", 32); // case-insensitive sheet name
confirm(feA, c, "INDIRECT(\" D3 \")", 34); // spaces around cell ref
confirm(feA, c, "INDIRECT(\"Sheet1! D3 \")", 34); // spaces around cell ref
confirm(feA, c, "INDIRECT(\"A1\", TRUE)", 11); // explicit arg1. only TRUE supported so far
// non-error cases
confirm(feA, c, "INDIRECT(\"C2\")", 23);
confirm(feA, c, "INDIRECT(\"$C2\")", 23);
confirm(feA, c, "INDIRECT(\"C$2\")", 23);
confirm(feA, c, "SUM(INDIRECT(\"Sheet2!B1:C3\"))", 351); // area ref
confirm(feA, c, "SUM(INDIRECT(\"Sheet2! B1 : C3 \"))", 351); // spaces in area ref
confirm(feA, c, "SUM(INDIRECT(\"'John''s sales'!A1:C1\"))", 93); // special chars in sheet name
confirm(feA, c, "INDIRECT(\"'Sheet1'!B3\")", 32); // redundant sheet name quotes
confirm(feA, c, "INDIRECT(\"sHeet1!B3\")", 32); // case-insensitive sheet name
confirm(feA, c, "INDIRECT(\" D3 \")", 34); // spaces around cell ref
confirm(feA, c, "INDIRECT(\"Sheet1! D3 \")", 34); // spaces around cell ref
confirm(feA, c, "INDIRECT(\"A1\", TRUE)", 11); // explicit arg1. only TRUE supported so far
confirm(feA, c, "INDIRECT(\"A1:G1\")", 13); // de-reference area ref (note formula is in C4)
confirm(feA, c, "INDIRECT(\"A1:G1\")", 13); // de-reference area ref (note formula is in C4)
confirm(feA, c, "SUM(INDIRECT(A4))", 50); // indirect defined name
confirm(feA, c, "SUM(INDIRECT(B4))", 351); // indirect defined name pointinh to other sheet
// simple error propagation:
// simple error propagation:
// arg0 is evaluated to text first
confirm(feA, c, "INDIRECT(#DIV/0!)", ErrorEval.DIV_ZERO);
confirm(feA, c, "INDIRECT(#DIV/0!)", ErrorEval.DIV_ZERO);
confirm(feA, c, "INDIRECT(#NAME?, \"x\")", ErrorEval.NAME_INVALID);
confirm(feA, c, "INDIRECT(#NUM!, #N/A)", ErrorEval.NUM_ERROR);
// arg0 is evaluated to text first
confirm(feA, c, "INDIRECT(#DIV/0!)", ErrorEval.DIV_ZERO);
confirm(feA, c, "INDIRECT(#DIV/0!)", ErrorEval.DIV_ZERO);
confirm(feA, c, "INDIRECT(#NAME?, \"x\")", ErrorEval.NAME_INVALID);
confirm(feA, c, "INDIRECT(#NUM!, #N/A)", ErrorEval.NUM_ERROR);
// arg1 is evaluated to boolean before arg0 is decoded
confirm(feA, c, "INDIRECT(\"garbage\", #N/A)", ErrorEval.NA);
confirm(feA, c, "INDIRECT(\"garbage\", \"\")", ErrorEval.VALUE_INVALID); // empty string is not valid boolean
confirm(feA, c, "INDIRECT(\"garbage\", \"flase\")", ErrorEval.VALUE_INVALID); // must be "TRUE" or "FALSE"
// arg1 is evaluated to boolean before arg0 is decoded
confirm(feA, c, "INDIRECT(\"garbage\", #N/A)", ErrorEval.NA);
confirm(feA, c, "INDIRECT(\"garbage\", \"\")", ErrorEval.VALUE_INVALID); // empty string is not valid boolean
confirm(feA, c, "INDIRECT(\"garbage\", \"flase\")", ErrorEval.VALUE_INVALID); // must be "TRUE" or "FALSE"
// spaces around sheet name (with or without quotes makes no difference)
confirm(feA, c, "INDIRECT(\"'Sheet1 '!D3\")", ErrorEval.REF_INVALID);
confirm(feA, c, "INDIRECT(\" Sheet1!D3\")", ErrorEval.REF_INVALID);
confirm(feA, c, "INDIRECT(\"'Sheet1' !D3\")", ErrorEval.REF_INVALID);
// spaces around sheet name (with or without quotes makes no difference)
confirm(feA, c, "INDIRECT(\"'Sheet1 '!D3\")", ErrorEval.REF_INVALID);
confirm(feA, c, "INDIRECT(\" Sheet1!D3\")", ErrorEval.REF_INVALID);
confirm(feA, c, "INDIRECT(\"'Sheet1' !D3\")", ErrorEval.REF_INVALID);
confirm(feA, c, "SUM(INDIRECT(\"'John's sales'!A1:C1\"))", ErrorEval.REF_INVALID); // bad quote escaping
confirm(feA, c, "INDIRECT(\"[Book1]Sheet1!A1\")", ErrorEval.REF_INVALID); // unknown external workbook
confirm(feA, c, "INDIRECT(\"Sheet3!A1\")", ErrorEval.REF_INVALID); // unknown sheet
// if (false) { // TODO - support evaluation of defined names
// confirm(feA, c, "INDIRECT(\"Sheet1!IW1\")", ErrorEval.REF_INVALID); // bad column
// confirm(feA, c, "INDIRECT(\"Sheet1!A65537\")", ErrorEval.REF_INVALID); // bad row
// }
confirm(feA, c, "INDIRECT(\"Sheet1!A 1\")", ErrorEval.REF_INVALID); // space in cell ref
wbA.close();
}
confirm(feA, c, "SUM(INDIRECT(\"'John's sales'!A1:C1\"))", ErrorEval.REF_INVALID); // bad quote escaping
confirm(feA, c, "INDIRECT(\"[Book1]Sheet1!A1\")", ErrorEval.REF_INVALID); // unknown external workbook
confirm(feA, c, "INDIRECT(\"Sheet3!A1\")", ErrorEval.REF_INVALID); // unknown sheet
// if (false) { // TODO - support evaluation of defined names
// confirm(feA, c, "INDIRECT(\"Sheet1!IW1\")", ErrorEval.REF_INVALID); // bad column
// confirm(feA, c, "INDIRECT(\"Sheet1!A65537\")", ErrorEval.REF_INVALID); // bad row
// }
confirm(feA, c, "INDIRECT(\"Sheet1!A 1\")", ErrorEval.REF_INVALID); // space in cell ref
wbA.close();
}
@Test
public void testMultipleWorkbooks() throws Exception {
HSSFWorkbook wbA = createWBA();
HSSFCell cellA = wbA.getSheetAt(0).createRow(10).createCell(0);
HSSFFormulaEvaluator feA = new HSSFFormulaEvaluator(wbA);
@Test
public void testMultipleWorkbooks() throws Exception {
HSSFWorkbook wbA = createWBA();
HSSFCell cellA = wbA.getSheetAt(0).createRow(10).createCell(0);
HSSFFormulaEvaluator feA = new HSSFFormulaEvaluator(wbA);
HSSFWorkbook wbB = createWBB();
HSSFCell cellB = wbB.getSheetAt(0).createRow(10).createCell(0);
HSSFFormulaEvaluator feB = new HSSFFormulaEvaluator(wbB);
HSSFWorkbook wbB = createWBB();
HSSFCell cellB = wbB.getSheetAt(0).createRow(10).createCell(0);
HSSFFormulaEvaluator feB = new HSSFFormulaEvaluator(wbB);
String[] workbookNames = { "MyBook", "Figures for January", };
HSSFFormulaEvaluator[] evaluators = { feA, feB, };
HSSFFormulaEvaluator.setupEnvironment(workbookNames, evaluators);
String[] workbookNames = { "MyBook", "Figures for January", };
HSSFFormulaEvaluator[] evaluators = { feA, feB, };
HSSFFormulaEvaluator.setupEnvironment(workbookNames, evaluators);
confirm(feB, cellB, "INDIRECT(\"'[Figures for January]## Look here!'!A1\")", 42); // same wb
confirm(feA, cellA, "INDIRECT(\"'[Figures for January]## Look here!'!A1\")", 42); // across workbooks
confirm(feB, cellB, "INDIRECT(\"'[Figures for January]## Look here!'!A1\")", 42); // same wb
confirm(feA, cellA, "INDIRECT(\"'[Figures for January]## Look here!'!A1\")", 42); // across workbooks
// 2 level recursion
confirm(feB, cellB, "INDIRECT(\"[MyBook]Sheet2!A1\")", 50); // set up (and check) first level
confirm(feA, cellA, "INDIRECT(\"'[Figures for January]Sheet1'!A11\")", 50); // points to cellB
wbB.close();
wbA.close();
}
// 2 level recursion
confirm(feB, cellB, "INDIRECT(\"[MyBook]Sheet2!A1\")", 50); // set up (and check) first level
confirm(feA, cellA, "INDIRECT(\"'[Figures for January]Sheet1'!A11\")", 50); // points to cellB
wbB.close();
wbA.close();
}
private static void confirm(FormulaEvaluator fe, Cell cell, String formula,
double expectedResult) {
fe.clearAllCachedResultValues();
cell.setCellFormula(formula);
CellValue cv = fe.evaluate(cell);
if (cv.getCellType() != Cell.CELL_TYPE_NUMERIC) {
fail("expected numeric cell type but got " + cv.formatAsString());
}
assertEquals(expectedResult, cv.getNumberValue(), 0.0);
}
private static void confirm(FormulaEvaluator fe, Cell cell, String formula,
ErrorEval expectedResult) {
fe.clearAllCachedResultValues();
cell.setCellFormula(formula);
CellValue cv = fe.evaluate(cell);
if (cv.getCellType() != Cell.CELL_TYPE_ERROR) {
fail("expected error cell type but got " + cv.formatAsString());
}
int expCode = expectedResult.getErrorCode();
if (cv.getErrorValue() != expCode) {
fail("Expected error '" + ErrorEval.getText(expCode)
+ "' but got '" + cv.formatAsString() + "'.");
}
}
private static void confirm(FormulaEvaluator fe, Cell cell, String formula,
double expectedResult) {
fe.clearAllCachedResultValues();
cell.setCellFormula(formula);
CellValue cv = fe.evaluate(cell);
if (cv.getCellType() != Cell.CELL_TYPE_NUMERIC) {
fail("expected numeric cell type but got " + cv.formatAsString());
}
assertEquals(expectedResult, cv.getNumberValue(), 0.0);
}
private static void confirm(FormulaEvaluator fe, Cell cell, String formula,
ErrorEval expectedResult) {
fe.clearAllCachedResultValues();
cell.setCellFormula(formula);
CellValue cv = fe.evaluate(cell);
if (cv.getCellType() != Cell.CELL_TYPE_ERROR) {
fail("expected error cell type but got " + cv.formatAsString());
}
int expCode = expectedResult.getErrorCode();
if (cv.getErrorValue() != expCode) {
fail("Expected error '" + ErrorEval.getText(expCode)
+ "' but got '" + cv.formatAsString() + "'.");
}
}
}