restbase.pp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425
  1. { **********************************************************************
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2015 by the Free Pascal development team
  4. Base for REST classes
  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. unit restbase;
  12. {$mode objfpc}{$H+}
  13. { $DEFINE DEBUGBASEOBJMEMLEAK}
  14. interface
  15. uses
  16. typinfo, fpjson, Classes, SysUtils, contnrs;
  17. Type
  18. ERESTAPI = Class(Exception);
  19. TStringArray = Array of string;
  20. TStringArrayArray = Array of TStringArray;
  21. TUnicodeStringArray = Array of UnicodeString;
  22. TIntegerArray = Array of Integer;
  23. TInt64Array = Array of Int64;
  24. TInt32Array = Array of Integer;
  25. TFloatArray = Array of TJSONFloat;
  26. TFloatArrayArray = Array of TFloatArray;
  27. TDoubleArray = Array of TJSONFloat;
  28. TDoubleArrayArray = Array of TDoubleArray;
  29. TDateTimeArray = Array of TDateTime;
  30. TBooleanArray = Array of boolean;
  31. TChildType = (ctArray,ctObject);
  32. TChildTypes = Set of TChildType;
  33. { TBaseObject }
  34. TObjectOption = (ooStartRecordingChanges,ooCreateObjectOnGet);
  35. TObjectOptions = set of TObjectOption;
  36. TDateTimeType = (dtNone,dtDateTime,dtDate,dtTime);
  37. Const
  38. DefaultObjectOptions = [ooStartRecordingChanges]; // Default for constructor.
  39. IndexShift = 3; // Number of bits reserved for flags.
  40. Type
  41. {$M+}
  42. TBaseObject = CLass(TObject)
  43. Private
  44. FObjectOptions : TObjectOptions;
  45. fadditionalProperties : TJSONObject;
  46. FBits : TBits;
  47. Function GetDynArrayProp(P: PPropInfo) : Pointer; virtual;
  48. procedure SetDynArrayProp(P: PPropInfo; AValue : Pointer); virtual;
  49. procedure SetObjectOptions(AValue: TObjectOptions);
  50. Function GetAdditionalProperties : TJSONObject;
  51. protected
  52. {$ifdef ver2_6}
  53. // Version 2.6.4 has a bug for i386 where the array cannot be set through RTTI.
  54. // This is a helper method that sets the length of the array to the desired length,
  55. // After which the new array pointer is read again.
  56. // AName is guaranteed to be lowercase
  57. Procedure SetArrayLength(const AName : String; ALength : Longint); virtual;
  58. {$endif}
  59. Procedure MarkPropertyChanged(AIndex : Integer);
  60. Function IsDateTimeProp(Info : PTypeInfo) : Boolean;
  61. Function DateTimePropType(Info : PTypeInfo) : TDateTimeType;
  62. // Load properties
  63. Procedure ClearProperty(P: PPropInfo); virtual;
  64. Procedure SetBooleanProperty(P: PPropInfo; AValue: Boolean); virtual;
  65. Procedure SetFloatProperty(P: PPropInfo; AValue: Extended); virtual;
  66. Procedure SetInt64Property(P: PPropInfo; AValue: Int64); virtual;
  67. {$ifndef ver2_6}
  68. Procedure SetQWordProperty(P: PPropInfo; AValue: QWord); virtual;
  69. {$endif}
  70. Procedure SetIntegerProperty(P: PPropInfo; AValue: Integer); virtual;
  71. Procedure SetStringProperty(P: PPropInfo; AValue: String); virtual;
  72. Procedure SetArrayProperty(P: PPropInfo; AValue : TJSONArray); virtual;
  73. Procedure SetObjectProperty(P: PPropInfo; AValue : TJSONObject); virtual;
  74. Procedure SetSetProperty(P: PPropInfo; AValue : TJSONArray); virtual;
  75. Procedure SetEnumProperty(P: PPropInfo; AValue : TJSONData); virtual;
  76. // Save properties
  77. Function GetBooleanProperty(P: PPropInfo) : TJSONData; virtual;
  78. Function GetIntegerProperty(P: PPropInfo) : TJSONData; virtual;
  79. Function GetInt64Property(P: PPropInfo) : TJSONData; virtual;
  80. Function GetQwordProperty(P: PPropInfo) : TJSONData; virtual;
  81. Function GetFloatProperty(P: PPropInfo) : TJSONData; virtual;
  82. Function GetStringProperty(P: PPropInfo) : TJSONData; virtual;
  83. Function GetSetProperty(P: PPropInfo) : TJSONData; virtual;
  84. Function GetEnumeratedProperty(P: PPropInfo) : TJSONData; virtual;
  85. Function GetArrayProperty(P: PPropInfo) : TJSONData; virtual;
  86. Function GetObjectProperty(P: PPropInfo) : TJSONData; virtual;
  87. // Clear properties on
  88. Procedure ClearChildren(ChildTypes : TChildTypes); virtual;
  89. Class Function ClearChildTypes : TChildTypes; virtual;
  90. Public
  91. Constructor Create(AOptions : TObjectOptions = DefaultObjectOptions); Virtual;
  92. Destructor Destroy; override;
  93. Procedure StartRecordPropertyChanges;
  94. Procedure ClearPropertyChanges;
  95. Procedure StopRecordPropertyChanges;
  96. Function IsPropertyModified(Info : PPropInfo) : Boolean;
  97. Function IsPropertyModified(const AName : String) : Boolean;
  98. Class Function AllowAdditionalProperties : Boolean; virtual;
  99. Class Function GetTotalPropCount : Integer; virtual;
  100. Class Function GetCurrentPropCount : Integer; virtual;
  101. Class Function GetParentPropCount : Integer; virtual;
  102. Class Function ExportPropertyName(Const AName : String) : string; virtual;
  103. Class Function CleanPropertyName(Const AName : String) : string;
  104. Class Function CreateObject(Const AKind : String; AClass: TClass = Nil) : TBaseObject;
  105. Class Procedure RegisterObject;
  106. Class Function ObjectRestKind : String; virtual;
  107. Procedure LoadPropertyFromJSON(Const AName : String; JSON : TJSONData); virtual;
  108. Function SavePropertyToJSON(Info : PPropInfo) : TJSONData; virtual;
  109. Procedure LoadFromJSON(JSON : TJSONObject); virtual;
  110. Procedure SaveToJSON(JSON : TJSONObject); virtual;
  111. Function SaveToJSON : TJSONObject;
  112. Property ObjectOptions : TObjectOptions Read FObjectOptions Write SetObjectOptions;
  113. Property additionalProperties : TJSONObject Read GetAdditionalProperties;
  114. end;
  115. TBaseObjectClass = Class of TBaseObject;
  116. TObjectArray = Array of TBaseObject;
  117. TObjectArrayArray = Array of TObjectArray;
  118. TBaseListEnumerator = class
  119. private
  120. FList: TFPObjectList;
  121. FPosition: Integer;
  122. public
  123. constructor Create(AList: TFPObjectList);
  124. function GetCurrent: TBaseObject; virtual;
  125. function MoveNext: Boolean;
  126. property Current: TBaseObject read GetCurrent;
  127. end;
  128. TBaseListEnumeratorClass = Class of TBaseListEnumerator;
  129. { TBaseObjectList }
  130. TBaseObjectList = Class(TBaseObject)
  131. private
  132. FList : TFPObjectList;
  133. Protected
  134. function GetO(Aindex : Integer): TBaseObject;
  135. procedure SetO(Aindex : Integer; AValue: TBaseObject);
  136. Class Function ObjectClass : TBaseObjectClass; virtual;
  137. Function DoCreateEnumerator(AEnumClass : TBaseListEnumeratorClass) : TBaseListEnumerator;
  138. Public
  139. Constructor Create(AOptions : TObjectOptions = DefaultObjectOptions); Override;
  140. Destructor Destroy; override;
  141. function GetEnumerator : TBaseListEnumerator;
  142. Function AddObject(Const AKind : String) : TBaseObject; virtual;
  143. Property Objects [Aindex : Integer] : TBaseObject Read GetO Write SetO; default;
  144. end;
  145. { TBaseObjectList }
  146. { TBaseNamedObjectList }
  147. TBaseNamedObjectList = Class(TBaseObject)
  148. private
  149. FList : TStringList;
  150. function GetN(Aindex : Integer): String;
  151. function GetO(Aindex : Integer): TBaseObject;
  152. function GetON(AName : String): TBaseObject;
  153. procedure SetN(Aindex : Integer; AValue: String);
  154. procedure SetO(Aindex : Integer; AValue: TBaseObject);
  155. procedure SetON(AName : String; AValue: TBaseObject);
  156. Protected
  157. Class Function ObjectClass : TBaseObjectClass; virtual;
  158. Public
  159. Constructor Create(AOptions : TObjectOptions = DefaultObjectOptions); Override;
  160. Destructor Destroy; override;
  161. Function AddObject(Const AName,AKind : String) : TBaseObject; virtual;
  162. Property Names [Aindex : Integer] : String Read GetN Write SetN;
  163. Property Objects [Aindex : Integer] : TBaseObject Read GetO Write SetO;
  164. Property ObjectByName [AName : String] : TBaseObject Read GetON Write SetON; default;
  165. end;
  166. // used to catch a general JSON schema.
  167. { TJSONSchema }
  168. TJSONSchema = Class(TBaseObject)
  169. private
  170. FSchema: String;
  171. Public
  172. Procedure SetArrayProperty(P: PPropInfo; AValue : TJSONArray); override;
  173. Procedure LoadFromJSON(JSON : TJSONObject); override;
  174. Property Schema : String Read FSchema Write FSchema;
  175. end;
  176. TJSONSchemaArray = Array of TJSONSchema;
  177. TTJSONSchemaArray = TJSONSchemaArray;
  178. { TObjectFactory }
  179. TObjectFactory = Class(TComponent)
  180. Private
  181. FList : TClassList;
  182. Public
  183. Constructor Create(AOwner : TComponent); override;
  184. Destructor Destroy; override;
  185. Procedure RegisterObject(A : TBaseObjectClass);
  186. Function GetObjectClass(Const AKind : String) : TBaseObjectClass;
  187. end;
  188. Function RESTFactory : TObjectFactory;
  189. Function DateTimeToRFC3339(ADate :TDateTime):string;
  190. Function DateToRFC3339(ADate :TDateTime):string;
  191. Function TimeToRFC3339(ADate :TDateTime):string;
  192. Function TryRFC3339ToDateTime(const Avalue: String; out ADateTime: TDateTime): Boolean;
  193. Function RFC3339ToDateTime(const Avalue: String): TDateTime;
  194. implementation
  195. Var
  196. Fact : TObjectFactory;
  197. function DateTimeToRFC3339(ADate :TDateTime):string;
  198. begin
  199. Result:=FormatDateTime('yyyy-mm-dd"T"hh":"nn":"ss"."zzz"Z"',ADate);
  200. end;
  201. function DateToRFC3339(ADate: TDateTime): string;
  202. begin
  203. Result:=FormatDateTime('yyyy-mm-dd',ADate);
  204. end;
  205. function TimeToRFC3339(ADate :TDateTime):string;
  206. begin
  207. Result:=FormatDateTime('hh":"nn":"ss"."zzz',ADate);
  208. end;
  209. Function TryRFC3339ToDateTime(const Avalue: String; out ADateTime: TDateTime): Boolean;
  210. // 1 2
  211. // 12345678901234567890123
  212. // yyyy-mm-ddThh:nn:ss.zzz
  213. Type
  214. TPartPos = (ppTime,ppYear,ppMonth,ppDay,ppHour,ppMinute,ppSec);
  215. TPos = Array [TPartPos] of byte;
  216. Const
  217. P : TPos = (11,1,6,9,12,15,18);
  218. var
  219. lY, lM, lD, lH, lMi, lS: Integer;
  220. begin
  221. if Trim(AValue) = '' then
  222. begin
  223. Result:=True;
  224. ADateTime:=0;
  225. end;
  226. lY:=StrToIntDef(Copy(AValue,P[ppYear],4),-1);
  227. lM:=StrToIntDef(Copy(AValue,P[ppMonth],2),-1);
  228. lD:=StrToIntDef(Copy(AValue,P[ppDay],2),-1);
  229. if (Length(AValue)>=P[ppTime]) then
  230. begin
  231. lH:=StrToIntDef(Copy(AValue,P[ppHour],2),-1);
  232. lMi:=StrToIntDef(Copy(AValue,P[ppMinute],2),-1);
  233. lS:=StrToIntDef(Copy(AValue,P[ppSec],2),-1);
  234. end
  235. else
  236. begin
  237. lH:=0;
  238. lMi:=0;
  239. lS:=0;
  240. end;
  241. Result:=(lY>=0) and (lM>=00) and (lD>=0) and (lH>=0) and (lMi>=0) and (ls>=0);
  242. if Not Result then
  243. ADateTime:=0
  244. else
  245. { Cannot EncodeDate if any part equals 0. EncodeTime is okay. }
  246. if (lY = 0) or (lM = 0) or (lD = 0) then
  247. ADateTime:=EncodeTime(lH, lMi, lS, 0)
  248. else
  249. ADateTime:=EncodeDate(lY, lM, lD) + EncodeTime(lH, lMi, lS, 0);
  250. end;
  251. Function CountProperties(TypeInfo : PTypeInfo; Recurse : Boolean): Integer;
  252. function aligntoptr(p : pointer) : pointer;inline;
  253. begin
  254. {$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
  255. result:=align(p,sizeof(p));
  256. {$else FPC_REQUIRES_PROPER_ALIGNMENT}
  257. result:=p;
  258. {$endif FPC_REQUIRES_PROPER_ALIGNMENT}
  259. end;
  260. var
  261. hp : PTypeData;
  262. pd : ^TPropData;
  263. begin
  264. Result:=0;
  265. while Assigned(TypeInfo) do
  266. begin
  267. // skip the name
  268. hp:=GetTypeData(Typeinfo);
  269. // the class info rtti the property rtti follows immediatly
  270. pd:=aligntoptr(pointer(pointer(@hp^.UnitName)+Length(hp^.UnitName)+1));
  271. Result:=Result+Pd^.PropCount;
  272. if Recurse then
  273. TypeInfo:=HP^.ParentInfo
  274. else
  275. TypeInfo:=Nil;
  276. end;
  277. end;
  278. Function RFC3339ToDateTime(const Avalue: String): TDateTime;
  279. begin
  280. if Not TryRFC3339ToDateTime(AValue,Result) then
  281. Result:=0;
  282. end;
  283. Function RESTFactory : TObjectFactory;
  284. begin
  285. if Fact=Nil then
  286. Fact:=TObjectfactory.Create(Nil);
  287. Result:=Fact;
  288. end;
  289. { TObjectFactory }
  290. Constructor TObjectFactory.Create(AOwner: TComponent);
  291. begin
  292. inherited Create(AOwner);
  293. FList:=TClassList.Create;
  294. end;
  295. Destructor TObjectFactory.Destroy;
  296. begin
  297. FreeAndNil(FList);
  298. inherited Destroy;
  299. end;
  300. Procedure TObjectFactory.RegisterObject(A: TBaseObjectClass);
  301. begin
  302. Flist.Add(A);
  303. end;
  304. Function TObjectFactory.GetObjectClass(Const AKind: String): TBaseObjectClass;
  305. Var
  306. I : Integer;
  307. N : String;
  308. begin
  309. I:=FList.Count-1;
  310. Result:=Nil;
  311. While (Result=Nil) and (I>=0) do
  312. begin
  313. Result:=TBaseObjectClass(FList[i]);
  314. N:=Result.ObjectRestKind;
  315. if CompareText(N,AKind)<>0 then
  316. Result:=nil;
  317. Dec(I);
  318. end;
  319. end;
  320. { TBaseNamedObjectList }
  321. function TBaseNamedObjectList.GetN(Aindex : Integer): String;
  322. begin
  323. Result:=Flist[AIndex];
  324. end;
  325. function TBaseNamedObjectList.GetO(Aindex: Integer): TBaseObject;
  326. begin
  327. Result:=TBaseObject(Flist.Objects[AIndex]);
  328. end;
  329. function TBaseNamedObjectList.GetON(AName : String): TBaseObject;
  330. Var
  331. I : Integer;
  332. begin
  333. I:=FList.IndexOf(AName);
  334. if I<>-1 then
  335. Result:=GetO(I)
  336. else
  337. Result:=Nil;
  338. end;
  339. procedure TBaseNamedObjectList.SetN(Aindex : Integer; AValue: String);
  340. begin
  341. Flist[AIndex]:=Avalue
  342. end;
  343. procedure TBaseNamedObjectList.SetO(Aindex: Integer; AValue: TBaseObject);
  344. begin
  345. Flist.Objects[AIndex]:=Avalue
  346. end;
  347. procedure TBaseNamedObjectList.SetON(AName : String; AValue: TBaseObject);
  348. Var
  349. I : Integer;
  350. begin
  351. I:=FList.IndexOf(AName);
  352. if I<>-1 then
  353. SetO(I,AValue)
  354. else
  355. Flist.AddObject(AName,AValue);
  356. end;
  357. Class Function TBaseNamedObjectList.ObjectClass: TBaseObjectClass;
  358. begin
  359. Result:=TBaseObject;
  360. end;
  361. Constructor TBaseNamedObjectList.Create(AOptions : TObjectOptions = DefaultObjectOptions);
  362. begin
  363. inherited Create(AOptions);
  364. FList:=TStringList.Create;
  365. Flist.OwnsObjects:=True;
  366. end;
  367. Destructor TBaseNamedObjectList.Destroy;
  368. begin
  369. FreeAndNil(Flist);
  370. inherited Destroy;
  371. end;
  372. Function TBaseNamedObjectList.AddObject(Const AName, AKind: String
  373. ): TBaseObject;
  374. begin
  375. Result:=CreateObject(AKind);
  376. ObjectByName[AName]:=Result;
  377. end;
  378. { TJSONSchema }
  379. Procedure TJSONSchema.SetArrayProperty(P: PPropInfo; AValue: TJSONArray);
  380. begin
  381. Schema:=AValue.asJSON
  382. end;
  383. Procedure TJSONSchema.LoadFromJSON(JSON: TJSONObject);
  384. begin
  385. Schema:=JSON.AsJSON;
  386. end;
  387. { TBaseObjectList }
  388. function TBaseObjectList.GetO(Aindex : Integer): TBaseObject;
  389. begin
  390. Result:=TBaseObject(FList[AIndex]);
  391. end;
  392. procedure TBaseObjectList.SetO(Aindex : Integer; AValue: TBaseObject);
  393. begin
  394. FList[AIndex]:=AValue;
  395. end;
  396. class function TBaseObjectList.ObjectClass: TBaseObjectClass;
  397. begin
  398. Result:=TBaseObject;
  399. end;
  400. function TBaseObjectList.DoCreateEnumerator(AEnumClass: TBaseListEnumeratorClass
  401. ): TBaseListEnumerator;
  402. begin
  403. Result:=AEnumClass.Create(FList);
  404. end;
  405. constructor TBaseObjectList.Create(AOptions: TObjectOptions);
  406. begin
  407. inherited Create(AOptions);
  408. FList:=TFPObjectList.Create;
  409. end;
  410. destructor TBaseObjectList.Destroy;
  411. begin
  412. FreeAndNil(FList);
  413. inherited Destroy;
  414. end;
  415. function TBaseObjectList.GetEnumerator: TBaseListEnumerator;
  416. begin
  417. Result:=TBaseListEnumerator.Create(FList);
  418. end;
  419. function TBaseObjectList.AddObject(const AKind: String): TBaseObject;
  420. Var
  421. C : TBaseObjectClass;
  422. begin
  423. if (AKind<>'') then
  424. begin
  425. C:=RestFactory.GetObjectClass(AKind);
  426. if Not C.InheritsFrom(ObjectClass) then
  427. Raise ERestAPI.CreateFmt('Cannot add object of kind "%s" to list, associated class "%s" is not a descendent of list class "%s"',[AKind,C.ClassName,ObjectClass.ClassName]);
  428. end;
  429. Result:=ObjectClass.Create;
  430. FList.Add(Result);
  431. end;
  432. constructor TBAseListEnumerator.Create(AList: TFPObjectList);
  433. begin
  434. inherited Create;
  435. FList := AList;
  436. FPosition := -1;
  437. end;
  438. function TBaseListEnumerator.GetCurrent: TBaseObject;
  439. begin
  440. Result := TBaseObject(FList[FPosition]);
  441. end;
  442. function TBaseListEnumerator.MoveNext: Boolean;
  443. begin
  444. Inc(FPosition);
  445. Result := FPosition < FList.Count;
  446. end;
  447. { TBaseObject }
  448. function TBaseObject.GetDynArrayProp(P: PPropInfo): Pointer;
  449. begin
  450. Result:=Pointer(GetObjectProp(Self,P));
  451. end;
  452. { $DEFINE DUMPARRAY}
  453. {$IFDEF DUMPARRAY}
  454. Procedure DumpArray(ClassName,N : String; P : Pointer);
  455. Type
  456. pdynarray = ^tdynarray;
  457. tdynarray = packed record
  458. refcount : ptrint;
  459. high : tdynarrayindex;
  460. end;
  461. Var
  462. R : pdynarray;
  463. begin
  464. if P=Nil then
  465. Writeln(ClassName,' property ',N, ' is nil')
  466. else
  467. begin
  468. r:=pdynarray(p-sizeof(tdynarray));
  469. Writeln(ClassName,' property ',N, ' has ref count ',r^.refcount,' and high ',r^.high);
  470. end;
  471. end;
  472. {$ENDIF}
  473. procedure TBaseObject.SetDynArrayProp(P: PPropInfo; AValue: Pointer);
  474. begin
  475. {$IFDEF DUMPARRAY}
  476. DumpArray(ClassName+' (set)',P^.PropType^.Name,AValue);
  477. {$ENDIF}
  478. SetObjectProp(Self,P,TObject(AValue));
  479. {$IFDEF DUMPARRAY}
  480. DumpArray(ClassName+' (check)',P^.PropType^.Name,AValue);
  481. {$ENDIF}
  482. end;
  483. procedure TBaseObject.SetObjectOptions(AValue: TObjectOptions);
  484. begin
  485. if FObjectOptions=AValue then Exit;
  486. FObjectOptions:=AValue;
  487. if ooStartRecordingChanges in FObjectOptions then
  488. StartRecordPropertyChanges
  489. end;
  490. procedure TBaseObject.MarkPropertyChanged(AIndex: Integer);
  491. begin
  492. If Assigned(FBits) then
  493. FBits.SetOn(GetParentPropCount+(AIndex shr IndexShift));
  494. end;
  495. function TBaseObject.IsDateTimeProp(Info: PTypeInfo): Boolean;
  496. begin
  497. Result:=DateTimePropType(Info)<>dtNone;
  498. end;
  499. function TBaseObject.DateTimePropType(Info: PTypeInfo): TDateTimeType;
  500. begin
  501. Result:=dtNone;
  502. if (Info=TypeInfo(TDateTime)) then
  503. Result:=dtDateTime
  504. else if (Info=TypeInfo(TDate)) then
  505. Result:=dtDate
  506. else if (Info=TypeInfo(TTime)) then
  507. Result:=dtTime
  508. end;
  509. procedure TBaseObject.ClearProperty(P: PPropInfo);
  510. begin
  511. Case P^.PropType^.Kind of
  512. tkInteger,
  513. tkChar,
  514. tkEnumeration,
  515. tkBool,
  516. tkSet : SetOrdProp(Self,P,0);
  517. tkFloat : SetFloatProp(Self,P,0.0);
  518. tkSString,
  519. tkLString,
  520. tkUChar,
  521. tkAString: SetStrProp(Self,P,'');
  522. tkWChar,
  523. tkWString: SetWideStrProp(Self,P,'');
  524. tkUString: SetUnicodeStrProp(Self,P,'');
  525. tkInt64,
  526. tkQWord : SetInt64Prop(Self,P,0);
  527. tkClass :
  528. begin
  529. GetObjectProp(Self,P).Free;
  530. SetObjectProp(Self,P,Nil);
  531. end
  532. else
  533. // Do nothing
  534. end;
  535. end;
  536. procedure TBaseObject.SetBooleanProperty(P: PPropInfo; AValue: Boolean);
  537. begin
  538. SetOrdProp(Self,P,Ord(AValue));
  539. end;
  540. procedure TBaseObject.SetFloatProperty(P: PPropInfo; AValue: Extended);
  541. begin
  542. SetFloatProp(Self,P,AValue);
  543. end;
  544. procedure TBaseObject.SetIntegerProperty(P: PPropInfo; AValue: Integer);
  545. begin
  546. SetOrdProp(Self,P,AValue);
  547. end;
  548. procedure TBaseObject.SetInt64Property(P: PPropInfo; AValue: Int64);
  549. begin
  550. SetInt64Prop(Self,P,AValue);
  551. end;
  552. {$ifndef ver2_6}
  553. procedure TBaseObject.SetQWordProperty(P: PPropInfo; AValue: QWord);
  554. begin
  555. SetInt64Prop(Self,P,Int64(AValue));
  556. end;
  557. {$endif}
  558. procedure TBaseObject.SetStringProperty(P: PPropInfo; AValue: String);
  559. Var
  560. D : TDateTime;
  561. begin
  562. if not IsDateTimeProp(P^.PropType) then
  563. SetStrProp(Self,P,AValue)
  564. else if TryRFC3339ToDateTime(AValue,D) then
  565. SetFloatProp(Self,P,D)
  566. else
  567. SetFloatProp(Self,P,0)
  568. end;
  569. procedure TBaseObject.SetArrayProperty(P: PPropInfo; AValue: TJSONArray);
  570. procedure SetObjectArrayProp(PropAsPtr: Pointer;
  571. const TypeName: ShortString;
  572. const ClassType: TClass;
  573. const JSONArray: TJSONArray);
  574. var
  575. ObjectArray: TObjectArray;
  576. BaseObject: TBaseObject;
  577. Idx: Integer;
  578. begin
  579. ObjectArray := TObjectArray(PropAsPtr);
  580. // Free all objects
  581. for Idx := Low(ObjectArray) to High(ObjectArray) do
  582. FreeAndNil(ObjectArray[Idx]);
  583. SetLength(ObjectArray, JSONArray.Count);
  584. for Idx := Low(ObjectArray) to High(ObjectArray) do
  585. begin
  586. BaseObject := CreateObject(TypeName, ClassType);
  587. ObjectArray[Idx] := BaseObject;
  588. BaseObject.LoadFromJSON(JSONArray.Objects[Idx]);
  589. end;
  590. end;
  591. procedure SetFloatArrayProp(PropAsPtr: Pointer;
  592. const JSONArray: TJSONArray);
  593. var
  594. FloatArray: TFloatArray;
  595. Idx: Integer;
  596. begin
  597. FloatArray := TFloatArray(PropAsPtr);
  598. SetLength(FloatArray, JSONArray.Count);
  599. for Idx := Low(FloatArray) to High(FloatArray) do
  600. FloatArray[Idx] := JSONArray.Floats[Idx];
  601. end;
  602. procedure SetDateTimeArrayProp(PropAsPtr: Pointer;
  603. const JSONArray: TJSONArray);
  604. var
  605. DateTimeArray: TDateTimeArray;
  606. Idx: Integer;
  607. begin
  608. DateTimeArray := TDateTimeArray(PropAsPtr);
  609. SetLength(DateTimeArray, JSONArray.Count);
  610. for Idx := Low(DateTimeArray) to High(DateTimeArray) do
  611. DateTimeArray[Idx] := RFC3339ToDateTime(JSONArray.Strings[Idx]);
  612. end;
  613. procedure SetInt64ArrayProp(PropAsPtr: Pointer;
  614. const JSONArray: TJSONArray);
  615. var
  616. Int64Array: TInt64Array;
  617. Idx: Integer;
  618. begin
  619. Int64Array := TInt64Array(PropAsPtr);
  620. SetLength(Int64Array, JSONArray.Count);
  621. for Idx := Low(Int64Array) to High(Int64Array) do
  622. Int64Array[Idx] := JSONArray.Int64s[Idx];
  623. end;
  624. procedure SetBooleanArrayProp(PropAsPtr: Pointer;
  625. const JSONArray: TJSONArray);
  626. var
  627. BooleanArray: TBooleanArray;
  628. Idx: Integer;
  629. begin
  630. BooleanArray := TBooleanArray(PropAsPtr);
  631. SetLength(BooleanArray, JSONArray.Count);
  632. for Idx := Low(BooleanArray) to High(BooleanArray) do
  633. BooleanArray[Idx] := JSONArray.Booleans[Idx];
  634. end;
  635. procedure SetIntegerArrayProp(PropAsPtr: Pointer;
  636. const JSONArray: TJSONArray);
  637. var
  638. IntegerArray: TIntegerArray;
  639. Idx: Integer;
  640. begin
  641. IntegerArray := TIntegerArray(PropAsPtr);
  642. SetLength(IntegerArray, JSONArray.Count);
  643. for Idx := Low(IntegerArray) to High(IntegerArray) do
  644. IntegerArray[Idx] := JSONArray.Integers[Idx];
  645. end;
  646. procedure SetUnicodeStringArrayProp(PropAsPtr: Pointer;
  647. const JSONArray: TJSONArray);
  648. var
  649. UnicodeStringArray: TUnicodeStringArray;
  650. Idx: Integer;
  651. begin
  652. UnicodeStringArray := TUnicodeStringArray(PropAsPtr);
  653. SetLength(UnicodeStringArray, JSONArray.Count);
  654. for Idx := Low(UnicodeStringArray) to High(UnicodeStringArray) do
  655. UnicodeStringArray[Idx] := UTF8Decode(JSONArray.Strings[Idx]);
  656. end;
  657. procedure SetStringArrayProp(PropAsPtr: Pointer;
  658. const JSONArray: TJSONArray);
  659. var
  660. Idx: Integer;
  661. StringArray: TStringArray;
  662. begin
  663. StringArray := TStringArray(PropAsPtr);
  664. SetLength(StringArray, JSONArray.Count);
  665. for Idx := Low(StringArray) to High(StringArray) do
  666. StringArray[Idx] := JSONArray.Strings[Idx];
  667. end;
  668. Var
  669. T : PTypeData;
  670. L : TBaseObjectList;
  671. D : TJSONEnum;
  672. PTD : PTypeData;
  673. ET : PTypeInfo;
  674. AN : String;
  675. AP : Pointer;
  676. S : TJSONSchema;
  677. begin
  678. if P^.PropType^.Kind=tkClass then
  679. begin
  680. T:=GetTypeData(P^.PropType);
  681. if T^.ClassType.InheritsFrom(TBaseObjectList) then
  682. begin
  683. L:=TBaseObjectList(TBaseObjectClass(T^.ClassType).Create);
  684. SetObjectProp(Self,P,L); //what if there is an existing object, are we clobbering it?
  685. For D in AValue do
  686. L.AddObject('').LoadFromJSON(D.Value as TJSONObject);
  687. end
  688. else if T^.ClassType.InheritsFrom(TJSONSchema) then
  689. begin
  690. S:=TJSONSchema.Create;
  691. S.SetArrayProperty(P,AValue);
  692. SetObjectProp(Self,P,S); //what if there is an existing object, are we clobbering it?
  693. end
  694. else
  695. Raise ERESTAPI.CreateFmt('Unsupported class %s for property %s',[T^.ClassType.ClassName,P^.Name]);
  696. end
  697. else if P^.PropType^.Kind=tkDynArray then
  698. begin
  699. // Get array value
  700. AP:=GetObjectProp(Self,P); //NOTE: AP is dynanmic array as an untyped pointer
  701. //Getting it like this bypasses the reference count management
  702. //Be careful what do we with it to avoid leaking memory.
  703. PTD:=GetTypeData(P^.PropType);
  704. ET:=PTD^.ElType2;
  705. AN:=ET^.Name;
  706. case ET^.Kind of
  707. tkClass: SetObjectArrayProp(AP, ET^.Name, GetTypeData(ET)^.ClassType, AValue);
  708. tkFloat:
  709. if IsDateTimeProp(ET) then
  710. SetDateTimeArrayProp(AP, AValue)
  711. else
  712. SetFloatArrayProp(AP, AValue);
  713. tkInt64: SetInt64ArrayProp(AP, AValue);
  714. tkBool: SetBooleanArrayProp(AP, AValue);
  715. tkInteger: SetIntegerArrayProp(AP, AValue);
  716. tkUstring,
  717. tkWstring: SetUnicodeStringArrayProp(AP, AValue);
  718. tkString,
  719. tkAstring,
  720. tkLString: SetStringArrayProp(AP, AValue);
  721. else
  722. Raise ERESTAPI.CreateFmt('%s: unsupported array element type for property of type %s: %s',[ClassName,AN,GetEnumName(TypeInfo(TTypeKind),Ord(ET^.Kind))]);
  723. end;
  724. end;
  725. end;
  726. procedure TBaseObject.SetObjectProperty(P: PPropInfo; AValue: TJSONObject);
  727. Var
  728. O : TBaseObject;
  729. A: Pointer;
  730. T : PTypeData;
  731. D : TJSONEnum;
  732. AN : String;
  733. I : Integer;
  734. L : TBaseObjectList;
  735. NL : TBaseNamedObjectList;
  736. begin
  737. if P^.PropType^.Kind=tkDynArray then
  738. begin
  739. A:=GetDynArrayProp(P);
  740. For I:=0 to Length(TObjectArray(A))-1 do
  741. FreeAndNil(TObjectArray(A)[i]);
  742. SetLength(TObjectArray(A),AValue.Count);
  743. T:=GetTypeData(P^.PropType);
  744. AN:=T^.ElType2^.Name;
  745. I:=0;
  746. For D in AValue do
  747. begin
  748. O:=CreateObject(AN);
  749. TObjectArray(A)[I]:=O;
  750. // Writeln(ClassName,' Adding instance of type: ',AN,' for key ',D.Key);
  751. if IsPublishedProp(O,'name') then
  752. SetStrProp(O,'name',D.Key);
  753. O.LoadFromJSON(D.Value as TJSONObject);
  754. Inc(I);
  755. end;
  756. // Writeln(ClassName,' Done with array ',P^.Name,', final array length: ', Length(TObjectArray(A)));
  757. SetDynArrayProp(P,A);
  758. Exit;
  759. end;
  760. if Not (P^.PropType^.Kind=tkClass) then
  761. Raise ERESTAPI.CreateFmt('%s: Unsupported type for property %s',[ClassName,P^.Name]);
  762. T:=GetTypeData(P^.PropType);
  763. if T^.ClassType.InheritsFrom(TBaseObject) then
  764. begin
  765. O:=TBaseObject(GetObjectProp(Self,P,TBaseObject));
  766. if O=Nil then
  767. begin
  768. O:=TBaseObjectClass(T^.ClassType).Create;
  769. SetObjectProp(Self,P,O);
  770. end;
  771. O.LoadFromJSON(AValue);
  772. end
  773. else if T^.ClassType.InheritsFrom(TBaseObjectList) then
  774. begin
  775. L:=TBaseObjectList(TBaseObjectClass(T^.ClassType).Create);
  776. SetObjectProp(Self,P,L);
  777. For D in AValue do
  778. L.AddObject('').LoadFromJSON(D.Value as TJSONObject);
  779. end
  780. else if T^.ClassType.InheritsFrom(TBaseNamedObjectList) then
  781. begin
  782. NL:=TBaseNamedObjectList(TBaseObjectClass(T^.ClassType).Create);
  783. SetObjectProp(Self,P,L);
  784. For D in AValue do
  785. NL.AddObject(D.Key,'').LoadFromJSON(D.Value as TJSONObject);
  786. end
  787. else
  788. Raise ERESTAPI.CreateFmt('%s: unsupported class %s for property %s',[ClassName, T^.ClassType.ClassName,P^.Name]);
  789. end;
  790. procedure TBaseObject.SetSetProperty(P: PPropInfo; AValue: TJSONArray);
  791. type
  792. TSet = set of 0..31;
  793. var
  794. S,I,V : Integer;
  795. CurValue: string;
  796. EnumTyp: PTypeInfo;
  797. EnumTypData: PTypeData;
  798. begin
  799. S:=0;
  800. EnumTyp:=GetTypeData(P^.PropType)^.CompType;
  801. EnumTypData:=GetTypeData(EnumTyp);
  802. For I:=0 to AValue.Count-1 do
  803. begin
  804. CurValue:=AValue.Strings[i];
  805. if Not TryStrToInt(CurValue,V) then
  806. V:=GetEnumValue(EnumTyp,CurValue);
  807. if (V<EnumTypData^.MinValue) or (V>EnumTypData^.MaxValue) or (V>31) then
  808. Raise ERESTAPI.CreateFmt('%s: Invalid value %s for property %s',[ClassName, CurValue,P^.Name]);
  809. Include(TSet(S),V);
  810. end;
  811. SetOrdProp(Self,P,S);
  812. end;
  813. procedure TBaseObject.SetEnumProperty(P: PPropInfo; AValue: TJSONData);
  814. Var
  815. I : Integer;
  816. begin
  817. I:=GetEnumValue(P^.PropType,AValue.AsString);
  818. if (I=-1) then
  819. Raise ERESTAPI.CreateFmt('%s: Invalid value %s for property %s',[ClassName, AValue.AsString,P^.Name]);
  820. SetOrdProp(Self,P,I);
  821. end;
  822. function TBaseObject.GetBooleanProperty(P: PPropInfo): TJSONData;
  823. begin
  824. Result:=TJSONBoolean.Create(GetOrdProp(Self,P)<>0);
  825. end;
  826. function TBaseObject.GetIntegerProperty(P: PPropInfo): TJSONData;
  827. begin
  828. Result:=TJSONIntegerNumber.Create(GetOrdProp(Self,P));
  829. end;
  830. function TBaseObject.GetInt64Property(P: PPropInfo): TJSONData;
  831. begin
  832. Result:=TJSONInt64Number.Create(GetInt64Prop(Self,P));
  833. end;
  834. function TBaseObject.GetQwordProperty(P: PPropInfo): TJSONData;
  835. begin
  836. Result:=TJSONInt64Number.Create(Int64(GetInt64Prop(Self,P)));
  837. end;
  838. function TBaseObject.GetFloatProperty(P: PPropInfo): TJSONData;
  839. begin
  840. Case DateTimePropType(P^.PropType) of
  841. dtDateTime:
  842. Result:=TJSONString.Create(DateTimeToRFC3339(GetFloatProp(Self,P)));
  843. dtDate:
  844. Result:=TJSONString.Create(DateToRFC3339(GetFloatProp(Self,P)));
  845. dtTime:
  846. Result:=TJSONString.Create(TimeToRFC3339(GetFloatProp(Self,P))) ;
  847. else
  848. Result:=TJSONFloatNumber.Create(GetFloatProp(Self,P));
  849. end;
  850. end;
  851. function TBaseObject.GetStringProperty(P: PPropInfo): TJSONData;
  852. begin
  853. Result:=TJSONString.Create(GetStrProp(Self,P));
  854. end;
  855. function TBaseObject.GetSetProperty(P: PPropInfo): TJSONData;
  856. type
  857. TSet = set of 0..31;
  858. var
  859. Typ: PTypeInfo;
  860. S, i: integer;
  861. begin
  862. Result:=TJSONArray.Create;
  863. Typ:=GetTypeData(P^.PropType)^.CompType;
  864. S:=GetOrdProp(Self,P);
  865. for i:=Low(TSet) to High(TSet) do
  866. if (i in TSet(S)) then
  867. TJSONArray(Result).Add(TJSONString.Create(GetEnumName(Typ,i)));
  868. end;
  869. function TBaseObject.GetEnumeratedProperty(P: PPropInfo): TJSONData;
  870. begin
  871. Result:=TJSONString.Create(GetEnumProp(Self,P));
  872. end;
  873. function TBaseObject.GetArrayProperty(P: PPropInfo): TJSONData;
  874. Var
  875. AO : TObject;
  876. I : Integer;
  877. ET : PTypeInfo;
  878. PTD : PTypeData;
  879. AP : Pointer;
  880. A : TJSONArray;
  881. O : TJSONObject;
  882. begin
  883. A:=TJSONArray.Create;
  884. Result:=A;
  885. // Get array value type
  886. AP:=GetObjectProp(Self,P);
  887. PTD:=GetTypeData(P^.PropType);
  888. ET:=PTD^.ElType2;
  889. // Fill in all elements
  890. Case ET^.Kind of
  891. tkClass:
  892. For I:=0 to Length(TObjectArray(AP))-1 do
  893. begin
  894. // Writeln(ClassName,' Adding instance of type: ',AN);
  895. AO:=TObjectArray(AP)[I];
  896. if AO.InheritsFrom(TBaseObject) then
  897. begin
  898. O:=TJSONObject.Create;
  899. A.Add(O);
  900. TBaseObject(AO).SaveToJSON(O);
  901. end;
  902. end;
  903. tkFloat:
  904. if IsDateTimeProp(ET) then
  905. For I:=0 to Length(TDateTimeArray(AP))-1 do
  906. A.Add(TJSONString.Create(DateTimeToRFC3339(TDateTimeArray(AP)[I])))
  907. else
  908. For I:=0 to Length(TFloatArray(AP))-1 do
  909. A.Add(TJSONFloatNumber.Create(TFloatArray(AP)[I]));
  910. tkInt64:
  911. For I:=0 to Length(TInt64Array(AP))-1 do
  912. A.Add(TJSONInt64Number.Create(TInt64Array(AP)[I]));
  913. tkBool:
  914. For I:=0 to Length(TInt64Array(AP))-1 do
  915. A.Add(TJSONBoolean.Create(TBooleanArray(AP)[I]));
  916. tkInteger :
  917. For I:=0 to Length(TIntegerArray(AP))-1 do
  918. A.Add(TJSONIntegerNumber.Create(TIntegerArray(AP)[I]));
  919. tkUstring,
  920. tkWstring :
  921. For I:=0 to Length(TUnicodeStringArray(AP))-1 do
  922. A.Add(TJSONString.Create(TUnicodeStringArray(AP)[I]));
  923. tkString,
  924. tkAstring,
  925. tkLString :
  926. For I:=0 to Length(TStringArray(AP))-1 do
  927. A.Add(TJSONString.Create(TStringArray(AP)[I]));
  928. else
  929. Raise ERESTAPI.CreateFmt('%s: unsupported array element type : %s',[ClassName,GetEnumName(TypeInfo(TTypeKind),Ord(ET^.Kind))]);
  930. end;
  931. end;
  932. function TBaseObject.GetObjectProperty(P: PPropInfo): TJSONData;
  933. Var
  934. O : TObject;
  935. begin
  936. O:=GetObjectProp(Self,P);
  937. if (O is TBaseObject) then
  938. Result:=TBaseObject(O).SaveToJSON
  939. else
  940. Result:=Nil; // maybe we need to add an option to return null ?
  941. end;
  942. procedure TBaseObject.ClearChildren(ChildTypes: TChildTypes);
  943. Type
  944. TObjectArr = Array of TObject;
  945. var
  946. PL: PPropList;
  947. P : PPropInfo;
  948. i,j,count,len:integer;
  949. A : pointer;
  950. PTD : PTypeData;
  951. O : TObject;
  952. begin
  953. Count:=GetPropList(Self,PL);
  954. try
  955. for i:=0 to Count-1 do
  956. begin
  957. P:=PL^[I];
  958. case P^.PropType^.Kind of
  959. tkClass:
  960. if (ctObject in ChildTypes) then
  961. begin
  962. // Writeln(ClassName,' Examining object: ',P^.Name);
  963. O:=GetObjectProp(Self,P);
  964. O.Free;
  965. SetObjectProp(Self,P,Nil);
  966. end;
  967. tkDynArray:
  968. if (ctArray in ChildTypes) then
  969. begin
  970. len:=Length(P^.PropType^.Name);
  971. PTD:=GetTypeData(P^.PropType);
  972. if PTD^.ElType2^.Kind=tkClass then
  973. begin
  974. A:=GetDynArrayProp(P);
  975. {$IFDEF DUMPARRAY}
  976. DumpArray(ClassName+' (clear)',P^.PropType^.Name,A);
  977. {$ENDIF}
  978. // Writeln(ClassName,' Examining array: ',P^.Name,'Count:',Length(TObjectArr(A)));
  979. For J:=0 to Length(TObjectArr(A))-1 do
  980. begin
  981. FreeAndNil(TObjectArr(A)[J]);
  982. end;
  983. end;
  984. // Length is set to nil by destructor
  985. end;
  986. end;
  987. end;
  988. finally
  989. FreeMem(PL);
  990. end;
  991. end;
  992. class function TBaseObject.ClearChildTypes: TChildTypes;
  993. begin
  994. Result:=[ctArray,ctObject]
  995. end;
  996. {$IFDEF DEBUGBASEOBJMEMLEAK}
  997. Var
  998. ObjCounter : TStrings;
  999. {$ENDIF}
  1000. constructor TBaseObject.Create(AOptions: TObjectOptions);
  1001. begin
  1002. {$IFDEF DEBUGBASEOBJMEMLEAK}
  1003. if ObjCounter=Nil then
  1004. ObjCounter:=TStringList.Create;
  1005. ObjCounter.Values[ClassName]:=IntToStr(StrToIntDef(ObjCounter.Values[ClassName],0)+1);
  1006. {$ENDIF}
  1007. ObjectOptions:=AOptions;
  1008. // Do nothing
  1009. end;
  1010. destructor TBaseObject.Destroy;
  1011. begin
  1012. StopRecordPropertyChanges;
  1013. {$IFDEF DEBUGBASEOBJMEMLEAK}
  1014. ObjCounter.Values[ClassName]:=IntToStr(StrToIntDef(ObjCounter.Values[ClassName],0)-1);
  1015. {$ENDIF}
  1016. FreeAndNil(fadditionalProperties);
  1017. if ClearChildTypes<>[] then
  1018. ClearChildren(ClearChildTypes);
  1019. inherited;
  1020. end;
  1021. procedure TBaseObject.StartRecordPropertyChanges;
  1022. begin
  1023. if Assigned(FBits) then
  1024. FBits.ClearAll
  1025. else
  1026. FBits:=TBits.Create(GetTotalPropCount);
  1027. end;
  1028. procedure TBaseObject.ClearPropertyChanges;
  1029. begin
  1030. FBits.ClearAll;
  1031. end;
  1032. procedure TBaseObject.StopRecordPropertyChanges;
  1033. begin
  1034. FreeAndNil(FBits);
  1035. end;
  1036. function TBaseObject.IsPropertyModified(Info: PPropInfo): Boolean;
  1037. begin
  1038. Result:=Not Assigned(FBits) or FBits.Bits[Info^.NameIndex]
  1039. end;
  1040. function TBaseObject.IsPropertyModified(const AName: String): Boolean;
  1041. begin
  1042. Result:=IsPropertyModified(GetPropInfo(Self,AName));
  1043. end;
  1044. function TBaseObject.GetAdditionalProperties: TJSONObject;
  1045. begin
  1046. if (fAdditionalProperties=Nil) and AllowAdditionalProperties then
  1047. fAdditionalProperties:=TJSONObject.Create;
  1048. Result:=fAdditionalProperties
  1049. end;
  1050. {$IFDEF VER2_6}
  1051. procedure TBaseObject.SetArrayLength(Const AName: String; ALength: Longint);
  1052. begin
  1053. Raise ERestAPI.CreateFmt('Unknown Array %s',[AName]);
  1054. end;
  1055. {$ENDIF}
  1056. class function TBaseObject.AllowAdditionalProperties: Boolean;
  1057. begin
  1058. Result:=False;
  1059. end;
  1060. class function TBaseObject.ExportPropertyName(const AName: String): string;
  1061. begin
  1062. Result:=AName;
  1063. end;
  1064. class function TBaseObject.CleanPropertyName(const AName: String): string;
  1065. Const
  1066. KW=';absolute;and;array;asm;begin;case;const;constructor;destructor;div;do;'+
  1067. 'downto;else;end;file;for;function;goto;if;implementation;in;inherited;'+
  1068. 'inline;interface;label;mod;nil;not;object;of;on;operator;or;packed;'+
  1069. 'procedure;program;record;reintroduce;repeat;self;set;shl;shr;string;then;'+
  1070. 'to;type;unit;until;uses;var;while;with;xor;dispose;exit;false;new;true;'+
  1071. 'as;class;dispinterface;except;exports;finalization;finally;initialization;'+
  1072. 'inline;is;library;on;out;packed;property;raise;resourcestring;threadvar;try;'+
  1073. 'private;published;length;setlength;';
  1074. Var
  1075. I : Integer;
  1076. begin
  1077. Result:=Aname;
  1078. For I:=Length(Result) downto 1 do
  1079. If Not ((Upcase(Result[i]) in ['_','A'..'Z'])
  1080. or ((I>1) and (Result[i] in (['0'..'9'])))) then
  1081. Delete(Result,i,1);
  1082. if Pos(';'+lowercase(Result)+';',KW)<>0 then
  1083. Result:='_'+Result
  1084. end;
  1085. class function TBaseObject.CreateObject(const AKind: String; AClass: TClass = Nil): TBaseObject;
  1086. Var
  1087. C : TBaseObjectClass;
  1088. begin
  1089. C:=RESTFactory.GetObjectClass(AKind);
  1090. if (C=Nil) and Assigned(AClass) and AClass.InheritsFrom(TBaseObject) then
  1091. C:=TBaseObjectClass(AClass);
  1092. if C<>Nil then
  1093. Result:=C.Create
  1094. else
  1095. Raise ERESTAPI.CreateFmt('Unknown class : "%s"',[AKind]);
  1096. // Do nothing
  1097. end;
  1098. class procedure TBaseObject.RegisterObject;
  1099. begin
  1100. RESTFactory.RegisterObject(Self);
  1101. end;
  1102. class function TBaseObject.ObjectRestKind: String;
  1103. begin
  1104. Result:=ClassName;
  1105. end;
  1106. class function TBaseObject.GetTotalPropCount: Integer;
  1107. begin
  1108. Result:=GetTypeData(ClassInfo)^.PropCount;
  1109. end;
  1110. class function TBaseObject.GetCurrentPropCount: Integer;
  1111. begin
  1112. Result:=CountProperties(ClassInfo,False);
  1113. end;
  1114. class function TBaseObject.GetParentPropCount: Integer;
  1115. begin
  1116. if (ClassParent=TBaseObject) or (ClassParent=Nil) then
  1117. Result:=0
  1118. else
  1119. Result:=TBaseObjectClass(ClassParent).GetTotalPropCount;
  1120. end;
  1121. procedure TBaseObject.LoadPropertyFromJSON(const AName: String; JSON: TJSONData
  1122. );
  1123. Var
  1124. P : PPropInfo;
  1125. o : TJSONObject;
  1126. begin
  1127. // Writeln(ClassName,' loading : ',ANAme,' -> ',CleanPropertyName(aName));
  1128. P:=GetPropInfo(Self,CleanPropertyName(aName));
  1129. if (P=Nil) then
  1130. begin
  1131. o:=additionalProperties;
  1132. if o=Nil then
  1133. Raise ERESTAPI.CreateFmt('%s : Unknown property "%s"',[ClassName,AName]);
  1134. o.Add(aName,JSON.Clone);
  1135. end
  1136. else
  1137. case JSON.JSONType of
  1138. jtstring :
  1139. if (P^.PropType^.Kind=tkEnumeration) then
  1140. SetEnumProperty(P,JSON)
  1141. else
  1142. SetStringproperty(P,JSON.AsString);
  1143. jtNumber :
  1144. case TJSONNumber(JSON).NumberType of
  1145. ntFloat : SetFloatProperty(P,JSON.asFloat);
  1146. ntInteger : SetIntegerProperty(P,JSON.asInteger);
  1147. ntInt64 : SetInt64Property(P,JSON.asInt64);
  1148. {$ifndef ver2_6}
  1149. ntqword : SetQWordProperty(P,JSON.asQWord);
  1150. {$endif}
  1151. end;
  1152. jtNull : ClearProperty(P);
  1153. jtBoolean : SetBooleanProperty(P,json.AsBoolean);
  1154. jtArray :
  1155. if P^.PropType^.Kind=tkSet then
  1156. SetSetProperty(P,TJSONArray(json))
  1157. else
  1158. SetArrayProperty(P,TJSONArray(json));
  1159. jtObject : SetObjectProperty(P,TJSONObject(json));
  1160. end;
  1161. end;
  1162. function TBaseObject.SavePropertyToJSON(Info: PPropInfo): TJSONData;
  1163. begin
  1164. Result:=Nil;
  1165. if Not IsPropertyModified(Info) then
  1166. Exit;
  1167. Case Info^.PropType^.Kind of
  1168. tkSet : Result:=GetSetProperty(Info);
  1169. tkEnumeration : Result:=GetEnumeratedProperty(Info);
  1170. tkAstring,
  1171. tkUstring,
  1172. tkWString,
  1173. tkwchar,
  1174. tkuchar,
  1175. tkString : Result:=GetStringProperty(Info);
  1176. tkFloat : Result:=GetFloatProperty(Info);
  1177. tkBool : Result:=GetBooleanProperty(Info);
  1178. tkClass : Result:=GetObjectProperty(Info);
  1179. tkDynArray : Result:=GetArrayProperty(Info);
  1180. tkQWord : Result:=GetQWordProperty(Info);
  1181. tkInt64 : Result:=GetInt64Property(Info);
  1182. tkInteger : Result:=GetIntegerProperty(Info);
  1183. end;
  1184. end;
  1185. procedure TBaseObject.LoadFromJSON(JSON: TJSONObject);
  1186. Var
  1187. D : TJSONEnum;
  1188. begin
  1189. StopRecordPropertyChanges;
  1190. For D in JSON Do
  1191. LoadPropertyFromJSON(D.Key,D.Value);
  1192. StartRecordPropertyChanges;
  1193. end;
  1194. procedure TBaseObject.SaveToJSON(JSON: TJSONObject);
  1195. var
  1196. PL: PPropList;
  1197. P : PPropInfo;
  1198. I,Count : integer;
  1199. D : TJSONData;
  1200. begin
  1201. Count:=GetPropList(Self,PL);
  1202. try
  1203. for i:=0 to Count-1 do
  1204. begin
  1205. P:=PL^[I];
  1206. D:=SavePropertyToJSON(P);
  1207. if (D<>Nil) then
  1208. JSON.add(ExportPropertyName(P^.Name),D);
  1209. end;
  1210. finally
  1211. FreeMem(PL);
  1212. end;
  1213. end;
  1214. function TBaseObject.SaveToJSON: TJSONObject;
  1215. begin
  1216. Result:=TJSONObject.Create;
  1217. try
  1218. SaveToJSON(Result);
  1219. except
  1220. FreeAndNil(Result);
  1221. Raise;
  1222. end;
  1223. end;
  1224. finalization
  1225. {$IFDEF DEBUGBASEOBJMEMLEAK}
  1226. if Assigned(ObjCounter) then
  1227. begin
  1228. Writeln(StdErr,'Object allocate-free count: ');
  1229. Writeln(StdErr,ObjCounter.Text);
  1230. FreeAndNil(ObjCounter);
  1231. end;
  1232. {$ENDIF}
  1233. FreeAndNil(Fact);
  1234. end.