git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xssf_structured_references@1747625 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_15_BETA2
@@ -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, |
@@ -32,51 +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(); | |||
/** | |||
* 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); | |||
/** | |||
* 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,11 +406,11 @@ public final class OperationEvaluationContext { | |||
// Must be an external function | |||
return new FunctionNameEval(name); | |||
} | |||
} | |||
public int getSheetIndex() { | |||
return _sheetIndex; | |||
} | |||
} | |||
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 |
@@ -49,78 +49,78 @@ public final class Indirect implements FreeRefFunction { | |||
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(final OperationEvaluationContext ec, String text, | |||
boolean isA1style) { | |||
ec.getRowIndex(); | |||
ec.getColumnIndex(); | |||
private static ValueEval evaluateIndirect(final OperationEvaluationContext ec, String text, | |||
boolean isA1style) { | |||
ec.getRowIndex(); | |||
ec.getColumnIndex(); | |||
// Search backwards for '!' because sheet names can contain '!' | |||
int plingPos = text.lastIndexOf('!'); | |||
// 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; | |||
String refStrPart1; | |||
String refStrPart2; | |||
if (Table.isStructuredReference.matcher(refText).matches()) { // The argument is structured reference | |||
Area3DPxg areaPtg = null; | |||
try{ | |||
@@ -130,128 +130,128 @@ public final class Indirect implements FreeRefFunction { | |||
} | |||
return ec.getArea3DEval(areaPtg); | |||
} else { // The argumnet is regular reference | |||
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; | |||
} | |||
} |
@@ -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 | |||
@@ -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(); | |||
} | |||
} | |||
/** |
@@ -1762,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){ | |||
@@ -1929,7 +1929,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { | |||
*/ | |||
@Internal | |||
public MapInfo getMapInfo(){ | |||
return mapInfo; | |||
return mapInfo; | |||
} | |||
/** | |||
@@ -1946,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 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(); | |||
} | |||
/** | |||
* Locks the structure of workbook. | |||
*/ | |||
public void lockStructure() { | |||
safeGetWorkbookProtection().setLockStructure(true); | |||
} | |||
/** | |||
* Unlocks the structure of workbook. | |||
*/ | |||
public void unLockStructure() { | |||
safeGetWorkbookProtection().setLockStructure(false); | |||
} | |||
/** | |||
* 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); | |||
} | |||
/** | |||
* Locks the workbook for revisions. | |||
*/ | |||
public void lockRevision() { | |||
safeGetWorkbookProtection().setLockRevision(true); | |||
} | |||
/** | |||
* 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) { | |||
/** | |||
* 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 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); | |||
} | |||
/** | |||
* Unlocks the structure of workbook. | |||
*/ | |||
public void unLockStructure() { | |||
safeGetWorkbookProtection().setLockStructure(false); | |||
} | |||
/** | |||
* 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); | |||
} | |||
/** | |||
* Locks the workbook for revisions. | |||
*/ | |||
public void lockRevision() { | |||
safeGetWorkbookProtection().setLockRevision(true); | |||
} | |||
/** | |||
* 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) { | |||
if (password == null && !workbookProtectionPresent()) return; | |||
setPassword(safeGetWorkbookProtection(), password, hashAlgo, "workbook"); | |||
} | |||
} | |||
/** | |||
* Validate the password against the stored hash, the hashing method will be determined | |||
@@ -2074,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()){ | |||
@@ -2084,7 +2084,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { | |||
} | |||
return workbook.getWorkbookProtection(); | |||
} | |||
/** | |||
* | |||
* Returns the locator of user-defined functions. |
@@ -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, | |||
}; | |||
/** | |||
* 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); | |||
/** | |||
* 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() { | |||
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); | |||
RefPtg refPtg = (RefPtg) convertedFormula[1]; | |||
assertEquals("$C101", refPtg.toFormulaString()); | |||
if (refPtg.getPtgClass() == Ptg.CLASS_REF) { | |||
throw new AssertionFailedError("Identified bug 45123"); | |||
} | |||
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())); | |||
} | |||
} | |||
} | |||
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"); | |||
} | |||
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())); | |||
} | |||
} | |||
} | |||
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; | |||
// 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)); | |||
} | |||
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); | |||
// 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); | |||
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()); | |||
// 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)); | |||
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); | |||
} | |||
/** | |||
* @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; | |||
} | |||
* 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)); | |||
// 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; | |||
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; | |||
// 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)); | |||
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); | |||
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()); | |||
} | |||
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; | |||
} | |||
} |