fpspreadsheet: Ignore the order of indexes in cell ranges when parsing formulas. Add corresponding unit tests.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6420 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-05-16 08:45:28 +00:00
parent bb2bf8b3ca
commit 613c96821c
3 changed files with 77 additions and 14 deletions

View File

@ -3991,16 +3991,55 @@ end;
constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser; constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser;
AWorksheet: TsWorksheet; ASheet1, ASheet2: String; AWorksheet: TsWorksheet; ASheet1, ASheet2: String;
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: tsRelFlags; Is3DRange: Boolean); ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: tsRelFlags; Is3DRange: Boolean);
var
tmp: Integer;
begin begin
FParser := AParser; FParser := AParser;
FWorksheet := AWorksheet; FWorksheet := AWorksheet;
FFlags := [];
if (ASheet1 = '') and (ASheet2 <> '') then
raise Exception.Create('Invalid parameters in cell range');
FSheet[1] := GetWorkbook.GetWorksheetIndex(ASheet1); FSheet[1] := GetWorkbook.GetWorksheetIndex(ASheet1);
if ASheet2 = '' then FSheet[2] := FSheet[1] else FSheet[2] := GetWorkbook.GetWorksheetIndex(ASheet2); if ASheet2 <> '' then
FRow[1] := ARow1; FSheet[2] := GetWorkbook.GetWorksheetIndex(ASheet2)
FCol[1] := ACol1; else
if ARow2 = Cardinal(-1) then FRow[2] := FRow[1] else FRow[2] := ARow2; FSheet[2] := FSheet[1];
if ACol2 = Cardinal(-1) then FCol[2] := FCol[1] else FCol[2] := ACol2; EnsureOrder(FSheet[1], FSheet[2]);
FFlags := AFlags;
if ARow2 = Cardinal(-1) then
ARow2 := ARow1;
if ARow1 <= ARow2 then
begin
FRow[1] := ARow1;
FRow[2] := ARow2;
FCol[1] := ACol1;
if rfRelRow in AFlags then Include(FFlags, rfRelRow);
if rfRelRow2 in AFlags then Include(FFlags, rfRelRow2);
end else
begin
FRow[1] := ARow2;
FRow[2] := ARow1;
if rfRelRow in AFlags then Include(FFlags, rfRelRow2);
if rfRelRow2 in AFlags then Include(FFlags, rfRelRow);
end;
if ACol2 = Cardinal(-1) then
ACol2 := ACol1;
if ACol1 <= ACol2 then
begin
FCol[1] := ACol1;
FCol[2] := ACol2;
if (rfRelCol in AFlags) then Include(FFlags, rfRelCol);
if (rfRelCol2 in AFlags) then Include(FFlags, rfRelCol2);
end else
begin
FCol[1] := ACol2;
FCol[2] := ACol1;
if (rfRelCol in AFlags) then Include(FFlags, rfRelCol2);
if (rfRelCol2 in AFlags) then Include(FFlags, rfRelCol);
end;
F3dRange := Is3dRange; F3dRange := Is3dRange;
end; end;

View File

@ -5731,12 +5731,12 @@ begin
parser := TsSpreadsheetParser.Create(self); parser := TsSpreadsheetParser.Create(self);
try try
if ALocalized then begin if ALocalized then
// Convert "localized" formula to standard format // Convert "localized" formula to standard format
parser.LocalizedExpression[Workbook.FormatSettings] := AFormula; parser.LocalizedExpression[Workbook.FormatSettings] := AFormula
AFormula := parser.Expression; else
end else
parser.Expression := AFormula; parser.Expression := AFormula;
AFormula := parser.Expression;
if parser.Has3DLinks if parser.Has3DLinks
then ACell.Flags := ACell.Flags + [cf3dFormula] then ACell.Flags := ACell.Flags + [cf3dFormula]
else ACell.Flags := ACell.Flags - [cf3dFormula]; else ACell.Flags := ACell.Flags - [cf3dFormula];

View File

