mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-27 02:53:40 +02:00
1164 lines
32 KiB
ObjectPascal
1164 lines
32 KiB
ObjectPascal
{
|
|
This file is part of the Free Component Library
|
|
|
|
XML serialisation driver
|
|
Copyright (c) 2000 by Sebastian Guenther, sg@freepascal.org
|
|
|
|
See the file COPYING.FPC, 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.
|
|
|
|
**********************************************************************}
|
|
{
|
|
Extended by Mattias Gaertner:
|
|
Reading/Writing childs, all kinds of properties,
|
|
custom properties (via DefineProperties).
|
|
}
|
|
unit Laz_XMLStreaming;
|
|
|
|
{$MODE objfpc}
|
|
{$H+}
|
|
|
|
{$I codetools.inc}
|
|
|
|
{$DEFINE HasReadWriteBuf}
|
|
{$IF defined(VER2_2_0)}
|
|
{$UNDEF HasReadWriteBuf}
|
|
{$ENDIF}
|
|
|
|
{$IF (FPC_VERSION = 2) AND (FPC_RELEASE >= 3)}
|
|
{$DEFINE USE_NEW_READER_WRITER}
|
|
{$IFEND}
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes, TypInfo, FileProcs,
|
|
{$IFDEF NewXMLCfg}
|
|
Laz2_DOM, Laz2_XMLWrite
|
|
{$ELSE}
|
|
Laz_DOM, Laz_XMLWrite
|
|
{$ENDIF};
|
|
|
|
type
|
|
|
|
TXMLObjectWriterStackElType = (elUnknown, elPropertyList, elChildrenList);
|
|
|
|
TXMLObjectWriterStackEl = class
|
|
public
|
|
Element, Parent: TDOMElement;
|
|
ElemType: TXMLObjectWriterStackElType;
|
|
PropertyName: String;
|
|
end;
|
|
|
|
{ TXMLObjectWriter }
|
|
|
|
TXMLObjectWriter = class(TAbstractObjectWriter)
|
|
private
|
|
FDoc: TDOMDocument;
|
|
FRootEl: TDOMElement;
|
|
FStack: TFPList;
|
|
StackEl: TXMLObjectWriterStackEl;
|
|
procedure StackPush(const Element: string = '';
|
|
ElementType: TXMLObjectWriterStackElType = elUnknown);
|
|
procedure StackPop;
|
|
function GetPropertyElement(const TypeName: String): TDOMElement;
|
|
public
|
|
constructor Create(ADoc: TDOMDocument; const APath: string; Append: Boolean);
|
|
|
|
{ Begin/End markers. Those ones who don't have an end indicator, use
|
|
"EndList", after the occurrence named in the comment. Note that this
|
|
only counts for "EndList" calls on the same level; each BeginXXX call
|
|
increases the current level. }
|
|
procedure BeginCollection; override;{ Ends with the next "EndList" }
|
|
procedure BeginComponent(Component: TComponent; Flags: TFilerFlags;
|
|
ChildPos: Integer); override;{ Ends after the second "EndList" }
|
|
procedure BeginList; override;
|
|
procedure EndList; override;
|
|
procedure BeginProperty(const PropName: String); override;
|
|
procedure EndProperty; override;
|
|
|
|
procedure WriteBinary(const Buffer; Count: Longint); override;
|
|
procedure WriteBoolean(Value: Boolean); override;
|
|
// procedure WriteChar(Value: Char);
|
|
procedure WriteFloat(const Value: Extended); override;
|
|
procedure WriteSingle(const Value: Single); override;
|
|
procedure WriteCurrency(const Value: Currency); override;
|
|
procedure WriteDate(const Value: TDateTime); override;
|
|
procedure WriteIdent(const Ident: string); override;
|
|
procedure WriteInteger(Value: Int64); override;
|
|
procedure WriteMethodName(const Name: String); override;
|
|
procedure WriteSet(Value: LongInt; SetType: Pointer); override;
|
|
procedure WriteString(const Value: String); override;
|
|
procedure WriteWideString(const Value: WideString); override;
|
|
{$IFDEF USE_NEW_READER_WRITER}
|
|
procedure WriteUInt64(Value: QWord); override;
|
|
procedure WriteUnicodeString(const Value: UnicodeString); override;
|
|
procedure WriteVariant(const VarValue: Variant); override;
|
|
{$ENDIF}
|
|
{$IFDEF HasReadWriteBuf}
|
|
procedure Write(const Buffer; Count: Longint); override;
|
|
{$ENDIF}
|
|
public
|
|
property Doc: TDOMDocument read FDoc;
|
|
end;
|
|
TXMLObjectWriterClass = class of TXMLObjectWriter;
|
|
|
|
|
|
{ TXMLObjectReader }
|
|
|
|
TXMLObjectReader = class(TAbstractObjectReader)
|
|
private
|
|
FDoc: TDOMDocument;
|
|
FElement: TDOMElement;
|
|
FElementPosition: integer;
|
|
FRootEl: TDOMElement;
|
|
function ReadNextValue(Stay: Boolean): TValueType;
|
|
public
|
|
constructor Create(ADoc: TDOMDocument; const APath: string);
|
|
destructor Destroy; override;
|
|
|
|
function GetRootClassName(out IsInherited: Boolean): string;
|
|
|
|
{ All ReadXXX methods are called _after_ the value type has been read! }
|
|
function NextValue: TValueType; override;
|
|
function ReadValue: TValueType; override;
|
|
procedure BeginRootComponent; override;
|
|
procedure BeginComponent(var Flags: TFilerFlags; var AChildPos: Integer;
|
|
var CompClassName, CompName: String); override;
|
|
function BeginProperty: String; override;
|
|
|
|
procedure ReadBinary(const DestData: TMemoryStream); override;
|
|
function ReadFloat: Extended; override;
|
|
function ReadSingle: Single; override;
|
|
function ReadCurrency: Currency; override;
|
|
function ReadDate: TDateTime; override;
|
|
function ReadIdent(ValueType: TValueType): String; override;
|
|
function ReadInt8: ShortInt; override;
|
|
function ReadInt16: SmallInt; override;
|
|
function ReadInt32: LongInt; override;
|
|
function ReadInt64: Int64; override;
|
|
function ReadSet(SetType: Pointer): Integer; override;
|
|
function ReadStr: String; override;
|
|
function ReadString(StringType: TValueType): String; override;
|
|
function ReadWideString: WideString; override;
|
|
{$IFDEF USE_NEW_READER_WRITER}
|
|
function ReadUnicodeString: UnicodeString; override;
|
|
{$ENDIF}
|
|
procedure SkipComponent(SkipComponentInfos: Boolean); override;
|
|
procedure SkipValue; override;
|
|
{$IFDEF HasReadWriteBuf}
|
|
procedure Read(var Buf; Count: LongInt); override;
|
|
{$ENDIF}
|
|
public
|
|
property Doc: TDOMDocument read FDoc;
|
|
property Element: TDOMElement read FElement;// current element node
|
|
property ElementPosition: integer read FElementPosition;
|
|
end;
|
|
TXMLObjectReaderClass = class of TXMLObjectReader;
|
|
|
|
procedure WriteComponentToXMLStream(AComponent: TComponent; AStream: TStream);
|
|
|
|
|
|
implementation
|
|
|
|
procedure WriteComponentToXMLStream(AComponent: TComponent; AStream: TStream);
|
|
var
|
|
Driver: TXMLObjectWriter;
|
|
Writer: TWriter;
|
|
XMLDocument: TXMLDocument;
|
|
begin
|
|
XMLDocument:=nil;
|
|
Driver:=nil;
|
|
Writer:=nil;
|
|
try
|
|
XMLDocument:=TXMLDocument.Create;
|
|
Driver:=TXMLObjectWriter.Create(XMLDocument,'fcl-persistent',true);
|
|
Writer:=TWriter.Create(Driver);
|
|
Writer.WriteDescendent(AComponent,nil);
|
|
WriteXMLFile(XMLDocument,AStream);
|
|
finally
|
|
Writer.Free;
|
|
Driver.Free;
|
|
XMLDocument.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.StackPush(const Element: string;
|
|
ElementType: TXMLObjectWriterStackElType);
|
|
var
|
|
Parent: TDOMElement;
|
|
i: Integer;
|
|
begin
|
|
if Assigned(FStack) then
|
|
begin
|
|
// append to stack
|
|
Parent := StackEl.Element;
|
|
if Parent=nil then begin
|
|
i:=FStack.Count-1;
|
|
while (i>=0) do begin
|
|
if (TXMLObjectWriterStackEl(FStack[i]).Element=nil) then
|
|
dec(i)
|
|
else begin
|
|
Parent:=TXMLObjectWriterStackEl(FStack[i]).Element;
|
|
break;
|
|
end;
|
|
end;
|
|
if Parent=nil then
|
|
Parent:=FRootEl;
|
|
end;
|
|
FStack.Add(StackEl);
|
|
StackEl := TXMLObjectWriterStackEl.Create;
|
|
StackEl.Parent := Parent;
|
|
end else
|
|
begin
|
|
// start stack
|
|
FStack := TFPList.Create;
|
|
StackEl := TXMLObjectWriterStackEl.Create;
|
|
StackEl.Parent := FRootEl;
|
|
end;
|
|
|
|
if Element<>'' then begin
|
|
// create element
|
|
StackEl.Element := FDoc.CreateElement(Element);
|
|
StackEl.Parent.AppendChild(StackEl.Element);
|
|
StackEl.ElemType:=ElementType;
|
|
end;
|
|
DebugLn('TXMLObjectWriter.StackPush Element="',Element,'" FStack.Count=',dbgs(FStack.Count),' ',DbgSName(StackEl.Parent));
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.StackPop;
|
|
begin
|
|
DebugLn('TXMLObjectWriter.StackPop ',dbgs(FStack.Count));
|
|
if FStack=nil then
|
|
raise Exception.Create('TXMLObjectWriter.StackPop stack empty');
|
|
StackEl.Free;
|
|
if FStack.Count > 0 then
|
|
begin
|
|
StackEl := TXMLObjectWriterStackEl(FStack[FStack.Count - 1]);
|
|
FStack.Delete(FStack.Count - 1);
|
|
end else
|
|
begin
|
|
FStack.Free;
|
|
FStack := nil;
|
|
StackEl := nil;
|
|
end;
|
|
end;
|
|
|
|
function TXMLObjectWriter.GetPropertyElement(const TypeName: String
|
|
): TDOMElement;
|
|
var
|
|
CustomElement: TDOMElement;
|
|
begin
|
|
if StackEl.PropertyName<>'' then begin
|
|
// normal property
|
|
if not Assigned(StackEl.Element) then begin
|
|
StackEl.Element := FDoc.CreateElement(TypeName);
|
|
StackEl.Parent.AppendChild(StackEl.Element);
|
|
StackEl.Element['name'] := StackEl.PropertyName;
|
|
Result := StackEl.Element;
|
|
end else begin
|
|
raise Exception.Create('TXMLObjectWriter.GetPropertyElement property already saved');
|
|
end;
|
|
end else begin
|
|
// custom defined property (via DefineProperties)
|
|
CustomElement := FDoc.CreateElement(TypeName);
|
|
StackEl.Element.AppendChild(CustomElement);
|
|
Result := CustomElement;
|
|
end;
|
|
end;
|
|
|
|
constructor TXMLObjectWriter.Create(ADoc: TDOMDocument;
|
|
const APath: string; Append: Boolean);
|
|
var
|
|
Node: TDOMNode;
|
|
PathLen: Integer;
|
|
StartPos: Integer;
|
|
EndPos: LongInt;
|
|
NodeName: string;
|
|
Child: TDOMNode;
|
|
ParentNode: TDOMNode;
|
|
begin
|
|
inherited Create;
|
|
FDoc := ADoc;
|
|
|
|
Node := Doc.DocumentElement;
|
|
PathLen:=length(APath);
|
|
StartPos:=1;
|
|
while True do begin
|
|
EndPos:=StartPos;
|
|
while (EndPos<=PathLen) and (APath[EndPos]<>'/') do inc(EndPos);
|
|
if EndPos>StartPos then begin
|
|
SetLength(NodeName,EndPos-StartPos);
|
|
Move(APath[StartPos],NodeName[1],EndPos-StartPos);
|
|
StartPos:=EndPos+1;
|
|
Child := Node.FindNode(NodeName);
|
|
if not Assigned(Child) then
|
|
begin
|
|
Child := Doc.CreateElement(NodeName);
|
|
Node.AppendChild(Child);
|
|
end;
|
|
Node := Child;
|
|
end else if EndPos>PathLen then begin
|
|
break;
|
|
end else begin
|
|
StartPos:=EndPos+1;
|
|
end;
|
|
end;
|
|
if Node is TDOMElement then
|
|
FRootEl:=TDOMElement(Node)
|
|
else
|
|
FRootEl:=nil;
|
|
|
|
NodeName:='fcl-persistent';
|
|
ParentNode:=nil;
|
|
if (not Append) and (FRootEl<>nil) then begin
|
|
NodeName:=FRootEl.NodeName;
|
|
ParentNode:=FRootEl.ParentNode;
|
|
if ParentNode<>nil then
|
|
ParentNode.RemoveChild(FRootEl)
|
|
else
|
|
FRootEl.Free;
|
|
FRootEl:=nil;
|
|
end;
|
|
if FRootEl=nil then
|
|
FRootEl := FDoc.CreateElement(NodeName);
|
|
if ParentNode=nil then
|
|
FDoc.AppendChild(FRootEl)
|
|
else
|
|
ParentNode.AppendChild(FRootEl);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.BeginCollection;
|
|
begin
|
|
GetPropertyElement('collectionproperty');
|
|
StackPush('collection');
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.BeginComponent(Component: TComponent; Flags: TFilerFlags;
|
|
ChildPos: Integer);
|
|
// TWriter expects to push two elements on the stack, which are popped by
|
|
// two EndList calls.
|
|
begin
|
|
StackPush('component');
|
|
|
|
if Length(Component.Name) > 0 then
|
|
StackEl.Element['name'] := Component.Name;
|
|
StackEl.Element['class'] := Component.ClassName;
|
|
|
|
StackPush('properties',elPropertyList);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.BeginList;
|
|
begin
|
|
StackPush('list');
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.EndList;
|
|
begin
|
|
if StackEl.ElemType = elPropertyList then
|
|
begin
|
|
// end the property list and start the children list
|
|
if not StackEl.Element.HasChildNodes then
|
|
StackEl.Parent.RemoveChild(StackEl.Element);
|
|
StackPop;
|
|
|
|
StackPush('children',elChildrenList);
|
|
end else if StackEl.ElemType = elChildrenList then
|
|
begin
|
|
// end the children list and the component
|
|
if not StackEl.Element.HasChildNodes then
|
|
StackEl.Parent.RemoveChild(StackEl.Element);
|
|
StackPop; // end children
|
|
StackPop; // end component
|
|
end else
|
|
StackPop;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.BeginProperty(const PropName: String);
|
|
begin
|
|
//DebugLn('TXMLObjectWriter.BeginProperty "',PropName,'"');
|
|
StackPush;
|
|
StackEl.PropertyName := PropName;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.EndProperty;
|
|
begin
|
|
StackPop;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteBinary(const Buffer; Count: Longint);
|
|
var
|
|
s: string;
|
|
begin
|
|
SetLength(s,Count);
|
|
if s<>'' then
|
|
System.Move(Buffer,s[1],length(s));
|
|
GetPropertyElement('binary')['value'] := s;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteBoolean(Value: Boolean);
|
|
begin
|
|
if Value then
|
|
GetPropertyElement('boolean')['value'] := 'true'
|
|
else
|
|
GetPropertyElement('boolean')['value'] := 'false';
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteFloat(const Value: Extended);
|
|
begin
|
|
GetPropertyElement('extended')['value'] := FloatToStr(Value);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteSingle(const Value: Single);
|
|
begin
|
|
GetPropertyElement('single')['value'] := FloatToStr(Value);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteCurrency(const Value: Currency);
|
|
begin
|
|
GetPropertyElement('Currency')['value'] := FloatToStr(Value);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteDate(const Value: TDateTime);
|
|
begin
|
|
GetPropertyElement('date')['value'] := FloatToStr(Value);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteIdent(const Ident: string);
|
|
begin
|
|
GetPropertyElement('ident')['value'] := Ident;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteInteger(Value: Int64);
|
|
begin
|
|
GetPropertyElement('integer')['value'] := IntToStr(Value);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteMethodName(const Name: String);
|
|
begin
|
|
GetPropertyElement('method')['value'] := Name;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteSet(Value: LongInt; SetType: Pointer);
|
|
var
|
|
i: Integer;
|
|
Mask: LongInt;
|
|
s: String;
|
|
begin
|
|
Mask := 1;
|
|
s:='';
|
|
for i := 0 to 31 do begin
|
|
if (Value and Mask) <> 0 then begin
|
|
if s<>'' then s:=s+',';
|
|
s:=s+GetEnumName(PTypeInfo(SetType), i);
|
|
end;
|
|
Mask := Mask shl 1;
|
|
end;
|
|
GetPropertyElement('set')['value'] := s;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteString(const Value: String);
|
|
begin
|
|
GetPropertyElement('string')['value'] := Value;
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteWideString(const Value: WideString);
|
|
// save widestrings as utf8
|
|
begin
|
|
GetPropertyElement('widestring')['value'] := System.UTF8Encode(Value);
|
|
end;
|
|
|
|
{$IFDEF USE_NEW_READER_WRITER}
|
|
procedure TXMLObjectWriter.WriteUInt64(Value: QWord);
|
|
begin
|
|
GetPropertyElement('uint64')['value'] := IntToStr(Value);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteUnicodeString(const Value: UnicodeString);
|
|
// save unicodestrings as utf8
|
|
begin
|
|
GetPropertyElement('unicodestring')['value'] := System.UTF8Encode(Value);
|
|
end;
|
|
|
|
procedure TXMLObjectWriter.WriteVariant(const VarValue: Variant);
|
|
begin
|
|
case tvardata(VarValue).vtype of
|
|
varEmpty:
|
|
begin
|
|
GetPropertyElement('uint64')['value'] := 'nil';
|
|
end;
|
|
varNull:
|
|
begin
|
|
GetPropertyElement('uint64')['value'] := 'null';
|
|
end;
|
|
{ all integer sizes must be split for big endian systems }
|
|
varShortInt,varSmallInt,varInteger,varInt64:
|
|
begin
|
|
WriteInteger(VarValue);
|
|
end;
|
|
varQWord:
|
|
begin
|
|
WriteUInt64(VarValue);
|
|
end;
|
|
varBoolean:
|
|
begin
|
|
WriteBoolean(VarValue);
|
|
end;
|
|
varCurrency:
|
|
begin
|
|
WriteCurrency(VarValue);
|
|
end;
|
|
varSingle:
|
|
begin
|
|
WriteSingle(VarValue);
|
|
end;
|
|
varDouble:
|
|
begin
|
|
WriteFloat(VarValue);
|
|
end;
|
|
varDate:
|
|
begin
|
|
WriteDate(VarValue);
|
|
end;
|
|
varOleStr,varString:
|
|
begin
|
|
WriteWideString(VarValue);
|
|
end;
|
|
else
|
|
raise EWriteError.CreateFmt('Unsupported property variant type %d', [Ord(tvardata(VarValue).vtype)]);
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
{$IFDEF HasReadWriteBuf}
|
|
procedure TXMLObjectWriter.Write(const Buffer; Count: Longint);
|
|
begin
|
|
// there can be arbitrary lots of Write calls
|
|
raise Exception.Create('TODO: TXMLObjectWriter.Write');
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ TXMLObjectReader }
|
|
|
|
function TXMLObjectReader.ReadNextValue(Stay: Boolean): TValueType;
|
|
|
|
procedure RaiseUnknownNode(Node: TDOMNode);
|
|
begin
|
|
raise EReadError.Create('TXMLObjectReader: unknown node "'+Node.NodeName+'"');
|
|
end;
|
|
|
|
procedure RaiseUnknownParentNode(Node: TDOMNode);
|
|
begin
|
|
raise EReadError.Create('TXMLObjectReader: unknown parent node "'+Node.NodeName+'" Element="'+FElement.NodeName+'"');
|
|
end;
|
|
|
|
procedure RaiseInvalidElementPosition;
|
|
begin
|
|
raise EReadError.Create('TXMLObjectReader: invalid ElementPosition='+IntToStr(FElementPosition)+' Node='+FElement.NodeName);
|
|
end;
|
|
|
|
procedure RaiseNodeNotFound(const NodeName: string);
|
|
begin
|
|
raise EReadError.Create('TXMLObjectReader: expected "'+NodeName+'", but found "'+FElement.NodeName+'"');
|
|
end;
|
|
|
|
procedure CheckNode(const NodeName: string);
|
|
begin
|
|
if FElement.NodeName<>NodeName then
|
|
RaiseNodeNotFound(NodeName);
|
|
end;
|
|
|
|
procedure GoToNextComponent;
|
|
begin
|
|
FElement:=FElement.ParentNode as TDOMElement;
|
|
CheckNode('component');
|
|
FElementPosition:=0;
|
|
if FElement.NextSibling is TDOMElement then begin
|
|
// go to next component
|
|
//writeln('TXMLObjectReader.ReadNextValue properties: next component');
|
|
FElement:=TDOMElement(FElement.NextSibling);
|
|
CheckNode('component');
|
|
end else begin
|
|
// end of children list
|
|
if FElement.ParentNode.NodeName='children' then begin
|
|
//writeln('TXMLObjectReader.ReadNextValue end of children list');
|
|
FElement:=FElement.ParentNode as TDOMElement;
|
|
FElementPosition:=1;
|
|
end else begin
|
|
//writeln('TXMLObjectReader.ReadNextValue END reading');
|
|
FElement:=nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
CurValue: String;
|
|
CurInt64: Int64;
|
|
begin
|
|
//writeln('TXMLObjectReader.ReadNextValue Stay=',Stay,' Element=',FElement.NodeName,' Pos=',FElementPosition);
|
|
|
|
if FElement.NodeName='component' then begin
|
|
//writeln('TXMLObjectReader.ReadNextValue is start of component');
|
|
Result:=vaString;
|
|
if not Stay then begin
|
|
// here a BeginComponent shoud be called, not ReadValue
|
|
RaiseUnknownNode(FElement);
|
|
end;
|
|
end
|
|
else if FElement.NodeName='properties' then begin
|
|
// FElement is at end of property list or non existing children list
|
|
// 0: end of property list
|
|
// 1: end of non existing children list
|
|
//writeln('TXMLObjectReader.ReadNextValue FElement is at end of property list');
|
|
Result:=vaNull;
|
|
if not Stay then begin
|
|
if FElement.NextSibling is TDOMElement then begin
|
|
// leave properties and go to first child component
|
|
//writeln('TXMLObjectReader.ReadNextValue properties: children');
|
|
FElement:=TDOMElement(FElement.NextSibling);
|
|
FElementPosition:=0;
|
|
CheckNode('children');
|
|
if not (FElement.FirstChild is TDOMElement) then
|
|
RaiseUnknownNode(FElement);
|
|
FElement:=TDOMElement(FElement.FirstChild);
|
|
end else begin
|
|
// there is no children list behind the properties -> simulate it
|
|
if FElementPosition=0 then begin
|
|
inc(FElementPosition);
|
|
end else begin
|
|
// children has been simulated -> now go to next component
|
|
GoToNextComponent;
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else if FElement.NodeName='children' then begin
|
|
// end of children list
|
|
//writeln('TXMLObjectReader.ReadNextValue End of children list');
|
|
Result:=vaNull;
|
|
if not Stay then begin
|
|
GoToNextComponent;
|
|
end;
|
|
end
|
|
else if FElement.NodeName='list' then begin
|
|
// FElement is a list element
|
|
// It has 2 positions:
|
|
// 0: vaList
|
|
// 1: end of list
|
|
case FElementPosition of
|
|
|
|
0:begin
|
|
//writeln('TXMLObjectReader.ReadNextValue list: vaList');
|
|
Result:=vaList;
|
|
if (FElement.FirstChild is TDOMElement) then begin
|
|
// the list has childs
|
|
if not Stay then begin
|
|
FElement:=TDOMElement(FElement.FirstChild);
|
|
FElementPosition:=0;
|
|
end;
|
|
end else begin
|
|
// empty list
|
|
if not Stay then
|
|
inc(FElementPosition);
|
|
end;
|
|
end;
|
|
|
|
1:begin
|
|
// end of list
|
|
Result:=vaNull;
|
|
if not Stay then begin
|
|
if (FElement.NextSibling is TDOMElement) then begin
|
|
//writeln('TXMLObjectReader.ReadNextValue list: end of childs, next list');
|
|
FElement:=TDOMElement(FElement.NextSibling);
|
|
FElementPosition:=0;
|
|
end else begin
|
|
//writeln('TXMLObjectReader.ReadNextValue list: end of childs, end of collection');
|
|
FElement:=FElement.ParentNode as TDOMElement;
|
|
FElementPosition:=0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
end
|
|
else if FElement.NodeName='collection' then begin
|
|
// FElement is at end of collection
|
|
//writeln('TXMLObjectReader.ReadNextValue FElement is at end of collection');
|
|
Result:=vaNull;
|
|
end
|
|
else if (FElement.ParentNode.NodeName='properties')
|
|
or (FElement.ParentNode.NodeName='list') then begin
|
|
// FElement is a property
|
|
// It has 3 positions:
|
|
// 0: name
|
|
// 1: value type
|
|
// 2: value
|
|
case FElementPosition of
|
|
|
|
0:// the property name
|
|
begin
|
|
Result:=vaString;
|
|
if not Stay then
|
|
inc(FElementPosition);
|
|
end;
|
|
|
|
1:// value type
|
|
begin
|
|
if FElement.NodeName='integer' then begin
|
|
CurValue:=FElement['value'];
|
|
CurInt64:=StrToInt64(CurValue);
|
|
if (CurInt64 >= -128) and (CurInt64 <= 127) then begin
|
|
Result:=vaInt8
|
|
end else if (CurInt64 >= -32768) and (CurInt64 <= 32767) then begin
|
|
Result:=vaInt16;
|
|
end else if (CurInt64 >= Low(Integer)) and (CurInt64 <= High(integer))
|
|
then begin
|
|
Result:=vaInt32;
|
|
end else
|
|
Result:=vaInt64;
|
|
end else if FElement.NodeName='string' then begin
|
|
CurValue:=FElement['value'];
|
|
if length(CurValue)<=255 then
|
|
Result:=vaString
|
|
else
|
|
Result:=vaLString;
|
|
end else if FElement.NodeName='ident' then
|
|
Result:=vaIdent
|
|
else if FElement.NodeName='boolean' then begin
|
|
if FElement['value']='true' then
|
|
Result:=vaTrue
|
|
else
|
|
Result:=vaFalse;
|
|
end else if FElement.NodeName='method' then
|
|
Result:=vaIdent
|
|
else if FElement.NodeName='set' then
|
|
Result:=vaSet
|
|
else if FElement.NodeName='extended' then
|
|
Result:=vaExtended
|
|
else if FElement.NodeName='widestring' then
|
|
Result:=vaWString
|
|
{$IFDEF USE_NEW_READER_WRITER}
|
|
else if FElement.NodeName = 'uint64' then
|
|
Result:=vaQWord
|
|
else if FElement.NodeName='unicodestring' then
|
|
Result:=vaUString
|
|
else if FElement.NodeName='variant' then
|
|
begin
|
|
if FElement['value'] = 'nil' then
|
|
Result := vaNil
|
|
else
|
|
Result := vaNull;
|
|
end
|
|
{$ENDIF}
|
|
else if FElement.NodeName='collectionproperty' then
|
|
Result:=vaCollection
|
|
else
|
|
RaiseUnknownNode(FElement);
|
|
if not Stay then begin
|
|
inc(FElementPosition);
|
|
case Result of
|
|
vaTrue, vaFalse: ReadValue;
|
|
vaCollection:
|
|
begin
|
|
// go to node 'collection'
|
|
FElement:=FElement.FirstChild as TDOMElement;
|
|
CheckNode('collection');
|
|
FElementPosition:=0;
|
|
// go to node 'list'
|
|
if Assigned(FElement.FirstChild) and (FElement.FirstChild.NodeName='list') then
|
|
FElement:=FElement.FirstChild as TDOMElement;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
2:// value
|
|
begin
|
|
if FElement.NextSibling<>nil then begin
|
|
Result:=vaString;
|
|
if not Stay then begin
|
|
FElement:=FElement.NextSibling as TDOMElement;
|
|
FElementPosition:=0;
|
|
end;
|
|
end else begin
|
|
// end of property list
|
|
Result:=vaNull;
|
|
if not Stay then begin
|
|
FElement:=FElement.ParentNode as TDOMElement;
|
|
FElementPosition:=0;
|
|
if FElement.NodeName='list' then
|
|
FElementPosition:=1;
|
|
end;
|
|
end;
|
|
end;
|
|
else
|
|
RaiseInvalidElementPosition;
|
|
end;
|
|
end else begin
|
|
RaiseUnknownParentNode(FElement.ParentNode);
|
|
end;
|
|
//writeln('TXMLObjectReader.ReadNextValue Result=',GetEnumName(TypeInfo(TValueType),ord(Result)));
|
|
end;
|
|
|
|
constructor TXMLObjectReader.Create(ADoc: TDOMDocument; const APath: string);
|
|
var
|
|
Node: TDOMNode;
|
|
PathLen: Integer;
|
|
StartPos: Integer;
|
|
EndPos: LongInt;
|
|
NodeName: string;
|
|
Child: TDOMNode;
|
|
|
|
procedure RaiseMissingNode;
|
|
begin
|
|
raise Exception.Create('XML node not found '+APath);
|
|
end;
|
|
|
|
procedure RaiseNotDOMElement;
|
|
begin
|
|
raise Exception.Create('invalid XML node '+APath);
|
|
end;
|
|
|
|
begin
|
|
inherited Create;
|
|
FDoc := ADoc;
|
|
|
|
Node := Doc.DocumentElement;
|
|
PathLen:=length(APath);
|
|
StartPos:=1;
|
|
while True do begin
|
|
EndPos:=StartPos;
|
|
while (EndPos<=PathLen) and (APath[EndPos]<>'/') do inc(EndPos);
|
|
if EndPos>StartPos then begin
|
|
SetLength(NodeName,EndPos-StartPos);
|
|
Move(APath[StartPos],NodeName[1],EndPos-StartPos);
|
|
StartPos:=EndPos+1;
|
|
Child := Node.FindNode(NodeName);
|
|
if not Assigned(Child) then
|
|
RaiseMissingNode;
|
|
Node := Child;
|
|
end else if EndPos>PathLen then begin
|
|
break;
|
|
end else begin
|
|
StartPos:=EndPos+1;
|
|
end;
|
|
end;
|
|
if not (Node is TDOMElement) then
|
|
RaiseNotDOMElement;
|
|
FRootEl:=TDOMElement(Node);
|
|
FElement:=FRootEl;
|
|
FElementPosition:=0;
|
|
end;
|
|
|
|
destructor TXMLObjectReader.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TXMLObjectReader.GetRootClassName(out IsInherited: Boolean): string;
|
|
var
|
|
ComponentNode: TDOMNode;
|
|
CompElement: TDOMElement;
|
|
begin
|
|
IsInherited:=false;
|
|
|
|
ComponentNode:=FRootEl.FindNode('component');
|
|
if ComponentNode=nil then
|
|
raise Exception.Create('component node not found');
|
|
if not (ComponentNode is TDOMElement) then
|
|
raise Exception.Create('component node is not a dom element');
|
|
CompElement:=TDOMElement(ComponentNode);
|
|
|
|
Result:=CompElement['class'];
|
|
DebugLn('TXMLObjectReader.GetRootClassName RootClassName="',Result,'"');
|
|
|
|
// TODO: IsInherited
|
|
end;
|
|
|
|
function TXMLObjectReader.NextValue: TValueType;
|
|
begin
|
|
Result:=ReadNextValue(true);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadValue: TValueType;
|
|
begin
|
|
Result:=ReadNextValue(false);
|
|
end;
|
|
|
|
procedure TXMLObjectReader.BeginRootComponent;
|
|
|
|
procedure RaiseComponentNodeNotFound;
|
|
begin
|
|
raise Exception.Create('component node not found');
|
|
end;
|
|
|
|
var
|
|
Node: TDOMNode;
|
|
begin
|
|
//writeln('TXMLObjectReader.BeginRootComponent ');
|
|
Node:=FElement.FindNode('component');
|
|
if Node=nil then
|
|
RaiseComponentNodeNotFound;
|
|
end;
|
|
|
|
procedure TXMLObjectReader.BeginComponent(var Flags: TFilerFlags;
|
|
var AChildPos: Integer; var CompClassName, CompName: String);
|
|
var
|
|
ComponentNode: TDOMNode;
|
|
PropertiesNode: TDOMNode;
|
|
begin
|
|
//writeln('TXMLObjectReader.BeginComponent START');
|
|
|
|
if FElement.NodeName='component' then
|
|
ComponentNode:=FElement
|
|
else
|
|
ComponentNode:=FElement.FindNode('component');
|
|
if ComponentNode=nil then
|
|
raise Exception.Create('component node not found');
|
|
if not (ComponentNode is TDOMElement) then
|
|
raise Exception.Create('component node is not a dom element');
|
|
FElement:=TDOMElement(ComponentNode);
|
|
|
|
CompName:=FElement['name'];
|
|
CompClassName:=FElement['class'];
|
|
DebugLn('TXMLObjectReader.BeginComponent CompName="',CompName,'" CompClassName="',CompClassName,'"');
|
|
|
|
PropertiesNode:=FElement.FindNode('properties');
|
|
if PropertiesNode=nil then
|
|
raise Exception.Create('properties node not found');
|
|
if not (PropertiesNode is TDOMElement) then
|
|
raise Exception.Create('properties node is not a dom element');
|
|
|
|
// if there are properties, then set FElement to the first property
|
|
FElement:=TDOMElement(PropertiesNode);
|
|
if FElement.FirstChild is TDOMElement then
|
|
FElement:=TDOMElement(FElement.FirstChild);
|
|
FElementPosition:=0;
|
|
end;
|
|
|
|
function TXMLObjectReader.BeginProperty: String;
|
|
begin
|
|
Result:=FElement['name'];
|
|
//writeln('TXMLObjectReader.BeginProperty Result="',Result,'"');
|
|
inc(FElementPosition);
|
|
end;
|
|
|
|
procedure TXMLObjectReader.ReadBinary(const DestData: TMemoryStream);
|
|
var
|
|
Value: String;
|
|
begin
|
|
Value:=FElement['value'];
|
|
if Value<>'' then
|
|
DestData.Write(Value[1],length(Value));
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadBinary ');
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadFloat: Extended;
|
|
var
|
|
Value: String;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Result:=StrToFloat(Value);
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadFloat ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadSingle: Single;
|
|
var
|
|
Value: String;
|
|
FloatError: integer;
|
|
Back: single;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Val(Value, Back, FloatError);
|
|
if FloatError=0 then ;
|
|
Result:=Back;
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadSingle ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadCurrency: Currency;
|
|
var
|
|
Value: String;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Result:=StrToFloat(Value);
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadCurrency ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadDate: TDateTime;
|
|
var
|
|
Value: String;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Result:=StrToFloat(Value);
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadDate ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadIdent(ValueType: TValueType): String;
|
|
begin
|
|
Result:=FElement['value'];
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadIdent ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadInt8: ShortInt;
|
|
var
|
|
Value: String;
|
|
FloatError: integer;
|
|
Back: ShortInt;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Val(Value, Back, FloatError);
|
|
if FloatError=0 then ;
|
|
Result:=Back;
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadInt8 ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadInt16: SmallInt;
|
|
var
|
|
Value: String;
|
|
FloatError: integer;
|
|
Back: SmallInt;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Val(Value, Back, FloatError);
|
|
if FloatError=0 then ;
|
|
Result:=Back;
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadInt16 ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadInt32: LongInt;
|
|
var
|
|
Value: String;
|
|
FloatError: integer;
|
|
Back: Longint;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Val(Value, Back, FloatError);
|
|
if FloatError=0 then ;
|
|
Result:=Back;
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadInt32 ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadInt64: Int64;
|
|
var
|
|
Value: String;
|
|
FloatError: integer;
|
|
Back: Int64;
|
|
begin
|
|
Result:=0;
|
|
Value:=FElement['value'];
|
|
Val(Value, Back, FloatError);
|
|
if FloatError=0 then ;
|
|
Result:=Back;
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadInt64 ',Result);
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadSet(SetType: Pointer): Integer;
|
|
var
|
|
s: String;
|
|
StartPos: Integer;
|
|
EndPos: LongInt;
|
|
Name: String;
|
|
i: LongInt;
|
|
begin
|
|
Result:=0;
|
|
s:=FElement['value'];
|
|
StartPos:=1;
|
|
while (StartPos<=length(s)) do begin
|
|
EndPos:=StartPos;
|
|
while (EndPos<=length(s)) and (s[EndPos]<>',') do inc(EndPos);
|
|
if EndPos>StartPos then begin
|
|
Name:=copy(s,StartPos,EndPos-StartPos);
|
|
i:=GetEnumValue(PTypeInfo(SetType),Name);
|
|
Result:=Result or (1 shl i);
|
|
end;
|
|
StartPos:=EndPos+1;
|
|
end;
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadSet ',HexStr(Cardinal(Result),8));
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadStr: String;
|
|
begin
|
|
Result:=FElement['value'];
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadStr "',Result,'"');
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadString(StringType: TValueType): String;
|
|
begin
|
|
Result:=FElement['value'];
|
|
if (StringType=vaString) and (length(Result)>255) then
|
|
raise Exception.Create('TXMLObjectReader.ReadString invalid StringType');
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadString "',Result,'"');
|
|
end;
|
|
|
|
function TXMLObjectReader.ReadWideString: WideString;
|
|
var
|
|
ValueAsUTF8: String;
|
|
begin
|
|
ValueAsUTF8:=FElement['value'];
|
|
Result:=System.UTF8Decode(ValueAsUTF8);
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.ReadWideString "',ValueAsUTF8,'"');
|
|
end;
|
|
|
|
{$IFDEF USE_NEW_READER_WRITER}
|
|
function TXMLObjectReader.ReadUnicodeString: UnicodeString;
|
|
var
|
|
ValueAsUTF8: String;
|
|
begin
|
|
ValueAsUTF8:=FElement['value'];
|
|
Result:=System.UTF8Decode(ValueAsUTF8);
|
|
ReadValue;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TXMLObjectReader.SkipComponent(SkipComponentInfos: Boolean);
|
|
var
|
|
NextNode: TDOMNode;
|
|
begin
|
|
NextNode:=FElement.NextSibling;
|
|
if (NextNode=nil) or (NextNode is TDOMElement) then
|
|
FElement:=TDOMElement(NextNode);
|
|
//writeln('TXMLObjectReader.SkipComponent ');
|
|
end;
|
|
|
|
procedure TXMLObjectReader.SkipValue;
|
|
begin
|
|
ReadValue;
|
|
//writeln('TXMLObjectReader.SkipValue ');
|
|
end;
|
|
|
|
{$IFDEF HasReadWriteBuf}
|
|
procedure TXMLObjectReader.Read(var Buf; Count: LongInt);
|
|
begin
|
|
raise Exception.Create('TODO: TXMLObjectReader.Read');
|
|
//writeln('TXMLObjectReader.Read ');
|
|
end;
|
|
{$ENDIF}
|
|
|
|
end.
|