rttiutils.pp 26 KB

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