Forráskód Böngészése

* Implemented abstract class TBufDatasetReader and TXMLBufDatasetReader

git-svn-id: trunk@11740 -
joost 17 éve
szülő
commit
1f960d8d59
1 módosított fájl, 386 hozzáadás és 257 törlés
  1. 386 257
      packages/fcl-db/src/base/bufdataset.pas

+ 386 - 257
packages/fcl-db/src/base/bufdataset.pas

@@ -279,9 +279,83 @@ type
     procedure EndUpdate; override;
   end;
 
+
+  { TBufDatasetReader }
+
+type
+  TChangeLogInfo = record
+       FirstChangeNode : pointer;
+       SecondChangeNode : pointer;
+       Bookmark   : TBufBookmark;
+  end;
+  TChangeLogEntry = record
+       UpdateKind : TUpdateKind;
+       OrigEntry  : integer;
+       NewEntry   : integer;
+  end;
+  TChangeLogInfoArr = array of TChangeLogInfo;
+  TChangeLogEntryArr = array of TChangeLogEntry;
+  TRowStateValue = (rsvOriginal, rsvDeleted, rsvInserted, rsvUpdated, rsvDetailUpdates);
+  TRowState = set of TRowStateValue;
+
+
+type
+  TBufDatasetReader = class(TObject)
+  public
+    constructor create; virtual;
+
+    procedure LoadFieldDefs(AFieldDefs : TFieldDefs); virtual; abstract;
+    procedure StoreFieldDefs(AFieldDefs : TFieldDefs); virtual; abstract;
+    procedure GetRecordUpdState(var AIsUpdate,AAddRecordBuffer,AIsFirstEntry : boolean); virtual; abstract;
+    procedure EndStoreRecord(const AChangeLog : TChangeLogEntryArr); virtual; abstract;
+    function GetCurrentRecord : boolean; virtual; abstract;
+    procedure GotoNextRecord; virtual; abstract;
+    function GetCurrentElement : pointer; virtual; abstract;
+    procedure GotoElement(const AnElement : pointer); virtual; abstract;
+    procedure RestoreRecord(ADataset : TDataset); virtual; abstract;
+    procedure StoreRecord(ADataset : TDataset; RowState : TRowState); virtual; abstract;
+    procedure InitLoadRecords(var AChangeLog : TChangeLogEntryArr); virtual; abstract;
+  end;
+
+  { TXMLBufDatasetReader }
+
+  TXMLBufDatasetReader = class(TBufDatasetReader)
+    FFileName : String;
+
+    XMLDocument    : TXMLDocument;
+    DataPacketNode : TDOMElement;
+    MetaDataNode   : TDOMNode;
+    FieldsNode     : TDOMNode;
+
+    FChangeLogNode,
+    FParamsNode,
+    FRowDataNode,
+    FRecordNode       : TDOMNode;
+
+  public
+    constructor create(AFileName : string); overload; virtual;
+    destructor destroy; override;
+    procedure LoadFieldDefs(AFieldDefs : TFieldDefs); override;
+    procedure StoreFieldDefs(AFieldDefs : TFieldDefs); override;
+    procedure GetRecordUpdState(var AIsUpdate, AAddRecordBuffer,
+                     AIsFirstEntry: boolean); override;
+    procedure EndStoreRecord(const AChangeLog : TChangeLogEntryArr); override;
+    function GetCurrentRecord : boolean; override;
+    procedure GotoNextRecord; override;
+    procedure GotoElement(const AnElement : pointer); override;
+    procedure InitLoadRecords(var AChangeLog : TChangeLogEntryArr); override;
+    function GetCurrentElement: pointer; override;
+    procedure RestoreRecord(ADataset : TDataset); override;
+    procedure StoreRecord(ADataset : TDataset; RowState : TRowState); override;
+
+    property FileName : string read FFileName write FFileName;
+  end;
+
+
   TBufDataset = class(TDBDataSet)
   private
     FFileName: string;
+    FDatasetReader  : TBufDatasetReader;
     FIndexes        : array of TBufIndex;
     FMaxIndexesCount: integer;
 
@@ -310,10 +384,6 @@ type
     FBlobBuffers      : array of PBlobBuffer;
     FUpdateBlobBuffers: array of PBlobBuffer;
     
-    FChangeLogNode,
-    FRowDataNode,
-    FRecordNode       : TDOMNode;
-
     procedure FetchAll;
     procedure BuildIndex(var AIndex : TBufIndex);
     function GetIndexDefs : TIndexDefs;
