You've already forked lazarus-ccr
fpspreadsheet: Implement parsing of cell references in "R1C1" syntax.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4375 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -76,6 +76,9 @@ function ParseCellRowString(const AStr: string;
|
||||
function ParseCellColString(const AStr: string;
|
||||
out AResult: Cardinal): Boolean;
|
||||
|
||||
function ParseCellString_R1C1(const AStr: String; ABaseRow, ABaseCol: Cardinal;
|
||||
out ACellRow, ACellCol: Cardinal; out AFlags: TsRelFlags): Boolean;
|
||||
|
||||
function GetColString(AColIndex: Integer): String;
|
||||
|
||||
function GetCellString(ARow,ACol: Cardinal;
|
||||
@ -584,6 +587,101 @@ begin
|
||||
Result := Scan(1);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Parses a cell string in "R1C1" notation into zero-based column and row numbers
|
||||
'AFlags' indicates relative addresses.
|
||||
|
||||
@param AStr Cell reference in R1C1 syntax, such as R[2]C[3] or R1C5
|
||||
@param ABaseRow Row index from which the cell reference is seen.
|
||||
@param ABaseCol Column index from which the cell reference is seen.
|
||||
@param ACellRow Row index of the top/left cell of the range (output)
|
||||
@param ACellCol Column index of the top/left cell of the range (output)
|
||||
@param AFlags A set containing an element for ACellRow and/or ACellCol,
|
||||
if they represent a relative cell address.
|
||||
@return FALSE if the string is not a valid cell range
|
||||
-------------------------------------------------------------------------------}
|
||||
function ParseCellString_R1C1(const AStr: String; ABaseRow, ABaseCol: Cardinal;
|
||||
out ACellRow, ACellCol: Cardinal; out AFlags: TsRelFlags): Boolean;
|
||||
var
|
||||
P: PChar;
|
||||
s: String;
|
||||
n: LongInt;
|
||||
inRowCol: Integer; // 1 = in row, 2 = in col
|
||||
r, c: LongInt;
|
||||
inBracket: Boolean;
|
||||
begin
|
||||
AFlags := [];
|
||||
inRowCol := 0;
|
||||
inBracket := false;
|
||||
P := @AStr[1];
|
||||
while P^ <> #0 do begin
|
||||
case P^ of
|
||||
'R', 'r': if inRowCol = 0 then
|
||||
begin
|
||||
inRowCol := 1;
|
||||
s := '';
|
||||
end else
|
||||
exit(false);
|
||||
'C', 'c': if inBracket then
|
||||
exit(false)
|
||||
else
|
||||
if inRowCol = 1 then
|
||||
begin
|
||||
if s = '' then
|
||||
begin
|
||||
Include(AFlags, rfRelRow);
|
||||
ACellRow := ABaseRow;
|
||||
end else
|
||||
if rfRelRow in AFlags then
|
||||
begin
|
||||
r := LongInt(ABaseRow) + StrToInt(s);
|
||||
if r < 0 then
|
||||
exit(false);
|
||||
ACellRow := r;
|
||||
end else
|
||||
ACellRow := StrToInt(s) - 1;
|
||||
s := '';
|
||||
inRowCol := 2;
|
||||
inBracket := false;
|
||||
end else
|
||||
exit(false);
|
||||
'0'..'9': s := s + P^;
|
||||
'-' : s := s + '-';
|
||||
'[' : begin
|
||||
case inRowCol of
|
||||
1: Include(AFlags, rfRelRow);
|
||||
2: Include(AFlags, rfRelCol);
|
||||
end;
|
||||
inBracket := true;
|
||||
end;
|
||||
']' : if inBracket then inBracket := false else exit(false);
|
||||
else exit(false);
|
||||
end;
|
||||
inc(P);
|
||||
end;
|
||||
|
||||
if inBracket then
|
||||
exit(false)
|
||||
else
|
||||
if inRowCol = 2 then
|
||||
begin
|
||||
if s = '' then
|
||||
begin
|
||||
Include(AFlags, rfRelCol);
|
||||
ACellCol := ABaseCol;
|
||||
end else
|
||||
if rfRelCol in AFlags then
|
||||
begin
|
||||
c := LongInt(ABaseCol) + StrToInt(s);
|
||||
if c < 0 then
|
||||
exit(false);
|
||||
ACellCol := c;
|
||||
end else
|
||||
ACellCol := StrToInt(s) - 1;
|
||||
end;
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Parses a cell string, like 'A1' into zero-based column and row numbers
|
||||
Note that there can be several letters to address for more than 26 columns.
|
||||
|
@ -39,6 +39,9 @@ type
|
||||
// Tests getting Excel style A1 cell locations from row/column based locations.
|
||||
// Bug 26447
|
||||
procedure TestCellString;
|
||||
// Tests cell references given in the "R1C1" syntax.
|
||||
procedure TestCellString_R1C1;
|
||||
|
||||
//todo: add more calls, rename sheets, try to get sheets with invalid indexes etc
|
||||
//(see strings tests for how to deal with expected exceptions)
|
||||
procedure GetSheetByIndex;
|
||||
@ -476,6 +479,129 @@ begin
|
||||
CheckEquals(s, GetCellString(r, c, flags));
|
||||
end;
|
||||
|
||||
{ Tests cell references given in the "R1C1" syntax. }
|
||||
procedure TSpreadInternalTests.TestCellString_R1C1;
|
||||
var
|
||||
r,c: Cardinal;
|
||||
s: String;
|
||||
flags: TsRelFlags;
|
||||
res: Boolean;
|
||||
begin
|
||||
// (1) Absolute reference of the cell at row=0 col=0
|
||||
res := ParseCellString_R1C1('R1C1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, true, 'Result mismatch in test 1');
|
||||
CheckEquals(r, 0, 'Row mismatch in test 1'); // base cell coordinates are ignored with absolute refs!
|
||||
CheckEquals(c, 0, 'Col mismatch in test 1');
|
||||
CheckEquals(true, flags = [], 'Flags mismatch in test 1');
|
||||
|
||||
// (2) Relative reference of the cell left of col 10 and above row 10
|
||||
res := ParseCellString_R1C1('R[-1]C[-1]', 10, 10, r, c, flags);
|
||||
CheckEquals(res, true, 'Result mismatch in test 2');
|
||||
CheckEquals(r, 9, 'Row mismatch in test 2');
|
||||
CheckEquals(c, 9, 'Col mismatch in test 2');
|
||||
CheckEquals(true, flags = [rfRelRow, rfRelCol], 'Flags mismatch in test 2');
|
||||
|
||||
// (3) Relative reference of the cell in row 10 and 2 cols at the right of col 10
|
||||
res := ParseCellString_R1C1('RC[2]', 10, 10, r, c, flags);
|
||||
CheckEquals(res, true, 'Result mismatch in test 3');
|
||||
CheckEquals(r, 10, 'Row mismatch in test 3');
|
||||
CheckEquals(c, 12, 'Col mismatch in test 3');
|
||||
CheckEquals(true, flags = [rfRelRow, rfRelCol], 'Flags mismatch in test 3');
|
||||
|
||||
// (4) Relative reference of the cell in col 10 and 2 rows below row 10
|
||||
res := ParseCellString_R1C1('R[2]C', 10, 10, r, c, flags);
|
||||
CheckEquals(res, true, 'Result mismatch in test 4');
|
||||
CheckEquals(r, 12, 'Row mismatch in test 4');
|
||||
CheckEquals(c, 10, 'Col mismatch in test 4');
|
||||
CheckEquals(true, flags = [rfRelRow, rfRelCol], 'Flags mismatch in test 4');
|
||||
|
||||
// (5) Relative reference of the cell 3 rows above row 10 and 2 cols left of col 10
|
||||
res := ParseCellString_R1C1('R[-3]C[-2]', 10, 10, r, c, flags);
|
||||
CheckEquals(res, true, 'Result mismatch in test 5');
|
||||
CheckEquals(r, 7, 'Row mismatch in test 5');
|
||||
CheckEquals(c, 8, 'Col mismatch in test 5');
|
||||
CheckEquals(true, flags = [rfRelRow, rfRelCol], 'Flags mismatch in test 5');
|
||||
|
||||
// (6) Mixed reference: base cell in row10/col10 (note: zero-based!).
|
||||
// Absolute reference to row, relative reference to 10 columns to the right
|
||||
res := ParseCellString_R1C1('R11C[10]', 10, 10, r, c, flags);
|
||||
CheckEquals(res, true, 'Result mismatch in test 6');
|
||||
CheckEquals(r, 10, 'Row mismatch in test 6');
|
||||
CheckEquals(c, 20, 'Col mismatch in test 6');
|
||||
CheckEquals(true, flags = [rfRelCol], 'Flags mismatch in test 6');
|
||||
|
||||
// (7) Mixed reference: base cell in row10/col10 (note: zero-based!).
|
||||
// Relative reference to 10 rows below, absolute reference to this col
|
||||
res := ParseCellString_R1C1('R[10]C11', 10, 10, r, c, flags);
|
||||
CheckEquals(res, true, 'Result mismatch in test 7');
|
||||
CheckEquals(r, 20, 'Row mismatch in test 7');
|
||||
CheckEquals(c, 10, 'Col mismatch in test 7');
|
||||
CheckEquals(true, flags = [rfRelRow], 'Flags mismatch in test 7');
|
||||
|
||||
// Error tests
|
||||
|
||||
// (E1) Relative reference of the cell 30 rows above row 10 and 2 cols left of col 10
|
||||
res := ParseCellString_R1C1('R[-30]C[-2]', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E1');
|
||||
|
||||
// (E2) Relative reference of the cell 30 rows to the left of row 10
|
||||
res := ParseCellString_R1C1('R[-30]C', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E2');
|
||||
|
||||
// (E3) Illegal "R" character
|
||||
res := ParseCellString_R1C1('x1C2', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E3');
|
||||
|
||||
// (E4) Illegal "C" character
|
||||
res := ParseCellString_R1C1('R1x2', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E4');
|
||||
|
||||
// (E5) Illegal row number character
|
||||
res := ParseCellString_R1C1('R10.1C2', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E5');
|
||||
|
||||
// (E6) Illegal row number character
|
||||
res := ParseCellString_R1C1('R1C10.1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E6');
|
||||
|
||||
// (E7) Illegal opening row bracket
|
||||
res := ParseCellString_R1C1('R(1]C1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E7');
|
||||
|
||||
// (E8 Illegal closing row bracket
|
||||
res := ParseCellString_R1C1('R[1)C1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E8');
|
||||
|
||||
// (E9) Illegal opening col bracket
|
||||
res := ParseCellString_R1C1('R1C(1]', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E9');
|
||||
|
||||
// (E10) Illegal closing col bracket
|
||||
res := ParseCellString_R1C1('RC[1)', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E10');
|
||||
|
||||
// (E11) Missing opening row bracket
|
||||
res := ParseCellString_R1C1('R1]C1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E11');
|
||||
|
||||
// (E12) Missing closing row bracket
|
||||
res := ParseCellString_R1C1('R[1C1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E12');
|
||||
|
||||
// (E13) Missing opening col bracket
|
||||
res := ParseCellString_R1C1('R1C1]', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E13');
|
||||
|
||||
// (E14) Missing closing col bracket
|
||||
res := ParseCellString_R1C1('R1C[1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E14');
|
||||
|
||||
// (E15) RC interchanged
|
||||
res := ParseCellString_R1C1('C1R1', 10, 10, r, c, flags);
|
||||
CheckEquals(res, false, 'Result mismatch in test E15');
|
||||
|
||||
end;
|
||||
|
||||
procedure TSpreadInternalTests.FractionTest(AMaxDigits: Integer);
|
||||
const
|
||||
N = 300;
|
||||
|
Reference in New Issue
Block a user