2
$Id: laz_xmlwrite.pas 32678 2011-10-04 22:42:44Z mattias $
3
This file is part of the Free Component Library
6
Copyright (c) 1999-2000 by Sebastian Guenther, sg@freepascal.org
8
See the file COPYING.modifiedLGPL.txt, included in this distribution,
9
for details about the copyright.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
**********************************************************************}
25
uses Classes, LazUTF8, Laz_DOM;
27
procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String); overload;
28
procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text); overload;
29
procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream); overload;
31
procedure WriteXML(Element: TDOMNode; const AFileName: String); overload;
32
procedure WriteXML(Element: TDOMNode; var AFile: Text); overload;
33
procedure WriteXML(Element: TDOMNode; AStream: TStream); overload;
36
// ===================================================================
42
// -------------------------------------------------------------------
43
// Writers for the different node types
44
// -------------------------------------------------------------------
46
procedure WriteElement(node: TDOMNode); forward;
47
procedure WriteAttribute(node: TDOMNode); forward;
48
procedure WriteText(node: TDOMNode); forward;
49
procedure WriteCDATA(node: TDOMNode); forward;
50
procedure WriteEntityRef(node: TDOMNode); forward;
51
procedure WriteEntity(node: TDOMNode); forward;
52
procedure WritePI(node: TDOMNode); forward;
53
procedure WriteComment(node: TDOMNode); forward;
54
procedure WriteDocument(node: TDOMNode); forward;
55
procedure WriteDocumentType(node: TDOMNode); forward;
56
procedure WriteDocumentFragment(node: TDOMNode); forward;
57
procedure WriteNotation(node: TDOMNode); forward;
59
function NodeFrontIsText(Node: TDOMNode): boolean;
61
Result:=(Node is TDOMText) or (Node.ParentNode is TDOMText)
62
or (Node.PreviousSibling is TDOMText);
65
function NodeAfterIsText(Node: TDOMNode): boolean;
67
Result:=(Node is TDOMText) or (Node.ParentNode is TDOMText)
68
or (Node.NextSibling is TDOMText);
72
TWriteNodeProc = procedure(node: TDOMNode);
75
WriteProcs: array[ELEMENT_NODE..NOTATION_NODE] of TWriteNodeProc =
76
(@WriteElement, @WriteAttribute, @WriteText, @WriteCDATA, @WriteEntityRef,
77
@WriteEntity, @WritePI, @WriteComment, @WriteDocument, @WriteDocumentType,
78
@WriteDocumentFragment, @WriteNotation);
79
LineEnd: shortstring = LineEnding;
81
procedure WriteNode(node: TDOMNode);
83
WriteProcs[node.NodeType](node);
87
// -------------------------------------------------------------------
88
// Text file and TStream support
89
// -------------------------------------------------------------------
92
TOutputProc = procedure(const Buffer; Count: Longint);
97
wrt, wrtln: TOutputProc;
99
procedure Text_Write(const Buffer; Count: Longint);
102
if Count>0 then begin
104
System.Move(Buffer,s[1],Count);
109
procedure Text_WriteLn(const Buffer; Count: Longint);
112
if Count>0 then begin
114
System.Move(Buffer,s[1],Count);
119
procedure Stream_Write(const Buffer; Count: Longint);
121
if Count > 0 then begin
122
stream.Write(Buffer, Count);
126
procedure Stream_WriteLn(const Buffer; Count: Longint);
128
if Count > 0 then begin
129
stream.Write(Buffer, Count);
130
stream.Write(LineEnd[1],length(LineEnd));
134
procedure wrtStr(const s: string);
140
procedure wrtStrLn(const s: string);
143
wrtln(s[1],length(s));
146
procedure wrtChr(c: char);
151
procedure wrtLineEnd;
153
wrt(LineEnd[1],length(LineEnd));
156
// -------------------------------------------------------------------
158
// -------------------------------------------------------------------
162
IndentCount: integer;
167
for i:=1 to IndentCount do
178
if IndentCount>0 then dec(IndentCount);
182
// -------------------------------------------------------------------
184
// -------------------------------------------------------------------
187
TCharacters = set of Char;
188
TSpecialCharCallback = procedure(c: Char);
191
AttrSpecialChars = ['<', '>', '"', '&'];
192
TextSpecialChars = ['<', '>', '&'];
195
procedure ConvWrite(const s: String; const SpecialChars: TCharacters;
196
const SpecialCharCallback: TSpecialCharCallback);
198
StartPos, EndPos: Integer;
202
while EndPos <= Length(s) do
204
if s[EndPos] in SpecialChars then
206
wrt(s[StartPos],EndPos - StartPos);
207
SpecialCharCallback(s[EndPos]);
208
StartPos := EndPos + 1;
212
if StartPos <= length(s) then
213
wrt(s[StartPos], EndPos - StartPos);
216
procedure AttrSpecialCharCallback(c: Char);
229
procedure TextnodeSpecialCharCallback(c: Char);
246
// -------------------------------------------------------------------
247
// Node writers implementations
248
// -------------------------------------------------------------------
250
procedure WriteElement(node: TDOMNode);
253
attr, child: TDOMNode;
256
if not NodeFrontIsText(Node) then
259
wrtStr(node.NodeName);
260
if not (node.IsEmpty) then begin
261
for i := 0 to node.Attributes.Length - 1 do
263
attr := node.Attributes.Item[i];
265
wrtStr(attr.NodeName);
268
// !!!: Replace special characters in "s" such as '&', '<', '>'
270
ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback);
274
Child := node.FirstChild;
275
if Child = nil then begin
278
if not NodeAfterIsText(Node) then
283
if not ((Node is TDOMText) or (Node.ParentNode is TDOMText) or
290
Child := Child.NextSibling;
293
if not ((Node is TDOMText) or (Node.ParentNode is TDOMText) or
294
(Node.LastChild is TDOMText))
299
wrtStr(node.NodeName);
301
if not NodeAfterIsText(Node) then
306
procedure WriteAttribute(node: TDOMNode);
311
procedure WriteText(node: TDOMNode);
313
ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback);
317
procedure WriteCDATA(node: TDOMNode);
319
if not NodeFrontIsText(Node) then
320
wrtStr('<![CDATA[' + node.NodeValue + ']]>')
323
wrtStrln('<![CDATA[' + node.NodeValue + ']]>')
327
procedure WriteEntityRef(node: TDOMNode);
330
wrtStr(node.NodeName);
334
procedure WriteEntity(node: TDOMNode);
339
procedure WritePI(node: TDOMNode);
341
if not NodeFrontIsText(Node) then wrtIndent;
342
wrtChr('<'); wrtChr('!');
343
wrtStr(TDOMProcessingInstruction(node).Target);
345
wrtStr(TDOMProcessingInstruction(node).Data);
347
if not NodeAfterIsText(Node) then wrtLineEnd;
350
procedure WriteComment(node: TDOMNode);
352
if not NodeFrontIsText(Node) then wrtIndent;
354
wrtStr(node.NodeValue);
356
if not NodeAfterIsText(Node) then wrtLineEnd;
359
procedure WriteDocument(node: TDOMNode);
364
procedure WriteDocumentType(node: TDOMNode);
369
procedure WriteDocumentFragment(node: TDOMNode);
374
procedure WriteNotation(node: TDOMNode);
379
procedure InitWriter;
381
SetLength(Indent, 0);
384
procedure RootWriter(doc: TXMLDocument);
389
wrtStr('<?xml version="');
390
if Length(doc.XMLVersion) > 0 then
391
ConvWrite(doc.XMLVersion, AttrSpecialChars, @AttrSpecialCharCallback)
395
if Length(doc.Encoding) > 0 then
397
wrtStr(' encoding="');
398
ConvWrite(doc.Encoding, AttrSpecialChars, @AttrSpecialCharCallback);
403
if Length(doc.StylesheetType) > 0 then
405
wrtStr('<?xml-stylesheet type="');
406
ConvWrite(doc.StylesheetType, AttrSpecialChars, @AttrSpecialCharCallback);
408
ConvWrite(doc.StylesheetHRef, AttrSpecialChars, @AttrSpecialCharCallback);
415
child := doc.FirstChild;
416
while Assigned(Child) do
419
Child := Child.NextSibling;
424
procedure WriteXMLMemStream(doc: TXMLDocument);
425
// internally used by the WriteXMLFile procedures
427
Stream:=TMemoryStream.Create;
428
WriteXMLFile(doc,Stream);
432
// -------------------------------------------------------------------
433
// Interface implementation
434
// -------------------------------------------------------------------
437
// widestrings ansistring conversion is slow and we only use ansistring anyway
438
{off $DEFINE UsesFPCWidestrings}
441
{$IFDEF UsesFPCWidestrings}
443
procedure SimpleWide2AnsiMove(source:pwidechar;dest:pchar;len:sizeint);
449
if word(source^)<256 then
450
dest^:=char(word(source^))
458
procedure SimpleAnsi2WideMove(source:pchar;dest:pwidechar;len:sizeint);
464
dest^:=widechar(byte(source^));
471
WideStringManager: TWideStringManager = (
472
Wide2AnsiMove: @SimpleWide2AnsiMove;
473
Ansi2WideMove: @SimpleAnsi2WideMove
478
procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
482
// write first to memory buffer and then as one whole block to file
483
WriteXMLMemStream(doc);
485
fs := TFileStream.Create(UTF8ToSys(AFileName), fmCreate);
486
fs.CopyFrom(Stream,Stream.Size);
493
procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
494
{$IFDEF UsesFPCWidestrings}
496
OldWideStringManager: TWideStringManager;
499
{$IFDEF UsesFPCWidestrings}
500
SetWideStringManager(WideStringManager, OldWideStringManager);
505
wrtln := @Text_WriteLn;
507
{$IFDEF UsesFPCWidestrings}
509
SetWideStringManager(OldWideStringManager);
514
procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream);
515
{$IFDEF UsesFPCWidestrings}
517
OldWideStringManager: TWideStringManager;
520
{$IFDEF UsesFPCWidestrings}
521
SetWideStringManager(WideStringManager, OldWideStringManager);
525
wrt := @Stream_Write;
526
wrtln := @Stream_WriteLn;
528
{$IFDEF UsesFPCWidestrings}
530
SetWideStringManager(OldWideStringManager);
536
procedure WriteXML(Element: TDOMNode; const AFileName: String);
537
{$IFDEF UsesFPCWidestrings}
539
OldWideStringManager: TWideStringManager;
542
{$IFDEF UsesFPCWidestrings}
543
SetWideStringManager(WideStringManager, OldWideStringManager);
546
Stream := TFileStream.Create(UTF8ToSys(AFileName), fmCreate);
547
wrt := @Stream_Write;
548
wrtln := @Stream_WriteLn;
552
{$IFDEF UsesFPCWidestrings}
554
SetWideStringManager(OldWideStringManager);
559
procedure WriteXML(Element: TDOMNode; var AFile: Text);
560
{$IFDEF UsesFPCWidestrings}
562
OldWideStringManager: TWideStringManager;
565
{$IFDEF UsesFPCWidestrings}
566
SetWideStringManager(WideStringManager, OldWideStringManager);
571
wrtln := @Text_WriteLn;
574
{$IFDEF UsesFPCWidestrings}
576
SetWideStringManager(OldWideStringManager);
581
procedure WriteXML(Element: TDOMNode; AStream: TStream);
582
{$IFDEF UsesFPCWidestrings}
584
OldWideStringManager: TWideStringManager;
587
{$IFDEF UsesFPCWidestrings}
588
SetWideStringManager(WideStringManager, OldWideStringManager);
592
wrt := @Stream_Write;
593
wrtln := @Stream_WriteLn;
596
{$IFDEF UsesFPCWidestrings}
598
SetWideStringManager(OldWideStringManager);