/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StMerge.pas
ViewVC logotype

Contents of /dao/DelphiScanner/Components/tpsystools_4.04/source/StMerge.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 12317 byte(s)
Added tpsystools component
1 (* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Original Code is TurboPower SysTools
15 *
16 * The Initial Developer of the Original Code is
17 * TurboPower Software
18 *
19 * Portions created by the Initial Developer are Copyright (C) 1996-2002
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * ***** END LICENSE BLOCK ***** *)
25
26 {*********************************************************}
27 {* SysTools: StMerge.pas 4.04 *}
28 {*********************************************************}
29 {* SysTools: "Mail Merge" functionality *}
30 {*********************************************************}
31
32 {$include StDefine.inc}
33
34 unit StMerge;
35
36 interface
37
38 uses
39 Windows, SysUtils, Classes;
40
41 const
42 StDefaultTagStart = '<';
43 StDefaultTagEnd = '>';
44 StDefaultEscapeChar = '\';
45
46 type
47 TStGotMergeTagEvent = procedure (Sender : TObject; Tag : AnsiString;
48 var Value : AnsiString; var Discard : Boolean) of object;
49
50 TStMergeProgressEvent = procedure (Sender : TObject; Index : Integer; var Abort : Boolean);
51
52 TStTextMerge = class
53 private
54 FBadTag: AnsiString;
55 FDefaultTags: TStrings;
56 FEscapeChar: AnsiChar;
57 FMergedText : TStrings;
58 FMergeTags: TStrings;
59 FTagEnd: AnsiString;
60 FTagStart: AnsiString;
61 FTemplate : TStrings;
62 FOnMergeStart: TNotifyEvent;
63 FOnMergeDone: TNotifyEvent;
64 FOnLineStart: TStMergeProgressEvent;
65 FOnLineDone: TStMergeProgressEvent;
66 FOnGotMergeTag: TStGotMergeTagEvent;
67 FOnGotUnknownTag: TStGotMergeTagEvent;
68 protected {private}
69 procedure DoGotUnknownTag(Tag: AnsiString; var Value: AnsiString;
70 var Discard: Boolean);
71 procedure DoGotMergeTag(Tag : AnsiString; var Value : AnsiString;
72 var Discard : Boolean);
73 procedure SetEscapeChar(const Value: AnsiChar);
74 procedure SetTagEnd(const Value: AnsiString);
75 procedure SetTagStart(const Value: AnsiString);
76 public
77 constructor Create;
78 destructor Destroy; override;
79
80 { Access and Update Methods }
81 procedure Merge;
82
83 { Persistence and streaming methods }
84 {template }
85 procedure LoadTemplateFromFile(const AFile : TFileName);
86 procedure LoadTemplateFromStream(AStream : TStream);
87 procedure SaveTemplateToFile(const AFile : TFileName);
88 procedure SaveTemplateToStream(AStream : TStream);
89 { merge result text }
90 procedure SaveMergeToFile(const AFile : TFileName);
91 procedure SaveMergeToStream(AStream : TStream);
92
93 { properties }
94 property BadTag : AnsiString
95 read FBadTag write FBadTag;
96 property DefaultTags : TStrings
97 read FDefaultTags;
98 property EscapeChar : AnsiChar
99 read FEscapeChar write SetEscapeChar;
100 property MergedText : TStrings
101 read FMergedText;
102 property MergeTags : TStrings
103 read FMergeTags;
104 property TagEnd : AnsiString
105 read FTagEnd write SetTagEnd;
106 property TagStart : AnsiString
107 read FTagStart write SetTagStart;
108 property Template : TStrings
109 read FTemplate;
110
111 { events }
112 property OnGotMergeTag : TStGotMergeTagEvent
113 read FOnGotMergeTag write FOnGotMergeTag;
114 property OnGotUnknownTag : TStGotMergeTagEvent
115 read FOnGotUnknownTag write FOnGotUnknownTag;
116 property OnLineDone : TStMergeProgressEvent
117 read FOnLineDone write FOnLineDone;
118 property OnLineStart : TStMergeProgressEvent
119 read FOnLineStart write FOnLineStart;
120 property OnMergeDone : TNotifyEvent
121 read FOnMergeDone write FOnMergeDone;
122 property OnMergeStart : TNotifyEvent
123 read FOnMergeStart write FOnMergeStart;
124 end;
125
126 implementation
127
128 { TStTextMerge }
129
130 constructor TStTextMerge.Create;
131 begin
132
133 inherited Create;
134 FDefaultTags := TStringList.Create;
135 FMergeTags := TStringList.Create;
136 FMergedText := TStringList.Create;
137 FTemplate := TStringList.Create;
138
139 FTagEnd := StDefaultTagEnd;
140 FTagStart := StDefaultTagStart;
141 FEscapeChar := StDefaultEscapeChar;
142 FBadTag := '';
143 end;
144
145 destructor TStTextMerge.Destroy;
146 begin
147 FDefaultTags.Free;
148 FMergeTags.Free;
149 FMergedText.Free;
150 FTemplate.Free;
151 inherited Destroy;
152 end;
153
154 procedure TStTextMerge.DoGotMergeTag(Tag : AnsiString;
155 var Value : AnsiString; var Discard : Boolean);
156 begin
157 if Assigned(FOnGotMergeTag) then
158 FOnGotMergeTag(self, Tag, Value, Discard);
159 end;
160
161 procedure TStTextMerge.DoGotUnknownTag(Tag : AnsiString;
162 var Value : AnsiString; var Discard : Boolean);
163 begin
164 if Assigned(FOnGotUnknownTag) then
165 FOnGotUnknownTag(self, Tag, Value, Discard)
166 else
167 Value := FBadTag;
168 end;
169
170 procedure TStTextMerge.LoadTemplateFromFile(const AFile: TFileName);
171 var
172 FS : TFileStream;
173 begin
174 FS := TFileStream.Create(AFile, fmOpenRead or fmShareDenyNone);
175 try
176 LoadTemplateFromStream(FS);
177 finally
178 FS.Free;
179 end;
180 end;
181
182 procedure TStTextMerge.LoadTemplateFromStream(AStream: TStream);
183 begin
184 FTemplate.Clear;
185 FTemplate.LoadFromStream(AStream);
186 end;
187
188 procedure TStTextMerge.Merge;
189 { merge template with current DataTags }
190 const
191 TagIDChars = ['A'..'Z', 'a'..'z', '0'..'9', '_'];
192
193 function MatchDelim(Delim : AnsiString; var PC : PAnsiChar) : Boolean;
194 { see if current sequence matches specified Tag delimiter }
195 var
196 Match : PAnsiChar;
197 Len : Integer;
198 begin
199
200 { compare text starting at PC with Tag delimiter }
201 Len := Length(Delim);
202 GetMem(Match, Len + 1);
203 FillChar(Match^, Len + 1, #0);
204 StrLCopy(Match, PC, Len);
205
206 Result := StrPas(Match) = Delim;
207 if Result then
208 Inc(PC, Len); {advance past Tag delimiter }
209
210 FreeMem(Match, Len + 1);
211 end;
212
213 function GetTag(const Tag: AnsiString; var Discard : Boolean) : AnsiString;
214 var
215 IdxMerge, IdxDef : Integer;
216 TagID : AnsiString;
217 begin
218 { extract TagID from delimiters }
219 TagID := Copy(Tag, Length(TagStart) + 1, Length(Tag));
220 TagID := Copy(TagID, 1, Length(TagID) - Length(TagEnd));
221
222 { see if it matches Tag in MergeTags or DefaultTags }
223 IdxMerge := FMergeTags.IndexOfName(TagID);
224 IdxDef := FDefaultTags.IndexOfName(TagID);
225
226 { fire events as needed }
227 if (IdxMerge < 0) and (IdxDef < 0) then begin { no match }
228 DoGotUnknownTag(TagID, Result, Discard)
229 end
230 else begin { found match }
231 if (IdxMerge > -1) then begin { match in MergeTags }
232 Result := FMergeTags.Values[TagID];
233 DoGotMergeTag(TagID, Result, Discard);
234 end
235 else { not in MergTags, use Default }
236 if (IdxDef > -1) then begin
237 Result := FDefaultTags.Values[TagID];
238 DoGotMergeTag(TagID, Result, Discard);
239 end;
240 end;
241 end;
242
243 procedure ReplaceTags(Idx : Integer);
244 type
245 TagSearchStates = (fsCollectingText, fsCollectingTagID);
246 var
247 i, Len : Integer;
248 P, Cur : PAnsiChar;
249 Buff, NewBuff, TagBuff, DataBuff, TextBuff : AnsiString;
250 State : TagSearchStates;
251 FS, FE, Prev : AnsiChar;
252 {Escaped,} Discard : Boolean;
253 begin
254 { copy current template line }
255 Buff := FTemplate[Idx];
256 Len := Length(Buff);
257
258 { output line starts empty }
259 NewBuff := '';
260 TagBuff := '';
261 TextBuff := '';
262
263 { starts of delimiter strings }
264 FS := FTagStart[1];
265 FE := FTagEnd[1];
266 Prev := ' ';
267
268 { point at start of current line }
269 P := PAnsiChar(Buff);
270 Cur := P;
271
272 { start looking for Tags }
273 State := fsCollectingText;
274 for i := 1 to Len do begin
275 case State of
276 { accumulating non-Tag text }
277 fsCollectingText: begin
278 { matching the start of a Tag? }
279 if (Cur^ = FS) and (Prev <> EscapeChar) and
280 MatchDelim(FTagStart, Cur) then
281 begin
282 { dump what we've got }
283 NewBuff := NewBuff + TextBuff;
284 TextBuff := '';
285
286 { start accumulating a TagID }
287 TagBuff := TagStart;
288 State := fsCollectingTagID;
289 end
290
291 else
292 if (Cur^ = FS) and (Prev = EscapeChar) and
293 MatchDelim(FTagStart, Cur) then
294 begin
295 { overwrite escape character }
296 TextBuff[Length(TextBuff)] := Cur^;
297
298 { go to next character }
299 Prev := Cur^;
300 Inc(Cur);
301 end
302
303 else
304 { accumulate text }
305 begin
306 TextBuff := TextBuff + Cur^;
307
308 { go to next character }
309 Prev := Cur^;
310 Inc(Cur);
311 end;
312 end;
313
314 { accumulating a possible Tag }
315 fsCollectingTagID: begin
316 { matching the end of a Tag? }
317 if (Cur^ = FE) and (Prev <> EscapeChar) and
318 MatchDelim(FTagEnd, Cur) then
319 begin
320 { insert Tag value in place of TagID }
321 TagBuff := TagBuff + TagEnd;
322 DataBuff := GetTag(TagBuff, Discard);
323 if not Discard then
324 NewBuff := NewBuff + DataBuff;
325
326 { switch back to accumulating non-Tag text }
327 State := fsCollectingText;
328 end
329
330 else
331 { accumulate TagID }
332 if (Cur^ in TagIDChars) then begin
333 TagBuff := TagBuff + Cur^;
334 { go to next character }
335 Prev := Cur^;
336 Inc(Cur);
337 end
338
339 else
340 { doesn't look like a TagID; pass it back to text collection logic }
341 begin
342 { turn the "failed Tag" into regular accumulated text }
343 TextBuff := TagBuff + Cur^;
344 TagBuff := '';
345
346 { go to next character }
347 Prev := Cur^;
348 Inc(Cur);
349
350 { switch back to accumulating non-Tag text }
351 State := fsCollectingText;
352 end;
353
354 end;
355 end; {case State}
356
357 end; {for}
358
359 { append anything remaining }
360 if State = fsCollectingText then
361 NewBuff := NewBuff + TextBuff
362 else
363 NewBuff := NewBuff + TagBuff;
364
365 { update merge text with current line }
366 FMergedText.Add(NewBuff);
367 end;
368
369 var
370 i : Integer;
371 Abort : Boolean;
372
373 begin
374 { notify start of merge }
375 if Assigned(FOnMergeStart) then
376 FOnMergeStart(self);
377
378 FMergedText.Clear;
379
380 Abort := False;
381 { iterate Template }
382 for i := 0 to Pred(FTemplate.Count) do begin
383 if Assigned(FOnLineStart) then
384 FOnLineStart(self, i, Abort);
385
386 if Abort then Break;
387
388 ReplaceTags(i);
389
390 if Assigned(FOnLineDone) then
391 FOnLineDone(self, i, Abort);
392
393 if Abort then Break;
394 end; {for}
395
396 { notify end of merge }
397 if Assigned(FOnMergeDone) then
398 FOnMergeDone(self);
399 end;
400
401 procedure TStTextMerge.SaveMergeToFile(const AFile: TFileName);
402 var
403 FS : TFileStream;
404 begin
405 FS := TFileStream.Create(AFile, fmCreate);
406 try
407 SaveMergeToStream(FS);
408 finally
409 FS.Free;
410 end;
411 end;
412
413 procedure TStTextMerge.SaveMergeToStream(AStream: TStream);
414 begin
415 FMergedText.SaveToStream(AStream);
416 end;
417
418 procedure TStTextMerge.SaveTemplateToFile(const AFile: TFileName);
419 var
420 FS : TFileStream;
421 begin
422 FS := TFileStream.Create(AFile, fmCreate);
423 try
424 SaveTemplateToStream(FS);
425 finally
426 FS.Free;
427 end;
428 end;
429
430 procedure TStTextMerge.SaveTemplateToStream(AStream: TStream);
431 begin
432 FTemplate.SaveToStream(AStream);
433 end;
434
435 procedure TStTextMerge.SetEscapeChar(const Value: AnsiChar);
436 begin
437 FEscapeChar := Value;
438 end;
439
440 procedure TStTextMerge.SetTagEnd(const Value: AnsiString);
441 begin
442 FTagEnd := Value;
443 end;
444
445 procedure TStTextMerge.SetTagStart(const Value: AnsiString);
446 begin
447 FTagStart := Value;
448 end;
449
450 end.

  ViewVC Help
Powered by ViewVC 1.1.20