rttiutils.pp 26 KB

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