@@ -414,12 +484,6 @@ implementation
 
 uses variants, dbconst, xmlwrite, xmlread;
 
-type TChangeLogEntry = record
-       UpdateKind : TUpdateKind;
-       OrigEntry  : integer;
-       NewEntry   : integer;
-     end;
-
 function DBCompareText(subValue, aValue: pointer; options: TLocateOptions): LargeInt;
 
 begin
@@ -810,7 +874,9 @@ procedure TBufDataset.InternalOpen;
 var IndexNr : integer;
 
 begin
-  if FFileName<>'' then IntLoadFielddefsFromFile(FFileName);
+  if not Assigned(FDatasetReader) and (FileName<>'') then
+    FDatasetReader := TXMLBufDatasetReader.Create(FFileName); // <-- MEM-LEAK
+  if assigned(FDatasetReader) then IntLoadFielddefsFromFile(FFileName);
   CalcRecordSize;
 
   FBRecordcount := 0;
@@ -830,7 +896,7 @@ begin
     on E: Exception do Filter := EmptyStr;
   end;
 
-  if FFileName<>'' then IntLoadRecordsFromFile;
+  if assigned(FDatasetReader) then IntLoadRecordsFromFile;
 end;
 
 procedure TBufDataset.InternalClose;
@@ -1187,9 +1253,7 @@ procedure TBufDataset.DoBeforeClose;
 begin
   inherited DoBeforeClose;
   if FFileName<>'' then
-    begin
     SaveToFile(FFileName);
-    end;
 end;
 
 function TBufDataset.GetActiveRecordUpdateBuffer : boolean;
@@ -2245,90 +2309,25 @@ const
 procedure TBufDataset.SaveToFile(const FileName: string;
   Format: TDataPacketFormat);
 
-type TRowStateValue = (rsvOriginal, rsvDeleted, rsvInserted, rsvUpdated, rsvDetailUpdates);
-     TRowState = set of TRowStateValue;
-
-var XMLDocument    : TXMLDocument;
-    DataPacketNode : TDOMElement;
-    MetaDataNode   : TDOMElement;
-    FieldsNode     : TDOMElement;
-    RowDataNode    : TDOMElement;
-    ParamsNode     : TDOMElement;
-    AFieldNode     : TDOMElement;
-    ARecordNode    : TDOMElement;
-    i              : integer;
+var i              : integer;
     ScrollResult   : TGetResult;
     StoreDSState   : TDataSetState;
     ABookMark      : PBufBookmark;
     ATBookmark     : TBufBookmark;
     ChangeLog      : array of TChangeLogEntry;
 
-  procedure SaveRecord(const RowState : TRowState);
-  var FieldNr : Integer;
-      RowStateInt : Integer;
-  begin
-    ARecordNode := XMLDocument.CreateElement('ROW');
-    for FieldNr := 0 to Fields.Count-1 do
-      begin
-      ARecordNode.SetAttribute(fields[FieldNr].FieldName,fields[FieldNr].AsString);
-      end;
-    RowStateInt:=0;
-    if rsvOriginal in RowState then RowStateInt := RowStateInt+1;
-    if rsvInserted in RowState then RowStateInt := RowStateInt+4;
-    if rsvUpdated in RowState then RowStateInt := RowStateInt+8;
-    RowStateInt:=integer(RowState);
-    if RowStateInt<>0 then
-      ARecordNode.SetAttribute('RowState',inttostr(RowStateInt));
-    RowDataNode.AppendChild(ARecordNode);
-  end;
-
 var RowState : TRowState;
     RecUpdBuf: integer;
     EntryNr  : integer;
     ChangeLogStr : String;
   
 begin
+  FDatasetReader := TXMLBufDatasetReader.Create(FileName);
+  try
+
 //  CheckActive;
   ABookMark:=@ATBookmark;
