Sfoglia il codice sorgente

[expressions] refactorized to allow any type

Exilon 5 anni fa
parent
commit
633e0e7f82
1 ha cambiato i file con 158 aggiunte e 32 eliminazioni
  1. 158 32
      Quick.Expression.pas

+ 158 - 32
Quick.Expression.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.0
   Created     : 04/05/2019
-  Modified    : 08/03/2020
+  Modified    : 12/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -36,6 +36,7 @@ interface
 uses
   SysUtils,
   StrUtils,
+  TypInfo,
   RTTI,
   Quick.Commons,
   Quick.RTTI.Utils,
@@ -52,7 +53,7 @@ type
     fCombine : TCombine;
   public
     property Combine : TCombine read fCombine write fCombine;
-    function Validate(aValue : TObject) : Boolean; virtual; abstract;
+    function Validate(const aValue : TValue) : Boolean; virtual; abstract;
     function IsNull : Boolean; virtual; abstract;
   end;
 
@@ -60,17 +61,22 @@ type
   private
     fValue1 : string;
     fOperator : TOperator;
-    fValue2 : string;
+    fValue2 : TFlexValue;
     {$IFNDEF FPC}
     function ListContains(aArrayObj : TObject; const aValue : string): Boolean;
     function IListContains(aArrayObj : TValue; const aValue : string): Boolean;
     {$ENDIF}
     function ArrayContains(aArray : TValue; const aValue : string): Boolean;
+    class function IsEqual(aValue1, aValue2 : TFlexValue) : Boolean;
+    class function IsEqualOrLower(aValue1, aValue2 : TFlexValue) : Boolean;
+    class function IsEqualOrGreater(aValue1, aValue2 : TFlexValue) : Boolean;
+    class function IsGreater(aValue1, aValue2 : TFlexValue) : Boolean;
+    class function IsLower(aValue1, aValue2 : TFlexValue) : Boolean;
   public
     property Value1 : string read fValue1 write fValue1;
     property &Operator : TOperator read fOperator write fOperator;
-    property Value2 : string read fValue2 write fValue2;
-    function Validate(aValue : TObject) : Boolean; override;
+    property Value2 : TFlexValue read fValue2 write fValue2;
+    function Validate(const aValue : TValue) : Boolean; override;
     function IsNull : Boolean; override;
   end;
 
@@ -82,7 +88,7 @@ type
   public
     destructor Destroy; override;
     property Items : TExpressionArray read fArray write fArray;
-    function Validate(aValue : TObject) : Boolean; override;
+    function Validate(const aValue : TValue) : Boolean; override;
     function IsNull : Boolean; override;
     procedure Add(aExpression : TExpression);
   end;
@@ -96,7 +102,7 @@ type
     class function GetCombine(const aValue : string) : TCombine;
   public
     class function Parse(const aExpression : string) : TExpression;
-    class function Validate(const obj : TObject; const aExpression : string) : Boolean;
+    class function Validate(const aValue : TValue; const aExpression : string) : Boolean;
   end;
 
   ENotValidExpression = class(Exception);
@@ -201,16 +207,16 @@ begin
   //determine like
   if Result.&Operator = opLike then
   begin
-    if Result.Value2.CountChar('%') = 2 then Result.Value2 := Copy(Result.Value2, 2, Result.Value2.Length - 2)
-    else if Result.Value2.StartsWith('%') then
+    if Result.Value2.AsString.CountChar('%') = 2 then Result.Value2 := Copy(Result.Value2.AsString, 2, Result.Value2.AsString.Length - 2)
+    else if Result.Value2.AsString.StartsWith('%') then
     begin
       Result.&Operator := TOperator.opLikeR;
-      Result.Value2 := Copy(Result.Value2, 2, Result.Value2.Length);
+      Result.Value2 := Copy(Result.Value2.AsString, 2, Result.Value2.AsString.Length);
     end
-    else if Result.Value2.EndsWith('%') then
+    else if Result.Value2.AsString.EndsWith('%') then
     begin
       Result.&Operator := TOperator.opLikeL;
-      Result.Value2 := Copy(Result.Value2,LOWSTR,Result.Value2.Length - 1);
+      Result.Value2 := Copy(Result.Value2.AsString,LOWSTR,Result.Value2.AsString.Length - 1);
     end
     else raise ENotValidExpression.Create('Not valid Like specified!');
   end;
@@ -232,13 +238,13 @@ begin
     else Result := GetMultiExpression(exp);
 end;
 
-class function TExpressionParser.Validate(const obj: TObject; const aExpression: string): Boolean;
+class function TExpressionParser.Validate(const aValue: TValue; const aExpression: string): Boolean;
 var
   exp : TExpression;
 begin
   exp := TExpressionParser.Parse(aExpression);
   try
-    Result := exp.Validate(obj);
+    Result := exp.Validate(aValue);
   finally
     exp.Free;
   end;
@@ -246,31 +252,113 @@ end;
 
 { TSingleExpression }
 
