瀏覽代碼

Quick.Options improved validations

Exilon 5 年之前
父節點
當前提交
e203d89b86
共有 1 個文件被更改,包括 71 次插入22 次删除
  1. 71 22
      Quick.Options.pas

+ 71 - 22
Quick.Options.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.0
   Created     : 18/10/2019
-  Modified    : 28/11/2019
+  Modified    : 16/12/2019
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -90,14 +90,16 @@ type
   TOptions = class(TInterfacedObject,IOptionsValidator)
   private
     fName : string;
-    procedure ValidateRequired(aProperty : TRttiProperty);
-    procedure ValidateStringLength(aProperty: TRttiProperty; aValidation : StringLength);
-    procedure ValidateRange(aProperty : TRttiProperty; aValidation : Range);
+    procedure ValidateRequired(const aInstance : TObject; aProperty: TRttiProperty);
+    procedure ValidateStringLength(const aInstance : TObject; aProperty: TRttiProperty; aValidation : StringLength);
+    procedure ValidateRange(const aInstance : TObject; aProperty: TRttiProperty; aValidation : Range);
     procedure DoValidateOptions; virtual;
+    procedure ValidateObject(aObj : TObject);
+    procedure ValidateArray(aValue : TValue);
   public
     constructor Create;
     property Name : string read fName write fName;
-    procedure DefaultValues; virtual; abstract;
+    procedure DefaultValues; virtual;
     function ConfigureOptions<T : TOptions>(aOptionsFunc : TConfigureOptionsProc<T>) : IOptionsValidator;
     procedure ValidateOptions;
   end;
@@ -122,7 +124,7 @@ type
   ['{A593C8BB-53CF-4AA4-9641-BF974E45CBD1}']
     function AddSection(aOption : TOptionsClass; const aOptionsName : string = '') : TOptions;
     function GetOptions(aOptionClass : TOptionsClass): TOptions;
-    function GetSection(aOptionsSection : TOptionsClass; aOptions : TOptions) : Boolean; overload;
+    function GetSection(aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
   end;
 
   TSectionList = TObjectList<TOptions>;
@@ -167,7 +169,7 @@ type
     procedure SetReloadIfFileChanged(const Value: Boolean);
     function GetOptions(aOptionClass : TOptionsClass): TOptions; overload;
     function GetOptions(aIndex : Integer) : TOptions; overload;
-    function GetSection(aOptionsSection : TOptionsClass; aOptions : TOptions) : Boolean; overload;
+    function GetSection(aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
   public
     constructor Create(const aFilename : string; aOptionsSerializer : IOptionsSerializer; aReloadIfFileChanged : Boolean = False);
     destructor Destroy; override;
@@ -282,7 +284,7 @@ begin
   Result := fSections[aIndex];
 end;
 
-function TOptionsContainer.GetSection(aOptionsSection: TOptionsClass; aOptions: TOptions): Boolean;
+function TOptionsContainer.GetSection(aOptionsSection: TOptionsClass; var vOptions: TOptions): Boolean;
 var
   option : TOptions;
 begin
@@ -291,7 +293,7 @@ begin
   begin
     if option is TOptionsClass then
     begin
-      aOptions := option as TOptionsClass;
+      vOptions := option as TOptionsClass;
       Exit;
     end;
   end;
@@ -396,16 +398,27 @@ begin
   fName := '';
 end;
 
+procedure TOptions.DefaultValues;
+begin
+  //nothing
+end;
+
 procedure TOptions.DoValidateOptions;
+begin
+  ValidateObject(Self);
+end;
+
+procedure TOptions.ValidateObject(aObj : TObject);
 var
   ctx : TRttiContext;
   rtype : TRttiType;
   rprop : TRttiProperty;
   attrib : TCustomAttribute;
+  rvalue : TValue;
 begin
   ctx := TRttiContext.Create;
   try
-    rtype := ctx.GetType(Self.ClassInfo);
+    rtype := ctx.GetType(aObj.ClassInfo);
     for rprop in rtype.GetProperties do
     begin
       //check only published properties
@@ -414,9 +427,45 @@ begin
         //check validation option attributes
         for attrib in rprop.GetAttributes do
         begin
-          if attrib is Required  then ValidateRequired(rprop)
-          else if attrib is StringLength then ValidateStringLength(rprop,StringLength(attrib))
-          else if attrib is Range then ValidateRange(rprop,Range(attrib));
+          if attrib is Required  then ValidateRequired(aObj,rprop)
+          else if attrib is StringLength then ValidateStringLength(aObj,rprop,StringLength(attrib))
+          else if attrib is Range then ValidateRange(aObj,rprop,Range(attrib));
+        end;
+        rvalue := rprop.GetValue(aObj);
+        if not rvalue.IsEmpty then
+        begin
+          case rvalue.Kind of
+            tkClass : ValidateObject(rvalue.AsObject);
+            tkDynArray : ValidateArray(rvalue);
+          end;
+        end;
+      end;
+    end;
+  finally
+    ctx.Free;
+  end;
+end;
+
+procedure TOptions.ValidateArray(aValue : TValue);
+type
+  PPByte = ^PByte;
+var
+  ctx : TRttiContext;
+  rDynArray : TRttiDynamicArrayType;
+  itvalue : TValue;
+  i : Integer;
+begin
+  ctx := TRttiContext.Create;
+  try
+    rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
+    for i := 0 to aValue.GetArrayLength - 1 do
+    begin
+      TValue.Make(PPByte(aValue.GetReferenceToRawData)^ + rDynArray.ElementType.TypeSize * i, rDynArray.ElementType.Handle,itvalue);
+      if not itvalue.IsEmpty then
+      begin
+        case itvalue.Kind of
+          tkClass : ValidateObject(itvalue.AsObject);
+          tkDynArray : ValidateArray(itvalue);
         end;
       end;
     end;
@@ -437,19 +486,19 @@ begin
   end;
 end;
 
-procedure TOptions.ValidateRange(aProperty: TRttiProperty; aValidation : Range);
+procedure TOptions.ValidateRange(const aInstance : TObject; aProperty: TRttiProperty; aValidation : Range);
 var
   value : TValue;
   msg : string;
 begin
-  value := aProperty.GetValue(Self);
+  value := aProperty.GetValue(aInstance);
   if not value.IsEmpty then
   begin
     if value.Kind = tkFloat then
     begin
       if (value.AsExtended < aValidation.Min) or (value.AsExtended > aValidation.Max) then
       begin
-        if aValidation.ErrorMsg.IsEmpty then msg := Format('Option "%s.%s" exceeds predefined range (%2f - %2f)',[Self.Name,aProperty.Name,aValidation.Min,aValidation.Max])
+        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])
           else msg := aValidation.ErrorMsg;
         raise EOptionValidationError.Create(msg);
       end;
