7
7
Author : Kike Pérez
8
8
Version : 1.0
9
9
Created : 18/10/2019
10
- Modified : 28/11 /2019
10
+ Modified : 16/12 /2019
11
11
12
12
This file is part of QuickLib: https://github.com/exilon/QuickLib
13
13
@@ -90,14 +90,16 @@ TOptions = class;
90
90
TOptions = class (TInterfacedObject,IOptionsValidator)
91
91
private
92
92
fName : string;
93
- procedure ValidateRequired (aProperty : TRttiProperty);
94
- procedure ValidateStringLength (aProperty: TRttiProperty; aValidation : StringLength);
95
- procedure ValidateRange (aProperty : TRttiProperty; aValidation : Range);
93
+ procedure ValidateRequired (const aInstance : TObject; aProperty : TRttiProperty);
94
+ procedure ValidateStringLength (const aInstance : TObject; aProperty: TRttiProperty; aValidation : StringLength);
95
+ procedure ValidateRange (const aInstance : TObject; aProperty : TRttiProperty; aValidation : Range);
96
96
procedure DoValidateOptions ; virtual ;
97
+ procedure ValidateObject (aObj : TObject);
98
+ procedure ValidateArray (aValue : TValue);
97
99
public
98
100
constructor Create;
99
101
property Name : string read fName write fName;
100
- procedure DefaultValues ; virtual ; abstract ;
102
+ procedure DefaultValues ; virtual ;
101
103
function ConfigureOptions <T : TOptions>(aOptionsFunc : TConfigureOptionsProc<T>) : IOptionsValidator;
102
104
procedure ValidateOptions ;
103
105
end ;
@@ -122,7 +124,7 @@ TOptionsClass = class of TOptions;
122
124
[' {A593C8BB-53CF-4AA4-9641-BF974E45CBD1}' ]
123
125
function AddSection (aOption : TOptionsClass; const aOptionsName : string = ' ' ) : TOptions;
124
126
function GetOptions (aOptionClass : TOptionsClass): TOptions;
125
- function GetSection (aOptionsSection : TOptionsClass; aOptions : TOptions) : Boolean; overload;
127
+ function GetSection (aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
126
128
end ;
127
129
128
130
TSectionList = TObjectList<TOptions>;
@@ -167,7 +169,7 @@ TOptionsContainer = class(TInterfacedObject,IOptionsContainer)
167
169
procedure SetReloadIfFileChanged (const Value : Boolean);
168
170
function GetOptions (aOptionClass : TOptionsClass): TOptions; overload;
169
171
function GetOptions (aIndex : Integer) : TOptions; overload;
170
- function GetSection (aOptionsSection : TOptionsClass; aOptions : TOptions) : Boolean; overload;
172
+ function GetSection (aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
171
173
public
172
174
constructor Create(const aFilename : string; aOptionsSerializer : IOptionsSerializer; aReloadIfFileChanged : Boolean = False);
173
175
destructor Destroy; override;
@@ -282,7 +284,7 @@ function TOptionsContainer.GetOptions(aIndex: Integer): TOptions;
282
284
Result := fSections[aIndex];
283
285
end ;
284
286
285
- function TOptionsContainer.GetSection (aOptionsSection: TOptionsClass; aOptions : TOptions): Boolean;
287
+ function TOptionsContainer.GetSection (aOptionsSection: TOptionsClass; var vOptions : TOptions): Boolean;
286
288
var
287
289
option : TOptions;
288
290
begin
@@ -291,7 +293,7 @@ function TOptionsContainer.GetSection(aOptionsSection: TOptionsClass; aOptions:
291
293
begin
292
294
if option is TOptionsClass then
293
295
begin
294
- aOptions := option as TOptionsClass;
296
+ vOptions := option as TOptionsClass;
295
297
Exit;
296
298
end ;
297
299
end ;
@@ -396,16 +398,27 @@ constructor TOptions.Create;
396
398
fName := ' ' ;
397
399
end ;
398
400
401
+ procedure TOptions.DefaultValues ;
402
+ begin
403
+ // nothing
404
+ end ;
405
+
399
406
procedure TOptions.DoValidateOptions ;
407
+ begin
408
+ ValidateObject(Self);
409
+ end ;
410
+
411
+ procedure TOptions.ValidateObject (aObj : TObject);
400
412
var
401
413
ctx : TRttiContext;
402
414
rtype : TRttiType;
403
415
rprop : TRttiProperty;
404
416
attrib : TCustomAttribute;
417
+ rvalue : TValue;
405
418
begin
406
419
ctx := TRttiContext.Create;
407
420
try
408
- rtype := ctx.GetType(Self .ClassInfo);
421
+ rtype := ctx.GetType(aObj .ClassInfo);
409
422
for rprop in rtype.GetProperties do
410
423
begin
411
424
// check only published properties
@@ -414,9 +427,45 @@ procedure TOptions.DoValidateOptions;
414
427
// check validation option attributes
415
428
for attrib in rprop.GetAttributes do
416
429
begin
417
- if attrib is Required then ValidateRequired(rprop)
418
- else if attrib is StringLength then ValidateStringLength(rprop,StringLength(attrib))
419
- else if attrib is Range then ValidateRange(rprop,Range(attrib));
430
+ if attrib is Required then ValidateRequired(aObj,rprop)
431
+ else if attrib is StringLength then ValidateStringLength(aObj,rprop,StringLength(attrib))
432
+ else if attrib is Range then ValidateRange(aObj,rprop,Range(attrib));
433
+ end ;
434
+ rvalue := rprop.GetValue(aObj);
435
+ if not rvalue.IsEmpty then
436
+ begin
437
+ case rvalue.Kind of
438
+ tkClass : ValidateObject(rvalue.AsObject);
439
+ tkDynArray : ValidateArray(rvalue);
440
+ end ;
441
+ end ;
442
+ end ;
443
+ end ;
444
+ finally
445
+ ctx.Free;
446
+ end ;
447
+ end ;
448
+
449
+ procedure TOptions.ValidateArray (aValue : TValue);
450
+ type
451
+ PPByte = ^PByte;
452
+ var
453
+ ctx : TRttiContext;
454
+ rDynArray : TRttiDynamicArrayType;
455
+ itvalue : TValue;
456
+ i : Integer;
457
+ begin
458
+ ctx := TRttiContext.Create;
459
+ try
460
+ rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
461
+ for i := 0 to aValue.GetArrayLength - 1 do
462
+ begin
463
+ TValue.Make(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType.Handle,itvalue);
464
+ if not itvalue.IsEmpty then
465
+ begin
466
+ case itvalue.Kind of
467
+ tkClass : ValidateObject(itvalue.AsObject);
468
+ tkDynArray : ValidateArray(itvalue);
420
469
end ;
421
470
end ;
422
471
end ;
@@ -437,19 +486,19 @@ procedure TOptions.ValidateOptions;
437
486
end ;
438
487
end ;
439
488
440
- procedure TOptions.ValidateRange (aProperty: TRttiProperty; aValidation : Range);
489
+ procedure TOptions.ValidateRange (const aInstance : TObject; aProperty: TRttiProperty; aValidation : Range);
441
490
var
442
491
value : TValue;
443
492
msg : string;
444
493
begin
445
- value := aProperty.GetValue(Self );
494
+ value := aProperty.GetValue(aInstance );
446
495
if not value .IsEmpty then
447
496
begin
448
497
if value .Kind = tkFloat then
449
498
begin
450
499
if (value .AsExtended < aValidation.Min) or (value .AsExtended > aValidation.Max) then
451
500
begin
452
- if aValidation.ErrorMsg.IsEmpty then msg := Format(' Option "%s.%s" exceeds predefined range (%2f - %2f)' ,[Self.Name ,aProperty.Name ,aValidation.Min,aValidation.Max])
501
+ if aValidation.ErrorMsg.IsEmpty then msg := Format(' Option %s "%s.%s" exceeds predefined range (%2f - %2f)' ,[Self.Name ,aInstance.ClassName ,aProperty.Name ,aValidation.Min,aValidation.Max])
453
502
else msg := aValidation.ErrorMsg;
454
503
raise EOptionValidationError.Create(msg);
455
504
end ;
@@ -458,28 +507,28 @@ procedure TOptions.ValidateRange(aProperty: TRttiProperty; aValidation : Range);
458
507
begin
459
508
if (value .AsInt64 < aValidation.Min) or (value .AsInt64 > aValidation.Max) then
460
509
begin
461
- if aValidation.ErrorMsg.IsEmpty then msg := Format(' Option "%s.%s" exceeds predefined range (%d - %d)' ,[Self.Name ,aProperty.Name ,Round(aValidation.Min),Round(aValidation.Max)])
510
+ if aValidation.ErrorMsg.IsEmpty then msg := Format(' Option %s "%s.%s" exceeds predefined range (%d - %d)' ,[Self.Name ,aInstance.ClassName ,aProperty.Name ,Round(aValidation.Min),Round(aValidation.Max)])
462
511
else msg := aValidation.ErrorMsg;
463
512
raise EOptionValidationError.Create(msg);
464
513
end ;
465
514
end ;
466
515
end ;
467
516
end ;
468
517
469
- procedure TOptions.ValidateRequired (aProperty: TRttiProperty);
518
+ procedure TOptions.ValidateRequired (const aInstance : TObject; aProperty: TRttiProperty);
470
519
begin
471
- if aProperty.GetValue(Self ).IsEmpty then raise EOptionValidationError.CreateFmt(' Option "%s.%s" is required' ,[Self.Name ,aProperty.Name ]);
520
+ if aProperty.GetValue(aInstance ).IsEmpty then raise EOptionValidationError.CreateFmt(' Option %s "%s.%s" is required' ,[Self.Name ,aInstance.ClassName ,aProperty.Name ]);
472
521
end ;
473
522
474
- procedure TOptions.ValidateStringLength (aProperty: TRttiProperty; aValidation : StringLength);
523
+ procedure TOptions.ValidateStringLength (const aInstance : TObject; aProperty: TRttiProperty; aValidation : StringLength);
475
524
var
476
525
value : TValue;
477
526
msg : string;
478
527
begin
479
- value := aProperty.GetValue(Self );
528
+ value := aProperty.GetValue(aInstance );
480
529
if (not value .IsEmpty) and (value .AsString.Length > aValidation.MaxLength) then
481
530
begin
482
- if aValidation.ErrorMsg.IsEmpty then msg := Format(' Option "%s.%s" exceeds max length (%d)' ,[Self.Name ,aProperty.Name ,aValidation.MaxLength])
531
+ if aValidation.ErrorMsg.IsEmpty then msg := Format(' Option %s "%s.%s" exceeds max length (%d)' ,[Self.Name ,aInstance.ClassName ,aProperty.Name ,aValidation.MaxLength])
483
532
else msg := aValidation.ErrorMsg;
484
533
485
534
raise EOptionValidationError.Create(msg);
0 commit comments