2
0

Quick.Lists.pas 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. { ***************************************************************************
  2. Copyright (c) 2016-2020 Kike Pérez
  3. Unit : Quick.Lists
  4. Description : Generic Lists functions
  5. Author : Kike Pérez
  6. Version : 1.2
  7. Created : 04/11/2018
  8. Modified : 12/03/2020
  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.Lists;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Classes,
  26. SysUtils,
  27. RTTI,
  28. TypInfo,
  29. Generics.Collections,
  30. Generics.Defaults,
  31. Quick.RTTI.Utils,
  32. Quick.Arrays,
  33. Quick.Value;
  34. //enable use of property paths (like namespaces) in search
  35. {$DEFINE PROPERTYPATH_MODE}
  36. type
  37. TClassField = (cfField, cfProperty);
  38. TSearchDictionary<TKey,TValue> = class(TObjectDictionary<TKey,TValue>)
  39. private
  40. fIndexName : string;
  41. fFieldName : string;
  42. fClassField : TClassField;
  43. public
  44. property IndexName : string read fIndexName write fIndexName;
  45. property FieldName : string read fFieldName write fFieldName;
  46. property ClassField : TClassField read fClassField write fClassField;
  47. end;
  48. TIndexList<T> = class
  49. private
  50. fList : TList<TSearchDictionary<Variant,T>>;
  51. fDictionaryIndex : TObjectDictionary<string,TSearchDictionary<Variant,T>>;
  52. public
  53. constructor Create;
  54. destructor Destroy; override;
  55. property List : TList<TSearchDictionary<Variant,T>> read fList;
  56. function Get(const aIndexName : string) : TSearchDictionary<Variant,T>;
  57. procedure Add(const aIndexName, aFieldName : string; aClassField : TClassField = cfProperty);
  58. procedure Remove(const aIndexName : string);
  59. end;
  60. TIndexedObjectList<T: class> = class(TList<T>)
  61. private
  62. fOwnsObjects: Boolean;
  63. fIndexes : TIndexList<T>;
  64. protected
  65. procedure Notify(const Value: T; Action: TCollectionNotification); override;
  66. public
  67. constructor Create(aOwnsObjects: Boolean = True); overload;
  68. constructor Create(const aComparer: IComparer<T>; aOwnsObjects: Boolean = True); overload;
  69. constructor Create(const aCollection: TEnumerable<T>; aOwnsObjects: Boolean = True); overload;
  70. destructor Destroy; override;
  71. property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
  72. property Indexes : TIndexList<T> read fIndexes;
  73. function Get(const aIndexName : string; aValue : Variant) : T;
  74. end;
  75. TSearchObjectList<T: class> = class(TObjectList<T>)
  76. public
  77. function Get(const aFieldName: string; const aValue: string; aClassField : TClassField = cfProperty) : T; overload;
  78. function Get(const aFieldName : string; aValue : Int64; aClassField : TClassField = cfProperty) : T; overload;
  79. function Get(const aFieldName : string; aValue : Double; aClassField : TClassField = cfProperty) : T; overload;
  80. function Get(const aFieldName : string; aValue : TDateTime; aClassField : TClassField = cfProperty) : T; overload;
  81. end;
  82. implementation
  83. { TIndexedObjectList<T> }
  84. constructor TIndexedObjectList<T>.Create(aOwnsObjects: Boolean);
  85. begin
  86. inherited Create;
  87. FOwnsObjects := aOwnsObjects;
  88. fIndexes := TIndexList<T>.Create;
  89. end;
  90. constructor TIndexedObjectList<T>.Create(const aComparer: IComparer<T>; aOwnsObjects: Boolean);
  91. begin
  92. inherited Create(aComparer);
  93. FOwnsObjects := aOwnsObjects;
  94. fIndexes := TIndexList<T>.Create;
  95. end;
  96. constructor TIndexedObjectList<T>.Create(const aCollection: TEnumerable<T>; aOwnsObjects: Boolean);
  97. begin
  98. inherited Create(aCollection);
  99. FOwnsObjects := aOwnsObjects;
  100. fIndexes := TIndexList<T>.Create;
  101. end;
  102. procedure TIndexedObjectList<T>.Notify(const Value: T; Action: TCollectionNotification);
  103. var
  104. sindex : TSearchDictionary<Variant,T>;
  105. propvalue : TValue;
  106. begin
  107. inherited;
  108. if Action = cnAdded then
  109. begin
  110. for sindex in fIndexes.List do
  111. begin
  112. try
  113. if sindex.ClassField = TClassField.cfField then propvalue := TRTTI.GetFieldValue(TObject(Value),sindex.FieldName)
  114. else
  115. begin
  116. {$IFNDEF PROPERTYPATH_MODE}
  117. propvalue := TRTTI.GetPropertyValue(TObject(Value),sindex.FieldName);
  118. {$ELSE}
  119. propvalue := TRTTI.GetPathValue(TObject(Value),sindex.FieldName);
  120. {$ENDIF}
  121. end;
  122. except
  123. raise Exception.CreateFmt('Cannot add value to "%s" search dictionary!',[sindex.IndexName]);
  124. end;
  125. sindex.Add(propvalue.AsVariant,Value);
  126. end;
  127. end;
  128. //remove object if owned
  129. if OwnsObjects and ((Action = cnRemoved) or (Action = cnExtracted)) then
  130. begin
  131. for sindex in fIndexes.List do
  132. begin
  133. try
  134. if sindex.ClassField = TClassField.cfField then propvalue := TRTTI.GetFieldValue(TObject(Value),sindex.FieldName)
  135. else
  136. begin
  137. {$IFNDEF PROPERTYPATH_MODE}
  138. propvalue := TRTTI.GetPropertyValue(TObject(Value),sindex.FieldName);
  139. {$ELSE}
  140. propvalue := TRTTI.GetPathValue(TObject(Value),sindex.FieldName);
  141. {$ENDIF}
  142. end;
  143. except
  144. raise Exception.CreateFmt('Cannot remove value to "%s" search dictionary!',[sindex.IndexName]);
  145. end;
  146. sindex.Remove(propvalue.AsVariant);
  147. end;
  148. Value.DisposeOf;
  149. end;
  150. end;
  151. destructor TIndexedObjectList<T>.Destroy;
  152. begin
  153. inherited;
  154. fIndexes.Free;
  155. end;
  156. function TIndexedObjectList<T>.Get(const aIndexName: string; aValue : Variant): T;
  157. var
  158. sindex : TSearchDictionary<Variant,T>;
  159. begin
  160. Result := nil;
  161. sindex := fIndexes.Get(aIndexName.ToLower);
  162. if sindex <> nil then sindex.TryGetValue(aValue,Result)
  163. else raise Exception.CreateFmt('Index "%s" not found!',[aIndexName]);
  164. end;
  165. { TIndexList<T> }
  166. procedure TIndexList<T>.Add(const aIndexName, aFieldName : string; aClassField : TClassField = cfProperty);
  167. var
  168. sdict : TSearchDictionary<Variant,T>;
  169. begin
  170. if aClassField = TClassField.cfField then
  171. begin
  172. if not TRTTI.FieldExists(TypeInfo(T),aFieldName) then raise Exception.CreateFmt('Not found field "%s" to create a search dictionary!',[aFieldName]);
  173. end
  174. else
  175. begin
  176. if not TRTTI.PropertyExists(TypeInfo(T),aFieldName) then raise Exception.CreateFmt('Not found property "%s" to create a search dictionary!',[aFieldName]);
  177. end;
  178. sdict := TSearchDictionary<Variant,T>.Create;
  179. sdict.IndexName := aIndexName;
  180. sdict.FieldName := aFieldName;
  181. sdict.ClassField := aClassField;
  182. fList.Add(sdict);
  183. fDictionaryIndex.Add(aIndexName.ToLower,sdict);
  184. end;
  185. procedure TIndexList<T>.Remove(const aIndexName: string);
  186. var
  187. sdict : TSearchDictionary<Variant,T>;
  188. begin
  189. if not fDictionaryIndex.ContainsKey(aIndexName) then raise Exception.CreateFmt('Cannot remove an inexistent "%s" search dictionary!',[aIndexName]);
  190. fList.Remove(sdict);
  191. fDictionaryIndex.Remove(aIndexName.ToLower);
  192. sdict.Free;
  193. end;
  194. constructor TIndexList<T>.Create;
  195. begin
  196. fList := TList<TSearchDictionary<Variant,T>>.Create;
  197. fDictionaryIndex := TObjectDictionary<string,TSearchDictionary<Variant,T>>.Create;
  198. end;
  199. destructor TIndexList<T>.Destroy;
  200. var
  201. sindex : TSearchDictionary<Variant,T>;
  202. begin
  203. for sindex in fList do sindex.Free;
  204. fList.Free;
  205. fDictionaryIndex.Free;
  206. inherited;
  207. end;
  208. function TIndexList<T>.Get(const aIndexName: string): TSearchDictionary<Variant, T>;
  209. begin
  210. Result := nil;
  211. fDictionaryIndex.TryGetValue(aIndexName,Result);
  212. end;
  213. { TSearchObjectList<T> }
  214. function TSearchObjectList<T>.Get(const aFieldName: string; const aValue: string; aClassField : TClassField = cfProperty): T;
  215. var
  216. val : T;
  217. begin
  218. Result := nil;
  219. for val in List do
  220. begin
  221. if aClassField = TClassField.cfField then
  222. begin
  223. if (val <> nil) and (TRTTI.GetFieldValue(TObject(val),aFieldName).AsString = aValue) then Exit(val);
  224. end
  225. else
  226. begin
  227. if (val <> nil) and (GetStrProp(TObject(val),aFieldName) = aValue) then Exit(val);
  228. end;
  229. end;
  230. end;
  231. function TSearchObjectList<T>.Get(const aFieldName: string; aValue: Int64; aClassField : TClassField = cfProperty): T;
  232. var
  233. val : T;
  234. begin
  235. Result := nil;
  236. for val in List do
  237. begin
  238. if aClassField = TClassField.cfField then
  239. begin
  240. if TRTTI.GetFieldValue(TObject(val),aFieldName).AsInt64 = aValue then Exit(val);
  241. end
  242. else
  243. begin
  244. if GetInt64Prop(TObject(val),aFieldName) = aValue then Exit(val);
  245. end;
  246. end;
  247. end;
  248. function TSearchObjectList<T>.Get(const aFieldName: string; aValue: Double; aClassField : TClassField = cfProperty): T;
  249. var
  250. val : T;
  251. begin
  252. Result := nil;
  253. for val in List do
  254. begin
  255. if aClassField = TClassField.cfField then
  256. begin
  257. if TRTTI.GetFieldValue(TObject(val),aFieldName).AsExtended = aValue then Exit(val);
  258. end
  259. else
  260. begin
  261. if GetFloatProp(TObject(val),aFieldName) = aValue then Exit(val);
  262. end;
  263. end;
  264. end;
  265. function TSearchObjectList<T>.Get(const aFieldName: string; aValue: TDateTime; aClassField : TClassField = cfProperty): T;
  266. var
  267. val : T;
  268. begin
  269. Result := nil;
  270. for val in List do
  271. begin
  272. if aClassField = TClassField.cfField then
  273. begin
  274. if TRTTI.GetFieldValue(TObject(val),aFieldName).AsExtended = aValue then Exit(val);
  275. end
  276. else
  277. begin
  278. if GetFloatProp(TObject(val),aFieldName) = aValue then Exit(val);
  279. end;
  280. end;
  281. end;
  282. end.