rttiutils.pp 26 KB

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