Menu

[r3204]: / branches / experimental / Src / TrunkSrc / USnippetExtraHelper.pas  Maximize  Restore  History

Download this file

252 lines (227 with data), 8.7 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
{
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/
*
* Copyright (C) 2008-2012, Peter Johnson (www.delphidabbler.com).
*
* $Rev$
* $Date$
*
* Implements a class that helps with parsing of a snippet's extra property as
* active text and vice versa.
}
unit USnippetExtraHelper;
interface
uses
// Project
ActiveText.UMain, UBaseObjects, UIStringList;
type
{
TSnippetExtraHelper:
Class that helps with the parsing of a snippet's Extra property as active
text and vice versa.
}
TSnippetExtraHelper = class(TNoConstructObject)
public
class function BuildActiveText(const PrefixText, CreditsMarkup,
URL: string): IActiveText; overload;
{Builds an active text object containing some plain followed by active
text defined by markup in the "Credits" format.
@param PrefixText [in] PrefixText text. If not empty string this is
added as plain text before any credits markup.
@param CreditsMarkup [in] "Credits" markup. May contain a link indicated
by bracketed text. Both plain text and links can be generated. May be
empty string.
@param URL [in] URL to use in any link defined in CreditsMarkup.
@return Required active text object. Will be an empty object if both
PrefixText and CreditsMarkup are empty.
}
class function BuildActiveText(const REML: string): IActiveText; overload;
{Builds an active text object from a string containing markup in REML
format.
@param REML [in] REML markup language that defines active text.
@return Required active text object. Will be an empty object if REML is
empty string.
}
class function BuildREMLMarkup(const ActiveText: IActiveText): string;
{Creates REML markup from an active text object using latest version.
@param ActiveText [in] Active text object used to generate markup.
@return Required REML markup.
}
/// <summary>Converts given plain text into an active text paragraph.
/// </summary>
/// <remarks>All text is treated as one paragraph, embedded newlines are
/// ignored. Text is trimmed of leading and trailing spaces. If trimmed
/// text is empty, empty active text is returned.</remarks>
class function PlainTextToActiveText(Text: string): IActiveText;
end;
implementation
uses
// Delphi
SysUtils,
// Project
UREMLDataIO, USnippetCreditsParser, UStrUtils;
{ TSnippetExtraHelper }
class function TSnippetExtraHelper.BuildActiveText(const PrefixText,
CreditsMarkup, URL: string): IActiveText;
{Builds an active text object containing some plain followed by active text
defined by markup in the "Credits" format.
@param PrefixText [in] PrefixText text. If not empty string this is added as
plain text before any credits markup.
@param CreditsMarkup [in] "Credits" markup. May contain a link indicated by
bracketed text. Both plain text and links can be generated. May be empty
string.
@param URL [in] URL to use in any link defined in CreditsMarkup.
@return Required active text object. Will be an empty object if both
PrefixText and CreditsMarkup are empty.
}
begin
// Create new empty active text object
Result := TActiveTextFactory.CreateActiveText;
if (PrefixText <> '') then
begin
// We have prefix text: add it to result as a paragraph containing a single
// text element
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsOpen));
Result.AddElem(
TActiveTextFactory.CreateTextElem(StrMakeSentence(PrefixText))
);
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsClose));
end;
if (CreditsMarkup <> '') then
begin
// We have credits: add credits and any hyperlink as a paragraph
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsOpen));
Result.Append(
TActiveTextFactory.CreateActiveText(
StrMakeSentence(CreditsMarkup),
TSnippetCreditsParser.Create(URL)
)
);
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsClose));
end;
end;
class function TSnippetExtraHelper.BuildActiveText(
const REML: string): IActiveText;
{Builds an active text object from a string containing markup in REML format.
@param REML [in] REML markup language that defines active text.
@return Required active text object. Will be an empty object if REML is
empty string.
}
function IsBlockOpener(Elem: IActiveTextElem): Boolean;
var
ActionElem: IActiveTextActionElem;
begin
if not Supports(Elem, IActiveTextActionElem, ActionElem) then
Exit(False);
Result := (ActionElem.DisplayStyle = dsBlock)
and (ActionElem.State = fsOpen);
end;
function IsBlockCloser(Elem: IActiveTextElem): Boolean;
var
ActionElem: IActiveTextActionElem;
begin
if not Supports(Elem, IActiveTextActionElem, ActionElem) then
Exit(False);
Result := (ActionElem.DisplayStyle = dsBlock)
and (ActionElem.State = fsClose);
end;
type
/// <summary>Describes different parts of parsed REML code in relation to
/// block tags.</summary>
/// <remarks>Can be within a pair of block tags; without, i.e. not enclosed
/// by block tags or in the transitional state between one and the other.
/// </remarks>
TBlockState = (bsWithin, bsWithout, bsTransition);
var
ActiveText: IActiveText; // receives active text built from REML
TextElem: IActiveTextTextElem;
Text: string;
BlockState: TBlockState; // state of current position relating to block tags
Elem: IActiveTextElem; // each element in active text
begin
Result := TActiveTextFactory.CreateActiveText;
if REML = '' then
Exit;
// Create active text by parsing REML
ActiveText := TActiveTextFactory.CreateActiveText(REML, TREMLReader.Create);
if ActiveText.IsEmpty then
Exit;
// Scan active text, inserting paragraph level block tags where the active
// text is not enclosed by them: this can be at the start, at the end or
// between existing blocks. E.g for "xxx <p>yyy</p> xxx <p>yyy</p> xxx", xxx
// is without any block and will be enclosed in paragraphs while yyy is within
// a block and will be unchanged.
BlockState := bsTransition;
for Elem in ActiveText do
begin
if IsBlockOpener(Elem) then
begin
Assert(BlockState <> bsWithin,
ClassName + '.BuildActiveText: Block is nested.');
if BlockState = bsWithout then
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsClose));
Result.AddElem(Elem);
BlockState := bsWithin;
end
else if IsBlockCloser(Elem) then
begin
Assert(BlockState = bsWithin,
ClassName + '.BuildActiveText: Block closer outside block.');
Result.AddElem(Elem);
BlockState := bsTransition;
end
else
begin
if BlockState = bsTransition then
begin
if Supports(Elem, IActiveTextTextElem, TextElem) then
begin
// make sure we don't start a paragraph block if text contains only
// spaces
Text := StrTrimLeft(TextElem.Text);
if Text <> '' then
begin
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsOpen));
Result.AddElem(TActiveTextFactory.CreateTextElem(Text));
BlockState := bsWithout;
end;
end
else
begin
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsOpen));
Result.AddElem(Elem);
BlockState := bsWithout;
end;
end
else
Result.AddElem(Elem);
end;
end;
if BlockState = bsWithout then
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsClose));
end;
class function TSnippetExtraHelper.BuildREMLMarkup(
const ActiveText: IActiveText): string;
{Creates REML markup from an active text object using latest version.
@param ActiveText [in] Active text object used to generate markup.
@return Required REML markup.
}
begin
Result := TREMLWriter.Render(ActiveText);
end;
class function TSnippetExtraHelper.PlainTextToActiveText(
Text: string): IActiveText;
begin
Result := TActiveTextFactory.CreateActiveText;
Text := StrTrim(Text);
if Text = '' then
Exit;
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsOpen));
Result.AddElem(
TActiveTextFactory.CreateTextElem(Text)
);
Result.AddElem(TActiveTextFactory.CreateActionElem(ekPara, fsClose));
end;
end.
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.