Quick.RTTI.Utils.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. { ***************************************************************************
  2. Copyright (c) 2016-2019 Kike Pérez
  3. Unit : Quick.RTTI.Utils
  4. Description : Files functions
  5. Author : Kike Pérez
  6. Version : 1.4
  7. Created : 09/03/2018
  8. Modified : 10/05/2019
  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.RTTI.Utils;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. SysUtils,
  26. Quick.Commons,
  27. Rtti;
  28. type
  29. TRTTI = class
  30. private class var
  31. fCtx : TRttiContext;
  32. public
  33. class function GetType(aTypeInfo : Pointer) : TRttiType;
  34. {$IFNDEF FPC}
  35. class function GetField(aInstance : TObject; const aFieldName : string) : TRttiField; overload;
  36. class function GetField(aTypeInfo : Pointer; const aFieldName : string) : TRttiField; overload;
  37. class function FieldExists(aTypeInfo : Pointer; const aFieldName : string) : Boolean;
  38. class function GetFieldValue(aInstance : TObject; const aFieldName : string) : TValue; overload;
  39. class function GetFieldValue(aTypeInfo : Pointer; const aFieldName: string) : TValue; overload;
  40. {$ENDIF}
  41. class function GetProperty(aInstance : TObject; const aPropertyName : string) : TRttiProperty; overload;
  42. class function GetProperty(aTypeInfo : Pointer; const aPropertyName : string) : TRttiProperty; overload;
  43. class function GetPropertyPath(aInstance : TObject; const aPropertyPath : string) : TRttiProperty;
  44. class function PathExists(aInstance: TObject; const aPropertyPath: string) : Boolean;
  45. class function GetPathValue(aInstance : TObject; const aPropertyPath : string) : TValue;
  46. class procedure SetPathValue(aInstance: TObject; const aPropertyPath: string; aValue : TValue);
  47. class procedure SetPropertyValue(aInstance : TObject; const aPropertyName : string; aValue : TValue);
  48. class function PropertyExists(aTypeInfo : Pointer; const aPropertyName : string) : Boolean;
  49. class function GetPropertyValue(aInstance : TObject; const aPropertyName : string) : TValue; overload;
  50. class function GetPropertyValue(aTypeInfo : Pointer; const aPropertyName : string) : TValue; overload;
  51. {$IFNDEF FPC}
  52. class function FindClass(const aClassName: string): TClass;
  53. {$ENDIF}
  54. end;
  55. ERTTIError = class(Exception);
  56. implementation
  57. { TRTTIUtils }
  58. {$IFNDEF FPC}
  59. class function TRTTI.FieldExists(aTypeInfo: Pointer; const aFieldName: string): Boolean;
  60. var
  61. rtype : TRttiType;
  62. begin
  63. rtype := fCtx.GetType(aTypeInfo);
  64. Result := rtype.GetField(aFieldName) <> nil;
  65. end;
  66. class function TRTTI.GetField(aInstance: TObject; const aFieldName: string): TRttiField;
  67. var
  68. rtype : TRttiType;
  69. begin
  70. rtype := fCtx.GetType(aInstance.ClassInfo);
  71. if rtype <> nil then
  72. begin
  73. Result := rtype.GetField(aFieldName);
  74. end;
  75. end;
  76. class function TRTTI.GetField(aTypeInfo: Pointer; const aFieldName: string): TRttiField;
  77. var
  78. rtype : TRttiType;
  79. begin
  80. rtype := fCtx.GetType(aTypeInfo);
  81. if rtype <> nil then
  82. begin
  83. Result := rtype.GetField(aFieldName);
  84. end;
  85. end;
  86. class function TRTTI.GetFieldValue(aInstance : TObject; const aFieldName: string): TValue;
  87. var
  88. rfield: TRttiField;
  89. begin
  90. rfield := GetField(aInstance,aFieldName);
  91. if rfield <> nil then Result := rfield.GetValue(aInstance);
  92. end;
  93. class function TRTTI.GetFieldValue(aTypeInfo : Pointer; const aFieldName: string): TValue;
  94. var
  95. rfield: TRttiField;
  96. begin
  97. rfield := GetField(aTypeInfo,aFieldName);
  98. if rfield <> nil then rfield.GetValue(aTypeInfo);
  99. end;
  100. {$ENDIF}
  101. class function TRTTI.GetProperty(aInstance: TObject; const aPropertyName: string): TRttiProperty;
  102. var
  103. rtype : TRttiType;
  104. begin
  105. rtype := fCtx.GetType(aInstance.ClassInfo);
  106. if rtype <> nil then Result := rtype.GetProperty(aPropertyName);
  107. end;
  108. class function TRTTI.GetProperty(aTypeInfo: Pointer; const aPropertyName: string): TRttiProperty;
  109. var
  110. rtype : TRttiType;
  111. begin
  112. rtype := fCtx.GetType(aTypeInfo);
  113. if rtype <> nil then Result := rtype.GetProperty(aPropertyName);
  114. end;
  115. class function TRTTI.GetPropertyPath(aInstance: TObject; const aPropertyPath: string): TRttiProperty;
  116. var
  117. prop : TRttiProperty;
  118. proppath : string;
  119. propname : string;
  120. i : Integer;
  121. value : TValue;
  122. rtype : TRttiType;
  123. {$IFNDEF FPC}
  124. rfield : TRttiField;
  125. {$ENDIF}
  126. begin
  127. Result := nil;
  128. proppath := aPropertyPath;
  129. rtype := fCtx.GetType(aInstance.ClassType);
  130. repeat
  131. i := proppath.IndexOf('.');
  132. if i > -1 then
  133. begin
  134. propname := Copy(proppath,1,i);
  135. Delete(proppath,1,i+1);
  136. end
  137. else propname := proppath;
  138. if rtype.TypeKind = TTypeKind.tkRecord then
  139. begin
  140. {$IFNDEF FPC}
  141. rfield := rtype.GetField(propname);
  142. if rfield <> nil then value := rfield.GetValue(aInstance);
  143. {$ELSE}
  144. raise ERTTIError.Create('FPC not supports record fields in RTTI');
  145. {$ENDIF}
  146. end
  147. else
  148. begin
  149. prop := rtype.GetProperty(propname);
  150. if prop = nil then Exit;
  151. value := prop.GetValue(aInstance);
  152. end;
  153. if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
  154. else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
  155. until i < 0;
  156. Result := prop;
  157. end;
  158. class function TRTTI.PathExists(aInstance: TObject; const aPropertyPath: string) : Boolean;
  159. var
  160. proppath : string;
  161. propname : string;
  162. i : Integer;
  163. value : TValue;
  164. rtype : TRttiType;
  165. rprop : TRttiProperty;
  166. {$IFNDEF FPC}
  167. rfield : TRttiField;
  168. {$ENDIF}
  169. lastsegment : Boolean;
  170. begin
  171. if not Assigned(aInstance) then Exit;
  172. lastsegment := False;
  173. proppath := aPropertyPath;
  174. rtype := fCtx.GetType(aInstance.ClassType);
  175. repeat
  176. Result := False;
  177. i := proppath.IndexOf('.');
  178. if i > -1 then
  179. begin
  180. propname := Copy(proppath,1,i);
  181. Delete(proppath,1,i+1);
  182. end
  183. else
  184. begin
  185. propname := proppath;
  186. lastsegment := True;
  187. end;
  188. if rtype.TypeKind = TTypeKind.tkRecord then
  189. begin
  190. {$IFNDEF FPC}
  191. rfield := rtype.GetField(propname);
  192. if rfield = nil then Exit
  193. else
  194. begin
  195. value := rfield.GetValue(value.GetReferenceToRawData);
  196. Result := True;
  197. end;
  198. {$ELSE}
  199. raise ERTTIError.Create('FPC not supports record fields in RTTI');
  200. {$ENDIF}
  201. end
  202. else
  203. begin
  204. rprop := rtype.GetProperty(propname);
  205. if rprop = nil then Exit
  206. else
  207. begin
  208. value := rprop.GetValue(aInstance);
  209. Result := True;
  210. end;
  211. end;
  212. if not lastsegment then
  213. begin
  214. if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
  215. else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
  216. end;
  217. until lastsegment;
  218. end;
  219. class function TRTTI.GetPathValue(aInstance: TObject; const aPropertyPath: string): TValue;
  220. var
  221. proppath : string;
  222. propname : string;
  223. i : Integer;
  224. value : TValue;
  225. rtype : TRttiType;
  226. rprop : TRttiProperty;
  227. {$IFNDEF FPC}
  228. rfield : TRttiField;
  229. {$ENDIF}
  230. lastsegment : Boolean;
  231. obj : TObject;
  232. begin
  233. Result := nil;
  234. if not Assigned(aInstance) then Exit;
  235. lastsegment := False;
  236. proppath := aPropertyPath;
  237. rtype := fCtx.GetType(aInstance.ClassType);
  238. repeat
  239. i := proppath.IndexOf('.');
  240. if i > -1 then
  241. begin
  242. propname := Copy(proppath,1,i);
  243. Delete(proppath,1,i+1);
  244. end
  245. else
  246. begin
  247. propname := proppath;
  248. lastsegment := True;
  249. end;
  250. if rtype.TypeKind = TTypeKind.tkRecord then
  251. begin
  252. {$IFNDEF FPC}
  253. rfield := rtype.GetField(propname);
  254. if rfield = nil then raise ERTTIError.CreateFmt('Field "%s" not found in record',[propname])
  255. else value := rfield.GetValue(value.GetReferenceToRawData);
  256. {$ELSE}
  257. raise ERTTIError.Create('FPC not supports record fields in RTTI');
  258. {$ENDIF}
  259. end
  260. else
  261. begin
  262. rprop := rtype.GetProperty(propname);
  263. if rprop = nil then raise ERTTIError.CreateFmt('Property "%s" not found in object',[propname])
  264. else value := rprop.GetValue(aInstance);
  265. end;
  266. if not lastsegment then
  267. begin
  268. if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
  269. else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
  270. end;
  271. until lastsegment;
  272. Result := value;
  273. end;
  274. class procedure TRTTI.SetPathValue(aInstance: TObject; const aPropertyPath: string; aValue : TValue);
  275. var
  276. proppath : string;
  277. propname : string;
  278. i : Integer;
  279. value : TValue;
  280. rtype : TRttiType;
  281. rprop : TRttiProperty;
  282. {$IFNDEF FPC}
  283. rfield : TRttiField;
  284. {$ENDIF}
  285. lastsegment : Boolean;
  286. begin
  287. if not Assigned(aInstance) then Exit;
  288. lastsegment := False;
  289. proppath := aPropertyPath;
  290. rtype := fCtx.GetType(aInstance.ClassType);
  291. repeat
  292. i := proppath.IndexOf('.');
  293. if i > -1 then
  294. begin
  295. propname := Copy(proppath,1,i);
  296. Delete(proppath,1,i+1);
  297. end
  298. else
  299. begin
  300. propname := proppath;
  301. lastsegment := True;
  302. end;
  303. if rtype.TypeKind = TTypeKind.tkRecord then
  304. begin
  305. {$IFNDEF FPC}
  306. rfield := rtype.GetField(propname);
  307. if rfield = nil then raise ERTTIError.CreateFmt('Field "%s" not found in record',[propname])
  308. else
  309. begin
  310. if lastsegment then rfield.SetValue(value.GetReferenceToRawData,aValue)
  311. else value := rfield.GetValue(value.GetReferenceToRawData);
  312. end;
  313. {$ELSE}
  314. raise ERTTIError.Create('FPC not supports record fields in RTTI');
  315. {$ENDIF}
  316. end
  317. else
  318. begin
  319. rprop := rtype.GetProperty(propname);
  320. if rprop = nil then raise ERTTIError.CreateFmt('Property "%s" not found in object',[propname])
  321. else
  322. begin
  323. if lastsegment then rprop.SetValue(aInstance,aValue)
  324. else value := rprop.GetValue(aInstance);
  325. end;
  326. end;
  327. if not lastsegment then
  328. begin
  329. if value.Kind = TTypeKind.tkClass then rType := fCtx.GetType(value.AsObject.ClassType)
  330. else if value.Kind = TTypeKind.tkRecord then rtype := fCtx.GetType(value.TypeInfo);
  331. end;
  332. until lastsegment;
  333. end;
  334. class function TRTTI.GetPropertyValue(aInstance: TObject; const aPropertyName: string): TValue;
  335. var
  336. rprop : TRttiProperty;
  337. begin
  338. rprop := GetProperty(aInstance,aPropertyName);
  339. if rprop <> nil then Result := rprop.GetValue(aInstance);
  340. end;
  341. class function TRTTI.GetPropertyValue(aTypeInfo: Pointer; const aPropertyName: string): TValue;
  342. var
  343. rprop : TRttiProperty;
  344. begin
  345. rprop := GetProperty(aTypeInfo,aPropertyName);
  346. if rprop <> nil then Result := rprop.GetValue(aTypeInfo);
  347. end;
  348. class function TRTTI.GetType(aTypeInfo: Pointer): TRttiType;
  349. begin
  350. Result := fCtx.GetType(aTypeInfo);
  351. end;
  352. class function TRTTI.PropertyExists(aTypeInfo: Pointer; const aPropertyName: string): Boolean;
  353. begin
  354. Result := fCtx.GetType(aTypeInfo).GetProperty(aPropertyName) <> nil;
  355. end;
  356. class procedure TRTTI.SetPropertyValue(aInstance: TObject; const aPropertyName: string; aValue: TValue);
  357. var
  358. rprop : TRttiProperty;
  359. begin
  360. rprop := GetProperty(aInstance,aPropertyName);
  361. if rprop <> nil then rprop.SetValue(aInstance,aValue);
  362. end;
  363. {$IFNDEF FPC}
  364. class function TRTTI.FindClass(const aClassName: string): TClass;
  365. var
  366. rType : TRttiType;
  367. rList : TArray<TRttiType>;
  368. begin
  369. Result := nil;
  370. rList := fCtx.GetTypes;
  371. for rType in rList do
  372. begin
  373. if (rType.IsInstance) and (aClassName.EndsWith(rType.Name)) then
  374. begin
  375. Result := rType.AsInstance.MetaClassType;
  376. Break;
  377. end;
  378. end;
  379. end;
  380. {$ENDIF}
  381. end.