-  XMLDocument := TXMLDocument.Create;
-  DataPacketNode := XMLDocument.CreateElement('DATAPACKET');
-  DataPacketNode.SetAttribute('Version','2.0');
-  
-  MetaDataNode := XMLDocument.CreateElement('METADATA');
-  FieldsNode := XMLDocument.CreateElement('FIELDS');
-  
-  for i := 0 to Fields.Count -1 do with fields[i] do
-    begin
-    AFieldNode := XMLDocument.CreateElement('FIELD');
-    if fields[i].Name <> '' then AFieldNode.SetAttribute('fieldname',fields[i].Name);
-    AFieldNode.SetAttribute('attrname',fields[i].FieldName);
-    if size <> 0 then AFieldNode.SetAttribute('width',IntToStr(Size));
-    AFieldNode.SetAttribute('fieldtype',XMLFieldtypenames[fields[i].DataType]);
-    case DataType of
-      ftAutoInc : begin
-                  AFieldNode.SetAttribute('readonly','true');
-                  AFieldNode.SetAttribute('subtype','Autoinc');
-                  end;
-      ftCurrency: AFieldNode.SetAttribute('subtype','Money');
-      ftVarBytes,
-        ftBlob  : AFieldNode.SetAttribute('subtype','Binary');
-      ftMemo    : AFieldNode.SetAttribute('subtype','Text');
-      ftTypedBinary,
-        ftGraphic: AFieldNode.SetAttribute('subtype','Graphics');
-      ftFmtMemo : AFieldNode.SetAttribute('subtype','Formatted');
-      ftParadoxOle,
-        ftDBaseOle : AFieldNode.SetAttribute('subtype','Ole');
-    end; {case}
-    if ReadOnly then AFieldNode.SetAttribute('readonly','true');
-    
-    FieldsNode.AppendChild(AFieldNode);
-    end;
-
-  MetaDataNode.AppendChild(FieldsNode);
-  ParamsNode := XMLDocument.CreateElement('PARAMS');
-  MetaDataNode.AppendChild(ParamsNode);
-  DataPacketNode.AppendChild(MetaDataNode);
-  RowDataNode := XMLDocument.CreateElement('ROWDATA');
+  FDatasetReader.StoreFieldDefs(FieldDefs);
 
   SetLength(ChangeLog,length(FUpdateBuffer));
   EntryNr:=1;
@@ -2365,7 +2364,7 @@ begin
       RowState:=[];
       end;
 
-    SaveRecord(RowState);
+    FDatasetReader.StoreRecord(Self,RowState);
     inc(EntryNr);
     ScrollResult:=FCurrentIndex.ScrollForward;
     end;
@@ -2376,7 +2375,7 @@ begin
       begin
       RowState:=[rsvDeleted];
       FFilterBuffer:=FUpdateBuffer[RecUpdBuf].OldValuesBuffer;
-      SaveRecord(RowState);
+      FDatasetReader.StoreRecord(Self, RowState);
       with ChangeLog[RecUpdBuf] do
         begin
         NewEntry:=EntryNr;
@@ -2389,7 +2388,7 @@ begin
       RowState:=[rsvUpdated];
       FCurrentIndex.GotoBookmark(@BookmarkData);
       FFilterBuffer:=FCurrentIndex.CurrentBuffer;
-      SaveRecord(RowState);
+      FDatasetReader.StoreRecord(Self, RowState);
       with ChangeLog[RecUpdBuf] do
         begin
         NewEntry:=EntryNr;
@@ -2401,36 +2400,22 @@ begin
 
   RestoreState(StoreDSState);
 
-  DataPacketNode.AppendChild(RowDataNode);
-
-  ChangeLogStr:='';
-  for i := 0 to length(ChangeLog) -1 do with ChangeLog[i] do
-    begin
-    ChangeLogStr:=ChangeLogStr+' '+inttostr(NewEntry)+' '+inttostr(OrigEntry)+' ';
-    if UpdateKind=ukModify then ChangeLogStr := ChangeLogStr+'8';
-    if UpdateKind=ukInsert then ChangeLogStr := ChangeLogStr+'4';
-    if UpdateKind=ukDelete then ChangeLogStr := ChangeLogStr+'2';
-    end;
-
-  if ChangeLogStr<>'' then
-    ParamsNode.SetAttribute('CHANGE_LOG',Trim(ChangeLogStr));
+  FDatasetReader.EndStoreRecord(ChangeLog);
   SetLength(ChangeLog,0);
-  XMLDocument.AppendChild(DataPacketNode);
-  WriteXML(XMLDocument,FileName);
 
-  FieldsNode.Free;
-  MetaDataNode.Free;
-  DataPacketNode.Free;
-  XMLDocument.Free;
+  finally
+    FDatasetReader.Free;
+  end;
 end;
 
 procedure TBufDataset.LoadFromFile(const AFileName: string);
-var StoreFileName : string;
 begin
