2
0

Quick.AutoMapper.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  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. obj : TObject;
  336. begin
  337. obj := aSrcObj as TObject;
  338. //objvalue := TValue.From(aSrcObj).AsObject;
  339. {$IFNDEF FPC}
  340. Result := TMapper<TClass2>.Map<TClass1>(obj,fOnDoMapping,fCustomMapping);
  341. {$ELSE}
  342. Result := TMapper<TClass2>.Map(obj,fOnDoMapping,fCustomMapping);
  343. {$ENDIF}
  344. if Assigned(fOnAfterMapping) then fOnAfterMapping(aSrcObj,Result);
  345. end;
  346. {$IFNDEF FPC}
  347. function TAutoMapper<TClass1, TClass2>.Map(aSrcObj: TClass2): TClass1;
  348. begin
  349. Result := TMapper<TClass1>.Map<TClass1>(aSrcObj,fOnDoMapping,fCustomMapping);
  350. end;
  351. procedure TAutoMapper<TClass1, TClass2>.SetOnAfterMapping(CustomProc: TAfterMappingProc<TClass1, TClass2>);
  352. begin
  353. fOnAfterMapping := CustomProc;
  354. end;
  355. procedure TAutoMapper<TClass1, TClass2>.SetOnDoMapping(CustomProc: TMappingProc<TClass1>);
  356. begin
  357. fOnDoMapping := CustomProc;
  358. end;
  359. {$ELSE}
  360. function TAutoMapper<TClass1, TClass2>.Map(aSrcObj: TClass2; dummy : Boolean = True): TClass1;
  361. begin
  362. Result := TMapper<TClass1>.Map(aSrcObj,fOnDoMapping,fCustomMapping);
  363. end;
  364. {$ENDIF}
  365. { TCustomMapping }
  366. procedure TCustomMapping.AddMap(const aName, aMapName: string);
  367. begin
  368. //add map fields
  369. fMapDictionary.Add(aName,aMapName);
  370. //add reverse lookup if not same name
  371. if aName <> aMapName then fMapDictionary.Add(aMapName,aName);
  372. end;
  373. function TCustomMapping.Count: Integer;
  374. begin
  375. Result := fMapDictionary.Count;
  376. end;
  377. constructor TCustomMapping.Create;
  378. begin
  379. fMapDictionary := TDictionary<string,string>.Create;
  380. end;
  381. destructor TCustomMapping.Destroy;
  382. begin
  383. fMapDictionary.Free;
  384. inherited;
  385. end;
  386. function TCustomMapping.GetMap(const aName: string; out vMapName: string): Boolean;
  387. begin
  388. Result := fMapDictionary.TryGetValue(aName,vMapName);
  389. end;
  390. { TListMapper }
  391. class procedure TListMapper.Map(aSrcList, aTgtList: TObject; aCustomMapping: TCustomMapping);
  392. {$IFNDEF FPC}
  393. var
  394. rtype: TRttiType;
  395. rtype2 : TRttiType;
  396. typinfo : PTypeInfo;
  397. methToArray: TRttiMethod;
  398. value: TValue;
  399. valuecop : TValue;
  400. obj : TObject;
  401. i : Integer;
  402. rprop : TRttiProperty;
  403. ctx : TRttiContext;
  404. begin
  405. rtype := ctx.GetType(aSrcList.ClassInfo);
  406. methToArray := rtype.GetMethod('ToArray');
  407. if Assigned(methToArray) then
  408. begin
  409. value := methToArray.Invoke(aSrcList,[]);
  410. Assert(value.IsArray);
  411. rtype2 := ctx.GetType(aTgtList.ClassInfo);
  412. rProp := rtype2.GetProperty('List');
  413. typinfo := GetTypeData(rProp.PropertyType.Handle).DynArrElType^;
  414. case typinfo.Kind of
  415. tkChar, tkString, tkWChar, tkWString : TList<string>(aTgtList).Capacity := value.GetArrayLength;
  416. tkInteger, tkInt64 : TList<Integer>(aTgtList).Capacity := value.GetArrayLength;
  417. tkFloat : TList<Extended>(aTgtList).Capacity := value.GetArrayLength;
  418. tkRecord :
  419. begin
  420. TObjMapper.Map(aSrcList,aTgtList,aCustomMapping);
  421. exit;
  422. end;
  423. else TList<TObject>(aTgtList).Capacity := value.GetArrayLength;
  424. end;
  425. for i := 0 to value.GetArrayLength - 1 do
  426. begin
  427. if typinfo.Kind = tkClass then
  428. begin
  429. obj := typinfo.TypeData.ClassType.Create;
  430. TObjMapper.Map(value.GetArrayElement(i).AsObject,obj,aCustomMapping);
  431. TList<TObject>(aTgtList).Add(obj);
  432. end
  433. else
  434. begin
  435. valuecop := value.GetArrayElement(i);
  436. case typinfo.Kind of
  437. tkChar, tkString, tkWChar, tkWString : TList<string>(aTgtList).Add(valuecop.AsString);
  438. tkInteger, tkInt64 : TList<Integer>(aTgtList).Add(valuecop.AsInt64);
  439. tkFloat : TList<Extended>(aTgtList).Add(valuecop.AsExtended);
  440. end;
  441. end;
  442. end;
  443. end;
  444. end;
  445. {$ELSE}
  446. begin
  447. end;
  448. {$ENDIF}
  449. { TObjListMapper }
  450. class procedure TObjListMapper.Map(aSrcObjList, aTgtObjList: TObject; aCustomMapping: TCustomMapping);
  451. begin
  452. {$IFNDEF FPC}
  453. Map<TObject>(aSrcObjList,aTgtObjList,nil,aCustomMapping);
  454. {$ELSE}
  455. Map(aSrcObjList,aTgtObjList,nil,aCustomMapping);
  456. {$ENDIF}
  457. end;
  458. {$IFNDEF FPC}
  459. class procedure TObjListMapper.Map<Tm>(aSrcObjList : TObject; aTgtObjList : TObject; aDoMappingProc : TMappingProc<Tm>; aCustomMapping : TCustomMapping = nil);
  460. var
  461. rtype: TRttiType;
  462. rtype2 : TRttiType;
  463. typinfo : PTypeInfo;
  464. methToArray: TRttiMethod;
  465. value: TValue;
  466. obj : TObject;
  467. i : Integer;
  468. rprop : TRttiProperty;
  469. ctx : TRttiContext;
  470. begin
  471. rtype := ctx.GetType(aSrcObjList.ClassInfo);
  472. methToArray := rtype.GetMethod('ToArray');
  473. if Assigned(methToArray) then
  474. begin
  475. value := methToArray.Invoke(aSrcObjList,[]);
  476. Assert(value.IsArray);
  477. rtype2 := ctx.GetType(aTgtObjList.ClassInfo);
  478. rProp := rtype2.GetProperty('List');
  479. typinfo := GetTypeData(rProp.PropertyType.Handle).DynArrElType^;
  480. TObjectList<TObject>(aTgtObjList).Capacity := value.GetArrayLength;
  481. for i := 0 to value.GetArrayLength - 1 do
  482. begin
  483. obj := typinfo.TypeData.ClassType.Create;
  484. TObjMapper.Map<Tm>(value.GetArrayElement(i).AsObject,obj,aDoMappingProc,aCustomMapping);
  485. TObjectList<TObject>(aTgtObjList).Add(obj);
  486. end;
  487. end;
  488. end;
  489. {$ELSE}
  490. class procedure TObjListMapper.Map(aSrcObjList : TObject; aTgtObjList : TObject; aDoMappingProc : TMappingProc<TObject>; aCustomMapping : TCustomMapping = nil);
  491. begin
  492. end;
  493. {$ENDIF}
  494. { TMapper }
  495. {$IFNDEF FPC}
  496. class function TMapper.Map<T>(aSrcObj: TObject): T;
  497. begin
  498. Result := T.Create;
  499. TObjMapper.Map(aSrcObj,Result,nil);
  500. end;
  501. {$ENDIF}
  502. end.