2011-06-18 15:51:22 +00:00
unit SpkXMLParser;
2011-06-18 16:17:03 +00:00
{$mode Delphi}
2011-06-18 15:51:22 +00:00
{$DEFINE SPKXMLPARSER}
2018-02-05 12:05:26 +00:00
{$WARN 4055 off : Conversion between ordinals and pointers is not portable}
2011-06-18 15:51:22 +00:00
interface
2018-02-12 00:15:47 +00:00
{TODO Organize the visibility and virtuality of methods and properties}
2011-06-18 15:51:22 +00:00
// Notatki: Stosuj� konsekwentnie case-insensitivity
2018-02-12 00:15:47 +00:00
// Notes: I apply case-insensitivity consistently
2011-06-18 15:51:22 +00:00
2011-06-18 16:17:03 +00:00
uses
SysUtils, Classes, ContNrs, Graphics, Math;
2011-06-18 15:51:22 +00:00
2011-06-18 16:17:03 +00:00
//todo: use LineEnding?
2011-06-18 15:51:22 +00:00
const CRLF= #13 #10 ;
2018-02-12 00:15:47 +00:00
type // The type of XML nodes
2011-06-18 15:51:22 +00:00
TXMLNodeType = ( xntNormal, xntControl, xntComment) ;
2018-02-12 00:15:47 +00:00
type // Forward declaration for XML nodes
2011-06-18 15:51:22 +00:00
TSpkXMLNode = class ;
TBinaryTreeNode = class ;
2018-02-12 00:15:47 +00:00
// I'm going to binary tree searches
2011-06-18 15:51:22 +00:00
TBinaryTreeNode = class( TObject)
private
2018-02-12 00:15:47 +00:00
// The left subtree
2011-06-18 15:51:22 +00:00
FLeft,
2018-02-12 00:15:47 +00:00
// The right subtree
2011-06-18 15:51:22 +00:00
FRight,
2018-02-12 00:15:47 +00:00
// Parent
2011-06-18 15:51:22 +00:00
FParent : TBinaryTreeNode;
2018-02-12 00:15:47 +00:00
// Data contained in the node
2011-06-18 15:51:22 +00:00
FData : array of TSpkXMLNode;
2018-02-12 00:15:47 +00:00
// The height of the subtree
2011-06-18 15:51:22 +00:00
FSubtreeSize : integer ;
2018-02-12 00:15:47 +00:00
2011-06-18 15:51:22 +00:00
protected
2018-02-12 00:15:47 +00:00
// *** Methods for the tree ***
2011-06-18 15:51:22 +00:00
2018-02-12 00:15:47 +00:00
// Setter for the left subtree
2011-06-18 15:51:22 +00:00
procedure SetLeft( ANode : TBinaryTreeNode) ;
// Setter dla prawego poddrzewa
procedure SetRight( ANode : TBinaryTreeNode) ;
// *** Metody dotycz�ce danych ***
// Getter dla liczby danych zawartych w w��le
function GetCount : integer ;
// Getter dla danych zawartych w w��le
function GetData( index : integer ) : TSpkXMLNode;
public
// Konstruktor
constructor create;
// Destruktor
destructor Destroy; override ;
// *** Metody dotycz�ce drzewa ***
// Wymuszenie od�wie�enia wysoko�ci poddrzewa
procedure RefreshSubtreeSize;
// Metoda powoduje odpi�cie od obecnego parenta (wywo�ywana tylko przez
// niego)
procedure DetachFromParent;
// Metoda powoduje przypi�cie do nowego parenta (wywo�ywana przez nowego
// parenta
procedure AttachToParent( AParent : TBinaryTreeNode) ;
// Metoda wywo�ywana przez jedno z dzieci w momencie, gdy jest ono
// przepinane do innego drzewa
procedure DetachChild( AChild : TBinaryTreeNode) ;
// *** Metody dotycz�ce danych ***
// Dodaje dane
procedure Add( AData : TSpkXMLNode) ;
// Usuwa dane z listy (nie zwalnia!)
procedure Remove( AData : TSpkXMLNode) ;
// Usuwa dane o zadanym indeksie (nie zwalnia!)
procedure Delete( index : integer ) ;
// Usuwa wszystkie dane
procedure Clear;
property Data[ index : integer ] : TSpkXMLNode read GetData;
property Left : TBinaryTreeNode read FLeft write SetLeft;
property Right : TBinaryTreeNode read FRight write SetRight;
property Parent : TBinaryTreeNode read FParent;
property SubtreeSize : integer read FSubtreeSize;
property Count : integer read GetCount;
end ;
// Klasa przechowuj�ca pojedynczy parametr ga��zi XMLowej
TSpkXMLParameter = class( TObject)
private
// Nazwa parametru
FName,
// Warto�� parametru
FValue : string ;
protected
// Getter dla w�asno�ci ValueAsInteger
function GetValueAsInteger : integer ;
// Setter dla w�asno�ci ValueAsInteger
procedure SetValueAsInteger( AValue : integer ) ;
// Getter dla w�asno�ci ValueAsExtended
function GetValueAsExtended : extended ;
// Setter dla w�asno�ci ValueAsExtended
procedure SetValueAsExtended( AValue : extended ) ;
// Getter dla w�asno�ci ValueAsColor
function GetValueAsColor : TColor;
// Setter dla w�asno�ci ValueAsColor
procedure SetValueAsColor( AValue : TColor) ;
// Getter dla w�asno�ci ValueAsBoolean
function GetValueAsBoolean : boolean ;
// Setter dla w�asno�ci ValueAsBoolean
procedure SetValueAsBoolean( AValue : boolean ) ;
public
// Konstruktor
constructor create; overload ;
// Konstruktor pozwalaj�cy nada� pocz�tkowe warto�ci parametrowi
constructor create( AName : string ; AValue : string ) ; overload ;
// Destruktor
destructor Destroy; override ;
property Name : string read FName write FName;
property Value : string read FValue write FValue;
property ValueAsInteger : integer read GetValueAsInteger write SetValueAsInteger;
property ValueAsExtended : extended read GetValueAsExtended write SetValueAsExtended;
property ValueAsColor : TColor read GetValueAsColor write SetValueAsColor;
property ValueAsBoolean : boolean read GetValueAsBoolean write SetValueAsBoolean;
end ;
// Lista parametr�w
TSpkXMLParameters = class( TObject)
private
// Wewn�trzna lista na kt�rej przechowywane s� parametry ga��zi
FList : TObjectList;
protected
// Getter dla w�asno�ci ParamByName (szuka parametru po jego nazwie)
function GetParamByName( index : string ; autocreate : boolean ) : TSpkXMLParameter;
// Getter dla w�asno�ci ParamByIndex (zwraca i-ty parametr)
function GetParamByIndex( index : integer ) : TSpkXMLParameter;
// Zwraca liczb� parametr�w
function GetCount : integer ;
public
// Konstruktor
constructor create;
// Destruktor
destructor Destroy; override ;
// Dodaje parametr na list�
procedure Add( AParameter : TSpkXMLParameter) ;
// Wstawia parametr na list� na zadane miejsce
procedure Insert( AIndex : integer ; AParameter : TSpkXMLParameter) ;
// Usuwa parametr o podanym indeksie z listy
procedure Delete( index : integer ) ;
// Usuwa zadany parametr z listy
procedure Remove( AParameter : TSpkXMLParameter) ;
// Zwraca indeks zadanego parametru
function IndexOf( AParameter : TSpkXMLParameter) : integer ;
// Czy�ci list� parametr�w
procedure Clear;
property ParamByName[ index : string ; autocreate : boolean ] : TSpkXMLParameter read GetParamByName; default ;
property ParamByIndex[ index : integer ] : TSpkXMLParameter read GetParamByIndex;
property Count : integer read GetCount;
end ;
TSpkBaseXmlNode = class ;
// Bazowa klasa dla ga��zi XMLowych, zapewniaj�ca przechowywanie, operacje
// i wyszukiwanie podga��zi.
TSpkBaseXmlNode = class( TObject)
private
FList : TObjectList;
FTree : TBinaryTreeNode;
FParent : TSpkBaseXmlNode;
protected
// *** Operacje na drzewie AVL ***
// Dodaje do drzewa ga��� z zadan� TSpkXMLNode
procedure TreeAdd( ANode : TSpkXMLNode) ;
// Usuwa z drzewa ga��� z zadan� TSpkXMLNode
procedure TreeDelete( ANode : TSpkXMLNode) ;
// Szuka ga��zi drzewa
function TreeFind( ANode : TSpkXMLNode) : TBinaryTreeNode;
// Balansuje wszystkie w�z�y od zadanego do korzenia w��cznie.
procedure Ballance( Leaf : TBinaryTreeNode) ;
// Obraca w�ze� w lewo i zwraca w�ze�, kt�ry znalaz� si� w miejscu
// obr�conego.
function RotateLeft( Root : TBinaryTreeNode) : TBinaryTreeNode;
// Obraca w�ze� w prawo i zwraca w�ze�, kt�ry znalaz� si� w miejscu
// obr�conego
function RotateRight( Root : TBinaryTreeNode) : TBinaryTreeNode;
function GetNodeByIndex( index : integer ) : TSpkXMLNode;
function GetNodeByName( index : string ; autocreate : boolean ) : TSpkXMLNode;
function GetCount : integer ;
public
// Konstruktor
constructor create; virtual ;
// Destruktor
destructor Destroy; override ;
// Dodaje podga��� i umieszcza w odpowiednim miejscu w drzewie
procedure Add( ANode : TSpkXMLNode) ;
// Wstawia podga��� w podane miejsce (na drzewie ma to taki sam efekt
// jak dodanie)
procedure Insert( AIndex : integer ; ANode : TSpkXMLNode) ;
// Usuwa podga��� z listy i z drzewa, a nast�pnie zwalnia pami��
procedure Delete( AIndex : integer ) ;
// Usuwa podga��� z listy i z drzewa, a nast�pnie zwalnia pami��
procedure Remove( ANode : TSpkXMLNode) ;
// Zwraca indeks podga��zi
function IndexOf( ANode : TSpkXMLNode) : integer ;
// Usuwa wszystkie podga��zie
procedure Clear; virtual ;
// Metoda powinna zosta� wywo�ana przed zmian� nazwy przez jedn� z podga��zi
procedure BeforeChildChangeName( AChild : TSpkXmlNode) ;
// Metoda powinna zosta� wywo�ana po zmianie nazwy przez jedn� z podga��zi
procedure AfterChildChangeName( AChild : TSpkXMLNode) ;
property NodeByIndex[ index : integer ] : TSpkXMLNode read GetNodeByIndex;
property NodeByName[ index : string ; autocreate : boolean ] : TSpkXMLNode read GetNodeByName; default ;
property Count : integer read GetCount;
property Parent : TSpkBaseXmlNode read FParent write FParent;
end ;
// Ga��� XMLa. Dzi�ki temu, �e dziedziczymy po TSpkBaseXMLNode mamy
// zapewnion� obs�ug� podga��zi, trzeba tylko doda� parametry, nazw� i
// tekst.
TSpkXMLNode = class( TSpkBaseXMLNode)
private
// Nazwa ga��zi
FName : string ;
// Tekst ga��zi
FText : string ;
// Parametry ga��zi
FParameters : TSpkXMLParameters;
// Rodzaj ga��zi
FNodeType : TXMLNodeType;
protected
// Setter dla w�asno�ci name (przed i po zmianie nazwy trzeba poinformowa�
// parenta, by poprawnie dzia�a�o wyszukiwanie po nazwie
procedure SetName( Value : string ) ;
// Getter dla TextAsInteger
function GetTextAsInteger : integer ;
// Setter dla TextAsInteger
procedure SetTextAsInteger( value : integer ) ;
// Getter dla TextAsExtended
function GetTextAsExtended : extended ;
// Setter dla TextAsExtended
procedure SetTextAsExtended( value : extended ) ;
// Getter dla TextAsColor
function GetTextAsColor : TColor;
// Setter dla TextAsColor
procedure SetTextAsColor( value : TColor) ;
// Getter dla TextAsBoolean
function GetTextAsBoolean : boolean ;
// Setter dla TextAsBoolean
procedure SetTextAsBoolean( value : boolean ) ;
public
// Konstruktor
constructor create( AName : string ; ANodeType : TXMLNodeType) ; reintroduce ;
// Destruktor
destructor Destroy; override ;
// Czy�ci ga��� (tekst, parametry, podga��zie)
procedure Clear; override ;
property Name : string read FName write SetName;
property Text : string read FText write FText;
property TextAsInteger : integer read GetTextAsInteger write SetTextAsInteger;
property TextAsExtended : extended read GetTextAsExtended write SetTextAsExtended;
property TextAsColor : TColor read GetTextAsColor write SetTextAsColor;
property TextAsBoolean : boolean read GetTextAsBoolean write SetTextAsBoolean;
property Parameters : TSpkXMLParameters read FParameters;
property NodeType : TXMLNodeType read FNodeType;
end ;
// Dzi�ki temu, �e dziedziczymy po TSpkBaseXMLNode, mamy zapewnion� obs�ug�
// podga��zi
TSpkXMLParser = class( TSpkBaseXMLNode)
private
protected
public
// Konstruktor
constructor create; override ;
// Destruktor
destructor Destroy; override ;
// Przetwarza tekst z XMLem podany jako parametr
procedure Parse( input : PChar ) ;
// Generuje XML na podstawie zawarto�ci komponentu
function Generate( UseFormatting : boolean = true ) : string ;
// Wczytuje plik XML z dysku
procedure LoadFromFile( AFile : string ) ;
// Zapisuje plik XML na dysk
procedure SaveToFile( AFile : string ; UseFormatting : boolean = true ) ;
// Wczytuje plik XML ze strumienia
procedure LoadFromStream( AStream : TStream) ;
// Zapisuje plik XML do strumienia
procedure SaveToStream( AStream : TStream; UseFormatting : boolean = true ) ;
end ;
implementation
{ TBinaryTreeNode }
procedure TBinaryTreeNode. SetLeft( ANode : TBinaryTreeNode) ;
begin
// Odpinamy poprzedni� lew� ga��� (o ile istnia�a)
if FLeft< > nil then
begin
FLeft. DetachFromParent;
FLeft: = nil ;
end ;
// Przypinamy now� ga���
FLeft: = ANode;
// Aktualizujemy jej parenta
if FLeft< > nil then
FLeft. AttachToParent( self) ;
// Od�wie�amy wysoko�� poddrzewa
RefreshSubtreeSize;
end ;
procedure TBinaryTreeNode. SetRight( ANode : TBinaryTreeNode) ;
begin
// Odpinamy poprzedni� praw� ga��� (o ile istnia�a)
if FRight< > nil then
begin
FRight. DetachFromParent;
FRight: = nil ;
end ;
// Przypinamy now� ga���
FRight: = ANode;
// Aktualizujemy jej parnenta
if FRight< > nil then
FRight. AttachToParent( self) ;
// Od�wie�amy wysoko�� poddrzewa
RefreshSubtreeSize;
end ;
function TBinaryTreeNode. GetCount : integer ;
begin
result : = length( FData) ;
end ;
function TBinaryTreeNode. GetData( index : integer ) : TSpkXMLNode;
begin
if ( index< 0 ) or ( index > high( FData) ) then
raise exception. create( 'Nieprawid�owy indeks!' ) ;
result : = FData[ index ] ;
end ;
constructor TBinaryTreeNode. create;
begin
inherited create;
FLeft: = nil ;
FRight: = nil ;
FParent: = nil ;
setlength( FData, 0 ) ;
FSubtreeSize: = 0 ;
end ;
destructor TBinaryTreeNode. destroy;
begin
// Odpinamy si� od parenta
if FParent< > nil then
FParent. DetachChild( self) ;
// Zwalniamy poddrzewa
if FLeft< > nil then
FLeft. free;
if FRight< > nil then
FRight. free;
inherited destroy;
end ;
procedure TBinaryTreeNode. RefreshSubtreeSize;
function LeftSubtreeSize : integer ;
begin
if FLeft= nil then result : = 0 else result : = 1 + FLeft. SubTreeSize;
end ;
function RightSubtreeSize : integer ;
begin
if FRight= nil then result : = 0 else result : = 1 + FRight. SubTreeSize;
end ;
begin
FSubtreeSize: = max( LeftSubtreeSize, RightSubtreeSize) ;
if Parent< > nil then
Parent. RefreshSubtreeSize;
end ;
2018-02-12 00:15:47 +00:00
// According to the assumptions, this method can only be called the current parent.
2011-06-18 15:51:22 +00:00
procedure TBinaryTreeNode. DetachFromParent;
begin
2018-02-12 00:15:47 +00:00
FParent : = nil ;
2011-06-18 15:51:22 +00:00
end ;
2018-02-12 00:15:47 +00:00
// According to the assumptions, this method is called by the new parent
// of the element. The element must take care to inform the previous parent
// about the fact that he is removable.
2011-06-18 15:51:22 +00:00
procedure TBinaryTreeNode. AttachToParent( AParent : TBinaryTreeNode) ;
begin
2018-02-12 00:15:47 +00:00
if AParent< > FParent then
begin
if FParent< > nil then
2011-06-18 15:51:22 +00:00
FParent. DetachChild( self) ;
2018-02-12 00:15:47 +00:00
FParent : = AParent;
end ;
2011-06-18 15:51:22 +00:00
end ;
procedure TBinaryTreeNode. DetachChild( AChild : TBinaryTreeNode) ;
begin
// Zgodnie z za�o�eniami, metod� t� mo�e wywo�a� tylko jeden z podelement�w
// - lewy lub prawy, podczas zmiany parenta.
if AChild= FLeft then FLeft: = nil ;
if AChild= FRight then FRight: = nil ;
// Przeliczamy ponownie wysoko�� poddrzewa
RefreshSubtreeSize;
end ;
procedure TBinaryTreeNode. Add( AData : TSpkXMLNode) ;
begin
2018-02-12 00:15:47 +00:00
{$B-}
if ( Length( FData) = 0 ) or ( ( Length( FData) > 0 ) and ( Uppercase( FData[ 0 ] . Name ) = Uppercase( AData. Name ) ) ) then
begin
SetLength( FData, Length( FData) + 1 ) ;
FData[ High( FData) ] : = AData;
end else
raise Exception. Create( 'A single node stores data with identical names!' ) ;
2011-06-18 15:51:22 +00:00
end ;
procedure TBinaryTreeNode. Remove( AData : TSpkXMLNode) ;
var i : integer ;
begin
i: = 0 ;
{$B-}
while ( i< = high( FData) ) and ( FData[ i] < > AData) do
inc( i) ;
if i< high( FData) then
self. Delete( i) ;
end ;
procedure TBinaryTreeNode. Delete( index : integer ) ;
var i : integer ;
begin
if ( index< 0 ) or ( index > high( FData) ) then
raise exception. create( 'Nieprawid�owy indeks.' ) ;
if index< high( FData) then
for i: = index to high( FData) - 1 do
FData[ i] : = FData[ i+ 1 ] ;
setlength( FData, length( FData) - 1 ) ;
end ;
procedure TBinaryTreeNode. Clear;
begin
setlength( FData, 0 ) ;
end ;
{ TSpkXMLParameter }
constructor TSpkXMLParameter. create;
begin
inherited create;
FName: = '' ;
FValue: = '' ;
end ;
constructor TSpkXMLParameter. create( AName, AValue: string ) ;
begin
inherited create;
FName: = AName;
FValue: = AValue;
end ;
destructor TSpkXMLParameter. destroy;
begin
inherited destroy;
end ;
function TSpkXMLParameter. GetValueAsBoolean: boolean ;
begin
if ( uppercase( FValue) = 'TRUE' ) or ( uppercase( FValue) = 'T' ) or
( uppercase( FValue) = 'YES' ) or ( uppercase( FValue) = 'Y' ) then result : = true else
if ( uppercase( FValue) = 'FALSE' ) or ( uppercase( FValue) = 'F' ) or
( uppercase( FValue) = 'NO' ) or ( uppercase( FValue) = 'N' ) then result : = false else
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
function TSpkXMLParameter. GetValueAsColor: TColor;
begin
try
result : = StrToInt( FValue) ;
except
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
end ;
function TSpkXMLParameter. GetValueAsExtended: extended ;
begin
try
result : = StrToFloat( FValue) ;
except
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
end ;
function TSpkXMLParameter. GetValueAsInteger: integer ;
begin
try
result : = StrToInt( FValue) ;
except
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
end ;
procedure TSpkXMLParameter. SetValueAsBoolean( AValue: boolean ) ;
begin
if AValue then FValue: = 'True' else FValue: = 'False' ;
end ;
procedure TSpkXMLParameter. SetValueAsColor( AValue: TColor) ;
begin
FValue: = IntToStr( AValue) ;
end ;
procedure TSpkXMLParameter. SetValueAsExtended( AValue: extended ) ;
begin
FValue: = FloatToStr( AValue) ;
end ;
procedure TSpkXMLParameter. SetValueAsInteger( AValue: integer ) ;
begin
FValue: = IntToStr( AValue) ;
end ;
{ TSpkXMLParameters }
procedure TSpkXMLParameters. Add( AParameter: TSpkXMLParameter) ;
begin
FList. add( AParameter) ;
end ;
procedure TSpkXMLParameters. Insert( AIndex : integer ; AParameter : TSpkXMLParameter) ;
begin
if ( AIndex< 0 ) or ( AIndex> FList. count- 1 ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Invalid index.' ) ;
2011-06-18 15:51:22 +00:00
FList. Insert( AIndex, AParameter) ;
end ;
procedure TSpkXMLParameters. Clear;
begin
FList. clear;
end ;
constructor TSpkXMLParameters. create;
begin
inherited create;
FList: = TObjectList. create;
FList. OwnsObjects: = true ;
end ;
procedure TSpkXMLParameters. Delete( index : integer ) ;
begin
if ( index< 0 ) or ( index > FList. count- 1 ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Invalid parameter index.' ) ;
2011-06-18 15:51:22 +00:00
FList. delete( index ) ;
end ;
procedure TSpkXMLParameters. Remove( AParameter : TSpkXMLParameter) ;
begin
FList. Remove( AParameter) ;
end ;
destructor TSpkXMLParameters. destroy;
begin
FList. Free;
inherited destroy;
end ;
function TSpkXMLParameters. GetCount: integer ;
begin
result : = FList. count;
end ;
function TSpkXMLParameters. GetParamByIndex( index : integer ) : TSpkXMLParameter;
begin
if ( index< 0 ) or ( index > Flist. count- 1 ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Invalid item index.' ) ;
2011-06-18 15:51:22 +00:00
result : = TSpkXMLParameter( FList[ index ] ) ;
end ;
function TSpkXMLParameters. GetParamByName( index : string ;
autocreate: boolean ) : TSpkXMLParameter;
var i : integer ;
AParameter : TSpkXMLParameter;
begin
// Szukamy elementu
i: = 0 ;
while ( i< = FList. count- 1 ) and ( uppercase( TSpkXMLParameter( FList[ i] ) . Name ) < > uppercase( index ) ) do inc( i) ;
if i< = FList. count- 1 then
result : = TSpkXMLParameter( FList[ i] ) else
begin
if autocreate then
begin
AParameter: = TSpkXMLParameter. create( index , '' ) ;
FList. add( AParameter) ;
result : = AParameter;
end else
result : = nil ;
end ;
end ;
function TSpkXMLParameters. IndexOf( AParameter: TSpkXMLParameter) : integer ;
begin
result : = FList. IndexOf( AParameter) ;
end ;
{ TSpkBaseXMLNode }
procedure TSpkBaseXMLNode. TreeAdd( ANode : TSpkXMLNode) ;
var Tree, Parent : TBinaryTreeNode;
begin
// Szukam miejsca do dodania nowej ga��zi drzewa
if Ftree= nil then
begin
// Nie mamy czego szuka�, tworzymy korze�
FTree: = TBinaryTreeNode. create;
FTree. Add( ANode) ;
// Nie ma potrzeby balansowania drzewa
end else
begin
Tree: = FTree;
Parent: = nil ;
{$B-}
while ( Tree< > nil ) and ( uppercase( Tree. Data[ 0 ] . Name ) < > uppercase( ANode. Name ) ) do
begin
Parent: = Tree;
if uppercase( ANode. Name ) < uppercase( Tree. Data[ 0 ] . Name ) then Tree: = Tree. Left else Tree: = Tree. Right;
end ;
if Tree< > nil then
begin
// Znalaz�em ga��� z takim samym identyfikatorem
Tree. Add( ANode) ;
// Nie ma potrzeby balansowania drzewa, bo faktycznie nie zosta�a
// dodana �adna ga���
end else
begin
Tree: = TBinaryTreeNode. create;
Tree. Add( ANode) ;
if uppercase( ANode. Name ) < uppercase( Parent. Data[ 0 ] . Name ) then
Parent. Left: = Tree else
Parent. Right: = Tree;
// Zosta�a dodana nowa ga���, wi�c balansujemy drzewo (o ile jest
// taka potrzeba)
self. Ballance( Tree) ;
end ;
end ;
end ;
procedure TSpkBaseXMLNode. TreeDelete( ANode : TSpkXMLNode) ;
procedure InternalTreeDelete( DelNode : TBinaryTreeNode) ;
var DelParent : TBinaryTreeNode;
Successor : TBinaryTreeNode;
SuccessorParent : TBinaryTreeNode;
DeletingRoot : boolean ;
i : integer ;
begin
// Najpierw sprawdzamy, czy b�dziemy usuwa� korze�. Je�li tak, po usuni�ciu
// mo�e by� potrzebna aktualizacja korzenia.
DeletingRoot: = DelNode= FTree;
// Kilka przypadk�w.
// 0. Mo�e elementu nie ma w drzewku?
if DelNode= nil then
2018-02-12 00:15:47 +00:00
raise exception. create( 'There is no such element in the AVL tree!' ) else
2011-06-18 15:51:22 +00:00
// 1. Je�li ga��� ta przechowuje wi�cej ni� tylko ten element, to usuwamy go
// z listy i ko�czymy dzia�anie.
if DelNode. Count> 1 then
begin
i: = 0 ;
while ( i< DelNode. Count) and ( DelNode. Data[ i] < > ANode) do inc( i) ;
DelNode. Delete( i) ;
end else
// 2. Je�li jest to li��, po prostu usuwamy go.
if ( DelNode. Left= nil ) and ( DelNode. Right= nil ) then
begin
DelParent: = DelNode. Parent;
// Odpinamy od parenta
if DelParent< > nil then
begin
if DelParent. Left= DelNode then DelParent. Left: = nil ;
if DelParent. Right= DelNode then DelParent. Right: = nil ;
end ;
// Ga��� automatycznie odpina wszystkie swoje podga��zie, ale zak�adamy
// tu, �e jest to li��.
DelNode. free;
// Je�li zachodzi taka potrzeba, balansujemy drzewo od ojca usuwanego
// elementu
if DelParent< > nil then
self. Ballance( DelParent) ;
// Je�li usuwali�my root, ustawiamy go na nil (bo by� to jedyny element)
if DeletingRoot then FTree: = nil ;
end else
// 3. Je�eli element ma tylko jedno dziecko, usuwamy je, poprawiamy powi�zania
// i balansujemy drzewo
if ( DelNode. Left= nil ) xor ( DelNode. Right= nil ) then
begin
DelParent: = DelNode. Parent;
if DelParent= nil then
begin
// Usuwamy korze�
if DelNode. Left< > nil then
begin
FTree: = DelNode. Left;
// Mechanizmy drzewa odepn� automatycznie ga��� od DelNode, dzi�ki
// czemu nie zostanie usuni�te ca�e poddrzewo
end else
if DelNode. Right< > nil then
begin
FTree: = DelNode. Right;
// Mechanizmy drzewa odepn� automatycznie ga��� od DelNode, dzi�ki
// czemu nie zostanie usuni�te ca�e poddrzewo
end ;
// Usuwamy element
DelNode. Free;
// Nie ma potrzeby balansowa� drzewa, z za�o�enie poddrzewo jest
// zbalansowane.
end else
if DelParent< > nil then
begin
// Cztery przypadki
if DelParent. Left= DelNode then
begin
if DelNode. Left< > nil then
begin
DelParent. Left: = DelNode. Left;
end else
if DelNode. Right< > nil then
begin
DelParent. Left: = DelNode. Right;
end ;
end else
if DelParent. Right= DelNode then
begin
if DelNode. Left< > nil then
begin
DelParent. Right: = DelNode. Left;
end else
if DelNode. Right< > nil then
begin
DelParent. Right: = DelNode. Right;
end ;
end ;
DelNode. Free;
self. Ballance( DelParent) ;
end ;
end else
// 4. Zamieniamy zawarto�� "usuwanego" poddrzewa z jego nast�pnikiem, kt�ry
// ma tylko jedno dziecko, a nast�pnie usuwamy nast�pnik.
if ( DelNode. Left< > nil ) and ( DelNode. Right< > nil ) then
begin
// Szukamy nast�pnika
Successor: = DelNode. Right;
while Successor. Left< > nil do Successor: = Successor. Left;
SuccessorParent: = Successor. Parent;
// Przepinamy dane z nast�pnika do "usuwanego" elementu
DelNode. Clear;
if Successor. Count> 0 then
for i: = 0 to Successor. Count- 1 do
begin
DelNode. Add( Successor. Data[ i] ) ;
end ;
// Teraz usuwamy nast�pnik
InternalTreeDelete( Successor) ;
// Od�wie�amy dane dotycz�ce poddrzew
self. Ballance( SuccessorParent) ;
end ;
end ;
begin
InternalTreeDelete( self. TreeFind( ANode) ) ;
end ;
function TSpkBaseXMLNode. TreeFind( ANode : TSpkXMLNode) : TBinaryTreeNode;
var Tree : TBinaryTreeNode;
i : integer ;
begin
Tree: = FTree;
while ( Tree< > nil ) and ( uppercase( Tree. Data[ 0 ] . Name ) < > uppercase( ANode. Name ) ) do
begin
if uppercase( ANode. Name ) < uppercase( Tree. Data[ 0 ] . Name ) then
Tree: = Tree. Left else
Tree: = Tree. Right;
end ;
if Tree< > nil then
begin
i: = 0 ;
{$B-}
while ( i< Tree. Count) and ( Tree. Data[ i] < > ANode) do inc( i) ;
if i= Tree. Count then result : = nil else result : = Tree;
end else result : = nil ;
end ;
procedure TSpkBaseXMLNode. Ballance( Leaf : TBinaryTreeNode) ;
function CalcLeft( Node : TBinaryTreeNode) : integer ;
begin
if Node. Left= nil then result : = 0 else result : = 1 + Node. Left. SubtreeSize;
end ;
function CalcRight( Node : TBinaryTreeNode) : integer ;
begin
if Node. Right= nil then result : = 0 else result : = 1 + Node. Right. SubtreeSize;
end ;
begin
if Leaf< > nil then
begin
while CalcLeft( Leaf) - CalcRight( Leaf) > = 2 do
Leaf: = RotateRight( Leaf) ;
while CalcRight( Leaf) - CalcLeft( Leaf) > = 2 do
Leaf: = RotateLeft( Leaf) ;
self. Ballance( Leaf. Parent) ;
end ;
end ;
{ RootParent
\ / \ /
1 Root 2
/ \ / \
A 2 RotNode ~ > 1 C
/ \ / \
B C A B
}
function TSpkBaseXMLNode. RotateLeft( Root : TBinaryTreeNode) : TBinaryTreeNode;
var RootParent : TBinaryTreeNode;
RotNode : TBinaryTreeNode;
begin
result : = nil ;
if Root. Right= nil then
raise exception. create( 'Prawa podga��� jest pusta!' ) ;
RootParent: = Root. Parent;
RotNode: = Root. Right;
if RootParent< > nil then
begin
if Root= RootParent. Left then
begin
Root. Right: = RotNode. Left;
RotNode. Left: = Root;
RootParent. Left: = RotNode;
result : = RotNode;
end else
if Root= RootParent. Right then
begin
Root. Right: = RotNode. Left;
RotNode. Left: = Root;
RootParent. Right: = RotNode;
result : = RotNode;
end ;
end else
if RootParent= nil then
begin
// Obracamy korze�
Root. Right: = RotNode. Left;
RotNode. Left: = Root;
FTree: = RotNode;
result : = RotNode;
end ;
end ;
{ RootParent
\ / \ /
Root 1 2
/ \ / \
RotNode 2 C ~ > A 1
/ \ / \
A B B C
}
function TSpkBaseXMLNode. RotateRight( Root : TBinaryTreeNode) : TBinaryTreeNode;
var RootParent : TBinaryTreeNode;
RotNode : TBinaryTreeNode;
begin
result : = nil ;
if Root. Left= nil then
raise exception. create( 'Lewa podga��� jest pusta!' ) ;
RootParent: = Root. Parent;
RotNode: = Root. Left;
if RootParent< > nil then
begin
if Root= RootParent. Left then
begin
Root. Left: = RotNode. Right;
RotNode. Right: = Root;
RootParent. Left: = RotNode;
result : = RotNode;
end else
if Root= RootParent. Right then
begin
Root. Left: = RotNode. Right;
RotNode. Right: = Root;
RootParent. Right: = RotNode;
result : = RotNode;
end ;
end else
if RootParent= nil then
begin
// Obracamy korze�
Root. Left: = RotNode. Right;
RotNode. Right: = Root;
FTree: = RotNode;
result : = RotNode;
end ;
end ;
function TSpkBaseXMLNode. GetNodeByIndex( index : integer ) : TSpkXMLNode;
begin
if ( index< 0 ) or ( index > FList. count- 1 ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Invalid index!' ) ;
2011-06-18 15:51:22 +00:00
result : = TSpkXMLNode( FList[ index ] ) ;
end ;
function TSpkBaseXMLNode. GetNodeByName( index : string ; autocreate : boolean ) : TSpkXMLNode;
var Tree : TBinaryTreeNode;
XmlNode : TSpkXMLNode;
begin
Tree: = FTree;
{$B-}
while ( Tree< > nil ) and ( uppercase( Tree. Data[ 0 ] . Name ) < > uppercase( index ) ) do
begin
if uppercase( index ) < uppercase( Tree. Data[ 0 ] . Name ) then
Tree: = Tree. Left else
Tree: = Tree. Right;
end ;
if Tree< > nil then result : = Tree. Data[ 0 ] else
begin
if not( autocreate) then
result : = nil else
begin
XmlNode: = TSpkXMLNode. create( index , xntNormal) ;
TreeAdd( XmlNode) ;
FList. add( XmlNode) ;
result : = XmlNode;
end ;
end ;
end ;
function TSpkBaseXMLNode. GetCount : integer ;
begin
result : = FList. Count;
end ;
constructor TSpkBaseXMLNode. create;
begin
inherited create;
FList: = TObjectList. create;
FList. OwnsObjects: = true ;
FTree: = nil ;
FParent: = nil ;
end ;
destructor TSpkBaseXMLNode. destroy;
begin
// Drzewko zadba o rekurencyjne wyczyszczenie
FTree. free;
// Lista zadba o zwolnienie podga��zi
FList. free;
inherited destroy;
end ;
procedure TSpkBaseXMLNode. Add( ANode : TSpkXMLNode) ;
begin
2018-02-12 00:15:47 +00:00
if ANode = self then
raise Exception. Create( 'Cannot add Self to list!' ) ;
if ANode. NodeType = xntNormal then
TreeAdd( ANode) ;
FList. add( ANode) ;
ANode. Parent : = self;
2011-06-18 15:51:22 +00:00
end ;
procedure TSpkBaseXMLNode. Insert( AIndex : integer ; ANode : TSpkXMLNode) ;
begin
if ( AIndex< 0 ) or ( AIndex> FList. count- 1 ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Invalid index!' ) ;
2011-06-18 15:51:22 +00:00
FList. Insert( AIndex, ANode) ;
TreeAdd( ANode) ;
ANode. Parent: = self;
end ;
procedure TSpkBaseXMLNode. Delete( AIndex : integer ) ;
begin
if ( AIndex< 0 ) or ( AIndex> FList. count- 1 ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Invalid index' ) ;
2011-06-18 15:51:22 +00:00
TreeDelete( TSpkXMLNode( FList[ AIndex] ) ) ;
// Poniewa� FList.OwnsObjects, automatycznie zwolni usuwany element.
FList. delete( AIndex) ;
end ;
procedure TSpkBaseXMLNode. Remove( ANode : TSpkXMLNode) ;
begin
TreeDelete( ANode) ;
// Poniewa� FList.OwnsObjects, automatycznie zwolni usuwany element.
FList. Remove( ANode) ;
end ;
function TSpkBaseXMLNode. IndexOf( ANode : TSpkXMLNode) : integer ;
begin
result : = FList. IndexOf( ANode) ;
end ;
procedure TSpkBaseXMLNode. Clear;
begin
FTree. Free;
FTree: = nil ;
// Poniewa� FList.OwnsObjects, automatycznie zwolni usuwany element.
FList. clear;
end ;
procedure TSpkBaseXMLNode. BeforeChildChangeName( AChild : TSpkXmlNode) ;
begin
TreeDelete( AChild) ;
end ;
procedure TSpkBaseXMLNode. AfterChildChangeName( AChild : TSpkXMLNode) ;
begin
TreeAdd( AChild) ;
end ;
{ TSpkXMLNode }
procedure TSpkXMLNode. SetName( Value : string ) ;
begin
if Parent< > nil then
Parent. BeforeChildChangeName( self) ;
FName: = Value;
if Parent< > nil then
Parent. AfterChildChangeName( self) ;
end ;
function TSpkXMLNode. GetTextAsInteger : integer ;
begin
try
result : = StrToInt( FText) ;
except
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
end ;
procedure TSpkXMLNode. SetTextAsInteger( value : integer ) ;
begin
FText: = IntToStr( value) ;
end ;
function TSpkXMLNode. GetTextAsExtended : extended ;
begin
try
result : = StrToFloat( FText) ;
except
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
end ;
procedure TSpkXMLNode. SetTextAsExtended( value : extended ) ;
begin
FText: = FloatToStr( value) ;
end ;
function TSpkXMLNode. GetTextAsColor : TColor;
begin
try
result : = StrToInt( FText) ;
except
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
end ;
procedure TSpkXMLNode. SetTextAsColor( value : TColor) ;
begin
FText: = IntToStr( value) ;
end ;
function TSpkXMLNode. GetTextAsBoolean : boolean ;
begin
if ( uppercase( FText) = 'TRUE' ) or ( uppercase( FText) = 'T' ) or
( uppercase( FText) = 'YES' ) or ( uppercase( FText) = 'Y' ) then result : = true else
if ( uppercase( FText) = 'FALSE' ) or ( uppercase( FText) = 'F' ) or
( uppercase( FText) = 'NO' ) or ( uppercase( FText) = 'N' ) then result : = false else
2018-02-12 00:15:47 +00:00
raise exception. create( 'Cannot convert values.' ) ;
2011-06-18 15:51:22 +00:00
end ;
procedure TSpkXMLNode. SetTextAsBoolean( value : boolean ) ;
begin
if value then FText: = 'True' else FText: = 'False' ;
end ;
constructor TSpkXMLNode. create( AName : string ; ANodeType : TXMLNodeType) ;
begin
inherited create;
FName: = AName;
FText: = '' ;
FNodeType: = ANodeType;
FParameters: = TSpkXMLParameters. create;
end ;
destructor TSpkXMLNode. destroy;
begin
FParameters. free;
inherited destroy;
end ;
procedure TSpkXMLNode. Clear;
begin
inherited Clear;
FParameters. Clear;
FText: = '' ;
end ;
{ TSpkXMLParser }
constructor TSpkXMLParser. create;
begin
inherited create;
end ;
destructor TSpkXMLParser. destroy;
begin
inherited destroy;
end ;
procedure TSpkXMLParser. Parse( input : PChar ) ;
type // Operacja, kt�r� aktualnie wykonuje parser.
TParseOperation = ( poNodes, //< Przetwarzanie (pod)ga��zi
poTagInterior, //< Przetwarzanie wn�trza zwyk�ego tagu (< > lub < />)
poTagText, //< Tekst taga, kt�ry przetwarzamy
poControlInterior, //< Przetwarzanie kontrolnego taga (<? ?>)
poCommentInterior, //< Przetwarzanie komentarza (<!-- -->)
poClosingInterior //< Przetwarzanie taga domykaj�cego.
) ;
var // Stos przetwarzanych ga��zi (niejawna rekurencja)
NodeStack : TObjectStack;
// Aktualna operacja. Podczas wychodzenia z operacji przetwarzaj�cych
// tagi, domy�lnymi operacjami s� poSubNodes b�d� poOuter.
CurrentOperation : TParseOperation;
// Wska�nik na pocz�tek tokena
TokenStart : PChar ;
// Przetwarzana ga��� XMLa
Node : TSpkXMLNode;
// Pomocnicze ci�gi znak�w
s, s1 : string ;
// Pozycja w pliku - linia i znak
ParseLine, ParseChar : integer ;
// Funkcja inkrementuje wska�nik wej�cia, pilnuj�c jednocze�nie, by uaktualni�
// pozycj� w pliku
procedure increment( var input : PChar ; count : integer = 1 ) ;
var i : integer ;
begin
for i: = 1 to count do
begin
if input^ = #10 then
begin
inc( ParseLine) ;
ParseChar: = 1 ;
end else
if input^ < > #13 then
begin
inc( ParseChar) ;
end ;
inc( input) ;
end ;
end ;
// Funkcja przetwarza tekst (wraz z <![CDATA[ ... ]]>) a� do napotkanego
// delimitera. Dodatkowo zamienia encje na zwyk�e znaki.
// Niestety, natura poni�szej funkcji powoduje, �e musz� dokleja� znaki
// do ci�gu, trac�c na wydajno�ci.
// DoTrim powoduje, �e wycinane s� pocz�tkowe i ko�cowe bia�e znaki (chyba,
// �e zosta�y wpisane jako encje albo w sekcji CDATA)
function ParseText( var input : PChar ; TextDelimiter : char ; DoTrim : boolean = false ) : string ;
var Finish : boolean ;
Entity : string ;
i : integer ;
WhiteChars : string ;
// Funkcja robi dok�adnie to, na co wygl�da ;]
function HexToInt( s : string ) : integer ;
var i : integer ;
begin
result : = 0 ;
for i: = 1 to length( s) do
begin
result : = result * 1 6 ;
if s[ i] in [ '0' .. '9' ] then result : = result + ord( s[ i] ) - ord( '0' ) else
if UpCase( s[ i] ) in [ 'A' .. 'F' ] then result : = result + ord( s[ i] ) - ord( 'A' ) + 1 0 else
raise exception. create( 'Nieprawid�owa liczba heksadecymalna!' ) ;
end ;
end ;
begin
result : = '' ;
// Wycinamy pocz�tkowe bia�e znaki
if DoTrim then
while input^ in [ #32 , #9 , #13 , #10 ] do increment( input) ;
while ( input^ < > TextDelimiter) or ( ( input^ = '<' ) and ( StrLComp( input, '<![CDATA[' , 9 ) = 0 ) ) do
begin
{$B-}
// Nie mo�e wyst�pi� tu koniec pliku
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of file.' ) else
2011-06-18 15:51:22 +00:00
// Je�li napotkali�my nawias k�towy, mo�e to by� sekcja CDATA
if ( input^ = '<' ) and ( StrLComp( input, '<![CDATA[' , 9 ) = 0 ) then
begin
// Wczytujemy blok CDATA a� do znacznika zamkni�cia "]]>"
// Pomijamy tag rozpoczynaj�cy CDATA
increment( input, 9 ) ;
Finish: = false ;
repeat
{$B-}
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of file.' ) ;
2011-06-18 15:51:22 +00:00
if ( input^ = ']' ) and ( StrLComp( input, ']]>' , 3 ) = 0 ) then Finish: = true else
begin
result : = result + input^ ;
increment( input) ;
end ;
until Finish;
// Pomijamy tag zamykaj�cy CDATA
increment( input, 3 ) ;
end else
// Obs�uga encji - np.
if input^ = '&' then
begin
// Encja
// Pomijamy znak ampersanda
increment( input) ;
Entity: = '' ;
while input^ < > ';' do
begin
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise Exception. Create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of file - entity not finished.' ) ;
2011-06-18 15:51:22 +00:00
Entity: = Entity+ input^ ;
increment( input) ;
end ;
// Pomijamy znak �rednika
increment( input) ;
// Analizujemy encj�
Entity: = uppercase( entity) ;
if Entity= 'AMP' then result : = result + '&' else
if Entity= 'LT' then result : = result + '<' else
if Entity= 'GT' then result : = result + '>' else
if Entity= 'QUOT' then result : = result + '"' else
if Entity= 'NBSP' then result : = result + ' ' else
if copy( Entity, 1 , 2 ) = '#x' then
begin
// Kod ASCII zapisany heksadecymalnie
i: = HexToInt( copy( Entity, 2 , length( Entity) - 1 ) ) ;
if not( i in [ 0 .. 2 5 5 ] ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid hexadecimal value of the entity (allowed: 0..255)' ) ;
2011-06-18 15:51:22 +00:00
result : = result + chr( i) ;
end else
if Entity[ 1 ] = '#' then
begin
i: = StrToInt( copy( Entity, 2 , length( Entity) - 1 ) ) ;
if not( i in [ 0 .. 2 5 5 ] ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid entity decimal value (acceptable: 0..255)' ) ;
2011-06-18 15:51:22 +00:00
result : = result + chr( i) ;
end else
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid (not supported) entity!' ) ;
2011-06-18 15:51:22 +00:00
end else
if ( DoTrim) and ( input^ in [ #32 , #9 , #10 , #13 ] ) then
begin
// Zbieramy bia�e znaki a� do pierwszego niebia�ego; je�eli b�dzie
// nim delimiter, bia�a sekwencja zostanie pomini�ta.
WhiteChars: = '' ;
repeat
WhiteChars: = input^ ;
increment( input) ;
until not( input^ in [ #32 , #9 , #10 , #13 ] ) ;
// Sprawdzamy, czy doda� sekwencj� bia�ych znak�w (ostro�nie z CDATA!)
if ( input^ < > TextDelimiter) or ( ( input^ = '<' ) and ( StrLComp( input, '<![CDATA[' , 9 ) = 0 ) ) then
result : = result + WhiteChars;
end else
// Zwyk�y znak (nie b�d�cy delimiterem!)
if input^ < > TextDelimiter then
begin
result : = result + input^ ;
increment( input) ;
end ;
end ;
end ;
begin
// Czy�cimy wszystkie ga��zie
self. Clear;
// Na wszelki wypadek...
if input^ = #0 then exit;
// Zerujemy parsowan� pozycj�
ParseLine: = 1 ;
ParseChar: = 1 ;
// Inicjujemy stos ga��zi
NodeStack: = TObjectStack. Create;
CurrentOperation: = poNodes;
try
while input^ < > #0 do
case CurrentOperation of
poNodes : begin
// Pomijamy bia�e znaki
while input^ in [ #32 , #9 , #10 , #13 ] do increment( input) ;
// Wej�cie mo�e si� tu ko�czy� tylko wtedy, gdy jeste�my
// maksymalnie na zewn�trz
if ( input^ = #0 ) and ( NodeStack. count> 0 ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of file.' ) ;
2011-06-18 15:51:22 +00:00
if ( input^ < > #0 ) and ( input^ < > '<' ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid character when processing the file.' ) ;
2011-06-18 15:51:22 +00:00
if input^ < > #0 then
if StrLComp( input, '<?' , 2 ) = 0 then
CurrentOperation: = poControlInterior else
if StrLComp( input, '<!--' , 4 ) = 0 then
CurrentOperation: = poCommentInterior else
if StrLComp( input, '</' , 2 ) = 0 then
CurrentOperation: = poClosingInterior else
if StrLComp( input, '<' , 1 ) = 0 then
CurrentOperation: = poTagInterior else
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid character when processing the file.' ) ;
2011-06-18 15:51:22 +00:00
end ;
poTagInterior,
poControlInterior : begin
Node: = nil ;
try
if CurrentOperation= poTagInterior then
begin
Node: = TSpkXMLNode. create( '' , xntNormal) ;
// Pomijamy znak otwarcia taga
increment( input) ;
end else
begin
Node: = TSpkXMLNode. create( '' , xntControl) ;
// Pomijamy znaki otwarcia taga
increment( input, 2 ) ;
end ;
// Plik nie mo�e si� tu ko�czy�
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of file' ) ;
2011-06-18 15:51:22 +00:00
// Oczekujemy nazwy taga, kt�ra jest postaci
// [a-zA-Z]([a-zA-Z0-9_]|([\-:][a-zA-Z0-9_]))*
2018-02-05 12:05:26 +00:00
if not ( input^ in [ 'a' .. 'z' , 'A' .. 'Z' ] ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid tag name!' ) ;
2011-06-18 15:51:22 +00:00
TokenStart: = input;
repeat
increment( input) ;
if input^ in [ '-' , ':' ] then
begin
increment( input) ;
if not( input^ in [ 'a' .. 'z' , 'A' .. 'Z' , '0' .. '9' , '_' ] ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid tag name!' ) ;
2011-06-18 15:51:22 +00:00
increment( input) ;
end ;
until not( input^ in [ 'a' .. 'z' , 'A' .. 'Z' , '0' .. '9' , '_' ] ) ;
2018-02-05 12:05:26 +00:00
SetLength( s, PtrUInt( input) - PtrUInt( TokenStart) ) ;
StrLCopy( PChar( s) , TokenStart, PtrUInt( input) - PtrUInt( TokenStart) ) ;
2011-06-18 15:51:22 +00:00
Node. Name : = s;
// Plik nie mo�e si� tu ko�czy�.
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
// Teraz b�dziemy wczytywa� parametry (o ile takowe s�).
repeat
// Wymagamy bia�ego znaku przed ka�dym parametrem.
if input^ in [ #32 , #9 , #10 , #13 ] then
begin
// Zjadamy bia�e znaki
while input^ in [ #32 , #9 , #10 , #13 ] do increment( input) ;
// Plik nie mo�e si� tu ko�czy�.
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
// Je�eli po bia�ych znakach jest litera,
// zaczynamy wczytywa� parametr
if input^ in [ 'a' .. 'z' , 'A' .. 'Z' ] then
begin
// Przetwarzamy parametr
TokenStart: = input;
repeat
increment( input)
until not( input^ in [ 'a' .. 'z' , 'A' .. 'Z' , '0' .. '9' , '_' ] ) ;
2018-02-05 12:05:26 +00:00
SetLength( s, {%H-} PtrUInt( input) - {%H-} PtrUInt( TokenStart) ) ;
StrLCopy( PChar( s) , TokenStart, {%H-} PtrUInt( input) - {%H-} PtrUInt( TokenStart) ) ;
2011-06-18 15:51:22 +00:00
// Pomijamy bia�e znaki
while input^ in [ #32 , #9 , #13 , #10 ] do increment( input) ;
// Plik nie mo�e si� tu ko�czy�
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
// Oczekujemy znaku '='
if input^ < > '=' then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Expected equality sign (probably invalid parameter name)' ) ;
2011-06-18 15:51:22 +00:00
increment( input) ;
// Pomijamy bia�e znaki
while input^ in [ #32 , #9 , #13 , #10 ] do increment( input) ;
// Plik nie mo�e si� tu ko�czy�
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
// Oczekujemy ' lub "
if input^ = '' '' then
begin
// Pomijamy znak apostrofu
increment( input) ;
s1: = ParseText( input, '' '' , false ) ;
// Pomijamy ko�cz�cy znak apostrofu
increment( input) ;
end else
if input^ = '"' then
begin
// Pomijamy znak cudzys�owu
increment( input) ;
s1: = ParseText( input, '"' , false ) ;
// Pomijamy ko�cz�cy znak cudzys�owu
increment( input) ;
end else
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid character, expected " or "' ) ;
2011-06-18 15:51:22 +00:00
// Dodajemy parametr o nazwie s i zawarto�ci s1
Node. Parameters[ s, true ] . Value: = s1;
end ;
end ;
// P�tla ko�czy si�, gdy na wej�ciu nie ma ju�
// bia�ego znaku, kt�ry jest wymagany przed i
// pomi�dzy parametrami. Sekwencja bia�ych znak�w
// po ostatnim parametrze zostanie pomini�ta wewn�trz
// p�tli.
until not( input^ in [ #32 , #9 , #10 , #13 ] ) ;
// Plik nie mo�e si� tu ko�czy�.
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
if CurrentOperation= poControlInterior then
begin
if StrLComp( input, '?>' , 2 ) < > 0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Incorrect closing of the control tag (should be:?>)' ) ;
2011-06-18 15:51:22 +00:00
// Pomijamy znaki zamkni�cia taga kontrolnego
increment( input, 2 ) ;
if NodeStack. count> 0 then
TSpkXMLNode( NodeStack. Peek) . Add( Node) else
Self. Add( Node) ;
CurrentOperation: = poNodes;
end else
if CurrentOperation= poTagInterior then
begin
if StrLComp( input, '/>' , 2 ) = 0 then
begin
// Pomijamy znaki zamkni�cia taga
increment( input, 2 ) ;
if NodeStack. count> 0 then
TSpkXMLNode( NodeStack. Peek) . add( Node) else
Self. add( Node) ;
CurrentOperation: = poNodes;
end else
if StrLComp( input, '>' , 1 ) = 0 then
begin
// Pomijamy znak zamkni�cia taga
increment( input) ;
NodeStack. Push( Node) ;
CurrentOperation: = poTagText;
end else
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Incorrect closing of the XML tag (should be:> or />)' ) ;
2011-06-18 15:51:22 +00:00
end ;
except
// Je�li co� p�jdzie nie tak, ga��� wisi w pami�ci i
// nie jest wrzucona na stos, trzeba j� zwolni�.
// Notatka jest taka, �e wszystkie wyj�tki, kt�re
// mog� si� pojawi�, s� *przed* wrzuceniem taga na
// stos lub do ga��zi na szczycie stosu.
if Node< > nil then Node. Free;
raise ;
end ;
end ;
poCommentInterior : begin
Node: = nil ;
try
Node: = TSpkXMLNode. create( '' , xntComment) ;
// Pomijamy znaki otwarcia taga
increment( input, 4 ) ;
// Wczytujemy komentarz
TokenStart: = input;
repeat
repeat
increment( input) ;
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
until input^ = '-' ;
until StrLComp( input, '-->' , 3 ) = 0 ;
2018-02-05 12:05:26 +00:00
setlength( s, PtrUInt( input) - PtrUInt( TokenStart) ) ;
StrLCopy( PChar( s) , TokenStart, PtrUInt( input) - PtrUInt( TokenStart) ) ;
2011-06-18 15:51:22 +00:00
Node. Text : = s;
// Pomijamy znaki zako�czenia komentarza
increment( input, 3 ) ;
if NodeStack. count> 0 then
TSpkXMLNode( NodeStack. Peek) . add( Node) else
Self. add( Node) ;
except
// Zarz�dzanie pami�ci� - zobacz poprzedni przypadek
if Node< > nil then Node. free;
raise
end ;
CurrentOperation: = poNodes;
end ;
poClosingInterior : begin
// Pomijamy znaki otwieraj�ce zamykaj�cy tag
increment( input, 2 ) ;
// Plik nie mo�e si� tu ko�czy�
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
// Wczytujemy nazw� zamykanego taga postaci
// [a-zA-Z]([a-zA-Z0-9_]|([\-:][a-zA-Z0-9_]))*
if not( input^ in [ 'a' .. 'z' , 'A' .. 'Z' ] ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid tag name!' ) ;
2011-06-18 15:51:22 +00:00
TokenStart: = input;
repeat
increment( input) ;
if input^ in [ '-' , ':' ] then
begin
increment( input) ;
if not( input^ in [ 'a' .. 'z' , 'A' .. 'Z' , '0' .. '9' , '_' ] ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Invalid tag name!' ) ;
2011-06-18 15:51:22 +00:00
increment( input) ;
end ;
until not( input^ in [ 'a' .. 'z' , 'A' .. 'Z' , '0' .. '9' , '_' ] ) ;
2018-02-05 12:05:26 +00:00
SetLength( s, PtrUInt( input) - PtrUInt( TokenStart) ) ;
StrLCopy( PChar( s) , TokenStart, PtrUInt( input) - PtrUInt( TokenStart) ) ;
2011-06-18 15:51:22 +00:00
// Pomijamy zb�dne znaki bia�e
while input^ in [ #32 , #9 , #10 , #13 ] do increment( input) ;
// Plik nie mo�e si� tu ko�czy�
if input^ = #0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of the file!' ) ;
2011-06-18 15:51:22 +00:00
// Oczekujemy znaku '>'
if input^ < > '>' then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Expected tag closing (>)' ) ;
2011-06-18 15:51:22 +00:00
// Pomijamy znak zamkni�cia taga
increment( input) ;
// Sprawdzamy, czy uppercase nazwa taga na stosie i
// wczytana pasuj� do siebie
if NodeStack. Count= 0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): The opening tag is not closed!' ) ;
2011-06-18 15:51:22 +00:00
if uppercase( s) < > uppercase( TSpkXMLNode( NodeStack. Peek) . Name ) then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): The closing tag (' + s + ') does not match the opening tag (' + TSpkXMLNode( NodeStack. Peek) . Name + ') !' ) ;
2011-06-18 15:51:22 +00:00
// Wszystko OK, zdejmujemy tag ze stosu i dodajemy go do taga pod nim
Node: = TSpkXMLNode( NodeStack. Pop) ;
if NodeStack. count> 0 then
TSpkXMLNode( NodeStack. Peek) . add( Node) else
Self. add( Node) ;
CurrentOperation: = poNodes;
end ;
poTagText : begin
// Wczytujemy tekst i przypisujemy go do taga znajduj�cego
// si� na szczycie stosu
s: = ParseText( input, '<' , true ) ;
if NodeStack. Count= 0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): The text can only be inside tags!' ) ;
2011-06-18 15:51:22 +00:00
TSpkXMLNode( NodeStack. Peek) . Text : = s;
CurrentOperation: = poNodes;
end ;
end ;
// Je�li na stosie pozosta�y jakie� ga��zie - oznacza to b��d (nie zosta�y
// domkni�te)
if NodeStack. Count> 0 then
2018-02-12 00:15:47 +00:00
raise exception. create( 'Error in XML syntax (line ' + IntToStr( ParseLine) + ', character ' + IntToStr( ParseChar) + '): Unexpected end of file (there are unclosed tags, the first of them is ' + TSpkXMLNode( NodeStack. Peek) . Name + ')' ) ;
2011-06-18 15:51:22 +00:00
// Wszystko w porz�dku, XML zosta� wczytany.
finally
// Czy�cimy nie przetworzone ga��zie
while NodeStack. Count> 0 do
NodeStack. Pop. Free;
NodeStack. Free;
end ;
end ;
function TSpkXMLParser. Generate( UseFormatting : boolean ) : string ;
function InternalGenerate( RootNode : TSpkXMLNode; indent : integer ; UseFormatting : boolean ) : string ;
var i : integer ;
function MkIndent( i : integer ) : string ;
begin
result : = '' ;
if indent< = 0 then exit;
setlength( result , i) ;
if i> 0 then
FillChar( result [ 1 ] , i, 3 2 ) ;
end ;
function MkText( AText : string ; CheckWhitespace : boolean = false ) : string ;
var s : string ;
prefix, postfix : string ;
begin
s: = AText;
s: = StringReplace( s, '&' , '&' , [ rfReplaceAll] ) ;
s: = StringReplace( s, '<' , '<' , [ rfReplaceAll] ) ;
s: = StringReplace( s, '>' , '>' , [ rfReplaceAll] ) ;
s: = StringReplace( s, '"' , '"' , [ rfReplaceAll] ) ;
s: = StringReplace( s, '' '' , ''' , [ rfReplaceAll] ) ;
prefix: = '' ;
postfix: = '' ;
if CheckWhitespace then
begin
// Je�li pierwszy znak jest bia�y, zamie� go na encj�
if s[ 1 ] = #32 then
begin
System. delete( s, 1 , 1 ) ;
prefix: = ' ' ;
end else
if s[ 1 ] = #9 then
begin
System. delete( s, 1 , 1 ) ;
prefix: = '	' ;
end else
if s[ 1 ] = #10 then
begin
System. delete( s, 1 , 1 ) ;
prefix: = ' ' ;
{$B-}
if ( length( s) > 0 ) and ( s[ 1 ] = #13 ) then
begin
System. delete( s, 1 , 1 ) ;
prefix: = prefix+ ' ' ;
end ;
end else
if s[ 1 ] = #13 then
begin
System. delete( s, 1 , 1 ) ;
prefix: = ' ' ;
{$B-}
if ( length( s) > 0 ) and ( s[ 1 ] = #10 ) then
begin
System. delete( s, 1 , 1 ) ;
prefix: = prefix+ ' ' ;
end ;
end ;
// Je�li ostatni znak jest bia�y, zamie� go na encj�
if length( s) > 0 then
begin
if s[ length( s) ] = #32 then
begin
System. delete( s, length( s) , 1 ) ;
postfix: = ' ' ;
end else
if s[ length( s) ] = #9 then
begin
System. delete( s, length( s) , 1 ) ;
postfix: = ' ' ;
end else
if s[ length( s) ] = #10 then
begin
System. Delete( s, length( s) , 1 ) ;
postfix: = ' ' ;
if ( length( s) > 0 ) and ( s[ length( s) ] = #13 ) then
begin
System. Delete( s, length( s) , 1 ) ;
postfix: = ' ' + postfix;
end ;
end else
if s[ length( s) ] = #13 then
begin
System. Delete( s, length( s) , 1 ) ;
postfix: = ' ' ;
if ( length( s) > 0 ) and ( s[ length( s) ] = #10 ) then
begin
System. Delete( s, length( s) , 1 ) ;
postfix: = ' ' + postfix;
end ;
end ;
end ;
end ;
result : = prefix+ s+ postfix;
end ;
begin
result : = '' ;
if RootNode= nil then
begin
if FList. count> 0 then
for i: = 0 to FList. count- 1 do
result : = result + InternalGenerate( TSpkXMLNode( FList[ i] ) , 0 , UseFormatting) ;
end else
begin
// Generowanie XMLa dla pojedynczej ga��zi
case RootNode. NodeType of
xntNormal : begin
if UseFormatting then
result : = MkIndent( indent) + '<' + RootNode. name else
result : = '<' + RootNode. name ;
if RootNode. Parameters. count> 0 then
for i: = 0 to RootNode. Parameters. count- 1 do
result : = result + ' ' + RootNode. Parameters. ParamByIndex[ i] . name + '="' + MkText( RootNode. Parameters. ParamByIndex[ i] . value, false ) + '"' ;
if ( RootNode. Count= 0 ) and ( RootNode. Text = '' ) then
begin
if UseFormatting then
result : = result + '/>' + CRLF else
result : = result + '/>' ;
end else
if ( RootNode. Count= 0 ) and ( RootNode. Text< > '' ) then
begin
result : = result + '>' ;
result : = result + MkText( RootNode. Text , true ) ;
if UseFormatting then
result : = result + '</' + RootNode. Name + '>' + CRLF else
result : = result + '</' + RootNode. Name + '>' ;
end else
if ( RootNode. Count> 0 ) and ( RootNode. Text = '' ) then
begin
if UseFormatting then
result : = result + '>' + CRLF else
result : = result + '>' ;
for i: = 0 to RootNode. count- 1 do
result : = result + InternalGenerate( RootNode. NodeByIndex[ i] , indent+ 2 , UseFormatting) ;
if UseFormatting then
result : = result + MkIndent( indent) + '</' + RootNode. name + '>' + CRLF else
result : = result + '</' + RootNode. name + '>' ;
end else
if ( RootNode. Count> 0 ) and ( RootNode. Text< > '' ) then
begin
result : = result + '>' ;
if UseFormatting then
result : = result + MkText( RootNode. Text , true ) + CRLF else
result : = result + MkText( RootNode. Text , true ) ;
for i: = 0 to RootNode. count- 1 do
result : = result + InternalGenerate( RootNode. NodeByIndex[ i] , indent+ 2 , UseFormatting) ;
if UseFormatting then
result : = result + MkIndent( indent) + '</' + RootNode. Name + '>' + CRLF else
result : = result + '</' + RootNode. Name + '>' ;
end ;
end ;
xntControl : begin
if UseFormatting then
result : = MkIndent( indent) + '<?' + RootNode. Name else
result : = '<?' + RootNode. Name ;
if RootNode. Parameters. count> 0 then
for i: = 0 to RootNode. Parameters. count- 1 do
result : = result + ' ' + RootNode. Parameters. ParamByIndex[ i] . name + '="' + MkText( RootNode. Parameters. ParamByIndex[ i] . value, false ) + '"' ;
if UseFormatting then
result : = result + '?>' + CRLF else
result : = result + '?>' ;
end ;
xntComment : begin
if UseFormatting then
result : = MkIndent( indent) + '<!--' + RootNode. text + '-->' + CRLF else
result : = '<!--' + RootNode. text + '-->' ;
end ;
end ;
end ;
end ;
begin
2018-02-12 00:15:47 +00:00
result : = InternalGenerate( nil , 0 , UseFormatting) ;
2011-06-18 15:51:22 +00:00
end ;
procedure TSpkXMLParser. LoadFromFile( AFile : string ) ;
2018-02-12 00:15:47 +00:00
var
sl : TStringList;
begin
sl: = TStringList. create;
try
sl. LoadFromFile( AFile) ;
if length( sl. text ) > 0 then
self. Parse( PChar( sl. text ) ) ;
finally
sl. free;
end ;
2011-06-18 15:51:22 +00:00
end ;
procedure TSpkXMLParser. SaveToFile( AFile : string ; UseFormatting : boolean ) ;
2018-02-12 00:15:47 +00:00
var
sl: TStringList;
begin
sl: = TStringList. create;
try
sl. text : = self. Generate( UseFormatting) ;
sl. savetofile( AFile) ;
finally
sl. free;
end ;
2011-06-18 15:51:22 +00:00
end ;
procedure TSpkXMLParser. LoadFromStream( AStream : TStream) ;
2018-02-12 00:15:47 +00:00
var
sl: TStringList;
begin
sl: = TStringList. create;
try
sl. LoadFromStream( AStream) ;
self. Parse( PChar( sl. text ) ) ;
finally
sl. free;
end ;
2011-06-18 15:51:22 +00:00
end ;
procedure TSpkXMLParser. SaveToStream( AStream : TStream; UseFormatting : boolean ) ;
2018-02-12 00:15:47 +00:00
var
sl: TStringList;
begin
sl: = TStringList. create;
try
sl. text : = self. Generate( UseFormatting) ;
sl. savetostream( AStream) ;
finally
sl. free;
end ;
2011-06-18 15:51:22 +00:00
end ;
end .