-  StoreFileName:=FileName;
-  FileName := AFileName;
-  Open;
-  FileName := StoreFileName;
+  FDatasetReader := TXMLBufDatasetReader.Create(AFileName);
+  try
+    Open;
+  finally
+    FDatasetReader.Free;
+  end;
 end;
 
 procedure TBufDataset.CreateDataset;
@@ -2441,160 +2426,33 @@ end;
 
 procedure TBufDataset.IntLoadFielddefsFromFile(const FileName: string);
 
-  function GetNodeAttribute(const aNode : TDOMNode; AttName : String) : string;
-  var AnAttr : TDomNode;
-  begin
-    AnAttr := ANode.Attributes.GetNamedItem(AttName);
-    if assigned(AnAttr) then result := AnAttr.NodeValue
-    else result := '';
-  end;
-
-var XMLDocument    : TXMLDocument;
-    DataPacketNode : TDOMNode;
-    MetaDataNode   : TDOMNode;
-    FieldsNode     : TDOMNode;
-    AFieldNode     : TDOMNode;
-    AFieldDef      : TFieldDef;
-    iFieldType     : TFieldType;
-    FTString       : string;
-    i              : integer;
-
 begin
-  ReadXMLFile(XMLDocument,FileName);
-  DataPacketNode := XMLDocument.FindNode('DATAPACKET');
-  if not assigned(DataPacketNode) then DatabaseError('Onbekend formaat');
-
-  MetaDataNode := DataPacketNode.FindNode('METADATA');
-  if not assigned(MetaDataNode) then DatabaseError('Onbekend formaat');
-
-  FieldsNode := MetaDataNode.FindNode('FIELDS');
-  if not assigned(FieldsNode) then DatabaseError('Onbekend formaat');
-  
-  with FieldsNode.ChildNodes do for i := 0 to Count - 1 do
-    begin
-    AFieldNode := item[i];
-    if AFieldNode.CompareName('FIELD')=0 then
-      begin
-      AFieldDef := TFieldDef.create(FieldDefs);
-      AFieldDef.DisplayName:=GetNodeAttribute(AFieldNode,'fieldname');
-      AFieldDef.Name:=GetNodeAttribute(AFieldNode,'attrname');
-      AFieldDef.Size:=StrToIntDef(GetNodeAttribute(AFieldNode,'width'),0);
-      FTString:=GetNodeAttribute(AFieldNode,'fieldtype');
-      
-      AFieldDef.DataType:=ftUnknown;
-      for iFieldType:=low(TFieldType) to high(TFieldType) do
-       if SameText(XMLFieldtypenames[iFieldType],FTString) then
-        begin
-        AFieldDef.DataType:=iFieldType;
-        break;
-        end;
-      end;
-    end;
-    
-  FChangeLogNode := MetaDataNode.FindNode('PARAMS');
-  if assigned(FChangeLogNode) then
-    FChangeLogNode := FChangeLogNode.Attributes.GetNamedItem('CHANGE_LOG');
-
-  FRowDataNode := DataPacketNode.FindNode('ROWDATA');
-  FRecordNode := nil;
-
-//  XMLDocument.Free;     <-- MEM LEAK!
+  FDatasetReader.LoadFielddefs(FieldDefs);
   if DefaultFields then CreateFields;
 end;
 
 procedure TBufDataset.IntLoadRecordsFromFile;
 
-type TChangeLogInfo = record
-       FirstChangeNode : TDomNode;
-       SecondChangeNode : TDomNode;
-       Bookmark   : TBufBookmark;
-     end;
 
-var ARowStateNode  : TDOmNode;
-    ARowState      : integer;
-    StoreState     : TDataSetState;
-    ChangeLog      : array of TChangeLogEntry;
+var StoreState     : TDataSetState;
+    ChangeLog      : TChangeLogEntryArr;
     ChangeLogStr   : string;
-    ChangeLogInfo  : array of TChangeLogInfo;
+    ChangeLogInfo  : TChangeLogInfoArr;
     EntryNr        : integer;
-    i,cp           : integer;
-    ps             : string;
+    i              : integer;
     IsUpdate,
     AddRecordBuffer,
     IsFirstEntry    : boolean;
 
