jsondataset.pas 49 KB

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