Quick.Linq.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. { ***************************************************************************
  2. Copyright (c) 2016-2022 Kike Pérez
  3. Unit : Quick.Linq
  4. Description : Arrays and Generic Lists Linq functions
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 04/04/2019
  8. Modified : 27/01/2022
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Linq;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. SysUtils,
  26. Generics.Collections,
  27. Generics.Defaults,
  28. RTTI,
  29. Quick.RTTI.Utils,
  30. Quick.Expression,
  31. Quick.Commons,
  32. Quick.Value,
  33. {$IFDEF FPC}
  34. Quick.Value.RTTI,
  35. {$ELSE}
  36. System.RegularExpressions,
  37. {$ENDIF}
  38. Quick.Arrays;
  39. type
  40. TOrderDirection = (odAscending, odDescending);
  41. {$IFNDEF FPC}
  42. TLinqExpression<T : class> = class(TExpression)
  43. private
  44. fPredicate : TPredicate<T>;
  45. public
  46. constructor Create(aPredicate : TPredicate<T>);
  47. function Validate(const aValue : TValue) : Boolean; override;
  48. function IsNull : Boolean; override;
  49. end;
  50. {$ENDIF}
  51. ILinqArray<T> = interface
  52. ['{3133DCAB-06C5-434B-B169-B32DC8C6308B}']
  53. function Any : Boolean; overload;
  54. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  55. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
  56. function OrderAscending : ILinqArray<T>;
  57. function OrderDescending : ILinqArray<T>;
  58. function SelectFirst : T;
  59. function SelectLast : T;
  60. function SelectTop(aLimit : Integer) : TArray<T>;
  61. function Select : TArray<T>; overload;
  62. function Count : Integer;
  63. function Update(const aNewValue : T) : Integer;
  64. function Delete : Integer;
  65. end;
  66. {$IFNDEF FPC}
  67. TLinqArray<T> = class(TInterfacedObject,ILinqArray<T>)
  68. type
  69. TLinqComparer = class(TInterfacedObject,IComparer<T>)
  70. private
  71. fSortAscending : Boolean;
  72. public
  73. constructor Create(aSortAscending : Boolean);
  74. property SortAscending : Boolean read fSortAscending;
  75. function Compare(const L, R: T): Integer;
  76. end;
  77. private
  78. fArray : TArray<T>;
  79. fMatchString : string;
  80. fUseRegEx : Boolean;
  81. function Validate(aValue : T) : Boolean;
  82. public
  83. constructor Create(aArray : TArray<T>);
  84. function Any : Boolean; overload;
  85. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  86. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
  87. function OrderAscending : ILinqArray<T>;
  88. function OrderDescending : ILinqArray<T>;
  89. function SelectFirst : T;
  90. function SelectLast : T;
  91. function SelectTop(aLimit : Integer) : TArray<T>;
  92. function Select : TArray<T>; overload;
  93. function Count : Integer;
  94. function Update(const aNewValue : T) : Integer;
  95. function Delete : Integer;
  96. end;
  97. TLinqArrayHelper = record helper for TArray<string>
  98. function Add(const aValue : string) : Integer;
  99. function AddIfNotExists(const aValue : string) : Integer;
  100. function Remove(const aValue : string) : Boolean;
  101. function Any : Boolean; overload;
  102. function Any(const aValue : string) : Boolean; overload;
  103. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  104. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>; overload;
  105. end;
  106. {$ENDIF}
  107. ILinqQuery<T> = interface
  108. ['{16B68C0B-EA38-488A-99D9-BAD1E8560E8E}']
  109. function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
  110. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  111. {$IFNDEF FPC}
  112. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  113. {$ENDIF}
  114. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  115. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  116. function SelectFirst : T;
  117. function SelectLast : T;
  118. function SelectTop(aLimit : Integer) : TxArray<T>;
  119. function Select : TxArray<T>; overload;
  120. function Select(const aPropertyName : string) : TFlexArray; overload;
  121. function Count : Integer;
  122. function Update(const aFields : array of string; aValues : array of const) : Integer;
  123. function Delete : Integer;
  124. end;
  125. TLinqQuery<T : class> = class(TInterfacedObject,ILinqQuery<T>)
  126. private type
  127. arrayOfT = array of T;
  128. TArrType = (atArray, atXArray, atList, atObjectList);
  129. private
  130. fWhereClause : TExpression;
  131. fOrderBy : TArray<string>;
  132. fOrderDirection : TOrderDirection;
  133. fPList : Pointer;
  134. fList : arrayOfT;
  135. fArrType : TArrType;
  136. function FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  137. procedure DoOrderBy(vArray : ArrayOfT);
  138. function Compare(const aPropertyName : string; L, R : T) : Integer;
  139. procedure Clear;
  140. public
  141. {$IFNDEF FPC}
  142. constructor Create(aObjectList : TObjectList<T>); overload;
  143. {$ENDIF}
  144. constructor Create(aList : TList<T>); overload;
  145. constructor Create(aXArray : TxArray<T>); overload;
  146. constructor Create(aArray : TArray<T>); overload;
  147. destructor Destroy; override;
  148. function Where(const aWhereClause : string; aWhereParams : array of const) : ILinqQuery<T>; overload;
  149. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  150. {$IFNDEF FPC}
  151. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  152. {$ENDIF}
  153. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  154. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  155. function SelectFirst : T;
  156. function SelectLast : T;
  157. function SelectTop(aLimit : Integer) : TxArray<T>;
  158. function Select : TxArray<T>; overload;
  159. function Select(const aPropertyName : string) : TFlexArray; overload;
  160. function Count : Integer;
  161. function Update(const aFields : array of string; aValues : array of const) : Integer;
  162. function Delete : Integer;
  163. end;
  164. TLinq<T : class> = class
  165. public
  166. {$IFNDEF FPC}
  167. class function From(aObjectList : TObjectList<T>) : ILinqQuery<T>; overload;
  168. {$ENDIF}
  169. class function From(aArray : TArray<T>) : ILinqQuery<T>; overload;
  170. class function From(aXArray : TXArray<T>) : ILinqQuery<T>; overload;
  171. end;
  172. ELinqNotValidExpression = class(Exception);
  173. ELinqError = class(Exception);
  174. implementation
  175. { TLinqQuery<T> }
  176. procedure TLinqQuery<T>.Clear;
  177. begin
  178. SetLength(fOrderBy,0);
  179. end;
  180. constructor TLinqQuery<T>.Create(aArray: TArray<T>);
  181. begin
  182. Clear;
  183. fPList := Pointer(aArray);
  184. fList := aArray;
  185. fArrType := TArrType.atArray;
  186. end;
  187. {$IFNDEF FPC}
  188. constructor TLinqQuery<T>.Create(aObjectList: TObjectList<T>);
  189. begin
  190. Clear;
  191. fPList := Pointer(aObjectList);
  192. {$IFDEF DELPHIRX104_UP}
  193. fList := aObjectList.PList^;
  194. {$ELSE}
  195. fList := aObjectList.List;
  196. {$ENDIF}
  197. fArrType := TArrType.atObjectList;
  198. end;
  199. {$ENDIF}
  200. constructor TLinqQuery<T>.Create(aXArray: TxArray<T>);
  201. begin
  202. Clear;
  203. fPList := Pointer(aXArray);
  204. fList := aXArray.PArray^;
  205. fArrType := TArrType.atXArray;
  206. end;
  207. constructor TLinqQuery<T>.Create(aList: TList<T>);
  208. begin
  209. Clear;
  210. fPList := Pointer(aList);
  211. {$IFDEF DELPHIRX104_UP}
  212. fList := aList.PList^;
  213. {$ELSE}
  214. fList := aList.ToArray;
  215. {$ENDIF}
  216. fArrType := TArrType.atList;
  217. end;
  218. function TLinqQuery<T>.Compare(const aPropertyName: string; L, R: T): Integer;
  219. var
  220. valueL : TValue;
  221. valueR : TValue;
  222. begin
  223. Result := 0;
  224. valueL := TRTTI.GetPathValue(L,aPropertyName);
  225. valueR := TRTTI.GetPathValue(R,aPropertyName);
  226. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(1)
  227. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(-1);
  228. case valueL.Kind of
  229. {$IFNDEF FPC}
  230. tkString,
  231. {$ENDIF}
  232. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  233. tkInteger, tkInt64 :
  234. begin
  235. if valueL.AsInteger > valueR.AsInteger then Result := 1
  236. else if valueL.AsInteger < valueR.AsInteger then Result := -1;
  237. end;
  238. tkFloat :
  239. begin
  240. if valueL.AsExtended > valueR.AsExtended then Result := 1
  241. else if valueL.AsExtended < valueR.AsExtended then Result := -1;
  242. end;
  243. end;
  244. end;
  245. function TLinqQuery<T>.Count: Integer;
  246. var
  247. i : Integer;
  248. begin
  249. Result := 0;
  250. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  251. for i := High(fList) downto Low(fList) do
  252. begin
  253. if fWhereClause.Validate(fList[i]) then
  254. begin
  255. Inc(Result);
  256. end;
  257. end;
  258. end;
  259. function TLinqQuery<T>.Delete: Integer;
  260. var
  261. i : Integer;
  262. begin
  263. Result := 0;
  264. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  265. for i := High(fList) downto Low(fList) do
  266. begin
  267. if fWhereClause.Validate(fList[i]) then
  268. begin
  269. case fArrType of
  270. TArrType.atArray, TArrType.atXArray :
  271. begin
  272. TObject(fList[i]).Free;
  273. System.Delete(fList,i,1);
  274. //fPList := Pointer(fList);
  275. end;
  276. TArrType.atList :
  277. begin
  278. TList<T>(fPList).Delete(i);
  279. end;
  280. TArrType.atObjectList :
  281. begin
  282. TObjectList<T>(fPList).Delete(i);
  283. end;
  284. end;
  285. Inc(Result);
  286. end;
  287. end;
  288. end;
  289. destructor TLinqQuery<T>.Destroy;
  290. begin
  291. if Assigned(fWhereClause) then fWhereClause.Free;
  292. inherited;
  293. end;
  294. procedure TLinqQuery<T>.DoOrderBy(vArray : ArrayOfT);
  295. begin
  296. if High(fOrderBy) < 0 then Exit;
  297. {$IFNDEF FPC}
  298. TArray.Sort<T>(vArray, TComparer<T>.Construct(
  299. function (const A, B: T): Integer
  300. var
  301. field : string;
  302. begin
  303. for field in fOrderBy do
  304. begin
  305. Result := Compare(field,A,B);
  306. if Result <> 0 then Break;
  307. end;
  308. if fOrderDirection = TOrderDirection.odDescending then Result := Result * -1;
  309. end)
  310. );
  311. {$ENDIF}
  312. end;
  313. function TLinqQuery<T>.OrderBy(const aFieldNames: string): ILinqQuery<T>;
  314. begin
  315. Result := Self;
  316. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  317. fOrderBy := aFieldNames.Split([',']);
  318. fOrderDirection := TOrderDirection.odAscending;
  319. end;
  320. function TLinqQuery<T>.OrderByDescending(const aFieldNames: string): ILinqQuery<T>;
  321. begin
  322. Result := Self;
  323. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  324. fOrderBy := aFieldNames.Split([',']);
  325. fOrderDirection := TOrderDirection.odDescending;
  326. end;
  327. function TLinqQuery<T>.Select(const aPropertyName: string): TFlexArray;
  328. var
  329. obj : T;
  330. value : TFlexValue;
  331. begin
  332. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  333. for obj in fList do
  334. begin
  335. {$IFNDEF FPC}
  336. if obj = nil then continue;
  337. {$ELSE}
  338. if Pointer(obj) = nil then continue;
  339. {$ENDIF}
  340. if fWhereClause.Validate(obj) then
  341. begin
  342. //value := TRTTI.GetProperty(obj,aPropertyName);
  343. {$IFNDEF FPC}
  344. value := TRTTI.GetPathValue(obj,aPropertyName).AsVariant;
  345. {$ELSE}
  346. value.AsTValue := TRTTI.GetPathValue(obj,aPropertyName);
  347. {$ENDIF}
  348. Result.Add(value);
  349. end;
  350. end;
  351. end;
  352. function TLinqQuery<T>.Select: TxArray<T>;
  353. var
  354. obj : T;
  355. begin
  356. {$If Defined(FPC) OR Defined(DELPHIRX102_UP)}
  357. Result := [];
  358. {$ELSE}
  359. Result := nil;
  360. {$ENDIF}
  361. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  362. for obj in fList do
  363. begin
  364. {$IFNDEF FPC}
  365. if obj = nil then continue;
  366. {$ELSE}
  367. if Pointer(obj) = nil then continue;
  368. {$ENDIF}
  369. if fWhereClause.Validate(obj) then Result.Add(obj);
  370. end;
  371. DoOrderBy(Result);
  372. end;
  373. function TLinqQuery<T>.SelectFirst: T;
  374. var
  375. obj : T;
  376. begin
  377. {$IFNDEF FPC}
  378. Result := nil;
  379. {$ENDIF}
  380. DoOrderBy(fList);
  381. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  382. for obj in fList do
  383. begin
  384. {$IFNDEF FPC}
  385. if obj = nil then continue;
  386. {$ELSE}
  387. if Pointer(obj) = nil then continue;
  388. {$ENDIF}
  389. if fWhereClause.Validate(obj) then Exit(obj);
  390. end;
  391. end;
  392. function TLinqQuery<T>.SelectLast: T;
  393. var
  394. obj : T;
  395. begin
  396. {$IFNDEF FPC}
  397. Result := nil;
  398. {$ENDIF}
  399. DoOrderBy(fList);
  400. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  401. for obj in fList do
  402. begin
  403. {$IFNDEF FPC}
  404. if obj = nil then continue;
  405. {$ELSE}
  406. if Pointer(obj) = nil then continue;
  407. {$ENDIF}
  408. if fWhereClause.Validate(obj) then Result := obj;
  409. end;
  410. end;
  411. function TLinqQuery<T>.SelectTop(aLimit: Integer): TxArray<T>;
  412. var
  413. obj : T;
  414. i : Integer;
  415. begin
  416. {$If Defined(FPC) OR Defined(DELPHIRX102_UP)}
  417. Result := [];
  418. {$ELSE}
  419. Result := nil;
  420. {$ENDIF}
  421. DoOrderBy(fList);
  422. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  423. i := 0;
  424. for obj in fList do
  425. begin
  426. {$IFNDEF FPC}
  427. if obj = nil then continue;
  428. {$ELSE}
  429. if Pointer(obj) = nil then continue;
  430. {$ENDIF}
  431. if fWhereClause.Validate(obj) then
  432. begin
  433. Result.Add(obj);
  434. Inc(i);
  435. if i > aLimit then Exit;
  436. end;
  437. end;
  438. end;
  439. function TLinqQuery<T>.Update(const aFields: array of string; aValues: array of const): Integer;
  440. var
  441. obj : T;
  442. i : Integer;
  443. {$IFDEF FPC}
  444. value : TValue;
  445. {$ENDIF}
  446. begin
  447. Result := 0;
  448. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  449. for obj in fList do
  450. begin
  451. {$IFNDEF FPC}
  452. if obj = nil then continue;
  453. {$ELSE}
  454. if Pointer(obj) = nil then continue;
  455. {$ENDIF}
  456. if fWhereClause.Validate(obj) then
  457. begin
  458. for i := Low(aFields) to High(aFields) do
  459. begin
  460. if not TRTTI.PropertyExists(TypeInfo(T),aFields[i]) then raise ELinqError.CreateFmt('Linq update field "%s" not found in obj',[aFields[i]]);
  461. try
  462. {$IFNDEF FPC}
  463. TRTTI.SetPropertyValue(obj,aFields[i],aValues[i]);
  464. {$ELSE}
  465. case aValues[i].VType of
  466. vtString : value := string(aValues[i].VString^);
  467. vtChar : value := string(aValues[i].VChar);
  468. {$IFDEF MSWINDOWS}
  469. vtAnsiString : value := AnsiString(aValues[i].VAnsiString);
  470. vtWideString : value := WideString(aValues[i].VWideString);
  471. {$ENDIF}
  472. {$IFDEF UNICODE}
  473. vtUnicodeString: AsString := string(aValues[i].VUnicodeString);
  474. {$ENDIF UNICODE}
  475. vtInteger : value := aValues[i].VInteger;
  476. vtInt64 : value := aValues[i].VInt64^;
  477. vtExtended : value := aValues[i].VExtended^;
  478. vtBoolean : value := aValues[i].VBoolean;
  479. else raise Exception.Create('DataType not supported by Linq update');
  480. end;
  481. TRTTI.SetPropertyValue(obj,aFields[i],value);
  482. {$ENDIF}
  483. except
  484. on E : Exception do raise ELinqError.CreateFmt('Linq update error: %s',[e.Message]);
  485. end;
  486. end;
  487. Inc(Result);
  488. end;
  489. end;
  490. end;
  491. function TLinqQuery<T>.FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  492. var
  493. i : Integer;
  494. begin
  495. Result := aWhereClause;
  496. if aWhereClause = '' then
  497. begin
  498. Result := '1 = 1';
  499. Exit;
  500. end;
  501. for i := 0 to aWhereClause.CountChar('?') - 1 do
  502. begin
  503. case aWhereParams[i].VType of
  504. vtInteger : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInteger),[]);
  505. vtInt64 : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInt64^),[]);
  506. vtExtended : Result := StringReplace(Result,'?',FloatToStr(aWhereParams[i].VExtended^),[]);
  507. vtBoolean : Result := StringReplace(Result,'?',BoolToStr(aWhereParams[i].VBoolean),[]);
  508. vtAnsiString : Result := StringReplace(Result,'?',string(aWhereParams[i].VAnsiString),[]);
  509. vtWideString : Result := StringReplace(Result,'?',string(aWhereParams[i].VWideString^),[]);
  510. {$IFNDEF NEXTGEN}
  511. vtString : Result := StringReplace(Result,'?',string(aWhereParams[i].VString^),[]);
  512. {$ENDIF}
  513. vtChar : Result := StringReplace(Result,'?',string(aWhereParams[i].VChar),[]);
  514. vtPChar : Result := StringReplace(Result,'?',string(aWhereParams[i].VPChar),[]);
  515. else Result := StringReplace(Result,'?', DbQuotedStr(string(aWhereParams[i].VUnicodeString)),[]);
  516. end;
  517. end;
  518. end;
  519. function TLinqQuery<T>.Where(const aWhereClause: string; aWhereParams: array of const): ILinqQuery<T>;
  520. begin
  521. Result := Where(FormatParams(aWhereClause,aWhereParams));
  522. end;
  523. function TLinqQuery<T>.Where(const aWhereClause: string): ILinqQuery<T>;
  524. begin
  525. Result := Self;
  526. try
  527. fWhereClause := TExpressionParser.Parse(aWhereClause);
  528. except
  529. on E : Exception do raise ELinqNotValidExpression.Create(e.Message);
  530. end;
  531. end;
  532. {$IFNDEF FPC}
  533. function TLinqQuery<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
  534. begin
  535. Result := Self;
  536. fWhereClause := TLinqExpression<T>.Create(aPredicate);
  537. end;
  538. {$ENDIF}
  539. { TLinq }
  540. {$IFNDEF FPC}
  541. class function TLinq<T>.From(aObjectList: TObjectList<T>): ILinqQuery<T>;
  542. begin
  543. Result := TLinqQuery<T>.Create(aObjectList);
  544. end;
  545. {$ENDIF}
  546. class function TLinq<T>.From(aArray: TArray<T>): ILinqQuery<T>;
  547. begin
  548. Result := TLinqQuery<T>.Create(aArray);
  549. end;
  550. class function TLinq<T>.From(aXArray : TXArray<T>) : ILinqQuery<T>;
  551. begin
  552. Result := TLinqQuery<T>.Create(aXArray);
  553. end;
  554. { TLinqExpression<T> }
  555. {$IFNDEF FPC}
  556. constructor TLinqExpression<T>.Create(aPredicate: TPredicate<T>);
  557. begin
  558. fPredicate := aPredicate;
  559. end;
  560. function TLinqExpression<T>.Validate(const aValue : TValue) : Boolean;
  561. begin
  562. Result := fPredicate(aValue.AsType<T>);
  563. end;
  564. function TLinqExpression<T>.IsNull : Boolean;
  565. begin
  566. Result := not Assigned(fPredicate);
  567. end;
  568. { TLinqArray<T> }
  569. function TLinqArray<T>.Any : Boolean;
  570. begin
  571. Result := High(fArray) >= 0;
  572. end;
  573. function TLinqArray<T>.Any(const aMatchString: string; aUseRegEx: Boolean): Boolean;
  574. begin
  575. fMatchString := aMatchString;
  576. fUseRegEx := aUseRegEx;
  577. end;
  578. function TLinqArray<T>.Count: Integer;
  579. begin
  580. Result := High(fArray) + 1;
  581. end;
  582. constructor TLinqArray<T>.Create(aArray: TArray<T>);
  583. begin
  584. {$IFDEF DELPHIRX104_UP}
  585. Pointer(fArray) := aArray;
  586. {$ELSE}
  587. fArray := aArray;
  588. {$ENDIF}
  589. end;
  590. function TLinqArray<T>.Delete: Integer;
  591. var
  592. i : Integer;
  593. {$IFNDEF DELPHIXE7_UP}
  594. n : Integer;
  595. len : Integer;
  596. {$ENDIF}
  597. begin
  598. Result := 0;
  599. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  600. for i := High(fArray) downto Low(fArray) do
  601. begin
  602. if Validate(fArray[i]) then
  603. begin
  604. //TObject(fArray[i]).Free;
  605. {$IFDEF DELPHIXE7_UP}
  606. System.Delete(fArray,i,1);
  607. {$ELSE}
  608. len := Length(fArray);
  609. if (len > 0) and (i < len) then
  610. begin
  611. for n := i + 1 to len - 1 do fArray[n - 1] := fArray[n];
  612. SetLength(fArray, len - 1);
  613. end;
  614. {$ENDIF}
  615. Inc(Result);
  616. end;
  617. end;
  618. end;
  619. function TLinqArray<T>.OrderAscending: ILinqArray<T>;
  620. var
  621. comparer : IComparer<T>;
  622. begin
  623. comparer := TLinqComparer.Create(True);
  624. TArray.Sort<T>(fArray,comparer);
  625. end;
  626. function TLinqArray<T>.OrderDescending: ILinqArray<T>;
  627. var
  628. comparer : IComparer<T>;
  629. begin
  630. comparer := TLinqComparer.Create(False);
  631. TArray.Sort<T>(fArray,comparer);
  632. end;
  633. function TLinqArray<T>.Select: TArray<T>;
  634. var
  635. value : T;
  636. begin
  637. Result := [];
  638. //DoOrderBy(fList);
  639. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  640. for value in fArray do
  641. begin
  642. if Validate(value) then Result := Result + [value];
  643. end;
  644. end;
  645. function TLinqArray<T>.SelectFirst: T;
  646. var
  647. value : T;
  648. begin
  649. //DoOrderBy(fList);
  650. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  651. for value in fArray do
  652. begin
  653. if Validate(value) then Exit(value);
  654. end;
  655. end;
  656. function TLinqArray<T>.SelectLast: T;
  657. var
  658. value : T;
  659. found : T;
  660. begin
  661. //DoOrderBy(fList);
  662. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  663. for value in fArray do
  664. begin
  665. if Validate(value) then found := value;
  666. end;
  667. Result := found;
  668. end;
  669. function TLinqArray<T>.SelectTop(aLimit: Integer): TArray<T>;
  670. var
  671. i : Integer;
  672. limit : Integer;
  673. begin
  674. Result := [];
  675. if aLimit > High(fArray) then limit := High(fArray)
  676. else limit := aLimit;
  677. SetLength(Result,limit);
  678. for i := Low(fArray) to limit do
  679. begin
  680. Result[i] := fArray[i];
  681. end;
  682. end;
  683. function TLinqArray<T>.Update(const aNewValue: T): Integer;
  684. var
  685. i : Integer;
  686. begin
  687. for i := Low(fArray) to High(fArray) do
  688. begin
  689. if Validate(fArray[i]) then fArray[i] := aNewValue;
  690. end;
  691. end;
  692. function TLinqArray<T>.Validate(aValue: T): Boolean;
  693. var
  694. regEx : TRegEx;
  695. value : TValue;
  696. begin
  697. value := TValue.From<T>(aValue);
  698. if fUseRegEx then
  699. begin
  700. regEx := TRegEx.Create(fMatchString,[roIgnoreCase,roMultiline]);
  701. try
  702. Result := regEx.IsMatch(value.AsString);
  703. except
  704. raise Exception.Create('TLinqArray not valid RegEx!');
  705. end;
  706. end
  707. else
  708. begin
  709. Result := CompareText(fMatchString,value.AsString) = 0;
  710. end;
  711. end;
  712. function TLinqArray<T>.Where(const aMatchString: string; aUseRegEx: Boolean): ILinqArray<T>;
  713. begin
  714. Result := Self;
  715. fMatchString := aMatchString;
  716. fUseRegEx := aUseRegEx;
  717. end;
  718. { TLinqArray<T>.TLinqComparer }
  719. constructor TLinqArray<T>.TLinqComparer.Create(aSortAscending : Boolean);
  720. begin
  721. fSortAscending := aSortAscending;
  722. end;
  723. function TLinqArray<T>.TLinqComparer.Compare(const L, R: T): Integer;
  724. var
  725. valueL : TValue;
  726. valueR : TValue;
  727. hr : Integer;
  728. lr : Integer;
  729. begin
  730. Result := 0;
  731. if fSortAscending then
  732. begin
  733. hr := 1;
  734. lr := -1;
  735. end
  736. else
  737. begin
  738. hr := -1;
  739. lr := 1;
  740. end;
  741. valueL := TValue.From<T>(L);
  742. valueR := TValue.From<T>(R);
  743. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(hr)
  744. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(lr);
  745. case valueL.Kind of
  746. {$IFNDEF FPC}
  747. tkString,
  748. {$ENDIF}
  749. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  750. tkInteger, tkInt64 :
  751. begin
  752. if valueL.AsInteger > valueR.AsInteger then Result := hr
  753. else if valueL.AsInteger < valueR.AsInteger then Result := lr;
  754. end;
  755. tkFloat :
  756. begin
  757. if valueL.AsExtended > valueR.AsExtended then Result := hr
  758. else if valueL.AsExtended < valueR.AsExtended then Result := lr;
  759. end;
  760. end;
  761. end;
  762. { TLinqArrayHelper }
  763. function TLinqArrayHelper.Add(const aValue : string) : Integer;
  764. begin
  765. SetLength(Self,Length(Self)+1);
  766. Self[High(Self)] := aValue;
  767. Result := High(Self);
  768. end;
  769. function TLinqArrayHelper.AddIfNotExists(const aValue : string) : Integer;
  770. var
  771. i : Integer;
  772. begin
  773. for i := Low(Self) to High(Self) do
  774. begin
  775. if CompareText(Self[i],aValue) = 0 then Exit(i);
  776. end;
  777. //if not exists add it
  778. Result := Self.Add(aValue);
  779. end;
  780. function TLinqArrayHelper.Remove(const aValue : string) : Boolean;
  781. var
  782. i : Integer;
  783. begin
  784. for i := Low(Self) to High(Self) do
  785. begin
  786. if CompareText(Self[i],aValue) = 0 then
  787. begin
  788. System.Delete(Self,i,1);
  789. Exit(True);
  790. end;
  791. end;
  792. Result := False;
  793. end;
  794. function TLinqArrayHelper.Any : Boolean;
  795. begin
  796. Result := High(Self) >= 0;
  797. end;
  798. function TLinqArrayHelper.Any(const aValue : string) : Boolean;
  799. var
  800. value : string;
  801. begin
  802. Result := False;
  803. for value in Self do
  804. begin
  805. if CompareText(value,aValue) = 0 then Exit(True)
  806. end;
  807. end;
  808. function TLinqArrayHelper.Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean;
  809. begin
  810. Result := TLinqArray<string>.Create(Self).Any(aMatchString,aUseRegEx);
  811. end;
  812. function TLinqArrayHelper.Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>;
  813. begin
  814. Result := TLinqArray<string>.Create(Self).Where(aMatchString,aUseRegEx);
  815. end;
  816. {$ENDIF}
  817. end.