-  procedure RestoreRecord;
-  var FieldNr : integer;
-      AFieldNode     : TDOMNode;
-  begin
-    FFilterBuffer:=FIndexes[0].SpareBuffer;
-    fillchar(FFilterBuffer^,FNullmaskSize,0);
-    for FieldNr:=0 to FieldCount-1 do
-      begin
-      AFieldNode := FRecordNode.Attributes.GetNamedItem(Fields[FieldNr].FieldName);
-      if assigned(AFieldNode) then
-        begin
-        Fields[FieldNr].AsString := AFieldNode.NodeValue;  // set it to the sparebuf
-        end
-      end;
-  end;
-
 begin
-  FRecordNode := FRowDataNode.FirstChild;
+  FDatasetReader.InitLoadRecords(ChangeLog);
   EntryNr:=1;
   StoreState:=SetTempState(dsFilter);
-  if assigned(FChangeLogNode) then
-    ChangeLogStr:=FChangeLogNode.NodeValue
-  else
-    ChangeLogStr:='';
-  ps := '';
-  cp := 0;
-  if ChangeLogStr<>'' then for i := 1 to length(ChangeLogStr)+1 do
-    begin
-    if not (ChangeLogStr[i] in [' ',#0]) then
-      ps := ps + ChangeLogStr[i]
-    else
-      begin
-      case (cp mod 3) of
-        0 : begin
-            SetLength(ChangeLog,length(ChangeLog)+1);
-            ChangeLog[cp div 3].OrigEntry:=StrToIntDef(ps,0);
-            end;
-        1 : ChangeLog[cp div 3].NewEntry:=StrToIntDef(ps,0);
-        2 : begin
-            if ps = '2' then
-              ChangeLog[cp div 3].UpdateKind:=ukDelete
-            else if ps = '4' then
-              ChangeLog[cp div 3].UpdateKind:=ukInsert
-            else if ps = '8' then
-              ChangeLog[cp div 3].UpdateKind:=ukModify;
-            end;
-      end; {case}
-      ps := '';
-      inc(cp);
-      end;
-    end;
   SetLength(ChangeLogInfo,length(ChangeLog));
 
-
-  while assigned(FRecordNode) do
+  while FDatasetReader.GetCurrentRecord do
     begin
-    ARowStateNode := FRecordNode.Attributes.GetNamedItem('RowState');
-    if ARowStateNode = nil then // This item is not edited
-      begin
-      IsUpdate:=False;
-      AddRecordBuffer:=True;
-      end
-    else
-      begin
-      IsUpdate:=True;
-      ARowState:=StrToIntDef(ARowStateNode.NodeValue,0);
-      AddRecordBuffer:=((ARowState and 5) = 4)      // This item contains an inserted record which is not edited afterwards
-                        or ((ARowState and 9) = 8); // This item contains the last edited record
-      IsFirstEntry:=((ARowState and 2) = 2)         // This item is deleted
-                   or ((ARowState and 8) = 8)       // This item is a change
-      end;
-
+    FDatasetReader.GetRecordUpdState(IsUpdate,AddRecordBuffer,IsFirstEntry);
 
     if IsUpdate then
       begin
@@ -2602,13 +2460,13 @@ begin
         begin
         for i := 0 to length(ChangeLog) -1 do
           if ChangeLog[i].OrigEntry=EntryNr then break;
-        ChangeLogInfo[i].FirstChangeNode:=FRecordNode;
+        ChangeLogInfo[i].FirstChangeNode:=FDatasetReader.GetCurrentElement;
         end
       else
         begin
         for i := 0 to length(ChangeLog) -1 do
           if ChangeLog[i].NewEntry=EntryNr then break;
-        ChangeLogInfo[i].SecondChangeNode:=FRecordNode;
+        ChangeLogInfo[i].SecondChangeNode:=FDatasetReader.GetCurrentElement;
         end;
 
       FIndexes[0].StoreSpareRecIntoBookmark(@ChangeLogInfo[i].Bookmark);
@@ -2616,14 +2474,15 @@ begin
 
     if AddRecordBuffer then
       begin
-      RestoreRecord;
+      FFilterBuffer:=FIndexes[0].SpareBuffer;
+      fillchar(FFilterBuffer^,FNullmaskSize,0);
+
+      FDatasetReader.RestoreRecord(self);
       FIndexes[0].AddRecord(IntAllocRecordBuffer);
       inc(FBRecordCount);
       end;
 
-    FRecordNode := FRecordNode.NextSibling;
-    while assigned(FRecordNode) and (FRecordNode.CompareName('ROW')<>0) do
-      FRecordNode := FRecordNode.NextSibling;
+    FDatasetReader.GotoNextRecord;
     inc(EntryNr);
     end;
 
@@ -2636,23 +2495,23 @@ begin
       ukDelete : begin
                  FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:=ukDelete;
                  FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData:=ChangeLogInfo[i].Bookmark;
-                 FRecordNode:=ChangeLogInfo[i].FirstChangeNode;
-                 RestoreRecord;
+                 FDatasetReader.GotoElement(ChangeLogInfo[i].FirstChangeNode);
+                 FDatasetReader.RestoreRecord(self);
                  FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer:=IntAllocRecordBuffer;
                  move(findexes[0].SpareBuffer^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
                  end;
       ukModify : begin
                  FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:=ukModify;
                  FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData:=ChangeLogInfo[i].Bookmark;
-                 FRecordNode:=ChangeLogInfo[i].SecondChangeNode;
-                 RestoreRecord;
+                 FDatasetReader.GotoElement(ChangeLogInfo[i].SecondChangeNode);
+                 FDatasetReader.RestoreRecord(self);
                  FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer:=IntAllocRecordBuffer;
                  move(findexes[0].SpareBuffer^,FUpdateBuffer[FCurrentUpdateBuffer].OldValuesBuffer^,FRecordSize);
                  end;
       ukInsert : begin
                  FUpdateBuffer[FCurrentUpdateBuffer].UpdateKind:=ukInsert;
                  FUpdateBuffer[FCurrentUpdateBuffer].BookmarkData:=ChangeLogInfo[i].Bookmark;
-                 FRecordNode:=ChangeLogInfo[i].FirstChangeNode;
+                 FDatasetReader.GotoElement(ChangeLogInfo[i].FirstChangeNode);
                  end;
     end; {case}
     end;
@@ -3074,5 +2933,275 @@ begin
 //  inherited EndUpdate;
 end;
 
+{ TBufDatasetReader }
+
+constructor TBufDatasetReader.create;
+begin
+  inherited;
+end;
+
+{ TXMLBufDatasetReader }
+
+constructor TXMLBufDatasetReader.create(AFileName: string);
+begin
+  inherited create;
+  FFileName:=AFileName;
+end;
+
+destructor TXMLBufDatasetReader.destroy;
+begin
+  FieldsNode.Free;
+  MetaDataNode.Free;
+  DataPacketNode.Free;
+  XMLDocument.Free;
+end;
+
+procedure TXMLBufDatasetReader.LoadFieldDefs(AFieldDefs : TFieldDefs);
+
+  function GetNodeAttribute(const aNode : TDOMNode; AttName : String) : string;
+  var AnAttr : TDomNode;
+  begin
+    AnAttr := ANode.Attributes.GetNamedItem(AttName);
+    if assigned(AnAttr) then result := AnAttr.NodeValue
+    else result := '';
+  end;
+
+var i           : integer;
+    AFieldDef   : TFieldDef;
+    iFieldType  : TFieldType;
+    FTString    : string;
+    AFieldNode  : TDOMNode;
+
+begin
+  ReadXMLFile(XMLDocument,FileName);
+  DataPacketNode := XMLDocument.FindNode('DATAPACKET') as TDOMElement;
+  if not assigned(DataPacketNode) then DatabaseError('Onbekend formaat');
+
+  MetaDataNode := DataPacketNode.FindNode('METADATA');
+  if not assigned(MetaDataNode) then DatabaseError('Onbekend formaat');
+
+  FieldsNode := MetaDataNode.FindNode('FIELDS');
+  if not assigned(FieldsNode) then DatabaseError('Onbekend formaat');
+
+  with FieldsNode.ChildNodes do for i := 0 to Count - 1 do
+    begin
+    AFieldNode := item[i];
+    if AFieldNode.CompareName('FIELD')=0 then
+      begin
+      AFieldDef := TFieldDef.create(AFieldDefs);
+      AFieldDef.DisplayName:=GetNodeAttribute(AFieldNode,'fieldname');
+      AFieldDef.Name:=GetNodeAttribute(AFieldNode,'attrname');
+      AFieldDef.Size:=StrToIntDef(GetNodeAttribute(AFieldNode,'width'),0);
+      FTString:=GetNodeAttribute(AFieldNode,'fieldtype');
+
+      AFieldDef.DataType:=ftUnknown;
+      for iFieldType:=low(TFieldType) to high(TFieldType) do
+       if SameText(XMLFieldtypenames[iFieldType],FTString) then
+        begin
+        AFieldDef.DataType:=iFieldType;
+        break;
+        end;
+      end;
+    end;
+
+  FChangeLogNode := MetaDataNode.FindNode('PARAMS');
+  if assigned(FChangeLogNode) then
+    FChangeLogNode := FChangeLogNode.Attributes.GetNamedItem('CHANGE_LOG');
+
+  FRowDataNode := DataPacketNode.FindNode('ROWDATA');
+  FRecordNode := nil;
+
+end;
+
+procedure TXMLBufDatasetReader.StoreFieldDefs(AFieldDefs: TFieldDefs);
+
+var i           : integer;
+    AFieldNode  : TDOMElement;
+
+begin
+  XMLDocument := TXMLDocument.Create;
+  DataPacketNode := XMLDocument.CreateElement('DATAPACKET');
+  DataPacketNode.SetAttribute('Version','2.0');
+
+  MetaDataNode := XMLDocument.CreateElement('METADATA');
+  FieldsNode := XMLDocument.CreateElement('FIELDS');
+
+  for i := 0 to AFieldDefs.Count -1 do with AFieldDefs[i] do
+    begin
+    AFieldNode := XMLDocument.CreateElement('FIELD');
+    if Name <> '' then AFieldNode.SetAttribute('fieldname',Name);
+    AFieldNode.SetAttribute('attrname',DisplayName);
+    if size <> 0 then AFieldNode.SetAttribute('width',IntToStr(Size));
+    AFieldNode.SetAttribute('fieldtype',XMLFieldtypenames[DataType]);
+    case DataType of
+      ftAutoInc : begin
+                  AFieldNode.SetAttribute('readonly','true');
+                  AFieldNode.SetAttribute('subtype','Autoinc');
+                  end;
+      ftCurrency: AFieldNode.SetAttribute('subtype','Money');
+      ftVarBytes,
+        ftBlob  : AFieldNode.SetAttribute('subtype','Binary');
+      ftMemo    : AFieldNode.SetAttribute('subtype','Text');
+      ftTypedBinary,
+        ftGraphic: AFieldNode.SetAttribute('subtype','Graphics');
+      ftFmtMemo : AFieldNode.SetAttribute('subtype','Formatted');
+      ftParadoxOle,
+        ftDBaseOle : AFieldNode.SetAttribute('subtype','Ole');
+    end; {case}
+    if faReadonly in Attributes then AFieldNode.SetAttribute('readonly','true');
+
+    FieldsNode.AppendChild(AFieldNode);
+    end;
+
+  MetaDataNode.AppendChild(FieldsNode);
+  FParamsNode := XMLDocument.CreateElement('PARAMS');
+  MetaDataNode.AppendChild(FParamsNode);
+  DataPacketNode.AppendChild(MetaDataNode);
+  FRowDataNode := XMLDocument.CreateElement('ROWDATA');
+end;
+
+procedure TXMLBufDatasetReader.GetRecordUpdState(var AIsUpdate,
+  AAddRecordBuffer, AIsFirstEntry: boolean);
+var ARowStateNode  : TDOmNode;
+    ARowState      : integer;
+
+begin
+  ARowStateNode := FRecordNode.Attributes.GetNamedItem('RowState');
+  if ARowStateNode = nil then // This item is not edited
+    begin
+    AIsUpdate:=False;
+    AAddRecordBuffer:=True;
+    end
+  else
+    begin
+    AIsUpdate:=True;
+    ARowState:=StrToIntDef(ARowStateNode.NodeValue,0);
+    AAddRecordBuffer:=((ARowState and 5) = 4)      // This item contains an inserted record which is not edited afterwards
+                      or ((ARowState and 9) = 8); // This item contains the last edited record
+    AIsFirstEntry:=((ARowState and 2) = 2)         // This item is deleted
+                 or ((ARowState and 8) = 8)       // This item is a change
+    end;
+end;
+
+procedure TXMLBufDatasetReader.EndStoreRecord(const AChangeLog : TChangeLogEntryArr);
+var ChangeLogStr : String;
+    i            : integer;
+begin
+  ChangeLogStr:='';
+  for i := 0 to length(AChangeLog) -1 do with AChangeLog[i] do
+    begin
+    ChangeLogStr:=ChangeLogStr+' '+inttostr(NewEntry)+' '+inttostr(OrigEntry)+' ';
+    if UpdateKind=ukModify then ChangeLogStr := ChangeLogStr+'8';
+    if UpdateKind=ukInsert then ChangeLogStr := ChangeLogStr+'4';
+    if UpdateKind=ukDelete then ChangeLogStr := ChangeLogStr+'2';
+    end;
+
+  if ChangeLogStr<>'' then
+    (FParamsNode as TDomElement).SetAttribute('CHANGE_LOG',Trim(ChangeLogStr));
+
+  DataPacketNode.AppendChild(FRowDataNode);
+  XMLDocument.AppendChild(DataPacketNode);
+  WriteXML(XMLDocument,FileName);
+end;
+
+function TXMLBufDatasetReader.GetCurrentRecord: boolean;
+begin
+  Result := assigned(FRecordNode);
+end;
+
+procedure TXMLBufDatasetReader.InitLoadRecords(
+  var AChangeLog: TChangeLogEntryArr);
+
+var ChangeLogStr : String;
+    i,cp         : integer;
+    ps           : string;
+
+begin
+  FRecordNode := FRowDataNode.FirstChild;
+  if assigned(FChangeLogNode) then
+    ChangeLogStr:=FChangeLogNode.NodeValue
+  else
+    ChangeLogStr:='';
+  ps := '';
+  cp := 0;
+  if ChangeLogStr<>'' then for i := 1 to length(ChangeLogStr)+1 do
+    begin
+    if not (ChangeLogStr[i] in [' ',#0]) then
+      ps := ps + ChangeLogStr[i]
+    else
+      begin
+      case (cp mod 3) of
+        0 : begin
+            SetLength(AChangeLog,length(AChangeLog)+1);
+            AChangeLog[cp div 3].OrigEntry:=StrToIntDef(ps,0);
+            end;
+        1 : AChangeLog[cp div 3].NewEntry:=StrToIntDef(ps,0);
+        2 : begin
+            if ps = '2' then
+              AChangeLog[cp div 3].UpdateKind:=ukDelete
+            else if ps = '4' then
+              AChangeLog[cp div 3].UpdateKind:=ukInsert
+            else if ps = '8' then
+              AChangeLog[cp div 3].UpdateKind:=ukModify;
+            end;
+      end; {case}
+      ps := '';
+      inc(cp);
+      end;
+    end;
+end;
+
+function TXMLBufDatasetReader.GetCurrentElement: pointer;
+begin
+  Result:=FRecordNode;
+end;
+
+procedure TXMLBufDatasetReader.RestoreRecord(ADataset : TDataset);
+var FieldNr    : integer;
+    AFieldNode : TDomNode;
+begin
+  with ADataset do for FieldNr:=0 to FieldCount-1 do
+    begin
+    AFieldNode := FRecordNode.Attributes.GetNamedItem(Fields[FieldNr].FieldName);
+    if assigned(AFieldNode) then
+      begin
+      Fields[FieldNr].AsString := AFieldNode.NodeValue;  // set it to the sparebuf
+      end
+    end;
+end;
+
+procedure TXMLBufDatasetReader.StoreRecord(ADataset: TDataset;
+  RowState: TRowState);
+var FieldNr : Integer;
+    RowStateInt : Integer;
+    ARecordNode : TDOMElement;
+begin
+  ARecordNode := XMLDocument.CreateElement('ROW');
+  for FieldNr := 0 to ADataset.Fields.Count-1 do
+    begin
+    ARecordNode.SetAttribute(ADataset.fields[FieldNr].FieldName,ADataset.fields[FieldNr].AsString);
+    end;
+  RowStateInt:=0;
+  if rsvOriginal in RowState then RowStateInt := RowStateInt+1;
+  if rsvInserted in RowState then RowStateInt := RowStateInt+4;
+  if rsvUpdated in RowState then RowStateInt := RowStateInt+8;
+  RowStateInt:=integer(RowState);
+  if RowStateInt<>0 then
+    ARecordNode.SetAttribute('RowState',inttostr(RowStateInt));
+  FRowDataNode.AppendChild(ARecordNode);
+end;
+
+procedure TXMLBufDatasetReader.GotoNextRecord;
+begin
+  FRecordNode := FRecordNode.NextSibling;
+  while assigned(FRecordNode) and (FRecordNode.CompareName('ROW')<>0) do
+    FRecordNode := FRecordNode.NextSibling;
+end;
+
+procedure TXMLBufDatasetReader.GotoElement(const AnElement: pointer);
+begin
+  FRecordNode:=TDomNode(AnElement);
+end;
+
 begin
 end.