One of my favorite functions in the Delphi runtime library
(rtl) is StringReplace. StringReplace performs a search and replace within a
string, and it can be particularly valuable for writing template processors.
A template processor is a function that performs a
systematic search and replace on a template file, replacing placeholders (tags)
with meaningful data.
I have written a little function named
PerformTemplateReplace that uses StringReplace to perform a data-driven search
and replace on a template file, replacing embedded tags in the template with
data from a TDataSet.
Here is the function. An explanation of how it works
follows:
function PerformTemplateReplace(const Template: String;
DataSetRecord: TDataSet): String;
var
sl: TStringList;
str: String;
i: Integer;
begin
//Delphi 7 note
//Make sure your uses clause includes Classes and StrUtils
//Delphi 8 note
//Make sure your uses clause includes Borland.Vcl.Classes
//and Borland.Vcl.StrUtils
sl := TStringList.Create;
try
sl.Text := Template;
//remove all comment lines
i := 0;
while i < sl.Count do
begin
if UpperCase(Copy(sl.Strings[i],1,3)) = 'REM' then
begin
sl.Delete(i);
continue;
end;
//This line is executed if a comment is NOT removed
inc(i);
end;
str := sl.Text;
finally
sl.Free;
end;
//we now have the template in a string.
//Use StringReplace to replace parts of the template
//with data from our database
//begin by iterating through the AttendeesDataSource.DataSet Fields
//Replace {fieldname} tags in str with field contents
for i := 0 to Pred(DataSetRecord.Fields.Count) do
str := StringReplace(str, '{'+DataSetRecord.Fields[i].FieldName+'}',
DataSetRecord.Fields[i].AsString, [rfReplaceAll,rfIgnoreCase]);
//Check for today's date tag {TODAY}
if Pos('{TODAY}', UpperCase(str)) > 0 then
begin
str := StringReplace(str, '{today}',
FormatDateTime('mmmm, dd, yyyy',Date),[rfReplaceAll, rfIgnoreCase]);
end;
//Check for current time tag {TIME}
if Pos('{TIME}', UpperCase(str)) > 0 then
begin
str := StringReplace(str, '{time}',
FormatDateTime('tt',Time),[rfReplaceAll, rfIgnoreCase]);
end;
//You can create any additional
//custom tags using this same technique
Result := str;
end;
This function assumes that a template consists of
comment lines, field reference tags, custom tags, and plain text. Comment lines
are any line that begins with the letters REM. They are used to embed
documentation into your template.
Field reference tags are field names from a TDataSet. This template processor
assumes that field names will appear within curly braces. For example, if you
have a field named Last Name, a valid field reference tag for this field is
{Last Name}. Field reference tags are case insensitive.
Custom tags are any custom, curly braced-enclosed tags that you want to
manually program. PerformTemplateReplace assumes that there are two custom tags,
{TODAY} and {TIME}. You can create as many of your own custom tags as you like
by mimicking the {TODAY} and {TIME} processing code in PerformTemplateReplace.
Finally, plain text is any static text that you want in your processed
result. It will be untouched by PerformTemplateReplace, so long as your plain
text does not duplicate a field reference tag or a custom tag.
PerformTemplateReplace is passed two arguments, a string that contains the
template text and a TDataSet. PerformTemplateReplace begins by creating a
TStringList, which the template text is placed into. This TStringList is then
navigated, and any line that begins with REM is deleted. Once this loop
finishes, the TStringList is no longer necessary, and its Text property is
assigned to a String variable.
Now the real fun begins. We assume that the data that should be embedded into
the template comes from the current record of the passed in TDataSet. Therefore,
we iterate through the fields of the current record, performing a StringReplace
on each field name enclosed in curly braces, replacing any discovered field
reference tags with the text of that field.
Finally, any custom tags that you have programmed are searched for in the
text, and replaced if found. When done, PerformTemplateReplace returns a string
that contains the processed result.
Consider the following template example:
REM This is a comment line
REM
REM All comment lines are removed before the template is processed
REM
REM To include dataa from a field from the TDataSet in the processed result,
REM add the field name enclosed in curly braces.
REM For example, to include the Company field, use {Company}
REM You can also use the special tags {TODAY} and {TIME}
{Contact}
{Company}
{Addr1} {Addr2}
{City}, {State} {Zip}
{TODAY}
Dear {Contact}:
I am writing to you because I noticed that we have not done business with you since {LASTINVOICEDATE}. You are a valued customer and we want to make sure that you are being treated well by us. I hope that you will agree that we are unequaled when it comes to quality customer services.
If you could take a few minutes out of your day, I would appreciate the opportunity to discuss some of our newest services and products, as well as to hear from you what we can do better to make your experience as our client a positive one.
Thank you very much for your time,
Bob Jones
Regional Sales Manager
Universal Software Corporation, LLP
(555) 555-1212
Assume that you have a ClientDataSet that points to the Customer.xml
file found in c:Program FilesCommon FilesBorland SharedData, and you click a
button that invokes PerformTemplateReplace using the following event handler:
with TForm2.Create(nil) do
begin
Memo1.Text := PerformTemplateReplace(Self.Memo1.Text, ClientDataSet1);
Show;
end;
end;
The template will be a processed and the result will look something like the
following:
Erica Norman
Kauai Dive Shoppe
4-976 Sugarloaf Hwy Suite 103
Kapaa Kauai, HI 94766-1234
March, 29, 2004
Dear Erica Norman:
I am writing to you because I noticed that we have not done business with you since 2/2/1995 1:05:03 AM. You are a valued customer and we want to make sure that you are being treated well by us. I hope that you will agree that we are unequaled when it comes to quality customer services.
If you could take a few minutes out of your day, I would appreciate the opportunity to discuss some of our newest services and products, as well as to hear from you what we can do better to make your experience as our client a positive one.
Thank you very much for your time,
Bob Jones
Regional Sales Manager
Universal Software Corporation, LLP
(555) 555-1212
The TemplateProcessor application, shown in the following figure,
demonstrates the use of PerformTemplateReplace. You can download this
application from Borland's Code Central by using this link.

The following figure shows a processed template result,
which was created by clicking the process template button on the main form.

About the Author
Cary Jensen is President of Jensen Data Systems, Inc., a training
and consulting company that won the 2002 and 2003 Delphi Informant Magazine
Readers Choice Awards for Best Training. He is the author and presenter for
Delphi Developer Days 2004 (www.DelphiDeveloperDays.com) and Advantage Developer Days 2004
(www.AdvantageDeveloperDays.com),
information-packed seminars that tour North America and
Europe.
Cary is also an award-winning, best-selling co-author of nineteen books,
including Advantage Database Server: The Official Guide (2003,
McGraw-Hill/Osborne), Building Kylix Applications (2001, Osborne/McGraw-Hill),
Oracle JDeveloper (1999, Oracle Press), JBuilder Essentials (1998,
Osborne/McGraw-Hill), and Delphi In Depth (1996, Osborne/McGraw-Hill). For information about onsite training and
consulting you can contact Cary at cjensen@jensendatasystems.com, or visit his
Web site at www.JensenDataSystems.com.


Copyright
) 2004 Cary Jensen, Jensen Data Systems, Inc.
ALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUT
THE EXPRESS, WRITTEN CONSENT OF THE AUTHOR.
Connect with Us