Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor - user exchange validation and handling (#334) #335

Merged
merged 1 commit into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion Contest.pas
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface

uses
SndTypes, Station, StnColl, MyStn, Ini, Log, System.Classes,
ExchFields,
MovAvg, Mixers, VolumCtl, DxStn;

type
Expand All @@ -25,6 +26,8 @@ TContest = class
constructor Create;
function IsReloadRequired(const AUserCallsign : String) : boolean;
procedure SetLastLoadCallsign(const AUserCallsign : String);
function ValidateExchField(const FieldDef: PFieldDefinition;
const Avalue: string) : Boolean;

public
BlockNumber: integer;
Expand All @@ -49,6 +52,9 @@ TContest = class
function PickCallOnly : string;

function OnSetMyCall(const AUserCallsign : string; out err : string) : boolean; virtual;
function ValidateMyExchange(const AExchange: string;
ATokens: TStringList;
out AExchError: string): boolean; virtual;
function OnContestPrepareToStart(const AUserCallsign: string;
const ASentExchange : string) : Boolean; virtual;
procedure SerialNrModeChanged; virtual;
Expand Down Expand Up @@ -85,7 +91,7 @@ implementation

uses
SysUtils, RndFunc, Math, DxOper,
ExchFields,
PerlRegEx,
Main, CallLst, ARRL;

{ TContest }
Expand Down Expand Up @@ -228,6 +234,66 @@ function TContest.OnSetMyCall(const AUserCallsign : string; out err : string) :
end;


{
Parse into two strings [Exch1, Exch2].
Validate each string and set error string in AExchError.
Return True upon success; False otherwise.
}
function TContest.ValidateMyExchange(const AExchange: string;
ATokens: TStringList;
out AExchError: string): boolean;
var
SentExchTypes : TExchTypes;
Field1Def: PFieldDefinition;
Field2Def: PFieldDefinition;
begin
SentExchTypes := Self.Me.SentExchTypes;
Field1Def := @Exchange1Settings[SentExchTypes.Exch1];
Field2Def := @Exchange2Settings[SentExchTypes.Exch2];

// parse into two strings [Exch1, Exch2]
ATokens.Clear;
ExtractStrings([' '], [], PChar(AExchange), ATokens);
if ATokens.Count = 0 then
ATokens.AddStrings(['', '']);
if ATokens.Count = 1 then
ATokens.AddStrings(['']);

// validate sent exchange strings
Result := ValidateExchField(Field1Def, ATokens[0]) and
ValidateExchField(Field2Def, ATokens[1]);

if not Result then
AExchError := Format('Invalid exchange: ''%s'' - expecting %s.',
[AExchange, ActiveContest.Msg]);
end;

function TContest.ValidateExchField(const FieldDef: PFieldDefinition;
const Avalue: string) : Boolean;
var
reg: TPerlRegEx;
s: string;
begin
if SimContest = scNaQp then begin
// special case - I can't figure out how to match an empty string,
// so manually check for an optional string.
s := FieldDef.R;
Result := s.StartsWith('()|(') and Avalue.IsEmpty;
if Result then Exit;
end;

reg := TPerlRegEx.Create();
try
reg.Subject := UTF8Encode(Avalue);
s:= '^(' + FieldDef.R + ')$';
reg.RegEx:= UTF8Encode(s);
Result:= Reg.Match;
finally
reg.Free;
end;
end;


{
OnContestPrepareToStart() event is called whenever a contest is started.
Some contests will override this method to provide additional contest-specfic
Expand Down
40 changes: 3 additions & 37 deletions Main.pas
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,6 @@ TMainForm = class(TForm)
procedure ConfigureExchangeFields;
procedure SetMyExch1(const AExchType: TExchange1Type; const Avalue: string);
procedure SetMyExch2(const AExchType: TExchange2Type; const Avalue: string);
function ValidateExchField(const FieldDef: PFieldDefinition;
const Avalue: string) : Boolean;
procedure ProcessSpace;
procedure SendMsg(AMsg: TStationMessage);
procedure ProcessEnter;
Expand Down Expand Up @@ -1080,6 +1078,7 @@ procedure TMainForm.SetContest(AContestNum: TSimContest);
function TMainForm.SetMyExchange(const AExchange: string) : Boolean;
var
sl: TStringList;
ExchError: string;
SentExchTypes : TExchTypes;
Field1Def: PFieldDefinition;
Field2Def: PFieldDefinition;
Expand All @@ -1093,19 +1092,11 @@ function TMainForm.SetMyExchange(const AExchange: string) : Boolean;
Field2Def := @Exchange2Settings[SentExchTypes.Exch2];

// parse into two strings [Exch1, Exch2]
ExtractStrings([' '], [], PChar(AExchange), sl);
if sl.Count = 0 then
sl.AddStrings(['', '']);
if sl.Count = 1 then
sl.AddStrings(['']);

// validate sent exchange strings
if not ValidateExchField(Field1Def, sl[0]) or
not ValidateExchField(Field2Def, sl[1]) then
if not Tst.ValidateMyExchange(AExchange, sl, ExchError) then
begin
Result := False;
sbar.Caption := Format('Invalid exchange: ''%s'' - expecting %s.',
[AExchange, ActiveContest.Msg]);
sbar.Caption := ExchError;

sbar.Align:= alBottom;
sbar.Visible:= true;
Expand Down Expand Up @@ -1378,31 +1369,6 @@ procedure TMainForm.SetMyExch2(const AExchType: TExchange2Type;
end;


function TMainForm.ValidateExchField(const FieldDef: PFieldDefinition;
const Avalue: string) : Boolean;
var
reg: TPerlRegEx;
s: string;
begin
if SimContest = scNaQp then begin
// special case - I can't figure out how to match an empty string,
// so manually check for an optional string.
s := FieldDef.R;
Result := s.StartsWith('()|(') and Avalue.IsEmpty;
if Result then Exit;
end;

reg := TPerlRegEx.Create();
try
reg.Subject := UTF8Encode(Avalue);
s:= '^(' + FieldDef.R + ')$';
reg.RegEx:= UTF8Encode(s);
Result:= Reg.Match;
finally
reg.Free;
end;
end;

{
Set pitch based on menu item number.
Must be within range [0, ComboBox1.Items.Count).
Expand Down