jsondataset.pas 50 KB

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