+class function TSingleExpression.IsEqual(aValue1, aValue2: TFlexValue): Boolean;
+begin
+  case aValue1.DataType of
+    TValueDataType.dtNull : Exit(False);
+    TValueDataType.dtString,
+    TValueDataType.dtWideString,
+    TValueDataType.dtAnsiString : Result := CompareText(aValue1,aValue2) = 0;
+    TValueDataType.dtInteger,
+    TValueDataType.dtInt64 : Result := aValue1.AsInt64 = aValue2.AsInt64;
+    TValueDataType.dtExtended,
+    TValueDataType.dtDouble : Result := aValue1.AsExtended = aValue2.AsExtended;
+    TValueDataType.dtBoolean : Result := aValue1.AsBoolean = aValue2.AsBoolean;
+    else raise EExpressionNotSupported.Create('Expression type not supported!');
+  end;
+end;
+
+class function TSingleExpression.IsEqualOrGreater(aValue1, aValue2: TFlexValue): Boolean;
+begin
+  case aValue1.DataType of
+    TValueDataType.dtNull : Exit(False);
+    TValueDataType.dtString,
+    TValueDataType.dtWideString,
+    TValueDataType.dtAnsiString : Result := CompareText(aValue1,aValue2) >= 0;
+    TValueDataType.dtInteger,
+    TValueDataType.dtInt64 : Result := aValue1.AsInt64 >= aValue2.AsInt64;
+    TValueDataType.dtExtended,
+    TValueDataType.dtDouble : Result := aValue1.AsExtended >= aValue2.AsExtended;
+    TValueDataType.dtBoolean : Result := aValue1.AsBoolean >= aValue2.AsBoolean;
+    else raise EExpressionNotSupported.Create('Expression type not supported!');
+  end;
+end;
+
+class function TSingleExpression.IsEqualOrLower(aValue1, aValue2: TFlexValue): Boolean;
+begin
+  case aValue1.DataType of
+    TValueDataType.dtNull : Exit(False);
+    TValueDataType.dtString,
+    TValueDataType.dtWideString,
+    TValueDataType.dtAnsiString : Result := CompareText(aValue1,aValue2) <= 0;
+    TValueDataType.dtInteger,
+    TValueDataType.dtInt64 : Result := aValue1.AsInt64 <= aValue2.AsInt64;
+    TValueDataType.dtExtended,
+    TValueDataType.dtDouble,
+    TValueDataType.dtDateTime : Result := aValue1.AsExtended <= aValue2.AsExtended;
+    TValueDataType.dtBoolean : Result := aValue1.AsBoolean <= aValue2.AsBoolean;
+    else raise EExpressionNotSupported.Create('Expression type not supported!');
+  end;
+end;
+
+class function TSingleExpression.IsGreater(aValue1, aValue2: TFlexValue): Boolean;
+begin
+  case aValue1.DataType of
+    TValueDataType.dtNull : Exit(False);
+    TValueDataType.dtString,
+    TValueDataType.dtWideString,
+    TValueDataType.dtAnsiString : Result := CompareText(aValue1,aValue2) > 0;
+    TValueDataType.dtInteger,
+    TValueDataType.dtInt64 : Result := aValue1.AsInt64 > aValue2.AsInt64;
+    TValueDataType.dtExtended,
+    TValueDataType.dtDouble,
+    TValueDataType.dtDateTime : Result := aValue1.AsExtended > aValue2.AsExtended;
+    TValueDataType.dtBoolean : Result := aValue1.AsBoolean > aValue2.AsBoolean;
+    else raise EExpressionNotSupported.Create('Expression type not supported!');
+  end;
+end;
+
+class function TSingleExpression.IsLower(aValue1, aValue2: TFlexValue): Boolean;
+begin
+  case aValue1.DataType of
+    TValueDataType.dtNull : Exit(False);
+    TValueDataType.dtString,
+    TValueDataType.dtWideString,
+    TValueDataType.dtAnsiString : Result := CompareText(aValue1,aValue2) < 0;
+    TValueDataType.dtInteger,
+    TValueDataType.dtInt64 : Result := aValue1.AsInt64 < aValue2.AsInt64;
+    TValueDataType.dtExtended,
+    TValueDataType.dtDouble,
+    TValueDataType.dtDateTime : Result := aValue1.AsExtended < aValue2.AsExtended;
+    TValueDataType.dtBoolean : Result := aValue1.AsBoolean < aValue2.AsBoolean;
+    else raise EExpressionNotSupported.Create('Expression type not supported!');
+  end;
+end;
+
 function TSingleExpression.IsNull: Boolean;
 begin
-  Result := (fValue1.IsEmpty) or (fValue2.IsEmpty);
+  Result := (fValue1.IsEmpty) or (fValue2.IsNullOrEmpty);
 end;
 
-function TSingleExpression.Validate(aValue : TObject) : Boolean;
+function TSingleExpression.Validate(const aValue : TValue) : Boolean;
 var
   value1 : TFlexValue;
-  //rvalue : TValue;
 begin
   Result := False;
