Explorar o código

Merge branch 'develop' into add-TGUID-serialiser

Exilon %!s(int64=5) %!d(string=hai) anos
pai
achega
aa2102f0d9

+ 3 - 0
.gitignore

@@ -59,3 +59,6 @@ release/
 *.lps
 bin/
 *.res
+*.tmp
+bin
+*.xml

+ 27 - 13
Quick.AutoMapper.pas

@@ -1,13 +1,13 @@
 { ***************************************************************************
 
-  Copyright (c) 2015-2018 Kike Pérez
+  Copyright (c) 2015-2020 Kike Pérez
 
   Unit        : Quick.AutoMapper
   Description : Auto Mapper object properties
   Author      : Kike Pérez
   Version     : 1.5
   Created     : 25/08/2018
-  Modified    : 16/10/2019
+  Modified    : 02/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -71,6 +71,7 @@ type
     destructor Destroy; override;
     procedure AddMap(const aName, aMapName : string);
     function GetMap(const aName : string; out vMapName : string) : Boolean;
+    function Count : Integer;
   end;
 
   TObjMapper = class
@@ -209,11 +210,17 @@ begin
           begin
             {$IFNDEF PROPERTYPATH_MODE}
               if rType.GetProperty(mapname) = nil then raise EAutoMapperError.CreateFmt('No valid custom mapping (Source: %s - Target: %s)',[mapname,tgtprop.Name]);
-              {$IFNDEF FPC}
-              tgtprop.SetValue(aTgtObj,rType.GetProperty(mapname).GetValue(aSrcObj))
-              {$ELSE}
-              SetPropValue(aTgtObj,tgtprop.Name,GetPropValue(aSrcObj,mapname));
-              {$ENDIF}
+              begin
+                try
+                  {$IFNDEF FPC}
+                  tgtprop.SetValue(aTgtObj,rType.GetProperty(mapname).GetValue(aSrcObj));
+                  {$ELSE}
+                  SetPropValue(aTgtObj,tgtprop.Name,GetPropValue(aSrcObj,mapname));
+                  {$ENDIF}
+                except
+                  on E : Exception do raise EAutoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
+                end;
+              end;
             {$ELSE}
               if not TRTTI.PathExists(aSrcObj,mapname) then raise EAutoMapperError.CreateFmt('No valid custom mapping (Source: %s - Target: %s)',[mapname,tgtprop.Name]);
               TRTTI.SetPathValue(aTgtObj,tgtprop.Name,TRTTI.GetPathValue(aSrcObj,mapname));
@@ -229,7 +236,7 @@ begin
               SetPropValue(aTgtObj,tgtprop.Name,GetPropValue(aSrcObj,tgtprop.Name));
               {$ENDIF}
             except
-              on E : Exception do raise EAUtoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
+              on E : Exception do raise EAutoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
             end;
           end;
         end
@@ -273,7 +280,7 @@ begin
       begin
         obj := tgtprop.GetValue(aTgtObj).AsObject;
         {$IFNDEF FPC}
-        if obj = nil then obj := TObject.Create;
+        if obj = nil then obj := GetObjectProp(aSrcObj,tgtprop.Name).ClassType.Create;// TObject.Create;
         {$ELSE}
         if obj = nil then obj := GetObjectProp(aSrcObj,tgtprop.Name).ClassType.Create;
         {$ENDIF}
@@ -282,7 +289,9 @@ begin
         begin
           {$IFNDEF FPC}
           try
-            clname := rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).AsObject.ClassName;
+            if (rType.GetProperty(tgtprop.Name) <> nil)
+              and (not rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).IsEmpty) then clname := rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).AsObject.ClassName
+            else Continue;
           except
             on E : Exception do raise EAUtoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
           end;
@@ -398,14 +407,19 @@ begin
 end;
 {$ENDIF}
 
-{ TCustomMappingFields }
+{ TCustomMapping }
 
 procedure TCustomMapping.AddMap(const aName, aMapName: string);
 begin
   //add map fields
   fMapDictionary.Add(aName,aMapName);
-  //add reverse lookup
-  fMapDictionary.Add(aMapName,aName);
+  //add reverse lookup if not same name
+  if aName <> aMapName then fMapDictionary.Add(aMapName,aName);
+end;
+
+function TCustomMapping.Count: Integer;
+begin
+  Result := fMapDictionary.Count;
 end;
 
 constructor TCustomMapping.Create;

+ 26 - 20
Quick.Azure.pas

