Quick.Lists.pas 9.0 KB

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