jsondataset.pas 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2019 by Michael Van Canneyt, member of the
  4. Free Pascal development team
  5. Simple JSON dataset component.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}
  13. unit JSONDataset;
  14. interface
  15. uses
  16. Types, JS, DB, Classes, SysUtils, typinfo, fpexprpars;
  17. type
  18. TBaseJSONDataset = Class;
  19. TJSONIndexDef = class;
  20. // How are rows encoded in the JSON ?
  21. TJSONRowType = (rtJSONObject, // Each row is an object.
  22. rtJSONArray // Each row is an array.
  23. );
  24. { TJSONFieldMapper }
  25. // This class is responsible for mapping the field objects of the records.
  26. TJSONFieldMapper = Class(TObject)
  27. Public
  28. // Remove a field from the
  29. Procedure RemoveField(Const FieldName : String; FieldIndex : Integer; Row : JSValue); virtual; abstract;
  30. // Return row TJSONData instance with data for field 'FieldName' or 'FieldIndex'.
  31. Function GetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row : JSValue) : JSValue; virtual; abstract;
  32. // Same, but now based on TField.
  33. Function GetJSONDataForField(F : TField; Row : JSValue) : JSValue; virtual;
  34. // Set data for field 'FieldName' or 'FieldIndex' to supplied TJSONData instance in row
  35. procedure SetJSONDataForField(Const FieldName : String; FieldIndex : Integer; Row,Data : JSValue); virtual; abstract;
  36. // Set data for field TField to supplied TJSONData instance
  37. procedure SetJSONDataForField(F : TField; Row,Data : JSValue); virtual;
  38. // Create a new row.
  39. Function CreateRow : JSValue; virtual; abstract;
  40. end;
  41. // JSON has no date/time type, so we use a string field.
  42. // ExtJS provides the date/time format in it's field config: 'dateFormat'
  43. // The below field classes store this in the NNNFormat field.
  44. { TJSONDateField }
  45. TJSONDateField = Class(TDateField)
  46. private
  47. FDateFormat: String;
  48. Published
  49. Property DateFormat : String Read FDateFormat Write FDateFormat;
  50. end;
  51. { TJSONTimeField }
  52. TJSONTimeField = Class(TTimeField)
  53. private
  54. FTimeFormat: String;
  55. Published
  56. Property TimeFormat : String Read FTimeFormat Write FTimeFormat;
  57. end;
  58. { TJSONDateTimeField }
  59. TJSONDateTimeField = Class(TDateTimeField)
  60. private
  61. FDateTimeFormat: String;
  62. Published
  63. Property DateTimeFormat : String Read FDateTimeFormat Write FDateTimeFormat;
  64. end;
  65. { TFieldComparer }
  66. TFieldComparer = Class
  67. Private
  68. FDesc: Boolean;
  69. FValue : JSValue;
  70. FField : TField;
  71. FOptions : TLocateOptions;
  72. FDataset : TBaseJSONDataset;
  73. Public
  74. Constructor Create(aDataset : TBaseJSONDataset; aField : TField; aValue : JSValue; aOptions : TLocateOptions);
  75. Function GetFieldValue(RowIndex : integer) : JSValue;
  76. Function CompareRows (RowIndex1,RowIndex2 : Integer) : Integer; virtual;
  77. // First value is always dataset value.
  78. Function Compare (RowIndex : Integer; aValue : JSValue) : Integer; virtual; abstract;
  79. Function Compare (RowIndex : Integer) : Integer; virtual;
  80. Property Value : JSValue read FValue Write FValue;
  81. Property Options : TLocateOptions Read FOptions Write FOptions;
  82. Property Dataset : TBaseJSONDataset Read FDataset;
  83. Property Field : TField Read FField;
  84. Property Desc : Boolean Read FDesc Write FDesc;
  85. end;
  86. TFieldComparerClass = Class of TFieldComparer;
  87. { TStringFieldComparer }
  88. TStringFieldComparer = Class (TFieldComparer)
  89. Function Compare (RowIndex : Integer; aValue : JSValue) : Integer; override;
  90. end;
  91. { TNativeIntFieldComparer }
  92. TNativeIntFieldComparer = Class (TFieldComparer)
  93. Function Compare (RowIndex : Integer; aValue : JSValue) : Integer; override;
  94. end;
  95. { TBooleanFieldComparer }
  96. TBooleanFieldComparer = Class (TFieldComparer)
  97. Function Compare (RowIndex : Integer; aValue : JSValue) : Integer; override;
  98. end;
  99. { TDateTimeFieldComparer }
  100. TDateTimeFieldComparer = Class (TFieldComparer)
  101. Function Compare (RowIndex : Integer; aValue : JSValue) : Integer; override;
  102. end;
  103. { TFloatFieldComparer }
  104. TFloatFieldComparer = Class (TFieldComparer)
  105. Function Compare (RowIndex : Integer; aValue : JSValue) : Integer; override;
  106. end;
  107. { TRecordComparer }
  108. TRecordComparer = class
  109. private
  110. FDataset: TBaseJSONDataset;
  111. FIndexBased: Boolean;
  112. FItems : Array of TFieldComparer;
  113. FOptions: TLocateOptions;
  114. FValues: TJSValueDynArray;
  115. function GetFieldComparer(Index : Integer): TFieldComparer;
  116. Protected
  117. procedure ConstructItems(aFields: String); virtual;
  118. function DataTypeToComparerClass(aFieldType: TFieldType): TFieldComparerClass;
  119. Function Compare(aRowindex : integer) : Integer;
  120. Function CompareRows(aRowindex1,aRowIndex2 : integer) : Integer;
  121. procedure updateFromIndex(aIndex : TJSONIndexDef); virtual;
  122. Public
  123. Constructor Create(aDataset : TBaseJSONDataset; aFields : String; aValues : JSValue; aOptions : TLocateOptions);
  124. Constructor Create(aDataset : TBaseJSONDataset; aIndex : TJSONIndexDef);
  125. Destructor Destroy; override;
  126. Property Dataset : TBaseJSONDataset Read FDataset;
  127. property Items [Index : Integer] : TFieldComparer Read GetFieldComparer;
  128. Property Options : TLocateOptions Read FOptions Write FOptions;
  129. Property Values : TJSValueDynArray Read FValues;
  130. Property IndexBased : Boolean Read FIndexBased;
  131. end;
  132. TRecordComparerClass = Class of TRecordComparer;
  133. { TBaseJSONDataSet }
  134. { TJSONIndex }
  135. TJSONIndex = Class
  136. FList : TJSArray; // Indexes of elements in FRows.
  137. FRows : TJSArray;
  138. FDataset : TBaseJSONDataset;
  139. private
  140. function GetRecordIndex(aListIndex : Integer): NativeInt;
  141. protected
  142. Function GetCount: Integer; virtual;
  143. Procedure CreateIndex; Virtual; abstract;
  144. Property List : TJSArray Read FList;
  145. Property Rows : TJSArray Read FRows;
  146. Property Dataset : TBaseJSONDataset Read FDataset;
  147. Public
  148. Constructor Create(aDataset: TBaseJSONDataset; aRows : TJSArray); reintroduce;
  149. // Append remainder of FRows to FList.
  150. Procedure AppendToIndex; virtual; abstract;
  151. // Delete aListIndex from list, not from row. Return Recordindex of deleted record.
  152. Function Delete(aListIndex : Integer) : Integer; virtual;
  153. // Append aRecordIndex to list. Return ListIndex of appended record.
  154. Function Append(aRecordIndex : Integer) : Integer; virtual; abstract;
  155. // Insert record into list. By default, this does an append. Return ListIndex of inserted record
  156. Function Insert(aCurrentIndex{%H-}, aRecordIndex : Integer) : Integer; virtual;
  157. // Record at index aCurrentIndex has changed. Update index and return new listindex.
  158. Function Update(aRecordIndex : Integer) : Integer; virtual; abstract;
  159. // Find list index for Record at index aCurrentIndex. Return -1 if not found.
  160. Function FindRecord(aRecordIndex : Integer) : Integer; virtual; abstract;
  161. // index of record in FRows based on aListIndex in List.
  162. Property RecordIndex[aListIndex : Integer] : NativeInt Read GetRecordIndex;
  163. // Number of records in index. This can differ from FRows, e.g. when filtering.
  164. Property Count : Integer Read GetCount;
  165. end;
  166. { TDefaultJSONIndex }
  167. TDefaultJSONIndex = Class(TJSONIndex)
  168. public
  169. Procedure CreateIndex; override;
  170. Procedure AppendToIndex; override;
  171. Function Append(aRecordIndex : Integer) : Integer; override;
  172. Function Insert(aCurrentIndex, aRecordIndex : Integer) : Integer; override;
  173. Function FindRecord(aRecordIndex : Integer) : Integer; override;
  174. Function Update(aRecordIndex : Integer) : Integer; override;
  175. end;
  176. { TSortedJSONIndex }
  177. TSortedJSONIndex = Class(TJSONIndex)
  178. Private
  179. FComparer : TRecordComparer;
  180. FUnique: Boolean;
  181. function FindPos(aRecordIndex: Integer): Integer;
  182. function MergeSort(aList: TJSArray): TJSArray;
  183. Protected
  184. Property Comparer : TRecordComparer Read FComparer Write FComparer;
  185. public
  186. Destructor Destroy; override;
  187. procedure CreateComparer(aIndex: TJSONIndexDef);
  188. Procedure CreateIndex; override;
  189. Procedure AppendToIndex; override;
  190. Function Append(aRecordIndex : Integer) : Integer; override;
  191. Function FindRecord(aRecordIndex : Integer) : Integer; override;
  192. Function Update(aRecordIndex : Integer) : Integer; override;
  193. Property Unique : Boolean Read FUnique Write FUnique;
  194. end;
  195. { TJSONIndexDef }
  196. TJSONIndexDef = class(TIndexDef)
  197. Private
  198. FIndex : TSortedJSONIndex;
  199. Protected
  200. Property Index : TSortedJSONIndex Read FIndex Write FIndex;
  201. Public
  202. Procedure BuildIndex(aDataset : TBaseJSONDataset);
  203. end;
  204. { TJSONIndexDefs }
  205. TJSONIndexDefs = Class(TIndexDefs)
  206. private
  207. function GetD(aIndex : Integer): TJSONIndexDef;
  208. procedure SetD(aIndex : Integer; AValue: TJSONIndexDef);
  209. Public
  210. Function AddJSONIndexDef: TJSONIndexDef;
  211. Property Defs[aIndex : Integer] : TJSONIndexDef Read GetD Write SetD; default;
  212. end;
  213. // basic JSON dataset. Does nothing ExtJS specific.
  214. TBaseJSONDataSet = class (TDataSet)
  215. private
  216. FActiveIndex: String;
  217. FIndexes: TJSONIndexDefs;
  218. FMUS: Boolean;
  219. FOwnsData : Boolean;
  220. FDefaultIndex : TJSONIndex; // Default index, built from array
  221. FCurrentIndex : TJSONIndex; // Currently active index.
  222. FCurrent: Integer; // Record Index in the current IndexList
  223. // Possible metadata to configure fields from.
  224. FMetaData : TJSObject;
  225. // This will contain the rows.
  226. FRows : TJSArray;
  227. // Deleted rows
  228. FDeletedRows : TJSArray;
  229. FFieldMapper : TJSONFieldMapper;
  230. // When editing, this object is edited.
  231. FEditIdx : Integer;
  232. FEditRow : JSValue;
  233. // When filtering, this is the current row;
  234. FFilterRow : JSValue;
  235. FUseDateTimeFormatFields: Boolean;
  236. FRowType: TJSONRowType;
  237. FFilterExpression : TFPExpressionParser;
  238. function GetFilterField(const AName: String): TFPExpressionResult;
  239. procedure RemoveCalcFields(Buf: JSValue);
  240. procedure SetActiveIndex(AValue: String);
  241. procedure SetIndexes(AValue: TJSONIndexDefs);
  242. procedure SetMetaData(AValue: TJSObject);
  243. procedure SetRows(AValue: TJSArray);
  244. procedure SetRowType(AValue: TJSONRowType);
  245. protected
  246. // Determine filter value type based on field type
  247. function FieldTypeToExpressionType(aDataType: TFieldType): TResultType; virtual;
  248. // Callback for IsNull filter function.
  249. function GetFilterIsNull(const Args: TExprParameterArray): TFPExpressionResult; virtual;
  250. // Expression parser class. Override this to create a customized version.
  251. function FilterExpressionClass: TFPExpressionParserClass; virtual;
  252. // Create filter expression.
  253. function CreateFilterExpression: TFPExpressionParser; virtual;
  254. // Function called to check if current buffer should be accepted.
  255. function DoFilterRecord: Boolean; virtual;
  256. // Override this to return customized version.
  257. function CreateIndexDefs: TJSONIndexDefs; virtual;
  258. // override this to return a customized version if you are so inclined
  259. function RecordComparerClass: TRecordComparerClass; virtual;
  260. // Return index of Row in FRows matching keyfields/values. If not found, -1 is returned.
  261. function LocateRecordIndex(const KeyFields: string; const KeyValues: JSValue; Options: TLocateOptions): Integer; virtual;
  262. // dataset virtual methods
  263. function AllocRecordBuffer: TDataRecord; override;
  264. procedure FreeRecordBuffer(var Buffer: TDataRecord); override;
  265. procedure InternalInitRecord(var Buffer: TDataRecord); override;
  266. function GetRecord(Var Buffer: TDataRecord; GetMode: TGetMode; DoCheck{%H-}: Boolean): TGetResult; override;
  267. function GetRecordSize: Word; override;
  268. procedure AddToRows(AValue: TJSArray);
  269. procedure InternalClose; override;
  270. procedure InternalDelete; override;
  271. procedure InternalFirst; override;
  272. procedure InternalLast; override;
  273. procedure InternalOpen; override;
  274. procedure InternalPost; override;
  275. procedure InternalInsert; override;
  276. procedure InternalEdit; override;
  277. procedure InternalCancel; override;
  278. procedure InternalInitFieldDefs; override;
  279. procedure InternalSetToRecord(Buffer: TDataRecord); override;
  280. procedure SetFilterText(const Value: string); override;
  281. procedure SetFiltered(Value: Boolean); override;
  282. function GetFieldClass(FieldType: TFieldType): TFieldClass; override;
  283. function IsCursorOpen: Boolean; override;
  284. // Bookmark operations
  285. procedure GetBookmarkData(Buffer: TDataRecord; var Data: TBookmark); override;
  286. function GetBookmarkFlag(Buffer: TDataRecord): TBookmarkFlag; override;
  287. procedure InternalGotoBookmark(ABookmark: TBookmark); override;
  288. procedure SetBookmarkFlag(Var Buffer: TDataRecord; Value: TBookmarkFlag); override;
  289. procedure SetBookmarkData(Var Buffer: TDataRecord; Data: TBookmark); override;
  290. function GetRecordCount: Integer; override;
  291. procedure SetRecNo(Value: Integer); override;
  292. function GetRecNo: Integer; override;
  293. Protected
  294. // New methods.
  295. // Build all sorted indexes. Default index is not built.
  296. Procedure BuildIndexes;
  297. // Called when dataset is closed. If OwnsData is true, metadata and rows are freed.
  298. Procedure FreeData; virtual;
  299. // Fill default list.
  300. procedure AppendToIndexes; virtual;
  301. Procedure CreateIndexes; virtual;
  302. // Convert MetaData object to FieldDefs.
  303. Procedure MetaDataToFieldDefs; virtual; abstract;
  304. // Initialize Date/Time info in all date/time fields. Called during InternalOpen
  305. procedure InitDateTimeFields; virtual;
  306. // Convert JSON date S to DateTime for Field F
  307. function ConvertDateTimeField(S: String; F: TField): TDateTime; virtual;
  308. // Format JSON date to from DT for Field F
  309. function FormatDateTimeField(DT : TDateTime; F: TField): String; virtual;
  310. // Create fieldmapper. A descendent MUST implement this.
  311. Function CreateFieldMapper : TJSONFieldMapper; virtual;
  312. // If True, then the dataset will free MetaData and FRows when it is closed.
  313. Property OwnsData : Boolean Read FownsData Write FOwnsData;
  314. // set to true if unknown field types should be handled as string fields.
  315. Property MapUnknownToStringType : Boolean Read FMUS Write FMUS;
  316. // Metadata
  317. Property MetaData : TJSObject Read FMetaData Write SetMetaData;
  318. // Rows
  319. Property Rows : TJSArray Read FRows Write SetRows;
  320. // RowType
  321. Property RowType : TJSONRowType Read FRowType Write SetRowType;
  322. // Fieldmapper
  323. Property FieldMapper : TJSONFieldMapper Read FFieldMapper;
  324. // FieldClass
  325. Property UseDateTimeFormatFields : Boolean Read FUseDateTimeFormatFields Write FUseDateTimeFormatFields;
  326. // Indexes
  327. Property Indexes : TJSONIndexDefs Read FIndexes Write SetIndexes;
  328. // Active index name. Set to empty for default index.
  329. Property ActiveIndex : String Read FActiveIndex Write SetActiveIndex;
  330. public
  331. constructor Create (AOwner: TComponent); override;
  332. destructor Destroy; override;
  333. function Locate(const KeyFields: string; const KeyValues: JSValue; Options: TLocateOptions): boolean; override;
  334. function Lookup(const KeyFields: string; const KeyValues: JSValue; const ResultFields: string): JSValue; override;
  335. function GetFieldData(Field: TField; Buffer: TDatarecord): JSValue; override;
  336. procedure SetFieldData(Field: TField; var Buffer{%H-}: TDatarecord; AValue : JSValue); override;
  337. function BookmarkValid(ABookmark: TBookmark): Boolean; override;
  338. function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint; override;
  339. end;
  340. TJSONDataset = Class(TBaseJSONDataset)
  341. published
  342. Property FieldDefs;
  343. Property RowType;
  344. Property UseDateTimeFormatFields;
  345. Property Indexes;
  346. Property ActiveIndex;
  347. property Active;
  348. property BeforeOpen;
  349. property AfterOpen;
  350. property BeforeClose;
  351. property AfterClose;
  352. property BeforeInsert;
  353. property AfterInsert;
  354. property BeforeEdit;
  355. property AfterEdit;
  356. property BeforePost;
  357. property AfterPost;
  358. property BeforeCancel;
  359. property AfterCancel;
  360. property BeforeDelete;
  361. property AfterDelete;
  362. property BeforeScroll;
  363. property AfterScroll;
  364. property OnCalcFields;
  365. property OnDeleteError;
  366. property OnEditError;
  367. property OnFilterRecord;
  368. property OnNewRecord;
  369. property OnPostError;
  370. end;
  371. { TJSONObjectFieldMapper }
  372. // Fieldmapper to be used when the data is in an object
  373. TJSONObjectFieldMapper = Class(TJSONFieldMapper)
  374. Public
  375. Procedure RemoveField(Const FieldName : String; FieldIndex : Integer; Row : JSValue); override;
  376. procedure SetJSONDataForField(Const FieldName : String; FieldIndex{%H-} : Integer; Row,Data : JSValue); override;
  377. Function GetJSONDataForField(Const FieldName : String; FieldIndex{%H-} : Integer; Row : JSValue) : JSValue; override;
  378. Function CreateRow : JSValue; override;
  379. end;
  380. { TJSONArrayFieldMapper }
  381. // Fieldmapper to be used when the data is in an array
  382. TJSONArrayFieldMapper = Class(TJSONFieldMapper)
  383. Public
  384. Procedure RemoveField(Const FieldName : String; FieldIndex : Integer; Row : JSValue); override;
  385. procedure SetJSONDataForField(Const FieldName{%H-} : String; FieldIndex : Integer; Row,Data : JSValue); override;
  386. Function GetJSONDataForField(Const FieldName{%H-} : String; FieldIndex : Integer; Row : JSValue) : JSValue; override;
  387. Function CreateRow : JSValue; override;
  388. end;
  389. EJSONDataset = Class(EDatabaseError);
  390. implementation
  391. uses DateUtils;
  392. { TJSONIndexDef }
  393. procedure TJSONIndexDef.BuildIndex(aDataset : TBaseJSONDataset);
  394. begin
  395. if Findex=Nil then
  396. FIndex:=TSortedJSONIndex.Create(aDataset,aDataset.Rows);
  397. FIndex.CreateComparer(Self);
  398. FIndex.CreateIndex;
  399. end;
  400. { TJSONIndexDefs }
  401. function TJSONIndexDefs.GetD(aIndex : Integer): TJSONIndexDef;
  402. begin
  403. Result:=Items[aIndex] as TJSONIndexDef;
  404. end;
  405. procedure TJSONIndexDefs.SetD(aIndex : Integer; AValue: TJSONIndexDef);
  406. begin
  407. Items[aIndex]:=aValue;
  408. end;
  409. function TJSONIndexDefs.AddJSONIndexDef: TJSONIndexDef;
  410. begin
  411. Result:=Add as TJSONIndexDef;
  412. end;
  413. { TSortedJSONIndex }
  414. Function TSortedJSONIndex.MergeSort(aList : TJSArray) : TJSArray;
  415. Var
  416. temp : TJSArray;
  417. l,p,q,e,tail : integer;
  418. insize, nmerges, psize, qsize : Integer;
  419. begin
  420. if aList=Nil then
  421. Exit(Nil);
  422. L:=aList.length;
  423. Result:=TJSArray.new(l);
  424. if L=0 then exit;
  425. insize:=1;
  426. Repeat
  427. p:=0;
  428. Tail:=0;
  429. nmerges := 0; // count number of merges we do in this pass
  430. while (p<L) do
  431. begin
  432. Inc(nmerges); { there exists a merge to be done }
  433. { step `insize' places along from p }
  434. pSize:=L-P;
  435. if Insize<pSize then
  436. pSize:=InSize;
  437. Q:=P+pSize;
  438. qsize:=insize;
  439. //* now we have two lists; merge them */
  440. while (psize>0) or ((qsize > 0) and (Q<L)) do
  441. begin // /* decide whether next element of merge comes from p or q */
  442. if (psize=0) then
  443. begin // * p is empty; e must come from q. */
  444. e := q; Inc(q); Dec(qsize);
  445. end
  446. else if ((qsize = 0) or (q>=L)) then
  447. begin // * q is empty; e must come from p. */
  448. e := p; Inc(p); Dec(psize);
  449. end
  450. else if (FComparer.CompareRows(Integer(aList[p]),Integer(aList[q])) <= 0) then
  451. begin // * First element of p is lower (or same); * e must come from p. */
  452. e:=p; inc(p); Dec(psize);
  453. end
  454. else
  455. begin // * First element of q is lower; e must come from q. */
  456. e := q; Inc(q); Dec(qsize);
  457. end;
  458. Result[Tail]:=aList[e];
  459. Inc(tail);
  460. end;
  461. p:=q;
  462. end;
  463. // * If we have done only one merge, we're finished. */
  464. if (nmerges <= 1) then //* allow for nmerges==0, the empty list case */
  465. exit;
  466. // * Otherwise repeat, merging lists twice the size */
  467. InSize:=Insize * 2;
  468. // Swap lists for next round.
  469. Temp:=Result;
  470. Result:=aList;
  471. aList:=Temp;
  472. until false;
  473. end;
  474. destructor TSortedJSONIndex.Destroy;
  475. begin
  476. FreeAndNil(FComparer);
  477. Inherited;
  478. end;
  479. procedure TSortedJSONIndex.CreateComparer(aIndex: TJSONIndexDef);
  480. begin
  481. FreeAndNil(FComparer);
  482. FComparer:=TRecordComparer.Create(Dataset,aindex);
  483. end;
  484. procedure TSortedJSONIndex.CreateIndex;
  485. Var
  486. Lst : TJSArray;
  487. Len : Integer;
  488. begin
  489. // CreateIndex is called during constructor. We cannot build index then, so we exit
  490. if FComparer=Nil then
  491. exit;
  492. Len:=FRows.Length-1;
  493. // Temp list, mergsort destroys list
  494. Lst:=TJSArray.New(Len+1);
  495. While Len>=0 do
  496. begin
  497. Lst[Len]:=Len;
  498. Dec(Len);
  499. end;
  500. FList:=MergeSort(Lst);
  501. end;
  502. procedure TSortedJSONIndex.AppendToIndex;
  503. begin
  504. // In theory, we could sort the rest of the list, and then merge the 2 sublists.
  505. CreateIndex;
  506. end;
  507. function TSortedJSONIndex.FindPos(aRecordIndex: Integer): Integer;
  508. Var
  509. L,R,I, CompareRes : Integer;
  510. begin
  511. if not Assigned(FComparer) then
  512. exit;
  513. L := 0;
  514. R := Count - 1;
  515. while (L<=R) do
  516. begin
  517. I := L + (R - L) div 2;
  518. CompareRes := FComparer.CompareRows(aRecordIndex, Integer(Flist[I]));
  519. if (CompareRes>0) then
  520. L := I+1
  521. else
  522. begin
  523. R := I-1;
  524. if (CompareRes=0) then
  525. begin
  526. if Unique then
  527. L := I; // forces end of while loop
  528. end;
  529. end;
  530. end;
  531. Result:=L;
  532. end;
  533. function TSortedJSONIndex.Append(aRecordIndex: Integer): Integer;
  534. begin
  535. Result:=FindPos(aRecordIndex);
  536. // insert in list
  537. FList.Splice(Result,0,aRecordIndex);
  538. end;
  539. function TSortedJSONIndex.FindRecord(aRecordIndex: Integer): Integer;
  540. begin
  541. Result:=FList.indexOf(aRecordIndex);
  542. end;
  543. function TSortedJSONIndex.Update(aRecordIndex: Integer): Integer;
  544. Var
  545. aCurrentIndex : Integer;
  546. begin
  547. // Old pos
  548. aCurrentIndex:=FindRecord(aRecordIndex);
  549. // New pos
  550. Result:=FindPos(aRecordIndex);
  551. if Result<>aCurrentIndex then
  552. FList.Splice(Result,0,FList.Splice(aCurrentIndex,1)[0])
  553. end;
  554. { TFloatFieldComparer }
  555. function TFloatFieldComparer.Compare(RowIndex: Integer; aValue: JSValue): Integer;
  556. var
  557. D1,D2 : Double;
  558. begin
  559. D1:=Double(GetFieldValue(Rowindex));
  560. D2:=Double(aValue);
  561. Result:=Round(D1-D2);
  562. end;
  563. { TDateTimeFieldComparer }
  564. function TDateTimeFieldComparer.Compare(RowIndex: Integer; aValue: JSValue): Integer;
  565. var
  566. D1,D2 : TDateTime;
  567. begin
  568. D1:=Dataset.ConvertDateTimeField(String(GetFieldValue(Rowindex)),Self.Field);
  569. D2:=TDateTime(aValue);
  570. Result:=Round(D1-D2);
  571. end;
  572. { TBooleanFieldComparer }
  573. function TBooleanFieldComparer.Compare(RowIndex: Integer; aValue: JSValue): Integer;
  574. var
  575. B1,B2 : Boolean;
  576. begin
  577. B1:=Boolean(GetFieldValue(Rowindex));
  578. B2:=Boolean(aValue);
  579. Result:=Ord(B1)-Ord(B2);
  580. end;
  581. { TNativeIntFieldComparer }
  582. function TNativeIntFieldComparer.Compare(RowIndex: Integer; aValue: JSValue): Integer;
  583. var
  584. I1,I2 : NativeInt;
  585. begin
  586. I1:=NativeInt(GetFieldValue(Rowindex));
  587. I2:=NativeInt(aValue);
  588. Result:=I1-I2;
  589. end;
  590. { TStringFieldComparer }
  591. function TStringFieldComparer.Compare(RowIndex: Integer; aValue: JSValue): Integer;
  592. var
  593. S1,S2 : String;
  594. begin
  595. S1:=String(GetFieldValue(Rowindex));
  596. S2:=String(aValue);
  597. if loPartialKey in Options then
  598. S1:=Copy(S1,1,Length(S2));
  599. if loCaseInsensitive in options then
  600. Result := CompareText(S1,S2)
  601. else
  602. Result := CompareStr(S1,S2);
  603. end;
  604. { TFieldComparer }
  605. constructor TFieldComparer.Create(aDataset: TBaseJSONDataset; aField: TField; aValue: JSValue; aOptions: TLocateOptions);
  606. begin
  607. FField:=AField;
  608. FValue:=aValue;
  609. FOptions:=aOptions;
  610. FDataset:=aDataset;
  611. end;
  612. function TFieldComparer.GetFieldValue(RowIndex: integer): JSValue;
  613. begin
  614. Result:=FDataset.FieldMapper.GetJSONDataForField(FField,FDataset.FRows[Rowindex]);
  615. end;
  616. function TFieldComparer.CompareRows(RowIndex1, RowIndex2: Integer): Integer;
  617. begin
  618. Result:=Compare(RowIndex1,GetFieldValue(RowIndex2));
  619. end;
  620. function TFieldComparer.Compare(RowIndex: Integer): Integer;
  621. begin
  622. Result:=Compare(RowIndex,FValue);
  623. end;
  624. { TRecordComparer }
  625. function TRecordComparer.GetFieldComparer(Index: Integer): TFieldComparer;
  626. begin
  627. if (Index<0) or (Index>=Length(Fitems)) then
  628. Raise EListError.CreateFmt('Index out of bounds: %d not in [%d,%d]',[Index,0,Length(Fitems)-1]);
  629. Result:=Items[Index];
  630. end;
  631. procedure TRecordComparer.ConstructItems(aFields : String);
  632. Var
  633. L : TFPlist;
  634. FCC : TFieldComparerClass;
  635. F : TField;
  636. I : Integer;
  637. begin
  638. L:=TFPList.Create;
  639. try
  640. Dataset.GetFieldList(L,aFields);
  641. if Not Indexbased and (L.Count<>Length(FValues)) then
  642. Raise EDatabaseError.CreateFmt('Array of values has different length (%d) from array of fields (%d)',[Length(FValues), L.Count]);
  643. SetLength(FItems,L.Count);
  644. For I:=0 to L.Count-1 do
  645. begin
  646. F:=TField(L[i]);
  647. FCC:=DataTypeToComparerClass(F.DataType);
  648. If FCC=Nil then
  649. Raise EDatabaseError.CreateFmt('Cannot locate on field %s of type %s)',[F.FieldName,GetEnumName(TypeInfo(TFieldType),Ord(F.DataType))]);
  650. if IndexBased then
  651. Fitems[i]:=FCC.Create(FDataset,F,Null,FOptions)
  652. else
  653. Fitems[i]:=FCC.Create(FDataset,F,FValues[i],FOptions);
  654. end;
  655. finally
  656. L.Free;
  657. end;
  658. end;
  659. function TRecordComparer.DataTypeToComparerClass(aFieldType: TFieldType): TFieldComparerClass;
  660. begin
  661. Case aFieldType of
  662. ftMemo, ftFixedChar,ftString :
  663. Result:=TStringFieldComparer;
  664. ftAutoInc, ftInteger, ftLargeInt:
  665. Result:=TNativeIntFieldComparer;
  666. ftBoolean:
  667. Result:=TBooleanFieldComparer;
  668. ftFloat:
  669. Result:=TFloatFieldComparer;
  670. ftDate, ftTime, ftDateTime:
  671. Result:=TDateTimeFieldComparer;
  672. else
  673. result:=Nil;
  674. end;
  675. end;
  676. function TRecordComparer.Compare(aRowindex: integer): Integer;
  677. Var
  678. I,L : Integer;
  679. begin
  680. Result:=0;
  681. I:=0;
  682. L:=Length(FItems);
  683. While (Result=0) and (I<L) do
  684. begin
  685. Result:=Fitems[i].Compare(aRowindex);
  686. Inc(I);
  687. end;
  688. end;
  689. function TRecordComparer.CompareRows(aRowindex1, aRowIndex2: integer): Integer;
  690. Var
  691. I,L : Integer;
  692. begin
  693. Result:=0;
  694. I:=0;
  695. L:=Length(FItems);
  696. While (Result=0) and (I<L) do
  697. begin
  698. Result:=Fitems[i].CompareRows(aRowindex1,aRowIndex2);
  699. if (Result<>0) and Fitems[i].Desc then
  700. Result:=-Result;
  701. Inc(I);
  702. end;
  703. end;
  704. procedure TRecordComparer.updateFromIndex(aIndex: TJSONIndexDef);
  705. Var
  706. L : TFPList;
  707. I : Integer;
  708. begin
  709. L:=TFPList.Create;
  710. try
  711. if (aIndex.CaseInsFields<>'') then
  712. begin
  713. Dataset.GetFieldList(L,aIndex.CaseInsFields);
  714. for I:=0 to Length(FItems)-1 do
  715. if L.IndexOf(FItems[i].Field)<>-1 then
  716. Fitems[i].Options:=Fitems[i].Options+[loCaseInsensitive];
  717. end;
  718. L.Clear;
  719. Dataset.GetFieldList(L,aIndex.DescFields);
  720. for I:=0 to Length(FItems)-1 do
  721. Fitems[i].Desc:=(ixDescending in aIndex.Options) or (L.IndexOf(FItems[i].Field)<>-1);
  722. finally
  723. L.Free;
  724. end;
  725. end;
  726. constructor TRecordComparer.Create(aDataset: TBaseJSONDataset; aFields: String; aValues: JSValue; aOptions: TLocateOptions);
  727. begin
  728. FDataset:=aDataset;
  729. if isArray(aValues) then
  730. FValues:=TJSValueDynArray(aValues)
  731. else
  732. begin
  733. SetLength(FValues,1);
  734. FValues[0]:=Avalues;
  735. end;
  736. Foptions:=aOptions;
  737. ConstructItems(aFields);
  738. end;
  739. constructor TRecordComparer.Create(aDataset: TBaseJSONDataset; aIndex: TJSONIndexDef);
  740. begin
  741. FDataset:=aDataset;
  742. FIndexBased:=True;
  743. if ixCaseInsensitive in aIndex.Options then
  744. FOptions:=[loCaseInsensitive];
  745. ConstructItems(aIndex.Fields);
  746. UpdateFromIndex(aIndex);
  747. end;
  748. destructor TRecordComparer.Destroy;
  749. Var
  750. I : Integer;
  751. begin
  752. For I:=0 to Length(FItems)-1 do
  753. FItems[i].Free;
  754. inherited Destroy;
  755. end;
  756. { TDefaultJSONIndex }
  757. procedure TDefaultJSONIndex.CreateIndex;
  758. Var
  759. I : Integer;
  760. begin
  761. For I:=0 to FRows.length-1 do
  762. FList[i]:=I;
  763. end;
  764. procedure TDefaultJSONIndex.AppendToIndex;
  765. Var
  766. I,L : Integer;
  767. begin
  768. L:=FList.Length;
  769. FList.Length:=FRows.Length;
  770. For I:=L to FRows.Length-1 do
  771. FList[i]:=I;
  772. end;
  773. function TDefaultJSONIndex.Append(aRecordIndex: Integer): Integer;
  774. begin
  775. Result:=FList.Push(aRecordIndex)-1;
  776. end;
  777. function TDefaultJSONIndex.Insert(aCurrentIndex, aRecordIndex: Integer
  778. ): Integer;
  779. begin
  780. FList.splice(aCurrentIndex, 0, aRecordIndex);
  781. Result:=aCurrentIndex;
  782. end;
  783. function TDefaultJSONIndex.FindRecord(aRecordIndex: Integer): Integer;
  784. begin
  785. Result:=FList.indexOf(aRecordIndex);
  786. end;
  787. function TDefaultJSONIndex.Update(aRecordIndex: Integer
  788. ): Integer;
  789. begin
  790. Result:=aRecordIndex;
  791. end;
  792. { TJSONIndex }
  793. constructor TJSONIndex.Create(aDataset: TBaseJSONDataset; aRows: TJSArray);
  794. begin
  795. FRows:=aRows;
  796. FList:=TJSArray.New(FRows.length);
  797. FDataset:=aDataset;
  798. CreateIndex;
  799. end;
  800. function TJSONIndex.Delete(aListIndex: Integer): Integer;
  801. Var
  802. a : TJSArray;
  803. begin
  804. A:=FList.Splice(aListIndex,1);
  805. If a.Length>0 then
  806. Result:=Integer(A[0])
  807. else
  808. Result:=-1;
  809. end;
  810. function TJSONIndex.Insert(aCurrentIndex, aRecordIndex: Integer): Integer;
  811. begin
  812. Result:=Append(aRecordIndex);
  813. end;
  814. function TJSONIndex.GetCount: Integer;
  815. begin
  816. Result:=FList.Length;
  817. end;
  818. function TJSONIndex.GetRecordIndex(aListIndex : Integer): NativeInt;
  819. begin
  820. if isUndefined(FList[aListIndex]) then
  821. Result:=-1
  822. else
  823. Result:=NativeInt(FList[aListIndex]);
  824. end;
  825. { TJSONFieldMapper }
  826. function TJSONFieldMapper.GetJSONDataForField(F: TField; Row: JSValue ): JSValue;
  827. begin
  828. // This supposes that Index is correct, i.e. the field positions have not been changed.
  829. Result:=GetJSONDataForField(F.FieldName,F.Index,Row);
  830. end;
  831. procedure TJSONFieldMapper.SetJSONDataForField(F: TField; Row,Data: JSValue);
  832. begin
  833. SetJSONDataForField(F.FieldName,F.Index,Row,Data);
  834. end;
  835. { TJSONArrayFieldMapper }
  836. procedure TJSONArrayFieldMapper.RemoveField(const FieldName: String; FieldIndex: Integer; Row: JSValue);
  837. begin
  838. TJSArray(Row).Splice(FieldIndex,1);
  839. end;
  840. procedure TJSONArrayFieldMapper.SetJSONDataForField(const FieldName: String;
  841. FieldIndex: Integer; Row, Data: JSValue);
  842. begin
  843. TJSValueDynArray(Row)[FieldIndex]:=Data;
  844. end;
  845. function TJSONArrayFieldMapper.GetJSONDataForField(const FieldName: String; FieldIndex: Integer; Row: JSValue): JSValue;
  846. begin
  847. Result:=TJSValueDynArray(Row)[FieldIndex];
  848. end;
  849. function TJSONArrayFieldMapper.CreateRow: JSValue;
  850. begin
  851. Result:=TJSArray.New;
  852. end;
  853. { TJSONObjectFieldMapper }
  854. procedure TJSONObjectFieldMapper.RemoveField(const FieldName: String; FieldIndex: Integer; Row: JSValue);
  855. begin
  856. jsDelete(Row,FieldName);
  857. end;
  858. procedure TJSONObjectFieldMapper.SetJSONDataForField(const FieldName: String;
  859. FieldIndex: Integer; Row, Data: JSValue);
  860. begin
  861. TJSObject(Row).Properties[FieldName]:=Data;
  862. end;
  863. function TJSONObjectFieldMapper.GetJSONDataForField(const FieldName: String;
  864. FieldIndex: Integer; Row: JSValue): JSValue;
  865. begin
  866. Result:=TJSObject(Row).Properties[FieldName];
  867. end;
  868. function TJSONObjectFieldMapper.CreateRow: JSValue;
  869. begin
  870. Result:=TJSObject.New;
  871. end;
  872. procedure TBaseJSONDataSet.SetMetaData(AValue: TJSObject);
  873. begin
  874. CheckInActive;
  875. FMetaData:=AValue;
  876. end;
  877. procedure TBaseJSONDataSet.SetIndexes(AValue: TJSONIndexDefs);
  878. begin
  879. if FIndexes=AValue then Exit;
  880. FIndexes.Assign(aValue);
  881. if Active then
  882. BuildIndexes;
  883. end;
  884. procedure TBaseJSONDataSet.SetActiveIndex(AValue: String);
  885. Var
  886. Idx : TJSONIndexDef;
  887. begin
  888. if FActiveIndex=AValue then Exit;
  889. if (csLoading in ComponentState) then
  890. FActiveIndex:=AValue
  891. else
  892. begin
  893. if (AValue<>'') then
  894. Idx:=FIndexes.Find(aValue) as TJSONIndexDef
  895. else
  896. Idx:=nil;
  897. FActiveIndex:=AValue;
  898. if Not (csLoading in ComponentState) then
  899. if Idx=Nil then
  900. FCurrentIndex:=FDefaultIndex
  901. else
  902. begin
  903. if Idx.Index=Nil then
  904. Idx.BuildIndex(Self);
  905. FCurrentIndex:=Idx.Index;
  906. end;
  907. if Active then
  908. Resync([rmCenter]);
  909. end;
  910. end;
  911. procedure TBaseJSONDataSet.AddToRows(AValue: TJSArray);
  912. begin
  913. if FRows=Nil then
  914. FRows:=AValue
  915. else
  916. begin
  917. FRows:=FRows.Concat(AValue);
  918. AppendToIndexes;
  919. end;
  920. end;
  921. procedure TBaseJSONDataSet.SetRows(AValue: TJSArray);
  922. begin
  923. if AValue=FRows then exit;
  924. CheckInActive;
  925. FRows:=Nil;
  926. AddToRows(AValue);
  927. end;
  928. procedure TBaseJSONDataSet.SetRowType(AValue: TJSONRowType);
  929. begin
  930. if FRowType=AValue then Exit;
  931. CheckInactive;
  932. FRowType:=AValue;
  933. end;
  934. function TBaseJSONDataSet.AllocRecordBuffer: TDataRecord;
  935. begin
  936. Result.data:=TJSObject.New;
  937. Result.bookmark:=null;
  938. Result.state:=rsNew;
  939. end;
  940. // the next two are particularly ugly.
  941. procedure TBaseJSONDataSet.InternalInitRecord(var Buffer: TDataRecord);
  942. begin
  943. // Writeln('TBaseJSONDataSet.InternalInitRecord');
  944. Buffer.Data:=FFieldMapper.CreateRow;
  945. Buffer.bookmark:=null;
  946. Buffer.state:=rsNew;
  947. end;
  948. procedure TBaseJSONDataSet.FreeRecordBuffer (var Buffer: TDataRecord);
  949. begin
  950. Buffer.Data:=Null;
  951. Buffer.bookmark:=null;
  952. Buffer.state:=rsNew;
  953. end;
  954. procedure TBaseJSONDataSet.GetBookmarkData(Buffer: TDataRecord; var Data: TBookmark);
  955. begin
  956. Data.Data:=Buffer.bookmark;
  957. end;
  958. function TBaseJSONDataSet.GetBookmarkFlag(Buffer: TDataRecord): TBookmarkFlag;
  959. begin
  960. Result :=Buffer.BookmarkFlag;
  961. end;
  962. function TBaseJSONDataSet.GetRecNo: Integer;
  963. Var
  964. bkmIdx : Integer;
  965. begin
  966. bkmIdx:=Integer(ActiveBuffer.bookmark);
  967. Result:=FCurrentIndex.FindRecord(bkmIdx)+1;
  968. end;
  969. procedure TBaseJSONDataSet.InternalInitFieldDefs;
  970. begin
  971. If Assigned(FMetaData) then
  972. MetaDataToFieldDefs;
  973. if (FieldDefs.Count=0) then
  974. Raise EJSONDataset.Create('No fields found');
  975. end;
  976. procedure TBaseJSONDataSet.FreeData;
  977. begin
  978. If FOwnsData then
  979. begin
  980. FRows:=Nil;
  981. FMetaData:=Nil;
  982. end;
  983. if (FCurrentIndex<>FDefaultIndex) then
  984. FreeAndNil(FCurrentIndex)
  985. else
  986. FCurrentIndex:=Nil;
  987. FreeAndNil(FDefaultindex);
  988. FreeAndNil(FFieldMapper);
  989. FCurrentIndex:=Nil;
  990. FDeletedRows:=Nil;
  991. end;
  992. procedure TBaseJSONDataSet.AppendToIndexes;
  993. begin
  994. FDefaultIndex.AppendToIndex;
  995. end;
  996. procedure TBaseJSONDataSet.CreateIndexes;
  997. begin
  998. FDefaultIndex:=TDefaultJSONIndex.Create(Self,FRows);
  999. AppendToIndexes;
  1000. FCurrentIndex:=FDefaultIndex;
  1001. end;
  1002. function TBaseJSONDataSet.FilterExpressionClass : TFPExpressionParserClass;
  1003. begin
  1004. Result:=TFPExpressionParser;
  1005. end;
  1006. function TBaseJSONDataSet.GetFilterIsNull(Const Args : TExprParameterArray) : TFPExpressionResult;
  1007. begin
  1008. Result.ResultType:=rtBoolean;
  1009. Result.ResValue:=FieldByName(String(Args[0].resValue)).IsNull;
  1010. end;
  1011. function TBaseJSONDataSet.FieldTypeToExpressionType(aDataType : TFieldType) : TResultType;
  1012. begin
  1013. Case aDataType of
  1014. ftMemo,
  1015. ftFixedChar,
  1016. ftString : Result:=rtString;
  1017. ftInteger,
  1018. ftAutoInc,
  1019. ftLargeInt : Result:=rtInteger;
  1020. ftBoolean : Result:=rtBoolean;
  1021. ftFloat : Result:=rtFloat;
  1022. ftDate,
  1023. ftTime,
  1024. ftDateTime : Result:=rtDateTime;
  1025. else
  1026. DatabaseErrorFmt('Fields of type %s are not supported in filter expressions.',[Fieldtypenames[aDataType]],Self);
  1027. end;
  1028. end;
  1029. function TBaseJSONDataSet.GetFilterField(Const AName : String) : TFPExpressionResult;
  1030. Var
  1031. F : TField;
  1032. C : Currency;
  1033. begin
  1034. F:=FieldByName(aName);
  1035. Result.resultType:=FieldTypeToExpressionType(F.DataType);
  1036. case Result.resultType of
  1037. rtBoolean : Result.resValue:=F.AsBoolean;
  1038. rtInteger : Result.resValue:=F.AsLargeInt;
  1039. rtFloat : Result.resValue:=F.AsFloat;
  1040. rtDateTime : Result.resValue:=F.AsDateTime;
  1041. rtString : Result.resValue:=F.AsString;
  1042. rtCurrency :
  1043. begin
  1044. C:=Currency(F.AsFloat);
  1045. Result.resValue:=C;
  1046. end;
  1047. end;
  1048. // Writeln('Filtering field ',aName,'value: ',result.resValue);
  1049. end;
  1050. function TBaseJSONDataSet.CreateFilterExpression : TFPExpressionParser;
  1051. Var
  1052. I : Integer;
  1053. begin
  1054. Result:=FilterExpressionClass.Create(Self);
  1055. for I:=0 to Fields.Count-1 do
  1056. Result.Identifiers.AddVariable(Fields[i].FieldName,FieldTypeToExpressionType(Fields[i].DataType),@GetFilterField);
  1057. Result.Identifiers.AddFunction('IsNull','B','S',@GetFilterIsNull);
  1058. Result.Expression:=Filter;
  1059. end;
  1060. function TBaseJSONDataSet.DoFilterRecord : Boolean;
  1061. Var
  1062. DS : TDatasetState;
  1063. begin
  1064. // Writeln('Filtering');
  1065. Result:=True;
  1066. DS:=SetTempState(dsFilter);
  1067. try
  1068. if Assigned(OnFilterRecord) then
  1069. begin
  1070. OnFilterRecord(Self,Result);
  1071. if Not Result then
  1072. Exit;
  1073. end;
  1074. if not Filtered or (Filter='') then
  1075. Exit;
  1076. if (FFilterExpression=Nil) then
  1077. FFilterExpression:=CreateFilterExpression;
  1078. Result:=FFilterExpression.AsBoolean;
  1079. finally
  1080. RestoreState(DS);
  1081. end;
  1082. end;
  1083. function TBaseJSONDataSet.GetRecord(Var Buffer: TDataRecord; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  1084. Var
  1085. BkmIdx : Integer;
  1086. recordAccepted : Boolean;
  1087. begin
  1088. Result := grOK; // default
  1089. Repeat
  1090. recordAccepted:=True;
  1091. case GetMode of
  1092. gmNext: // move on
  1093. if fCurrent < fCurrentIndex.Count - 1 then
  1094. Inc (fCurrent)
  1095. else
  1096. Result := grEOF; // end of file
  1097. gmPrior: // move back
  1098. if fCurrent > 0 then
  1099. Dec (fCurrent)
  1100. else
  1101. Result := grBOF; // begin of file
  1102. gmCurrent: // check if empty
  1103. if fCurrent >= fCurrentIndex.Count then
  1104. Result := grEOF;
  1105. end;
  1106. if Result = grOK then // read the data
  1107. begin
  1108. BkmIdx:=FCurrentIndex.RecordIndex[FCurrent];
  1109. Buffer.Data:=FRows[bkmIdx];
  1110. Buffer.BookmarkFlag := bfCurrent;
  1111. Buffer.Bookmark:=BkmIdx;
  1112. CalculateFields(Buffer);
  1113. if Filtered then
  1114. begin
  1115. FFilterRow:=Buffer.Data;
  1116. recordAccepted:=DoFilterRecord;
  1117. end;
  1118. end;
  1119. until recordAccepted;
  1120. end;
  1121. function TBaseJSONDataSet.GetRecordCount: Integer;
  1122. begin
  1123. Result:=FCurrentIndex.Count;
  1124. end;
  1125. function TBaseJSONDataSet.GetRecordSize: Word;
  1126. begin
  1127. Result := 0; // actual data without house-keeping
  1128. end;
  1129. procedure TBaseJSONDataSet.InternalClose;
  1130. begin
  1131. // disconnet and destroy field objects
  1132. BindFields (False);
  1133. if DefaultFields then
  1134. DestroyFields;
  1135. FreeData;
  1136. end;
  1137. procedure TBaseJSONDataSet.InternalDelete;
  1138. Var
  1139. Idx : Integer;
  1140. begin
  1141. Idx:=FCurrentIndex.Delete(FCurrent);
  1142. if (Idx<>-1) then
  1143. begin
  1144. // Add code here to Delete from other indexes as well.
  1145. // ...
  1146. // Add to array of deleted records.
  1147. if Not Assigned(FDeletedRows) then
  1148. FDeletedRows:=TJSArray.New(FRows[idx])
  1149. else
  1150. FDeletedRows.Push(FRows[Idx]);
  1151. FRows[Idx]:=Undefined;
  1152. end;
  1153. end;
  1154. procedure TBaseJSONDataSet.InternalFirst;
  1155. begin
  1156. FCurrent := -1;
  1157. end;
  1158. procedure TBaseJSONDataSet.InternalGotoBookmark(ABookmark: TBookmark);
  1159. begin
  1160. if isNumber(ABookmark.Data) then
  1161. FCurrent:=FCurrentIndex.FindRecord(Integer(ABookmark.Data));
  1162. // Writeln('Fcurrent', FCurrent,' from ',ABookmark.Data);
  1163. end;
  1164. procedure TBaseJSONDataSet.InternalInsert;
  1165. Var
  1166. I : Integer;
  1167. D : TFieldDef;
  1168. begin
  1169. // Writeln('TBaseJSONDataSet.InternalInsert');
  1170. FEditRow:=ActiveBuffer.Data;
  1171. For I:=0 to FieldDefs.Count-1 do
  1172. begin
  1173. D:=FieldDefs[i];
  1174. FFieldMapper.SetJSONDataForField(D.Name,D.Index,FEditRow,Null);
  1175. end;
  1176. end;
  1177. procedure TBaseJSONDataSet.RemoveCalcFields(Buf : JSValue);
  1178. Var
  1179. i : integer;
  1180. begin
  1181. For I:=0 to Fields.Count-1 do
  1182. if Fields[i].FieldKind in [fkCalculated,fkInternalCalc] then
  1183. FieldMapper.RemoveField(FIelds[i].FieldName,FIelds[i].Index,Buf);
  1184. end;
  1185. procedure TBaseJSONDataSet.InternalEdit;
  1186. begin
  1187. // Writeln('TBaseJSONDataSet.InternalEdit: ');
  1188. FEditIdx:=FCurrentIndex.RecordIndex[FCurrent];
  1189. if not isUndefined(Rows[FEditIdx]) then
  1190. begin
  1191. FEditRow:=TJSJSON.parse(TJSJSON.stringify(Rows[FEditIdx]));
  1192. RemoveCalcFields(FEditRow);
  1193. end
  1194. else
  1195. FEditRow:=TJSObject.new;
  1196. // Writeln('TBaseJSONDataSet.InternalEdit: ',FEditRow);
  1197. end;
  1198. procedure TBaseJSONDataSet.InternalCancel;
  1199. begin
  1200. FEditIdx:=-1;
  1201. FEditRow:=Nil;
  1202. end;
  1203. procedure TBaseJSONDataSet.InternalLast;
  1204. begin
  1205. // The first thing that will happen is a GetPrior Record.
  1206. FCurrent:=FCurrentIndex.Count;
  1207. end;
  1208. procedure TBaseJSONDataSet.InitDateTimeFields;
  1209. begin
  1210. // Do nothing
  1211. end;
  1212. procedure TBaseJSONDataSet.InternalOpen;
  1213. begin
  1214. FreeAndNil(FFieldMapper);
  1215. FFieldMapper:=CreateFieldMapper;
  1216. IF (FRows=Nil) then // opening from fielddefs ?
  1217. begin
  1218. FRows:=TJSArray.New;
  1219. OwnsData:=True;
  1220. end;
  1221. CreateIndexes;
  1222. InternalInitFieldDefs;
  1223. if DefaultFields then
  1224. CreateFields;
  1225. BindFields (True);
  1226. InitDateTimeFields;
  1227. FCurrent := -1;
  1228. end;
  1229. procedure TBaseJSONDataSet.InternalPost;
  1230. Var
  1231. I,NewCurrent,Idx : integer;
  1232. B : TBookmark;
  1233. begin
  1234. NewCurrent:=-1;
  1235. GetBookMarkData(ActiveBuffer,B);
  1236. if (State=dsInsert) then
  1237. begin // Insert or Append
  1238. Idx:=FRows.push(FEditRow)-1;
  1239. if GetBookMarkFlag(ActiveBuffer)=bfEOF then
  1240. begin // Append
  1241. FDefaultIndex.Append(Idx);
  1242. // Must replace this by updating all indexes
  1243. for I:=0 to FIndexes.Count-1 do
  1244. begin
  1245. NewCurrent:=FIndexes[i].Findex.Append(Idx);
  1246. if FIndexes[i].Findex<>FCurrentIndex then
  1247. NewCurrent:=-1;
  1248. end;
  1249. end
  1250. else // insert
  1251. begin
  1252. FCurrent:=FDefaultIndex.Insert(FCurrent,Idx);
  1253. for I:=0 to FIndexes.Count-1 do
  1254. begin
  1255. NewCurrent:=FIndexes[i].Findex.Append(Idx);
  1256. if FIndexes[i].Findex<>FCurrentIndex then
  1257. NewCurrent:=-1;
  1258. end;
  1259. end;
  1260. end
  1261. else
  1262. begin // Edit
  1263. if (FEditIdx=-1) then
  1264. DatabaseErrorFmt('Failed to retrieve record index for record %d',[FCurrent]);
  1265. // Update source record
  1266. Idx:=FEditIdx;
  1267. FRows[Idx]:=FEditRow;
  1268. FDefaultIndex.Update(Idx);
  1269. // Must replace this by updating all indexes.
  1270. // Note that this will change current index.
  1271. for I:=0 to FIndexes.Count-1 do
  1272. begin
  1273. // Determine old index.
  1274. NewCurrent:=FCurrentIndex.Update(Idx);
  1275. if FIndexes[i].Findex<>FCurrentIndex then
  1276. NewCurrent:=-1;
  1277. end;
  1278. end;
  1279. // We have an active index, set current to that index.
  1280. if NewCurrent<>-1 then
  1281. FCurrent:=NewCurrent;
  1282. FEditIdx:=-1;
  1283. FEditRow:=Nil;
  1284. end;
  1285. procedure TBaseJSONDataSet.InternalSetToRecord(Buffer: TDataRecord);
  1286. begin
  1287. FCurrent:=FCurrentIndex.FindRecord(Integer(Buffer.Bookmark));
  1288. end;
  1289. procedure TBaseJSONDataSet.SetFilterText(const Value: string);
  1290. begin
  1291. inherited SetFilterText(Value);
  1292. FreeAndNil(FFilterExpression);
  1293. if Active then
  1294. Resync([rmCenter]);
  1295. end;
  1296. procedure TBaseJSONDataSet.SetFiltered(Value: Boolean);
  1297. begin
  1298. inherited SetFiltered(Value);
  1299. FreeAndNil(FFilterExpression);
  1300. if Active then
  1301. Resync([rmCenter]);
  1302. end;
  1303. function TBaseJSONDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
  1304. begin
  1305. If UseDateTimeFormatFields and (FieldType in [ftDate,ftDateTime,ftTime]) then
  1306. case FieldType of
  1307. ftDate : Result:=TJSONDateField;
  1308. ftDateTime : Result:=TJSONDateTimeField;
  1309. ftTime : Result:=TJSONTimeField;
  1310. end
  1311. else
  1312. Result:=inherited GetFieldClass(FieldType);
  1313. end;
  1314. function TBaseJSONDataSet.IsCursorOpen: Boolean;
  1315. begin
  1316. Result := Assigned(FDefaultIndex);
  1317. end;
  1318. function TBaseJSONDataSet.BookmarkValid(ABookmark: TBookmark): Boolean;
  1319. begin
  1320. Result:=isNumber(ABookmark.Data);
  1321. end;
  1322. procedure TBaseJSONDataSet.SetBookmarkData(var Buffer: TDataRecord; Data: TBookmark);
  1323. begin
  1324. Buffer.Bookmark:=Data.Data;
  1325. // Writeln('Set Bookmark from: ',Data.Data);
  1326. end;
  1327. function TBaseJSONDataSet.ConvertDateTimeField(S : String; F : TField) : TDateTime;
  1328. Var
  1329. Ptrn : string;
  1330. begin
  1331. Result:=0;
  1332. Ptrn:='';
  1333. Case F.DataType of
  1334. ftDate : if F is TJSONDateField then
  1335. Ptrn:=(F as TJSONDateField).DateFormat;
  1336. ftTime : if F is TJSONTimeField then
  1337. Ptrn:=(F as TJSONTimeField).TimeFormat;
  1338. ftDateTime : if F is TJSONDateTimeField then
  1339. Ptrn:=(F as TJSONDateTimeField).DateTimeFormat;
  1340. end;
  1341. If (Ptrn='') then
  1342. Case F.DataType of
  1343. ftDate : Result:=StrToDate(S);
  1344. ftTime : Result:=StrToTime(S);
  1345. ftDateTime : Result:=StrToDateTime(S);
  1346. end
  1347. else
  1348. begin
  1349. Result:=ScanDateTime(ptrn,S,1);
  1350. end;
  1351. end;
  1352. function TBaseJSONDataSet.FormatDateTimeField(DT: TDateTime; F: TField
  1353. ): String;
  1354. Var
  1355. Ptrn : string;
  1356. begin
  1357. Result:='';
  1358. Ptrn:='';
  1359. Case F.DataType of
  1360. ftDate : if F is TJSONDateField then
  1361. Ptrn:=TJSONDateField(F).DateFormat;
  1362. ftTime : if F is TJSONTimeField then
  1363. Ptrn:=TJSONTimeField(F).TimeFormat;
  1364. ftDateTime : if F is TJSONDateTimeField then
  1365. Ptrn:=TJSONDateTimeField(F).DateTimeFormat;
  1366. end;
  1367. If (Ptrn='') then
  1368. Case F.DataType of
  1369. ftDate : Result:=DateToStr(DT);
  1370. ftTime : Result:=TimeToStr(DT);
  1371. ftDateTime : Result:=DateTimeToStr(DT);
  1372. end
  1373. else
  1374. Result:=FormatDateTime(ptrn,DT);
  1375. end;
  1376. function TBaseJSONDataSet.CreateFieldMapper: TJSONFieldMapper;
  1377. begin
  1378. if FRowType=rtJSONArray then
  1379. Result:=TJSONArrayFieldMapper.Create
  1380. else
  1381. Result:=TJSONObjectFieldMapper.Create;
  1382. end;
  1383. function TBaseJSONDataSet.GetFieldData(Field: TField; Buffer: TDatarecord): JSValue;
  1384. var
  1385. R : JSValue;
  1386. begin
  1387. if State in [dsCalcFields,dsInternalCalc] then
  1388. R:=CalcBuffer.data
  1389. else if (State=dsFilter) then
  1390. R:=FFilterRow
  1391. else if (FEditIdx=Buffer.Bookmark) then
  1392. begin
  1393. if State=dsOldValue then
  1394. R:=Buffer.data
  1395. else
  1396. R:=FEditRow
  1397. end
  1398. else
  1399. begin
  1400. if State=dsOldValue then
  1401. Exit(Null)
  1402. else
  1403. R:=Buffer.data;
  1404. end;
  1405. Result:=FFieldMapper.GetJSONDataForField(Field,R);
  1406. end;
  1407. procedure TBaseJSONDataSet.SetFieldData(Field: TField; var Buffer: TDatarecord; AValue : JSValue);
  1408. var
  1409. R : JSValue;
  1410. begin
  1411. if State in [dsCalcFields,dsInternalCalc] then
  1412. R:=CalcBuffer.Data
  1413. else
  1414. R:=FEditRow;
  1415. FFieldMapper.SetJSONDataForField(Field,R,AValue);
  1416. if not(State in [dsCalcFields, dsInternalCalc, dsFilter, dsNewValue]) then
  1417. DataEvent(deFieldChange, Field);
  1418. SetModified(True);
  1419. // FFieldMapper.SetJSONDataForField(Field,Buffer.Data,AValue);
  1420. end;
  1421. procedure TBaseJSONDataSet.SetBookmarkFlag(var Buffer: TDataRecord; Value: TBookmarkFlag);
  1422. begin
  1423. Buffer.BookmarkFlag := Value;
  1424. end;
  1425. function TBaseJSONDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Longint;
  1426. begin
  1427. if isNumber(Bookmark1.Data) and isNumber(Bookmark2.Data) then
  1428. Result := Integer(Bookmark2.Data) - Integer(Bookmark1.Data)
  1429. else
  1430. begin
  1431. if isNumber(Bookmark1.Data) then
  1432. Result := -1
  1433. else
  1434. if isNumber(Bookmark2.Data) then
  1435. Result := 1
  1436. else
  1437. Result := 0;
  1438. end;
  1439. end;
  1440. procedure TBaseJSONDataSet.SetRecNo(Value: Integer);
  1441. begin
  1442. if (Value < 1) or (Value > FCurrentIndex.Count) then
  1443. raise EJSONDataset.CreateFmt('%s: SetRecNo: index %d out of range',[Name,Value]);
  1444. FCurrent := Value - 1;
  1445. Resync([]);
  1446. DoAfterScroll;
  1447. end;
  1448. constructor TBaseJSONDataSet.Create(AOwner: TComponent);
  1449. begin
  1450. inherited;
  1451. FownsData:=True;
  1452. UseDateTimeFormatFields:=False;
  1453. FEditIdx:=-1;
  1454. FIndexes:=CreateIndexDefs;
  1455. end;
  1456. destructor TBaseJSONDataSet.Destroy;
  1457. begin
  1458. FreeAndNil(FFilterExpression);
  1459. FreeAndNil(FIndexes);
  1460. FEditIdx:=-1;
  1461. FreeData;
  1462. inherited;
  1463. end;
  1464. Function TBaseJSONDataSet.CreateIndexDefs : TJSONIndexDefs;
  1465. begin
  1466. Result:=TJSONIndexDefs.Create(Self,Self,TJSONIndexDef);
  1467. end;
  1468. procedure TBaseJSONDataSet.BuildIndexes;
  1469. Var
  1470. I : integer;
  1471. begin
  1472. For I:=0 to FIndexes.Count-1 do
  1473. FIndexes[i].BuildIndex(Self);
  1474. end;
  1475. function TBaseJSONDataSet.RecordComparerClass : TRecordComparerClass;
  1476. begin
  1477. Result:=TRecordComparer;
  1478. end;
  1479. function TBaseJSONDataSet.LocateRecordIndex(const KeyFields: string; const KeyValues: JSValue; Options: TLocateOptions): Integer;
  1480. Var
  1481. Comp : TRecordComparer;
  1482. RI,I : Integer;
  1483. begin
  1484. Result:=-1;
  1485. Comp:=RecordComparerClass.Create(Self,KeyFields,KeyValues,Options);
  1486. try
  1487. I:=FCurrent;
  1488. RI:=FCurrentIndex.GetRecordIndex(I);
  1489. While (Result=-1) and (RI<>-1) do
  1490. begin
  1491. if Comp.Compare(RI)=0 then
  1492. Result:=RI;
  1493. inc(I);
  1494. RI:=FCurrentIndex.GetRecordIndex(I);
  1495. end;
  1496. finally
  1497. Comp.Free;
  1498. end;
  1499. end;
  1500. function TBaseJSONDataSet.Locate(const KeyFields: string; const KeyValues: JSValue; Options: TLocateOptions): boolean;
  1501. Var
  1502. I : Integer;
  1503. BM : TBookMark;
  1504. begin
  1505. Result:=Inherited;
  1506. I:=LocateRecordIndex(KeyFields,KeyValues,Options);
  1507. Result:=I<>-1;
  1508. if Result then
  1509. begin
  1510. // Construct bookmark.
  1511. // Bookmark is always the index in the FRows array.
  1512. BM.Data:=I;
  1513. BM.Flag:=bfCurrent;
  1514. GotoBookMark(BM);
  1515. end;
  1516. end;
  1517. function TBaseJSONDataSet.Lookup(const KeyFields: string; const KeyValues: JSValue; const ResultFields: string): JSValue;
  1518. Var
  1519. RI,I : Integer;
  1520. l : TFPList;
  1521. Vals : TJSValueDynArray;
  1522. begin
  1523. Result:=Null;
  1524. l:=TFPList.Create;
  1525. try
  1526. GetFieldList(L,ResultFields);
  1527. Result:=inherited Lookup(KeyFields, KeyValues, ResultFields);
  1528. RI:=LocateRecordIndex(KeyFields,KeyValues,[]);
  1529. Result:=RI<>-1;
  1530. if Result then
  1531. begin
  1532. SetLength(Vals,L.Count);
  1533. For I:=0 to L.Count-1 do
  1534. Vals[i]:=FFieldMapper.GetJSONDataForField(TField(L[I]),FRows[RI]);
  1535. if L.Count=1 then
  1536. Result:=Vals[i]
  1537. else
  1538. Result:=Vals;
  1539. end;
  1540. finally
  1541. L.Free;
  1542. end;
  1543. end;
  1544. end.