-  if aValue = nil then Exit;
-  value1.AsTValue := TRTTI.GetPathValue(aValue,fValue1);
-  //rvalue := TRTTI.GetPathValue(aValue,fValue1);
+  if aValue.IsEmpty then Exit;
+  if aValue.IsObject then
+  begin
+    if fValue1.Contains('.') then value1.AsTValue := TRTTI.GetPathValue(aValue.AsObject,fValue1)
+      else value1.AsTValue := TRTTI.GetPropertyValueEx(aValue.AsObject,fValue1);
+  end
+  else value1.AsTValue := aValue;
   case fOperator of
-    TOperator.opEqual :
-      begin
-        if value1.IsString then Result := CompareText(value1,fValue2) = 0
-          else Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF} = fValue2;
-      end;
-    TOperator.opNotEqual : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF}  <> fValue2;
-    TOperator.opGreater : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF}  > fValue2;
-    TOperator.opEqualOrGreater : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF}  >= fValue2;
-    TOperator.opLower : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF}  < fValue2;
-    TOperator.opEqualOrLower : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF}  <= fValue2;
+    TOperator.opEqual : Result := IsEqual(value1,fValue2);
+    TOperator.opNotEqual : Result := not IsEqual(value1,fValue2);
+    TOperator.opGreater : Result := IsGreater(value1,fValue2);
+    TOperator.opEqualOrGreater : Result := IsEqualOrGreater(value1,fValue2);
+    TOperator.opLower : Result := IsLower(value1,fValue2);
+    TOperator.opEqualOrLower : Result := IsEqualOrLower(value1,fValue2);
     TOperator.opLike : Result := {$IFNDEF FPC}ContainsText(value1,fValue2);{$ELSE}AnsiContainsText(value1.AsAnsiString,fValue2);{$ENDIF}
     TOperator.opLikeR : Result := EndsText(fValue2,value1);
     TOperator.opLikeL : Result := StartsText(fValue2,value1);
@@ -288,6 +376,43 @@ begin
   end;
 end;
 
+//function TSingleExpression.Validate(aValue : TObject) : Boolean;
+//var
+//  value1 : TFlexValue;
+//  //rvalue : TValue;
+//begin
+//  Result := False;
+//  if aValue = nil then Exit;
+//  value1.AsTValue := TRTTI.GetPathValue(aValue,fValue1);
+//  //rvalue := TRTTI.GetPathValue(aValue,fValue1);
+//  case fOperator of
+//    TOperator.opEqual :
+//      begin
+//        if value1.IsString then Result := CompareText(value1,fValue2) = 0
+//          else Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF} = fValue2;
+//      end;
+//    TOperator.opNotEqual : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF} <> fValue2;
+//    TOperator.opGreater : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF} > fValue2;
+//    TOperator.opEqualOrGreater : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF} >= fValue2;
+//    TOperator.opLower : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF} < fValue2;
+//    TOperator.opEqualOrLower : Result := value1{$IFDEF FPC}.AsAnsiString{$ENDIF} <= fValue2;
+//    TOperator.opLike : Result := {$IFNDEF FPC}ContainsText(value1,fValue2);{$ELSE}AnsiContainsText(value1.AsAnsiString,fValue2);{$ENDIF}
+//    TOperator.opLikeR : Result := EndsText(fValue2,value1);
+//    TOperator.opLikeL : Result := StartsText(fValue2,value1);
+//    TOperator.opContains :
+//      begin
+//        {$IFNDEF FPC}
+//        if value1.IsObject then Result := ListContains(value1.AsObject,fValue2)
+//        else if value1.IsInterface then Result := IListContains(value1.AsTValue,fValue2)
+//          else if value1.IsArray then Result := ArrayContains(value1.AsTValue,fValue2);
+//        {$ELSE}
+//        if value1.IsArray then Result := ArrayContains(value1.AsTValue,fValue2);
+//        {$ENDIF}
+//      end
+//    else raise ENotValidExpression.Create('Operator not defined');
+//  end;
+//end;
+
 {$IFNDEF FPC}
 function TSingleExpression.ListContains(aArrayObj : TObject; const aValue : string): Boolean;
 var
@@ -347,7 +472,8 @@ begin
       tkString,
       {$ENDIF}
       tkUnicodeString, tkWideString : Result := CompareText(arrItem.AsString,aValue) = 0;
-      tkInteger, tkInt64 : Result := arrItem.AsInt64 = aValue.ToInt64;
+      tkInteger,
+      tkInt64 : Result := arrItem.AsInt64 = aValue.ToInt64;
       tkFloat : Result := arrItem.AsExtended = aValue.ToExtended;
       else raise EExpressionNotSupported.CreateFmt('Type Array<%s> not supported',[arrItem.TypeInfo.Name]);
     end;
@@ -375,7 +501,7 @@ begin
   Result := High(fArray) < 0;
 end;
 
-function TMultiExpression.Validate(aValue : TObject) : Boolean;
+function TMultiExpression.Validate(const aValue : TValue) : Boolean;
 var
   i : Integer;
 begin