Componentes.Terceros.RemObj.../internal/5.0.23.613/1/RemObjects SDK for Delphi/Source/uROXmlRpcMessage.pas
david 2824855ea7 - Modificación del paquete RemObjects_Core_D10 para que sea un paquete de runtime/designtime (antes era designtime sólo)
- Recompilación en Delphi10 de todos los paquetes de RO para generar las DCU's en Lib\D10
- Recompilación en Delphi10 de todos los paquetes de DA para generar las DCU's en Lib\D10

git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.RemObjects@9 b6239004-a887-0f4b-9937-50029ccdca16
2007-09-10 14:06:19 +00:00

1276 lines
42 KiB
ObjectPascal

unit uROXmlRpcMessage;
{----------------------------------------------------------------------------}
{ RemObjects SDK Library - Core Library }
{ }
{ compiler: Delphi 5 and up, Kylix 2 and up }
{ platform: Win32, Linux }
{ }
{ (c)opyright RemObjects Software. all rights reserved. }
{ }
{ Using this code requires a valid license of the RemObjects SDK }
{ which can be obtained at http://www.remobjects.com. }
{----------------------------------------------------------------------------}
{$I RemObjects.inc}
interface
uses
{$IFDEF REMOBJECTS_TRIAL}uROTrial, {$ENDIF}
Classes, SysUtils, TypInfo, uROClasses, uROTypes, uROHttpTools,
uROSerializer, uROClient, uROClientIntf, uROXmlIntf, uRoRes, FMTBcd;
type
TROSimpleXmlWriter = class;
EROXmlRcpSerializerException = class(Exception);
TROXmlRpcType = (XRpcRequest, XRpcResponse, XRpcFaultResponse);
TROXmlRpcState = (XrsParam, XrsArray, Xrsstruct);
TROXmlRpcSerializer = class(TROSerializer)
private
fCurrState: TROXmlRpcState;
fDocument: IXMLDocument;
fStateCount: Integer;
fStates: array of TROXmlRpcState;
fType: TROXmlRpcType;
fMethodName: string;
fCurrentParamElement: IXMLNode;
fWriter: TROSimpleXmlWriter;
procedure PushState(aKind: TROXmlRpcState);
function PopState: TROXmlRpcState;
procedure BeginValue(aName: string);
procedure EndValue;
function StructGetMember(aRoot: IXMLNode; const aName: string; aRaiseException: Boolean): IXMLNode;
function GetValueNodecontentsForName(const aName: string): IXMLNode;
protected
procedure BeginWriteObject(const aName: string; aClass : TClass; anObject: TObject; var LevelRef : IUnknown;
var IsValidType : boolean; out IsAssigned:Boolean; ArrayElementId : integer = -1); override;
procedure EndWriteObject(const aName: string; aClass : TClass; anObject: TObject; const LevelRef : IUnknown); override;
procedure CustomWriteObject(const aName : string; aClass : TClass; const Ref; ArrayElementId : integer = -1); override;
procedure CustomReadObject(const aName: string; aClass: TClass;var Ref; ArrayElementId: integer);override;
procedure BeginReadObject(const aName : string; aClass : TClass; var anObject : TObject; var LevelRef : IUnknown;
var IsValidType : boolean; ArrayElementId : integer = -1); override;
procedure EndReadObject(const aName : string; aClass : TClass; var anObject : TObject; const LevelRef : IUnknown); override;
public
{ Writers }
procedure WriteInteger(const aName : string; anOrdType : TOrdType; const Ref; ArrayElementId : integer = -1); override;
procedure WriteInt64(const aName : string; const Ref; ArrayElementId : integer = -1); override;
procedure WriteEnumerated(const aName : string; anEnumTypeInfo : PTypeInfo; const Ref; ArrayElementId : integer = -1); override;
procedure WriteUTF8String(const aName : string; const Ref; ArrayElementId : integer = -1); override;
procedure WriteWideString(const aName : string; const Ref; ArrayElementId : integer = -1); override;
procedure WriteDateTime(const aName : string; const Ref; ArrayElementId : integer = -1); override;
procedure WriteDouble(const aName : string; aFloatType : TFloatType; const Ref; ArrayElementId : integer = -1); override;
procedure WriteVariant(const aName : string; const Ref; ArrayElementId : integer = -1); override;
procedure WriteXml(const aName: String; const Ref; ArrayElementId: Integer = -1); override;
procedure WriteGuid(const aName: String; const Ref; ArrayElementId: Integer = -1); override;
procedure WriteDecimal(const aName: String; const Ref; ArrayElementId: Integer = -1); override;
procedure WriteBinary(const aName : string; const Ref; ArrayElementId : integer = -1);override;
procedure WriteStruct(const aName : string; const Ref; ArrayElementId : integer = -1); override;
procedure WriteArray(const aName : string; const Ref; ArrayElementId : integer = -1); override;
procedure WriteException(const aName : string; const Ref; ArrayElementId : integer = -1); overload; override;
{ Readers }
procedure ReadInteger(const aName : string; anOrdType : TOrdType; var Ref; ArrayElementId : integer = -1); override;
procedure ReadInt64(const aName : string; var Ref; ArrayElementId : integer = -1); override;
procedure ReadEnumerated(const aName : string; anEnumTypeInfo : PTypeInfo; var Ref; ArrayElementId : integer = -1); override;
procedure ReadUTF8String(const aName : string; var Ref; ArrayElementId : integer = -1; iMaxLength:integer=-1); override;
procedure ReadWideString(const aName : string; var Ref; ArrayElementId : integer = -1; iMaxLength:integer=-1); override;
procedure ReadDateTime(const aName : string; var Ref; ArrayElementId : integer = -1); override;
procedure ReadDouble(const aName : string; aFloatType : TFloatType; var Ref; ArrayElementId : integer = -1); override;
procedure ReadVariant(const aName : string; var Ref; ArrayElementId : integer = -1); override;
procedure ReadXml(const aName: String; var Ref; ArrayElementId: Integer = -1); override;
procedure ReadDecimal(const aName: String; var Ref; ArrayElementId: Integer = -1); override;
procedure ReadGuid(const aName: String; var Ref; ArrayElementId: Integer = -1); override;
procedure ReadBinary(const aName : string; var Ref; ArrayElementId : integer = -1);override;
function ReadStruct(const aName : string; aClass : TClass; var Ref; ArrayElementId : integer = -1): Boolean; override;
function ReadArray(const aName : string; aClass : TClass; var Ref; ArrayElementId : integer = -1): Boolean; override;
procedure ReadException(const aName : string; var Ref; ArrayElementId : integer = -1); overload;override;
public
constructor Create;
destructor Destroy; override;
procedure InitializeWrite(aType: TROXmlRpcType; aMethodName: string);
procedure InitializeRead(aStream: TStream);
property aType: TROXmlRpcType read fType write fType;
property aMethodName: string read fMethodName write fMethodName;
procedure WriteToStream(aStream: TStream);
procedure WriteException(aCode: Integer; aMsg: string);reintroduce;overload;
procedure ReadException(var aCode: Integer; var aMsg: string);reintroduce;overload;
end;
TROXmlRpcMessage = class(TROMessage)
protected
{ Internals }
function ReadException: Exception; override;
procedure WriteException(aStream: TStream; anException: Exception); override;
function CreateSerializer: TROSerializer; override;
{ IROMessage }
procedure Initialize(const aTransport: IROTransport; const anInterfaceName, aMessageName: string; aType: TMessageType); override;
procedure WriteToStream(aStream: TStream); override;
procedure ReadFromStream(aStream: TStream); override;
procedure InitializeExceptionMessage(const aTransport: IROTransport;
const aLibraryName: String; const anInterfaceName: String;
const aMessageName: String); override;
public
function IsValidMessage(aData: PChar; aLength: Integer): boolean; override;
end;
TROSimpleXmlWriter = class
private
fElementStack: TStrings;
fOutput: TMemoryStream;
fFinished: Boolean;
procedure WriteRawString(const s: string);
public
procedure WriteStartElement(const aElementName: string);
procedure WriteEndElement;
procedure WriteString(const aValue: WideString);
procedure SaveToStream(aDest: TStream);
constructor Create;
destructor Destroy; override;
property Finished: Boolean read fFinished;
end;
function StringToCurrInvariant(const aValue: string): Currency;
function CurrToStringInvariant(aValue: Currency): String;
implementation
uses
uROCompression, DateUtils, StrUtils, uROBinaryHelpers;
function FirstNonText(node: IXMLNode): IXMLNode;
begin
Result := Node;
while (Result <> nil) and (copy(Result.Name, 1, 1) = '#') do
Result := Result.NextSibling;
end;
function StringToCurrInvariant(const aValue: string): Currency;
var
s: string;
i: Integer;
Res: Int64;
begin
s := aValue;
i := Pos('.', s);
if i = 0 then begin
s := s + '.0000';
end
else begin
if Length(s) - i < 4 then
s := s + StringOfChar('0', 4 - (Length(s) - i))
else if Length(s) - i > 4 then
Delete(s, i + 5, MaxInt);
end;
Delete(s, i, 1);
Res := StrToInt64(s);
Move(Res, Result, 8);
end;
function CurrToStringInvariant(aValue: Currency): string;
var
Val: Int64;
begin
Move(aValue, Val, 8);
Result := IntToStr(Val); // we need it locale independent and without any thousand seperators
if Length(Result) <= 4 then
Result := StringOfChar('0', 5 - Length(Result)) + Result;
Insert('.', Result, Length(Result) - 3);
end;
{ TROXmlRpcMessage }
function TROXmlRpcMessage.CreateSerializer: TROSerializer;
begin
result := TROXmlRpcSerializer.Create;
end;
procedure TROXmlRpcMessage.Initialize(const aTransport: IROTransport;
const anInterfaceName, aMessageName: string; aType: TMessageType);
begin
inherited;
SetHTTPInfo(aTransport, DataFormatXml);
case aType of
mtRequest: TROXmlRpcSerializer(Serializer).InitializeWrite(XRpcRequest, anInterfaceName + '.' + aMessageName);
mtResponse: TROXmlRpcSerializer(Serializer).InitializeWrite(XRpcResponse, anInterfaceName + '.' + aMessageName);
mtException: TROXmlRpcSerializer(Serializer).InitializeWrite(XRpcFaultResponse, anInterfaceName + '.' + aMessageName)
else
raise EROXmlRcpSerializerException.Create('Unsupported request type');
end
end;
procedure TROXmlRpcMessage.InitializeExceptionMessage(
const aTransport: IROTransport; const aLibraryName, anInterfaceName,
aMessageName: String);
begin
inherited;
SetHTTPInfo(aTransport, DataFormatXml);
end;
function TROXmlRpcMessage.IsValidMessage(aData: PChar;
aLength: Integer): boolean;
var
str: string;
begin
SetString(str, aData, aLength);
Result :=
(Pos('<methodRequest>', str) > 0) or
(Pos('<methodCall>', str) > 0) or
(Pos('<methodResponse>', str) > 0);
end;
function TROXmlRpcMessage.ReadException: Exception;
var
lCode: Integer;
lMsg: string;
cl: string;
idx: Integer;
begin
TROXmlRpcSerializer(Serializer).ReadException(lCode, lMsg);
case lCode of
-32700, -32701, -32702, -32600, -32603, -32300:
begin
Result := EROXmlRcpSerializerException.Create(lMsg);
exit;
end;
end;
idx := Pos(': ', lMsg);
if (idx = 0) or (idx > Pos(' ', lMsg)) then begin
result := Exception.Create(lMsg);
exit;
end;
cl := Copy(lMsg, 1, Idx -1);
Delete(lMsg, 1, Idx + 1);
result := CreateException(cl, lMsg);
// Reads the other fields which have been properly serialized
//if result.InheritsFrom(EROException) then
//EROException(Result).Read(Serializer);
end;
procedure TROXmlRpcMessage.ReadFromStream(aStream: TStream);
var
s: string;
encryptedheader: array[0..5] of char;
begin
inherited;
aStream.Position := 0;
try
TROXmlRpcSerializer(Serializer).InitializeRead(aStream);
except
aStream.Position := 0;
aStream.Read(encryptedheader, 6);
if (encryptedheader[0] = 'r') and (encryptedheader[1] = 'o') and
(encryptedheader[2] = 'r') and (encryptedheader[3] = 'o') and
(encryptedheader[4] = 'c') and (encryptedheader[5] = 'k') then
RaiseInvalidStreamError(err_InvalidHeaderEncrypted, [], aStream);
raise;
end;
if TROXmlRpcSerializer(Serializer).aType = XRpcFaultResponse then
ProcessException
else begin
s := TROXmlRpcSerializer(Serializer).aMethodName;
if pos('.', s) = 0 then
MessageName := s
else begin
MessageName := copy(s, pos('.', s) + 1, MaxInt);
InterfaceName := copy(s, 1, pos('.', s) - 1);
end;
end;
end;
procedure TROXmlRpcMessage.WriteException(aStream: TStream;
anException: Exception);
begin
inherited;
TROXmlRpcSerializer(Serializer).InitializeWrite(XRpcFaultResponse, '');
if anException is EROXmlRcpSerializerException then
TROXmlRpcSerializer(Serializer).WriteException(-32600, anException.Message)
else
TROXmlRpcSerializer(Serializer).WriteException(1, anException.ClassName+': '+anException.Message);
// if anException is EROException then EROException(anException).Write(Serializer);
TROXmlRpcSerializer(Serializer).WriteToStream(aStream);
end;
procedure TROXmlRpcMessage.WriteToStream(aStream: TStream);
begin
TROXmlRpcSerializer(Serializer).WriteToStream(aStream);
inherited;
end;
{ TROXmlRpcSerializer }
constructor TROXmlRpcSerializer.Create;
begin
inherited Create;
end;
procedure TROXmlRpcSerializer.InitializeWrite(aType: TROXmlRpcType; aMethodName: string);
begin
ftype := aType;
fMethodName := aMethodName;
if fWriter <> nil then fWriter.Free;
fWriter := TROSimpleXmlWriter.Create;
case aType of
XRpcFaultResponse:
begin
fWriter.WriteStartElement('methodResponse');
fWriter.WriteStartElement('fault');
fCurrState := XrsArray;
end;
XRpcResponse:
begin
fWriter.WriteStartElement('methodResponse');
fWriter.WriteStartElement('params');
end;
XRpcRequest:
begin
fWriter.WriteStartElement('methodCall');
fWriter.WriteStartElement('methodName');
fWriter.WriteString(fMethodName);
fWriter.WriteEndElement();
fWriter.WriteStartElement('params');
end;
end;
end;
procedure TROXmlRpcSerializer.CustomWriteObject(const aName: string;
aClass: TClass; const Ref; ArrayElementId: integer);
var
anObject: TObject absolute Ref;
lTemp: Binary;
begin
if anObject is TStream then begin
lTemp := Binary.Create;
try
TStream(anObject).Seek(0, soFromBeginning);
EncodeStream(TStream(anObject), lTemp);
fWriter.WriteString(lTemp.ToString);
finally
lTemp.Free;
end;
end else
inherited;
end;
procedure TROXmlRpcSerializer.BeginWriteObject(const aName: string;
aClass: TClass; anObject: TObject; var LevelRef: IInterface;
var IsValidType: boolean; out IsAssigned: Boolean;
ArrayElementId: integer);
begin
if aClass <> nil then
inherited;
if not assigned(anObject) then raise EROXmlRcpSerializerException.Create('Nil objects not supported by xmlrpc');
IsAssigned := True;
BeginValue(aName);
if anObject is TStream then begin
fWriter.WriteStartElement('base64');
// write binary
IsValidType := true; // Adds streams/binaries as supported type
end else if anObject is TROArray then begin
fWriter.WriteStartElement('array');
PushState(XrsArray);
fWriter.WriteStartElement('data');
IsValidType := true; // Adds Array as supported type
end
else if anObject is TROComplexType then begin
fWriter.WriteStartElement('struct');
PushState(Xrsstruct);
IsValidType := true;
end;
end;
procedure TROXmlRpcSerializer.EndWriteObject(const aName: string;
aClass: TClass; anObject: TObject; const LevelRef: IInterface);
begin
if anObject is TStream then begin
fWriter.WriteEndElement;
end else if anObject is TROArray then begin
fWriter.WriteEndElement;
fWriter.WriteEndElement;
if PopState <> xrsArray then raise EROXmlRcpSerializerException.Create('Invalid state');
end
else if anObject is TROComplexType then begin
if PopState <> Xrsstruct then raise EROXmlRcpSerializerException.Create('Invalid state');
fWriter.WriteEndElement;
end;
EndValue;
end;
procedure TROXmlRpcSerializer.WriteDateTime(const aName: string; const Ref;
ArrayElementId: integer);
begin
BeginValue(aName);
fWriter.WriteStartElement('dateTime.iso8601');
fWriter.WriteString(FormatDateTime('yyyymmdd"T"hh:nn:ss', DateTime(Ref)));
fWriter.WriteEndElement;
EndValue;
end;
procedure TROXmlRpcSerializer.WriteEnumerated(const aName: string;
anEnumTypeInfo: PTypeInfo; const Ref; ArrayElementId: integer);
begin
BeginValue(aName);
if anEnumTypeInfo.Name = 'Boolean' then
begin
fWriter.WriteStartElement('boolean');
if boolean(Ref) then
fWriter.WriteString('1')
else
fWriter.WriteString('0');
fWriter.WriteEndElement;
end else
begin
fWriter.WriteStartElement('string');
fWriter.WriteString(GetEnumName(anEnumTypeInfo, Ord(byte(Ref))));
fWriter.WriteEndElement;
end;
EndValue;
end;
procedure TROXmlRpcSerializer.WriteDouble(const aName: string;
aFloatType: TFloatType; const Ref; ArrayElementId: integer);
var
s: string;
begin
// Str doesn't use the locale settings
case aFloatType of
ftSingle: s := FloatToStr(Single(Ref));
ftDouble: s := FloatToStr(Double(Ref));
ftExtended: s := FloatToStr(Extended(Ref));
ftComp: s := FloatToStr(Comp(Ref));
ftCurr:
begin
s :=CurrToStringInvariant(currency(Ref));
WriteUTF8String(aName, s);
exit;
end;
end;
s := StringReplace(s, DecimalSeparator, '.', []);
BeginValue(aName);
fWriter.WriteStartElement('double');
fWriter.WriteString(s);
fWriter.WriteEndElement();
EndValue();
end;
procedure TROXmlRpcSerializer.WriteInt64(const aName: string; const Ref;
ArrayElementId: integer);
begin
BeginValue(aName);
fWriter.WriteStartElement('string'); // no way around
fWriter.WriteString(IntToStr(Int64(ref)));
fWriter.WriteEndElement();
EndValue();
end;
procedure TROXmlRpcSerializer.WriteInteger(const aName: string;
anOrdType: TOrdType; const Ref; ArrayElementId: integer);
var
aIntVal: Integer;
begin
case anOrdType of
otSByte: aIntVal := ShortInt(Ref);
otUByte: aIntVal := Byte(Ref);
otSWord: aIntVal := SmallInt(Ref);
otUWord: aIntVal := Word(Ref);
otSLong: aIntVal := Integer(Ref);
otULong: aIntVal := Cardinal(Ref);
else
aIntVal := 0;
end;
BeginValue(aName);
fWriter.WriteStartElement('i4');
fWriter.WriteString(IntToStr(aIntVal));
fWriter.WriteEndElement();
EndValue();
end;
procedure TROXmlRpcSerializer.WriteUTF8String(const aName: string; const Ref;
ArrayElementId: integer);
begin
BeginValue(aName);
fWriter.WriteStartElement('string');
fWriter.WriteString(string(Ref));
fWriter.WriteEndElement;
EndValue;
end;
procedure TROXmlRpcSerializer.WriteToStream(aStream: TStream);
begin
if not fWriter.Finished then
begin
fWriter.WriteEndElement(); // params/fault
fWriter.WriteEndElement(); // methodRequest/methodRespones
end;
fWriter.SaveToStream(aStream);
end;
procedure TROXmlRpcSerializer.WriteVariant(const aName: string; const Ref;
ArrayElementId: integer);
begin
raise EROXmlRcpSerializerException.Create('Variants not supported by xmlrpc');
end;
procedure TROXmlRpcSerializer.WriteWideString(const aName: string;
const Ref; ArrayElementId: integer);
begin
BeginValue(aName);
fWriter.WriteStartElement('string');
fWriter.WriteString(WideString(Ref));
fWriter.WriteEndElement;
EndValue;
end;
procedure TROXmlRpcSerializer.WriteException(aCode: Integer; aMsg: string);
begin
BeginValue('');
fWriter.WriteStartElement('struct');
PushState(Xrsstruct);
WriteInteger('faultCode', otSLong, aCode);
WriteUTF8String('faultString', aMsg);
if PopState <> Xrsstruct then raise EROXmlRcpSerializerException.Create('Invalid state');
fWriter.WriteEndElement;
EndValue;
end;
procedure TROXmlRpcSerializer.InitializeRead(aStream: TStream);
var
el, tmp: IXMLNode;
begin
fDocument := NewROXmlDocument;
fDocument.New;
fDocument.LoadFromStream(aStream);
if (fDocument.DocumentNode.Name = 'methodRequest') or (fDocument.DocumentNode.Name = 'methodCall') then
fType := XRpcRequest
else if fDocument.DocumentNode.Name = 'methodResponse' then
fType := XRpcResponse
else
raise EROXmlRcpSerializerException.Create('Invalid xmlrpc document');
el := fDocument.DocumentNode;
if fType = xrpcRequest then begin
tmp := el.GetNodeByName('methodName');
if tmp = nil then raise EROXmlRcpSerializerException.Create('Invalid xmlrpc document');
fMethodName := tmp.Value;
tmp := el.GetNodeByName('params');
if tmp = nil then raise EROXmlRcpSerializerException.Create('Invalid xmlrpc document');
fCurrentParamElement := FirstNonText(tmp.FirstChild);
end else begin
tmp := el.GetNodeByName('fault');
if tmp = nil then begin
tmp := el.GetNodeByName('params');
if tmp = nil then raise EROXmlRcpSerializerException.Create('Invalid xmlrpc document');
tmp := FirstNonText(tmp.FirstChild);
end
else
fType := XRpcFaultResponse;
fCurrentParamElement := tmp;
end;
end;
function TROXmlRpcSerializer.StructGetMember(aRoot: IXMLNode; const aName: string; aRaiseException: Boolean): IXMLNode;
var
i: Integer;
tmp: IXMLNode;
begin
for i := 0 to aRoot.ChildrenCount -1 do
begin
Result := aRoot.Children[i];
tmp := Result.GetNodeByName('name');
if (tmp <> nil) and (tmp.Value = aName) then begin
Result := result.GetNodeByName('value');
if (Result = nil) and aRaiseException then EROXmlRcpSerializerException.Create('Unknown node: '+aName);
exit;
end;
end;
if (Result = nil) and aRaiseException then EROXmlRcpSerializerException.Create('Unknown node: '+aName);
result := nil;
end;
procedure TROXmlRpcSerializer.ReadException(var aCode: Integer; var aMsg: string);
var
tmp, val: IXMLNode;
begin
tmp := fCurrentParamElement.GetNodeByName('value');
if tmp = nil then raise EROXmlRcpSerializerException.Create('Invalid xmlrpc exception');
tmp := tmp.GetNodeByName('struct');
if tmp = nil then raise EROXmlRcpSerializerException.Create('Invalid xmlrpc exception');
val := StructGetMember(tmp, 'faultCode', true);
val := val.GetNodeByName('i4');
if val = nil then raise EROXmlRcpSerializerException.Create('Invalid xmlrpc exception');
acode := val.Value;
val := StructGetMember(tmp, 'faultString', true);
val := val.GetNodeByName('string');
if val = nil then raise EROXmlRcpSerializerException.Create('Invalid xmlrpc exception');
aMsg := val.Value;
end;
procedure TROXmlRpcSerializer.BeginReadObject(const aName: string;
aClass: TClass; var anObject: TObject; var LevelRef: IInterface;
var IsValidType: boolean; ArrayElementId: integer);
var
el: IXMLNode;
begin
if aClass.InheritsFrom(TStream) then begin
anObject := TROBinaryMemoryStream.Create;
IsValidType := true;
end
else if aclass.InheritsFrom(TROArray) then begin
anObject := TROComplexTypeClass(aClass).Create;
IsValidType := true;
end
else if aclass.InheritsFrom(TROComplexType) then begin
anObject := TROComplexTypeClass(aClass).Create;
el :=GetValueNodeContentsForName(aName);
if (el = nil) or (el.Name <> 'struct') then raise EROXmlRcpSerializerException.Create('Not a struct');
PushState(Xrsstruct);
fCurrentParamElement := el;
IsValidType := true;
end else
IsValidType := false;
end;
procedure TROXmlRpcSerializer.CustomReadObject(const aName: string;
aClass: TClass; var Ref; ArrayElementId: integer);
var
obj: TObject absolute ref;
el,
tmp: IXMLNode;
ci: Integer;
itemref: Pointer;
tmpstream: Binary;
begin
if obj is TROArray then begin
el := GetValueNodeContentsForName(aName);
if el.Name <> 'array' then raise EROXmlRcpSerializerException.Create('Not at array');
el := FirstNonText(el.FirstChild);
if el.Name <> 'data' then raise EROXmlRcpSerializerException.Create('Not an array');
tmp := fCurrentParamElement;
fCurrentParamElement := FirstNonText(el.FirstChild);
PushState(XrsArray);
ci := 0;
TROArray(obj).Resize(el.ChildrenCount); // might differ if there are junk entries
while fCurrentParamElement <> nil do begin
if TROArray(obj).GetItemClass <> nil then begin
itemref := nil;
Read(RO_ArrayItemName, TROArray(obj).GetItemType, itemref, ci);
TROArray(obj).SetItemRef(ci, itemref);
end else begin
itemref := TROArray(obj).GetItemRef(ci);
Read(RO_ArrayItemName, TROArray(obj).GetItemType, itemref^, ci);
end;
inc(ci);
end;
if ci <> el.ChildrenCount then TROArray(obj).Resize(ci);
if PopState <> XrsArray then raise EROXmlRcpSerializerException.Create('Invalid state');
fCurrentParamElement := tmp;
end else if obj is TStream then begin
el := GetValueNodecontentsForName(aName);
if el.Name <> 'base64' then raise EROXmlRcpSerializerException.Create('Not a base64 block');
tmpstream := TROBinaryMemoryStream.Create(el.Value);
try
DecodeStream(tmpstream, TStream(obj));
TStream(obj).Position := 0;
finally
tmpstream.Free;
end;
end else
inherited;
end;
procedure TROXmlRpcSerializer.EndReadObject(const aName: string;
aClass: TClass; var anObject: TObject; const LevelRef: IInterface);
var
el: IXMLNode;
begin
if aclass.InheritsFrom(TROComplexType) and not (aClass.InheritsFrom(TStream) or aclass.InheritsFrom(TROArray)) then begin
el := fCurrentParamElement;
if (el = nil) or (el.Name <> 'struct') or (PopState() <> Xrsstruct)then raise EROXmlRcpSerializerException.Create('Invalid state');
case fCurrState of
XrsArray:
el := el.Parent;
XrsParam:
el := el.Parent.Parent;
Xrsstruct:
el := el.Parent.Parent.Parent;
end;
fCurrentParamElement := FirstNonText(el.NextSibling);
end
end;
procedure TROXmlRpcSerializer.ReadDateTime(const aName: string; var Ref;
ArrayElementId: integer);
var
el: IXMLNode;
s: string;
h,m,sec: Integer;
begin
el := GetValueNodeContentsForName(aName);
if el.Name <> 'dateTime.iso8601' then raise EROXmlRcpSerializerException.Create('Not a datetime');
s := el.Value;
if (Length(s) <> 17) or (s[9] <> 'T') then raise EROXmlRcpSerializerException.Create('Invalid date format');
DateTime(ref) := EncodeDate(
StrToInt(copy(s,1,4)), //yyyy
StrToInt(copy(s,5,2)), //mm
StrToInt(copy(s,7,2))); //dd
// skip the T
delete(s,1,9);
h := StrToInt(copy(s,1,2)); //hh
delete(s,1,2);
if copy(s,1,1) = ':' then delete(s,1,1);
m := StrToInt(copy(s,1,2)); //nn
delete(s,1,2);
if copy(s,1,1) = ':' then delete(s,1,1);
sec := StrToInt(copy(s,1,2)); //ss
DateTime(ref) := DateTime(ref) + EncodeTime(H, M, Sec, 0)
end;
procedure TROXmlRpcSerializer.ReadEnumerated(const aName: string;
anEnumTypeInfo: PTypeInfo; var Ref; ArrayElementId: integer);
var
enumval: Integer;
s: string;
el: IXMLNode;
begin
if anEnumTypeInfo.Name = 'Boolean' then begin
el := GetValueNodeContentsForName(aName);
if el.Name <> 'boolean' then raise EROXmlRcpSerializerException.Create('Not a boolean');
if (el.Value = '1') or (el.Value = 'true') then
enumval := 1
else
enumval := 0;
end else begin
ReadUTF8String(aName, s);
enumval := GetEnumValue(anEnumTypeInfo, s);
if enumval < 0 then
raise EROXmlRcpSerializerException.Create('Unknown value "'+ s +'" for enum "'+anEnumTypeInfo^.Name+'"');
end;
byte(Ref) := enumval;
end;
procedure TROXmlRpcSerializer.ReadDouble(const aName: string;
aFloatType: TFloatType; var Ref; ArrayElementId: integer);
var
d: Double;
el: IXMLNode;
c: Integer;
s: string;
begin
if aFloatType = ftCurr then
begin
ReadUTF8String(aName, s);
Currency(Ref) := StringToCurrInvariant(s);
exit;
end;
el := GetValueNodeContentsForName(aName);
if el.Name <> 'double' then raise EROXmlRcpSerializerException.Create('Not a double');
Val(el.Value, d, c);
if c <> 0 then raise EROXmlRcpSerializerException.Create('Invalid float format');
case aFloatType of
ftSingle: Single(Ref) := d;
ftDouble: Double(Ref) := d;
ftExtended: Extended(Ref) := d;
ftComp: Comp(Ref) := d;
end;
end;
procedure TROXmlRpcSerializer.ReadInt64(const aName: string; var Ref;
ArrayElementId: integer);
var
s: string;
begin
ReadUTF8String(aName, s);
Int64(Ref) := StrToInt64(s);
end;
procedure TROXmlRpcSerializer.ReadInteger(const aName: string;
anOrdType: TOrdType; var Ref; ArrayElementId: integer);
var
el: IXMLNode;
aVal: Integer;
begin
el := GetValueNodeContentsForName(aName);
if (el.Name <> 'i4') and (el.Name <> 'int') then raise EROXmlRcpSerializerException.Create('Not an integer');
aVal := StrToInt(el.Value);
case anOrdType of
otSByte: ShortInt(Ref) := aVal;
otUByte: Byte(Ref) := aVal;
otSWord: SmallInt(Ref) := aVal;
otUWord: Word(Ref) := aVal;
otSLong: Longint(Ref) := aVal;
otULong: Cardinal(Ref) := aVal;
end;
end;
procedure TROXmlRpcSerializer.ReadUTF8String(const aName: string; var Ref;
ArrayElementId, iMaxLength: integer);
var
el: IXMLNode;
begin
el := GetValueNodeContentsForName(aName);
if el.Name <> 'string' then raise EROXmlRcpSerializerException.Create('Not a string');
string(Ref) := el.Value;
end;
procedure TROXmlRpcSerializer.ReadVariant(const aName: string; var Ref;
ArrayElementId: integer);
begin
raise EROXmlRcpSerializerException.Create('Variants not supported by xmlrpc');
end;
procedure TROXmlRpcSerializer.ReadWideString(const aName: string; var Ref;
ArrayElementId, iMaxLength: integer);
var
el: IXMLNode;
begin
el := GetValueNodeContentsForName(aName);
if el.Name <> 'string' then raise EROXmlRcpSerializerException.Create('Not a string');
WideString(Ref) := el.Value;
end;
destructor TROXmlRpcSerializer.Destroy;
begin
fWriter.Free;
inherited Destroy;
end;
function TROXmlRpcSerializer.PopState: TROXmlRpcState;
begin
if fStateCount = 0 then raise EROXmlRcpSerializerException.Create('Invalid state');
Dec(fStateCount);
Result := fCurrState;
fCurrState := fStates[fStatecount];
end;
procedure TROXmlRpcSerializer.PushState(aKind: TROXmlRpcState);
begin
if fStateCount = Length(fStates) then
SetLength(fStates, Length(FStates) + 4);
fStates[fStateCount] := fCurrState;
Inc(FStateCount);
fCurrState := aKind;
end;
procedure TROXmlRpcSerializer.BeginValue(aName: string);
begin
case fCurrState of
XrsParam: fWriter.WriteStartElement('param');
Xrsstruct:
begin
fWriter.WriteStartElement('member');
fWriter.WriteStartElement('name');
fWriter.WriteString(aName);
fWriter.WriteEndElement;
end;
end;
fWriter.WriteStartElement('value');
end;
procedure TROXmlRpcSerializer.EndValue;
begin
fWriter.WriteEndElement;
case fCurrState of
XrsParam,
Xrsstruct:
fWriter.WriteEndElement;
end;
end;
function TROXmlRpcSerializer.GetValueNodecontentsForName(
const aName: string): IXMLNode;
begin
if fCurrentParamElement = nil then raise EROXmlRcpSerializerException.Create('Invalid state');
case fCurrState of
XrsArray:
begin
Result := FirstNonText(fCurrentParamElement.FirstChild);
fCurrentParamElement := FirstNonText(fCurrentParamElement.NextSibling);
end;
XrsParam:
begin
Result := FirstNonText(fCurrentParamElement.FirstChild);
if result = nil then raise EROXmlRcpSerializerException.Create('Invalid state');
if result.Name = 'value' then Result := FirstNonText(Result.FirstChild);
fCurrentParamElement := FirstNonText(fCurrentParamElement.NextSibling);
end;
Xrsstruct:
begin
Result := fCurrentParamElement;
if result = nil then raise EROXmlRcpSerializerException.Create('Invalid state');
Result := StructGetMember(Result, aName, True);
Result := FirstNonText(Result.FirstChild);
end;
end;
end;
procedure TROXmlRpcSerializer.ReadXml(const aName: String; var Ref;
ArrayElementId: Integer);
var
s: WideString;
res: IXMLDocument;
begin
ReadWideString(aName, s, ArrayElementID);
if s = '' then
IXmlNode(Ref) := nil
else begin
res := NewROXmlDocument;
Res.New;
Res.XML := s;
IXmlNode(Ref) := res.DocumentNode;
end;
end;
procedure TROXmlRpcSerializer.WriteXml(const aName: String; const Ref;
ArrayElementId: Integer);
var
s: WideString;
begin
if IXMLNode(Ref) = nil then
S := ''
else
s := IXMLNode(Ref).XML;
WriteWideString(aName, s, ArrayElementId);
end;
procedure TROXmlRpcSerializer.ReadDecimal(const aName: String; var Ref;
ArrayElementId: Integer);
var
s: string;
begin
ReadUTF8String(aName, s, ArrayElementId);
if DecimalSeparator <> '.' then s := StringReplace(s, '.', DecimalSeparator, []);
Variant(Ref) := BCDToVariant(StrToBcd(s));
end;
procedure TROXmlRpcSerializer.ReadGuid(const aName: String; var Ref;
ArrayElementId: Integer);
var
s: string;
begin
ReadUTF8String(aName, s, ArrayElementId);
if Copy(s,1,1) <> '{' then s := '{'+s+'}';
string(Ref) := s;
end;
procedure TROXmlRpcSerializer.WriteDecimal(const aName: String; const Ref;
ArrayElementId: Integer);
var
s: string;
begin
s := BcdToStr(VariantToBCD(variant(Ref)));
if DecimalSeparator <> '.' then s := StringReplace(s, DecimalSeparator, '.', []); // delphi has no way to format a bcd with a specific format
WriteUTF8String(aName, s, ArrayElementId);
end;
procedure TROXmlRpcSerializer.WriteGuid(const aName: String; const Ref;
ArrayElementId: Integer);
var
s: string;
begin
s := GuidToString(StringToGUID(string(Ref)));
s := copy(s,2,length(s) -2); // remove curlies
WriteUTF8String(aName, s, ArrayElementId);
end;
procedure TROXmlRpcSerializer.ReadBinary(const aName: string; var Ref;
ArrayElementId: integer);
var
obj: Binary absolute ref;
el: IXMLNode;
tmpstream: Binary;
begin
el := GetValueNodecontentsForName(aName);
if el.Name <> 'base64' then raise EROXmlRcpSerializerException.Create('Not a base64 block');
Obj := TROBinaryMemoryStream.Create;
tmpstream := TROBinaryMemoryStream.Create(el.Value);
try
DecodeStream(tmpstream, obj);
obj.Position := 0;
finally
tmpstream.Free;
end;
end;
procedure TROXmlRpcSerializer.WriteBinary(const aName: string; const Ref;
ArrayElementId: integer);
var
obj: Binary absolute Ref;
lTemp: Binary;
begin
if not assigned(obj) then raise EROXmlRcpSerializerException.Create('Nil objects not supported by xmlrpc');
BeginValue(aName);
fWriter.WriteStartElement('base64');
// write binary
lTemp := Binary.Create;
try
obj.Seek(0, soFromBeginning);
EncodeStream(obj, lTemp);
fWriter.WriteString(lTemp.ToString);
finally
lTemp.Free;
end;
// endwriteobject
fWriter.WriteEndElement;
EndValue;
end;
function TROXmlRpcSerializer.ReadArray(const aName: string; aClass: TClass;
var Ref; ArrayElementId: integer): Boolean;
var
obj : TROArray absolute Ref;
el,
tmp: IXMLNode;
// ci: Integer;
// itemref: Pointer;
begin
Result:=True;
obj := TROArrayClass(aClass).Create;
el := GetValueNodeContentsForName(aName);
if el.Name <> 'array' then raise EROXmlRcpSerializerException.Create('Not at array');
el := FirstNonText(el.FirstChild);
if el.Name <> 'data' then raise EROXmlRcpSerializerException.Create('Not an array');
tmp := fCurrentParamElement;
fCurrentParamElement := FirstNonText(el.FirstChild);
PushState(XrsArray);
obj.Resize(el.ChildrenCount); // might differ if there are junk entries
(*
ci := 0;
while fCurrentParamElement <> nil do begin
if obj.GetItemClass <> nil then begin
itemref := nil;
Read(RO_ArrayItemName, obj.GetItemType, itemref, ci);
obj.SetItemRef(ci, itemref);
end else begin
itemref := obj.GetItemRef(ci);
Read(RO_ArrayItemName, obj.GetItemType, itemref^, ci);
end;
inc(ci);
end;
if ci <> el.ChildrenCount then obj.Resize(ci);
*)
obj.ReadComplex(Self);
if PopState <> XrsArray then raise EROXmlRcpSerializerException.Create('Invalid state');
fCurrentParamElement := tmp;
end;
function TROXmlRpcSerializer.ReadStruct(const aName: string;
aClass: TClass; var Ref; ArrayElementId: integer): Boolean;
var
obj : TROComplexType absolute Ref;
el: IXMLNode;
begin
Result:=True;
obj := TROComplexTypeClass(aClass).Create;
el :=GetValueNodeContentsForName(aName);
if (el = nil) or (el.Name <> 'struct') then raise EROXmlRcpSerializerException.Create('Not a struct');
PushState(Xrsstruct);
fCurrentParamElement := el;
Obj.ReadComplex(Self);
el := fCurrentParamElement;
if (el = nil) or (el.Name <> 'struct') or (PopState() <> Xrsstruct)then raise EROXmlRcpSerializerException.Create('Invalid state');
case fCurrState of
XrsArray:
el := el.Parent;
XrsParam:
el := el.Parent.Parent;
Xrsstruct:
el := el.Parent.Parent.Parent;
end;
fCurrentParamElement := FirstNonText(el.NextSibling);
end;
procedure TROXmlRpcSerializer.WriteArray(const aName: string; const Ref;
ArrayElementId: integer);
var
obj : TROArray absolute Ref;
begin
if not assigned(obj) then raise EROXmlRcpSerializerException.Create('Nil objects not supported by xmlrpc');
BeginValue(aName);
fWriter.WriteStartElement('array');
PushState(XrsArray);
fWriter.WriteStartElement('data');
obj.WriteComplex(Self);
fWriter.WriteEndElement;
fWriter.WriteEndElement;
if PopState <> xrsArray then raise EROXmlRcpSerializerException.Create('Invalid state');
EndValue;
end;
procedure TROXmlRpcSerializer.WriteStruct(const aName: string; const Ref;
ArrayElementId: integer);
var
obj : TROComplexType absolute Ref;
begin
if not assigned(obj) then raise EROXmlRcpSerializerException.Create('Nil objects not supported by xmlrpc');
BeginValue(aName);
fWriter.WriteStartElement('struct');
PushState(Xrsstruct);
obj.WriteComplex(Self);
if PopState <> Xrsstruct then raise EROXmlRcpSerializerException.Create('Invalid state');
fWriter.WriteEndElement;
EndValue;
end;
procedure TROXmlRpcSerializer.ReadException(const aName: string; var Ref;
ArrayElementId: integer);
begin
// not used
end;
procedure TROXmlRpcSerializer.WriteException(const aName: string;
const Ref; ArrayElementId: integer);
begin
// not used
end;
{ TROSimpleXmlWriter }
constructor TROSimpleXmlWriter.Create;
begin
inherited Create;
fElementStack := TStringList.Create;
fOutput := TMemoryStream.Create;
end;
destructor TROSimpleXmlWriter.Destroy;
begin
fOutput.Free;
fElementStack.Free;
inherited;
end;
procedure TROSimpleXmlWriter.SaveToStream(aDest: TStream);
begin
if not fFinished then raise EROXmlRcpSerializerException.Create('XmlDocument not finished yet');
fOutput.Position := 0;
fOutput.SaveToStream(aDest);
end;
procedure TROSimpleXmlWriter.WriteEndElement;
begin
if fFinished then raise EROXmlRcpSerializerException.Create('XmlDocument already finished');
if fElementStack.Count = 0 then raise EROXmlRcpSerializerException.Create('Root element missing');
WriteRawString('</' + fElementStack[fElementStack.Count -1] + '>');
fElementStack.Delete(fElementStack.Count -1);
if fElementStack.Count = 0 then fFinished := true;
end;
procedure TROSimpleXmlWriter.WriteRawString(const s: string);
begin
if s <> '' then
fOutput.Write(s[1], Length(s));
end;
procedure TROSimpleXmlWriter.WriteStartElement(const aElementName: string);
begin
if fFinished then raise EROXmlRcpSerializerException.Create('XmlDocument already finished');
fElementStack.Add(aElementName);
WriteRawString('<' + aElementName + '>');
end;
procedure TROSimpleXmlWriter.WriteString(const aValue: WideString);
var
lInputValue: UTF8String;
lCurrPos, i: Integer;
lRealString: UTF8String;
begin
if fFinished then raise EROXmlRcpSerializerException.Create('XmlDocument already finished');
if fElementStack.Count = 0 then raise EROXmlRcpSerializerException.Create('Root element missing');
lInputValue := UTF8Encode(aValue);
SetLength(lRealString, Length(lInputValue) + 16);
lCurrPos := 0;
for i := 1 to length(lInputValue) do
begin
case lInputValue[i] of
'<':
begin
if lCurrPos + 4 > Length(lRealString) then
SetLength(lRealString, lCurrPos + 16 + 4);
lRealString[lCurrPos + 1] := '&';
lRealString[lCurrPos + 2] := 'l';
lRealString[lCurrPos + 3] := 't';
lRealString[lCurrPos + 4] := ';';
inc(lCurrPos, 4);
end;
'>':
begin
if lCurrPos + 4 > Length(lRealString) then
SetLength(lRealString, lCurrPos + 16 + 4);
lRealString[lCurrPos + 1] := '&';
lRealString[lCurrPos + 2] := 'g';
lRealString[lCurrPos + 3] := 't';
lRealString[lCurrPos + 4] := ';';
inc(lCurrPos, 4);
end;
'&':
begin
if lCurrPos + 5 > Length(lRealString) then
SetLength(lRealString, lCurrPos + 16 + 5);
lRealString[lCurrPos + 1] := '&';
lRealString[lCurrPos + 2] := 'a';
lRealString[lCurrPos + 3] := 'm';
lRealString[lCurrPos + 4] := 'p';
lRealString[lCurrPos + 5] := ';';
inc(lCurrPos, 5);
end;
'"':
begin
if lCurrPos + 6 > Length(lRealString) then
SetLength(lRealString, lCurrPos + 16 + 6);
lRealString[lCurrPos + 1] := '&';
lRealString[lCurrPos + 2] := 'q';
lRealString[lCurrPos + 3] := 'u';
lRealString[lCurrPos + 4] := 'o';
lRealString[lCurrPos + 5] := 't';
lRealString[lCurrPos + 6] := ';';
inc(lCurrPos, 6);
end;
else
begin
if lCurrPos + 1 > Length(lRealString) then
SetLength(lRealString, lCurrPos + 16 + 1);
Inc(lCurrPos);
lRealString[lCurrPos] := lInputValue[i];
end;
end;
end;
SetLength(lRealString, lCurrPos);
WriteRawString(lRealString);
end;
initialization
RegisterMessageClass(TROXmlRpcMessage);
finalization
UnregisterMessageClass(TROXmlRpcMessage);
end.