jsondataset.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. {$mode objfpc}
  2. unit JSONDataset;
  3. interface
  4. uses
  5. Types, JS, DB, Classes, SysUtils;
  6. type
  7. { TJSONFieldMapper }
  8. // This class is responsible for mapping the field objects of the records.
  9. TJSONFieldMapper = Class(TObject)
  10. Public
  11. // Return row TJSONData instance with data for field 'FieldName' or 'FieldIndex'.
  12. Function GetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row : JSValue) : JSValue; virtual; abstract;
  13. // Same, but now based on TField.
  14. Function GetJSONDataForField(F : TField; Row : JSValue) : JSValue; virtual;
  15. // Set data for field 'FieldName' or 'FieldIndex' to supplied TJSONData instance in row
  16. procedure SetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row,Data : JSValue); virtual; abstract;
  17. // Set data for field TField to supplied TJSONData instance
  18. procedure SetJSONDataForField(F : TField; Row,Data : JSValue); virtual;
  19. // Create a new row.
  20. Function CreateRow : JSValue; virtual; abstract;
  21. end;
  22. // JSON has no date/time type, so we use a string field.
  23. // ExtJS provides the date/time format in it's field config: 'dateFormat'
  24. // The below field classes store this in the NNNFormat field.
  25. { TJSONDateField }
  26. TJSONDateField = Class(TDateField)
  27. private
  28. FDateFormat: String;
  29. Published
  30. Property DateFormat : String Read FDateFormat Write FDateFormat;
  31. end;
  32. { TJSONTimeField }
  33. TJSONTimeField = Class(TTimeField)
  34. private
  35. FTimeFormat: String;
  36. Published
  37. Property TimeFormat : String Read FTimeFormat Write FTimeFormat;
  38. end;
  39. { TJSONDateTimeField }
  40. TJSONDateTimeField = Class(TDateTimeField)
  41. private
  42. FDateTimeFormat: String;
  43. Published
  44. Property DateTimeFormat : String Read FDateTimeFormat Write FDateTimeFormat;
  45. end;
  46. { TBaseJSONDataSet }
  47. { TJSONIndex }
  48. TJSONIndex = Class
  49. FList : TJSArray; // Indexes of elements in FRows.
  50. FRows : TJSArray;
  51. FDataset : TDataset;
  52. private
  53. function GetRecordIndex(aListIndex : Integer): NativeInt;
  54. protected
  55. Function GetCount: Integer; virtual;
  56. Procedure CreateIndex; Virtual; abstract;
  57. Property List : TJSArray Read FList;
  58. Property Rows : TJSArray Read FRows;
  59. Property Dataset : TDataset Read FDataset;
  60. Public
  61. Constructor Create(aDataset: TDataset; aRows : TJSArray); reintroduce;
  62. // Append remainder of FRows to FList.
  63. Procedure AppendToIndex; virtual; abstract;
  64. // Delete aListIndex from list, not from row. Return Recordindex of deleted record.
  65. Function Delete(aListIndex : Integer) : Integer; virtual;
  66. // Append aRecordIndex to list. Return ListIndex of appended record.
  67. Function Append(aRecordIndex : Integer) : Integer; virtual; abstract;
  68. // Insert record into list. By default, this does an append. Return ListIndex of inserted record
  69. Function Insert(aCurrentIndex{%H-}, aRecordIndex : Integer) : Integer; virtual;
  70. // Record at index aCurrentIndex has changed. Update index and return new listindex.
  71. Function Update(aCurrentIndex, aRecordIndex : Integer) : Integer; virtual; abstract;
  72. // Find list index for Record at index aCurrentIndex. Return -1 if not found.
  73. Function FindRecord(aRecordIndex : Integer) : Integer; virtual; abstract;
  74. // index of record in FRows based on aListIndex in List.
  75. Property RecordIndex[aListIndex : Integer] : NativeInt Read GetRecordIndex;
  76. // Number of records in index. This can differ from FRows, e.g. when filtering.
  77. Property Count : Integer Read GetCount;
  78. end;
  79. { TDefaultJSONIndex }
  80. TDefaultJSONIndex = Class(TJSONIndex)
  81. public
  82. Procedure CreateIndex; override;
  83. Procedure AppendToIndex; override;
  84. Function Append(aRecordIndex : Integer) : Integer; override;
  85. Function Insert(aCurrentIndex, aRecordIndex : Integer) : Integer; override;
  86. Function FindRecord(aRecordIndex : Integer) : Integer; override;
  87. Function Update(aCurrentIndex, aRecordIndex : Integer) : Integer; override;
  88. end;
  89. // basic JSON dataset. Does nothing ExtJS specific.
  90. TBaseJSONDataSet = class (TDataSet)
  91. private
  92. FMUS: Boolean;
  93. FOwnsData : Boolean;
  94. FDefaultIndex : TJSONIndex; // Default index, built from array
  95. FCurrentIndex : TJSONIndex; // Currently active index.
  96. FCurrent: Integer; // Record Index in the current IndexList
  97. // Possible metadata to configure fields from.
  98. FMetaData : TJSObject;
  99. // This will contain the rows.
  100. FRows : TJSArray;
  101. // Deleted rows
  102. FDeletedRows : TJSArray;
  103. FFieldMapper : TJSONFieldMapper;
  104. // When editing, this object is edited.
  105. FEditIdx : Integer;
  106. FEditRow : JSValue;
  107. FUseDateTimeFormatFields: Boolean;
  108. procedure SetMetaData(AValue: TJSObject);
  109. procedure SetRows(AValue: TJSArray);
  110. protected
  111. // dataset virtual methods
  112. function AllocRecordBuffer: TDataRecord; override;
  113. procedure FreeRecordBuffer(var Buffer: TDataRecord); override;
  114. procedure InternalInitRecord(var Buffer: TDataRecord); override;
  115. function GetRecord(Var Buffer: TDataRecord; GetMode: TGetMode; DoCheck{%H-}: Boolean): TGetResult; override;
  116. function GetRecordSize: Word; override;
  117. procedure AddToRows(AValue: TJSArray);
  118. procedure InternalClose; override;
  119. procedure InternalDelete; override;
  120. procedure InternalFirst; override;
  121. procedure InternalLast; override;
  122. procedure InternalOpen; override;
  123. procedure InternalPost; override;
  124. procedure InternalInsert; override;
  125. procedure InternalEdit; override;
  126. procedure InternalCancel; override;
  127. procedure InternalInitFieldDefs; override;
  128. procedure InternalSetToRecord(Buffer: TDataRecord); override;
  129. function GetFieldClass(FieldType: TFieldType): TFieldClass; override;
  130. function IsCursorOpen: Boolean; override;
  131. // Bookmark operations
  132. procedure GetBookmarkData(Buffer: TDataRecord; var Data: TBookmark); override;
  133. function GetBookmarkFlag(Buffer: TDataRecord): TBookmarkFlag; override;
  134. procedure InternalGotoBookmark(ABookmark: TBookmark); override;
  135. procedure SetBookmarkFlag(Var Buffer: TDataRecord; Value: TBookmarkFlag); override;
  136. procedure SetBookmarkData(Var Buffer: TDataRecord; Data: TBookmark); override;
  137. function GetRecordCount: Integer; override;
  138. procedure SetRecNo(Value: Integer); override;
  139. function GetRecNo: Integer; override;
  140. Protected
  141. // New methods.
  142. // Called when dataset is closed. If OwnsData is true, metadata and rows are freed.
  143. Procedure FreeData; virtual;
  144. // Fill default list.
  145. procedure AppendToIndexes; virtual;
  146. Procedure CreateIndexes; virtual;
  147. // Convert MetaData object to FieldDefs.
  148. Procedure MetaDataToFieldDefs; virtual; abstract;
  149. // Initialize Date/Time info in all date/time fields. Called during InternalOpen
  150. procedure InitDateTimeFields; virtual;
  151. // Convert JSON date S to DateTime for Field F
  152. function ConvertDateTimeField(S: String; F: TField): TDateTime; virtual;
  153. // Format JSON date to from DT for Field F
  154. function FormatDateTimeField(DT : TDateTime; F: TField): String; virtual;
  155. // Create fieldmapper. A descendent MUST implement this.
  156. Function CreateFieldMapper : TJSONFieldMapper; virtual; abstract;
  157. // If True, then the dataset will free MetaData and FRows when it is closed.
  158. Property OwnsData : Boolean Read FownsData Write FOwnsData;
  159. // set to true if unknown field types should be handled as string fields.
  160. Property MapUnknownToStringType : Boolean Read FMUS Write FMUS;
  161. // Metadata
  162. Property MetaData : TJSObject Read FMetaData Write SetMetaData;
  163. // Rows
  164. Property Rows : TJSArray Read FRows Write SetRows;
  165. // Fieldmapper
  166. Property FieldMapper : TJSONFieldMapper Read FFieldMapper;
  167. // FieldClass
  168. Property UseDateTimeFormatFields : Boolean Read FUseDateTimeFormatFields Write FUseDateTimeFormatFields;
  169. public
  170. constructor Create (AOwner: TComponent); override;
  171. destructor Destroy; override;
  172. function GetFieldData(Field: TField; Buffer: TDatarecord): JSValue; override;
  173. procedure SetFieldData(Field: TField; var Buffer{%H-}: TDatarecord; AValue : JSValue); override;
  174. function BookmarkValid(ABookmark: TBookmark): Boolean; override;
  175. function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; override;
  176. end;
  177. TJSONDataset = Class(TBaseJSONDataset)
  178. published
  179. Property FieldDefs;
  180. // redeclared data set properties
  181. property Active;
  182. property BeforeOpen;
  183. property AfterOpen;
  184. property BeforeClose;
  185. property AfterClose;
  186. property BeforeInsert;
  187. property AfterInsert;
  188. property BeforeEdit;
  189. property AfterEdit;
  190. property BeforePost;
  191. property AfterPost;
  192. property BeforeCancel;
  193. property AfterCancel;
  194. property BeforeDelete;
  195. property AfterDelete;
  196. property BeforeScroll;
  197. property AfterScroll;
  198. property OnCalcFields;
  199. property OnDeleteError;
  200. property OnEditError;
  201. property OnFilterRecord;
  202. property OnNewRecord;
  203. property OnPostError;
  204. end;
  205. { TJSONObjectFieldMapper }
  206. // Fieldmapper to be used when the data is in an object
  207. TJSONObjectFieldMapper = Class(TJSONFieldMapper)
  208. Public
  209. procedure SetJSONDataForField(Const FieldName : String; FieldIndex{%H-} : Integer; Row,Data : JSValue); override;
  210. Function GetJSONDataForField(Const FieldName : String; FieldIndex{%H-} : Integer; Row : JSValue) : JSValue; override;
  211. Function CreateRow : JSValue; override;
  212. end;
  213. { TJSONArrayFieldMapper }
  214. // Fieldmapper to be used when the data is in an array
  215. TJSONArrayFieldMapper = Class(TJSONFieldMapper)
  216. Public
  217. procedure SetJSONDataForField(Const FieldName{%H-} : String; FieldIndex : Integer; Row,Data : JSValue); override;
  218. Function GetJSONDataForField(Const FieldName{%H-} : String; FieldIndex : Integer; Row : JSValue) : JSValue; override;
  219. Function CreateRow : JSValue; override;
  220. end;
  221. EJSONDataset = Class(EDatabaseError);
  222. implementation
  223. uses DateUtils;
  224. { TDefaultJSONIndex }
  225. procedure TDefaultJSONIndex.CreateIndex;
  226. Var
  227. I : Integer;
  228. begin
  229. For I:=0 to FRows.length-1 do
  230. FList[i]:=I;
  231. end;
  232. procedure TDefaultJSONIndex.AppendToIndex;
  233. Var
  234. I,L : Integer;
  235. begin
  236. L:=FList.Length;
  237. FList.Length:=FRows.Length;
  238. For I:=L to FRows.Length-1 do
  239. FList[i]:=I;
  240. end;
  241. function TDefaultJSONIndex.Append(aRecordIndex: Integer): Integer;
  242. begin
  243. Result:=FList.Push(aRecordIndex)-1;
  244. end;
  245. function TDefaultJSONIndex.Insert(aCurrentIndex, aRecordIndex: Integer
  246. ): Integer;
  247. begin
  248. FList.splice(aCurrentIndex, 0, aRecordIndex);
  249. Result:=aCurrentIndex;
  250. end;
  251. function TDefaultJSONIndex.FindRecord(aRecordIndex: Integer): Integer;
  252. begin
  253. Result:=FList.indexOf(aRecordIndex);
  254. end;
  255. function TDefaultJSONIndex.Update(aCurrentIndex, aRecordIndex: Integer
  256. ): Integer;
  257. begin
  258. Result:=0;
  259. If RecordIndex[aCurrentIndex]<>aRecordIndex then
  260. DatabaseErrorFmt('Inconsistent record index in default index, expected %d, got %d.',[aCurrentIndex,RecordIndex[aCurrentIndex]],Dataset);
  261. end;
  262. { TJSONIndex }
  263. constructor TJSONIndex.Create(aDataset: TDataset; aRows: TJSArray);
  264. begin
  265. FRows:=aRows;
  266. FList:=TJSArray.New(FRows.length);
  267. FDataset:=aDataset;
  268. CreateIndex;
  269. end;
  270. function TJSONIndex.Delete(aListIndex: Integer): Integer;
  271. Var
  272. a : TJSArray;
  273. begin
  274. A:=FList.Splice(aListIndex,1);
  275. If a.Length>0 then
  276. Result:=Integer(A[0])
  277. else
  278. Result:=-1;
  279. end;
  280. function TJSONIndex.Insert(aCurrentIndex, aRecordIndex: Integer): Integer;
  281. begin
  282. Result:=Append(aRecordIndex);
  283. end;
  284. function TJSONIndex.GetCount: Integer;
  285. begin
  286. Result:=FList.Length;
  287. end;
  288. function TJSONIndex.GetRecordIndex(aListIndex : Integer): NativeInt;
  289. begin
  290. if isUndefined(FList[aListIndex]) then
  291. Result:=-1
  292. else
  293. Result:=NativeInt(FList[aListIndex]);
  294. end;
  295. { TJSONFieldMapper }
  296. function TJSONFieldMapper.GetJSONDataForField(F: TField; Row: JSValue ): JSValue;
  297. begin
  298. // This supposes that Index is correct, i.e. the field positions have not been changed.
  299. Result:=GetJSONDataForField(F.FieldName,F.Index,Row);
  300. end;
  301. procedure TJSONFieldMapper.SetJSONDataForField(F: TField; Row,Data: JSValue);
  302. begin
  303. SetJSONDataForField(F.FieldName,F.Index,Row,Data);
  304. end;
  305. { TJSONArrayFieldMapper }
  306. procedure TJSONArrayFieldMapper.SetJSONDataForField(const FieldName: String;
  307. FieldIndex: Integer; Row, Data: JSValue);
  308. begin
  309. TJSValueDynArray(Row)[FieldIndex]:=Data;
  310. end;
  311. function TJSONArrayFieldMapper.GetJSONDataForField(Const FieldName: String;
  312. FieldIndex: Integer; Row: JSValue): JSValue;
  313. begin
  314. Result:=TJSValueDynArray(Row)[FieldIndex];
  315. end;
  316. function TJSONArrayFieldMapper.CreateRow: JSValue;
  317. begin
  318. Result:=TJSArray.New;
  319. end;
  320. { TJSONObjectFieldMapper }
  321. procedure TJSONObjectFieldMapper.SetJSONDataForField(const FieldName: String;
  322. FieldIndex: Integer; Row, Data: JSValue);
  323. begin
  324. TJSObject(Row).Properties[FieldName]:=Data;
  325. end;
  326. function TJSONObjectFieldMapper.GetJSONDataForField(const FieldName: String;
  327. FieldIndex: Integer; Row: JSValue): JSValue;
  328. begin
  329. Result:=TJSObject(Row).Properties[FieldName];
  330. end;
  331. function TJSONObjectFieldMapper.CreateRow: JSValue;
  332. begin
  333. Result:=TJSObject.New;
  334. end;
  335. procedure TBaseJSONDataSet.SetMetaData(AValue: TJSObject);
  336. begin
  337. CheckInActive;
  338. FMetaData:=AValue;
  339. end;
  340. procedure TBaseJSONDataSet.AddToRows(AValue: TJSArray);
  341. begin
  342. if FRows=Nil then
  343. FRows:=AValue
  344. else
  345. begin
  346. FRows:=FRows.Concat(AValue);
  347. AppendToIndexes;
  348. end;
  349. end;
  350. procedure TBaseJSONDataSet.SetRows(AValue: TJSArray);
  351. begin
  352. if AValue=FRows then exit;
  353. CheckInActive;
  354. FRows:=Nil;
  355. AddToRows(AValue);
  356. end;
  357. function TBaseJSONDataSet.AllocRecordBuffer: TDataRecord;
  358. begin
  359. Result.data:=TJSObject.New;
  360. Result.bookmark:=null;
  361. Result.state:=rsNew;
  362. end;
  363. // the next two are particularly ugly.
  364. procedure TBaseJSONDataSet.InternalInitRecord(var Buffer: TDataRecord);
  365. begin
  366. // Writeln('TBaseJSONDataSet.InternalInitRecord');
  367. Buffer.Data:=FFieldMapper.CreateRow;
  368. Buffer.bookmark:=null;
  369. Buffer.state:=rsNew;
  370. end;
  371. procedure TBaseJSONDataSet.FreeRecordBuffer (var Buffer: TDataRecord);
  372. begin
  373. Buffer.Data:=Null;
  374. Buffer.bookmark:=null;
  375. Buffer.state:=rsNew;
  376. end;
  377. procedure TBaseJSONDataSet.GetBookmarkData(Buffer: TDataRecord; var Data: TBookmark);
  378. begin
  379. Data.Data:=Buffer.bookmark;
  380. end;
  381. function TBaseJSONDataSet.GetBookmarkFlag(Buffer: TDataRecord): TBookmarkFlag;
  382. begin
  383. Result :=Buffer.BookmarkFlag;
  384. end;
  385. function TBaseJSONDataSet.GetRecNo: Integer;
  386. Var
  387. bkmIdx : Integer;
  388. begin
  389. bkmIdx:=Integer(ActiveBuffer.bookmark);
  390. Result:=FCurrentIndex.FindRecord(bkmIdx)+1;
  391. end;
  392. procedure TBaseJSONDataSet.InternalInitFieldDefs;
  393. begin
  394. If Assigned(FMetaData) then
  395. MetaDataToFieldDefs;
  396. if (FieldDefs.Count=0) then
  397. Raise EJSONDataset.Create('No fields found');
  398. end;
  399. procedure TBaseJSONDataSet.FreeData;
  400. begin
  401. If FOwnsData then
  402. begin
  403. FRows:=Nil;
  404. FMetaData:=Nil;
  405. end;
  406. if (FCurrentIndex<>FDefaultIndex) then
  407. FreeAndNil(FCurrentIndex)
  408. else
  409. FCurrentIndex:=Nil;
  410. FreeAndNil(FDefaultindex);
  411. FreeAndNil(FFieldMapper);
  412. FCurrentIndex:=Nil;
  413. FDeletedRows:=Nil;
  414. end;
  415. procedure TBaseJSONDataSet.AppendToIndexes;
  416. begin
  417. FDefaultIndex.AppendToIndex;
  418. end;
  419. procedure TBaseJSONDataSet.CreateIndexes;
  420. begin
  421. FDefaultIndex:=TDefaultJSONIndex.Create(Self,FRows);
  422. AppendToIndexes;
  423. FCurrentIndex:=FDefaultIndex;
  424. end;
  425. function TBaseJSONDataSet.GetRecord(Var Buffer: TDataRecord; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  426. Var
  427. BkmIdx : Integer;
  428. begin
  429. Result := grOK; // default
  430. case GetMode of
  431. gmNext: // move on
  432. if fCurrent < fCurrentIndex.Count - 1 then
  433. Inc (fCurrent)
  434. else
  435. Result := grEOF; // end of file
  436. gmPrior: // move back
  437. if fCurrent > 0 then
  438. Dec (fCurrent)
  439. else
  440. Result := grBOF; // begin of file
  441. gmCurrent: // check if empty
  442. if fCurrent >= fCurrentIndex.Count then
  443. Result := grEOF;
  444. end;
  445. if Result = grOK then // read the data
  446. begin
  447. BkmIdx:=FCurrentIndex.RecordIndex[FCurrent];
  448. Buffer.Data:=FRows[bkmIdx];
  449. Buffer.BookmarkFlag := bfCurrent;
  450. Buffer.Bookmark:=BkmIdx;
  451. CalculateFields(Buffer);
  452. end;
  453. end;
  454. function TBaseJSONDataSet.GetRecordCount: Integer;
  455. begin
  456. Result:=FCurrentIndex.Count;
  457. end;
  458. function TBaseJSONDataSet.GetRecordSize: Word;
  459. begin
  460. Result := 0; // actual data without house-keeping
  461. end;
  462. procedure TBaseJSONDataSet.InternalClose;
  463. begin
  464. // disconnet and destroy field objects
  465. BindFields (False);
  466. if DefaultFields then
  467. DestroyFields;
  468. FreeData;
  469. end;
  470. procedure TBaseJSONDataSet.InternalDelete;
  471. Var
  472. Idx : Integer;
  473. begin
  474. Idx:=FCurrentIndex.Delete(FCurrent);
  475. if (Idx<>-1) then
  476. begin
  477. // Add code here to Delete from other indexes as well.
  478. // ...
  479. // Add to array of deleted records.
  480. if Not Assigned(FDeletedRows) then
  481. FDeletedRows:=TJSArray.New(FRows[idx])
  482. else
  483. FDeletedRows.Push(FRows[Idx]);
  484. FRows[Idx]:=Undefined;
  485. end;
  486. end;
  487. procedure TBaseJSONDataSet.InternalFirst;
  488. begin
  489. FCurrent := -1;
  490. end;
  491. procedure TBaseJSONDataSet.InternalGotoBookmark(ABookmark: TBookmark);
  492. begin
  493. if isNumber(ABookmark.Data) then
  494. FCurrent:=FCurrentIndex.FindRecord(Integer(ABookmark.Data));
  495. // Writeln('Fcurrent', FCurrent,' from ',ABookmark.Data);
  496. end;
  497. procedure TBaseJSONDataSet.InternalInsert;
  498. Var
  499. I : Integer;
  500. D : TFieldDef;
  501. begin
  502. // Writeln('TBaseJSONDataSet.InternalInsert');
  503. FEditRow:=ActiveBuffer.Data;
  504. For I:=0 to FieldDefs.Count-1 do
  505. begin
  506. D:=FieldDefs[i];
  507. FFieldMapper.SetJSONDataForField(D.Name,D.Index,FEditRow,Null);
  508. end;
  509. end;
  510. procedure TBaseJSONDataSet.InternalEdit;
  511. begin
  512. // Writeln('TBaseJSONDataSet.InternalEdit: ');
  513. FEditIdx:=FCurrentIndex.RecordIndex[FCurrent];
  514. if not isUndefined(Rows[FEditIdx]) then
  515. FEditRow:=TJSJSON.parse(TJSJSON.stringify(Rows[FEditIdx]))
  516. else
  517. FEditRow:=TJSObject.new;
  518. // Writeln('TBaseJSONDataSet.InternalEdit: ',FEditRow);
  519. end;
  520. procedure TBaseJSONDataSet.InternalCancel;
  521. begin
  522. FEditIdx:=-1;
  523. FEditRow:=Nil;
  524. end;
  525. procedure TBaseJSONDataSet.InternalLast;
  526. begin
  527. // The first thing that will happen is a GetPrior Record.
  528. FCurrent:=FCurrentIndex.Count;
  529. end;
  530. procedure TBaseJSONDataSet.InitDateTimeFields;
  531. begin
  532. // Do nothing
  533. end;
  534. procedure TBaseJSONDataSet.InternalOpen;
  535. begin
  536. FreeAndNil(FFieldMapper);
  537. FFieldMapper:=CreateFieldMapper;
  538. IF (FRows=Nil) then // opening from fielddefs ?
  539. begin
  540. FRows:=TJSArray.New;
  541. OwnsData:=True;
  542. end;
  543. CreateIndexes;
  544. InternalInitFieldDefs;
  545. if DefaultFields then
  546. CreateFields;
  547. BindFields (True);
  548. InitDateTimeFields;
  549. FCurrent := -1;
  550. end;
  551. procedure TBaseJSONDataSet.InternalPost;
  552. Var
  553. Idx : integer;
  554. B : TBookmark;
  555. begin
  556. GetBookMarkData(ActiveBuffer,B);
  557. if (State=dsInsert) then
  558. begin // Insert or Append
  559. Idx:=FRows.push(FEditRow)-1;
  560. if GetBookMarkFlag(ActiveBuffer)=bfEOF then
  561. begin // Append
  562. FDefaultIndex.Append(Idx);
  563. // Must replace this by updating all indexes
  564. if (FCurrentIndex<>FDefaultIndex) then
  565. FCurrentIndex.Append(Idx);
  566. end
  567. else // insert
  568. begin
  569. FCurrent:=FDefaultIndex.Insert(FCurrent,Idx);
  570. // Must replace this by updating all indexes.
  571. // Note that this will change current index.
  572. if (FCurrentIndex<>FDefaultIndex) then
  573. FCurrent:=FCurrentIndex.Insert(FCurrent,Idx);
  574. end;
  575. end
  576. else
  577. begin // Edit
  578. if (FEditIdx=-1) then
  579. DatabaseErrorFmt('Failed to retrieve record index for record %d',[FCurrent]);
  580. // Update source record
  581. Idx:=FEditIdx;
  582. FRows[Idx]:=FEditRow;
  583. FDefaultIndex.Update(FCurrent,Idx);
  584. // Must replace this by updating all indexes.
  585. // Note that this will change current index.
  586. if (FCurrentIndex<>FDefaultIndex) then
  587. FCurrentIndex.Update(FCurrent,Idx);
  588. end;
  589. FEditIdx:=-1;
  590. FEditRow:=Nil;
  591. end;
  592. procedure TBaseJSONDataSet.InternalSetToRecord(Buffer: TDataRecord);
  593. begin
  594. FCurrent:=FCurrentIndex.FindRecord(Integer(Buffer.Bookmark));
  595. end;
  596. function TBaseJSONDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
  597. begin
  598. If UseDateTimeFormatFields and (FieldType in [ftDate,ftDateTime,ftTime]) then
  599. case FieldType of
  600. ftDate : Result:=TJSONDateField;
  601. ftDateTime : Result:=TJSONDateTimeField;
  602. ftTime : Result:=TJSONTimeField;
  603. end
  604. else
  605. Result:=inherited GetFieldClass(FieldType);
  606. end;
  607. function TBaseJSONDataSet.IsCursorOpen: Boolean;
  608. begin
  609. Result := Assigned(FDefaultIndex);
  610. end;
  611. function TBaseJSONDataSet.BookmarkValid(ABookmark: TBookmark): Boolean;
  612. begin
  613. Result:=isNumber(ABookmark.Data);
  614. end;
  615. procedure TBaseJSONDataSet.SetBookmarkData(var Buffer: TDataRecord; Data: TBookmark);
  616. begin
  617. Buffer.Bookmark:=Data.Data;
  618. // Writeln('Set Bookmark from: ',Data.Data);
  619. end;
  620. function TBaseJSONDataSet.ConvertDateTimeField(S : String; F : TField) : TDateTime;
  621. Var
  622. Ptrn : string;
  623. begin
  624. Result:=0;
  625. Case F.DataType of
  626. ftDate : Ptrn:=TJSONDateField(F).DateFormat;
  627. ftTime : Ptrn:=TJSONTimeField(F).TimeFormat;
  628. ftDateTime : Ptrn:=TJSONDateTimeField(F).DateTimeFormat;
  629. end;
  630. If (Ptrn='') then
  631. Case F.DataType of
  632. ftDate : Result:=StrToDate(S);
  633. ftTime : Result:=StrToTime(S);
  634. ftDateTime : Result:=StrToDateTime(S);
  635. end
  636. else
  637. begin
  638. Result:=ScanDateTime(ptrn,S,1);
  639. end;
  640. end;
  641. function TBaseJSONDataSet.FormatDateTimeField(DT: TDateTime; F: TField
  642. ): String;
  643. Var
  644. Ptrn : string;
  645. begin
  646. Result:='';
  647. Case F.DataType of
  648. ftDate : Ptrn:=TJSONDateField(F).DateFormat;
  649. ftTime : Ptrn:=TJSONTimeField(F).TimeFormat;
  650. ftDateTime : Ptrn:=TJSONDateTimeField(F).DateTimeFormat;
  651. end;
  652. If (Ptrn='') then
  653. Case F.DataType of
  654. ftDate : Result:=DateToStr(DT);
  655. ftTime : Result:=TimeToStr(DT);
  656. ftDateTime : Result:=DateTimeToStr(DT);
  657. end
  658. else
  659. Result:=FormatDateTime(ptrn,DT);
  660. end;
  661. function TBaseJSONDataSet.GetFieldData(Field: TField; Buffer: TDatarecord): JSValue;
  662. var
  663. R : JSValue;
  664. begin
  665. if State in [dsCalcFields,dsInternalCalc] then
  666. R:=CalcBuffer.data
  667. else if (FEditIdx=Buffer.Bookmark) then
  668. begin
  669. if State=dsOldValue then
  670. R:=Buffer.data
  671. else
  672. R:=FEditRow
  673. end
  674. else
  675. begin
  676. if State=dsOldValue then
  677. Exit(Null)
  678. else
  679. R:=Buffer.data;
  680. end;
  681. Result:=FFieldMapper.GetJSONDataForField(Field,R);
  682. end;
  683. procedure TBaseJSONDataSet.SetFieldData(Field: TField; var Buffer: TDatarecord; AValue : JSValue);
  684. var
  685. R : JSValue;
  686. begin
  687. if State in [dsCalcFields,dsInternalCalc] then
  688. R:=CalcBuffer.Data
  689. else
  690. R:=FEditRow;
  691. FFieldMapper.SetJSONDataForField(Field,R,AValue);
  692. if not(State in [dsCalcFields, dsInternalCalc, dsFilter, dsNewValue]) then
  693. DataEvent(deFieldChange, Field);
  694. SetModified(True);
  695. // FFieldMapper.SetJSONDataForField(Field,Buffer.Data,AValue);
  696. end;
  697. procedure TBaseJSONDataSet.SetBookmarkFlag(var Buffer: TDataRecord; Value: TBookmarkFlag);
  698. begin
  699. Buffer.BookmarkFlag := Value;
  700. end;
  701. function TBaseJSONDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
  702. begin
  703. if isNumber(Bookmark1.Data) and isNumber(Bookmark2.Data) then
  704. Result := Integer(Bookmark2.Data) - Integer(Bookmark1.Data)
  705. else
  706. begin
  707. if isNumber(Bookmark1.Data) then
  708. Result := -1
  709. else
  710. if isNumber(Bookmark2.Data) then
  711. Result := 1
  712. else
  713. Result := 0;
  714. end;
  715. end;
  716. procedure TBaseJSONDataSet.SetRecNo(Value: Integer);
  717. begin
  718. if (Value < 1) or (Value > FCurrentIndex.Count) then
  719. raise EJSONDataset.CreateFmt('%s: SetRecNo: index %d out of range',[Name,Value]);
  720. FCurrent := Value - 1;
  721. Resync([]);
  722. DoAfterScroll;
  723. end;
  724. constructor TBaseJSONDataSet.Create(AOwner: TComponent);
  725. begin
  726. inherited;
  727. FownsData:=True;
  728. UseDateTimeFormatFields:=False;
  729. FEditIdx:=-1;
  730. end;
  731. destructor TBaseJSONDataSet.Destroy;
  732. begin
  733. FEditIdx:=-1;
  734. FreeData;
  735. inherited;
  736. end;
  737. end.