Quick.AutoMapper.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. { ***************************************************************************
  2. Copyright (c) 2015-2021 Kike Pérez
  3. Unit : Quick.AutoMapper
  4. Description : Auto Mapper object properties
  5. Author : Kike Pérez
  6. Version : 1.5
  7. Created : 25/08/2018
  8. Modified : 07/04/2021
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.AutoMapper;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. SysUtils,
  26. Generics.Collections,
  27. typinfo,
  28. Quick.Value,
  29. {$IFDEF FPC}
  30. Variants,
  31. {$ENDIF}
  32. RTTI,
  33. Quick.RTTI.Utils;
  34. //enable use of property paths (like namespaces) in custom mapping
  35. {$DEFINE PROPERTYPATH_MODE}
  36. type
  37. {$IFNDEF FPC}
  38. TFlexValue = TValue;
  39. {$ELSE}
  40. TFlexValue = variant;
  41. {$ENDIF}
  42. {$IFNDEF FPC}
  43. TMappingProc<TClass1> = reference to procedure(const aSrcObj : TClass1; const aTargetName : string; out Value : TFlexValue);
  44. TAfterMappingProc<TClass1,TClass2> = reference to procedure(const aSrcObj : TClass1; aTgtObj : TClass2);
  45. {$ELSE}
  46. TMappingProc<TObject> = procedure(const aSrcObj : TObject; const aTargetName : string; out Value : TFlexValue) of object;
  47. TAfterMappingProc<TClass1,TClass2> = procedure(const aSrcObj : TClass1; aTgtObj : TClass2) of object;
  48. {$ENDIF}
  49. TCustomMapping = class
  50. private
  51. fMapDictionary : TDictionary<string,string>;
  52. public
  53. constructor Create;
  54. destructor Destroy; override;
  55. procedure AddMap(const aName, aMapName : string);
  56. function GetMap(const aName : string; out vMapName : string) : Boolean;
  57. function Count : Integer;
  58. end;
  59. TObjMapper = class
  60. public
  61. class procedure Map(aSrcObj : TObject; aTgtObj : TObject; aCustomMapping: TCustomMapping = nil); overload;
  62. {$IFNDEF FPC}
  63. class procedure Map<Tm>(aSrcObj : TObject; aTgtObj : TObject; aDoMappingProc : TMappingProc<Tm>; aCustomMapping: TCustomMapping = nil); overload;
  64. {$ELSE}
  65. class procedure Map(aSrcObj : TObject; aTgtObj : TObject; aDoMappingProc : TMappingProc<TObject>; aCustomMapping: TCustomMapping = nil); overload;
  66. {$ENDIF}
  67. end;
  68. TListMapper = class
  69. public
  70. class procedure Map(aSrcList, aTgtList: TObject; aCustomMapping: TCustomMapping);
  71. end;
  72. TObjListMapper = class
  73. public
  74. class procedure Map(aSrcObjList : TObject; aTgtObjList : TObject; aCustomMapping : TCustomMapping = nil); overload;
  75. {$IFNDEF FPC}
  76. class procedure Map<Tm>(aSrcObjList : TObject; aTgtObjList : TObject; aDoMappingProc : TMappingProc<Tm>; aCustomMapping : TCustomMapping = nil); overload;
  77. {$ELSE}
  78. class procedure Map(aSrcObjList : TObject; aTgtObjList : TObject; aDoMappingProc : TMappingProc<TObject>; aCustomMapping : TCustomMapping = nil); overload;
  79. {$ENDIF}
  80. end;
  81. {$IFNDEF FPC}
  82. TMapper = class
  83. public
  84. class function Map<T : class, constructor>(aSrcObj : TObject) : T;
  85. end;
  86. {$ENDIF}
  87. TMapper<T : class, constructor> = class
  88. public
  89. class function Map(aSrcObj : TObject; aCustomMapping: TCustomMapping = nil): T; overload;
  90. class procedure Map(aSrcObj : TObject; aTgtObj : T; aCustomMapping : TCustomMapping = nil); overload;
  91. {$IFNDEF FPC}
  92. class function Map<Tm>(aSrcObj : TObject; aDoMappingProc : TMappingProc<Tm>; aCustomMapping: TCustomMapping = nil): T; overload;
  93. class procedure Map<Tm>(aSrcObj : TObject; aTgtObj : T; aDoMappingProc : TMappingProc<Tm>; aCustomMapping: TCustomMapping = nil); overload;
  94. {$ELSE}
  95. class function Map(aSrcObj : TObject; aDoMappingProc : TMappingProc<TObject>; aCustomMapping: TCustomMapping = nil): T; overload;
  96. class procedure Map(aSrcObj : TObject; aTgtObj : T; aDoMappingProc : TMappingProc<TObject>; aCustomMapping: TCustomMapping);
  97. {$ENDIF}
  98. end;
  99. IAutoMapper<TClass1, TClass2 : class, constructor> = interface
  100. ['{9F7B2DEA-76D8-4DD1-95D0-22C22AEB5DD0}']
  101. function Map(aSrcObj : TClass1) : TClass2; overload;
  102. {$IFNDEF FPC}
  103. function Map(aSrcObj : TClass2) : TClass1; overload;
  104. procedure SetOnDoMapping(CustomProc : TMappingProc<TClass1>);
  105. procedure SetOnAfterMapping(CustomProc : TAfterMappingProc<TClass1,TClass2>);
  106. {$ELSE}
  107. //freepascal detects overload with generic types as duplicated function, added dummy field to avoid this
  108. function Map(aSrcObj : TClass2; dummy : Boolean = True) : TClass1; overload;
  109. {$ENDIF}
  110. end;
  111. TAutoMapper<TClass1, TClass2 : class, constructor> = class(TInterfacedObject, IAutoMapper<TClass1, TClass2>)
  112. private
  113. fCustomMapping : TCustomMapping;
  114. {$IFNDEF FPC}
  115. fOnDoMapping : TMappingProc<TClass1>;
  116. {$ELSE}
  117. fOnDoMapping : TMappingProc<TObject>;
  118. {$ENDIF}
  119. fOnAfterMapping : TAfterMappingProc<TClass1,TClass2>;
  120. public
  121. constructor Create;
  122. destructor Destroy; override;
  123. property CustomMapping : TCustomMapping read fCustomMapping write fCustomMapping;
  124. {$IFNDEF FPC}
  125. property OnDoMapping : TMappingProc<TClass1> read fOnDoMapping write fOnDoMapping;
  126. {$ELSE}
  127. property OnDoMapping : TMappingProc<TObject> read fOnDoMapping write fOnDoMapping;
  128. {$ENDIF}
  129. property OnAfterMapping : TAfterMappingProc<TClass1,TClass2> read fOnAfterMapping write fOnAfterMapping;
  130. function Map(aSrcObj : TClass1) : TClass2; overload;
  131. {$IFNDEF FPC}
  132. function Map(aSrcObj : TClass2) : TClass1; overload;
  133. procedure SetOnDoMapping(CustomProc : TMappingProc<TClass1>);
  134. procedure SetOnAfterMapping(CustomProc : TAfterMappingProc<TClass1,TClass2>);
  135. {$ELSE}
  136. //freepascal detects overload with generic types as duplicated function, added dummy field to avoid this
  137. function Map(aSrcObj : TClass2; dummy : Boolean = True) : TClass1; overload;
  138. {$ENDIF}
  139. end;
  140. EAutoMapperError = class(Exception);
  141. implementation
  142. { TObjMapper }
  143. class procedure TObjMapper.Map(aSrcObj : TObject; aTgtObj : TObject; aCustomMapping: TCustomMapping = nil);
  144. begin
  145. Map{$IFNDEF FPC}<TObject>{$ENDIF}(aSrcObj,aTgtObj,nil,aCustomMapping);
  146. end;
  147. {$IFNDEF FPC}
  148. class procedure TObjMapper.Map<Tm>(aSrcObj : TObject; aTgtObj : TObject; aDoMappingProc : TMappingProc<Tm>; aCustomMapping: TCustomMapping = nil);
  149. {$ELSE}
  150. class procedure TObjMapper.Map(aSrcObj : TObject; aTgtObj : TObject; aDoMappingProc : TMappingProc<TObject>; aCustomMapping: TCustomMapping = nil);
  151. {$ENDIF}
  152. var
  153. ctx : TRttiContext;
  154. rType : TRttiType;
  155. tgtprop : TRttiProperty;
  156. mapname : string;
  157. obj : TObject;
  158. manualmapping : Boolean;
  159. value : TFlexValue;
  160. {$IFNDEF FPC}
  161. clname : string;
  162. objvalue : TValue;
  163. {$ENDIF}
  164. begin
  165. //if aTgtObj = nil then aTgtObj := GetTypeData(aTgtObj.ClassInfo).classType.Create;
  166. if aTgtObj = nil then raise EAutoMapperError.Create('TObjMapper: Target Object passed must be created before');
  167. {$IFNDEF FPC}
  168. objvalue := TValue.From(aSrcObj);
  169. {$ENDIF}
  170. rType := ctx.GetType(aSrcObj.ClassInfo);
  171. for tgtprop in ctx.GetType(aTgtObj.ClassInfo).GetProperties do
  172. begin
  173. if tgtprop.IsWritable then
  174. begin
  175. if not tgtprop.PropertyType.IsInstance then
  176. begin
  177. if Assigned(aCustomMapping) and (not Assigned(aDoMappingProc)) then
  178. begin
  179. if aCustomMapping.GetMap(tgtprop.Name,mapname) then
  180. begin
  181. {$IFNDEF PROPERTYPATH_MODE}
  182. if rType.GetProperty(mapname) = nil then raise EAutoMapperError.CreateFmt('No valid custom mapping (Source: %s - Target: %s)',[mapname,tgtprop.Name]);
  183. begin
  184. try
  185. {$IFNDEF FPC}
  186. tgtprop.SetValue(aTgtObj,rType.GetProperty(mapname).GetValue(aSrcObj));
  187. {$ELSE}
  188. SetPropValue(aTgtObj,tgtprop.Name,GetPropValue(aSrcObj,mapname));
  189. {$ENDIF}
  190. except
  191. on E : Exception do raise EAutoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
  192. end;
  193. end;
  194. {$ELSE}
  195. if not TRTTI.PathExists(aSrcObj,mapname) then raise EAutoMapperError.CreateFmt('No valid custom mapping (Source: %s - Target: %s)',[mapname,tgtprop.Name]);
  196. TRTTI.SetPathValue(aTgtObj,tgtprop.Name,TRTTI.GetPathValue(aSrcObj,mapname));
  197. {$ENDIF}
  198. end
  199. else
  200. begin
  201. if rType.GetProperty(tgtprop.Name) <> nil then
  202. try
  203. {$IFNDEF FPC}
  204. tgtprop.SetValue(aTgtObj,rType.GetProperty(tgtprop.Name).GetValue(aSrcObj));
  205. {$ELSE}
  206. SetPropValue(aTgtObj,tgtprop.Name,GetPropValue(aSrcObj,tgtprop.Name));
  207. {$ENDIF}
  208. except
  209. on E : Exception do raise EAutoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
  210. end;
  211. end;
  212. end
  213. else
  214. begin
  215. try
  216. if Assigned(aDoMappingProc) then
  217. begin
  218. {$IFNDEF FPC}
  219. aDoMappingProc(objvalue.AsType<Tm>,tgtprop.Name,value);
  220. manualmapping := not value.IsEmpty;
  221. {$ELSE}
  222. aDoMappingProc(aSrcObj,tgtprop.Name,value);
  223. manualmapping := not varType(value) = varEmpty;
  224. {$ENDIF}
  225. end
  226. else manualmapping := False;
  227. if manualmapping then
  228. begin
  229. {$IFNDEF FPC}
  230. tgtprop.SetValue(aTgtObj,value);
  231. {$ELSE}
  232. SetPropValue(aTgtObj,tgtprop.Name,value);
  233. {$ENDIF}
  234. end
  235. else
  236. begin
  237. {$IFNDEF FPC}
  238. if rType.GetProperty(tgtprop.Name) <> nil then tgtprop.SetValue(aTgtObj,rType.GetProperty(tgtprop.Name).GetValue(aSrcObj));
  239. {$ELSE}
  240. if rType.GetProperty(tgtprop.Name) <> nil then SetPropValue(aTgtObj,tgtprop.Name,GetPropValue(aSrcObj,tgtprop.Name));
  241. {$ENDIF}
  242. end;
  243. except
  244. on E : Exception do raise EAUtoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
  245. end;
  246. end;
  247. end
  248. else
  249. begin
  250. obj := tgtprop.GetValue(aTgtObj).AsObject;
  251. {$IFNDEF FPC}
  252. if obj = nil then obj := GetObjectProp(aSrcObj,tgtprop.Name).ClassType.Create;// TObject.Create;
  253. {$ELSE}
  254. if obj = nil then obj := GetObjectProp(aSrcObj,tgtprop.Name).ClassType.Create;
  255. {$ENDIF}
  256. if obj <> nil then
  257. begin
  258. {$IFNDEF FPC}
  259. try
  260. if (rType.GetProperty(tgtprop.Name) <> nil)
  261. and (not rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).IsEmpty) then clname := rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).AsObject.ClassName
  262. else Continue;
  263. except
  264. on E : Exception do raise EAUtoMapperError.CreateFmt('Error mapping property "%s" : %s',[tgtprop.Name,e.message]);
  265. end;
  266. if clname.StartsWith('TList') then TListMapper.Map(rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).AsObject,obj,aCustomMapping)
  267. else if clname.StartsWith('TObjectList') then TObjListMapper.Map(rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).AsObject,obj,aCustomMapping)
  268. else TObjMapper.Map(rType.GetProperty(tgtprop.Name).GetValue(aSrcObj).AsObject,obj,aCustomMapping)
  269. {$ELSE}
  270. TObjMapper.Map(GetObjectProp(aSrcObj,tgtprop.Name),obj,aCustomMapping);
  271. SetObjectProp(aTgtObj,tgtprop.Name,obj);
  272. {$ENDIF}
  273. end
  274. else raise EAutoMapperError.CreateFmt('Target object "%s" not autocreated by class',[tgtprop.Name]);
  275. end;
  276. end;
  277. end;
  278. end;
  279. class function TMapper<T>.Map(aSrcObj : TObject; aCustomMapping: TCustomMapping = nil) : T;
  280. begin
  281. Result := Map{$IFNDEF FPC}<TObject>{$ENDIF}(aSrcObj,nil,aCustomMapping);
  282. end;
  283. {$IFNDEF FPC}
  284. class function TMapper<T>.Map<Tm>(aSrcObj : TObject; aDoMappingProc : TMappingProc<Tm>; aCustomMapping: TCustomMapping = nil): T;
  285. {$ELSE}
  286. class function TMapper<T>.Map(aSrcObj : TObject; aDoMappingProc : TMappingProc<TObject>; aCustomMapping: TCustomMapping = nil): T;
  287. {$ENDIF}
  288. var
  289. obj : T;
  290. begin
  291. obj := T.Create;
  292. {$IFNDEF FPC}
  293. TObjMapper.Map<Tm>(aSrcObj,obj,aDoMappingProc,aCustomMapping);
  294. {$ELSE}
  295. TObjMapper.Map(aSrcObj,obj,aDoMappingProc,aCustomMapping);
  296. {$ENDIF}
  297. Result := obj;
  298. end;
  299. class procedure TMapper<T>.Map(aSrcObj : TObject; aTgtObj : T; aCustomMapping : TCustomMapping = nil);
  300. begin
  301. {$IFNDEF FPC}
  302. Map<T>(aSrcObj,aTgtObj,nil,aCustomMapping);
  303. {$ELSE}
  304. Map(aSrcObj,aTgtObj,nil,aCustomMapping);
  305. {$ENDIF}
  306. end;
  307. {$IFNDEF FPC}
  308. class procedure TMapper<T>.Map<Tm>(aSrcObj : TObject; aTgtObj : T; aDoMappingProc : TMappingProc<Tm>; aCustomMapping : TCustomMapping = nil);
  309. {$ELSE}
  310. class procedure TMapper<T>.Map(aSrcObj : TObject; aTgtObj : T; aDoMappingProc : TMappingProc<TObject>; aCustomMapping : TCustomMapping);
  311. {$ENDIF}
  312. begin
  313. {$IFNDEF FPC}
  314. TObjMapper.Map<Tm>(aSrcObj, aTgtObj, aDoMappingProc, aCustomMapping);
  315. {$ELSE}
  316. TObjMapper.Map(aSrcObj, aTgtObj, aDoMappingProc, aCustomMapping);
  317. {$ENDIF}
  318. end;
  319. { TAutoMapper<TClass1, TClass2> }
  320. constructor TAutoMapper<TClass1, TClass2>.Create;
  321. begin
  322. fCustomMapping := TCustomMapping.Create;
  323. fOnDoMapping := nil;
  324. fOnAfterMapping := nil;
  325. end;
  326. destructor TAutoMapper<TClass1, TClass2>.Destroy;
  327. begin
  328. if Assigned(fCustomMapping) then fCustomMapping.Free;
  329. fOnDoMapping := nil;
  330. fOnAfterMapping := nil;
  331. inherited;
  332. end;
  333. function TAutoMapper<TClass1, TClass2>.Map(aSrcObj: TClass1): TClass2;
  334. var
  335. objvalue : TValue;
  336. obj : TObject;
  337. begin
  338. obj := aSrcObj as TObject;
  339. //objvalue := TValue.From(aSrcObj).AsObject;
  340. {$IFNDEF FPC}
  341. Result := TMapper<TClass2>.Map<TClass1>(obj,fOnDoMapping,fCustomMapping);
  342. {$ELSE}
  343. Result := TMapper<TClass2>.Map(obj,fOnDoMapping,fCustomMapping);
  344. {$ENDIF}
  345. if Assigned(fOnAfterMapping) then fOnAfterMapping(aSrcObj,Result);
  346. end;
  347. {$IFNDEF FPC}
  348. function TAutoMapper<TClass1, TClass2>.Map(aSrcObj: TClass2): TClass1;
  349. begin
  350. Result := TMapper<TClass1>.Map<TClass1>(aSrcObj,fOnDoMapping,fCustomMapping);
  351. end;
  352. procedure TAutoMapper<TClass1, TClass2>.SetOnAfterMapping(CustomProc: TAfterMappingProc<TClass1, TClass2>);
  353. begin
  354. fOnAfterMapping := CustomProc;
  355. end;
  356. procedure TAutoMapper<TClass1, TClass2>.SetOnDoMapping(CustomProc: TMappingProc<TClass1>);
  357. begin
  358. fOnDoMapping := CustomProc;
  359. end;
  360. {$ELSE}
  361. function TAutoMapper<TClass1, TClass2>.Map(aSrcObj: TClass2; dummy : Boolean = True): TClass1;
  362. begin
  363. Result := TMapper<TClass1>.Map(aSrcObj,fOnDoMapping,fCustomMapping);
  364. end;
  365. {$ENDIF}
  366. { TCustomMapping }
  367. procedure TCustomMapping.AddMap(const aName, aMapName: string);
  368. begin
  369. //add map fields
  370. fMapDictionary.Add(aName,aMapName);
  371. //add reverse lookup if not same name
  372. if aName <> aMapName then fMapDictionary.Add(aMapName,aName);
  373. end;
  374. function TCustomMapping.Count: Integer;
  375. begin
  376. Result := fMapDictionary.Count;
  377. end;
  378. constructor TCustomMapping.Create;
  379. begin
  380. fMapDictionary := TDictionary<string,string>.Create;
  381. end;
  382. destructor TCustomMapping.Destroy;
  383. begin
  384. fMapDictionary.Free;
  385. inherited;
  386. end;
  387. function TCustomMapping.GetMap(const aName: string; out vMapName: string): Boolean;
  388. begin
  389. Result := fMapDictionary.TryGetValue(aName,vMapName);
  390. end;
  391. { TListMapper }
  392. class procedure TListMapper.Map(aSrcList, aTgtList: TObject; aCustomMapping: TCustomMapping);
  393. {$IFNDEF FPC}
  394. var
  395. rtype: TRttiType;
  396. rtype2 : TRttiType;
  397. typinfo : PTypeInfo;
  398. methToArray: TRttiMethod;
  399. value: TValue;
  400. valuecop : TValue;
  401. obj : TObject;
  402. i : Integer;
  403. rprop : TRttiProperty;
  404. ctx : TRttiContext;
  405. begin
  406. rtype := ctx.GetType(aSrcList.ClassInfo);
  407. methToArray := rtype.GetMethod('ToArray');
  408. if Assigned(methToArray) then
  409. begin
  410. value := methToArray.Invoke(aSrcList,[]);
  411. Assert(value.IsArray);
  412. rtype2 := ctx.GetType(aTgtList.ClassInfo);
  413. rProp := rtype2.GetProperty('List');
  414. typinfo := GetTypeData(rProp.PropertyType.Handle).DynArrElType^;
  415. case typinfo.Kind of
  416. tkChar, tkString, tkWChar, tkWString : TList<string>(aTgtList).Capacity := value.GetArrayLength;
  417. tkInteger, tkInt64 : TList<Integer>(aTgtList).Capacity := value.GetArrayLength;
  418. tkFloat : TList<Extended>(aTgtList).Capacity := value.GetArrayLength;
  419. tkRecord :
  420. begin
  421. TObjMapper.Map(aSrcList,aTgtList,aCustomMapping);
  422. exit;
  423. end;
  424. else TList<TObject>(aTgtList).Capacity := value.GetArrayLength;
  425. end;
  426. for i := 0 to value.GetArrayLength - 1 do
  427. begin
  428. if typinfo.Kind = tkClass then
  429. begin
  430. obj := typinfo.TypeData.ClassType.Create;
  431. TObjMapper.Map(value.GetArrayElement(i).AsObject,obj,aCustomMapping);
  432. TList<TObject>(aTgtList).Add(obj);
  433. end
  434. else
  435. begin
  436. valuecop := value.GetArrayElement(i);
  437. case typinfo.Kind of
  438. tkChar, tkString, tkWChar, tkWString : TList<string>(aTgtList).Add(valuecop.AsString);
  439. tkInteger, tkInt64 : TList<Integer>(aTgtList).Add(valuecop.AsInt64);
  440. tkFloat : TList<Extended>(aTgtList).Add(valuecop.AsExtended);
  441. end;
  442. end;
  443. end;
  444. end;
  445. end;
  446. {$ELSE}
  447. begin
  448. end;
  449. {$ENDIF}
  450. { TObjListMapper }
  451. class procedure TObjListMapper.Map(aSrcObjList, aTgtObjList: TObject; aCustomMapping: TCustomMapping);
  452. begin
  453. {$IFNDEF FPC}
  454. Map<TObject>(aSrcObjList,aTgtObjList,nil,aCustomMapping);
  455. {$ELSE}
  456. Map(aSrcObjList,aTgtObjList,nil,aCustomMapping);
  457. {$ENDIF}
  458. end;
  459. {$IFNDEF FPC}
  460. class procedure TObjListMapper.Map<Tm>(aSrcObjList : TObject; aTgtObjList : TObject; aDoMappingProc : TMappingProc<Tm>; aCustomMapping : TCustomMapping = nil);
  461. var
  462. rtype: TRttiType;
  463. rtype2 : TRttiType;
  464. typinfo : PTypeInfo;
  465. methToArray: TRttiMethod;
  466. value: TValue;
  467. obj : TObject;
  468. i : Integer;
  469. rprop : TRttiProperty;
  470. ctx : TRttiContext;
  471. begin
  472. rtype := ctx.GetType(aSrcObjList.ClassInfo);
  473. methToArray := rtype.GetMethod('ToArray');
  474. if Assigned(methToArray) then
  475. begin
  476. value := methToArray.Invoke(aSrcObjList,[]);
  477. Assert(value.IsArray);
  478. rtype2 := ctx.GetType(aTgtObjList.ClassInfo);
  479. rProp := rtype2.GetProperty('List');
  480. typinfo := GetTypeData(rProp.PropertyType.Handle).DynArrElType^;
  481. TObjectList<TObject>(aTgtObjList).Capacity := value.GetArrayLength;
  482. for i := 0 to value.GetArrayLength - 1 do
  483. begin
  484. obj := typinfo.TypeData.ClassType.Create;
  485. TObjMapper.Map<Tm>(value.GetArrayElement(i).AsObject,obj,aDoMappingProc,aCustomMapping);
  486. TObjectList<TObject>(aTgtObjList).Add(obj);
  487. end;
  488. end;
  489. end;
  490. {$ELSE}
  491. class procedure TObjListMapper.Map(aSrcObjList : TObject; aTgtObjList : TObject; aDoMappingProc : TMappingProc<TObject>; aCustomMapping : TCustomMapping = nil);
  492. begin
  493. end;
  494. {$ENDIF}
  495. { TMapper }
  496. {$IFNDEF FPC}
  497. class function TMapper.Map<T>(aSrcObj: TObject): T;
  498. begin
  499. Result := T.Create;
  500. TObjMapper.Map(aSrcObj,Result,nil);
  501. end;
  502. {$ENDIF}
  503. end.