ljgridutils.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. (*
  2. LCLJSONGrid Utils plugin.
  3. Copyright (C) 2012-2014 Silvio Clecio.
  4. Please see the LICENSE file.
  5. *)
  6. unit LJGridUtils;
  7. {$mode objfpc}{$H+}
  8. interface
  9. uses
  10. Grids, FPJSON, JSONParser, Classes, SysUtils;
  11. { Load JSON data to grid. }
  12. procedure LoadJSON(AGrid: TCustomStringGrid; AJSON: TJSONData;
  13. const AShowErrorMsg: Boolean = False; const AAutoSizeColumns: Boolean = True;
  14. const AAutoClean: Boolean = True);
  15. { Load JSON stream to grid. }
  16. procedure LoadJSON(AGrid: TCustomStringGrid; AStream: TStream;
  17. const AShowErrorMsg: Boolean = False; const AAutoSizeColumns: Boolean = True;
  18. const AAutoClean: Boolean = True);
  19. { Load JSON file to grid. }
  20. procedure LoadJSON(AGrid: TCustomStringGrid; const AFileName: TFileName;
  21. const AShowErrorMsg: Boolean = False; const AAutoSizeColumns: Boolean = True;
  22. const AAutoClean: Boolean = True);
  23. { Save grid to JSON array. }
  24. procedure SaveJSON(AGrid: TCustomStringGrid; out AJSON: TJSONArray;
  25. const ASaveAllAsString: Boolean = False);
  26. { Save grid to JSON stream. }
  27. procedure SaveJSON(AGrid: TCustomStringGrid; AStream: TStream;
  28. const ASaveAllAsString: Boolean = False);
  29. { Save grid to JSON file. }
  30. procedure SaveJSON(AGrid: TCustomStringGrid; const AFileName: TFileName;
  31. const ASaveAllAsString: Boolean = False);
  32. { Find item in a StringGrid. }
  33. function FindItem(AGrid: TCustomStringGrid; const AText: string;
  34. const ACaseSensitive: Boolean = False;
  35. const AFindNext: Boolean = True): Boolean;
  36. { Clear grid. }
  37. procedure ClearGrid(AGrid: TCustomStringGrid;
  38. const AIndicatorWidth: Integer = 12);
  39. { Get selected row as JSONObject. }
  40. procedure GetSelectedRow(AGrid: TCustomStringGrid; ARow: TJSONObject);
  41. function GetSelectedRow(AGrid: TCustomStringGrid): TJSONObject;
  42. { Get selected rows as JSONArray. }
  43. procedure GetSelectedRows(AGrid: TCustomStringGrid; ARows: TJSONArray);
  44. function GetSelectedRows(AGrid: TCustomStringGrid): TJSONArray;
  45. var
  46. JSON_UNKNOWN_STR: ShortString = '[UNKNOWN]';
  47. JSON_NULL_STR: ShortString = '[NULL]';
  48. JSON_ARRAY_STR: ShortString = '[ARRAY]';
  49. JSON_OBJECT_STR: ShortString = '[OBJECT]';
  50. JSON_BOOL_FALSE_STR: ShortString = 'FALSE';
  51. JSON_BOOL_TRUE_STR: ShortString = 'TRUE';
  52. JSON_FORMAT_FLOAT_STR: ShortString = '%f';
  53. JSON_FORMAT_INT_STR: ShortString = '%d';
  54. implementation
  55. var
  56. _SelectedRow: TJSONObject = nil;
  57. _SelectedRows: TJSONArray = nil;
  58. procedure LoadJSON(AGrid: TCustomStringGrid; AJSON: TJSONData;
  59. const AShowErrorMsg: Boolean; const AAutoSizeColumns: Boolean;
  60. const AAutoClean: Boolean);
  61. var
  62. VIsObject: Boolean;
  63. VJSONCols: TJSONObject;
  64. VRecord: TJSONData = nil;
  65. I, J, VFixedCols, VFixedRows: Integer;
  66. begin
  67. if not Assigned(AJSON) then
  68. begin
  69. if AShowErrorMsg then
  70. raise Exception.Create('JSON ERROR: Empty database.')
  71. else
  72. Exit;
  73. end;
  74. if AJSON.JSONType <> jtArray then
  75. begin
  76. if AShowErrorMsg then
  77. raise Exception.CreateFmt(
  78. 'JSON ERROR: Got "%s", expected "TJSONArray".', [AJSON.ClassName])
  79. else
  80. Exit;
  81. end;
  82. if AJSON.Count < 1 then
  83. begin
  84. if AAutoClean then
  85. AGrid.Clean(0, AGrid.FixedRows, AGrid.ColCount - 1, AGrid.RowCount - 1,
  86. [gzNormal, gzFixedCols, gzFixedRows, gzFixedCells]);
  87. if AShowErrorMsg then
  88. raise Exception.Create('JSON ERROR: Empty array.')
  89. else
  90. Exit;
  91. end;
  92. VJSONCols := TJSONObject(AJSON.Items[0]);
  93. VIsObject := VJSONCols.JSONType = jtObject;
  94. if VIsObject and (VJSONCols.Count < 1) then
  95. begin
  96. if AShowErrorMsg then
  97. raise Exception.Create('JSON ERROR: Empty object.')
  98. else
  99. Exit;
  100. end;
  101. VFixedCols := AGrid.FixedCols;
  102. VFixedRows := AGrid.FixedRows;
  103. AGrid.BeginUpdate;
  104. try
  105. if not AGrid.Columns.Enabled then
  106. AGrid.ColCount := VFixedCols + VJSONCols.Count;
  107. AGrid.RowCount := VFixedRows + AJSON.Count;
  108. for I := 0 to Pred(AJSON.Count) do
  109. begin
  110. VJSONCols := TJSONObject(AJSON.Items[I]);
  111. for J := 0 to Pred(VJSONCols.Count) do
  112. begin
  113. if Pred(AGrid.ColCount - AGrid.FixedCols) < J then
  114. Continue;
  115. if (I = 0) and VIsObject then
  116. AGrid.Cols[VFixedCols + J].Text := VJSONCols.Names[J];
  117. VRecord := VJSONCols.Items[J];
  118. case VRecord.JSONType of
  119. jtUnknown: AGrid.Cells[J + VFixedCols, I + VFixedRows] := JSON_UNKNOWN_STR;
  120. jtNumber:
  121. begin
  122. if VRecord is TJSONFloatNumber then
  123. AGrid.Cells[J + VFixedCols, I + VFixedRows] :=
  124. Format(JSON_FORMAT_FLOAT_STR, [VRecord.AsFloat])
  125. else
  126. AGrid.Cells[J + VFixedCols, I + VFixedRows] :=
  127. Format(JSON_FORMAT_INT_STR, [VRecord.AsInt64]);
  128. end;
  129. jtString: AGrid.Cells[J + VFixedCols, I + VFixedRows] :=
  130. VRecord.AsString;
  131. jtBoolean: AGrid.Cells[J + VFixedCols, I + VFixedRows] :=
  132. BoolToStr(VRecord.AsBoolean, JSON_BOOL_TRUE_STR, JSON_BOOL_FALSE_STR);
  133. jtNull: AGrid.Cells[J + VFixedCols, I + VFixedRows] := JSON_NULL_STR;
  134. jtArray: AGrid.Cells[J + VFixedCols, I + VFixedRows] := JSON_ARRAY_STR;
  135. jtObject: AGrid.Cells[J + VFixedCols, I + VFixedRows] := JSON_OBJECT_STR;
  136. end;
  137. end;
  138. end;
  139. if AAutoSizeColumns then
  140. for I := 1 to Pred(AGrid.ColCount) do
  141. AGrid.AutoSizeColumn(I);
  142. finally
  143. AGrid.EndUpdate;
  144. end;
  145. end;
  146. procedure LoadJSON(AGrid: TCustomStringGrid; AStream: TStream;
  147. const AShowErrorMsg: Boolean; const AAutoSizeColumns: Boolean;
  148. const AAutoClean: Boolean);
  149. var
  150. VJSON: TJSONData;
  151. VParser: TJSONParser;
  152. begin
  153. VParser := TJSONParser.Create(AStream);
  154. try
  155. VJSON := VParser.Parse;
  156. LoadJSON(AGrid, VJSON, AShowErrorMsg, AAutoSizeColumns, AAutoClean);
  157. finally
  158. VJSON.Free;
  159. VParser.Free;
  160. end;
  161. end;
  162. procedure LoadJSON(AGrid: TCustomStringGrid; const AFileName: TFileName;
  163. const AShowErrorMsg: Boolean; const AAutoSizeColumns: Boolean;
  164. const AAutoClean: Boolean);
  165. var
  166. VFile: TFileStream;
  167. begin
  168. VFile := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
  169. try
  170. LoadJSON(AGrid, VFile, AShowErrorMsg, AAutoSizeColumns, AAutoClean);
  171. finally
  172. VFile.Free;
  173. end;
  174. end;
  175. procedure SaveJSON(AGrid: TCustomStringGrid; out AJSON: TJSONArray;
  176. const ASaveAllAsString: Boolean);
  177. var
  178. VIsObject: Boolean;
  179. VInt64Value: Int64;
  180. VFloatValue: TJSONFloat;
  181. I, J, VRowCount: Integer;
  182. VJSONArrayData: TJSONArray;
  183. VObjectName, VCellValue: string;
  184. VJSONObjectData: TJSONObject = nil;
  185. begin
  186. VRowCount := AGrid.RowCount;
  187. VIsObject := (VRowCount > 0) and (Trim(AGrid.Rows[0].Text) <> '');
  188. AJSON := TJSONArray.Create;
  189. for J := AGrid.FixedRows to Pred(AGrid.RowCount) do
  190. begin
  191. if VIsObject then
  192. VJSONObjectData := TJSONObject.Create
  193. else
  194. VJSONArrayData := TJSONArray.Create;
  195. for I := AGrid.FixedCols to Pred(AGrid.ColCount) do
  196. begin
  197. VObjectName := AGrid.Cells[I, 0];
  198. VCellValue := AGrid.Cells[I, J];
  199. if ASaveAllAsString then
  200. begin
  201. if VIsObject then
  202. VJSONObjectData.Add(VObjectName, VCellValue)
  203. else
  204. VJSONArrayData.Add(VCellValue);
  205. end
  206. else
  207. begin
  208. if SameText(VCellValue, JSON_UNKNOWN_STR) or
  209. SameText(VCellValue, JSON_NULL_STR) then
  210. begin
  211. if VIsObject then
  212. VJSONObjectData.Add(VObjectName, TJSONNull.Create)
  213. else
  214. VJSONArrayData.Add(TJSONNull.Create);
  215. end
  216. else
  217. if SameText(VCellValue, JSON_ARRAY_STR) then
  218. begin
  219. if VIsObject then
  220. VJSONObjectData.Add(VObjectName, TJSONArray.Create)
  221. else
  222. VJSONArrayData.Add(TJSONArray.Create);
  223. end
  224. else
  225. if SameText(VCellValue, JSON_OBJECT_STR) then
  226. begin
  227. if VIsObject then
  228. VJSONObjectData.Add(VObjectName, TJSONObject.Create)
  229. else
  230. VJSONArrayData.Add(TJSONObject.Create);
  231. end
  232. else
  233. if SameText(VCellValue, JSON_BOOL_FALSE_STR) then
  234. begin
  235. if VIsObject then
  236. VJSONObjectData.Add(VObjectName, False)
  237. else
  238. VJSONArrayData.Add(False);
  239. end
  240. else
  241. if SameText(VCellValue, JSON_BOOL_TRUE_STR) then
  242. begin
  243. if VIsObject then
  244. VJSONObjectData.Add(VObjectName, True)
  245. else
  246. VJSONArrayData.Add(True);
  247. end
  248. else
  249. if TryStrToInt64(VCellValue, VInt64Value) then
  250. begin
  251. if VIsObject then
  252. VJSONObjectData.Add(VObjectName, VInt64Value)
  253. else
  254. VJSONArrayData.Add(VInt64Value);
  255. end
  256. else
  257. if TryStrToFloat(VCellValue, VFloatValue) then
  258. begin
  259. if VIsObject then
  260. VJSONObjectData.Add(VObjectName, VFloatValue)
  261. else
  262. VJSONArrayData.Add(VFloatValue);
  263. end
  264. else
  265. begin
  266. if VIsObject then
  267. VJSONObjectData.Add(VObjectName, VCellValue)
  268. else
  269. VJSONArrayData.Add(VCellValue);
  270. end;
  271. end;
  272. end;
  273. if VIsObject then
  274. AJSON.Add(VJSONObjectData)
  275. else
  276. AJSON.Add(VJSONArrayData);
  277. end;
  278. end;
  279. procedure SaveJSON(AGrid: TCustomStringGrid; AStream: TStream;
  280. const ASaveAllAsString: Boolean);
  281. var
  282. L: Integer;
  283. VJSON: string;
  284. VJSONArray: TJSONArray;
  285. begin
  286. SaveJSON(AGrid, VJSONArray, ASaveAllAsString);
  287. try
  288. VJSON := VJSONArray.AsJSON;
  289. L := Length(VJSON);
  290. if L > 0 then
  291. AStream.Write(Pointer(VJSON)^, L);
  292. finally
  293. VJSONArray.Free;
  294. end;
  295. end;
  296. procedure SaveJSON(AGrid: TCustomStringGrid; const AFileName: TFileName;
  297. const ASaveAllAsString: Boolean);
  298. var
  299. VFile: TFileStream;
  300. begin
  301. VFile := TFileStream.Create(AFileName, fmCreate);
  302. try
  303. SaveJSON(AGrid, VFile, ASaveAllAsString);
  304. finally
  305. VFile.Free;
  306. end;
  307. end;
  308. function FindItem(AGrid: TCustomStringGrid; const AText: string;
  309. const ACaseSensitive: Boolean; const AFindNext: Boolean): Boolean;
  310. var
  311. VGridRect: TGridRect;
  312. VTargetText, VCellText: string;
  313. I, X, Y, VCurX, VCurY, VGridWidth, VGridHeight: Integer;
  314. begin
  315. Result := False;
  316. if AFindNext then
  317. begin
  318. VCurY := AGrid.Selection.Top;
  319. VCurX := AGrid.Selection.Left + 1;
  320. end
  321. else
  322. begin
  323. VCurY := 0;
  324. VCurX := 0;
  325. VGridRect.Left := 0;
  326. VGridRect.Right := 0;
  327. VGridRect.Top := 0;
  328. VGridRect.Bottom := 0;
  329. AGrid.Selection := VGridRect;
  330. end;
  331. VGridWidth := AGrid.ColCount;
  332. VGridHeight := AGrid.RowCount;
  333. Y := VCurY;
  334. X := VCurX;
  335. if ACaseSensitive then
  336. VTargetText := AText
  337. else
  338. VTargetText := AnsiLowerCase(AText);
  339. while Y < VGridHeight do
  340. begin
  341. while X < VGridWidth do
  342. begin
  343. if ACaseSensitive then
  344. VCellText := AGrid.Cells[X, Y]
  345. else
  346. VCellText := AnsiLowerCase(AGrid.Cells[X, Y]);
  347. I := Pos(VTargetText, VCellText);
  348. if I > 0 then
  349. begin
  350. VGridRect.Left := X;
  351. VGridRect.Right := X;
  352. VGridRect.Top := Y;
  353. VGridRect.Bottom := Y;
  354. AGrid.Selection := VGridRect;
  355. Result := True;
  356. Exit;
  357. end;
  358. Inc(X);
  359. end;
  360. Inc(Y);
  361. X := AGrid.FixedCols;
  362. end;
  363. if AFindNext {and not Result} then
  364. begin
  365. VGridRect.Left := 0;
  366. VGridRect.Right := 0;
  367. VGridRect.Top := 0;
  368. VGridRect.Bottom := 0;
  369. AGrid.Selection := VGridRect;
  370. end;
  371. end;
  372. procedure ClearGrid(AGrid: TCustomStringGrid; const AIndicatorWidth: Integer);
  373. var
  374. I: Integer;
  375. begin
  376. with AGrid do
  377. try
  378. BeginUpdate;
  379. if not Columns.Enabled then
  380. begin
  381. ColCount := 1 + FixedCols;
  382. ColWidths[0] := AIndicatorWidth;
  383. for I := 1 to Pred(ColCount) do
  384. ColWidths[I] := DefaultColWidth;
  385. end;
  386. RowCount := 1 + FixedRows;
  387. Clean;
  388. finally
  389. EndUpdate;
  390. end;
  391. end;
  392. procedure GetSelectedRow(AGrid: TCustomStringGrid; ARow: TJSONObject);
  393. var
  394. I: Integer;
  395. begin
  396. ARow.Clear;
  397. for I := AGrid.FixedCols to Pred(AGrid.ColCount) do
  398. ARow.Add(AGrid.Cols[I][0], AGrid.Rows[AGrid.Row][I]);
  399. if (ARow.Count > 0) and (ARow.Names[0] = '') and
  400. (ARow.Items[0].AsString = '') then
  401. ARow.Clear;
  402. end;
  403. function GetSelectedRow(AGrid: TCustomStringGrid): TJSONObject;
  404. begin
  405. if not Assigned(_SelectedRow) then
  406. _SelectedRow := TJSONObject.Create;
  407. Result := _SelectedRow;
  408. GetSelectedRow(AGrid, Result);
  409. end;
  410. procedure GetSelectedRows(AGrid: TCustomStringGrid; ARows: TJSONArray);
  411. var
  412. I, J: Integer;
  413. VItem: TJSONObject;
  414. begin
  415. ARows.Clear;
  416. for I := AGrid.Selection.Top to AGrid.Selection.Bottom do
  417. begin
  418. VItem := TJSONObject.Create;
  419. for J := AGrid.FixedCols to Pred(AGrid.ColCount) do
  420. VItem.Add(AGrid.Cols[J][0], AGrid.Rows[I][J]);
  421. ARows.Add(VItem);
  422. end;
  423. if Assigned(VItem) and (VItem.Count > 0) and
  424. (VItem.Names[0] = '') and (VItem.Items[0].AsString = '') then
  425. ARows.Clear;
  426. end;
  427. function GetSelectedRows(AGrid: TCustomStringGrid): TJSONArray;
  428. begin
  429. if not Assigned(_SelectedRows) then
  430. _SelectedRows := TJSONArray.Create;
  431. Result := _SelectedRows;
  432. GetSelectedRows(AGrid, Result);
  433. end;
  434. finalization
  435. FreeAndNil(_SelectedRow);
  436. FreeAndNil(_SelectedRows);
  437. end.