@ -23,7 +23,8 @@ type
procedure SetUp; override; procedure SetUp; override;
procedure TearDown; override; procedure TearDown; override;
procedure TestFloatFormula(AFormula: String; AExpected: Double; procedure TestFloatFormula(AFormula: String; AExpected: Double;
ATestKind: TFormulaTestKind; AFormat: TsSpreadsheetFormat); ATestKind: TFormulaTestKind; AFormat: TsSpreadsheetFormat;
AExpectedFormula: String = '');
published published
procedure AddConst_BIFF2; procedure AddConst_BIFF2;
@ -54,6 +55,10 @@ type
procedure SumMultiSheetRange_OOXML; procedure SumMultiSheetRange_OOXML;
procedure SumMultiSheetRange_ODS; procedure SumMultiSheetRange_ODS;
procedure SumMultiSheetRange_FlippedCells_OOXML;
procedure SumMultiSheetRange_FlippedSheets_OOXML;
procedure SumMultiSheetRange_FlippedSheetsAndCells_OOXML;
end; end;
implementation implementation
@ -78,7 +83,8 @@ begin
end; end;
procedure TSpreadSingleFormulaTests.TestFloatFormula(AFormula: String; procedure TSpreadSingleFormulaTests.TestFloatFormula(AFormula: String;
AExpected: Double; ATestKind: TFormulaTestKind; AFormat: TsSpreadsheetFormat); AExpected: Double; ATestKind: TFormulaTestKind; AFormat: TsSpreadsheetFormat;
AExpectedFormula: String = '');
const const
SHEET1 = 'Sheet1'; SHEET1 = 'Sheet1';
SHEET2 = 'Sheet2'; SHEET2 = 'Sheet2';
@ -95,6 +101,7 @@ var
actualValue: Double; actualValue: Double;
begin begin
TempFile := GetTempFileName; TempFile := GetTempFileName;
if AExpectedFormula = '' then AExpectedFormula := AFormula;
try try
// Create test workbook and write test formula and needed cells // Create test workbook and write test formula and needed cells
@ -132,7 +139,7 @@ begin
// Read formula before saving // Read formula before saving
actualFormula := cell^.Formulavalue; actualFormula := cell^.Formulavalue;
CheckEquals(AFormula, actualFormula, 'Unsaved formula text mismatch'); CheckEquals(AExpectedFormula, actualFormula, 'Unsaved formula text mismatch');
// Read calculated value before saving // Read calculated value before saving
actualvalue := worksheet.ReadAsNumber(TESTCELL_ROW, TESTCELL_COL); actualvalue := worksheet.ReadAsNumber(TESTCELL_ROW, TESTCELL_COL);
@ -157,7 +164,7 @@ begin
cell := worksheet.FindCell(TESTCELL_ROW, TESTCELL_COL); cell := worksheet.FindCell(TESTCELL_ROW, TESTCELL_COL);
actualformula := cell^.FormulaValue; actualformula := cell^.FormulaValue;
CheckEquals(AFormula, actualformula, 'Saved formula text mismatch.'); CheckEquals(AExpectedFormula, actualformula, 'Saved formula text mismatch.');
finally finally
workbook.Free; workbook.Free;
end; end;
@ -290,6 +297,23 @@ begin
TestFloatFormula('SUM(Sheet2:Sheet3!C3:C5)', 55.0, ftkCellRangeSheetRange, sfOpenDocument); TestFloatFormula('SUM(Sheet2:Sheet3!C3:C5)', 55.0, ftkCellRangeSheetRange, sfOpenDocument);
end; end;
{ --- }
procedure TSpreadSingleFormulaTests.SumMultiSheetRange_FlippedSheetsAndCells_OOXML;
begin
TestFloatFormula('SUM(Sheet3:Sheet2!C5:C3)', 55.0, ftkCellRangeSheetRange, sfOOXML, 'SUM(Sheet2:Sheet3!C3:C5)');
end;
procedure TSpreadSingleFormulaTests.SumMultiSheetRange_FlippedCells_OOXML;
begin
TestFloatFormula('SUM(Sheet2:Sheet3!C5:C3)', 55.0, ftkCellRangeSheetRange, sfOOXML, 'SUM(Sheet2:Sheet3!C3:C5)');
end;
procedure TSpreadSingleFormulaTests.SumMultiSheetRange_FlippedSheets_OOXML;
begin
TestFloatFormula('SUM(Sheet3:Sheet2!C3:C5)', 55.0, ftkCellRangeSheetRange, sfOOXML, 'SUM(Sheet2:Sheet3!C3:C5)');
end;
initialization initialization
// Register to include these tests in a full run // Register to include these tests in a full run
RegisterTest(TSpreadSingleFormulaTests); RegisterTest(TSpreadSingleFormulaTests);