@@ -1,13 +1,13 @@
 { ***************************************************************************
 
-  Copyright (c) 2015-2019 Kike Pérez
+  Copyright (c) 2015-2020 Kike Pérez
 
   Unit        : Quick.Azure
   Description : Azure blobs operations
   Author      : Kike Pérez
   Version     : 1.4
   Created     : 27/08/2015
-  Modified    : 08/10/2019
+  Modified    : 02/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -91,6 +91,7 @@ type
       function PutBlob(const azContainer : string; cStream : TStream; const azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean; overload;
       function GetBlob(const azContainer, azBlobName, cFilenameTo : string; out azResponseInfo : TAzureResponseInfo) : Boolean; overload;
       function GetBlob(const azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo; out Stream : TMemoryStream) : Boolean; overload;
+      function GetBlob(const azContainer, azBlobName: string; out azResponseInfo: TAzureResponseInfo; var Stream: TStream): Boolean; overload;
       function GetBlob(const azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo) : TMemoryStream; overload;
       function CopyBlob(const azSourceContainer, azSourceBlobName : string; azTargetContainer, azTargetBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
       function RenameBlob(const azContainer, azSourceBlobName, azTargetBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
@@ -100,7 +101,7 @@ type
       function ListBlobs(const azContainer, azBlobsStartWith : string; Recursive : Boolean; out azResponseInfo : TAzureResponseInfo) : TBlobList;
       function ListBlobsNames(const azContainer, azBlobsStartWith : string; Recursive : Boolean; out azResponseInfo : TAzureResponseInfo) : TStrings;
       function ExistsContainer(const azContainer : string) : Boolean;
-      function ListContainers(const azContainersStartWith : string; azResponseInfo : TAzureResponseInfo) : TStrings;
+      function ListContainers(const azContainersStartWith : string; out azResponseInfo : TAzureResponseInfo) : TStrings;
       function CreateContainer(const azContainer : string; azPublicAccess : TBlobPublicAccess; out azResponseInfo : TAzureResponseInfo) : Boolean;
       function DeleteContainer(const azContainer : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
   end;
@@ -376,6 +377,21 @@ begin
 end;
 
 function TQuickAzure.GetBlob(const azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo; out Stream : TMemoryStream) : Boolean;
+begin
+  Stream := TMemoryStream.Create;
+  try
+    GetBlob(azContainer,azBlobName,azResponseInfo,TStream(Stream));
+  except
+    Stream.Free;
+  end;
+end;
+
+function TQuickAzure.GetBlob(const azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo) : TMemoryStream;
+begin
+  GetBlob(azContainer,azBlobName,azResponseInfo,Result);
+end;
+
+function TQuickAzure.GetBlob(const azContainer, azBlobName: string; out azResponseInfo: TAzureResponseInfo; var Stream: TStream): Boolean;
 var
   BlobService : TAzureBlobService;
   CloudResponseInfo : TCloudResponseInfo;
@@ -383,33 +399,23 @@ var
   blobname : string;
 begin
   Result := False;
-  Stream := TMemoryStream.Create;
   container := CheckContainer(azContainer);
   blobname := RemoveFirstSlash(azBlobName);
   BlobService := TAzureBlobService.Create(fconAzure);
   try
     BlobService.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
     try
-      CloudResponseInfo := TCloudResponseInfo.Create;
-      try
-        Result := BlobService.GetBlob(container,blobname,Stream,EmptyStr,CloudResponseInfo);
-        azResponseInfo := GetResponseInfo(CloudResponseInfo);
-      finally
-        CloudResponseInfo.Free;
-      end;
-    except
-      Stream := nil;
+      Result := BlobService.GetBlob(container,blobname,Stream,EmptyStr,CloudResponseInfo);
+      azResponseInfo := GetResponseInfo(CloudResponseInfo);
+    finally
+      CloudResponseInfo.Free;
     end;
   finally
     BlobService.Free;
   end;
 end;
 
-function TQuickAzure.GetBlob(const azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo) : TMemoryStream;
-begin
-  GetBlob(azContainer,azBlobName,azResponseInfo,Result);
-end;
-
 function TQuickAzure.CheckContainer(const aContainer: string): string;
 begin
   if aContainer = '' then Result := '$root'
@@ -763,7 +769,7 @@ begin
   end;
 end;
 
-function TQuickAzure.ListContainers(const azContainersStartWith : string; azResponseInfo : TAzureResponseInfo) : TStrings;
+function TQuickAzure.ListContainers(const azContainersStartWith : string; out azResponseInfo : TAzureResponseInfo) : TStrings;
 var
   BlobService : TAzureBlobService;
   CloudResponseInfo : TCloudResponseInfo;
@@ -794,7 +800,7 @@ begin
             end;
           end;
         finally
-          if Assigned(AzContainer) then
+          if Assigned(AzContainers) then
           begin
             //frees ContainerList objects
             for AzContainer in AzContainers do AzContainer.Free;

+ 510 - 0
Quick.Collections.pas

@@ -0,0 +1,510 @@
+{ ***************************************************************************
+
+  Copyright (c) 2016-2020 Kike Pérez
+
+  Unit        : Quick.Collections
+  Description : Generic Collections
+  Author      : Kike Pérez
+  Version     : 1.2
+  Created     : 07/03/2020
+  Modified    : 20/03/2020
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Collections;
+
+{$i QuickLib.inc}
+
+interface
+
+uses
+  System.SysUtils,
+  TypInfo,
+  System.Types,
+  System.Generics.Defaults,
+  System.Generics.Collections,
+  Quick.Linq;
+
+type
+
+  IListBase<T> = interface
+  ['{9A9B2DB9-14E4-49DD-A628-F84F50539F41}']
+    function GetList: TArray<T>;
+    function GetCapacity: Integer;
+    procedure SetCapacity(Value: Integer); overload;
+    function GetCount : Integer;
+    procedure SetCount(Value: Integer);
+    function GetItem(Index: Integer): T;
+    procedure SetItem(Index: Integer; const Value: T);
+    function GetEnumerator : TEnumerator<T>;
+    function Add(const Value: T): Integer;
+    procedure AddRange(const Values: array of T); overload;
+    procedure AddRange(const Collection: IEnumerable<T>); overload;
+    procedure AddRange(const Collection: TEnumerable<T>); overload;
+    procedure Insert(Index: Integer; const Value: T);
+    procedure InsertRange(Index: Integer; const Values: array of T; Count: Integer); overload;
+    procedure InsertRange(Index: Integer; const Values: array of T); overload;
+    procedure InsertRange(Index: Integer; const Collection: IEnumerable<T>); overload;
+    procedure InsertRange(Index: Integer; const Collection: TEnumerable<T>); overload;
+    function Remove(const Value: T): Integer;
+    function RemoveItem(const Value: T; Direction: TDirection): Integer;
+    procedure Delete(Index: Integer);
+    procedure DeleteRange(AIndex, ACount: Integer);
+    function ExtractItem(const Value: T; Direction: TDirection): T;
+    function Extract(const Value: T): T;
+    function ExtractAt(Index: Integer): T;
+    procedure Exchange(Index1, Index2: Integer);
+    procedure Move(CurIndex, NewIndex: Integer);
+    function First: T;
+    function Last: T;
+    procedure Clear;
+    function Contains(const Value: T): Boolean;
+    function IndexOf(const Value: T): Integer;
+    function IndexOfItem(const Value: T; Direction: TDirection): Integer;
+    function LastIndexOf(const Value: T): Integer;
+    procedure Reverse;
+    procedure Sort; overload;
+    procedure Sort(const AComparer: IComparer<T>); overload;
+    function BinarySearch(const Item: T; out Index: Integer): Boolean; overload;
+    function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean; overload;
+    procedure TrimExcess;
+    function ToArray: TArray<T>;
+    procedure FromList(const aList : TList<T>);
+    function ToList : TList<T>;
+    property Capacity: Integer read GetCapacity write SetCapacity;
+    property Count: Integer read GetCount write SetCount;
+    property Items[Index: Integer]: T read GetItem write SetItem; default;
+    property List: TArray<T> read GetList;
+    function Any : Boolean; overload;
+  end;
+
+  IList<T> = interface(IListBase<T>)
+  ['{78952BD5-7D15-42BB-ADCB-2F835DF879A0}']
+    function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
+    function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
+    function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
+    function Where(const aWhereClause: string): ILinqQuery<T>; overload;
+    {$IFNDEF FPC}
+    function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
+    {$ENDIF}
+  end;
+
+  IObjectList<T : class> = interface(IListBase<T>)
+  ['{7380847B-9F94-4FB8-8B73-DC8ACAFF1729}']
+    function Any(const aWhereClause : string; aValues : array of const) : Boolean; overload;
+    function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
+    function Where(const aWhereClause: string): ILinqQuery<T>; overload;
+    function Where(aPredicate : TPredicate<T>): ILinqQuery<T>; overload;
+  end;
+
+  TXList<T> = class(TInterfacedObject,IList<T>)
+  private type
+    arrayofT = array of T;
+  private
+    fList : TList<T>;
+    function GetList: TArray<T>;
+    function GetCapacity: Integer;
+    procedure SetCapacity(Value: Integer); overload;
+    function GetCount : Integer;
+    procedure SetCount(Value: Integer);
+    function GetItem(Index: Integer): T;
+    procedure SetItem(Index: Integer; const Value: T);
+    function GetEnumerator : TEnumerator<T>;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    function Add(const Value: T): Integer; inline;
+    procedure AddRange(const Values: array of T); overload;
+    procedure AddRange(const Collection: IEnumerable<T>); overload; inline;
+    procedure AddRange(const Collection: TEnumerable<T>); overload;
+    procedure Insert(Index: Integer; const Value: T); inline;
+    procedure InsertRange(Index: Integer; const Values: array of T; Count: Integer); overload;
+    procedure InsertRange(Index: Integer; const Values: array of T); overload;
+    procedure InsertRange(Index: Integer; const Collection: IEnumerable<T>); overload;
+    procedure InsertRange(Index: Integer; const Collection: TEnumerable<T>); overload;
+    function Remove(const Value: T): Integer; inline;
+    function RemoveItem(const Value: T; Direction: TDirection): Integer; inline;
+    procedure Delete(Index: Integer); inline;
+    procedure DeleteRange(AIndex, ACount: Integer); inline;
+    function ExtractItem(const Value: T; Direction: TDirection): T; inline;
+    function Extract(const Value: T): T; inline;
+    function ExtractAt(Index: Integer): T; inline;
+    procedure Exchange(Index1, Index2: Integer); inline;
+    procedure Move(CurIndex, NewIndex: Integer); inline;
+    function First: T; inline;
+    function Last: T; inline;
+    procedure Clear; inline;
+    function Contains(const Value: T): Boolean; inline;
+    function IndexOf(const Value: T): Integer; inline;
+    function IndexOfItem(const Value: T; Direction: TDirection): Integer; inline;
+    function LastIndexOf(const Value: T): Integer; inline;
+    procedure Reverse; inline;
+    procedure Sort; overload; inline;
+    procedure Sort(const AComparer: IComparer<T>); overload; inline;
+    function BinarySearch(const Item: T; out Index: Integer): Boolean; overload; inline;
+    function BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean; overload; inline;
+    procedure TrimExcess; inline;
+    function ToArray: TArray<T>; inline;
+    property Capacity: Integer read GetCapacity write SetCapacity;
+    property Count: Integer read GetCount write SetCount;
+    property Items[Index: Integer]: T read GetItem write SetItem; default;
+    property List: arrayofT read GetList;
+    procedure FromList(const aList : TList<T>);
+    function ToList : TList<T>;
+    function Any : Boolean; overload; virtual;
+    function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
+    function Where(const aWhereClause: string): ILinqQuery<T>; overload;
+    function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
+    function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
+    {$IFNDEF FPC}
+    function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
+    {$ENDIF}
+  end;
+
+  TXObjectList<T : class> = class(TXList<T>,IObjectList<T>)
+  private
+    fOwnsObjects : Boolean;
+    procedure InternalOnNotify(Sender: TObject; const Item: T; Action: TCollectionNotification);
+  public
+    constructor Create(aOwnedObjects : Boolean = True);
+    destructor Destroy; override;
+    function Any(const aWhereClause : string; aValues : array of const) : Boolean; overload;
+    function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
+    function Where(const aWhereClause: string): ILinqQuery<T>; overload;
+    function Where(aPredicate : TPredicate<T>): ILinqQuery<T>; overload;
+  end;
+
+  ECollectionError = class(Exception);
+  ECollectionNotSupported = class(Exception);
+
+implementation
+
+
+
+{ TXList<T> }
+
+constructor TXList<T>.Create;
+begin
+  fList := TList<T>.Create;
+end;
+
+destructor TXList<T>.Destroy;
+begin
+  fList.Free;
+  inherited;
+end;
+
+function TXList<T>.Add(const Value: T): Integer;
+begin
+  Result := fList.Add(Value);
+end;
+
+procedure TXList<T>.AddRange(const Values: array of T);
+begin
+  fList.AddRange(Values);
+end;
+
+procedure TXList<T>.AddRange(const Collection: IEnumerable<T>);
+begin
+  fList.AddRange(Collection);
+end;
+
+procedure TXList<T>.AddRange(const Collection: TEnumerable<T>);
+begin
+  fList.AddRange(Collection);
+end;
+
+function TXList<T>.Any(const aMatchString: string; aUseRegEx: Boolean): Boolean;
+begin
+  Result := Where(aMatchString,aUseRegEx).Any;
+end;
+
+function TXList<T>.Any: Boolean;
+begin
+  Result := fList.Count > 0;
+end;
+
+function TXList<T>.BinarySearch(const Item: T; out Index: Integer): Boolean;
+begin
+  Result := fList.BinarySearch(Item,Index);
+end;
+
+function TXList<T>.BinarySearch(const Item: T; out Index: Integer; const AComparer: IComparer<T>): Boolean;
+begin
+  Result := fList.BinarySearch(Item,Index,AComparer);
+end;
+
+procedure TXList<T>.Clear;
+begin
+  fList.Clear;
+end;
+
+function TXList<T>.Contains(const Value: T): Boolean;
+begin
+  Result := fList.Contains(Value);
+end;
+
+procedure TXList<T>.Delete(Index: Integer);
+begin
+  fList.Delete(Index);
+end;
+
+procedure TXList<T>.DeleteRange(AIndex, ACount: Integer);
+begin
+  fList.DeleteRange(aIndex,aCount);
+end;
+
+procedure TXList<T>.Exchange(Index1, Index2: Integer);
+begin
+  fList.Exchange(Index1,Index2);
+end;
+
+function TXList<T>.Extract(const Value: T): T;
+begin
+  Result := fList.Extract(Value);
+end;
+
+function TXList<T>.ExtractAt(Index: Integer): T;
+begin
+  Result := fList.ExtractAt(Index);
+end;
+
+function TXList<T>.ExtractItem(const Value: T; Direction: TDirection): T;
+begin
+  Result := fList.ExtractItem(Value,Direction);
+end;
+
+function TXList<T>.First: T;
+begin
+  Result := fList.First;
+end;
+
+procedure TXList<T>.FromList(const aList: TList<T>);
+var
+  value : T;
+begin
+  for value in aList do fList.Add(value);
+end;
+
+function TXList<T>.GetCapacity: Integer;
+begin
+  Result := fList.Capacity;
+end;
+
+function TXList<T>.GetCount: Integer;
+begin
+  Result := fList.Count;
+end;
+
+function TXList<T>.GetEnumerator: TEnumerator<T>;
+begin
+  Result := fList.GetEnumerator;
+end;
+
+function TXList<T>.GetItem(Index: Integer): T;
+begin
+  Result := fList.Items[Index];
+end;
+
+function TXList<T>.GetList: TArray<T>;
+begin
+  Result := fList.ToArray;
+end;
+
+function TXList<T>.IndexOf(const Value: T): Integer;
+begin
+  Result := fList.IndexOf(Value);
+end;
+
+function TXList<T>.IndexOfItem(const Value: T; Direction: TDirection): Integer;
+begin
+  Result := fList.IndexOfItem(Value,Direction);
+end;
+
+procedure TXList<T>.Insert(Index: Integer; const Value: T);
+begin
+  fList.Insert(Index,Value);
+end;
+
+procedure TXList<T>.InsertRange(Index: Integer; const Collection: IEnumerable<T>);
+begin
+  fList.InsertRange(Index,Collection);
+end;
+
+procedure TXList<T>.InsertRange(Index: Integer; const Collection: TEnumerable<T>);
+begin
+  fList.InsertRange(index,Collection);
+end;
+
+procedure TXList<T>.InsertRange(Index: Integer; const Values: array of T; Count: Integer);
+begin
+  fList.InsertRange(Index,Values,Count);
+end;
+
+procedure TXList<T>.InsertRange(Index: Integer; const Values: array of T);
+begin
+  fList.InsertRange(index,Values);
+end;
+
+function TXList<T>.Last: T;
+begin
+  Result := fList.Last;
+end;
+
+function TXList<T>.LastIndexOf(const Value: T): Integer;
+begin
+  Result := fList.LastIndexOf(Value);
+end;
+
+procedure TXList<T>.Move(CurIndex, NewIndex: Integer);
+begin
+  fList.Move(CurIndex,NewIndex);
+end;
+
+function TXList<T>.Remove(const Value: T): Integer;
+begin
+  Result := fList.Remove(Value);
+end;
+
+function TXList<T>.RemoveItem(const Value: T; Direction: TDirection): Integer;
+begin
+  Result := fList.RemoveItem(Value,Direction);
+end;
+
+procedure TXList<T>.Reverse;
+begin
+  fList.Reverse;
+end;
+
+procedure TXList<T>.SetCapacity(Value: Integer);
+begin
+  fList.Capacity := Value;
+end;
+
+procedure TXList<T>.SetCount(Value: Integer);
+begin
+  fList.Count := Value;
+end;
+
+procedure TXList<T>.SetItem(Index: Integer; const Value: T);
+begin
+  fList.Items[Index] := Value;
+end;
+
+procedure TXList<T>.Sort(const AComparer: IComparer<T>);
+begin
+  fList.Sort(AComparer);
+end;
+
+procedure TXList<T>.Sort;
+begin
+  fList.Sort;
+end;
+
+function TXList<T>.ToArray: TArray<T>;
+begin
+  Result := fList.ToArray;
+end;
+
+function TXList<T>.ToList: TList<T>;
+var
+  value : T;
+begin
+  Result := TList<T>.Create;
+  for value in fList do Result.Add(value);    
+end;
+
+procedure TXList<T>.TrimExcess;
+begin
+  fList.TrimExcess;
+end;
+
+function TXList<T>.Where(const aMatchString: string; aUseRegEx: Boolean): ILinqArray<T>;
+begin
+  Result := TLinqArray<T>.Create(fList.ToArray);
+  Result.Where(aMatchString, aUseRegEx);
+end;
+
+function TXList<T>.Where(const aWhereClause: string; aWhereValues: array of const): ILinqQuery<T>;
+begin
+  if PTypeInfo(typeInfo(T)).Kind <> tkClass then raise ECollectionNotSupported.Create('TXList<T>.Where only supports classes. Use MatchString overload method instead!');
+  Result := TLinqQuery<TObject>.Create(TObjectList<TObject>(Self.fList)).Where(aWhereClause,aWhereValues) as ILinqQuery<T>;
+end;
+
+function TXList<T>.Where(const aWhereClause: string): ILinqQuery<T>;
+begin
+  if PTypeInfo(typeInfo(T)).Kind <> tkClass then raise ECollectionNotSupported.Create('TXList<T>.Where only supports classes. Use MatchString overload method instead!');
+  Result := TLinqQuery<TObject>.Create(TObjectList<TObject>(Self.fList)).Where(aWhereClause) as ILinqQuery<T>;
+end;
+
+function TXList<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
+begin
+  if PTypeInfo(typeInfo(T)).Kind <> tkClass then raise ECollectionNotSupported.Create('TXList<T>.Where only supports classes. Use MatchString overload method instead!');
+  Result := TLinqQuery<TObject>.Create(TObjectList<TObject>(Self.fList)).Where(TPredicate<TObject>(aPredicate)) as ILinqQuery<T>;
+end;
+
+{ TXObjectList<T> }
+
+function TXObjectList<T>.Any(const aWhereClause: string; aValues: array of const): Boolean;
+var
+  query : ILinqQuery<T>;
+begin
+  query := TLinqQuery<T>.Create(Self.fList);
+  Result := query.Where(aWhereClause,aValues).Count > 0;
+end;
+
+constructor TXObjectList<T>.Create(aOwnedObjects: Boolean);
+begin
+  inherited Create;
+  fOwnsObjects := aOwnedObjects;
+  fList.OnNotify := InternalOnNotify;
+end;
+
+destructor TXObjectList<T>.Destroy;
+begin
+
+  inherited;
+end;
+
+procedure TXObjectList<T>.InternalOnNotify(Sender: TObject; const Item: T; Action: TCollectionNotification);
+begin
+  if (fOwnsObjects) and (Action = TCollectionNotification.cnRemoved) then
+  begin
+    if Assigned(Item) then Item.DisposeOf;
+    //if PTypeInfo(typeInfo(T)).Kind = tkClass then
+    //PObject(@Item).DisposeOf;
+  end;
+end;
+
+function TXObjectList<T>.Where(const aWhereClause: string): ILinqQuery<T>;
+begin
+    Result := TLinqQuery<T>.Create(Self.fList).Where(aWhereClause);
+end;
+
+function TXObjectList<T>.Where(const aWhereClause: string; aWhereValues: array of const): ILinqQuery<T>;
+begin
+  Result := TLinqQuery<T>.Create(Self.fList).Where(aWhereClause,aWhereValues);
+end;
+
+function TXObjectList<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
+begin
+  Result := TLinqQuery<T>.Create(Self.fList).Where(aPredicate);
+end;
+
+end.

+ 20 - 13
Quick.Commons.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.9
   Created     : 14/07/2017
-  Modified    : 26/02/2020
+  Modified    : 14/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -172,6 +172,8 @@ type
   {$IFNDEF FPC}
   TArrayOfStringHelper = record helper for TArray<string>
   public
+    function Any : Boolean; overload;
+    function Any(const aValue : string) : Boolean; overload;
     function Add(const aValue : string) : Integer;
     function AddIfNotExists(const aValue : string; aCaseSense : Boolean = False) : Integer;
     function Remove(const aValue : string) : Boolean;
@@ -1568,6 +1570,16 @@ end;
 { TArrayOfStringHelper}
 
 {$IFNDEF FPC}
+function TArrayOfStringHelper.Any : Boolean;
+begin
+  Result := High(Self) >= 0;
+end;
+
+function TArrayOfStringHelper.Any(const aValue : string) : Boolean;
+begin
+  Result := Exists(aValue);
+end;
+
 function TArrayOfStringHelper.Add(const aValue : string) : Integer;
 begin
   SetLength(Self,Length(Self)+1);
@@ -1578,25 +1590,20 @@ end;
 function TArrayOfStringHelper.AddIfNotExists(const aValue : string; aCaseSense : Boolean = False) : Integer;
 var
   i : Integer;
-  found : Boolean;
 begin
-  found := False;
   for i := Low(Self) to High(Self) do
   begin
-    if aCaseSense then found := Self[i] = aValue
+    if aCaseSense then
+    begin
+      if Self[i] = aValue then Exit(i);
+    end
     else
     begin
-      found := CompareText(Self[i],aValue) = 0;
-      Exit(i)
+      if CompareText(Self[i],aValue) = 0 then Exit(i)
     end;
   end;
-  if not found then
-  begin
-    //if not exists add it
-    i := Self.Add(aValue);
-    Exit(i);
-  end;
-  Result := -1;
+  //if not exists add it
+  Result := Self.Add(aValue);
 end;
 
 function TArrayOfStringHelper.Remove(const aValue : string) : Boolean;

+ 170 - 33
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,15 +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;
 
@@ -80,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;
@@ -94,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);
@@ -199,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;
@@ -230,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;
@@ -244,44 +252,168 @@ 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);
     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;
 
+//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
   ctx : TRttiContext;
@@ -321,6 +453,7 @@ begin
     raise EExpressionValidateError.Create('Interface property not supported');
   end;
 end;
+{$ENDIF}
 
 function TSingleExpression.ArrayContains(aArray : TValue; const aValue : string): Boolean;
 var
@@ -335,8 +468,12 @@ begin
     Dec(count);
     arrItem := aArray.GetArrayElement(count);
     case arrItem.Kind of
-      tkString, tkUnicodeString, tkWideString : Result := CompareText(arrItem.AsString,aValue) = 0;
-      tkInteger, tkInt64 : Result := arrItem.AsInt64 = aValue.ToInt64;
+      {$IFNDEF FPC}
+      tkString,
+      {$ENDIF}
+      tkUnicodeString, tkWideString : Result := CompareText(arrItem.AsString,aValue) = 0;
+      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;
@@ -364,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

+ 1 - 1
Quick.HttpServer.Types.pas

@@ -550,7 +550,7 @@ end;
 constructor EControlledException.Create(aCaller: TObject; aMessage: string);
 begin
   inherited Create(aMessage);
-  fCallerClass := aCaller.ClassType;
+  if aCaller <> nil then fCallerClass := aCaller.ClassType;
 end;
 
 initialization

+ 1 - 1
Quick.HttpServer.pas

@@ -218,7 +218,7 @@ begin
   begin
     if not StrInArray(aRequestInfo.RawHeaders.Names[i],['Host','Accept-Encoding','Accept','User-Agent','Connection','Cache-Control']) then
     begin
-      Result.Headers.Add(aRequestInfo.RawHeaders.Names[i],aRequestInfo.RawHeaders.ValueFromIndex[i]);
+      Result.Headers.Add(aRequestInfo.RawHeaders.Names[i],aRequestInfo.RawHeaders.Values[aRequestInfo.RawHeaders.Names[i]]);
     end;
   end;
 end;

+ 12 - 2
Quick.IOC.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.0
   Created     : 19/10/2019
-  Modified    : 03/03/2020
+  Modified    : 10/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -168,7 +168,8 @@ type
     function RegisterInstance<T : class>(const aName: string = ''): TIocRegistration<T>; overload;
     function RegisterInstance(aTypeInfo : PTypeInfo; const aName : string = '') : TIocRegistration; overload;
     function RegisterInstance<TInterface : IInterface>(aInstance : TInterface; const aName : string = '') : TIocRegistration; overload;
-    function RegisterOptions<T : TOptions>(aOptions : TOptions) : TIocRegistration<T>;
+    function RegisterOptions<T : TOptions>(aOptions : TOptions) : TIocRegistration<T>; overload;
+    function RegisterOptions<T : TOptions>(aOptions : TConfigureOptionsProc<T>) : TIocRegistration<T>; overload;
     function Resolve<T>(const aName : string = ''): T; overload;
     function Resolve(aServiceType: PTypeInfo; const aName : string = ''): TValue; overload;
     function AbstractFactory<T : class, constructor>(aClass : TClass) : T; overload;
@@ -330,6 +331,15 @@ begin
   Result := fRegistrator.RegisterOptions<T>(aOptions).AsSingleton;
 end;
 
+function TIocContainer.RegisterOptions<T>(aOptions: TConfigureOptionsProc<T>): TIocRegistration<T>;
+var
+  options : T;
+begin
+  options := T.Create;
+  aOptions(options);
+  Result := Self.RegisterOptions<T>(options);
+end;
+
 function TIocContainer.Resolve(aServiceType: PTypeInfo; const aName: string): TValue;
 begin
   Result := fResolver.Resolve(aServiceType,aName);

+ 2 - 1
Quick.Json.Serializer.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.11
   Created     : 21/05/2018
-  Modified    : 10/02/2020
+  Modified    : 12/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -942,6 +942,7 @@ var
 begin
   Result := nil;
   pinfo := GetPropInfo(Instance,PropertyName);
+  if pinfo = nil then raise EJsonSerializeError.CreateFmt('Property "%s" not found!',[PropertyName]);
   case pinfo.PropType^.Kind of
     tkInteger : Result := GetOrdProp(Instance,pinfo);
     tkInt64 : Result := GetInt64Prop(Instance,PropertyName);

+ 339 - 5
Quick.Linq.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.0
   Created     : 04/04/2019
-  Modified    : 01/12/2019
+  Modified    : 22/03/20120
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -44,6 +44,8 @@ uses
   Quick.Value,
   {$IFDEF FPC}
   Quick.Value.RTTI,
+  {$ELSE}
+  System.RegularExpressions,
   {$ENDIF}
   Quick.Arrays;
 
@@ -51,19 +53,83 @@ type
 
   TOrderDirection = (odAscending, odDescending);
 
+  {$IFNDEF FPC}
   TLinqExpression<T : class> = class(TExpression)
   private
     fPredicate : TPredicate<T>;
   public
     constructor Create(aPredicate : TPredicate<T>);
-    function Validate(aValue : TObject) : Boolean; override;
+    function Validate(const aValue : TValue) : Boolean; override;
+    function IsNull : Boolean; override;
+  end;
+  {$ENDIF}
+
+  ILinqArray<T> = interface
+  ['{3133DCAB-06C5-434B-B169-B32DC8C6308B}']
+    function Any : Boolean; overload;
+    function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
+    function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
+    function OrderAscending : ILinqArray<T>;
+    function OrderDescending : ILinqArray<T>;
+    function SelectFirst : T;
+    function SelectLast : T;
+    function SelectTop(aLimit : Integer) : TArray<T>;
+    function Select : TArray<T>; overload;
+    function Count : Integer;
+    function Update(const aNewValue : T) : Integer;
+    function Delete : Integer;
+  end;
+
+  TLinqArray<T> = class(TInterfacedObject,ILinqArray<T>)
+  type
+    TLinqComparer = class(TInterfacedObject,IComparer<T>)
+    private
+      fSortAscending : Boolean;
+    public
+      constructor Create(aSortAscending : Boolean);
+      property SortAscending : Boolean read fSortAscending;
+      function Compare(const L, R: T): Integer;
+    end;
+  private
+    fArray : TArray<T>;
+    fMatchString : string;
+    fUseRegEx : Boolean;
+    function Validate(aValue : T) : Boolean;
+  public
+    constructor Create(aArray : TArray<T>);
+    function Any : Boolean; overload;
+    function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
+    function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
+    function OrderAscending : ILinqArray<T>;
+    function OrderDescending : ILinqArray<T>;
+    function SelectFirst : T;
+    function SelectLast : T;
+    function SelectTop(aLimit : Integer) : TArray<T>;
+    function Select : TArray<T>; overload;
+    function Count : Integer;
+    function Update(const aNewValue : T) : Integer;
+    function Delete : Integer;
   end;
 
+  {$IFNDEF FPC}
+  TLinqArrayHelper = record helper for TArray<string>
+    function Add(const aValue : string) : Integer;
+    function AddIfNotExists(const aValue : string) : Integer;
+    function Remove(const aValue : string) : Boolean;
+    function Any : Boolean; overload;
+    function Any(const aValue : string) : Boolean; overload;
+    function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
+    function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>; overload;
+  end;
+  {$ENDIF}
+
   ILinqQuery<T> = interface
   ['{16B68C0B-EA38-488A-99D9-BAD1E8560E8E}']
     function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
     function Where(const aWhereClause: string): ILinqQuery<T>; overload;
+    {$IFNDEF FPC}
     function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
+    {$ENDIF}
     function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
     function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
     function SelectFirst : T;
@@ -99,7 +165,9 @@ type
     destructor Destroy; override;
     function Where(const aWhereClause : string; aWhereParams : array of const) : ILinqQuery<T>; overload;
     function Where(const aWhereClause: string): ILinqQuery<T>; overload;
+    {$IFNDEF FPC}
     function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
+    {$ENDIF}
     function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
     function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
     function SelectFirst : T;
@@ -445,11 +513,13 @@ begin
   end;
 end;
 
+{$IFNDEF FPC}
 function TLinqQuery<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
 begin
   Result := Self;
   fWhereClause := TLinqExpression<T>.Create(aPredicate);
 end;
+{$ENDIF}
 
 { TLinq }
 
@@ -470,17 +540,281 @@ begin
   Result := TLinqQuery<T>.Create(aXArray);
 end;
 
-
 { TLinqExpression<T> }
 
+{$IFNDEF FPC}
 constructor TLinqExpression<T>.Create(aPredicate: TPredicate<T>);
 begin
   fPredicate := aPredicate;
 end;
 
-function TLinqExpression<T>.Validate(aValue : TObject) : Boolean;
+function TLinqExpression<T>.Validate(const aValue : TValue) : Boolean;
+begin
+  Result := fPredicate(aValue.AsType<T>);
+end;
+
+function TLinqExpression<T>.IsNull : Boolean;
+begin
+  Result := not Assigned(fPredicate);
+end;
+{$ENDIF}
+
+{ TLinqArray<T> }
+
+function TLinqArray<T>.Any : Boolean;
+begin
+  Result := High(fArray) >= 0;
+end;
+
+function TLinqArray<T>.Any(const aMatchString: string; aUseRegEx: Boolean): Boolean;
+begin
+  fMatchString := aMatchString;
+  fUseRegEx := aUseRegEx;
+end;
+
+function TLinqArray<T>.Count: Integer;
+begin
+  Result := High(fArray) + 1;
+end;
+
+constructor TLinqArray<T>.Create(aArray: TArray<T>);
+begin
+  fArray := aArray;
+end;
+
+function TLinqArray<T>.Delete: Integer;
+var
+  i : Integer;
+begin
+  Result := 0;
+  if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
+  for i := High(fArray) downto Low(fArray) do
+  begin
+    if Validate(fArray[i]) then
+    begin
+      //TObject(fArray[i]).Free;
+      System.Delete(fArray,i,1);
+      Inc(Result);
+    end;
+  end;
+end;
+
+function TLinqArray<T>.OrderAscending: ILinqArray<T>;
+var
+  comparer : IComparer<T>;
+begin
+  comparer := TLinqComparer.Create(True);
+  TArray.Sort<T>(fArray,comparer);
+end;
+
+function TLinqArray<T>.OrderDescending: ILinqArray<T>;
+var
+  comparer : IComparer<T>;
+begin
+  comparer := TLinqComparer.Create(False);
+  TArray.Sort<T>(fArray,comparer);
+end;
+
+function TLinqArray<T>.Select: TArray<T>;
+var
+  value : T;
+begin
+  //DoOrderBy(fList);
+  if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
+  for value in fArray do
+  begin
+    if Validate(value) then Result := Result + [value];
+  end;
+end;
+
+function TLinqArray<T>.SelectFirst: T;
+var
+  value : T;
+begin
+  //DoOrderBy(fList);
+  if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
+  for value in fArray do
+  begin
+    if Validate(value) then Exit(value);
+  end;
+end;
+
+function TLinqArray<T>.SelectLast: T;
+var
+  value : T;
+  found : T;
+begin
+  //DoOrderBy(fList);
+  if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
+  for value in fArray do
+  begin
+    if Validate(value) then found := value;
+  end;
+  Result := found;
+end;
+
+function TLinqArray<T>.SelectTop(aLimit: Integer): TArray<T>;
+var
+  i : Integer;
+  limit : Integer;
+begin
+  if aLimit > High(fArray) then limit := High(fArray)
+    else limit := aLimit;
+  SetLength(Result,limit);
+  for i := Low(fArray) to limit do
+  begin
+    Result[i] := fArray[i];
+  end;
+end;
+
+function TLinqArray<T>.Update(const aNewValue: T): Integer;
+var
+  i : Integer;
+begin
+  for i := Low(fArray) to High(fArray) do
+  begin
+    if Validate(fArray[i]) then fArray[i] := aNewValue;
+  end;
+end;
+
+function TLinqArray<T>.Validate(aValue: T): Boolean;
+var
+  regEx : TRegEx;
+  value : TValue;
+begin
+  value := TValue.From<T>(aValue);
+  if fUseRegEx then
+  begin
+    regEx := TRegEx.Create(fMatchString,[roIgnoreCase,roMultiline]);
+    try
+      Result := regEx.IsMatch(value.AsString);
+    except
+      raise Exception.Create('TLinqArray not valid RegEx!');
+    end;
+  end
+  else
+  begin
+    Result := CompareText(fMatchString,value.AsString) = 0;
+  end;
+end;
+
+function TLinqArray<T>.Where(const aMatchString: string; aUseRegEx: Boolean): ILinqArray<T>;
 begin
-  Result := fPredicate(aValue as T);
+  Result := Self;
+  fMatchString := aMatchString;
+  fUseRegEx := aUseRegEx;
+end;
+
+{ TLinqArray<T>.TLinqComparer }
+
+constructor TLinqArray<T>.TLinqComparer.Create(aSortAscending : Boolean);
+begin
+  fSortAscending := aSortAscending;
 end;
 
+function TLinqArray<T>.TLinqComparer.Compare(const L, R: T): Integer;
+var
+  valueL : TValue;
+  valueR : TValue;
+  hr : Integer;
+  lr : Integer;
+begin
+  Result := 0;
+  if fSortAscending then
+  begin
+    hr := 1;
+    lr := -1;
+  end
+  else
+  begin
+    hr := -1;
+    lr := 1;
+  end;
+  valueL := TValue.From<T>(L);
+  valueR := TValue.From<T>(R);
+  if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(hr)
+    else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(lr);
+
+  case valueL.Kind of
+    {$IFNDEF FPC}
+    tkString,
+    {$ENDIF}
+    tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
+    tkInteger, tkInt64 :
+      begin
+        if valueL.AsInteger > valueR.AsInteger then Result := hr
+          else if valueL.AsInteger < valueR.AsInteger then Result := lr;
+      end;
+    tkFloat :
+      begin
+        if valueL.AsExtended > valueR.AsExtended then Result := hr
+          else if valueL.AsExtended < valueR.AsExtended then Result := lr;
+      end;
+    end;
+end;
+
+{ TLinqArrayHelper }
+
+{$IFNDEF FPC}
+function TLinqArrayHelper.Add(const aValue : string) : Integer;
+begin
+  SetLength(Self,Length(Self)+1);
+  Self[High(Self)] := aValue;
+  Result := High(Self);
+end;
+
+function TLinqArrayHelper.AddIfNotExists(const aValue : string) : Integer;
+var
+  i : Integer;
+begin
+  for i := Low(Self) to High(Self) do
+  begin
+    if CompareText(Self[i],aValue) = 0 then Exit(i);
+  end;
+  //if not exists add it
+  Result := Self.Add(aValue);
+end;
+
+function TLinqArrayHelper.Remove(const aValue : string) : Boolean;
+var
+  i : Integer;
+begin
+  for i := Low(Self) to High(Self) do
+  begin
+    if CompareText(Self[i],aValue) = 0 then
+    begin
+      System.Delete(Self,i,1);
+      Exit(True);
+    end;
+  end;
+  Result := False;
+end;
+
+function TLinqArrayHelper.Any : Boolean;
+begin
+  Result := High(Self) >= 0;
+end;
+
+function TLinqArrayHelper.Any(const aValue : string) : Boolean;
+var
+  value : string;
+begin
+  Result := False;
+  for value in Self do
+  begin
+    if CompareText(value,aValue) = 0 then Exit(True)
+  end;
+end;
+
+function TLinqArrayHelper.Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean;
+begin
+  Result := TLinqArray<string>.Create(Self).Any(aMatchString,aUseRegEx);
+end;
+
+function TLinqArrayHelper.Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>;
+begin
+  Result := TLinqArray<string>.Create(Self).Where(aMatchString,aUseRegEx);
+end;
+{$ENDIF}
+
 end.

+ 4 - 4
Quick.Lists.pas

@@ -1,13 +1,13 @@
 { ***************************************************************************
 
-  Copyright (c) 2016-2019 Kike Pérez
+  Copyright (c) 2016-2020 Kike Pérez
 
   Unit        : Quick.Lists
   Description : Generic Lists functions
   Author      : Kike Pérez
   Version     : 1.2
   Created     : 04/11/2018
-  Modified    : 11/05/2019
+  Modified    : 12/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -257,11 +257,11 @@ begin
   begin
     if aClassField = TClassField.cfField then
     begin
-      if TRTTI.GetFieldValue(TObject(val),aFieldName).AsString = aValue then Exit(val);
+      if (val <> nil) and (TRTTI.GetFieldValue(TObject(val),aFieldName).AsString = aValue) then Exit(val);
     end
     else
     begin
-      if GetStrProp(TObject(val),aFieldName) = aValue then Exit(val);
+      if (val <> nil) and (GetStrProp(TObject(val),aFieldName) = aValue) then Exit(val);
     end;
   end;
 end;

+ 26 - 35
Quick.Options.pas

@@ -425,6 +425,7 @@ begin
       fSerializer.Save(fFilename,fSections);
     finally
       //set last state
+      Sleep(0);
       fFileMonitor.Enabled := laststate;
     end;
   end
@@ -494,33 +495,28 @@ var
   attrib : TCustomAttribute;
   rvalue : TValue;
 begin
-  ctx := TRttiContext.Create;
-  try
-    rtype := ctx.GetType(aObj.ClassInfo);
-    for rprop in rtype.GetProperties do
+  rtype := ctx.GetType(aObj.ClassInfo);
+  for rprop in rtype.GetProperties do
+  begin
+    //check only published properties
+    if rprop.Visibility = TMemberVisibility.mvPublished then
     begin
-      //check only published properties
-      if rprop.Visibility = TMemberVisibility.mvPublished then
+      //check validation option attributes
+      for attrib in rprop.GetAttributes do
       begin
-        //check validation option attributes
-        for attrib in rprop.GetAttributes do
-        begin
-          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;
+        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;
-  finally
-    ctx.Free;
   end;
 end;
 
@@ -543,22 +539,17 @@ var
   itvalue : TValue;
   i : Integer;
 begin
-  ctx := TRttiContext.Create;
-  try
-    rDynArray := ctx.GetType(aValue.TypeInfo) as TRTTIDynamicArrayType;
-    for i := 0 to aValue.GetArrayLength - 1 do
+  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
-      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;
+      case itvalue.Kind of
+        tkClass : ValidateObject(itvalue.AsObject);
+        tkDynArray : ValidateArray(itvalue);
       end;
     end;
-  finally
-    ctx.Free;
   end;
 end;
 

+ 296 - 0
Quick.Pooling.pas

@@ -0,0 +1,296 @@
+{ ***************************************************************************
+
+  Copyright (c) 2016-2020 Kike Pérez
+
+  Unit        : Quick.Pooling
+  Description : Pooling objects
+  Author      : Kike Pérez
+  Version     : 1.9
+  Created     : 28/02/2020
+  Modified    : 29/02/2020
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Pooling;
+
+{$i QuickLib.inc}
+
+interface
+
+uses
+  System.SysUtils,
+  System.SyncObjs,
+  System.DateUtils,
+  Quick.Commons,
+  Quick.Threads;
+
+type
+
+  IPoolItem<T : class, constructor> = interface
+  ['{D52E794B-FDC1-42C1-94BA-823DB74703E4}']
+    function Item : T;
+    function GetRefCount : Integer;
+    function GetItemIndex : Integer;
+    function GetLastAccess: TDateTime;
+    property RefCount: Integer read GetRefCount;
+    property ItemIndex : Integer read GetItemIndex;
+    property LastAccess: TDateTime read GetLastAccess;
+  end;
+
+  TCreateDelegator<T> = reference to procedure(var aInstance : T);
+
+  TPoolItem<T : class, constructor> = class(TInterfacedObject,IPoolItem<T>)
+  private
+    fItem : T;
+    fItemIndex : Integer;
+    fLastAccess : TDateTime;
+    function GetRefCount : Integer;
+    function GetLastAccess: TDateTime;
+    function GetItemIndex : Integer;
+  protected
+    fLock : TCriticalSection;
+    fSemaphore : TSemaphore;
+    function _AddRef: Integer; stdcall;
+    function _Release: Integer; stdcall;
+  public
+    constructor Create(aSemaphore : TSemaphore; aLock : TCriticalSection; aItemIndex : Integer; aCreateProc : TCreateDelegator<T>);
+    destructor Destroy; override;
+    function Item : T;
+    property RefCount: Integer read GetRefCount;
+    property ItemIndex : Integer read GetItemIndex;
+    property LastAccess: TDateTime read GetLastAccess;
+  end;
+
+  IObjectPool<T : class, constructor> = interface
+  ['{AA856DFB-AE8C-46FE-A107-034677010A58}']
+    function GetPoolSize: Integer;
+    function Get : IPoolItem<T>;
+    property PoolSize : Integer read GetPoolSize;
+    function TimeoutMs(aTimeout : Integer) : IObjectPool<T>;
+    function CreateDelegate(aCreateProc : TCreateDelegator<T>) : IObjectPool<T>;
+    function AutoFreeIdleItemTimeMs(aIdleTimeMs : Integer) : IObjectPool<T>;
+  end;
+
+  TObjectPool<T : class, constructor> = class(TInterfacedObject,IObjectPool<T>)
+  private
+    fPool : TArray<IPoolItem<T>>;
+    fPoolSize : Integer;
+    fWaitTimeoutMs : Integer;
+    fLock : TCriticalSection;
+    fDelegate : TCreateDelegator<T>;
+    fSemaphore : TSemaphore;
+    fAutoFreeIdleItemTimeMs : Integer;
+    fScheduler : TScheduledTasks;
+    function GetPoolSize: Integer;
+    procedure CreateScheduler;
+    procedure CheckForIdleItems;
+  public
+    constructor Create(aPoolSize : Integer; aAutoFreeIdleItemTimeMs : Integer = 30000; aCreateProc : TCreateDelegator<T> = nil);
+    destructor Destroy; override;
+    property PoolSize : Integer read GetPoolSize;
+    function TimeoutMs(aTimeout : Integer) : IObjectPool<T>;
+    function CreateDelegate(aCreateProc : TCreateDelegator<T>) : IObjectPool<T>;
+    function AutoFreeIdleItemTimeMs(aIdleTimeMs : Integer) : IObjectPool<T>;
+    function Get : IPoolItem<T>;
+  end;
+
+implementation
+
+{ TObjectPool<T> }
+
+function TObjectPool<T>.AutoFreeIdleItemTimeMs(aIdleTimeMs: Integer): IObjectPool<T>;
+begin
+  fAutoFreeIdleItemTimeMs := aIdleTimeMs;
+end;
+
+constructor TObjectPool<T>.Create(aPoolSize : Integer; aAutoFreeIdleItemTimeMs : Integer = 30000; aCreateProc : TCreateDelegator<T> = nil);
+begin
+  fLock := TCriticalSection.Create;
+  fPoolSize := aPoolSize;
+  fWaitTimeoutMs := 30000;
+  fDelegate := aCreateProc;
+  fAutoFreeIdleItemTimeMs := aAutoFreeIdleItemTimeMs;
+  fSemaphore := TSemaphore.Create(nil,fPoolSize,fPoolSize,'');
+  CreateScheduler;
+end;
+
+procedure TObjectPool<T>.CreateScheduler;
+begin
+  fScheduler := TScheduledTasks.Create;
+  fScheduler.AddTask('IdleCleaner',[],True,procedure(task : ITask)
+      begin
+        CheckForIdleItems;
+      end)
+      .StartInSeconds(10).RepeatEvery(fAutoFreeIdleItemTimeMs,TTimeMeasure.tmMilliseconds);
+  fScheduler.Start;
+end;
+
+procedure TObjectPool<T>.CheckForIdleItems;
+var
+  i : Integer;
+begin
+  fLock.Enter;
+  try
+    for i := low(fPool) to High(fPool) do
+    begin
+      //check if item was not used for long time
+      if (fPool[i] <> nil) and (fPool[i].RefCount = 1) and (MilliSecondsBetween(Now,fPool[i].LastAccess) > fAutoFreeIdleItemTimeMs) then
+      begin
+        fPool[i] := nil;
+      end;
+    end;
+  finally
+    fLock.Leave;
+  end;
+end;
+
+function TObjectPool<T>.CreateDelegate(aCreateProc: TCreateDelegator<T>): IObjectPool<T>;
+begin
+  fDelegate := aCreateProc;
+end;
+
+destructor TObjectPool<T>.Destroy;
+  var
+  i: Integer;
+begin
+  fScheduler.Stop;
+  fScheduler.Free;
+  fLock.Enter;
+  try
+    for i := Low(fPool) to High(fPool) do fPool[i] := nil;
+    SetLength(FPool,0);
+  finally
+    fLock.Leave;
+  end;
+  fLock.Free;
+  fSemaphore.Free;
+  inherited;
+end;
+
+function TObjectPool<T>.Get: IPoolItem<T>;
+var
+  i : Integer;
+  waitResult: TWaitResult;
+begin
+  Result := nil;
+  waitResult := fSemaphore.WaitFor(fWaitTimeoutMs);
+  if waitResult <> TWaitResult.wrSignaled then raise Exception.Create('Connection Pool Timeout: Cannot obtain a connection');
+  fLock.Enter;
+  try
+    if High(fPool) < fPoolSize then SetLength(fPool,High(fPool)+2);
+    for i := Low(fPool) to High(fPool) do
+    begin
+      if fPool[i] = nil then
+      begin
+        fPool[i] := TPoolItem<T>.Create(fSemaphore,fLock,i,fDelegate);
+        //writeln('create ' + i.ToString);
+        Exit(fPool[i]);
+      end;
+      if fPool[i].RefCount = 1 then
+      begin
+        //writeln('get ' + i.ToString);
+        Exit(fPool[i]);
+      end;
+    end;
+  finally
+    fLock.Leave;
+  end;
+end;
+
+function TObjectPool<T>.GetPoolSize: Integer;
+begin
+  Result := fPoolSize;
+end;
+
+function TObjectPool<T>.TimeoutMs(aTimeout: Integer): IObjectPool<T>;
+begin
+  fWaitTimeoutMs := aTimeout;
+end;
+
+{ TPoolItem<T> }
+
+function TPoolItem<T>.Item: T;
+begin
+  fLastAccess := Now();
+  Result := fItem;
+end;
+
+constructor TPoolItem<T>.Create(aSemaphore : TSemaphore; aLock : TCriticalSection; aItemIndex : Integer; aCreateProc : TCreateDelegator<T>);
+begin
+  fLastAccess := Now();
+  fItemIndex := aItemIndex;
+  if Assigned(aCreateProc) then aCreateProc(fItem)
+    else fItem := T.Create;
+  fLock := aLock;
+  fSemaphore := aSemaphore;
+end;
+
+destructor TPoolItem<T>.Destroy;
+begin
+  if Assigned(fItem) then fItem.Free;
+  inherited;
+end;
+
+function TPoolItem<T>.GetItemIndex: Integer;
+begin
+  Result := fItemIndex;
+end;
+
+function TPoolItem<T>.GetLastAccess: TDateTime;
+begin
+  Result := fLastAccess;
+end;
+
+function TPoolItem<T>.GetRefCount: Integer;
+begin
+  Result := FRefCount;
+end;
+
+function TPoolItem<T>._AddRef: Integer;
+begin
+  fLock.Enter;
+  //writeln('enter');
+  try
+    Inc(FRefCount);
+    Result := FRefCount;
+  finally
+    fLock.Leave;
+  end;
+end;
+
+function TPoolItem<T>._Release: Integer;
+begin
+  fLock.Enter;
+  //writeln('exit');
+  try
+    Dec(fRefCount);
+    Result := fRefCount;
+    if Result = 0 then
+    begin
+      FreeAndNil(fItem);
+      Destroy;
+    end
+    else fLastAccess := Now;
+  finally
+    fLock.Leave;
+    if fRefCount = 1 then fSemaphore.Release;
+  end;
+end;
+
+end.

+ 139 - 20
Quick.RTTI.Utils.pas

@@ -1,13 +1,13 @@
 { ***************************************************************************
 
-  Copyright (c) 2016-2019 Kike Pérez
+  Copyright (c) 2016-2020 Kike Pérez
 
   Unit        : Quick.RTTI.Utils
   Description : Files functions
   Author      : Kike Pérez
   Version     : 1.4
   Created     : 09/03/2018
-  Modified    : 29/10/2019
+  Modified    : 12/03/2020
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -36,9 +36,7 @@ interface
 uses
   SysUtils,
   Quick.Commons,
-  {$IFDEF FPC}
   TypInfo,
-  {$ENDIF}
   Rtti;
 
 type
@@ -60,6 +58,9 @@ type
     class function GetProperty(aInstance : TObject; const aPropertyName : string) : TRttiProperty; overload;
     class function GetProperty(aTypeInfo : Pointer; const aPropertyName : string) : TRttiProperty; overload;
     class function GetPropertyPath(aInstance : TObject; const aPropertyPath : string) : TRttiProperty;
+    {$IFNDEF FPC}
+    class function GetMemberPath(aInstance: TObject; const aPropertyPath: string): TRttiMember;
+    {$ENDIF}
     class function PathExists(aInstance: TObject; const aPropertyPath: string) : Boolean;
     class function GetPathValue(aInstance : TObject; const aPropertyPath : string) : TValue;
     class procedure SetPathValue(aInstance: TObject; const aPropertyPath: string; aValue : TValue);
@@ -67,6 +68,7 @@ type
     class function PropertyExists(aTypeInfo : Pointer; const aPropertyName : string) : Boolean;
     class function GetPropertyValue(aInstance : TObject; const aPropertyName : string) : TValue; overload;
     class function GetPropertyValue(aTypeInfo : Pointer; const aPropertyName : string) : TValue; overload;
+    class function GetPropertyValueEx(aInstance: TObject; const aPropertyName: string): TValue;
     {$IFNDEF FPC}
     class function FindClass(const aClassName: string): TClass;
     class function CreateInstance<T>: T;
@@ -122,6 +124,7 @@ class function TRTTI.GetField(aInstance: TObject; const aFieldName: string): TRt
 var
   rtype : TRttiType;
 begin
+  Result := nil;
   rtype := fCtx.GetType(aInstance.ClassInfo);
   if rtype <> nil then
   begin
@@ -133,6 +136,7 @@ class function TRTTI.GetField(aTypeInfo: Pointer; const aFieldName: string): TRt
 var
   rtype : TRttiType;
 begin
+  Result := nil;
   rtype := fCtx.GetType(aTypeInfo);
   if rtype <> nil then
   begin
@@ -161,6 +165,7 @@ class function TRTTI.GetProperty(aInstance: TObject; const aPropertyName: string
 var
   rtype : TRttiType;
 begin
+  Result := nil;
   rtype := fCtx.GetType(aInstance.ClassInfo);
   if rtype <> nil then Result := rtype.GetProperty(aPropertyName);
 end;
@@ -169,6 +174,7 @@ class function TRTTI.GetProperty(aTypeInfo: Pointer; const aPropertyName: string
 var
   rtype : TRttiType;
 begin
+  Result := nil;
   rtype := fCtx.GetType(aTypeInfo);
   if rtype <> nil then  Result := rtype.GetProperty(aPropertyName);
 end;
@@ -184,9 +190,11 @@ var
   {$IFNDEF FPC}
   rfield : TRttiField;
   {$ENDIF}
+  lastsegment : Boolean;
 begin
   Result := nil;
   proppath := aPropertyPath;
+  lastsegment := False;
   rtype := fCtx.GetType(aInstance.ClassType);
   repeat
     i := proppath.IndexOf('.');
@@ -195,7 +203,11 @@ begin
       propname := Copy(proppath,1,i);
       Delete(proppath,1,i+1);
     end
-    else propname := proppath;
+    else
+    begin
+      propname := proppath;
+      lastsegment := True;
+    end;
     if rtype.TypeKind = TTypeKind.tkRecord then
     begin
       {$IFNDEF FPC}
@@ -209,13 +221,76 @@ begin
     begin
       prop := rtype.GetProperty(propname);
       if prop = nil then Exit;
-      value := prop.GetValue(aInstance);
+      if lastsegment then Exit(prop)
+        else value := prop.GetValue(aInstance);
+    end;
+    if not lastsegment then
+    begin
+      if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
+        else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
+    end;
+  until lastsegment;
+  Result := nil;
+end;
+
+{$IFNDEF FPC}
+class function TRTTI.GetMemberPath(aInstance: TObject; const aPropertyPath: string): TRttiMember;
+var
+  prop : TRttiProperty;
+  proppath : string;
+  propname : string;
+  i : Integer;
+  value : TValue;
+  rtype : TRttiType;
+  {$IFNDEF FPC}
+  rfield : TRttiField;
+  {$ENDIF}
+  lastsegment : Boolean;
+begin
+  Result := nil;
+  proppath := aPropertyPath;
+  lastsegment := False;
+  rtype := fCtx.GetType(aInstance.ClassType);
+  repeat
+    i := proppath.IndexOf('.');
+    if i > -1 then
+    begin
+      propname := Copy(proppath,1,i);
+      Delete(proppath,1,i+1);
+    end
+    else
+    begin
+      propname := proppath;
+      lastsegment := True;
+    end;
+    if rtype.TypeKind = TTypeKind.tkRecord then
+    begin
+      {$IFNDEF FPC}
+      rfield := rtype.GetField(propname);
+      if rfield <> nil then
+      begin
+        if lastsegment then Exit(rfield)
+          else value := rfield.GetValue(value.GetReferenceToRawData);
+      end;
+      {$ELSE}
+      raise ERTTIError.Create('FPC not supports record fields in RTTI');
+      {$ENDIF}
+    end
+    else
+    begin
+      prop := rtype.GetProperty(propname);
+      if prop = nil then Exit;
+      if lastsegment then Exit(prop)
+        else value := prop.GetValue(aInstance);
+    end;
+    if not lastsegment then
+    begin
+      if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
+        else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
     end;
-    if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
-      else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
-  until i < 0;
-  Result := prop;
+  until lastsegment;
 end;
+{$ENDIF}
 
 class function TRTTI.PathExists(aInstance: TObject; const aPropertyPath: string) : Boolean;
 var
@@ -230,7 +305,7 @@ var
   {$ENDIF}
   lastsegment : Boolean;
 begin
-  if not Assigned(aInstance) then Exit;
+  if not Assigned(aInstance) then Exit(False);
   lastsegment := False;
   proppath := aPropertyPath;
   rtype := fCtx.GetType(aInstance.ClassType);
@@ -327,15 +402,15 @@ begin
     begin
       rprop := rtype.GetProperty(propname);
       if rprop = nil then raise ERTTIError.CreateFmt('Property "%s" not found in object',[propname])
-        {$IFNDEF FPC}
-        else value := rprop.GetValue(aInstance);
-        {$ELSE}
-        else
-        begin
-          if rprop.PropertyType.IsInstance then value := GetObjectProp(value.AsObject,propname)
-             else value := rprop.GetValue(value.AsObject);
-        end;
-        {$ENDIF}
+      {$IFNDEF FPC}
+      else value := rprop.GetValue(aInstance);
+      {$ELSE}
+      else
+      begin
+        if rprop.PropertyType.IsInstance then value := GetObjectProp(value.AsObject,propname)
+           else value := rprop.GetValue(value.AsObject);
+      end;
+      {$ENDIF}
     end;
     if not lastsegment then
     begin
@@ -439,6 +514,49 @@ begin
   end;
 end;
 
+class function TRTTI.GetPropertyValueEx(aInstance: TObject; const aPropertyName: string): TValue;
+var
+  pinfo : PPropInfo;
+begin
+  Result := nil;
+  pinfo := GetPropInfo(aInstance,aPropertyName);
+  if pinfo = nil then
+  begin
+    //if not found can be a public property
+    Result := GetPropertyValue(aInstance,aPropertyName);
+    Exit;
+  end;
+  case pinfo.PropType^.Kind of
+    tkInteger : Result := GetOrdProp(aInstance,pinfo);
+    tkInt64 : Result := GetInt64Prop(aInstance,aPropertyName);
+    tkFloat : Result := GetFloatProp(aInstance,aPropertyName);
+    tkChar : Result := Char(GetOrdProp(aInstance,aPropertyName));
+    {$IFDEF FPC}
+    tkWString : Result := GetWideStrProp(aInstance,aPropertyName);
+    tkSString,
+    tkAString,
+    {$ELSE}
+    tkUString,
+    tkWString,
+    {$ENDIF}
+    tkLString : Result := GetStrProp(aInstance,pinfo);
+    {$IFDEF FPC}
+    tkEnumeration :Result  := GetOrdProp(aInstance,aPropertyName);
+    {$ELSE}
+    tkEnumeration : Result := GetOrdProp(aInstance,aPropertyName);
+    {$ENDIF}
+    tkSet : Result := GetSetProp(aInstance,pinfo,True);
+    {$IFNDEF FPC}
+    tkClass :
+    {$ELSE}
+    tkBool : Result := Boolean(GetOrdProp(aInstance,pinfo));
+    tkObject :
+    {$ENDIF} Result := GetObjectProp(aInstance,pinfo);
+    tkDynArray : Result := GetDynArrayProp(aInstance,pinfo);
+  end;
+end;
+
+
 class function TRTTI.GetType(aTypeInfo: Pointer): TRttiType;
 begin
   Result := fCtx.GetType(aTypeInfo);
@@ -448,6 +566,7 @@ class function TRTTI.PropertyExists(aTypeInfo: Pointer; const aPropertyName: str
 var
   rtype : TRttiType;
 begin
+  Result := False;
   rtype := fCtx.GetType(aTypeInfo);
   if rtype <> nil then Result := rtype.GetProperty(aPropertyName) <> nil;
 end;

+ 2 - 2
Quick.Value.RTTI.pas

@@ -91,9 +91,9 @@ begin
       dtInt64 : Result := AsInt64;
       {$IFNDEF FPC}
       dtVariant : Result := TValue.FromVariant(AsVariant);
+      dtInterface : Result := TValue.FromVariant(AsInterface);
       {$ENDIF}
       dtObject : Result := AsObject;
-      dtInterface : Result := TValue.FromVariant(AsInterface);
       dtArray : Result := (Self.Data as IValueTValue).Value;
       else raise Exception.Create('DataType not supported');
     end;
@@ -122,9 +122,9 @@ begin
     tkSet : AsInteger := Value.AsInteger;
     tkClass : AsObject := Value.AsObject;
     tkInterface : AsInterface := Value.AsInterface;
+    {$IFNDEF FPC}
     tkArray,
     tkDynArray : Self.SetAsCustom(TValueTValue.Create(Value),TValueDataType.dtArray);
-    {$IFNDEF FPC}
     else AsVariant := Value.AsVariant;
     {$ENDIF}
   end;

+ 4 - 0
Quick.Value.pas

@@ -244,7 +244,9 @@ type
     property Data : TValueData read fDataIntf;
     {$ENDIF}
     property DataType : TValueDataType read fDataType;
+    {$IFNDEF FPC}
     procedure SetAsCustom(aData : IInterface; aType : TValueDataType);
+    {$ENDIF}
     property AsString : string read CastToString write SetAsString;
     {$IFDEF MSWINDOWS}
     property AsAnsiString : AnsiString read CastToAnsiString write SetAsAnsiString;
@@ -978,11 +980,13 @@ begin
   fDataType := TValueDataType.dtClass;
 end;
 
+{$IFNDEF FPC}
 procedure TFlexValue.SetAsCustom(aData: IInterface; aType: TValueDataType);
 begin
   fDataIntf := aData;
   fDataType := aType;
 end;
+{$ENDIF}
 
 procedure TFlexValue.SetAsDateTime(const Value: TDateTime);
 begin

+ 1 - 1
Quick.YAML.Serializer.pas

@@ -637,11 +637,11 @@ begin
               Yaml.Free;
             end;
           end;
-        {$ENDIF}
         tkSet :
           begin
             rValue := DeserializeType(aObject,aProperty.PropertyType.TypeKind,aProperty.GetValue(aObject).TypeInfo,member.ToYaml)
           end
+        {$ENDIF}
       else
         begin
           {$IFNDEF FPC}

+ 61 - 1
README.md

@@ -50,10 +50,14 @@ Small delphi/Firemonkey(Windows, Linux, Android, OSX & IOS) and fpc(Windows & Li
 * **Quick.Expression:** Evaluate object properties using expressions.
 * **Quick.Linq:** Makes Linq queries to any TObjectList<T>, TList<T>, TArray<T> and TXArray<T>, performing Select by complex Where like SQL syntax, update and order over your list.
 * **Quick.MemoryCache:** Caches objects/info with an expiration time, to avoid generate this info everytime is needed (database queries, hard to calculate info, etc).
+* **Quick.Collections:** Collections improvements like IList and IObjectList with Linq inherited.
+* **Quick.Pooling:** Creation of object pool to avoid external resource consum exhausts and overheads.
 
 
 **Updates:**
 
+* NEW: Collections: IList and IObjectList with linQ support.
+* NEW: Pooling: ObjectPool.
 * NEW: Options file settings with sections.
 * NEW: MemoryCache with expiration & object compression.
 * NEW: Now included on RAD Studio GetIt package manager.
@@ -812,7 +816,8 @@ Serialize/Deserialize object from/to Yaml.
 
 **Quick.Linq:**
 --
-Makes Linq queries to any TObjectList<T>, TList<T>, TArray<T> and TXArray<T>, performing Select by complex Where like SQL syntax, update and order over your list.
+Makes Linq queries to any TObjectList<T>, TList<T>, TArray<T> and TXArray<T>, performing Select by complex Where like SQL syntax, update and order over your list. Where clauses uses namespaces to determine nested properties. LinQ can search for a element into a property array. 
+Now includes and TArray<string> helper to add, remove and search with regular expressions into array.
 - **From:** Array, XArray or TObjectList to use.
 - **Where:** Expression to search. You can use a dots to define property path.
 - **SelectAll:** Returns an array of objects matching where clause
@@ -1050,3 +1055,58 @@ Options.OnFileModified := procedure
     cout('Detected config file modification!',etWarning);
   end;
 ```
+
+**Quick.Collections:**
+ --
+ Define pool of connection, threads or any object you want to control to avoid resource consum like database connections, http clients, etc...
+
+Create http client pool:
+```delphi
+ pool := TObjectPool<THTTPClient>.Create(5,5000,procedure(var aInstance : THTTPClient)
+        begin
+          aInstance := THTTPClient.Create;
+          aInstante.UserAgent := 'MyAgent';
+        end);
+```
+Get object from pool:
+
+```delphi
+httpcli := pool.Get.Item;
+statuscode := httpcli.Get('https://www.mydomain.com').StatusCode;
+```
+
+**Quick.Collections:**
+ --
+Defines interfaced List and Objectlist with linQ support inherited.
+
+- TXList<T> / IList<T>: Interfaced List allowing LinQ regEx search/remove/update.
+
+```delphi
+myarray := ['Joe','Mat','Lee'];
+//search for regex match
+cout('Search for regex match',ccYellow);
+for name in myarray.Where('e$',True).Select do
+begin
+  cout('User %s ends with "e"',[name],etInfo);
+end;
+```
+
+- TXObjectList<T> / IObjectList<T>: Interfaced ObjectList allowing LinQ predicate or expression search/remove/update.
+Expression search:
+```delphi
+user := ListObj.Where('Profile.Name = ?',['Lee']).SelectFirst;
+```
+Expression search for item array:
+```delphi
+users := ListObj.Where('Roles CONTAINS ?',['SuperAdmin']).Select;
+```
+Predicate search:
+
+```delphi
+user := ListObj.Where(function(aUser : TUser) : Boolean
+      begin
+        Result := aUser.Name.StartsWith('J');
+      end).SelectFirst;
+    if user <> nil then cout('%s starts with J letter',[user.Name],etInfo);
+```
+See Quick.Linq section to view more functions allowed.

+ 112 - 0
samples/delphi/QuickCollections/InterfacedLists.dpr

@@ -0,0 +1,112 @@
+program InterfacedLists;
+
+{$APPTYPE CONSOLE}
+
+{$R *.res}
+
+uses
+  System.SysUtils,
+  Quick.Commons,
+  Quick.Console,
+  System.Generics.Collections,
+  Quick.Collections,
+  Quick.Linq;
+
+type
+
+  TUser = class
+  private
+    fName : string;
+    fAge : Integer;
+    fRoles : TArray<string>;
+    fRoles2 : IList<string>;
+  public
+    constructor Create(const aName : string; aAge : Integer; aRoles : TArray<string>);
+    property Name : string read fName write fName;
+    property Age : Integer read fAge write fAge;
+    property Roles : TArray<string> read fRoles write fRoles;
+    property Roles2 : IList<string> read fRoles2 write fRoles2;
+  end;
+
+var
+  List : IList<string>;
+
+  ListObj : IObjectList<TUser>;
+
+  myarray : TArray<string>;
+
+  user : TUser;
+  name : string;
+
+{ TMyItem }
+
+constructor TUser.Create(const aName : string; aAge : Integer; aRoles : TArray<string>);
+begin
+  fName := aName;
+  fAge := aAge;
+  fRoles := aRoles;
+  fRoles2 := TXList<string>.Create;
+  fRoles2.AddRange(aRoles);
+end;
+
+begin
+  try
+    ReportMemoryLeaksOnShutdown := True;
+
+    //add values
+    myarray := ['Joe','Mat','Lee'];
+    //search for regex match
+    cout('Search for regex match',ccYellow);
+    for name in myarray.Where('e$',True).Select do
+    begin
+      cout('User %s ends with "e"',[name],etInfo);
+    end;
+
+    //add values to list
+    List := TXList<string>.Create;
+    List.Add('Joe');
+    List.Add('Mat');
+    List.Add('Lee');
+
+    //get from index
+    cout('User is %s',[List[2]],etInfo);
+
+    //search for regex match
+    cout('Search for regex match',ccYellow);
+    for name in List.Where('e$',True).Select do
+    begin
+      cout('User %s ends with "e"',[name],etInfo);
+    end;
+
+    //add values to objectlist
+    ListObj := TXObjectList<TUser>.Create;
+    ListObj.Add(TUser.Create('Joe',22,['LocalAdmin']));
+    ListObj.Add(TUser.Create('Mat',30,['SuperAdmin','DomainAdmin']));
+    ListObj.Add(TUser.Create('Lee',40,['User']));
+
+    //search for a object property
+    cout('Search for a object property match with WhereClause',ccYellow);
+    user := ListObj.Where('Name = ?',['Lee']).SelectFirst;
+    if user <> nil then cout('%s is %d years old',[user.Name,user.Age],etInfo);
+
+    //search with predicate
+    cout('Search for a property match with Predicate',ccYellow);
+    user := ListObj.Where(function(aUser : TUser) : Boolean
+      begin
+        Result := aUser.Name.StartsWith('J');
+      end).SelectFirst;
+    if user <> nil then cout('%s starts with J letter',[user.Name],etInfo);
+
+    //search into a array property
+    cout('Search into a array property',ccYellow);
+    user := ListObj.Where('Roles2 CONTAINS ?',['SuperAdmin']).SelectFirst;
+    if user <> nil then cout('%s is %s',[user.Name,CommaText(user.Roles)],etInfo);
+
+
+    cout('Press ENTER to Exit',ccYellow);
+    ConsoleWaitForEnterKey;
+  except
+    on E: Exception do
+      Writeln(E.ClassName, ': ', E.Message);
+  end;
+end.

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 141 - 0
samples/delphi/QuickCollections/InterfacedLists.dproj


+ 20 - 4
samples/delphi/QuickLinq/LinqLists/LinqList.dpr

@@ -6,6 +6,7 @@ program LinqList;
 
 uses
   System.SysUtils,
+  System.Generics.Collections,
   Quick.Commons,
   Quick.Console,
   Quick.Chrono,
@@ -46,6 +47,7 @@ const
 var
   users : TIndexedObjectList<TUser>;
   users2 : TSearchObjectList<TUser>;
+  users3 : TObjectList<TUser>;
   user : TUser;
   i : Integer;
   n : Integer;
@@ -62,6 +64,7 @@ begin
     users.Indexes.Add('id','Id');
 
     users2 := TSearchObjectList<TUser>.Create(False);
+    users3 := TObjectList<TUser>.Create(True);
 
     cout('Generating list...',etInfo);
     //generate first dummy entries
@@ -74,6 +77,7 @@ begin
       user.Age := 18 + Random(20);
       users.Add(user);
       users2.Add(user);
+      users3.Add(user);
     end;
 
     //generate real entries to search
@@ -91,6 +95,7 @@ begin
 
       users.Add(user);
       users2.Add(user);
+      users3.Add(user);
     end;
 
     crono := TChronometer.Create;
@@ -110,8 +115,8 @@ begin
       //if (users[i].Name = 'Anus') or (users[i].SurName = 'Smith') then
       if users[i].Name = 'Peter' then
       begin
-        crono.Stop;
         user := users[i];
+        crono.Stop;
         Break;
       end;
     end;
@@ -122,11 +127,22 @@ begin
     //test search by Linq iteration
     crono.Start;
     //user := TLinq.From<TUser>(users2).Where('(Name = ?) OR (SurName = ?)',['Anus','Smith']).OrderBy('Name').SelectFirst;
-    user := TLinq<TUser>.From(users2).Where('Name = ?',['Peter']).SelectFirst;
+    user := TLinq<TUser>.From(users3).Where('Name = ?',['Peter']).SelectFirst;
     crono.Stop;
     if user <> nil then cout('Found by Linq: %s %s in %s',[user.Name,user.SurName,crono.ElapsedTime],etSuccess)
       else cout('Not found by Linq! (%s)',[crono.ElapsedTime],etError);
 
+    //test search by Linq iteration (predicate)
+    crono.Start;
+    //user := TLinq.From<TUser>(users2).Where('(Name = ?) OR (SurName = ?)',['Anus','Smith']).OrderBy('Name').SelectFirst;
+    user := TLinq<TUser>.From(users3).Where(function(aUser : TUser) : Boolean
+      begin
+        Result := aUser.Name = 'Peter';
+      end).SelectFirst;
+    crono.Stop;
+    if user <> nil then cout('Found by Linq (predicate): %s %s in %s',[user.Name,user.SurName,crono.ElapsedTime],etSuccess)
+      else cout('Not found by Linq! (%s)',[crono.ElapsedTime],etError);
+
     //test search by embeded iteration
     crono.Start;
     user := users2.Get('Name','Peter');
@@ -158,7 +174,7 @@ begin
     user := nil;
     n := 0;
     cout('Found by Linq:',etInfo);
-    //TLinq<TUser>.From(users2).Where('SurName Like ?',['p%']).Delete;
+    //TLinq<TUser>.From(users2).Where('Name Like ?',['p%']).Delete;
 
     TLinq<TUser>.From(users2).Where('Name = ?',['Peter']).Update(['Name'],['Poter']);
 
@@ -173,7 +189,7 @@ begin
                                          .Select do
     begin
       Inc(n);
-      cout('%d. %s %s',[n,user.Name,user.SurName],etSuccess);
+      cout('Login.Username: %d. %s %s',[n,user.Name,user.SurName],etSuccess);
     end;
     if user = nil then cout('Not found by Linq!',etError);
 

+ 448 - 2
samples/delphi/QuickLinq/LinqLists/LinqList.dproj

@@ -1,13 +1,13 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     <PropertyGroup>
         <ProjectGuid>{634B996C-7A5E-47E4-87E8-A2798A11331B}</ProjectGuid>
-        <ProjectVersion>18.6</ProjectVersion>
+        <ProjectVersion>18.8</ProjectVersion>
         <FrameworkType>None</FrameworkType>
         <MainSource>LinqList.dpr</MainSource>
         <Base>True</Base>
         <Config Condition="'$(Config)'==''">Debug</Config>
         <Platform Condition="'$(Platform)'==''">Win32</Platform>
-        <TargetedPlatforms>7</TargetedPlatforms>
+        <TargetedPlatforms>4103</TargetedPlatforms>
         <AppType>Console</AppType>
     </PropertyGroup>
     <PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
@@ -18,6 +18,11 @@
         <CfgParent>Base</CfgParent>
         <Base>true</Base>
     </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Android64' and '$(Base)'=='true') or '$(Base_Android64)'!=''">
+        <Base_Android64>true</Base_Android64>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
     <PropertyGroup Condition="('$(Platform)'=='iOSDevice32' and '$(Base)'=='true') or '$(Base_iOSDevice32)'!=''">
         <Base_iOSDevice32>true</Base_iOSDevice32>
         <CfgParent>Base</CfgParent>
@@ -38,6 +43,11 @@
         <CfgParent>Base</CfgParent>
         <Base>true</Base>
     </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='OSX64' and '$(Base)'=='true') or '$(Base_OSX64)'!=''">
+        <Base_OSX64>true</Base_OSX64>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
     <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
         <Base_Win32>true</Base_Win32>
         <CfgParent>Base</CfgParent>
@@ -92,6 +102,29 @@
         <EnabledSysJars>android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar</EnabledSysJars>
         <VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=</VerInfo_Keys>
         <BT_BuildType>Debug</BT_BuildType>
+        <Android_NotificationIcon24>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png</Android_NotificationIcon24>
+        <Android_NotificationIcon36>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png</Android_NotificationIcon36>
+        <Android_NotificationIcon48>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png</Android_NotificationIcon48>
+        <Android_NotificationIcon72>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png</Android_NotificationIcon72>
+        <Android_NotificationIcon96>$(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png</Android_NotificationIcon96>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_Android64)'!=''">
+        <VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=</VerInfo_Keys>
+        <BT_BuildType>Debug</BT_BuildType>
+        <Base_Android>true</Base_Android>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;FMXComponents;tethering;bindcompfmx;FmxTeeUI;fmx;dbexpress;IndyCore;dsnap;bindengine;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;rtl;DbxClientDriver;CustomIPTransport;bindcomp;CoolTrayIcon_D210_XE7;IndyIPClient;dbxcds;dsnapxml;dbrtl;IndyProtocols;$(DCC_UsePackage);$(DCC_UsePackage)</DCC_UsePackage>
+        <Android_LauncherIcon36>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png</Android_LauncherIcon36>
+        <Android_LauncherIcon48>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png</Android_LauncherIcon48>
+        <Android_LauncherIcon72>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png</Android_LauncherIcon72>
+        <Android_LauncherIcon96>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png</Android_LauncherIcon96>
+        <Android_LauncherIcon144>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png</Android_LauncherIcon144>
+        <Android_SplashImage426>$(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png</Android_SplashImage426>
+        <Android_SplashImage470>$(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png</Android_SplashImage470>
+        <Android_SplashImage640>$(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png</Android_SplashImage640>
+        <Android_SplashImage960>$(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png</Android_SplashImage960>
+        <EnabledSysJars>android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar</EnabledSysJars>
     </PropertyGroup>
     <PropertyGroup Condition="'$(Base_iOSDevice32)'!=''">
         <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;dbexpress;IndyCore;dsnap;bindengine;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
@@ -108,6 +141,15 @@
         <VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts</VerInfo_Keys>
         <BT_BuildType>Debug</BT_BuildType>
     </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_OSX64)'!=''">
+        <VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts</VerInfo_Keys>
+        <BT_BuildType>Debug</BT_BuildType>
+        <Base_OSX32>true</Base_OSX32>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;inetdb;FmxTeeUI;fmx;fmxdae;dbexpress;IndyCore;dsnap;bindengine;DBXMySQLDriver;FireDACMySQLDriver;FireDACCommonODBC;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDACPgDriver;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;fmxobj;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage)</DCC_UsePackage>
+        <DCC_ConsoleTarget>true</DCC_ConsoleTarget>
+    </PropertyGroup>
     <PropertyGroup Condition="'$(Base_Win32)'!=''">
         <DCC_UsePackage>DBXSqliteDriver;UbuntuProgressPackage;DBXInterBaseDriver;vclactnband;vclFireDAC;FMXComponents;tethering;svnui;JvGlobus;FireDACADSDriver;JvPluginSystem;JvMM;tmsxlsdXE11;vcltouch;JvBands;vcldb;bindcompfmx;svn;Intraweb;JvJans;JvNet;inetdb;JvAppFrm;EssentialsDR;vcwdedXE11;vcwdXE11;FmxTeeUI;JvDotNetCtrls;AbbreviaVCLD;fmx;fmxdae;tmsdXE11;vclib;JvWizards;tmsexdXE11;dbexpress;IndyCore;vclx;JvPageComps;dsnap;JvDB;VCLRESTComponents;JclDeveloperTools;vclie;bindengine;DBXMySQLDriver;JvCmp;FireDACMySQLDriver;JvHMI;FireDACCommonODBC;FMXComponentEd;LockBoxDR;bindcompdbx;IndyIPCommon;JvCustom;advchartdedxe11;vcl;IndyIPServer;GR32_D;JvXPCtrls;PngComponents;IndySystem;advchartdxe11;dsnapcon;FireDACMSAccDriver;fmxFireDAC;vclimg;madBasic_;TeeDB;Jcl;FrameViewer;JvCore;JvCrypt;FireDACPgDriver;ibmonitor;FMXTee;SevenZippro;DbxCommonDriver;JvDlgs;JvRuntimeDesign;ibxpress;Tee;JvManagedThreads;xmlrtl;ibxbindings;fmxobj;vclwinx;JvTimeFramework;rtl;GR32_R;DbxClientDriver;CustomIPTransport;vcldsnap;JvSystem;JvStdCtrls;bindcomp;appanalytics;CoolTrayIcon_D210_XE7;tmswizdXE11;nTrayIcon;IndyIPClient;bindcompvcl;TeeUI;TMSFMXPackPkgDXE11;JvDocking;dbxcds;VclSmp;JvPascalInterpreter;adortl;KernowSoftwareFMX;JclVcl;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;JvControls;JvPrintPreview;Analog_XE7;JclContainers;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
         <DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
@@ -238,12 +280,20 @@
                         <RemoteDir>classes</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>classes</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="AndroidFileProvider">
                     <Platform Name="Android">
                         <RemoteDir>res\xml</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\xml</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="AndroidGDBServer">
                     <Platform Name="Android">
@@ -256,96 +306,242 @@
                         <RemoteDir>library\lib\armeabi</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>library\lib\armeabi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidLibnativeArmeabiv7aFile">
+                    <Platform Name="Android64">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="AndroidLibnativeMipsFile">
                     <Platform Name="Android">
                         <RemoteDir>library\lib\mips</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>library\lib\mips</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="AndroidServiceOutput">
                     <Platform Name="Android">
                         <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>library\lib\arm64-v8a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidServiceOutput_Android32">
+                    <Platform Name="Android64">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="AndroidSplashImageDef">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="AndroidSplashStyles">
                     <Platform Name="Android">
                         <RemoteDir>res\values</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\values</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="AndroidSplashStylesV21">
                     <Platform Name="Android">
                         <RemoteDir>res\values-v21</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\values-v21</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_Colors">
+                    <Platform Name="Android">
+                        <RemoteDir>res\values</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\values</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_DefaultAppIcon">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_LauncherIcon144">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-xxhdpi</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-xxhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_LauncherIcon36">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-ldpi</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-ldpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_LauncherIcon48">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-mdpi</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-mdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_LauncherIcon72">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-hdpi</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-hdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_LauncherIcon96">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-xhdpi</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-xhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_NotificationIcon24">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-mdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-mdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_NotificationIcon36">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-hdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-hdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_NotificationIcon48">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-xhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_NotificationIcon72">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xxhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-xxhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_NotificationIcon96">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xxxhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-xxxhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_SplashImage426">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-small</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-small</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_SplashImage470">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-normal</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-normal</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_SplashImage640">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-large</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-large</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="Android_SplashImage960">
                     <Platform Name="Android">
                         <RemoteDir>res\drawable-xlarge</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\drawable-xlarge</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_Strings">
+                    <Platform Name="Android">
+                        <RemoteDir>res\values</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>res\values</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="DebugSymbols">
                     <Platform Name="iOSSimulator">
@@ -434,6 +630,9 @@
                     <Platform Name="Android">
                         <Operation>0</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <Operation>0</Operation>
+                    </Platform>
                     <Platform Name="iOSDevice32">
                         <Operation>0</Operation>
                     </Platform>
@@ -466,6 +665,17 @@
                         <Operation>1</Operation>
                     </Platform>
                 </DeployClass>
+                <DeployClass Name="iPad_Launch1024x768">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
                 <DeployClass Name="iPad_Launch1536">
                     <Platform Name="iOSDevice32">
                         <Operation>1</Operation>
@@ -477,6 +687,39 @@
                         <Operation>1</Operation>
                     </Platform>
                 </DeployClass>
+                <DeployClass Name="iPad_Launch1536x2048">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch1668">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch1668x2388">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
                 <DeployClass Name="iPad_Launch2048">
                     <Platform Name="iOSDevice32">
                         <Operation>1</Operation>
@@ -488,6 +731,61 @@
                         <Operation>1</Operation>
                     </Platform>
                 </DeployClass>
+                <DeployClass Name="iPad_Launch2048x1536">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch2048x2732">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch2224">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch2388x1668">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch2732x2048">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
                 <DeployClass Name="iPad_Launch768">
                     <Platform Name="iOSDevice32">
                         <Operation>1</Operation>
@@ -499,6 +797,116 @@
                         <Operation>1</Operation>
                     </Platform>
                 </DeployClass>
+                <DeployClass Name="iPad_Launch768x1024">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch1125">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch1136x640">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch1242">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch1242x2688">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch1334">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch1792">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch2208">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch2436">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch2688x1242">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
                 <DeployClass Name="iPhone_Launch320">
                     <Platform Name="iOSDevice32">
                         <Operation>1</Operation>
@@ -532,10 +940,35 @@
                         <Operation>1</Operation>
                     </Platform>
                 </DeployClass>
+                <DeployClass Name="iPhone_Launch750">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch828">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
                 <DeployClass Name="ProjectAndroidManifest">
                     <Platform Name="Android">
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <Operation>1</Operation>
+                    </Platform>
                 </DeployClass>
                 <DeployClass Name="ProjectiOSDeviceDebug">
                     <Platform Name="iOSDevice32">
@@ -628,6 +1061,10 @@
                         <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
+                    <Platform Name="Android64">
+                        <RemoteDir>library\lib\arm64-v8a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
                     <Platform Name="iOSDevice32">
                         <Operation>1</Operation>
                     </Platform>
@@ -652,6 +1089,12 @@
                         <Operation>0</Operation>
                     </Platform>
                 </DeployClass>
+                <DeployClass Name="ProjectOutput_Android32">
+                    <Platform Name="Android64">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
                 <DeployClass Name="ProjectUWPManifest">
                     <Platform Name="Win32">
                         <Operation>1</Operation>
@@ -689,13 +1132,16 @@
                 <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
                 <ProjectRoot Platform="OSX64" Name="$(PROJECTNAME).app"/>
                 <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
+                <ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
             </Deployment>
             <Platforms>
                 <Platform value="Android">False</Platform>
+                <Platform value="Android64">False</Platform>
                 <Platform value="iOSDevice32">False</Platform>
                 <Platform value="iOSDevice64">False</Platform>
                 <Platform value="iOSSimulator">False</Platform>
                 <Platform value="OSX32">True</Platform>
+                <Platform value="OSX64">True</Platform>
                 <Platform value="Win32">True</Platform>
                 <Platform value="Win64">True</Platform>
             </Platforms>

+ 53 - 0
samples/delphi/QuickPooling/HttpPool.dpr

@@ -0,0 +1,53 @@
+program HttpPool;
+
+{$APPTYPE CONSOLE}
+
+{$R *.res}
+
+uses
+  Classes,
+  System.SysUtils,
+  System.Net.HttpClient,
+  Quick.Commons,
+  Quick.Console,
+  Quick.Threads,
+  Quick.Pooling;
+
+var
+  pool : IObjectPool<THTTPClient>;
+  tasks : TBackgroundTasks;
+  i : Integer;
+
+begin
+  try
+    ReportMemoryLeaksOnShutdown := True;
+    pool := TObjectPool<THTTPClient>.Create(5,5000,procedure(var aInstance : THTTPClient)
+        begin
+          aInstance := THTTPClient.Create;
+        end);
+    tasks := TBackgroundTasks.Create(20);
+    for i := 0 to 100 do
+    begin
+      tasks.AddTask(procedure(task : ITask)
+        var
+          httpcli : THTTPClient;
+          statuscode : Integer;
+          poolitem : IPoolItem<THTTPClient>;
+        begin
+          poolitem := pool.Get;
+          cout('Got connection pool: %d',[poolitem.ItemIndex],etInfo);
+          httpcli := poolitem.Item;
+          statuscode := httpcli.Get('http://www.google.com').StatusCode;
+          if statuscode = 200 then cout('Download ok',etSuccess);
+          //Sleep(Random(2000));
+          cout(statuscode.ToString,etInfo);
+        end).Run;
+    end;
+    tasks.Start;
+    ConsoleWaitForEnterKey;
+    tasks.Free;
+  except
+    on E: Exception do
+      Writeln(E.ClassName, ': ', E.Message);
+  end;
+end.

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 145 - 0
samples/delphi/QuickPooling/HttpPool.dproj


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio