mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-03 13:23:46 +02:00
604 lines
13 KiB
ObjectPascal
604 lines
13 KiB
ObjectPascal
{
|
|
$Id$
|
|
This file is part of the Free Component Library
|
|
|
|
XML writing routines
|
|
Copyright (c) 1999-2000 by Sebastian Guenther, sg@freepascal.org
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
|
|
|
|
unit Laz_XMLWrite;
|
|
|
|
{$MODE objfpc}
|
|
{$H+}
|
|
|
|
interface
|
|
|
|
uses Classes, Laz_DOM, FileProcs;
|
|
|
|
procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String); overload;
|
|
procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text); overload;
|
|
procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream); overload;
|
|
|
|
procedure WriteXML(Element: TDOMNode; const AFileName: String); overload;
|
|
procedure WriteXML(Element: TDOMNode; var AFile: Text); overload;
|
|
procedure WriteXML(Element: TDOMNode; AStream: TStream); overload;
|
|
|
|
|
|
// ===================================================================
|
|
|
|
implementation
|
|
|
|
uses SysUtils;
|
|
|
|
// -------------------------------------------------------------------
|
|
// Writers for the different node types
|
|
// -------------------------------------------------------------------
|
|
|
|
procedure WriteElement(node: TDOMNode); forward;
|
|
procedure WriteAttribute(node: TDOMNode); forward;
|
|
procedure WriteText(node: TDOMNode); forward;
|
|
procedure WriteCDATA(node: TDOMNode); forward;
|
|
procedure WriteEntityRef(node: TDOMNode); forward;
|
|
procedure WriteEntity(node: TDOMNode); forward;
|
|
procedure WritePI(node: TDOMNode); forward;
|
|
procedure WriteComment(node: TDOMNode); forward;
|
|
procedure WriteDocument(node: TDOMNode); forward;
|
|
procedure WriteDocumentType(node: TDOMNode); forward;
|
|
procedure WriteDocumentFragment(node: TDOMNode); forward;
|
|
procedure WriteNotation(node: TDOMNode); forward;
|
|
|
|
function NodeFrontIsText(Node: TDOMNode): boolean;
|
|
begin
|
|
Result:=(Node is TDOMText) or (Node.ParentNode is TDOMText)
|
|
or (Node.PreviousSibling is TDOMText);
|
|
end;
|
|
|
|
function NodeAfterIsText(Node: TDOMNode): boolean;
|
|
begin
|
|
Result:=(Node is TDOMText) or (Node.ParentNode is TDOMText)
|
|
or (Node.NextSibling is TDOMText);
|
|
end;
|
|
|
|
type
|
|
TWriteNodeProc = procedure(node: TDOMNode);
|
|
|
|
const
|
|
WriteProcs: array[ELEMENT_NODE..NOTATION_NODE] of TWriteNodeProc =
|
|
(@WriteElement, @WriteAttribute, @WriteText, @WriteCDATA, @WriteEntityRef,
|
|
@WriteEntity, @WritePI, @WriteComment, @WriteDocument, @WriteDocumentType,
|
|
@WriteDocumentFragment, @WriteNotation);
|
|
LineEnd: shortstring = LineEnding;
|
|
|
|
procedure WriteNode(node: TDOMNode);
|
|
begin
|
|
WriteProcs[node.NodeType](node);
|
|
end;
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// Text file and TStream support
|
|
// -------------------------------------------------------------------
|
|
|
|
type
|
|
TOutputProc = procedure(const Buffer; Count: Longint);
|
|
|
|
threadvar
|
|
f: ^Text;
|
|
stream: TStream;
|
|
wrt, wrtln: TOutputProc;
|
|
|
|
procedure Text_Write(const Buffer; Count: Longint);
|
|
var s: string;
|
|
begin
|
|
if Count>0 then begin
|
|
SetLength(s,Count);
|
|
System.Move(Buffer,s[1],Count);
|
|
Write(f^, s);
|
|
end;
|
|
end;
|
|
|
|
procedure Text_WriteLn(const Buffer; Count: Longint);
|
|
var s: string;
|
|
begin
|
|
if Count>0 then begin
|
|
SetLength(s,Count);
|
|
System.Move(Buffer,s[1],Count);
|
|
writeln(f^, s);
|
|
end;
|
|
end;
|
|
|
|
procedure Stream_Write(const Buffer; Count: Longint);
|
|
begin
|
|
if Count > 0 then begin
|
|
stream.Write(Buffer, Count);
|
|
end;
|
|
end;
|
|
|
|
procedure Stream_WriteLn(const Buffer; Count: Longint);
|
|
begin
|
|
if Count > 0 then begin
|
|
stream.Write(Buffer, Count);
|
|
stream.Write(LineEnd[1],length(LineEnd));
|
|
end;
|
|
end;
|
|
|
|
procedure wrtStr(const s: string);
|
|
begin
|
|
if s<>'' then
|
|
wrt(s[1],length(s));
|
|
end;
|
|
|
|
procedure wrtStrLn(const s: string);
|
|
begin
|
|
if s<>'' then
|
|
wrtln(s[1],length(s));
|
|
end;
|
|
|
|
procedure wrtChr(c: char);
|
|
begin
|
|
wrt(c,1);
|
|
end;
|
|
|
|
procedure wrtLineEnd;
|
|
begin
|
|
wrt(LineEnd[1],length(LineEnd));
|
|
end;
|
|
|
|
// -------------------------------------------------------------------
|
|
// Indent handling
|
|
// -------------------------------------------------------------------
|
|
|
|
threadvar
|
|
Indent: String;
|
|
IndentCount: integer;
|
|
|
|
procedure wrtIndent;
|
|
var i: integer;
|
|
begin
|
|
for i:=1 to IndentCount do
|
|
wrtStr(Indent);
|
|
end;
|
|
|
|
procedure IncIndent;
|
|
begin
|
|
inc(IndentCount);
|
|
end;
|
|
|
|
procedure DecIndent;
|
|
begin
|
|
if IndentCount>0 then dec(IndentCount);
|
|
end;
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// String conversion
|
|
// -------------------------------------------------------------------
|
|
|
|
type
|
|
TCharacters = set of Char;
|
|
TSpecialCharCallback = procedure(c: Char);
|
|
|
|
const
|
|
AttrSpecialChars = ['<', '>', '"', '&'];
|
|
TextSpecialChars = ['<', '>', '&'];
|
|
|
|
|
|
procedure ConvWrite(const s: String; const SpecialChars: TCharacters;
|
|
const SpecialCharCallback: TSpecialCharCallback);
|
|
var
|
|
StartPos, EndPos: Integer;
|
|
begin
|
|
StartPos := 1;
|
|
EndPos := 1;
|
|
while EndPos <= Length(s) do
|
|
begin
|
|
if s[EndPos] in SpecialChars then
|
|
begin
|
|
wrt(s[StartPos],EndPos - StartPos);
|
|
SpecialCharCallback(s[EndPos]);
|
|
StartPos := EndPos + 1;
|
|
end;
|
|
Inc(EndPos);
|
|
end;
|
|
if StartPos <= length(s) then
|
|
wrt(s[StartPos], EndPos - StartPos);
|
|
end;
|
|
|
|
procedure AttrSpecialCharCallback(c: Char);
|
|
const
|
|
QuotStr = '"';
|
|
AmpStr = '&';
|
|
begin
|
|
if c = '"' then
|
|
wrtStr(QuotStr)
|
|
else if c = '&' then
|
|
wrtStr(AmpStr)
|
|
else
|
|
wrt(c,1);
|
|
end;
|
|
|
|
procedure TextnodeSpecialCharCallback(c: Char);
|
|
const
|
|
ltStr = '<';
|
|
gtStr = '>';
|
|
AmpStr = '&';
|
|
begin
|
|
if c = '<' then
|
|
wrtStr(ltStr)
|
|
else if c = '>' then
|
|
wrtStr(gtStr)
|
|
else if c = '&' then
|
|
wrtStr(AmpStr)
|
|
else
|
|
wrt(c,1);
|
|
end;
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// Node writers implementations
|
|
// -------------------------------------------------------------------
|
|
|
|
procedure WriteElement(node: TDOMNode);
|
|
var
|
|
i: Integer;
|
|
attr, child: TDOMNode;
|
|
s: String;
|
|
begin
|
|
if not NodeFrontIsText(Node) then
|
|
wrtIndent;
|
|
wrtChr('<');
|
|
wrtStr(node.NodeName);
|
|
if not (node.IsEmpty) then begin
|
|
for i := 0 to node.Attributes.Length - 1 do
|
|
begin
|
|
attr := node.Attributes.Item[i];
|
|
wrtChr(' ');
|
|
wrtStr(attr.NodeName);
|
|
wrtChr('=');
|
|
s := attr.NodeValue;
|
|
// !!!: Replace special characters in "s" such as '&', '<', '>'
|
|
wrtChr('"');
|
|
ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback);
|
|
wrtChr('"');
|
|
end;
|
|
end;
|
|
Child := node.FirstChild;
|
|
if Child = nil then begin
|
|
wrtChr('/');
|
|
wrtChr('>');
|
|
if not NodeAfterIsText(Node) then
|
|
wrtLineEnd;
|
|
end else
|
|
begin
|
|
wrtChr('>');
|
|
if not ((Node is TDOMText) or (Node.ParentNode is TDOMText) or
|
|
(Child is TDOMText))
|
|
then
|
|
wrtLineEnd;
|
|
IncIndent;
|
|
repeat
|
|
WriteNode(Child);
|
|
Child := Child.NextSibling;
|
|
until child = nil;
|
|
DecIndent;
|
|
if not ((Node is TDOMText) or (Node.ParentNode is TDOMText) or
|
|
(Node.LastChild is TDOMText))
|
|
then
|
|
wrtIndent;
|
|
wrtChr('<');
|
|
wrtChr('/');
|
|
wrtStr(node.NodeName);
|
|
wrtChr('>');
|
|
if not NodeAfterIsText(Node) then
|
|
wrtLineEnd;
|
|
end;
|
|
end;
|
|
|
|
procedure WriteAttribute(node: TDOMNode);
|
|
begin
|
|
if node=nil then ;
|
|
end;
|
|
|
|
procedure WriteText(node: TDOMNode);
|
|
begin
|
|
ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback);
|
|
if node=nil then ;
|
|
end;
|
|
|
|
procedure WriteCDATA(node: TDOMNode);
|
|
begin
|
|
if not NodeFrontIsText(Node) then
|
|
wrtStr('<![CDATA[' + node.NodeValue + ']]>')
|
|
else begin
|
|
wrtIndent;
|
|
wrtStrln('<![CDATA[' + node.NodeValue + ']]>')
|
|
end;
|
|
end;
|
|
|
|
procedure WriteEntityRef(node: TDOMNode);
|
|
begin
|
|
wrtChr('&');
|
|
wrtStr(node.NodeName);
|
|
wrtChr(';');
|
|
end;
|
|
|
|
procedure WriteEntity(node: TDOMNode);
|
|
begin
|
|
if node=nil then ;
|
|
end;
|
|
|
|
procedure WritePI(node: TDOMNode);
|
|
begin
|
|
if not NodeFrontIsText(Node) then wrtIndent;
|
|
wrtChr('<'); wrtChr('!');
|
|
wrtStr(TDOMProcessingInstruction(node).Target);
|
|
wrtChr(' ');
|
|
wrtStr(TDOMProcessingInstruction(node).Data);
|
|
wrtChr('>');
|
|
if not NodeAfterIsText(Node) then wrtLineEnd;
|
|
end;
|
|
|
|
procedure WriteComment(node: TDOMNode);
|
|
begin
|
|
if not NodeFrontIsText(Node) then wrtIndent;
|
|
wrtStr('<!--');
|
|
wrtStr(node.NodeValue);
|
|
wrtStr('-->');
|
|
if not NodeAfterIsText(Node) then wrtLineEnd;
|
|
end;
|
|
|
|
procedure WriteDocument(node: TDOMNode);
|
|
begin
|
|
if node=nil then ;
|
|
end;
|
|
|
|
procedure WriteDocumentType(node: TDOMNode);
|
|
begin
|
|
if node=nil then ;
|
|
end;
|
|
|
|
procedure WriteDocumentFragment(node: TDOMNode);
|
|
begin
|
|
if node=nil then ;
|
|
end;
|
|
|
|
procedure WriteNotation(node: TDOMNode);
|
|
begin
|
|
if node=nil then ;
|
|
end;
|
|
|
|
procedure InitWriter;
|
|
begin
|
|
SetLength(Indent, 0);
|
|
end;
|
|
|
|
procedure RootWriter(doc: TXMLDocument);
|
|
var
|
|
Child: TDOMNode;
|
|
begin
|
|
InitWriter;
|
|
wrtStr('<?xml version="');
|
|
if Length(doc.XMLVersion) > 0 then
|
|
ConvWrite(doc.XMLVersion, AttrSpecialChars, @AttrSpecialCharCallback)
|
|
else
|
|
wrtStr('1.0');
|
|
wrtChr('"');
|
|
if Length(doc.Encoding) > 0 then
|
|
begin
|
|
wrtStr(' encoding="');
|
|
ConvWrite(doc.Encoding, AttrSpecialChars, @AttrSpecialCharCallback);
|
|
wrtStr('"');
|
|
end;
|
|
wrtStrln('?>');
|
|
|
|
if Length(doc.StylesheetType) > 0 then
|
|
begin
|
|
wrtStr('<?xml-stylesheet type="');
|
|
ConvWrite(doc.StylesheetType, AttrSpecialChars, @AttrSpecialCharCallback);
|
|
wrtStr('" href="');
|
|
ConvWrite(doc.StylesheetHRef, AttrSpecialChars, @AttrSpecialCharCallback);
|
|
wrtStrln('"?>');
|
|
end;
|
|
|
|
Indent := ' ';
|
|
IndentCount := 0;
|
|
|
|
child := doc.FirstChild;
|
|
while Assigned(Child) do
|
|
begin
|
|
WriteNode(Child);
|
|
Child := Child.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure WriteXMLMemStream(doc: TXMLDocument);
|
|
// internally used by the WriteXMLFile procedures
|
|
begin
|
|
Stream:=TMemoryStream.Create;
|
|
WriteXMLFile(doc,Stream);
|
|
Stream.Position:=0;
|
|
end;
|
|
|
|
// -------------------------------------------------------------------
|
|
// Interface implementation
|
|
// -------------------------------------------------------------------
|
|
|
|
{$IFDEF FPC}
|
|
// widestrings ansistring conversion is slow and we only use ansistring anyway
|
|
{off $DEFINE UsesFPCWidestrings}
|
|
{$ENDIF}
|
|
|
|
{$IFDEF UsesFPCWidestrings}
|
|
|
|
procedure SimpleWide2AnsiMove(source:pwidechar;dest:pchar;len:sizeint);
|
|
var
|
|
i : sizeint;
|
|
begin
|
|
for i:=1 to len do
|
|
begin
|
|
if word(source^)<256 then
|
|
dest^:=char(word(source^))
|
|
else
|
|
dest^:='?';
|
|
inc(dest);
|
|
inc(source);
|
|
end;
|
|
end;
|
|
|
|
procedure SimpleAnsi2WideMove(source:pchar;dest:pwidechar;len:sizeint);
|
|
var
|
|
i : sizeint;
|
|
begin
|
|
for i:=1 to len do
|
|
begin
|
|
dest^:=widechar(byte(source^));
|
|
inc(dest);
|
|
inc(source);
|
|
end;
|
|
end;
|
|
|
|
const
|
|
WideStringManager: TWideStringManager = (
|
|
Wide2AnsiMove: @SimpleWide2AnsiMove;
|
|
Ansi2WideMove: @SimpleAnsi2WideMove
|
|
);
|
|
|
|
{$ENDIF}
|
|
|
|
procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
|
|
var
|
|
fs: TFileStream;
|
|
begin
|
|
// write first to memory buffer and then as one whole block to file
|
|
WriteXMLMemStream(doc);
|
|
try
|
|
fs := TFileStream.Create(UTF8ToSys(AFileName), fmCreate);
|
|
fs.CopyFrom(Stream,Stream.Size);
|
|
fs.Free;
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
var
|
|
OldWideStringManager: TWideStringManager;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF UsesFPCWidestrings}
|
|
SetWideStringManager(WideStringManager, OldWideStringManager);
|
|
try
|
|
{$ENDIF}
|
|
f := @AFile;
|
|
wrt := @Text_Write;
|
|
wrtln := @Text_WriteLn;
|
|
RootWriter(doc);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
finally
|
|
SetWideStringManager(OldWideStringManager);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
var
|
|
OldWideStringManager: TWideStringManager;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF UsesFPCWidestrings}
|
|
SetWideStringManager(WideStringManager, OldWideStringManager);
|
|
try
|
|
{$ENDIF}
|
|
Stream := AStream;
|
|
wrt := @Stream_Write;
|
|
wrtln := @Stream_WriteLn;
|
|
RootWriter(doc);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
finally
|
|
SetWideStringManager(OldWideStringManager);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
procedure WriteXML(Element: TDOMNode; const AFileName: String);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
var
|
|
OldWideStringManager: TWideStringManager;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF UsesFPCWidestrings}
|
|
SetWideStringManager(WideStringManager, OldWideStringManager);
|
|
try
|
|
{$ENDIF}
|
|
Stream := TFileStream.Create(UTF8ToSys(AFileName), fmCreate);
|
|
wrt := @Stream_Write;
|
|
wrtln := @Stream_WriteLn;
|
|
InitWriter;
|
|
WriteNode(Element);
|
|
Stream.Free;
|
|
{$IFDEF UsesFPCWidestrings}
|
|
finally
|
|
SetWideStringManager(OldWideStringManager);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure WriteXML(Element: TDOMNode; var AFile: Text);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
var
|
|
OldWideStringManager: TWideStringManager;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF UsesFPCWidestrings}
|
|
SetWideStringManager(WideStringManager, OldWideStringManager);
|
|
try
|
|
{$ENDIF}
|
|
f := @AFile;
|
|
wrt := @Text_Write;
|
|
wrtln := @Text_WriteLn;
|
|
InitWriter;
|
|
WriteNode(Element);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
finally
|
|
SetWideStringManager(OldWideStringManager);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure WriteXML(Element: TDOMNode; AStream: TStream);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
var
|
|
OldWideStringManager: TWideStringManager;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF UsesFPCWidestrings}
|
|
SetWideStringManager(WideStringManager, OldWideStringManager);
|
|
try
|
|
{$ENDIF}
|
|
stream := AStream;
|
|
wrt := @Stream_Write;
|
|
wrtln := @Stream_WriteLn;
|
|
InitWriter;
|
|
WriteNode(Element);
|
|
{$IFDEF UsesFPCWidestrings}
|
|
finally
|
|
SetWideStringManager(OldWideStringManager);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
end.
|