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:
commit
12d7fb30a4
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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() + "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user