985 lines
34 KiB
ObjectPascal
985 lines
34 KiB
ObjectPascal
unit uDAXMLAdapter;
|
|
|
|
{----------------------------------------------------------------------------}
|
|
{ Data Abstract Library - Core Library }
|
|
{ }
|
|
{ compiler: Delphi 6 and up, Kylix 3 and up }
|
|
{ platform: Win32, Linux }
|
|
{ }
|
|
{ (c)opyright RemObjects Software. all rights reserved. }
|
|
{ }
|
|
{ Using this code requires a valid license of the Data Abstract }
|
|
{ which can be obtained at http://www.remobjects.com. }
|
|
{----------------------------------------------------------------------------}
|
|
|
|
{$I DataAbstract.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, TypInfo, SysUtils,
|
|
uROClasses, uROXMLIntf,
|
|
uDAInterfaces, uDADataStreamer, uDADelta;
|
|
|
|
const
|
|
// Data stream node names
|
|
nn_DocumentName = 'XMLData';
|
|
nn_Schema = 'Schema';
|
|
nn_Datasets = 'Datasets';
|
|
nn_Fields = 'Fields';
|
|
nn_Field = 'Field';
|
|
nn_Params = 'Params';
|
|
nn_Param = 'Param';
|
|
nn_Row = 'Row';
|
|
|
|
// Delta stream node names
|
|
nn_Deltas = 'Deltas';
|
|
|
|
attr_RecId = 'RecId';
|
|
nn_SourceTableFieldName = '___SourceTableFieldName';
|
|
|
|
type
|
|
|
|
TDADataForAppendXML = class(TDADataForAppend)
|
|
public
|
|
Node: iXMLNode;
|
|
end;
|
|
|
|
TDAXMLSchemaOption = (soIncludeEmptyAttributes);
|
|
TDAXMLSchemaOptions = set of TDAXMLSchemaOption;
|
|
|
|
TDAXMLRowOption = (roCompressBlobs);
|
|
TDAXMLRowOptions = set of TDAXMLRowOption;
|
|
|
|
TDAXmlDataStreamerOption = (xaoUseDatasetXSLTs, xaoUseDeltaXSLTs);
|
|
TDAXmlDataStreamerOptions = set of TDAXmlDataStreamerOption;
|
|
|
|
{ TDAXmlDataStreamer }
|
|
TDAXmlDataStreamer = class(TDADataStreamer)
|
|
private
|
|
fWriteXSLT,
|
|
fReadXSLT{,
|
|
fWriteDeltaXSLT,
|
|
fReadDeltaXSLT} : IXMLDocument;
|
|
|
|
fXMLDocument : IXMLDocument;
|
|
fSchemaRoot,
|
|
fRootNode,
|
|
fDatasetSchemaNode,
|
|
fDeltaSchemaNode,
|
|
fDatasetNode,
|
|
fDeltaNode : IXMLNode;
|
|
|
|
fDAFieldPropInfoList : PPropList;
|
|
fDAMemPropCount,
|
|
fDAFieldPropCount : integer;
|
|
fSchemaOptions: TDAXMLSchemaOptions;
|
|
fRowOptions: TDAXMLRowOptions;
|
|
fOptions: TDAXmlDataStreamerOptions;
|
|
fDocumentName: string;
|
|
fSkipNull: boolean;
|
|
param_proplistcount: integer;
|
|
param_PropList: PPropList;
|
|
|
|
procedure WriteBlobValue(const aDestination : IXMLNode; const anAttributeName : string; const aBlobValue : Variant);
|
|
function ReadBlobValue(const aSource: IXMLNode; const anAttributeName : string) : Variant;
|
|
function GetReadXSLT: IXMLDocument;
|
|
function GetWriteXSLT: IXMLDocument;
|
|
{function GetReadDeltaXSLT: IXMLDocument;
|
|
function GetWriteDeltaXSLT: IXMLDocument;}
|
|
|
|
function GetXMLDocument(var XMLDocument: IXMLDocument): IXMLDocument;
|
|
function SaveDocumentName: Boolean;
|
|
procedure SetDocumentName(const Value: string);
|
|
procedure ClearXMLNodes;
|
|
|
|
protected
|
|
function DoCreateStream: TStream; override;
|
|
procedure DoInitialize(Mode: TDAAdapterInitialization); override;
|
|
procedure DoFinalize; override;
|
|
function DoWriteDataset(const Source: IDADataset; Options: TDAWriteOptions; MaxRows: integer;ADynFieldNames: array of string): integer; override;
|
|
procedure DoWriteDelta(const Source: IDADelta); override;
|
|
procedure DoReadDataset(const DatasetName: string; const Destination: IDADataset; ApplySchema: boolean); override;
|
|
procedure DoReadDelta(const DeltaName: string; const Destination: IDADelta); override;
|
|
function DoBeginWriteDataset( const Source: IDADataset; const Schema: TDADataset;
|
|
Options: TDAWriteOptions; MaxRows: integer;
|
|
ADynFieldNames: array of string): TDADataForAppend; override;
|
|
function DoWriteDatasetData(const Source: IDADataset; var aDataForAppend: TDADataForAppend; aDataIndex: Integer = -1): Integer; override;
|
|
function DoEndWriteDataset(aDataForAppend: TDADataForAppend): Integer; override;
|
|
public
|
|
constructor Create(aOwner : TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
function GetTargetDataType: TRODataType; override;
|
|
|
|
property ReadXSLT : IXMLDocument read GetReadXSLT write fReadXSLT;
|
|
property WriteXSLT : IXMLDocument read GetWriteXSLT write fWriteXSLT;
|
|
{property ReadDeltaXSLT : IXMLDocument read GetReadDeltaXSLT write fReadDeltaXSLT;
|
|
property WriteDeltaXSLT : IXMLDocument read GetWriteDeltaXSLT write fWriteDeltaXSLT;}
|
|
|
|
published
|
|
property SkipNull : boolean read fSkipNull write fSkipNull default true;
|
|
property SchemaOptions : TDAXMLSchemaOptions read fSchemaOptions write fSchemaOptions;
|
|
property RowOptions : TDAXMLRowOptions read fRowOptions write fRowOptions;
|
|
property Options : TDAXmlDataStreamerOptions read fOptions write fOptions;
|
|
property DocumentName : string read fDocumentName write SetDocumentName stored SaveDocumentName;
|
|
end;
|
|
|
|
TDAXMLAdapter = class(TDAXmlDataStreamer) end deprecated;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Variants, uROXMLSerializer, uROCompression, uROBinaryHelpers
|
|
,uDAClasses, uDAEngine
|
|
{$IFDEF MSWINDOWS}
|
|
,uROZLib
|
|
{$ENDIF};
|
|
|
|
const
|
|
XML_DateTimeFormat = 'yyyy-mm-dd"T"hh":"nn":"ss"."zzz';
|
|
|
|
function XMLDateTimeToDateTime(const aXMLDateTime : string) : TDateTime;
|
|
var year, month, day, hour, min, sec, msec : word;
|
|
begin {yyyy-mm-ddThh:nn:ss.zzz}
|
|
year := StrToInt(Copy(aXMLDateTime,1,4));
|
|
month := StrToInt(Copy(aXMLDateTime,6,2));
|
|
day := StrToInt(Copy(aXMLDateTime,9,2));
|
|
hour := StrToInt(Copy(aXMLDateTime,12,2));
|
|
min := StrToInt(Copy(aXMLDateTime,15,2));
|
|
sec := StrToInt(Copy(aXMLDateTime,18,2));
|
|
msec := StrToInt(Copy(aXMLDateTime,21,3));
|
|
|
|
result := EncodeDate(year, month, day);
|
|
// The code below is required! Do not adjust
|
|
if (result<0)
|
|
then result := result-EncodeTime(hour, min, sec, msec)
|
|
else result := result+EncodeTime(hour, min, sec, msec)
|
|
end;
|
|
|
|
function DateTimeToXMLDateTime(aDateTime : TDateTime) : string;
|
|
begin
|
|
Result := FormatDateTime(XML_DateTimeFormat, aDateTime);
|
|
end;
|
|
|
|
function WriteFloat(Val: Variant): String;
|
|
begin
|
|
Result := FloatToStr(Val{$IFDEF DELPHI7UP},SOAPFormatSettings{$ENDIF});
|
|
|
|
{$IFNDEF DELPHI7UP}
|
|
if DecimalSeparator <> '.' then
|
|
ReplaceChar(Result, [DecimalSeparator], '.');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
function ReadFloat(text: String): Variant;
|
|
begin
|
|
{$IFNDEF DELPHI7UP}
|
|
if DecimalSeparator <> '.' then
|
|
ReplaceChar(text, ['.'], DecimalSeparator);
|
|
{$ENDIF}
|
|
|
|
Result := SOAPStrToFloat(text);
|
|
end;
|
|
|
|
{ TDAXmlDataStreamer }
|
|
|
|
constructor TDAXmlDataStreamer.Create(aOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
|
|
fDocumentName := nn_DocumentName;
|
|
|
|
fSchemaOptions := [soIncludeEmptyAttributes];
|
|
|
|
fXMLDocument := NewROXmlDocument;
|
|
|
|
fWriteXSLT := NIL;
|
|
fReadXSLT := NIL;
|
|
{fWriteDeltaXSLT := NIL;
|
|
fReadDeltaXSLT := NIL;}
|
|
|
|
fOptions := [xaoUseDatasetXSLTs, xaoUseDeltaXSLTs];
|
|
|
|
fDAMemPropCount := GetTypeData(TDAField.ClassInfo).PropCount;
|
|
GetMem(fDAFieldPropInfoList, fDAMemPropCount*SizeOf(PPropInfo));
|
|
fDAFieldPropCount := GetPropList(TDAField.ClassInfo, tkProperties, fDAFieldPropInfoList);
|
|
|
|
GetMem(param_PropList, GetTypeData(TDAParam.ClassInfo).PropCount * SizeOf(PPropInfo));
|
|
param_proplistcount := GetPropList(TDAParam.ClassInfo, tkProperties, param_PropList);
|
|
fSkipNull := true;
|
|
end;
|
|
|
|
destructor TDAXmlDataStreamer.Destroy;
|
|
begin
|
|
fWriteXSLT := NIL;
|
|
fReadXSLT := NIL;
|
|
{fWriteDeltaXSLT := NIL;
|
|
fReadDeltaXSLT := NIL;}
|
|
|
|
FreeMem(fDAFieldPropInfoList, fDAMemPropCount*SizeOf(PPropInfo));
|
|
FreeMem(param_PropList);
|
|
|
|
ClearXMLNodes;
|
|
|
|
inherited;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.GetTargetDataType: TRODataType;
|
|
begin
|
|
result := rtString;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.DoCreateStream: TStream;
|
|
begin
|
|
result := NIL;
|
|
end;
|
|
|
|
procedure TDAXmlDataStreamer.DoInitialize(Mode: TDAAdapterInitialization);
|
|
var
|
|
i : integer;
|
|
begin
|
|
ClearXMLNodes;
|
|
fXMLDocument.New(fDocumentName);
|
|
|
|
if (Mode in AdapterReadModes) then begin
|
|
|
|
fXMLDocument.LoadFromStream(Data);
|
|
|
|
// Applies the XSLT (if any). The resulting document MUST be a valid XML document
|
|
// that conforms to the Data Abstract XML format
|
|
if (fReadXSLT<>NIL)
|
|
then fXMLDocument.XML := fXMLDocument.Transform(fReadXSLT.XML);
|
|
|
|
// Parses the document
|
|
fRootNode := fXMLDocument.DocumentNode;
|
|
|
|
// Schema sections
|
|
fSchemaRoot := fRootNode.GetNodeByName(nn_Schema);
|
|
if (fSchemaRoot<>NIL) then begin
|
|
fDatasetSchemaNode := fSchemaRoot.GetNodeByName(nn_Datasets);
|
|
fDeltaSchemaNode := fSchemaRoot.GetNodeByName(nn_Deltas);
|
|
end;
|
|
|
|
// Data sections
|
|
fDatasetNode := fRootNode.GetNodeByName(nn_Datasets);
|
|
fDeltaNode := fRootNode.GetNodeByName(nn_Deltas);
|
|
|
|
if (fDatasetSchemaNode<>NIL) and (fDatasetSchemaNode.ChildrenCount>0) then begin
|
|
for i := 0 to (fDatasetSchemaNode.ChildrenCount-1) do
|
|
AddingDataset(fDatasetSchemaNode.Children[i].Name);
|
|
end
|
|
else if (fDatasetNode<>NIL) then begin
|
|
for i := 0 to (fDatasetNode.ChildrenCount-1) do
|
|
AddingDataset(fDatasetNode.Children[i].Name);
|
|
end;
|
|
|
|
if (fDeltaSchemaNode<>NIL) and (fDeltaSchemaNode.ChildrenCount>0) then begin
|
|
for i := 0 to (fDeltaSchemaNode.ChildrenCount-1) do
|
|
AddingDelta(fDeltaSchemaNode.Children[i].Name);
|
|
end
|
|
else if (fDeltaNode<>NIL) then begin
|
|
for i := 0 to (fDeltaNode.ChildrenCount-1) do
|
|
AddingDataset(fDeltaNode.Children[i].Name);
|
|
end;
|
|
|
|
end
|
|
else if (Mode in AdapterWriteModes) then begin
|
|
fRootNode := fXMLDocument.DocumentNode;
|
|
// Schema sections
|
|
fSchemaRoot := fRootNode.Add(nn_Schema);
|
|
fDatasetSchemaNode := fSchemaRoot.Add(nn_Datasets);
|
|
fDeltaSchemaNode := fSchemaRoot.Add(nn_Deltas);
|
|
|
|
// Data sections
|
|
fDatasetNode := fRootNode.Add(nn_Datasets);
|
|
fDeltaNode := fRootNode.Add(nn_Deltas);
|
|
end;
|
|
end;
|
|
|
|
procedure TDAXmlDataStreamer.DoFinalize;
|
|
var finaldocument : string;
|
|
begin
|
|
if (AdapterInitialization in AdapterWriteModes) then begin
|
|
// Removes the delta sections if they don't contain data
|
|
if (fDeltaSchemaNode.ChildrenCount=0) then begin
|
|
fSchemaRoot.Remove(fDeltaSchemaNode);
|
|
fDeltaSchemaNode := NIL;
|
|
end;
|
|
|
|
if (fDeltaNode.ChildrenCount=0) then begin
|
|
fRootNode.Remove(fDeltaNode);
|
|
fDeltaNode := NIL;
|
|
end;
|
|
|
|
// Removes the datasets sections if they don't contain data
|
|
if (fDatasetSchemaNode.ChildrenCount=0) then begin
|
|
fSchemaRoot.Remove(fDatasetSchemaNode);
|
|
fDatasetSchemaNode := NIL;
|
|
end;
|
|
|
|
if (fDatasetNode.ChildrenCount=0) then begin
|
|
fRootNode.Remove(fDatasetNode);
|
|
fDeltaNode := NIL;
|
|
end;
|
|
|
|
// Applies the XSLT (if any)
|
|
if (fWriteXSLT<>NIL)
|
|
then begin
|
|
// The resulting document might not be a valid XML document!!
|
|
finaldocument := fXMLDocument.Transform(fWriteXSLT.XML);
|
|
if (finaldocument<>'')
|
|
then Data.Write(finaldocument[1], Length(finaldocument)*SizeOf(char));
|
|
end
|
|
else fXMLDocument.SaveToStream(Data);
|
|
end;
|
|
ClearXMLNodes;
|
|
end;
|
|
|
|
procedure TDAXmlDataStreamer.WriteBlobValue(const aDestination : IXMLNode; const anAttributeName : string; const aBlobValue : Variant);
|
|
var ss, compressedstream : TStringStream;
|
|
binstream : TROBinaryMemoryStream;
|
|
begin
|
|
compressedstream := NIL;
|
|
binstream := TROBinaryMemoryStream.Create;
|
|
ss := TStringStream.Create('');
|
|
|
|
try
|
|
//binstream := BinaryFromVariant(aBlobValue);//, ms.Stream);
|
|
WriteVariantToBinary(aBlobValue, binstream);
|
|
binstream.Position := 0;
|
|
|
|
if (roCompressBlobs in fRowOptions) then begin
|
|
compressedstream := TStringStream.Create('');
|
|
ZCompressStream(binstream, compressedstream);
|
|
compressedstream.Position := 0;
|
|
|
|
EncodeStream(compressedstream, ss);
|
|
end
|
|
else begin
|
|
EncodeStream(binstream, ss);
|
|
end;
|
|
|
|
aDestination.AddAttribute(anAttributeName, ss.DataString);
|
|
|
|
finally
|
|
FreeAndNIL(ss);
|
|
FreeAndNIL(compressedstream);
|
|
FreeAndNIL(binstream);
|
|
end;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.ReadBlobValue(const aSource: IXMLNode; const anAttributeName : string) : Variant;
|
|
var data, compressedstream : TStringStream;
|
|
finaldata : IROStream;
|
|
begin
|
|
compressedstream := NIL;
|
|
|
|
data := TStringStream.Create(aSource.GetAttributeValue(anAttributeName, NULL));
|
|
finaldata := NewROStream;
|
|
|
|
try
|
|
data.Position := 0;
|
|
|
|
if (roCompressBlobs in fRowOptions) then begin
|
|
compressedstream := TStringStream.Create('');
|
|
DecodeStream(data, compressedstream);
|
|
compressedstream.Position := 0;
|
|
|
|
ZDecompressStream(compressedstream, finaldata.Stream);
|
|
finaldata.Position := 0;
|
|
end
|
|
else begin
|
|
DecodeStream(data, finaldata.Stream);
|
|
end;
|
|
|
|
finaldata.Position := 0;
|
|
result := ReadVariantFromBinary(finaldata.Stream);
|
|
|
|
finally
|
|
FreeAndNIL(data);
|
|
FreeAndNIL(compressedstream);
|
|
end;
|
|
end;
|
|
|
|
procedure TDAXmlDataStreamer.DoReadDataset(const DatasetName: string;
|
|
const Destination: IDADataset; ApplySchema: boolean);
|
|
var sourcenode : IXMLNode;
|
|
i, x : integer;
|
|
attr, node, scNode : IXMLNode;
|
|
fld : TDAField;
|
|
par: TDAParam;
|
|
val: Variant;
|
|
editable : IDAEditableDataset;
|
|
lName: string;
|
|
lProgress: Boolean;
|
|
lTotal: integer;
|
|
begin
|
|
// Reads and applies the schema
|
|
if ApplySchema then begin
|
|
scNode := fDatasetSchemaNode.GetNodeByName(DatasetName);
|
|
if (scNode=NIL) then exit;//raise EROException.Create('Cannot find schema for dataset '+DatasetName)
|
|
sourcenode := scNode.GetNodeByName(nn_Fields); // For now we only have fields, so this is ok
|
|
if sourcenode <> nil then begin
|
|
Destination.Fields.Clear;
|
|
|
|
for i := 0 to (sourcenode.ChildrenCount-1) do begin
|
|
fld := Destination.Fields.Add;
|
|
node := sourcenode.Children[i];
|
|
|
|
for x := 0 to (fDAFieldPropCount-1) do begin
|
|
lName:= {$IFDEF UNICODE}UTF8ToString{$ENDIF}(fDAFieldPropInfoList^[x].Name);
|
|
attr := node.GetAttributeByName(lName);
|
|
if (attr=NIL) then Continue;
|
|
|
|
if (fDAFieldPropInfoList^[x].PropType^.Kind<>tkClass) then SetPropValue(fld, lName, VarToStr(attr.Value));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
sourcenode := scNode.GetNodeByName(nn_Params); // For now we only have fields, so this is ok
|
|
if sourcenode <> nil then begin
|
|
Destination.Params.Clear;
|
|
|
|
for i := 0 to (sourcenode.ChildrenCount-1) do begin
|
|
par := Destination.Params.Add;
|
|
node := sourcenode.Children[i];
|
|
|
|
for x := 0 to (param_proplistcount-1) do begin
|
|
lName := {$IFDEF UNICODE}UTF8ToString{$ENDIF}(param_PropList^[x].Name);
|
|
attr := node.GetAttributeByName(lName);
|
|
if (attr=NIL) then Continue;
|
|
|
|
if (param_PropList^[x].PropType^.Kind<>tkClass) then SetPropValue(par, lName, VarToStr(attr.Value));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end
|
|
else begin
|
|
editable := Destination as IDAEditableDataset;
|
|
sourcenode := fDatasetNode.GetNodeByName(DatasetName);
|
|
if (sourcenode=NIL)
|
|
then raise EROException.Create('Cannot find schema for dataset '+DatasetName);
|
|
|
|
if not Destination.Active then Destination.Open;
|
|
|
|
{$IFDEF STORERECID}
|
|
Destination.CurrentRecIdValue := 0;
|
|
{$ENDIF}
|
|
|
|
lProgress := Assigned(OnReadDatasetProgress);
|
|
lTotal := sourcenode.ChildrenCount;
|
|
|
|
Destination.DisableConstraints;
|
|
try
|
|
for i := 0 to sourcenode.ChildrenCount-1 do begin
|
|
editable.Append;
|
|
|
|
node := sourcenode.Children[i];
|
|
|
|
{$IFDEF STORERECID}
|
|
Destination.CurrentRecIdValue := node.GetAttributeValue(attr_RecId, -1);
|
|
for x := 1 to node.AttributeCount-1 do begin
|
|
{$ELSE}
|
|
for x := 0 to node.AttributeCount-1 do begin
|
|
{$ENDIF}
|
|
attr := node.Attributes[x];
|
|
if attr.Name = nn_SourceTableFieldName then
|
|
fld := Destination.Fields.FieldByName(def_SourceTableFieldName)
|
|
else
|
|
fld := Destination.Fields.FieldByName(attr.Name);
|
|
|
|
if fld.Calculated or fld.Lookup then Continue;
|
|
if (attr.Value = '') and (fld.DataType <> datBlob) then begin
|
|
val := NULL;
|
|
end else begin
|
|
case fld.DataType of
|
|
datBlob : val := ReadBlobValue(node, attr.Name); // <--- MARC!!!
|
|
datDateTime : val := XMLDateTimeToDateTime(attr.Value);
|
|
datFloat,
|
|
datCurrency: val := ReadFloat(attr.Value);
|
|
// TODO -cAleF: remember to use the proper XML conversion routines here!
|
|
else val := attr.Value;
|
|
end;
|
|
end;
|
|
|
|
if Assigned(OnReadFieldValue) then OnReadFieldValue(fld, val);
|
|
fld.Value := val;
|
|
|
|
end;
|
|
|
|
editable.Post;
|
|
if lProgress then OnReadDatasetProgress(Self, Destination, i, lTotal);
|
|
end;
|
|
finally
|
|
Destination.EnableConstraints;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.DoWriteDataset(const Source: IDADataset;
|
|
Options: TDAWriteOptions; MaxRows: integer; ADynFieldNames: array of string): integer;
|
|
var
|
|
lDataForAppend: TDADataForAppend;
|
|
begin
|
|
lDataForAppend := DoBeginWriteDataset(Source, {schema}nil, Options, MaxRows, ADynFieldNames);
|
|
if woRows in Options then begin
|
|
DoWriteDatasetData(Source, lDataForAppend);
|
|
result := DoEndWriteDataset(lDataForAppend);
|
|
end
|
|
else begin
|
|
result := -1;
|
|
end;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.DoBeginWriteDataset( const Source: IDADataset; const Schema: TDADataset;
|
|
Options: TDAWriteOptions; MaxRows: integer;
|
|
ADynFieldNames: array of string): TDADataForAppend;
|
|
var
|
|
i: integer;
|
|
fld: TDAField;
|
|
lfields: array of integer;
|
|
lDataForAppend : TDADataForAppendXML;
|
|
lSchemaFields: TDAFieldCollection;
|
|
lSchemaParams: TDAParamCollection;
|
|
lLogicalName: String;
|
|
x : integer;
|
|
subnode, node, scNode : IXMLNode;
|
|
s : string;
|
|
lName: string;
|
|
|
|
begin
|
|
lDataForAppend := TDADataForAppendXML.Create();
|
|
result := lDataForAppend;
|
|
|
|
if Assigned(Schema) then begin
|
|
lDataForAppend.TableSchema := Schema;
|
|
if Schema is TDAUnionDataTable then begin
|
|
fld := Schema.FindField(def_SourceTableFieldName);
|
|
if not Assigned(fld) then begin
|
|
fld := Schema.Fields.Add();
|
|
fld.Name := def_SourceTableFieldName;
|
|
fld.DataType := datInteger;
|
|
fld.InPrimaryKey := true;
|
|
fld.ServerAutoRefresh := true;
|
|
end;
|
|
end;
|
|
lSchemaFields := Schema.Fields;
|
|
lSchemaParams := Schema.Params;
|
|
lLogicalName := Schema.Name;
|
|
end else begin
|
|
if Assigned(Source) then begin
|
|
lSchemaFields := Source.Fields;
|
|
lSchemaParams := Source.Params;
|
|
lLogicalName := Source.LogicalName;
|
|
end else begin
|
|
raise EDAException.Create('Schema or source should be assigned.');
|
|
end;
|
|
end;
|
|
|
|
|
|
if Length(ADynFieldNames) > 0 then begin
|
|
SetLength(lfields, Length(ADynFieldNames));
|
|
For i:=0 to High(ADynFieldNames) do begin
|
|
fld:=lSchemaFields.FindField(ADynFieldNames[i]);
|
|
if fld <> nil then
|
|
lfields[i]:= fld.Index
|
|
else
|
|
lfields[i]:= -1;
|
|
end;
|
|
end else begin
|
|
SetLength(lfields, lSchemaFields.Count);
|
|
For i:=0 to lSchemaFields.Count-1 do
|
|
lfields[i]:=i;
|
|
end;
|
|
|
|
// Writes the schema
|
|
if (woSchema in Options) or (Length(ADynFieldNames)>0) then begin
|
|
scNode := fDatasetSchemaNode.Add(lLogicalName);
|
|
node := scNode.Add(nn_Fields);
|
|
|
|
for i := 0 to high(lfields) do begin
|
|
subnode := node.Add(nn_Field);
|
|
|
|
for x := 0 to (fDAFieldPropCount-1) do begin
|
|
lName := {$IFDEF UNICODE}UTF8ToString{$ENDIF}(fDAFieldPropInfoList^[x].Name);
|
|
s := VarToStr(GetPropValue(lSchemaFields[lfields[i]], lName, TRUE));
|
|
|
|
if (s<>'') or (soIncludeEmptyAttributes in SchemaOptions) then subnode.AddAttribute(lName, s);
|
|
end;
|
|
end;
|
|
node := scNode.Add(nn_Params);
|
|
|
|
for i := 0 to (lSchemaParams.Count-1) do begin
|
|
subnode := node.Add(nn_Param);
|
|
|
|
for x := 0 to (param_proplistcount-1) do begin
|
|
lName := {$IFDEF UNICODE}UTF8ToString{$ENDIF}(param_PropList^[x].Name);
|
|
s := VarToStr(GetPropValue(lSchemaParams.Params[i], lName, TRUE));
|
|
|
|
if (s<>'') or (soIncludeEmptyAttributes in SchemaOptions) then subnode.AddAttribute(lName, s);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
SetLength(lDataForAppend.RealFields, Length(lFields));
|
|
for i:= 0 to Length(lFields) -1 do
|
|
lDataForAppend.RealFields[i] := lFields[i];
|
|
|
|
lDataForAppend.node := fDatasetNode.Add(lLogicalName);
|
|
lDataForAppend.MaxRowCount := MaxRows;
|
|
lDataForAppend.RecordCount := 0;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.DoWriteDatasetData(const Source: IDADataset; var aDataForAppend: TDADataForAppend; aDataIndex: Integer = -1): Integer;
|
|
var
|
|
i, max, k : integer;
|
|
subnode, node : IXMLNode;
|
|
val: Variant;
|
|
lfields: array of integer;
|
|
flds: array of TDAField;
|
|
flds_names: array of string;
|
|
lProgress: Boolean;
|
|
f,ev, ev1: boolean;
|
|
NativeDataset: IDASQLCommandNativeObject;
|
|
lColumnMappings: TDAColumnMappingCollection;
|
|
lColumnMapping: TDAColumnMapping;
|
|
lFieldName: string;
|
|
lDataForAppend: TDADataForAppendXML;
|
|
lMapToFieldName: String;
|
|
begin
|
|
f:=(Source.QueryInterface(IDASQLCommandNativeObject, NativeDataset) = 0) and not NativeDataset.IsTDatasetCompatible;
|
|
lDataForAppend := TDADataForAppendXML(aDataForAppend);
|
|
|
|
SetLength(lfields, Length(lDataForAppend.RealFields));
|
|
SetLength(flds, Length(lDataForAppend.RealFields));
|
|
SetLength(flds_names, Length(lDataForAppend.RealFields));
|
|
for i:= 0 to Length(lDataForAppend.RealFields) -1 do
|
|
lFields[i] := lDataForAppend.RealFields[i];
|
|
|
|
|
|
k := lDataForAppend.RecordCount;
|
|
max := lDataForAppend.MaxRowCount;
|
|
|
|
// Mapping fields of Source table to the streamed dataset
|
|
if Assigned(lDataForAppend.TableSchema) and (lDataForAppend.TableSchema is TDAUnionDataTable) then begin
|
|
lColumnMappings := TDAUnionSourceTable(TDAUnionDataTable(lDataForAppend.TableSchema).SourceTables.ItemByName(Source.Name)).ColumnMappings;
|
|
for i := 0 to lDataForAppend.TableSchema.Fields.Count - 1 do begin
|
|
lFieldName := lDataForAppend.TableSchema.Fields[lFields[i]].Name;
|
|
if lFieldName = def_SourceTableFieldName then begin
|
|
lFields[i] := -10;
|
|
flds_names[i] := nn_SourceTableFieldName;
|
|
continue;
|
|
end;
|
|
flds_names[i] := lFieldName;
|
|
lMapToFieldName := lFieldName;
|
|
if Assigned(lColumnMappings) then begin
|
|
lColumnMapping := lColumnMappings.MappingByDatasetField(lFieldName);
|
|
if Assigned(lColumnMapping) and (lColumnMapping.TableField <> '') then
|
|
lMapToFieldName := lColumnMapping.TableField;
|
|
end;
|
|
lFields[i] := Source.FieldByName(lMapToFieldName).Index;
|
|
end;
|
|
end
|
|
else begin
|
|
for i := 0 to Length(lfields) -1 do
|
|
flds_names[i] := Source.Fields[lfields[i]].Name;
|
|
end;
|
|
|
|
Source.DisableControls;
|
|
try
|
|
if not Source.Active then Source.Open;
|
|
for i:= 0 to Length(lfields) -1 do begin
|
|
if lfields[i] = -10 then
|
|
flds[i] := lDataForAppend.TableSchema.FieldByName(def_SourceTableFieldName)
|
|
else
|
|
flds[i] := Source.Fields[lfields[i]];
|
|
end;
|
|
|
|
|
|
ev1 := Assigned(OnBeforeFieldValueSerialization);
|
|
ev := Assigned(OnWriteFieldValue);
|
|
f := f or ev or ev1;
|
|
lProgress := Assigned(onWriteDatasetProgress);
|
|
node := lDataForAppend.Node;
|
|
// Writes the actual records
|
|
while (k<>max) and not source.EOF do begin
|
|
|
|
subnode := node.Add(nn_Row);
|
|
|
|
{$IFDEF STORERECID}
|
|
subnode.AddAttribute(attr_RecId, Source.GetRowRecIdValue);
|
|
{$ENDIF}
|
|
|
|
for i := 0 to Length(lfields) - 1 do begin
|
|
//RealFields[i] = -10 then this is @SourceTable field
|
|
if lfields[i] = -10 then
|
|
val := aDataIndex
|
|
else
|
|
val := Source.FieldValues[lfields[i]];
|
|
if ev1 then OnBeforeFieldValueSerialization(flds[i], val);
|
|
if flds[i].Calculated or flds[i].Lookup then Continue;
|
|
if fSkipNull and
|
|
(lfields[i] <> -10) and
|
|
((f and VarIsNull(Val)) or (flds[i].IsNull)) then continue;
|
|
if ev then OnWriteFieldValue(flds[i], val);
|
|
if (VarIsEmpty(val) or VarIsNull(val)) and (flds[i].DataType <> datBlob) then begin
|
|
subnode.AddAttribute(flds_names[i], VarToStr(Null))
|
|
end else begin
|
|
case flds[i].DataType of
|
|
datBlob : WriteBlobValue(subnode, flds_names[i], val);// <--- MARC!!!
|
|
datDateTime : subnode.AddAttribute(flds_names[i], DateTimeToXMLDateTime(val));
|
|
datFloat,
|
|
datCurrency: subnode.AddAttribute(flds_names[i], WriteFloat(val));
|
|
else
|
|
// TODO -cAleF: remember to use the proper XML conversion reoutines here!
|
|
subnode.AddAttribute(flds_names[i], VarToStr(val));
|
|
end;
|
|
end;
|
|
end;
|
|
Inc(k);
|
|
if lProgress then OnWriteDatasetProgress(Self, Source, k, max);
|
|
Source.Next;
|
|
if Source.EOF then Break;
|
|
end;
|
|
aDataForAppend.RecordCount := k;
|
|
Result := k;
|
|
finally
|
|
Source.EnableControls;
|
|
end;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.DoEndWriteDataset(aDataForAppend: TDADataForAppend): Integer;
|
|
begin
|
|
result := aDataForAppend.RecordCount;
|
|
TDADataForAppendXML(aDataForAppend).Node := nil;
|
|
aDataForAppend.Free();
|
|
end;
|
|
|
|
procedure TDAXmlDataStreamer.DoReadDelta(const DeltaName: string;
|
|
const Destination: IDADelta);
|
|
var schema, rootnode, node, subnode : IXMLNode;
|
|
x, i : integer;
|
|
fieldname : string;
|
|
datatype : TDADataType;
|
|
changetype : TDAChangeType;
|
|
changestatus : TDAChangeStatus;
|
|
changemessage : string;
|
|
recid : integer;
|
|
change : TDADeltaChange;
|
|
val : Variant;
|
|
lProgress: Boolean;
|
|
lTotal : integer;
|
|
begin
|
|
Destination.Clear(TRUE, TRUE);
|
|
schema := fDeltaSchemaNode.GetNodeByName(DeltaName);
|
|
|
|
// Logged fields and their types
|
|
rootnode := schema.GetNodeByName('LoggedFields');
|
|
for i := 0 to (rootnode.ChildrenCount-1) do begin
|
|
fieldname := rootnode.Children[i].GetAttributeByName('Name').Value;
|
|
datatype := TDADataType(GetEnumValue(TypeInfo(TDADataType), rootnode.Children[i].GetAttributeByName('DataType').Value));
|
|
|
|
Destination.AddFieldName(fieldname);
|
|
Destination.LoggedFieldTypes[i] := datatype;
|
|
end;
|
|
|
|
// Key fields
|
|
rootnode := schema.GetNodeByName('KeyFields');
|
|
for i := 0 to (rootnode.ChildrenCount-1) do begin
|
|
fieldname := rootnode.Children[i].GetAttributeByName('Name').Value;
|
|
Destination.AddKeyFieldName(fieldname);
|
|
end;
|
|
|
|
// Actual changes
|
|
rootnode := fDeltaNode.GetNodeByName(DeltaName);
|
|
lProgress := Assigned(OnReadDeltaProgress);
|
|
lTotal := rootnode.ChildrenCount;
|
|
for i := 0 to (rootnode.ChildrenCount-1) do begin
|
|
node := rootnode.Children[i];
|
|
|
|
recid := node.GetNodeByName('RecID').Value;
|
|
changetype := TDAChangeType(GetEnumValue(TypeInfo(TDAChangeType), node.GetNodeByName('ChangeType').Value));
|
|
changestatus := TDAChangeStatus(GetEnumValue(TypeInfo(TDAChangeStatus), node.GetNodeByName('Status').Value));
|
|
changemessage := node.GetNodeByName('Message').Value;
|
|
|
|
change := Destination.Add(recid, changetype, changestatus, changemessage);
|
|
|
|
subnode := node.GetNodeByName('OldValues');
|
|
for x := 0 to Destination.LoggedFieldCount-1 do begin
|
|
val := subnode.GetAttributeValue(Destination.LoggedFieldNames[x], Null);
|
|
if (val = '') and (Destination.LoggedFieldTypes[x] <> datblob) then begin
|
|
change.OldValues[x] := NULL;
|
|
end else begin
|
|
case Destination.LoggedFieldTypes[x] of
|
|
datBlob : change.OldValues[x] := ReadBlobValue(subnode, Destination.LoggedFieldNames[x]);
|
|
datDateTime : change.OldValues[x] := XMLDateTimeToDateTime(val);
|
|
datFloat,
|
|
datCurrency: change.OldValues[x] := ReadFloat(val);
|
|
else
|
|
change.OldValues[x] := val;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
subnode := node.GetNodeByName('NewValues');
|
|
for x := 0 to Destination.LoggedFieldCount-1 do begin
|
|
val := subnode.GetAttributeValue(Destination.LoggedFieldNames[x], Null);
|
|
if (val = '') and (Destination.LoggedFieldTypes[x] <> datblob) then begin
|
|
change.NewValues[x] := NULL;
|
|
end else begin
|
|
case Destination.LoggedFieldTypes[x] of
|
|
datBlob : change.NewValues[x] := ReadBlobValue(subnode, Destination.LoggedFieldNames[x]);
|
|
datDateTime : change.NewValues[x] := XMLDateTimeToDateTime(val);
|
|
datFloat,
|
|
datCurrency: change.NewValues[x] := ReadFloat(val);
|
|
else
|
|
change.NewValues[x] := val;
|
|
end;
|
|
end;
|
|
end;
|
|
if lProgress then OnReadDeltaProgress(Self, Destination, i, lTotal);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TDAXmlDataStreamer.DoWriteDelta(const Source: IDADelta);
|
|
var
|
|
rootnode, node, subnode : IXMLNode;
|
|
x, i : integer;
|
|
lProgress: Boolean;
|
|
lTotal : integer;
|
|
begin
|
|
{
|
|
<XMLData>
|
|
<Schema>
|
|
<Datasets/>
|
|
<Deltas/>
|
|
</Schema>
|
|
<Datasets/>
|
|
<Deltas/>
|
|
</XMLData>
|
|
}
|
|
|
|
// Writes the schema of the delta
|
|
rootnode := fDeltaSchemaNode.Add(Source.LogicalName);
|
|
node := rootnode.Add('LoggedFields');
|
|
for i := 0 to (Source.LoggedFieldCount-1) do begin
|
|
subnode := node.Add('Field');
|
|
subnode.AddAttribute('Name', Source.LoggedFieldNames[i]);
|
|
subnode.AddAttribute('DataType', GetEnumName(TypeInfo(TDADataType), Ord(Source.LoggedFieldTypes[i])));
|
|
end;
|
|
node := rootnode.Add('KeyFields');
|
|
for i := 0 to (Source.KeyFieldCount-1) do begin
|
|
subnode := node.Add('Field');
|
|
subnode.AddAttribute('Name', Source.KeyFieldNames[i]);
|
|
end;
|
|
|
|
lProgress := Assigned(OnWriteDeltaProgress);
|
|
lTotal := Source.Count;
|
|
// Writes the actual changes
|
|
rootnode := fDeltaNode.Add(Source.LogicalName);
|
|
Source.RemoveUnchangedChanges;
|
|
for i := 0 to (Source.Count-1) do begin
|
|
node := rootnode.Add(nn_Row);
|
|
|
|
node.Add('RecID').Value := Source[i].RecID;
|
|
node.Add('ChangeType').Value := GetEnumName(TypeInfo(TDAChangeType), Ord(Source[i].ChangeType));
|
|
node.Add('Status').Value := GetEnumName(TypeInfo(TDAChangeStatus), Ord(Source[i].Status));
|
|
node.Add('Message').Value := Source[i].Message;
|
|
|
|
subnode := node.Add('OldValues');
|
|
for x := 0 to Source.LoggedFieldCount-1 do begin
|
|
if VarIsNull(Source.Changes[i].OldValues[x]) and (Source.LoggedFieldTypes[x] <> datblob) then begin
|
|
subnode.AddAttribute(Source.LoggedFieldNames[x], VarToStr(Null))
|
|
end else begin
|
|
case Source.LoggedFieldTypes[x] of
|
|
datblob: WriteBlobValue(subnode, Source.LoggedFieldNames[x], Source.Changes[i].OldValues[x]);
|
|
datDateTime : subnode.AddAttribute(Source.LoggedFieldNames[x], DateTimeToXMLDateTime(Source.Changes[i].OldValues[x]));
|
|
datFloat,
|
|
datCurrency: subnode.AddAttribute(Source.LoggedFieldNames[x], WriteFloat(Source.Changes[i].OldValues[x]));
|
|
// TODO -cAleF: remember to use the proper XML conversion reoutines here!
|
|
else subnode.AddAttribute(Source.LoggedFieldNames[x], VarToStr(Source.Changes[i].OldValues[x]));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
subnode := node.Add('NewValues');
|
|
for x := 0 to Source.LoggedFieldCount-1 do begin
|
|
if VarIsNull(Source.Changes[i].NewValues[x]) and (Source.LoggedFieldTypes[x] <> datblob) then begin
|
|
subnode.AddAttribute(Source.LoggedFieldNames[x], VarToStr(Null))
|
|
end else begin
|
|
case Source.LoggedFieldTypes[x] of
|
|
datblob: WriteBlobValue(subnode, Source.LoggedFieldNames[x], Source.Changes[i].NewValues[x]);
|
|
datDateTime : subnode.AddAttribute(Source.LoggedFieldNames[x], DateTimeToXMLDateTime(Source.Changes[i].NewValues[x]));
|
|
datFloat,
|
|
datCurrency: subnode.AddAttribute(Source.LoggedFieldNames[x], WriteFloat(Source.Changes[i].NewValues[x]));
|
|
// TODO -cAleF: remember to use the proper XML conversion reoutines here!
|
|
else subnode.AddAttribute(Source.LoggedFieldNames[x], VarToStr(Source.Changes[i].NewValues[x]));
|
|
end;
|
|
end;
|
|
end;
|
|
if lProgress then OnWriteDeltaProgress(Self, Source, i+1, lTotal);
|
|
end;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.GetXMLDocument(var XMLDocument : IXMLDocument) : IXMLDocument;
|
|
begin
|
|
if (XMLDocument=NIL) then begin
|
|
XMLDocument := NewROXmlDocument;
|
|
XMLDocument.New();
|
|
end;
|
|
|
|
result := XMLDocument;
|
|
end;
|
|
|
|
function TDAXmlDataStreamer.GetReadXSLT: IXMLDocument;
|
|
begin
|
|
result := GetXMLDocument(fReadXSLT)
|
|
end;
|
|
|
|
{function TDAXmlDataStreamer.GetReadDeltaXSLT: IXMLDocument;
|
|
begin
|
|
result := GetXMLDocument(fReadDeltaXSLT);
|
|
end;}
|
|
|
|
function TDAXmlDataStreamer.GetWriteXSLT: IXMLDocument;
|
|
begin
|
|
result := GetXMLDocument(fWriteXSLT);
|
|
end;
|
|
|
|
{function TDAXmlDataStreamer.GetWriteDeltaXSLT: IXMLDocument;
|
|
begin
|
|
result := GetXMLDocument(fWriteDeltaXSLT);
|
|
end;}
|
|
|
|
function TDAXmlDataStreamer.SaveDocumentName: Boolean;
|
|
begin
|
|
result := fDocumentName<>nn_DocumentName
|
|
end;
|
|
|
|
procedure TDAXmlDataStreamer.SetDocumentName(const Value: string);
|
|
var n : string;
|
|
begin
|
|
n := Trim(Value);
|
|
if (n<>'')
|
|
then fDocumentName := n;
|
|
end;
|
|
|
|
procedure TDAXmlDataStreamer.ClearXMLNodes;
|
|
begin
|
|
fXMLDocument := NIL;
|
|
fXMLDocument := NewROXmlDocument;
|
|
|
|
fSchemaRoot := NIL;
|
|
fRootNode := NIL;
|
|
fDatasetSchemaNode := NIL;
|
|
fDeltaSchemaNode := NIL;
|
|
fDatasetNode := NIL;
|
|
fDeltaNode := NIL;
|
|
end;
|
|
|
|
end.
|