jsondataset.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. {$mode objfpc}
  2. {$h+}
  3. unit JSONDataset;
  4. interface
  5. uses
  6. Types, JS, DB, Classes, SysUtils;
  7. type
  8. { TJSONFieldMapper }
  9. // This class is responsible for mapping the field objects of the records.
  10. TJSONFieldMapper = Class(TObject)
  11. Protected
  12. // Return row TJSONData instance with data for field 'FieldName' or 'FieldIndex'.
  13. Function GetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row : JSValue) : JSValue; virtual; abstract;
  14. // Same, but now based on TField.
  15. Function GetJSONDataForField(F : TField; Row : JSValue) : JSValue; virtual;
  16. // Set data for field 'FieldName' or 'FieldIndex' to supplied TJSONData instance in row
  17. procedure SetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row,Data : JSValue); virtual; abstract;
  18. // Set data for field TField to supplied TJSONData instance
  19. procedure SetJSONDataForField(F : TField; Row,Data : JSValue); virtual;
  20. // Create a new row.
  21. Function CreateRow : JSValue; virtual; abstract;
  22. end;
  23. // JSON has no date/time type, so we use a string field.
  24. // ExtJS provides the date/time format in it's field config: 'dateFormat'
  25. // The below field classes store this in the NNNFormat field.
  26. { TJSONDateField }
  27. TJSONDateField = Class(TDateField)
  28. private
  29. FDateFormat: String;
  30. Published
  31. Property DateFormat : String Read FDateFormat Write FDateFormat;
  32. end;
  33. { TJSONTimeField }
  34. TJSONTimeField = Class(TTimeField)
  35. private
  36. FTimeFormat: String;
  37. Published
  38. Property TimeFormat : String Read FTimeFormat Write FTimeFormat;
  39. end;
  40. { TJSONDateTimeField }
  41. TJSONDateTimeField = Class(TDateTimeField)
  42. private
  43. FDateTimeFormat: String;
  44. Published
  45. Property DateTimeFormat : String Read FDateTimeFormat Write FDateTimeFormat;
  46. end;
  47. { TBaseJSONDataSet }
  48. // basic JSON dataset. Does nothing ExtJS specific.
  49. TBaseJSONDataSet = class (TDataSet)
  50. private
  51. FMUS: Boolean;
  52. FOwnsData : Boolean;
  53. FDefaultList : TFPList;
  54. FCurrentList: TFPList;
  55. FCurrent: Integer;
  56. // Possible metadata to configure fields from.
  57. FMetaData : TJSObject;
  58. // This will contain the rows.
  59. FRows : TJSArray;
  60. FFieldMapper : TJSONFieldMapper;
  61. // When editing, this object is edited.
  62. FEditRow : JSValue;
  63. procedure SetMetaData(AValue: TJSObject);
  64. procedure SetRows(AValue: TJSArray);
  65. protected
  66. // dataset virtual methods
  67. function AllocRecordBuffer: TDataRecord; override;
  68. procedure FreeRecordBuffer(var Buffer: TDataRecord); override;
  69. procedure InternalInitRecord(var Buffer: TDataRecord); override;
  70. procedure GetBookmarkData(Buffer: TDataRecord; var Data: TBookmark); override;
  71. function GetBookmarkFlag(Buffer: TDataRecord): TBookmarkFlag; override;
  72. function GetRecord(Var Buffer: TDataRecord; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  73. function GetRecordSize: Word; override;
  74. procedure AddToRows(AValue: TJSArray);
  75. procedure InternalClose; override;
  76. procedure InternalDelete; override;
  77. procedure InternalFirst; override;
  78. procedure InternalGotoBookmark(ABookmark: TBookmark); override;
  79. procedure InternalLast; override;
  80. procedure InternalOpen; override;
  81. procedure InternalPost; override;
  82. procedure InternalInsert; override;
  83. procedure InternalEdit; override;
  84. procedure InternalCancel; override;
  85. procedure InternalInitFieldDefs; override;
  86. procedure InternalSetToRecord(Buffer: TDataRecord); override;
  87. function GetFieldClass(FieldType: TFieldType): TFieldClass; override;
  88. function IsCursorOpen: Boolean; override;
  89. procedure SetBookmarkFlag(Var Buffer: TDataRecord; Value: TBookmarkFlag); override;
  90. procedure SetBookmarkData(Var Buffer: TDataRecord; Data: TBookmark); override;
  91. function GetRecordCount: Integer; override;
  92. procedure SetRecNo(Value: Integer); override;
  93. function GetRecNo: Integer; override;
  94. Protected
  95. // New methods.
  96. // Called when dataset is closed. If OwnsData is true, metadata and rows are freed.
  97. Procedure FreeData; virtual;
  98. // Fill default list.
  99. procedure AddToList; virtual;
  100. Procedure FillList; virtual;
  101. // Convert MetaData object to FieldDefs.
  102. Procedure MetaDataToFieldDefs; virtual; abstract;
  103. // Initialize Date/Time info in all date/time fields. Called during InternalOpen
  104. procedure InitDateTimeFields; virtual;
  105. // Convert JSON date S to DateTime for Field F
  106. function ConvertDateTimeField(S: String; F: TField): TDateTime; virtual;
  107. // Format JSON date to from DT for Field F
  108. function FormatDateTimeField(DT : TDateTime; F: TField): String; virtual;
  109. // Create fieldmapper. A descendent MUST implement this.
  110. Function CreateFieldMapper : TJSONFieldMapper; virtual; abstract;
  111. // If True, then the dataset will free MetaData and FRows when it is closed.
  112. Property OwnsData : Boolean Read FownsData Write FOwnsData;
  113. // set to true if unknown field types should be handled as string fields.
  114. Property MapUnknownToStringType : Boolean Read FMUS Write FMUS;
  115. // Metadata
  116. Property MetaData : TJSObject Read FMetaData Write SetMetaData;
  117. // Rows
  118. Property Rows : TJSArray Read FRows Write SetRows;
  119. public
  120. constructor Create (AOwner: TComponent); override;
  121. destructor Destroy; override;
  122. function GetFieldData(Field: TField; Buffer: TDatarecord): JSValue; override;
  123. procedure SetFieldData(Field: TField; var Buffer: TDatarecord; AValue : JSValue); override;
  124. published
  125. Property FieldDefs;
  126. // redeclared data set properties
  127. property Active;
  128. property BeforeOpen;
  129. property AfterOpen;
  130. property BeforeClose;
  131. property AfterClose;
  132. property BeforeInsert;
  133. property AfterInsert;
  134. property BeforeEdit;
  135. property AfterEdit;
  136. property BeforePost;
  137. property AfterPost;
  138. property BeforeCancel;
  139. property AfterCancel;
  140. property BeforeDelete;
  141. property AfterDelete;
  142. property BeforeScroll;
  143. property AfterScroll;
  144. property OnCalcFields;
  145. property OnDeleteError;
  146. property OnEditError;
  147. property OnFilterRecord;
  148. property OnNewRecord;
  149. property OnPostError;
  150. end;
  151. { TExtJSJSONDataSet }
  152. // Base for ExtJS datasets. It handles MetaData conversion.
  153. TExtJSJSONDataSet = Class(TBaseJSONDataset)
  154. Private
  155. FFields : TJSArray;
  156. FIDField: String;
  157. FRoot: String;
  158. Protected
  159. // Data proxy support
  160. Procedure InternalOpen; override;
  161. function DoResolveRecordUpdate(anUpdate: TRecordUpdateDescriptor): Boolean; override;
  162. Function DataPacketReceived(ARequest: TDataRequest) : Boolean; override;
  163. Function GenerateMetaData : TJSObject;
  164. function ConvertDateFormat(S: String): String; virtual;
  165. Procedure MetaDataToFieldDefs; override;
  166. procedure InitDateTimeFields; override;
  167. function StringToFieldType(S: String): TFieldType;virtual;
  168. function GetStringFieldLength(F: TJSObject; AName: String; AIndex: Integer): integer; virtual;
  169. Public
  170. // Can be set directly if the dataset is closed.
  171. Property MetaData;
  172. // Can be set directly if the dataset is closed. If metadata is set, it must match the data.
  173. Property Rows;
  174. // Root of data array in data packet
  175. property Root : String Read FRoot Write FRoot;
  176. // property IDField
  177. property IDField : String Read FIDField Write FIDField;
  178. end;
  179. { TExtJSJSONObjectDataSet }
  180. // Use this dataset for data where the data is an array of objects.
  181. TExtJSJSONObjectDataSet = Class(TExtJSJSONDataSet)
  182. Protected
  183. Function CreateFieldMapper : TJSONFieldMapper; override;
  184. end;
  185. { TExtJSJSONArrayDataSet }
  186. // Use this dataset for data where the data is an array of arrays.
  187. TExtJSJSONArrayDataSet = Class(TExtJSJSONDataSet)
  188. Protected
  189. Function CreateFieldMapper : TJSONFieldMapper; override;
  190. end;
  191. { TJSONObjectFieldMapper }
  192. // Fieldmapper to be used when the data is in an object
  193. TJSONObjectFieldMapper = Class(TJSONFieldMapper)
  194. Public
  195. procedure SetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row,Data : JSValue); override;
  196. Function GetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row : JSValue) : JSValue; override;
  197. Function CreateRow : JSValue; override;
  198. end;
  199. { TJSONArrayFieldMapper }
  200. // Fieldmapper to be used when the data is in an array
  201. TJSONArrayFieldMapper = Class(TJSONFieldMapper)
  202. Public
  203. procedure SetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row,Data : JSValue); override;
  204. Function GetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row : JSValue) : JSValue; override;
  205. Function CreateRow : JSValue; override;
  206. end;
  207. EJSONDataset = Class(EDatabaseError);
  208. implementation
  209. uses DateUtils;
  210. { TJSONFieldMapper }
  211. function TJSONFieldMapper.GetJSONDataForField(F: TField; Row: JSValue ): JSValue;
  212. begin
  213. // This supposes that Index is correct, i.e. the field positions have not been changed.
  214. Result:=GetJSONDataForField(F.FieldName,F.Index,Row);
  215. end;
  216. procedure TJSONFieldMapper.SetJSONDataForField(F: TField; Row,Data: JSValue);
  217. begin
  218. SetJSONDataForField(F.FieldName,F.Index,Row,Data);
  219. end;
  220. { TJSONArrayDataSet }
  221. function TExtJSJSONArrayDataSet.CreateFieldMapper: TJSONFieldMapper;
  222. begin
  223. Result:=TJSONArrayFieldMapper.Create;
  224. end;
  225. { TJSONObjectDataSet }
  226. function TExtJSJSONObjectDataSet.CreateFieldMapper: TJSONFieldMapper;
  227. begin
  228. Result:=TJSONObjectFieldMapper.Create;
  229. end;
  230. { TJSONArrayFieldMapper }
  231. procedure TJSONArrayFieldMapper.SetJSONDataForField(const FieldName: String;
  232. FieldIndex: Integer; Row, Data: JSValue);
  233. begin
  234. TJSValueDynArray(Row)[FieldIndex]:=Data;
  235. end;
  236. function TJSONArrayFieldMapper.GetJSONDataForField(Const FieldName: String;
  237. FieldIndex: Integer; Row: JSValue): JSValue;
  238. begin
  239. Result:=TJSValueDynArray(Row)[FieldIndex];
  240. end;
  241. function TJSONArrayFieldMapper.CreateRow: JSValue;
  242. begin
  243. Result:=TJSArray.New;
  244. end;
  245. { TJSONObjectFieldMapper }
  246. procedure TJSONObjectFieldMapper.SetJSONDataForField(const FieldName: String;
  247. FieldIndex: Integer; Row, Data: JSValue);
  248. begin
  249. TJSObject(Row).Properties[FieldName]:=Data;
  250. end;
  251. function TJSONObjectFieldMapper.GetJSONDataForField(const FieldName: String;
  252. FieldIndex: Integer; Row: JSValue): JSValue;
  253. begin
  254. Result:=TJSObject(Row).Properties[FieldName];
  255. end;
  256. function TJSONObjectFieldMapper.CreateRow: JSValue;
  257. begin
  258. Result:=TJSObject.New;
  259. end;
  260. procedure TBaseJSONDataSet.SetMetaData(AValue: TJSObject);
  261. begin
  262. CheckInActive;
  263. FMetaData:=AValue;
  264. end;
  265. procedure TBaseJSONDataSet.AddToRows(AValue: TJSArray);
  266. begin
  267. if FRows=Nil then
  268. FRows:=AValue
  269. else
  270. begin
  271. FRows:=FRows.Concat(AValue);
  272. AddToList;
  273. end;
  274. end;
  275. procedure TBaseJSONDataSet.SetRows(AValue: TJSArray);
  276. begin
  277. if AValue=FRows then exit;
  278. CheckInActive;
  279. FRows:=Nil;
  280. AddToRows(AValue);
  281. end;
  282. function TBaseJSONDataSet.AllocRecordBuffer: TDataRecord;
  283. begin
  284. Result.data:=TJSObject.New;
  285. Result.bookmark:=null;
  286. Result.state:=rsNew;
  287. end;
  288. // the next two are particularly ugly.
  289. procedure TBaseJSONDataSet.InternalInitRecord(var Buffer: TDataRecord);
  290. begin
  291. // Writeln('TBaseJSONDataSet.InternalInitRecord');
  292. Buffer.Data:=FFieldMapper.CreateRow;
  293. Buffer.bookmark:=null;
  294. Buffer.state:=rsNew;
  295. end;
  296. procedure TBaseJSONDataSet.FreeRecordBuffer (var Buffer: TDataRecord);
  297. begin
  298. Buffer.Data:=Null;
  299. Buffer.bookmark:=null;
  300. Buffer.state:=rsNew;
  301. end;
  302. procedure TBaseJSONDataSet.GetBookmarkData(Buffer: TDataRecord; var Data: TBookmark);
  303. begin
  304. // writeln('Bookmark :',Buffer.bookmark);
  305. Data.Data:=Buffer.bookmark;
  306. end;
  307. function TBaseJSONDataSet.GetBookmarkFlag(Buffer: TDataRecord): TBookmarkFlag;
  308. begin
  309. Result :=Buffer.BookmarkFlag;
  310. end;
  311. function TBaseJSONDataSet.GetRecNo: Integer;
  312. begin
  313. Result := FCurrent + 1;
  314. end;
  315. procedure TBaseJSONDataSet.InternalInitFieldDefs;
  316. begin
  317. If Assigned(FMetaData) then
  318. MetaDataToFieldDefs;
  319. if (FieldDefs.Count=0) then
  320. Raise EJSONDataset.Create('No fields found');
  321. end;
  322. procedure TBaseJSONDataSet.FreeData;
  323. begin
  324. If FOwnsData then
  325. begin
  326. FreeAndNil(FRows);
  327. FreeAndNil(FMetaData);
  328. end;
  329. if (FCurrentList<>FDefaultList) then
  330. FreeAndNil(FCurrentList)
  331. else
  332. FCurrentList:=Nil;
  333. FreeAndNil(FDefaultList);
  334. FreeAndNil(FFieldMapper);
  335. FCurrentList:=Nil;
  336. end;
  337. procedure TBaseJSONDataSet.AddToList;
  338. Var
  339. I : Integer;
  340. begin
  341. For I:=FDefaultList.Count to FRows.Length-1 do
  342. FDefaultList.Add(FRows[i]);
  343. end;
  344. procedure TBaseJSONDataSet.FillList;
  345. begin
  346. FDefaultList:=TFPList.Create;
  347. AddToList;
  348. FCurrentList:=FDefaultList;
  349. end;
  350. Function TExtJSJSONDataSet.StringToFieldType(S : String) : TFieldType;
  351. begin
  352. if (s='int') then
  353. Result:=ftLargeInt
  354. else if (s='float') then
  355. Result:=ftFloat
  356. else if (s='boolean') then
  357. Result:=ftBoolean
  358. else if (s='date') then
  359. Result:=ftDateTime
  360. else if (s='string') or (s='auto') or (s='') then
  361. Result:=ftString
  362. else
  363. if MapUnknownToStringType then
  364. Result:=ftString
  365. else
  366. Raise EJSONDataset.CreateFmt('Unknown JSON data type : %s',[s]);
  367. end;
  368. Function TExtJSJSONDataSet.GetStringFieldLength(F : TJSObject; AName : String; AIndex : Integer) : integer;
  369. Var
  370. I,L : Integer;
  371. D : JSValue;
  372. begin
  373. Result:=0;
  374. D:=F.Properties['maxlen'];
  375. if Not jsIsNan(toNumber(D)) then
  376. begin
  377. Result:=Trunc(toNumber(D));
  378. if (Result<=0) then
  379. Raise EJSONDataset.CreateFmt('Invalid maximum length specifier for field %s',[AName])
  380. end
  381. else
  382. begin
  383. For I:=0 to FRows.Length-1 do
  384. begin
  385. D:=FFieldMapper.GetJSONDataForField(Aname,AIndex,FRows[i]);
  386. if isString(D) then
  387. begin
  388. l:=Length(String(D));
  389. if L>Result then
  390. Result:=L;
  391. end;
  392. end;
  393. end;
  394. if (Result=0) then
  395. Result:=20;
  396. end;
  397. procedure TExtJSJSONDataSet.MetaDataToFieldDefs;
  398. Var
  399. A : TJSArray;
  400. F : TJSObject;
  401. I,J,FS : Integer;
  402. N,idf : String;
  403. ft: TFieldType;
  404. D : JSValue;
  405. begin
  406. FieldDefs.Clear;
  407. D:=FMetadata.Properties['fields'];
  408. if Not IsArray(D) then
  409. Raise EJSONDataset.Create('Invalid metadata object');
  410. A:=TJSArray(D);
  411. For I:=0 to A.Length-1 do
  412. begin
  413. If Not isObject(A[i]) then
  414. Raise EJSONDataset.CreateFmt('Field definition %d in metadata is not an object',[i]);
  415. F:=TJSObject(A[i]);
  416. D:=F.Properties['name'];
  417. If Not isString(D) then
  418. Raise EJSONDataset.CreateFmt('Field definition %d in has no or invalid name property',[i]);
  419. N:=String(D);
  420. D:=F.Properties['type'];
  421. If IsNull(D) or isUndefined(D) then
  422. ft:=ftstring
  423. else If Not isString(D) then
  424. begin
  425. Raise EJSONDataset.CreateFmt('Field definition %d in has invalid type property',[i])
  426. end
  427. else
  428. begin
  429. ft:=StringToFieldType(String(D));
  430. end;
  431. if (ft=ftString) then
  432. fs:=GetStringFieldLength(F,N,I)
  433. else
  434. fs:=0;
  435. FieldDefs.Add(N,ft,fs);
  436. end;
  437. FFields:=A;
  438. end;
  439. procedure TExtJSJSONDataSet.InternalOpen;
  440. Var
  441. I : integer;
  442. begin
  443. inherited InternalOpen;
  444. Writeln('Checking ID field ',IDField, ' as key field');
  445. for I:=0 to Fields.Count-1 do
  446. If SameText(Fields[i].FieldName,IDField) then
  447. begin
  448. Fields[i].ProviderFlags:=Fields[i].ProviderFlags+[pfInKey];
  449. Writeln('Setting ID field ',IDField, ' as key field');
  450. end;
  451. end;
  452. function TExtJSJSONDataSet.DoResolveRecordUpdate(anUpdate: TRecordUpdateDescriptor): Boolean;
  453. Var
  454. D : JSValue;
  455. O : TJSObject;
  456. A : TJSArray;
  457. I,RecordIndex : Integer;
  458. FN : String;
  459. begin
  460. Result:=True;
  461. if anUpdate.OriginalStatus=usDeleted then
  462. exit;
  463. D:=anUpdate.ServerData;
  464. If isNull(D) then
  465. exit;
  466. if not isNumber(AnUpdate.Bookmark.Data) then
  467. exit(False);
  468. RecordIndex:=Integer(AnUpdate.Bookmark.Data);
  469. If isString(D) then
  470. O:=TJSOBject(TJSJSON.Parse(String(D)))
  471. else if isObject(D) then
  472. O:=TJSOBject(D)
  473. else
  474. Exit(False);
  475. if Not isArray(O[Root]) then
  476. exit(False)
  477. A:=TJSArray(O[Root]);
  478. If A.Length=1 then
  479. begin
  480. O:=TJSObject(A[0]);
  481. For I:=0 to Fields.Count-1 do
  482. begin
  483. if O.hasOwnProperty(Fields[i].FieldName) then
  484. Self.FFieldMapper.SetJSONDataForField(Fields[i],Rows[RecordIndex],O[FN]);
  485. end;
  486. end;
  487. end;
  488. function TExtJSJSONDataSet.DataPacketReceived(ARequest: TDataRequest): Boolean;
  489. Var
  490. O : TJSObject;
  491. A : TJSArray;
  492. begin
  493. Result:=False;
  494. If isNull(aRequest.Data) then
  495. exit;
  496. If isString(aRequest.Data) then
  497. O:=TJSOBject(TJSJSON.Parse(String(aRequest.Data)))
  498. else if isObject(aRequest.Data) then
  499. O:=TJSOBject(aRequest.Data)
  500. else
  501. DatabaseError('Cannot handle data packet');
  502. if (Root='') then
  503. root:='rows';
  504. if (IDField='') then
  505. idField:='id';
  506. if O.hasOwnProperty('metaData') and isObject(o['metaData']) then
  507. begin
  508. if not Active then // Load fields from metadata
  509. metaData:=TJSObject(o['metaData']);
  510. // We must always check this one...
  511. if metaData.hasOwnProperty('root') and isString(metaData['root']) then
  512. Root:=string(metaData['root']);
  513. if metaData.hasOwnProperty('idField') and isString(metaData['idField']) then
  514. IDField:=string(metaData['idField']);
  515. end;
  516. if O.hasOwnProperty(Root) and isArray(o[Root]) then
  517. begin
  518. A:=TJSArray(o[Root]);
  519. Result:=A.Length>0;
  520. AddToRows(A);
  521. end;
  522. end;
  523. function TExtJSJSONDataSet.GenerateMetaData: TJSObject;
  524. Var
  525. F : TJSArray;
  526. O : TJSObject;
  527. I,M : Integer;
  528. T : STring;
  529. begin
  530. Result:=TJSObject.New;
  531. F:=TJSArray.New;
  532. Result.Properties['fields']:=F;
  533. For I:=0 to FieldDefs.Count -1 do
  534. begin
  535. O:=New(['name',FieldDefs[i].name]);
  536. F.push(O);
  537. M:=0;
  538. case FieldDefs[i].DataType of
  539. ftfixedchar,
  540. ftString:
  541. begin
  542. T:='string';
  543. M:=FieldDefs[i].Size;
  544. end;
  545. ftBoolean: T:='boolean';
  546. ftDate,
  547. ftTime,
  548. ftDateTime: T:='date';
  549. ftFloat: t:='float';
  550. ftInteger,
  551. ftAutoInc,
  552. ftLargeInt : t:='int';
  553. else
  554. Raise EJSONDataset.CreateFmt('Unsupported field type : %s',[Ord(FieldDefs[i].DataType)]);
  555. end; // case
  556. O.Properties['type']:=t;
  557. if M<>0 then
  558. O.Properties['maxlen']:=M;
  559. end;
  560. Result.Properties['root']:='rows';
  561. end;
  562. Function TExtJSJSONDataSet.ConvertDateFormat(S : String) : String;
  563. { Not handled: N S w z W t L o O P T Z c U MS }
  564. begin
  565. Result:=StringReplace(S,'y','yy',[rfReplaceall]);
  566. Result:=StringReplace(Result,'Y','yyyy',[rfReplaceall]);
  567. Result:=StringReplace(Result,'g','h',[rfReplaceall]);
  568. Result:=StringReplace(Result,'G','hh',[rfReplaceall]);
  569. Result:=StringReplace(Result,'F','mmmm',[rfReplaceall]);
  570. Result:=StringReplace(Result,'M','mmm',[rfReplaceall]);
  571. Result:=StringReplace(Result,'n','m',[rfReplaceall]);
  572. Result:=StringReplace(Result,'D','ddd',[rfReplaceall]);
  573. Result:=StringReplace(Result,'j','d',[rfReplaceall]);
  574. Result:=StringReplace(Result,'l','dddd',[rfReplaceall]);
  575. Result:=StringReplace(Result,'i','nn',[rfReplaceall]);
  576. Result:=StringReplace(Result,'u','zzz',[rfReplaceall]);
  577. Result:=StringReplace(Result,'a','am/pm',[rfReplaceall,rfIgnoreCase]);
  578. Result:=LowerCase(Result);
  579. end;
  580. procedure TExtJSJSONDataSet.InitDateTimeFields;
  581. Var
  582. F : TJSObject;
  583. FF : TField;
  584. I,J : Integer;
  585. Fmt : String;
  586. D : JSValue;
  587. begin
  588. If (FFields=Nil) then
  589. Exit;
  590. For I:=0 to FFields.Length-1 do
  591. begin
  592. F:=TJSObject(FFields[i]);
  593. D:=F.Properties['type'];
  594. if isString(D) and (String(D)='date') then
  595. begin
  596. D:=F.Properties['dateFormat'];
  597. if isString(D) then
  598. begin
  599. FMT:=ConvertDateFormat(String(D));
  600. FF:=FindField(String(F.Properties['name']));
  601. if (FF<>Nil) and (FF.DataType in [ftDate,ftTime,ftDateTime]) and (FF.FieldKind=fkData) then
  602. begin
  603. if FF is TJSONDateField then
  604. TJSONDateField(FF).DateFormat:=Fmt
  605. else if FF is TJSONTimeField then
  606. TJSONTimeField(FF).TimeFormat:=Fmt
  607. else if FF is TJSONDateTimeField then
  608. TJSONDateTimeField(FF).DateTimeFormat:=Fmt;
  609. end;
  610. end;
  611. end;
  612. end;
  613. end;
  614. function TBaseJSONDataSet.GetRecord(Var Buffer: TDataRecord; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  615. begin
  616. Result := grOK; // default
  617. case GetMode of
  618. gmNext: // move on
  619. if fCurrent < fCurrentList.Count - 1 then
  620. Inc (fCurrent)
  621. else
  622. Result := grEOF; // end of file
  623. gmPrior: // move back
  624. if fCurrent > 0 then
  625. Dec (fCurrent)
  626. else
  627. Result := grBOF; // begin of file
  628. gmCurrent: // check if empty
  629. if fCurrent >= fCurrentList.Count then
  630. Result := grEOF;
  631. end;
  632. if Result = grOK then // read the data
  633. begin
  634. Buffer.Data:=FRows[FCurrent];
  635. Buffer.BookmarkFlag := bfCurrent;
  636. Buffer.Bookmark:=fCurrent;
  637. end;
  638. end;
  639. function TBaseJSONDataSet.GetRecordCount: Integer;
  640. begin
  641. Result := FCurrentList.Count;
  642. end;
  643. function TBaseJSONDataSet.GetRecordSize: Word;
  644. begin
  645. Result := 0; // actual data without house-keeping
  646. end;
  647. procedure TBaseJSONDataSet.InternalClose;
  648. begin
  649. // disconnet and destroy field objects
  650. BindFields (False);
  651. if DefaultFields then
  652. DestroyFields;
  653. FreeData;
  654. end;
  655. procedure TBaseJSONDataSet.InternalDelete;
  656. Var
  657. R : JSValue;
  658. begin
  659. R:=JSValue(FCurrentList[FCurrent]);
  660. FCurrentList.Delete(FCurrent);
  661. if (FCurrent>=FCurrentList.Count) then
  662. Dec(FCurrent);
  663. FRows:=FRows.Splice(FCurrent,1);
  664. end;
  665. procedure TBaseJSONDataSet.InternalFirst;
  666. begin
  667. FCurrent := -1;
  668. end;
  669. procedure TBaseJSONDataSet.InternalGotoBookmark(ABookmark: TBookmark);
  670. begin
  671. if isNumber(ABookmark.Data) then
  672. FCurrent:=Integer(ABookmark.Data);
  673. // Writeln('Fcurrent', FCurrent,' from ',ABookmark.Data);
  674. end;
  675. procedure TBaseJSONDataSet.InternalInsert;
  676. Var
  677. I : Integer;
  678. D : TFieldDef;
  679. begin
  680. // Writeln('TBaseJSONDataSet.InternalInsert');
  681. FEditRow:=ActiveBuffer.Data;
  682. For I:=0 to FieldDefs.Count-1 do
  683. begin
  684. D:=FieldDefs[i];
  685. FFieldMapper.SetJSONDataForField(D.Name,D.Index,FEditRow,Null);
  686. end;
  687. end;
  688. procedure TBaseJSONDataSet.InternalEdit;
  689. begin
  690. // Writeln('TBaseJSONDataSet.InternalEdit: ');
  691. FEditRow:=TJSJSON.parse(TJSJSON.stringify(FRows[FCurrent]));
  692. // Writeln('TBaseJSONDataSet.InternalEdit: ',FEditRow);
  693. end;
  694. procedure TBaseJSONDataSet.InternalCancel;
  695. begin
  696. end;
  697. procedure TBaseJSONDataSet.InternalLast;
  698. begin
  699. // The first thing that will happen is a GetPrior Record.
  700. FCurrent:=FCurrentList.Count;
  701. end;
  702. procedure TBaseJSONDataSet.InitDateTimeFields;
  703. begin
  704. // Do nothing
  705. end;
  706. procedure TBaseJSONDataSet.InternalOpen;
  707. begin
  708. FreeAndNil(FFieldMapper);
  709. FFieldMapper:=CreateFieldMapper;
  710. IF (FRows=Nil) then // opening from fielddefs ?
  711. begin
  712. FRows:=TJSArray.New;
  713. OwnsData:=True;
  714. end;
  715. FillList;
  716. InternalInitFieldDefs;
  717. if DefaultFields then
  718. CreateFields;
  719. BindFields (True);
  720. InitDateTimeFields;
  721. FCurrent := -1;
  722. end;
  723. procedure TBaseJSONDataSet.InternalPost;
  724. Var
  725. RI,I : integer;
  726. B : TBookmark;
  727. begin
  728. GetBookMarkData(ActiveBuffer,B);
  729. if (State=dsInsert) then
  730. begin // Insert or Append
  731. FRows.push(FEditRow);
  732. if GetBookMarkFlag(ActiveBuffer)=bfEOF then
  733. begin // Append
  734. FDefaultList.Add(FEditRow);
  735. if (FCurrentList<>FDefaultList) then
  736. FCurrentList.Add(FEditRow);
  737. end
  738. else // insert
  739. begin
  740. FCurrentList.Insert(FCurrent,FEditRow);
  741. if (FCurrentList<>FDefaultList) then
  742. FDefaultList.Add(FEditRow);
  743. end;
  744. end
  745. else
  746. begin // Edit
  747. RI:=FRows.IndexOf(JSValue(FCurrentList[FCurrent]));
  748. if (RI<>-1) then
  749. FRows[RI]:=FEditRow
  750. else
  751. FRows.push(FEditRow);
  752. FCurrentList[FCurrent]:=FEditRow;
  753. if (FCurrentList<>FDefaultList) then
  754. FDefaultList[FCurrent]:=FEditRow;
  755. end;
  756. FEditRow:=Nil;
  757. end;
  758. procedure TBaseJSONDataSet.InternalSetToRecord(Buffer: TDataRecord);
  759. begin
  760. FCurrent := Integer(Bookmark.Data);
  761. end;
  762. function TBaseJSONDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
  763. begin
  764. case FieldType of
  765. ftDate : Result:=TJSONDateField;
  766. ftDateTime : Result:=TJSONDateTimeField;
  767. ftTime : Result:=TJSONTimeField;
  768. else
  769. Result:=inherited GetFieldClass(FieldType);
  770. end;
  771. end;
  772. function TBaseJSONDataSet.IsCursorOpen: Boolean;
  773. begin
  774. Result := Assigned(FDefaultList);
  775. end;
  776. procedure TBaseJSONDataSet.SetBookmarkData(var Buffer: TDataRecord; Data: TBookmark);
  777. begin
  778. Buffer.Bookmark:=Data.Data;
  779. // Writeln('Set Bookmark from: ',Data.Data);
  780. end;
  781. function TBaseJSONDataSet.ConvertDateTimeField(S : String; F : TField) : TDateTime;
  782. Var
  783. Ptrn : string;
  784. begin
  785. Result:=0;
  786. Case F.DataType of
  787. ftDate : Ptrn:=TJSONDateField(F).DateFormat;
  788. ftTime : Ptrn:=TJSONTimeField(F).TimeFormat;
  789. ftDateTime : Ptrn:=TJSONDateTimeField(F).DateTimeFormat;
  790. end;
  791. If (Ptrn='') then
  792. Case F.DataType of
  793. ftDate : Result:=StrToDate(S);
  794. ftTime : Result:=StrToTime(S);
  795. ftDateTime : Result:=StrToDateTime(S);
  796. end
  797. else
  798. begin
  799. Result:=ScanDateTime(ptrn,S,1);
  800. end;
  801. end;
  802. function TBaseJSONDataSet.FormatDateTimeField(DT: TDateTime; F: TField
  803. ): String;
  804. Var
  805. Ptrn : string;
  806. begin
  807. Result:='';
  808. Case F.DataType of
  809. ftDate : Ptrn:=TJSONDateField(F).DateFormat;
  810. ftTime : Ptrn:=TJSONTimeField(F).TimeFormat;
  811. ftDateTime : Ptrn:=TJSONDateTimeField(F).DateTimeFormat;
  812. end;
  813. If (Ptrn='') then
  814. Case F.DataType of
  815. ftDate : Result:=DateToStr(DT);
  816. ftTime : Result:=TimeToStr(DT);
  817. ftDateTime : Result:=DateTimeToStr(DT);
  818. end
  819. else
  820. Result:=FormatDateTime(ptrn,DT);
  821. end;
  822. function TBaseJSONDataSet.GetFieldData(Field: TField; Buffer: TDatarecord): JSValue;
  823. var
  824. R : JSValue;
  825. begin
  826. // Writeln('Getting data for field ',Field.FieldName,'Buffer ',Buffer);
  827. if (FEditRow<>Nil) then
  828. R:=FEditRow
  829. else
  830. R:=Buffer.data;
  831. Result:=FFieldMapper.GetJSONDataForField(Field,R);
  832. end;
  833. procedure TBaseJSONDataSet.SetFieldData(Field: TField; var Buffer: TDatarecord; AValue : JSValue);
  834. begin
  835. FFieldMapper.SetJSONDataForField(Field,FEditRow,AValue);
  836. // FFieldMapper.SetJSONDataForField(Field,Buffer.Data,AValue);
  837. end;
  838. procedure TBaseJSONDataSet.SetBookmarkFlag(var Buffer: TDataRecord; Value: TBookmarkFlag);
  839. begin
  840. Buffer.BookmarkFlag := Value;
  841. end;
  842. procedure TBaseJSONDataSet.SetRecNo(Value: Integer);
  843. begin
  844. if (Value < 0) or (Value > FCurrentList.Count) then
  845. raise EJSONDataset.CreateFmt('SetRecNo: index %d out of range',[Value]);
  846. FCurrent := Value - 1;
  847. Resync([]);
  848. DoAfterScroll;
  849. end;
  850. constructor TBaseJSONDataSet.Create(AOwner: TComponent);
  851. begin
  852. inherited;
  853. FownsData:=True;
  854. end;
  855. destructor TBaseJSONDataSet.Destroy;
  856. begin
  857. FreeData;
  858. inherited;
  859. end;
  860. end.