Menu

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

Download this file

547 lines (508 with data), 21.0 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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
{
* 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 static class that helps with input and output to CodeSnip XML
* documents.
}
unit UXMLDocHelper;
interface
uses
// Delphi
XMLIntf,
// Project
Compilers.UGlobals, DB.USnippetKind, UExceptions, UIStringList, UStructs,
UXMLDocumentEx;
type
{
TXMLDocHelper:
Static class that helps with input and output to CodeSnip XML documents.
Provides functionality common to two or more XML read/write classes.
}
TXMLDocHelper = class(TObject)
strict private
class function FindRootNodeType(const XMLDoc: IXMLDocumentEx;
const ANodeType: TNodeType): IXMLNode;
{Finds a specified type of root node.
@param XMLDoc [in] Document containing node.
@param ANodeType [in] Type of node required.
@return Reference to found node or nil if no node found.
}
public
class function CreateXMLDoc: IXMLDocumentEx;
{Creates a new XML document object with required properties.
@return New XML document object.
}
class procedure CreateXMLProcInst(const XMLDoc: IXMLDocumentEx);
{Creates xml processing instruction in document.
@param XMLDoc [in] Document in which processing instruction is inserted.
}
class procedure CreateComment(const XMLDoc: IXMLDocumentEx;
const Comment: string);
{Creates a comment at the top level of an XML document.
@param XMLDoc [in] Document in which comment is inserted.
@param Comment [in] Comment to be inserted.
}
class function CreateRootNode(const XMLDoc: IXMLDocumentEx;
const NodeName, Watermark: string; const Version: Integer): IXMLNode;
{Creates a root in XML document.
@param XMLDoc [in] Document in which to insert root node.
@param NodeName [in] Name of root node.
@param Watermark [in] Value of root node's "watermark" attribute.
@param Version [in] Value of root node's "version" attribute.
@return Reference to new root node.
}
class function GetSubTagText(const XMLDoc: IXMLDocumentEx;
const ParentNode: IXMLNode; const SubTagName: string): string;
{Gets text of subtag of a parent node in an XML document.
@param XMLDoc [in] XML document containing sub tag.
@param ParentNode [in] Parent node of sub tag.
@param SubTagName [in] Name of desired subtag.
@return Sub tag's text if sub tag exists and is a text node, ''
otherwise.
}
class procedure GetPascalNameList(const XMLDoc: IXMLDocumentEx;
const ListNode: IXMLNode; const NameList: IStringList);
{Gets a list of names in <pascal-name> elements with a list.
@param XMLDoc [in] XML document containing name list.
@param ListNode [in] Node that contains list.
@param NameList [in] Receives text of all <pascal-name> elements in
list.
}
class function GetCompilerResults(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode): TCompileResults;
{Gets compiler results for a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Document node that contains compiler results
tag.
@return Array of compiler results. Provides defaults for missing
compilers.
}
class function GetStandardFormat(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Default: Boolean): Boolean;
{Gets value of a <standard-format> node of a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Snippet node that contains standard format tag.
@param Default [in] Value to use if node doesn't exist or has
non-standard value.
@return Value of node, or default value.
}
class function GetSnippetKind(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Default: TSnippetKind): TSnippetKind;
{Gets value of <kind> node of a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Snippet node that contains kind tag.
@param Default [in] Value to use if node doesn't exist or has
non-standard value.
@return Required snippet kind.
}
class function GetHiliteSource(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Default: Boolean): Boolean;
{Gets value of a <highlight-source> node of a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Snippet node that contains highlight source tag.
@param Default [in] Value to use if node doesn't exist or has
non-standard value.
@return Value of node, or default value.
}
class procedure WriteCompilerResults(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const CompRes: TCompileResults);
{Writes compile results for a snippet to XML document.
@param XMLDoc [in] XML document to receive compile results.
@param SnippetNode [in] Node containing snippet that received compile
results.
@param CompRes [in] Array of compiler results.
}
class procedure WritePascalNameList(const XMLDoc: IXMLDocumentEx;
const Parent: IXMLNode; const ListName: string; const Names: IStringList);
{Writes a Pascal name list to an XML document.
@param XMLDoc [in] XML document to which list is written.
@param Parent [in] Parent node that is to contain name list.
@param ListName [in] Name of new list node that is parent of list.
@param Names [in] List of Pascal names.
}
class procedure WriteSnippetKind(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Value: TSnippetKind);
{Writes a <kind> node to a an XML document.
@param XMLDoc [in] XML document to receive the node.
@param SnippetNode [in] Node containing snippet that receives kind node.
@param Value [in] Value of <kind> node.
}
class function ValidateRootNode(const XMLDoc: IXMLDocumentEx;
const ANodeName, AWatermark: string; const AVersions: TRange): Integer;
{Validates the root node of an XML document.
@param XMLDoc [in] XML document to be validated.
@param ANodeName [in] Name of root mode.
@param AWatermark [in] Required value of root node's "watermark"
attribute.
@param AVersions [in] Range of acceptable file version numbers.
@return Document version.
@except ECodeSnipXML raised on error.
}
class procedure ValidateProcessingInstr(const XMLDoc: IXMLDocumentEx);
{Checks that an XML document has a valid xml processing instruction.
@param XMLDoc [in] Document to be checked.
@except ECodeSnipXML raised on error.
}
end;
{
ECodeSnipXML:
Class of exception raised by TXMLDocHelper validation methods.
}
ECodeSnipXML = class(ECodeSnip);
implementation
uses
// Delphi
Windows {for inlining},
// Project
UStrUtils, UXMLDocConsts;
{ TXMLDocHelper }
class procedure TXMLDocHelper.CreateComment(const XMLDoc: IXMLDocumentEx;
const Comment: string);
{Creates a comment at the top level of an XML document.
@param XMLDoc [in] Document in which comment is inserted.
@param Comment [in] Comment to be inserted.
}
begin
XMLDoc.ChildNodes.Add(XMLDoc.CreateNode(' ' + Comment + ' ', ntComment));
end;
class function TXMLDocHelper.CreateRootNode(const XMLDoc: IXMLDocumentEx;
const NodeName, Watermark: string; const Version: Integer): IXMLNode;
{Creates a root in XML document.
@param XMLDoc [in] Document in which to insert root node.
@param NodeName [in] Name of root node.
@param Watermark [in] Value of root node's "watermark" attribute.
@param Version [in] Value of root node's "version" attribute.
@return Reference to new root node.
}
begin
Result := XMLDoc.CreateNode(NodeName);
Result.SetAttribute(cRootWatermarkAttr, Watermark);
Result.SetAttribute(cRootVersionAttr, Version);
XMLDoc.ChildNodes.Add(Result);
end;
class function TXMLDocHelper.CreateXMLDoc: IXMLDocumentEx;
{Creates a new XML document object with required properties.
@return New XML document object.
}
begin
Result := TXMLDocumentEx.Create(nil);
Result.Options := [doNodeAutoIndent];
Result.ParseOptions := [poPreserveWhiteSpace];
end;
class procedure TXMLDocHelper.CreateXMLProcInst(const XMLDoc: IXMLDocumentEx);
{Creates xml processing instruction in document.
@param XMLDoc [in] Document in which processing instruction is inserted.
}
begin
XMLDoc.ChildNodes.Add(
XMLDoc.CreateNode(cXMLNode, ntProcessingInstr, cXMLNodeText)
);
end;
class function TXMLDocHelper.FindRootNodeType(const XMLDoc: IXMLDocumentEx;
const ANodeType: TNodeType): IXMLNode;
{Finds a specified type of root node.
@param XMLDoc [in] Document containing node.
@param ANodeType [in] Type of node required.
@return Reference to found node or nil if no node found.
}
var
Idx: Integer; // loops thru immediate child nodes of xml document
begin
Result := nil;
for Idx := 0 to Pred(XMLDoc.ChildNodes.Count) do
begin
if XMLDoc.ChildNodes.Nodes[Idx].NodeType = ANodeType then
begin
Result := XMLDoc.ChildNodes.Nodes[Idx];
Break;
end;
end;
end;
class function TXMLDocHelper.GetCompilerResults(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode): TCompileResults;
{Gets compiler results for a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Document node that contains compiler results tag.
@return Array of compiler results. Provides defaults for missing compilers.
}
// -------------------------------------------------------------------------
function IDStrToCompID(const IDStr: string;
out Match: TCompilerID): Boolean;
{Converts an identifier string to a compiler ID.
@param IDStr [in] Identifier string.
@param Match [out] Set to compiler ID that matches IDStr. Undefined if
IDStr not recognised.
@return True if IDStr is recognised, False if not.
}
var
CompID: TCompilerID; // loops thru all compiler IDs
begin
Result := False;
for CompID := Low(TCompilerID) to High(TCompilerID) do
begin
if cCompilerIDs[CompID] = IDStr then
begin
Result := True;
Match := CompID;
Break;
end;
end;
end;
// -------------------------------------------------------------------------
var
ListNode: IXMLNode; // node that enclose compiler result nodes
ResultsNodes: IXMLSimpleNodeList; // list of compiler-result nodes
ResultNode: IXMLNode; // a compiler-result node
CompID: TCompilerID; // loops thru compiler IDs
CompResultStr: string; // compiler id string from result node
begin
// Initialise all results to unknown (query)
for CompID := Low(TCompilerID) to High(TCompilerID) do
Result[CompID] := crQuery;
// Find enclosing node: valid if this is not present
ListNode := XMLDoc.FindFirstChildNode(SnippetNode, cCompilerResultsNode);
if not Assigned(ListNode) then
Exit;
// Get list of compiler-result nodes contained in list and process each one
ResultsNodes := XMLDoc.FindChildNodes(ListNode, cCompilerResultNode);
for ResultNode in ResultsNodes do
begin
if ResultNode.IsTextElement then
begin
// get compile result identifier
CompResultStr := ResultNode.Text;
if CompResultStr = '' then
CompResultStr := '?';
// add specified result function result
if IDStrToCompID(
ResultNode.Attributes[cCompilerResultIdAttr], CompID
) then
begin
case CompResultStr[1] of
'Y': Result[CompID] := crSuccess;
'N': Result[CompID] := crError;
'W': Result[CompiD] := crWarning;
else Result[CompID] := crQuery;
end;
end;
end;
end;
end;
class function TXMLDocHelper.GetHiliteSource(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Default: Boolean): Boolean;
{Gets value of a <highlight-source> node of a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Snippet node that contains highlight source tag.
@param Default [in] Value to use if node doesn't exist or has non-standard
value.
@return Value of node, or default value.
}
var
Value: string; // text value of HiliteSource node
begin
Value := GetSubTagText(XMLDoc, SnippetNode, cHighlightSource);
if Value <> '' then
Result := Value <> '0'
else
Result := Default;
end;
class procedure TXMLDocHelper.GetPascalNameList(const XMLDoc: IXMLDocumentEx;
const ListNode: IXMLNode; const NameList: IStringList);
{Gets a list of names in <pascal-name> elements with a list.
@param XMLDoc [in] XML document containing name list.
@param ListNode [in] Node that contains list.
@param NameList [in] Receives text of all <pascal-name> elements in list.
}
var
NameNode: IXMLNode; // name of a node in the list
NodeList: IXMLSimpleNodeList; // list of matching child nodes if ListNode
begin
NameList.Clear;
if not Assigned(ListNode) then
Exit; // this is permitted since snippet lists can be empty or missing
NodeList := XMLDoc.FindChildNodes(ListNode, cPascalNameNode);
for NameNode in NodeList do
if NameNode.IsTextElement then
NameList.Add(NameNode.Text);
end;
class function TXMLDocHelper.GetSnippetKind(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Default: TSnippetKind): TSnippetKind;
{Gets value of <kind> node of a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Snippet node that contains kind tag.
@param Default [in] Value to use if node doesn't exist or has non-standard
value.
@return Required snippet kind.
}
var
Value: string; // text value of Kind node
begin
Value := GetSubTagText(XMLDoc, SnippetNode, cKindNode);
if StrSameText(Value, 'freeform') then
Result := skFreeform
else if StrSameText(Value, 'routine') then
Result := skRoutine
else if StrSameText(Value, 'const') then
Result := skConstant
else if StrSameText(Value, 'type') then
Result := skTypeDef
else if StrSameText(Value, 'unit') then
Result := skUnit
else if StrSameText(Value, 'class') then
Result := skClass
else
Result := Default;
end;
class function TXMLDocHelper.GetStandardFormat(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Default: Boolean): Boolean;
{Gets value of a <standard-format> node of a snippet in an XML document.
@param XMLDoc [in] XML document containing snippet.
@param SnippetNode [in] Snippet node that contains standard format tag.
@param Default [in] Value to use if node doesn't exist or has non-standard
value.
@return Value of node, or default value.
}
var
Value: string; // text value of Kind node
begin
Value := GetSubTagText(XMLDoc, SnippetNode, cStandardFormatNode);
if Value <> '' then
Result := Value <> '0'
else
Result := Default;
end;
class function TXMLDocHelper.GetSubTagText(const XMLDoc: IXMLDocumentEx;
const ParentNode: IXMLNode; const SubTagName: string): string;
{Gets text of subtag of a parent node in an XML document.
@param XMLDoc [in] XML document containing sub tag.
@param ParentNode [in] Parent node of sub tag.
@param SubTagName [in] Name of desired subtag.
@return Sub tag's text if sub tag exists and is a text node, '' otherwise.
}
var
PropNode: IXMLNode; // sub tag's node
begin
Result := '';
PropNode := XMLDoc.FindFirstChildNode(ParentNode, SubTagName);
if Assigned(PropNode) and (PropNode.IsTextElement) then
Result := PropNode.Text;
end;
class procedure TXMLDocHelper.ValidateProcessingInstr(
const XMLDoc: IXMLDocumentEx);
{Checks that an XML document has a valid xml processing instruction.
@param XMLDoc [in] Document to be checked.
@except ECodeSnipXML raised on error.
}
var
XMLNode: IXMLNOde; // xml processing node
resourcestring
// Error messages
sNoXMLProcInst = 'Invalid document: must begin with a valid XML processing '
+ 'instruction';
begin
// Must have correct processing instruction (<?xml .... ?>)
XMLNode := FindRootNodeType(XMLDoc, ntProcessingInstr);
if not Assigned(XMLNode) or (XMLNode.NodeName <> cXMLNode)
or (XMLNode.NodeType <> ntProcessingInstr) then
raise ECodeSnipXML.Create(sNoXMLProcInst);
end;
class function TXMLDocHelper.ValidateRootNode(const XMLDoc: IXMLDocumentEx;
const ANodeName, AWatermark: string; const AVersions: TRange): Integer;
{Validates the root node of an XML document.
@param XMLDoc [in] XML document to be validated.
@param ANodeName [in] Name of root mode.
@param AWatermark [in] Required value of root node's "watermark" attribute.
@param AVersions [in] Range of acceptable file version numbers.
@return Document version.
@except ECodeSnipXML raised on error.
}
var
RootNode: IXMLNode; // document root node
resourcestring
// Error messages
sNoRootNode = 'Invalid document: no root element present';
sBadRootName = 'Invalid document: root element must be named <%s>';
sBadWatermark = 'Invalid document: watermark is incorrect';
sBadVersion = 'Invalid document: unsupported document version %d';
begin
RootNode := XMLDoc.DocumentElement;
// There must be a root node
if not Assigned(RootNode) then
raise ECodeSnipXML.Create(sNoRootNode);
// Correct root node must be present, with valid watermark and version
if RootNode.NodeName <> ANodeName then
raise ECodeSnipXML.CreateFmt(sBadRootName, [ANodeName]);
if RootNode.Attributes[cRootWatermarkAttr] <> AWatermark then
raise ECodeSnipXML.Create(sBadWatermark);
Result := RootNode.Attributes[cRootVersionAttr];
if not AVersions.Contains(Result) then
raise ECodeSnipXML.CreateFmt(sBadVersion, [Result]);
end;
class procedure TXMLDocHelper.WriteCompilerResults(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const CompRes: TCompileResults);
{Writes compile results for a snippet to XML document.
@param XMLDoc [in] XML document to receive compile results.
@param SnippetNode [in] Node containing snippet that received compile
results.
@param CompRes [in] Array of compiler results.
}
const
// map of compiler results onto character representation store in XML file.
cCompResMap: array[TCompileResult] of Char = ('Y', 'W', 'N', '?');
var
CompResultsNode: IXMLNode; // node that stores all compiler results
CompResultNode: IXMLNode; // each compiler result node
CompID: TCompilerID; // loops thru all supported compilers
begin
// compiler results value: only write known results
CompResultsNode := XMLDoc.CreateElement(SnippetNode, cCompilerResultsNode);
for CompID := Low(TCompilerID) to High(TCompilerID) do
begin
if CompRes[CompID] <> crQuery then
begin
CompResultNode := XMLDoc.CreateElement(
CompResultsNode, cCompilerResultNode,
cCompResMap[CompRes[CompID]]
);
CompResultNode.Attributes[cCompilerResultIdAttr] := cCompilerIDs[CompID];
end;
end;
end;
class procedure TXMLDocHelper.WritePascalNameList(const XMLDoc: IXMLDocumentEx;
const Parent: IXMLNode; const ListName: string; const Names: IStringList);
{Writes a Pascal name list to an XML document.
@param XMLDoc [in] XML document to which list is written.
@param Parent [in] Parent node that is to contain name list.
@param ListName [in] Name of new list node that is parent of list.
@param Names [in] List of Pascal names.
}
var
ListNode: IXMLNode; // reference to enclosing list node
Name: string; // a name item in list
begin
ListNode := XMLDoc.CreateElement(Parent, ListName);
for Name in Names do
XMLDoc.CreateElement(ListNode, cPascalNameNode, Name);
end;
class procedure TXMLDocHelper.WriteSnippetKind(const XMLDoc: IXMLDocumentEx;
const SnippetNode: IXMLNode; const Value: TSnippetKind);
{Writes a <kind> node to a an XML document.
@param XMLDoc [in] XML document to receive the node.
@param SnippetNode [in] Node containing snippet that receives kind node.
@param Value [in] Value of <kind> node.
}
const
cValues: array[TSnippetKind] of string = (
'freeform', 'routine', 'const', 'type', 'unit', 'class'
);
begin
XMLDoc.CreateElement(SnippetNode, cKindNode, cValues[Value]);
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.