fptemplate.pp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  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. {$mode objfpc}
  12. {$H+}
  13. {$define NOCONTNRS}
  14. unit fpTemplate;
  15. interface
  16. uses
  17. SysUtils,
  18. Classes;
  19. Const
  20. DefaultParseDepth = 100;
  21. MaxDelimLength = 5;
  22. Type
  23. TParseDelimiter = String[5];
  24. Var
  25. DefaultStartDelimiter : TParseDelimiter = '{';
  26. DefaultEndDelimiter : TParseDelimiter = '}';
  27. Type
  28. TGetParamEvent = Procedure(Sender : TObject; Const ParamName : String; Out AValue : String) Of Object;
  29. { TTemplateParser }
  30. TTemplateParser = Class(TObject)
  31. Private
  32. FParseLevel : Integer;
  33. FMaxParseDepth : Integer;
  34. FEndDelimiter: TParseDelimiter;
  35. FStartDelimiter: TParseDelimiter;
  36. FRecursive: Boolean;
  37. FValues : TStringList;
  38. FOnGetParam: TGetParamEvent;
  39. function GetDelimiter(Index: integer): TParseDelimiter;
  40. function GetValue(Key : String): String;
  41. procedure SetDelimiter(Index: integer; const AValue: TParseDelimiter);
  42. procedure SetValue(Key : String; const AValue: String);
  43. Public
  44. Constructor Create;
  45. Destructor Destroy; override;
  46. Procedure Clear;
  47. Function GetParam(Const Key : String; Out AValue : String) : Boolean;
  48. Function ParseString(Src : String) : String;
  49. Function ParseStream(Src : TStream; Dest : TStream) : Integer; // Wrapper, Returns number of bytes written.
  50. Procedure ParseStrings(Src : TStrings; Dest : TStrings) ; // Wrapper
  51. Property OnGetParam : TGetParamEvent Read FOnGetParam Write FOnGetParam; // Called if not found in values
  52. Property StartDelimiter : TParseDelimiter Index 1 Read GetDelimiter Write SetDelimiter; // Start char/string, default '}'
  53. Property EndDelimiter : TParseDelimiter Index 2 Read GetDelimiter Write SetDelimiter; // end char/string, default '}'
  54. Property Values[Key : String] : String Read GetValue Write SetValue; // Contains static values.
  55. Property Recursive : Boolean Read FRecursive Write FRecursive;
  56. end;
  57. { TFPCustomTemplate }
  58. TFPCustomTemplate = Class(TPersistent)
  59. private
  60. FEndDelimiter: TParseDelimiter;
  61. FStartDelimiter: TParseDelimiter;
  62. FFileName: String;
  63. FTemplate: String;
  64. FOnGetParam: TGetParamEvent;
  65. Protected
  66. Procedure GetParam(Sender : TObject; Const ParamName : String; Out AValue : String);virtual;
  67. Function CreateParser : TTemplateParser; virtual;
  68. Public
  69. Function HasContent : Boolean;
  70. Function GetContent : String;
  71. Procedure Assign(Source : TPersistent); override;
  72. Property StartDelimiter : TParseDelimiter Read FStartDelimiter Write FStartDelimiter;
  73. Property EndDelimiter : TParseDelimiter Read FEndDelimiter Write FEndDelimiter;
  74. Property FileName : String Read FFileName Write FFileName;
  75. Property Template : String Read FTemplate Write FTemplate;
  76. Property OnGetParam : TGetParamEvent Read FOnGetParam Write FOnGetParam;
  77. end;
  78. TFPTemplate = Class(TFPCustomTemplate)
  79. Published
  80. Property FileName;
  81. Property Template;
  82. end;
  83. ETemplateParser = Class(Exception);
  84. Var
  85. MaxParseDepth : Integer = DefaultParseDepth;
  86. implementation
  87. Resourcestring
  88. SErrParseDepthExceeded = 'Maximum parse level (%d) exceeded.';
  89. SErrNoEmptyDelimiters = 'Delimiters cannot be empty';
  90. { TTemplateParser }
  91. Type
  92. { TStringItem }
  93. TStringItem = Class(TObject)
  94. Private
  95. FValue : String;
  96. Public
  97. Constructor Create(AValue : String);
  98. Property Value : String Read FValue Write FValue;
  99. end;
  100. { TStringItem }
  101. constructor TStringItem.Create(AValue: String);
  102. begin
  103. FValue:=AValue;
  104. end;
  105. function TTemplateParser.GetValue(Key : String): String;
  106. Var
  107. I : Integer;
  108. begin
  109. Result:='';
  110. If Assigned(FValues) then
  111. begin
  112. I:=FValues.IndexOf(Key);
  113. If (I<>-1) then
  114. Result:=TStringItem(FValues.Objects[i]).Value;
  115. end;
  116. end;
  117. function TTemplateParser.GetDelimiter(Index: integer): TParseDelimiter;
  118. begin
  119. If Index=1 then
  120. Result:=FStartDelimiter
  121. else
  122. Result:=FEndDelimiter;
  123. end;
  124. procedure TTemplateParser.SetDelimiter(Index: integer;
  125. const AValue: TParseDelimiter);
  126. begin
  127. If Length(AValue)=0 then
  128. Raise ETemplateParser.Create(SErrNoEmptyDelimiters);
  129. If Index=1 then
  130. FStartDelimiter:=AValue
  131. else
  132. FEndDelimiter:=AValue;
  133. end;
  134. procedure TTemplateParser.SetValue(Key : String; const AValue: String);
  135. Var
  136. I : Integer;
  137. SI : TStringItem;
  138. begin
  139. If (AValue='') then
  140. begin
  141. If Assigned(FValues) then
  142. begin
  143. I:=FValues.IndexOf(Key);
  144. If (I<>-1) then
  145. begin
  146. FValues.Objects[i].Free;
  147. FValues.Delete(I);
  148. end;
  149. end;
  150. end
  151. else
  152. begin
  153. if Not Assigned(FValues) then
  154. begin
  155. FVAlues:=TStringList.Create;
  156. FValues.Sorted:=True;
  157. end;
  158. I:=FValues.IndexOf(Key);
  159. If (I=-1) then
  160. FValues.AddObject(Key,TStringItem.Create(AValue))
  161. else
  162. TStringItem(FValues.Objects[I]).Value:=AValue;
  163. end;
  164. end;
  165. constructor TTemplateParser.Create;
  166. begin
  167. FMaxParseDepth:=MaxParseDepth;
  168. FStartDelimiter:=DefaultStartDelimiter;
  169. FEndDelimiter:=DefaultEndDelimiter;
  170. end;
  171. destructor TTemplateParser.Destroy;
  172. begin
  173. Clear;
  174. inherited Destroy;
  175. end;
  176. procedure TTemplateParser.Clear;
  177. Var
  178. I : Integer;
  179. begin
  180. If Assigned(FValues) then
  181. For I:=0 to FValues.Count-1 do
  182. FValues.Objects[i].Free;
  183. FreeAndNil(FValues);
  184. end;
  185. function TTemplateParser.GetParam(const Key: String; out AValue: String): Boolean;
  186. Var
  187. I : Integer;
  188. begin
  189. If Assigned(FValues) then
  190. I:=FValues.IndexOf(Key)
  191. else
  192. I:=-1;
  193. Result:=(I<>-1);
  194. If Result then
  195. AValue:=TStringItem(FValues.Objects[i]).Value
  196. else
  197. begin
  198. Result:=Assigned(FOnGetParam);
  199. If Result then
  200. FOnGetParam(Self,Key,AValue);
  201. end;
  202. If Result and Recursive then
  203. AValue:=ParseString(AValue);
  204. end;
  205. Function FindDelimiter(SP : PChar; D : TParseDelimiter; MaxLen : Integer) : PChar; Inline;
  206. Var
  207. P,P2 : PChar;
  208. I,DLen : Integer;
  209. begin
  210. Result:=Nil;
  211. DLen:=Length(D);
  212. Dec(MaxLen,(DLen-1));
  213. If MaxLen<=0 then
  214. exit;
  215. P:=SP;
  216. While (Result=Nil) and (P-SP<=MaxLen) do
  217. begin
  218. While (P-SP<=MaxLen) and (P^<>D[1]) do
  219. Inc(P);
  220. If ((P-SP)<=MaxLen) then
  221. begin
  222. Result:=P;
  223. P2:=P+1;
  224. // Check Other characters
  225. I:=2;
  226. While (I<=DLen) and (Result<>Nil) do
  227. If (P2^=D[i]) then
  228. begin
  229. inc(i);
  230. Inc(p2);
  231. end
  232. else
  233. begin
  234. P:=Result;
  235. Result:=Nil;
  236. end;
  237. // Either result<>Nil -> match or result=nil -> no match
  238. inc(P);
  239. end;
  240. end;
  241. end;
  242. Procedure AddToString(Var S : String; P : PChar; NChars : Integer);inline;
  243. Var
  244. SLen : Integer;
  245. begin
  246. SLen:=Length(S);
  247. SetLength(S,SLen+NChars);
  248. Move(P^,S[Slen+1],NChars);
  249. end;
  250. function TTemplateParser.ParseString(Src: String): String;
  251. Var
  252. PN,PV : String;
  253. i,RLen,SLen,STlen : Integer;
  254. TS,TE,SP,P : PChar;
  255. begin
  256. Inc(FParseLevel);
  257. If FParseLevel>FMaxParseDepth then
  258. Raise ETemplateParser.CreateFmt(SErrParseDepthExceeded,[FMaxParseDepth]);
  259. SLen:=Length(Src); // Minimum
  260. If SLen=0 then
  261. exit;
  262. STLen:=Length(FStartDelimiter);
  263. Result:='';
  264. SP:=PChar(Src);
  265. P:=SP;
  266. While (P-SP<SLen) do
  267. begin
  268. TS:=FindDelimiter(P,FStartDelimiter,SLen-(P-SP));
  269. If (TS=Nil) then
  270. begin
  271. TS:=P;
  272. P:=SP+SLen
  273. end
  274. else
  275. begin
  276. I:=TS-P;
  277. TE:=FindDelimiter(TS,FendDelimiter,SLen-I+1);
  278. If (TE=Nil) then
  279. begin
  280. TS:=P;
  281. P:=SP+SLen;
  282. end
  283. else
  284. begin
  285. // Add text prior to template to result
  286. AddToString(Result,P,I);
  287. // retrieve template name
  288. inc(TS,Length(FendDelimiter));
  289. I:=TE-TS;
  290. Setlength(PN,I);
  291. Move(TS^,PN[1],I);
  292. If GetParam(PN,PV) then
  293. begin
  294. Result:=Result+PV;
  295. end;
  296. P:=TE+Length(FEndDelimiter);
  297. TS:=P;
  298. end;
  299. end
  300. end;
  301. I:=P-TS;
  302. If (I>0) then
  303. AddToString(Result,TS,I);
  304. end;
  305. function TTemplateParser.ParseStream(Src: TStream; Dest: TStream): Integer;
  306. Var
  307. SS : TStringStream;
  308. S,R : String;
  309. begin
  310. SS:=TStringStream.Create('');
  311. Try
  312. SS.CopyFrom(Src,0);
  313. S:=SS.DataString;
  314. Finally
  315. SS.Free;
  316. end;
  317. R:=ParseString(S);
  318. Result:=Length(R);
  319. If (Result>0) then
  320. Dest.Write(R[1],Result);
  321. end;
  322. procedure TTemplateParser.ParseStrings(Src: TStrings; Dest: TStrings);
  323. Var
  324. I : Integer;
  325. begin
  326. For I:=0 to Src.Count-1 do
  327. Dest.Add(ParseString(Src[i]));
  328. end;
  329. { TFPCustomTemplate }
  330. procedure TFPCustomTemplate.GetParam(Sender: TObject; const ParamName: String;
  331. out AValue: String);
  332. begin
  333. If Assigned(FOnGetParam) then
  334. FOnGetParam(Self,ParamName,AValue);
  335. end;
  336. function TFPCustomTemplate.CreateParser: TTemplateParser;
  337. begin
  338. Result:=TTemplateParser.Create;
  339. If (FStartDelimiter<>'') then
  340. Result.StartDelimiter:=FStartDelimiter;
  341. If (FEndDelimiter<>'') then
  342. Result.EndDelimiter:=FEndDelimiter;
  343. Result.OnGetParam:=@GetParam;
  344. end;
  345. function TFPCustomTemplate.HasContent: Boolean;
  346. begin
  347. Result:=(FTemplate<>'') or (FFileName<>'');
  348. end;
  349. function TFPCustomTemplate.GetContent: String;
  350. Var
  351. P : TTemplateParser;
  352. S : TStringStream;
  353. F : TFileStream;
  354. begin
  355. F:=Nil;
  356. S:=Nil;
  357. If HasContent then
  358. begin
  359. if (FFileName<>'') then
  360. begin
  361. F:=TFileStream.Create(FFileName,fmOpenRead);
  362. S:=TStringStream.Create('');
  363. end;
  364. Try
  365. P:=CreateParser;
  366. Try
  367. If (F=Nil) then
  368. begin
  369. P.ParseStream(F,S);
  370. Result:=S.DataString;
  371. end
  372. else
  373. Result:=P.ParseString(FTemplate);
  374. Finally
  375. P.Free;
  376. end;
  377. Finally
  378. F.Free;
  379. S.Free;
  380. end;
  381. end;
  382. end;
  383. procedure TFPCustomTemplate.Assign(Source: TPersistent);
  384. Var
  385. T : TFPCustomTemplate;
  386. begin
  387. If Source is TFPCustomTemplate then
  388. begin
  389. T:=Source as TFPCustomTemplate;
  390. FEndDelimiter:=T.EndDelimiter;
  391. FStartDelimiter:=T.StartDelimiter;
  392. FFileName:=T.FileName;
  393. FTemplate:=T.Template;
  394. FOnGetParam:=T.OnGetParam;
  395. end
  396. else
  397. inherited Assign(Source);
  398. end;
  399. end.