@@ -458,7 +507,7 @@ begin
     begin
       if (value.AsInt64 < aValidation.Min) or (value.AsInt64 > aValidation.Max) then
       begin
-        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)])
+        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)])
           else msg := aValidation.ErrorMsg;
         raise EOptionValidationError.Create(msg);
       end;
@@ -466,20 +515,20 @@ begin
   end;
 end;
 
-procedure TOptions.ValidateRequired(aProperty: TRttiProperty);
+procedure TOptions.ValidateRequired(const aInstance : TObject; aProperty: TRttiProperty);
 begin
-  if aProperty.GetValue(Self).IsEmpty then raise EOptionValidationError.CreateFmt('Option "%s.%s" is required',[Self.Name,aProperty.Name]);
+  if aProperty.GetValue(aInstance).IsEmpty then raise EOptionValidationError.CreateFmt('Option %s "%s.%s" is required',[Self.Name,aInstance.ClassName,aProperty.Name]);
 end;
 
-procedure TOptions.ValidateStringLength(aProperty: TRttiProperty; aValidation : StringLength);
+procedure TOptions.ValidateStringLength(const aInstance : TObject; aProperty: TRttiProperty; aValidation : StringLength);
 var
   value : TValue;
   msg : string;
 begin
-  value := aProperty.GetValue(Self);
+  value := aProperty.GetValue(aInstance);
   if (not value.IsEmpty) and (value.AsString.Length > aValidation.MaxLength) then
   begin
-    if aValidation.ErrorMsg.IsEmpty then msg := Format('Option "%s.%s" exceeds max length (%d)',[Self.Name,aProperty.Name,aValidation.MaxLength])
+    if aValidation.ErrorMsg.IsEmpty then msg := Format('Option %s "%s.%s" exceeds max length (%d)',[Self.Name,aInstance.ClassName,aProperty.Name,aValidation.MaxLength])
       else msg := aValidation.ErrorMsg;
 
     raise EOptionValidationError.Create(msg);