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 { } // 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.