fpdbmustache.pp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. {
  2. This file is part of the Free Pascal Run time library.
  3. Copyright (c) 2021 by Michael Van Canneyt ([email protected])
  4. This file contains a Mustache DB context, getting data from a dataset
  5. See the File COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit fpdbmustache;
  12. {$mode ObjFPC}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, db, fpMustache;
  16. Type
  17. { TDatasetCollectionItem }
  18. TDatasetCollectionItem = Class(TCollectionItem)
  19. private
  20. FDataset: TDataSet;
  21. FSection: String;
  22. Public
  23. Property Dataset : TDataSet Read FDataset Write FDataset;
  24. Property SectionName : String Read FSection Write FSection;
  25. end;
  26. TDatasetCollection = Class(TCollection)
  27. private
  28. function GetDS(aIndex : Integer): TDatasetCollectionItem;
  29. Public
  30. Function IndexOfDataset(aDataset : TDataset) : Integer;
  31. Function IndexOfSection(aSection : String) : Integer;
  32. Property Datasets[aIndex : Integer] : TDatasetCollectionItem Read GetDS; default;
  33. end;
  34. { TMustacheDBContext }
  35. TMustacheDBContext = Class(TMustacheContext)
  36. Private
  37. Type
  38. TPair = Record
  39. atStart : Boolean;
  40. Value : TDataset;
  41. end;
  42. Private
  43. FStack : Array of TPair;
  44. FCount : Integer;
  45. FStaticValues: TStrings;
  46. FDatasets : TDatasetCollection;
  47. Function FindField(Const aName : TMustacheString) : TField;
  48. function GetDataset(aIndex : Integer): TDatasetCollectionItem;
  49. function GetDatasetCount: INteger;
  50. procedure SetStaticValues(AValue: TStrings);
  51. Public
  52. Constructor Create(aCallback : TGetTextValueEvent); override;
  53. Destructor destroy; override;
  54. Procedure Clear;
  55. Function MoveNextSectionItem(Const aName : TMustacheString) : Boolean; override;
  56. Function PushSection(Const aName : TMustacheString) : TMustacheSectionType; override;
  57. Procedure PopSection(Const aName : TMustacheString); override;
  58. Function GetTextValue(Const aName : TMustacheString) : TMustacheString; override;
  59. Procedure AddDataset(aDataset : TDataset; aSectionName : String = '');
  60. Procedure RemoveDataset(aDataset : TDataset);
  61. Property StaticValues : TStrings Read FStaticValues Write SetStaticValues;
  62. Property Datasets[aIndex : Integer] : TDatasetCollectionItem Read GetDataset;
  63. Property DatasetCount : INteger Read GetDatasetCount;
  64. end;
  65. implementation
  66. uses StrUtils;
  67. Resourcestring
  68. SErrPopSectionNoPush = 'PopSection %s without push';
  69. SErrDatasetNameEmpty = 'Dataset name and section cannot both be empty';
  70. SErrDatasetEmpty = 'Dataset is Nil';
  71. SErrDuplicateDataSetName = 'Duplicate dataset name: %s';
  72. { TMustacheDBContext }
  73. function TMustacheDBContext.FindField(const aName: TMustacheString): TField;
  74. Var
  75. aCount : Integer;
  76. begin
  77. Result:=Nil;
  78. aCount:=FCount-1;
  79. While (Result=Nil) and (aCount>=0) do
  80. begin
  81. Result:=FStack[aCount].Value.FieldByName(aName);
  82. Dec(aCount);
  83. end;
  84. end;
  85. function TMustacheDBContext.GetDataset(aIndex : Integer
  86. ): TDatasetCollectionItem;
  87. begin
  88. Result:=FDatasets[aIndex];
  89. end;
  90. function TMustacheDBContext.GetDatasetCount: INteger;
  91. begin
  92. Result:=FDatasets.Count;
  93. end;
  94. procedure TMustacheDBContext.SetStaticValues(AValue: TStrings);
  95. begin
  96. if FStaticValues=AValue then Exit;
  97. FStaticValues.Assign(AValue);
  98. end;
  99. constructor TMustacheDBContext.Create(aCallback: TGetTextValueEvent);
  100. begin
  101. inherited Create(aCallback);
  102. FDatasets:=TDatasetCollection.Create(TDatasetCollectionItem);
  103. FStaticValues:=TStringList.Create;
  104. SetLength(FStack,JSONListGrowCount);
  105. FCount:=0;
  106. end;
  107. destructor TMustacheDBContext.destroy;
  108. begin
  109. FreeAndNil(FStaticValues);
  110. FreeAndNil(FDatasets);
  111. inherited destroy;
  112. end;
  113. procedure TMustacheDBContext.Clear;
  114. begin
  115. FStaticValues.Clear;
  116. FDatasets.Clear;
  117. end;
  118. function TMustacheDBContext.MoveNextSectionItem(const aName: TMustacheString
  119. ): Boolean;
  120. begin
  121. if FStack[FCount-1].atStart then
  122. FStack[FCount-1].atStart:=False
  123. else
  124. FStack[FCount-1].Value.Next;
  125. Result:=Not FStack[FCount-1].Value.EOF;
  126. end;
  127. function TMustacheDBContext.PushSection(const aName: TMustacheString
  128. ): TMustacheSectionType;
  129. Var
  130. aDS : TDataset;
  131. Idx : Integer;
  132. begin
  133. Result:=mstNone;
  134. Idx:=FDatasets.IndexOfSection(aName);
  135. if Idx=-1 then
  136. Exit;
  137. aDS:=FDatasets[Idx].Dataset;
  138. if aDS.IsEmpty then
  139. exit;
  140. if FCount=Length(FStack) then
  141. SetLength(FStack,FCount+JSONListGrowCount);
  142. FStack[FCount].Value:=aDS;
  143. FStack[FCount].atStart:=True;
  144. Inc(FCount,1);
  145. Result:=mstList;
  146. end;
  147. procedure TMustacheDBContext.PopSection(const aName: TMustacheString);
  148. begin
  149. if FCount<1 then
  150. Raise EMustache.CreateFmt(SErrPopSectionNoPush,[aName]);
  151. Dec(FCount,1);
  152. end;
  153. function TMustacheDBContext.GetTextValue(const aName: TMustacheString
  154. ): TMustacheString;
  155. Var
  156. F : TField;
  157. idx : Integer;
  158. begin
  159. F:=Nil;
  160. if Pos('.',aName)=0 then
  161. F:=FindField(aName)
  162. else if WordCount(aName,['.'])=2 then
  163. begin
  164. Idx:=FDatasets.IndexOfSection(ExtractWord(1,aName,['.']));
  165. if (Idx<>-1) then
  166. F:=FDatasets[Idx].Dataset.FindField(ExtractWord(2,aName,['.']));
  167. end;
  168. If Assigned(F) then
  169. Result:=F.AsString
  170. else
  171. begin
  172. Idx:=FStaticValues.IndexOfName(aName);
  173. if Idx<>-1 then
  174. Result:=FStaticValues.ValueFromIndex[Idx]
  175. else
  176. Result:=Inherited GetTextValue(aName);
  177. end;
  178. end;
  179. procedure TMustacheDBContext.AddDataset(aDataset: TDataset; aSectionName: String);
  180. Var
  181. DCI : TDatasetCollectionItem;
  182. aName : String;
  183. begin
  184. aName:=aSectionName;
  185. if aName='' then
  186. aName:=aDataset.Name;
  187. if aName='' then
  188. raise EMustache.Create(SErrDatasetNameEmpty);
  189. if aDataset=Nil then
  190. raise EMustache.Create(SErrDatasetEmpty);
  191. if FDatasets.IndexOfSection(aName)<>-1 then
  192. raise EMustache.CreateFmt(SErrDuplicateDataSetName, [aName]);
  193. DCI:=FDatasets.Add as TDatasetCollectionItem;
  194. DCI.Dataset:=aDataset;
  195. DCI.SectionName:=aName;
  196. end;
  197. procedure TMustacheDBContext.RemoveDataset(aDataset: TDataset);
  198. Var
  199. Idx : Integer;
  200. begin
  201. Idx:=FDatasets.IndexOfDataset(aDataset);
  202. if Idx<>-1 then
  203. FDatasets.Delete(Idx);
  204. end;
  205. { TDatasetCollection }
  206. function TDatasetCollection.GetDS(aIndex : Integer): TDatasetCollectionItem;
  207. begin
  208. Result:=Items[aIndex] as TDatasetCollectionItem;
  209. end;
  210. function TDatasetCollection.IndexOfDataset(aDataset: TDataset): Integer;
  211. begin
  212. Result:=Count-1;
  213. While (Result>=0) and (GetDS(Result).Dataset<>ADataset) do
  214. Dec(Result);
  215. end;
  216. function TDatasetCollection.IndexOfSection(aSection: String): Integer;
  217. begin
  218. Result:=Count-1;
  219. While (Result>=0) and not SameText(GetDS(Result).SectionName,ASection) do
  220. Dec(Result);
  221. end;
  222. end.