jsondataset.pas 49 KB

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