rttiutils.pp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2004 by the Free Pascal development team
  4. Some RTTI utils, based on RX rtti utils.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. { **********************************************************************
  12. Based on the rttiutils unit that comes with RXLib.
  13. Adapted to work with FCL, free of VCL dependencies.
  14. Fixed some errors along the way as well. MVC.
  15. To make it work across the 'Root Component' (Form/Datamodule etc),
  16. you MUST set the FindGlobalComponentCallBack event handler.
  17. Original copyright:
  18. Delphi VCL Extensions (RX)
  19. Copyright (c) 1995, 1996 AO ROSNO
  20. Copyright (c) 1997 Master-Bank
  21. **********************************************************************}
  22. {$mode objfpc}
  23. {$H+}
  24. {$IFNDEF FPC_DOTTEDUNITS}
  25. unit RttiUtils;
  26. {$ENDIF FPC_DOTTEDUNITS}
  27. interface
  28. {$IFDEF FPC_DOTTEDUNITS}
  29. uses
  30. System.SysUtils, System.Classes, {Graphics, MacOsApi.Controls, Forms,} System.TypInfo, System.StrUtils;
  31. {$ELSE FPC_DOTTEDUNITS}
  32. uses
  33. SysUtils, Classes, {Graphics, Controls, Forms,} TypInfo, StrUtils;
  34. {$ENDIF FPC_DOTTEDUNITS}
  35. type
  36. { TPropInfoList }
  37. TPropInfoList = class(TObject)
  38. private
  39. FList: PPropList;
  40. FCount: Integer;
  41. FSize: Integer;
  42. function Get(Index: Integer): PPropInfo;
  43. public
  44. constructor Create(AObject: TObject; Filter: TTypeKinds; Sorted: Boolean = True);
  45. destructor Destroy; override;
  46. function Contains(P: PPropInfo): Boolean;
  47. function Find(const AName: string): PPropInfo;
  48. procedure Delete(Index: Integer);
  49. procedure Intersect(List: TPropInfoList);
  50. property Count: Integer read FCount;
  51. property Items[Index: Integer]: PPropInfo read Get; default;
  52. end;
  53. { TPropsStorage }
  54. TReadStrEvent = function(const ASection, Item, Default: string): string of object;
  55. TWriteStrEvent = procedure(const ASection, Item, Value: string) of object;
  56. TEraseSectEvent = procedure(const ASection: string) of object;
  57. TPropStorageOption = (psoAlwaysStoreStringsCount);
  58. TPropStorageOptions = set of TPropStorageOption;
  59. TPropsStorage = class(TObject)
  60. private
  61. FObject: TObject;
  62. FOwner: TComponent;
  63. FPrefix: string;
  64. FSection: string;
  65. FOptions : TPropStorageOptions;
  66. FOnReadString: TReadStrEvent;
  67. FOnWriteString: TWriteStrEvent;
  68. FOnEraseSection: TEraseSectEvent;
  69. function StoreIntegerProperty(PropInfo: PPropInfo): string;
  70. function StoreCharProperty(PropInfo: PPropInfo): string;
  71. function StoreEnumProperty(PropInfo: PPropInfo): string;
  72. function StoreFloatProperty(PropInfo: PPropInfo): string;
  73. function StoreStringProperty(PropInfo: PPropInfo): string;
  74. function StoreSetProperty(PropInfo: PPropInfo): string;
  75. function StoreClassProperty(PropInfo: PPropInfo): string;
  76. function StoreStringsProperty(PropInfo: PPropInfo): string;
  77. function StoreComponentProperty(PropInfo: PPropInfo): string;
  78. function StoreLStringProperty(PropInfo: PPropInfo): string;
  79. function StoreWCharProperty(PropInfo: PPropInfo): string;
  80. function StoreVariantProperty(PropInfo: PPropInfo): string;
  81. procedure LoadLStringProperty(const S: string; PropInfo: PPropInfo);
  82. procedure LoadWCharProperty(const S: string; PropInfo: PPropInfo);
  83. procedure LoadVariantProperty(const S: string; PropInfo: PPropInfo);
  84. function StoreInt64Property(PropInfo: PPropInfo): string;
  85. procedure LoadInt64Property(const S: string; PropInfo: PPropInfo);
  86. procedure LoadIntegerProperty(const S: string; PropInfo: PPropInfo);
  87. procedure LoadCharProperty(const S: string; PropInfo: PPropInfo);
  88. procedure LoadEnumProperty(const S: string; PropInfo: PPropInfo);
  89. procedure LoadFloatProperty(const S: string; PropInfo: PPropInfo);
  90. procedure LoadStringProperty(const S: string; PropInfo: PPropInfo);
  91. procedure LoadSetProperty(const S: string; PropInfo: PPropInfo);
  92. procedure LoadClassProperty(const S: string; PropInfo: PPropInfo);
  93. procedure LoadStringsProperty(const S: string; PropInfo: PPropInfo);
  94. procedure LoadComponentProperty(const S: string; PropInfo: PPropInfo);
  95. function CreateInfoList(AComponent: TComponent; StoredList: TStrings): TStrings;
  96. procedure FreeInfoLists(Info: TStrings);
  97. protected
  98. function ReadString(const ASection, Item, Default: string): string; virtual;
  99. procedure WriteString(const ASection, Item, Value: string); virtual;
  100. procedure EraseSection(const ASection: string); virtual;
  101. function GetItemName(const APropName: string): string; virtual;
  102. function CreateStorage: TPropsStorage; virtual;
  103. public
  104. procedure StoreAnyProperty(PropInfo: PPropInfo);
  105. procedure LoadAnyProperty(PropInfo: PPropInfo);
  106. procedure StoreProperties(PropList: TStrings);
  107. procedure LoadProperties(PropList: TStrings);
  108. procedure LoadObjectsProps(AComponent: TComponent; StoredList: TStrings);
  109. procedure StoreObjectsProps(AComponent: TComponent; StoredList: TStrings);
  110. Property Options : TPropStorageOptions Read FOptions Write FOptions;
  111. property AObject: TObject read FObject write FObject;
  112. property Prefix: string read FPrefix write FPrefix;
  113. property Section: string read FSection write FSection;
  114. property OnReadString: TReadStrEvent read FOnReadString write FOnReadString;
  115. property OnWriteString: TWriteStrEvent read FOnWriteString write FOnWriteString;
  116. property OnEraseSection: TEraseSectEvent read FOnEraseSection write FOnEraseSection;
  117. end;
  118. { Utility routines }
  119. procedure UpdateStoredList(AComponent: TComponent; AStoredList: TStrings; FromForm: Boolean);
  120. function CreateStoredItem(const CompName, PropName: string): string;
  121. function ParseStoredItem(const Item: string; var CompName, PropName: string): Boolean;
  122. const
  123. sPropNameDelimiter: string = '_';
  124. Type
  125. TFindComponentEvent = Function (Const Name : String) : TComponent;
  126. Var
  127. FindGlobalComponentCallBack : TFindComponentEvent;
  128. implementation
  129. const
  130. sCount = 'Count';
  131. sItem = 'Item%d';
  132. sNull = '(null)';
  133. type
  134. TCardinalSet = set of 0..SizeOf(Cardinal) * 8 - 1;
  135. function GetPropType(PropInfo: PPropInfo): PTypeInfo;
  136. begin
  137. Result := PropInfo^.PropType;
  138. end;
  139. { TPropInfoList }
  140. constructor TPropInfoList.Create(AObject: TObject; Filter: TTypeKinds; Sorted: Boolean);
  141. begin
  142. if AObject <> nil then
  143. begin
  144. FCount := GetPropList(AObject.ClassInfo, Filter, nil, Sorted);
  145. FSize := FCount * SizeOf(Pointer);
  146. GetMem(FList, FSize);
  147. GetPropList(AObject.ClassInfo, Filter, FList, Sorted);
  148. end
  149. else
  150. begin
  151. FCount := 0;
  152. FList := nil;
  153. end;
  154. end;
  155. destructor TPropInfoList.Destroy;
  156. begin
  157. if FList <> nil then FreeMem(FList, FSize);
  158. end;
  159. function TPropInfoList.Contains(P: PPropInfo): Boolean;
  160. var
  161. I: Integer;
  162. begin
  163. for I := 0 to FCount - 1 do
  164. with FList^[I]^ do
  165. if (PropType = P^.PropType) and (CompareText(Name, P^.Name) = 0) then
  166. begin
  167. Result := True;
  168. Exit;
  169. end;
  170. Result := False;
  171. end;
  172. function TPropInfoList.Find(const AName: string): PPropInfo;
  173. var
  174. I: Integer;
  175. begin
  176. for I := 0 to FCount - 1 do
  177. with FList^[I]^ do
  178. if (CompareText(Name, AName) = 0) then
  179. begin
  180. Result := FList^[I];
  181. Exit;
  182. end;
  183. Result := nil;
  184. end;
  185. procedure TPropInfoList.Delete(Index: Integer);
  186. begin
  187. Dec(FCount);
  188. if Index < FCount then Move(FList^[Index + 1], FList^[Index],
  189. (FCount - Index) * SizeOf(Pointer));
  190. end;
  191. function TPropInfoList.Get(Index: Integer): PPropInfo;
  192. begin
  193. Result := FList^[Index];
  194. end;
  195. procedure TPropInfoList.Intersect(List: TPropInfoList);
  196. var
  197. I: Integer;
  198. begin
  199. for I := FCount - 1 downto 0 do
  200. if not List.Contains(FList^[I]) then Delete(I);
  201. end;
  202. { Utility routines }
  203. function CreateStoredItem(const CompName, PropName: string): string;
  204. begin
  205. Result := '';
  206. if (CompName <> '') and (PropName <> '') then
  207. Result := CompName + '.' + PropName;
  208. end;
  209. function ParseStoredItem(const Item: string; var CompName, PropName: string): Boolean;
  210. var
  211. I: Integer;
  212. begin
  213. Result := False;
  214. if Length(Item) = 0 then Exit;
  215. I := Pos('.', Item);
  216. if I > 0 then begin
  217. CompName := Trim(Copy(Item, 1, I - 1));
  218. PropName := Trim(Copy(Item, I + 1, MaxInt));
  219. Result := (Length(CompName) > 0) and (Length(PropName) > 0);
  220. end;
  221. end;
  222. function ReplaceComponentName(const Item, CompName: string): string;
  223. var
  224. ACompName, APropName: string;
  225. begin
  226. Result := '';
  227. if ParseStoredItem(Item, ACompName, APropName) then
  228. Result := CreateStoredItem(CompName, APropName);
  229. end;
  230. procedure UpdateStoredList(AComponent: TComponent; AStoredList: TStrings; FromForm: Boolean);
  231. var
  232. I: Integer;
  233. Component: TComponent;
  234. CompName, PropName: string;
  235. begin
  236. if (AStoredList = nil) or (AComponent = nil) then
  237. Exit;
  238. for I := AStoredList.Count - 1 downto 0 do
  239. begin
  240. if ParseStoredItem(AStoredList[I], CompName, PropName) then
  241. begin
  242. if FromForm then
  243. begin
  244. Component := AComponent.FindComponent(CompName);
  245. if Component = nil then
  246. AStoredList.Delete(I)
  247. else
  248. AStoredList.Objects[I]:=Component;
  249. end
  250. else
  251. begin
  252. Component := TComponent(AStoredList.Objects[I]);
  253. if Component <> nil then
  254. AStoredList[I] := ReplaceComponentName(AStoredList[I], Component.Name)
  255. else
  256. AStoredList.Delete(I);
  257. end;
  258. end
  259. else
  260. AStoredList.Delete(I);
  261. end;
  262. end;
  263. function FindGlobalComponent(const Name: string): TComponent;
  264. begin
  265. Result:=Nil;
  266. If Assigned(FindGlobalComponentCallBack) then
  267. Result:=FindGlobalComponentCallBack(Name);
  268. end;
  269. { TPropsStorage }
  270. function TPropsStorage.GetItemName(const APropName: string): string;
  271. begin
  272. Result := Prefix + APropName;
  273. end;
  274. procedure TPropsStorage.LoadAnyProperty(PropInfo: PPropInfo);
  275. var
  276. S, Def: string;
  277. begin
  278. try
  279. if PropInfo <> nil then
  280. begin
  281. case PropInfo^.PropType^.Kind of
  282. tkBool,
  283. tkInteger: Def := StoreIntegerProperty(PropInfo);
  284. tkChar: Def := StoreCharProperty(PropInfo);
  285. tkEnumeration: Def := StoreEnumProperty(PropInfo);
  286. tkFloat: Def := StoreFloatProperty(PropInfo);
  287. tkWChar: Def := StoreWCharProperty(PropInfo);
  288. tkAstring,
  289. tkLString: Def := StoreLStringProperty(PropInfo);
  290. tkWString: Def := StoreLStringProperty(PropInfo);
  291. tkVariant: Def := StoreVariantProperty(PropInfo);
  292. tkInt64: Def := StoreInt64Property(PropInfo);
  293. tkString: Def := StoreStringProperty(PropInfo);
  294. tkSet: Def := StoreSetProperty(PropInfo);
  295. tkClass: Def := '';
  296. else
  297. Exit;
  298. end;
  299. if (Def <> '') or (PropInfo^.PropType^.Kind in [tkString, tkClass])
  300. or (PropInfo^.PropType^.Kind in [tkAString,tkLString, tkWString, tkWChar]) then
  301. S := Trim(ReadString(Section, GetItemName(PropInfo^.Name), Def))
  302. else
  303. S := '';
  304. case PropInfo^.PropType^.Kind of
  305. tkBool:LoadIntegerProperty(S,PropInfo);
  306. tkInteger: LoadIntegerProperty(S, PropInfo);
  307. tkChar: LoadCharProperty(S, PropInfo);
  308. tkEnumeration: LoadEnumProperty(S, PropInfo);
  309. tkFloat: LoadFloatProperty(S, PropInfo);
  310. tkWChar: LoadWCharProperty(S, PropInfo);
  311. tkAString,
  312. tkLString: LoadLStringProperty(S, PropInfo);
  313. tkWString: LoadLStringProperty(S, PropInfo);
  314. tkVariant: LoadVariantProperty(S, PropInfo);
  315. tkInt64: LoadInt64Property(S, PropInfo);
  316. tkString: LoadStringProperty(S, PropInfo);
  317. tkSet: LoadSetProperty(S, PropInfo);
  318. tkClass: LoadClassProperty(S, PropInfo);
  319. else
  320. Exit;
  321. end;
  322. end;
  323. except
  324. { ignore any exception }
  325. end;
  326. end;
  327. procedure TPropsStorage.StoreAnyProperty(PropInfo: PPropInfo);
  328. var
  329. S: string;
  330. begin
  331. if PropInfo <> nil then
  332. begin
  333. case PropInfo^.PropType^.Kind of
  334. tkInteger: S := StoreIntegerProperty(PropInfo);
  335. tkChar: S := StoreCharProperty(PropInfo);
  336. tkEnumeration: S := StoreEnumProperty(PropInfo);
  337. tkFloat: S := StoreFloatProperty(PropInfo);
  338. tkAstring: S := StoreLStringProperty(PropInfo);
  339. tkWString: S := StoreLStringProperty(PropInfo);
  340. tkWChar: S := StoreWCharProperty(PropInfo);
  341. tkVariant: S := StoreVariantProperty(PropInfo);
  342. tkInt64: S := StoreInt64Property(PropInfo);
  343. tkString: S := StoreStringProperty(PropInfo);
  344. tkSet: S := StoreSetProperty(PropInfo);
  345. tkClass: S := StoreClassProperty(PropInfo);
  346. tkBool: S:=StoreIntegerProperty(PropInfo);
  347. else
  348. Exit;
  349. end;
  350. if (S <> '') or (PropInfo^.PropType^.Kind in [tkString
  351. , tkLString, tkAString, tkWString, tkWChar ]) then
  352. WriteString(Section, GetItemName(PropInfo^.Name), Trim(S));
  353. end;
  354. end;
  355. function TPropsStorage.StoreIntegerProperty(PropInfo: PPropInfo): string;
  356. begin
  357. Result := IntToStr(GetOrdProp(FObject, PropInfo));
  358. end;
  359. function TPropsStorage.StoreCharProperty(PropInfo: PPropInfo): string;
  360. begin
  361. Result := Char(GetOrdProp(FObject, PropInfo));
  362. end;
  363. function TPropsStorage.StoreEnumProperty(PropInfo: PPropInfo): string;
  364. begin
  365. Result := GetEnumName(GetPropType(PropInfo), GetOrdProp(FObject, PropInfo));
  366. end;
  367. function TPropsStorage.StoreFloatProperty(PropInfo: PPropInfo): string;
  368. const
  369. Precisions: array[TFloatType] of Integer = (7, 15, 18, 18, 19);
  370. begin
  371. Result := StringReplace(FloatToStrF(GetFloatProp(FObject, PropInfo), ffGeneral,
  372. Precisions[GetTypeData(GetPropType(PropInfo))^.FloatType], 0),
  373. DecimalSeparator, '.',[rfReplaceAll]);
  374. end;
  375. function TPropsStorage.StoreStringProperty(PropInfo: PPropInfo): string;
  376. begin
  377. Result := GetStrProp(FObject, PropInfo);
  378. end;
  379. function TPropsStorage.StoreLStringProperty(PropInfo: PPropInfo): string;
  380. begin
  381. Result := GetStrProp(FObject, PropInfo);
  382. end;
  383. function TPropsStorage.StoreWCharProperty(PropInfo: PPropInfo): string;
  384. begin
  385. Result := Char(GetOrdProp(FObject, PropInfo));
  386. end;
  387. function TPropsStorage.StoreVariantProperty(PropInfo: PPropInfo): string;
  388. begin
  389. Result := GetVariantProp(FObject, PropInfo);
  390. end;
  391. function TPropsStorage.StoreInt64Property(PropInfo: PPropInfo): string;
  392. begin
  393. Result := IntToStr(GetInt64Prop(FObject, PropInfo));
  394. end;
  395. function TPropsStorage.StoreSetProperty(PropInfo: PPropInfo): string;
  396. var
  397. TypeInfo: PTypeInfo;
  398. W: Cardinal;
  399. I: Integer;
  400. begin
  401. Result := '[';
  402. W := GetOrdProp(FObject, PropInfo);
  403. TypeInfo := GetTypeData(GetPropType(PropInfo))^.CompType;
  404. for I := 0 to SizeOf(TCardinalSet) * 8 - 1 do
  405. if I in TCardinalSet(W) then begin
  406. if Length(Result) <> 1 then Result := Result + ',';
  407. Result := Result + GetEnumName(TypeInfo, I);
  408. end;
  409. Result := Result + ']';
  410. end;
  411. function TPropsStorage.StoreStringsProperty(PropInfo: PPropInfo): string;
  412. var
  413. List: TObject;
  414. I: Integer;
  415. SectName: string;
  416. begin
  417. Result := '';
  418. List := TObject(GetObjectProp(Self.FObject, PropInfo));
  419. SectName := Format('%s.%s', [Section, GetItemName(PropInfo^.Name)]);
  420. EraseSection(SectName);
  421. if (List is TStrings)
  422. and ((TStrings(List).Count > 0) or (psoAlwaysStoreStringsCount in Options)) then
  423. begin
  424. WriteString(SectName, sCount, IntToStr(TStrings(List).Count));
  425. for I := 0 to TStrings(List).Count - 1 do
  426. WriteString(SectName, Format(sItem, [I]), TStrings(List)[I]);
  427. end;
  428. end;
  429. function TPropsStorage.StoreComponentProperty(PropInfo: PPropInfo): string;
  430. var
  431. Comp: TComponent;
  432. RootName: string;
  433. begin
  434. Comp := TComponent(GetObjectProp(FObject, PropInfo));
  435. if Comp <> nil then begin
  436. Result := Comp.Name;
  437. if (Comp.Owner <> nil) and (Comp.Owner <> FOwner) then begin
  438. RootName := Comp.Owner.Name;
  439. if RootName = '' then begin
  440. RootName := Comp.Owner.ClassName;
  441. if (RootName <> '') and (UpCase(RootName[1]) = 'T') then
  442. Delete(RootName, 1, 1);
  443. end;
  444. Result := Format('%s.%s', [RootName, Result]);
  445. end;
  446. end
  447. else Result := sNull;
  448. end;
  449. function TPropsStorage.StoreClassProperty(PropInfo: PPropInfo): string;
  450. var
  451. Saver: TPropsStorage;
  452. I: Integer;
  453. Obj: TObject;
  454. procedure StoreObjectProps(Obj: TObject; const APrefix, ASection: string);
  455. var
  456. I: Integer;
  457. Props: TPropInfoList;
  458. begin
  459. with Saver do begin
  460. AObject := Obj;
  461. Prefix := APrefix;
  462. Section := ASection;
  463. FOnWriteString := Self.FOnWriteString;
  464. FOnEraseSection := Self.FOnEraseSection;
  465. Props := TPropInfoList.Create(AObject, tkProperties);
  466. try
  467. for I := 0 to Props.Count - 1 do StoreAnyProperty(Props.Items[I]);
  468. finally
  469. Props.Free;
  470. end;
  471. end;
  472. end;
  473. begin
  474. Result := '';
  475. Obj := TObject(GetObjectProp(Self.FObject, PropInfo));
  476. if (Obj <> nil) then begin
  477. if Obj is TStrings then StoreStringsProperty(PropInfo)
  478. else if Obj is TCollection then begin
  479. EraseSection(Format('%s.%s', [Section, Prefix + PropInfo^.Name]));
  480. Saver := CreateStorage;
  481. try
  482. WriteString(Section, Format('%s.%s', [Prefix + PropInfo^.Name, sCount]),
  483. IntToStr(TCollection(Obj).Count));
  484. for I := 0 to TCollection(Obj).Count - 1 do begin
  485. StoreObjectProps(TCollection(Obj).Items[I],
  486. Format(sItem, [I]) + sPropNameDelimiter,
  487. Format('%s.%s', [Section, Prefix + PropInfo^.Name]));
  488. end;
  489. finally
  490. Saver.Free;
  491. end;
  492. end
  493. else if Obj is TComponent then begin
  494. Result := StoreComponentProperty(PropInfo);
  495. Exit;
  496. end;
  497. end;
  498. Saver := CreateStorage;
  499. try
  500. with Saver do begin
  501. StoreObjectProps(Obj, Self.Prefix + PropInfo^.Name, Self.Section);
  502. end;
  503. finally
  504. Saver.Free;
  505. end;
  506. end;
  507. procedure TPropsStorage.LoadIntegerProperty(const S: string; PropInfo: PPropInfo);
  508. begin
  509. SetOrdProp(FObject, PropInfo, StrToIntDef(S, 0));
  510. end;
  511. procedure TPropsStorage.LoadCharProperty(const S: string; PropInfo: PPropInfo);
  512. begin
  513. SetOrdProp(FObject, PropInfo, Integer(S[1]));
  514. end;
  515. procedure TPropsStorage.LoadEnumProperty(const S: string; PropInfo: PPropInfo);
  516. var
  517. I: Integer;
  518. EnumType: PTypeInfo;
  519. begin
  520. EnumType := GetPropType(PropInfo);
  521. with GetTypeData(EnumType)^ do
  522. for I := MinValue to MaxValue do
  523. if CompareText(GetEnumName(EnumType, I), S) = 0 then
  524. begin
  525. SetOrdProp(FObject, PropInfo, I);
  526. Exit;
  527. end;
  528. end;
  529. procedure TPropsStorage.LoadFloatProperty(const S: string; PropInfo: PPropInfo);
  530. begin
  531. SetFloatProp(FObject, PropInfo, StrToFloat(StringReplace(S, '.',
  532. DecimalSeparator,[rfReplaceAll])));
  533. end;
  534. procedure TPropsStorage.LoadInt64Property(const S: string; PropInfo: PPropInfo);
  535. begin
  536. SetInt64Prop(FObject, PropInfo, StrToInt64Def(S, 0));
  537. end;
  538. procedure TPropsStorage.LoadLStringProperty(const S: string; PropInfo: PPropInfo);
  539. begin
  540. SetStrProp(FObject, PropInfo, S);
  541. end;
  542. procedure TPropsStorage.LoadWCharProperty(const S: string; PropInfo: PPropInfo);
  543. begin
  544. SetOrdProp(FObject, PropInfo, Longint(S[1]));
  545. end;
  546. procedure TPropsStorage.LoadVariantProperty(const S: string; PropInfo: PPropInfo);
  547. begin
  548. SetVariantProp(FObject, PropInfo, S);
  549. end;
  550. procedure TPropsStorage.LoadStringProperty(const S: string; PropInfo: PPropInfo);
  551. begin
  552. SetStrProp(FObject, PropInfo, S);
  553. end;
  554. procedure TPropsStorage.LoadSetProperty(const S: string; PropInfo: PPropInfo);
  555. const
  556. Delims = [' ', ',', '[', ']'];
  557. var
  558. TypeInfo: PTypeInfo;
  559. W: Cardinal;
  560. I, N: Integer;
  561. Count: Integer;
  562. EnumName: string;
  563. begin
  564. W := 0;
  565. TypeInfo := GetTypeData(GetPropType(PropInfo))^.CompType;
  566. Count := WordCount(S, Delims);
  567. for N := 1 to Count do begin
  568. EnumName := ExtractWord(N, S, Delims);
  569. try
  570. I := GetEnumValue(TypeInfo, EnumName);
  571. if I >= 0 then Include(TCardinalSet(W), I);
  572. except
  573. end;
  574. end;
  575. SetOrdProp(FObject, PropInfo, W);
  576. end;
  577. procedure TPropsStorage.LoadStringsProperty(const S: string; PropInfo: PPropInfo);
  578. var
  579. List: TObject;
  580. Temp: TStrings;
  581. I, Cnt: Integer;
  582. SectName: string;
  583. begin
  584. List := TObject(GetObjectProp(Self.FObject, PropInfo));
  585. if (List is TStrings) then begin
  586. SectName := Format('%s.%s', [Section, GetItemName(PropInfo^.Name)]);
  587. Cnt := StrToIntDef(Trim(ReadString(SectName, sCount, '0')), 0);
  588. if Cnt > 0 then begin
  589. Temp := TStringList.Create;
  590. try
  591. for I := 0 to Cnt - 1 do
  592. Temp.Add(ReadString(SectName, Format(sItem, [I]), ''));
  593. TStrings(List).Assign(Temp);
  594. finally
  595. Temp.Free;
  596. end;
  597. end;
  598. end;
  599. end;
  600. procedure TPropsStorage.LoadComponentProperty(const S: string; PropInfo: PPropInfo);
  601. var
  602. RootName, Name: string;
  603. Root: TComponent;
  604. P: Integer;
  605. begin
  606. if Trim(S) = '' then Exit;
  607. if CompareText(SNull, Trim(S)) = 0 then begin
  608. SetOrdProp(FObject, PropInfo, Longint(nil));
  609. Exit;
  610. end;
  611. P := Pos('.', S);
  612. if P > 0 then begin
  613. RootName := Trim(Copy(S, 1, P - 1));
  614. Name := Trim(Copy(S, P + 1, MaxInt));
  615. end
  616. else begin
  617. RootName := '';
  618. Name := Trim(S);
  619. end;
  620. if RootName <> '' then Root := FindGlobalComponent(RootName)
  621. else Root := FOwner;
  622. if (Root <> nil) then
  623. SetObjectProp(FObject, PropInfo, Root.FindComponent(Name));
  624. end;
  625. procedure TPropsStorage.LoadClassProperty(const S: string; PropInfo: PPropInfo);
  626. var
  627. Loader: TPropsStorage;
  628. I: Integer;
  629. Cnt: Integer;
  630. Recreate: Boolean;
  631. Obj: TObject;
  632. procedure LoadObjectProps(Obj: TObject; const APrefix, ASection: string);
  633. var
  634. I: Integer;
  635. Props: TPropInfoList;
  636. begin
  637. with Loader do begin
  638. AObject := Obj;
  639. Prefix := APrefix;
  640. Section := ASection;
  641. FOnReadString := Self.FOnReadString;
  642. Props := TPropInfoList.Create(AObject, tkProperties);
  643. try
  644. for I := 0 to Props.Count - 1 do LoadAnyProperty(Props.Items[I]);
  645. finally
  646. Props.Free;
  647. end;
  648. end;
  649. end;
  650. begin
  651. Obj := TObject(GetObjectProp(Self.FObject, PropInfo));
  652. if (Obj <> nil) then begin
  653. if Obj is TStrings then LoadStringsProperty(S, PropInfo)
  654. else if Obj is TCollection then begin
  655. Loader := CreateStorage;
  656. try
  657. Cnt := TCollection(Obj).Count;
  658. Cnt := StrToIntDef(ReadString(Section, Format('%s.%s',
  659. [Prefix + PropInfo^.Name, sCount]), IntToStr(Cnt)), Cnt);
  660. Recreate := TCollection(Obj).Count <> Cnt;
  661. TCollection(Obj).BeginUpdate;
  662. try
  663. if Recreate then TCollection(Obj).Clear;
  664. for I := 0 to Cnt - 1 do begin
  665. if Recreate then TCollection(Obj).Add;
  666. LoadObjectProps(TCollection(Obj).Items[I],
  667. Format(sItem, [I]) + sPropNameDelimiter,
  668. Format('%s.%s', [Section, Prefix + PropInfo^.Name]));
  669. end;
  670. finally
  671. TCollection(Obj).EndUpdate;
  672. end;
  673. finally
  674. Loader.Free;
  675. end;
  676. end
  677. else if Obj is TComponent then begin
  678. LoadComponentProperty(S, PropInfo);
  679. Exit;
  680. end;
  681. end;
  682. Loader := CreateStorage;
  683. try
  684. LoadObjectProps(Obj, Self.Prefix + PropInfo^.Name, Self.Section);
  685. finally
  686. Loader.Free;
  687. end;
  688. end;
  689. procedure TPropsStorage.StoreProperties(PropList: TStrings);
  690. var
  691. I: Integer;
  692. Props: TPropInfoList;
  693. begin
  694. Props := TPropInfoList.Create(AObject, tkProperties);
  695. try
  696. for I := 0 to PropList.Count - 1 do
  697. StoreAnyProperty(Props.Find(PropList[I]));
  698. finally
  699. Props.Free;
  700. end;
  701. end;
  702. procedure TPropsStorage.LoadProperties(PropList: TStrings);
  703. var
  704. I: Integer;
  705. Props: TPropInfoList;
  706. begin
  707. Props := TPropInfoList.Create(AObject, tkProperties);
  708. try
  709. for I := 0 to PropList.Count - 1 do
  710. LoadAnyProperty(Props.Find(PropList[I]));
  711. finally
  712. Props.Free;
  713. end;
  714. end;
  715. function TPropsStorage.CreateInfoList(AComponent: TComponent; StoredList: TStrings): TStrings;
  716. var
  717. I: Integer;
  718. Obj: TComponent;
  719. Props: TPropInfoList;
  720. begin
  721. UpdateStoredList(AComponent, StoredList, False);
  722. Result := TStringList.Create;
  723. try
  724. TStringList(Result).Sorted := True;
  725. for I := 0 to StoredList.Count - 1 do
  726. begin
  727. Obj := TComponent(StoredList.Objects[I]);
  728. if Result.IndexOf(Obj.Name) < 0 then
  729. begin
  730. Props := TPropInfoList.Create(Obj, tkProperties);
  731. try
  732. Result.AddObject(Obj.Name, Props);
  733. except
  734. Props.Free;
  735. raise;
  736. end;
  737. end;
  738. end;
  739. except
  740. On E : Exception do
  741. begin
  742. Result.Free;
  743. Result := nil;
  744. end;
  745. end;
  746. end;
  747. procedure TPropsStorage.FreeInfoLists(Info: TStrings);
  748. var
  749. I: Integer;
  750. begin
  751. for I := Info.Count - 1 downto 0 do Info.Objects[I].Free;
  752. Info.Free;
  753. end;
  754. procedure TPropsStorage.LoadObjectsProps(AComponent: TComponent; StoredList: TStrings);
  755. var
  756. Info: TStrings;
  757. I, Idx: Integer;
  758. Props: TPropInfoList;
  759. CompName, PropName: string;
  760. begin
  761. Info := CreateInfoList(AComponent, StoredList);
  762. if Info <> nil then
  763. try
  764. FOwner := AComponent;
  765. for I := 0 to StoredList.Count - 1 do
  766. begin
  767. if ParseStoredItem(StoredList[I], CompName, PropName) then
  768. begin
  769. AObject := StoredList.Objects[I];
  770. Prefix := TComponent(AObject).Name;
  771. Idx := Info.IndexOf(Prefix);
  772. if Idx >= 0 then
  773. begin
  774. Prefix := Prefix + sPropNameDelimiter;
  775. Props := TPropInfoList(Info.Objects[Idx]);
  776. if Props <> nil then
  777. LoadAnyProperty(Props.Find(PropName));
  778. end;
  779. end;
  780. end;
  781. finally
  782. FOwner := nil;
  783. FreeInfoLists(Info);
  784. end;
  785. end;
  786. procedure TPropsStorage.StoreObjectsProps(AComponent: TComponent; StoredList: TStrings);
  787. var
  788. Info: TStrings;
  789. I, Idx: Integer;
  790. Props: TPropInfoList;
  791. CompName, PropName: string;
  792. begin
  793. Info := CreateInfoList(AComponent, StoredList);
  794. if Info <> nil then
  795. try
  796. FOwner := AComponent;
  797. for I := 0 to StoredList.Count - 1 do
  798. begin
  799. if ParseStoredItem(StoredList[I], CompName, PropName) then
  800. begin
  801. AObject := StoredList.Objects[I];
  802. Prefix := TComponent(AObject).Name;
  803. Idx := Info.IndexOf(Prefix);
  804. if Idx >= 0 then
  805. begin
  806. Prefix := Prefix + sPropNameDelimiter;
  807. Props := TPropInfoList(Info.Objects[Idx]);
  808. if Props <> nil then
  809. StoreAnyProperty(Props.Find(PropName));
  810. end;
  811. end;
  812. end;
  813. finally
  814. FOwner := nil;
  815. FreeInfoLists(Info);
  816. end;
  817. end;
  818. function TPropsStorage.CreateStorage: TPropsStorage;
  819. begin
  820. Result := TPropsStorage.Create;
  821. end;
  822. function TPropsStorage.ReadString(const ASection, Item, Default: string): string;
  823. begin
  824. if Assigned(FOnReadString) then Result := FOnReadString(ASection, Item, Default)
  825. else Result := '';
  826. end;
  827. procedure TPropsStorage.WriteString(const ASection, Item, Value: string);
  828. begin
  829. if Assigned(FOnWriteString) then FOnWriteString(ASection, Item, Value);
  830. end;
  831. procedure TPropsStorage.EraseSection(const ASection: string);
  832. begin
  833. if Assigned(FOnEraseSection) then FOnEraseSection(ASection);
  834. end;
  835. end.