restbase.pp 39 KB

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