xmldatapacketreader.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2008 by Joost van der Sluis, member of the
  4. Free Pascal development team
  5. TXMLDatapacketReader implementation
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit XMLDatapacketReader;
  13. {$mode objfpc}{$H+}
  14. interface
  15. uses
  16. Classes, SysUtils, Bufdataset, dom, db;
  17. type
  18. TChangeLogEntry = record
  19. UpdateKind : TUpdateKind;
  20. OrigEntry : integer;
  21. NewEntry : integer;
  22. end;
  23. TChangeLogEntryArr = array of TChangeLogEntry;
  24. type
  25. { TXMLDatapacketReader }
  26. TXMLDatapacketReader = class(TDataPacketReader)
  27. XMLDocument : TXMLDocument;
  28. DataPacketNode : TDOMElement;
  29. MetaDataNode : TDOMNode;
  30. FieldsNode : TDOMNode;
  31. FChangeLogNode,
  32. FParamsNode,
  33. FRowDataNode,
  34. FRecordNode : TDOMNode;
  35. FChangeLog : TChangeLogEntryArr;
  36. FEntryNr : integer;
  37. FLastChange : integer;
  38. public
  39. destructor destroy; override;
  40. procedure StoreFieldDefs(AFieldDefs : TFieldDefs; AnAutoIncValue : integer); override;
  41. procedure StoreRecord(ADataset : TCustomBufDataset; ARowState : TRowState; AUpdOrder : integer = 0); override;
  42. procedure FinalizeStoreRecords; override;
  43. procedure LoadFieldDefs(AFieldDefs : TFieldDefs; var AnAutoIncValue : integer); override;
  44. procedure InitLoadRecords; override;
  45. function GetCurrentRecord : boolean; override;
  46. function GetRecordRowState(out AUpdOrder : Integer) : TRowState; override;
  47. procedure RestoreRecord(ADataset : TCustomBufDataset); override;
  48. procedure GotoNextRecord; override;
  49. class function RecognizeStream(AStream : TStream) : boolean; override;
  50. end;
  51. implementation
  52. uses xmlwrite, xmlread, base64;
  53. const
  54. XMLFieldtypenames : Array [TFieldType] of String[16] =
  55. (
  56. 'Unknown',
  57. 'string',
  58. 'i2',
  59. 'i4',
  60. 'i4',
  61. 'boolean',
  62. 'r8',
  63. 'r8:Money',
  64. 'fixed',
  65. 'date',
  66. 'time',
  67. 'datetime',
  68. 'bin.hex',
  69. 'bin.hex',
  70. 'i4:Autoinc',
  71. 'bin.hex:Binary',
  72. 'bin.hex:Text',
  73. 'bin.hex:Graphics',
  74. 'bin.hex:Formatted',
  75. 'bin.hex:Ole',
  76. 'bin.hex:Ole',
  77. 'bin.hex:Graphics',
  78. '',
  79. 'string', // ftFixedChar
  80. 'string.uni', // ftWideString
  81. 'i8',
  82. '',
  83. '',
  84. '',
  85. '',
  86. '',
  87. '',
  88. '',
  89. '',
  90. '',
  91. '',
  92. '',
  93. 'fixedFMT',
  94. 'string.uni', // ftFixedWideChar
  95. 'bin.hex:WideText' // ftWideMemo
  96. );
  97. resourcestring
  98. sUnknownXMLDatasetFormat = 'Unknown XML Dataset format';
  99. { TXMLDatapacketReader }
  100. destructor TXMLDatapacketReader.destroy;
  101. begin
  102. FieldsNode.Free;
  103. MetaDataNode.Free;
  104. DataPacketNode.Free;
  105. XMLDocument.Free;
  106. inherited destroy;
  107. end;
  108. procedure TXMLDatapacketReader.LoadFieldDefs(AFieldDefs: TFieldDefs; var AnAutoIncValue: integer);
  109. function GetNodeAttribute(const aNode : TDOMNode; AttName : String) : string;
  110. var AnAttr : TDomNode;
  111. begin
  112. AnAttr := ANode.Attributes.GetNamedItem(AttName);
  113. if assigned(AnAttr) then result := AnAttr.NodeValue
  114. else result := '';
  115. end;
  116. var i : integer;
  117. AFieldDef : TFieldDef;
  118. iFieldType : TFieldType;
  119. FTString : string;
  120. SubFTString : string;
  121. AFieldNode : TDOMNode;
  122. AnAutoIncNode: TDomNode;
  123. begin
  124. ReadXMLFile(XMLDocument,Stream);
  125. DataPacketNode := XMLDocument.FindNode('DATAPACKET') as TDOMElement;
  126. if not assigned(DataPacketNode) then DatabaseError(sUnknownXMLDatasetFormat);
  127. MetaDataNode := DataPacketNode.FindNode('METADATA');
  128. if not assigned(MetaDataNode) then DatabaseError(sUnknownXMLDatasetFormat);
  129. FieldsNode := MetaDataNode.FindNode('FIELDS');
  130. if not assigned(FieldsNode) then DatabaseError(sUnknownXMLDatasetFormat);
  131. with FieldsNode.ChildNodes do for i := 0 to Count - 1 do
  132. begin
  133. AFieldNode := item[i];
  134. if AFieldNode.CompareName('FIELD')=0 then
  135. begin
  136. AFieldDef := TFieldDef.create(AFieldDefs);
  137. AFieldDef.DisplayName:=GetNodeAttribute(AFieldNode,'fieldname');
  138. AFieldDef.Name:=GetNodeAttribute(AFieldNode,'attrname');
  139. AFieldDef.Size:=StrToIntDef(GetNodeAttribute(AFieldNode,'width'),0);
  140. FTString:=GetNodeAttribute(AFieldNode,'fieldtype');
  141. SubFTString:=GetNodeAttribute(AFieldNode,'subtype');
  142. if SubFTString<>'' then
  143. FTString:=FTString+':'+SubFTString;
  144. AFieldDef.DataType:=ftUnknown;
  145. for iFieldType:=low(TFieldType) to high(TFieldType) do
  146. if SameText(XMLFieldtypenames[iFieldType],FTString) then
  147. begin
  148. AFieldDef.DataType:=iFieldType;
  149. break;
  150. end;
  151. end;
  152. end;
  153. FParamsNode := MetaDataNode.FindNode('PARAMS');
  154. if assigned(FParamsNode) then
  155. begin
  156. FChangeLogNode := FParamsNode.Attributes.GetNamedItem('CHANGE_LOG');
  157. AnAutoIncNode := FParamsNode.Attributes.GetNamedItem('AUTOINCVALUE');
  158. if assigned(AnAutoIncNode) then
  159. AnAutoIncValue := StrToIntDef(AnAutoIncNode.NodeValue,-1);
  160. end;
  161. FRowDataNode := DataPacketNode.FindNode('ROWDATA');
  162. FRecordNode := nil;
  163. end;
  164. procedure TXMLDatapacketReader.StoreFieldDefs(AFieldDefs: TFieldDefs; AnAutoIncValue: integer);
  165. var i,p : integer;
  166. AFieldNode : TDOMElement;
  167. AStringFT : string;
  168. begin
  169. XMLDocument := TXMLDocument.Create;
  170. DataPacketNode := XMLDocument.CreateElement('DATAPACKET');
  171. DataPacketNode.SetAttribute('Version','2.0');
  172. MetaDataNode := XMLDocument.CreateElement('METADATA');
  173. FieldsNode := XMLDocument.CreateElement('FIELDS');
  174. for i := 0 to AFieldDefs.Count -1 do with AFieldDefs[i] do
  175. begin
  176. AFieldNode := XMLDocument.CreateElement('FIELD');
  177. if Name <> '' then AFieldNode.SetAttribute('fieldname',Name);
  178. AFieldNode.SetAttribute('attrname',DisplayName);
  179. if size <> 0 then AFieldNode.SetAttribute('width',IntToStr(Size));
  180. AStringFT:=XMLFieldtypenames[DataType];
  181. p := pos(':',AStringFT);
  182. if p > 1 then
  183. begin
  184. AFieldNode.SetAttribute('fieldtype',copy(AStringFT,1,p-1));
  185. AFieldNode.SetAttribute('subtype',copy(AStringFT,p+1,25));
  186. end
  187. else
  188. AFieldNode.SetAttribute('fieldtype',AStringFT);
  189. if faReadonly in Attributes then AFieldNode.SetAttribute('readonly','true');
  190. FieldsNode.AppendChild(AFieldNode);
  191. end;
  192. MetaDataNode.AppendChild(FieldsNode);
  193. FParamsNode := XMLDocument.CreateElement('PARAMS');
  194. if AnAutoIncValue>-1 then
  195. (FParamsNode as TDomElement).SetAttribute('AUTOINCVALUE',IntToStr(AnAutoIncValue));
  196. MetaDataNode.AppendChild(FParamsNode);
  197. DataPacketNode.AppendChild(MetaDataNode);
  198. FRowDataNode := XMLDocument.CreateElement('ROWDATA');
  199. setlength(FChangeLog,0);
  200. FEntryNr:=0;
  201. FLastChange:=-1;
  202. end;
  203. procedure TXMLDatapacketReader.FinalizeStoreRecords;
  204. var ChangeLogStr : String;
  205. i : integer;
  206. begin
  207. ChangeLogStr:='';
  208. for i := 0 to length(FChangeLog)-1 do with FChangeLog[i] do
  209. begin
  210. ChangeLogStr:=ChangeLogStr+' '+inttostr(NewEntry)+' '+inttostr(OrigEntry)+' ';
  211. if UpdateKind=ukModify then ChangeLogStr := ChangeLogStr+'8';
  212. if UpdateKind=ukInsert then ChangeLogStr := ChangeLogStr+'4';
  213. if UpdateKind=ukDelete then ChangeLogStr := ChangeLogStr+'2';
  214. end;
  215. setlength(FChangeLog,0);
  216. if ChangeLogStr<>'' then
  217. (FParamsNode as TDomElement).SetAttribute('CHANGE_LOG',Trim(ChangeLogStr));
  218. DataPacketNode.AppendChild(FRowDataNode);
  219. XMLDocument.AppendChild(DataPacketNode);
  220. WriteXML(XMLDocument,Stream);
  221. end;
  222. function TXMLDatapacketReader.GetCurrentRecord: boolean;
  223. begin
  224. Result := assigned(FRecordNode);
  225. end;
  226. function TXMLDatapacketReader.GetRecordRowState(out AUpdOrder: Integer
  227. ): TRowState;
  228. var ARowStateNode : TDOmNode;
  229. ARowState : integer;
  230. i : integer;
  231. begin
  232. ARowStateNode := FRecordNode.Attributes.GetNamedItem('RowState');
  233. if ARowStateNode = nil then // This item is not edited
  234. Result := []
  235. else
  236. begin
  237. Result := ByteToRowState(StrToIntDef(ARowStateNode.NodeValue,0));
  238. if Result = [rsvOriginal] then
  239. begin
  240. for i := 0 to length(FChangeLog)-1 do
  241. if FChangeLog[i].NewEntry=FEntryNr then break;
  242. assert(FChangeLog[i].NewEntry=FEntryNr);
  243. end
  244. else
  245. begin
  246. for i := 0 to length(FChangeLog)-1 do
  247. if FChangeLog[i].OrigEntry=FEntryNr then break;
  248. assert(FChangeLog[i].OrigEntry=FEntryNr);
  249. end;
  250. AUpdOrder:=i;
  251. end;
  252. end;
  253. procedure TXMLDatapacketReader.InitLoadRecords;
  254. var ChangeLogStr : String;
  255. i,cp : integer;
  256. ps : string;
  257. begin
  258. FRecordNode := FRowDataNode.FirstChild;
  259. FEntryNr := 1;
  260. setlength(FChangeLog,0);
  261. if assigned(FChangeLogNode) then
  262. ChangeLogStr:=FChangeLogNode.NodeValue
  263. else
  264. ChangeLogStr:='';
  265. ps := '';
  266. cp := 0;
  267. if ChangeLogStr<>'' then for i := 1 to length(ChangeLogStr)+1 do
  268. begin
  269. if not (ChangeLogStr[i] in [' ',#0]) then
  270. ps := ps + ChangeLogStr[i]
  271. else
  272. begin
  273. case (cp mod 3) of
  274. 0 : begin
  275. SetLength(FChangeLog,length(FChangeLog)+1);
  276. FChangeLog[cp div 3].OrigEntry:=StrToIntDef(ps,0);
  277. end;
  278. 1 : FChangeLog[cp div 3].NewEntry:=StrToIntDef(ps,0);
  279. 2 : begin
  280. if ps = '2' then
  281. FChangeLog[cp div 3].UpdateKind:=ukDelete
  282. else if ps = '4' then
  283. FChangeLog[cp div 3].UpdateKind:=ukInsert
  284. else if ps = '8' then
  285. FChangeLog[cp div 3].UpdateKind:=ukModify;
  286. end;
  287. end; {case}
  288. ps := '';
  289. inc(cp);
  290. end;
  291. end;
  292. end;
  293. procedure TXMLDatapacketReader.RestoreRecord(ADataset : TCustomBufDataset);
  294. var FieldNr : integer;
  295. AFieldNode : TDomNode;
  296. ABufBlobField: TBufBlobField;
  297. AField: TField;
  298. s: string;
  299. begin
  300. with ADataset do for FieldNr:=0 to FieldDefs.Count-1 do
  301. begin
  302. AFieldNode := FRecordNode.Attributes.GetNamedItem(FieldDefs[FieldNr].Name);
  303. if assigned(AFieldNode) then
  304. begin
  305. s := AFieldNode.NodeValue;
  306. AField := Fields.FieldByNumber(FieldDefs[FieldNr].FieldNo);
  307. if (FieldDefs[FieldNr].DataType in [ftBlob, ftBytes, ftVarBytes]) and (s <> '') then
  308. s := DecodeStringBase64(s);
  309. if FieldDefs[FieldNr].DataType in [ftBlob, ftMemo, ftWideMemo] then
  310. begin
  311. ABufBlobField.BlobBuffer:=ADataset.GetNewBlobBuffer;
  312. ABufBlobField.BlobBuffer^.Size:=length(s);
  313. ReAllocMem(ABufBlobField.BlobBuffer^.Buffer,ABufBlobField.BlobBuffer^.Size);
  314. move(s[1],ABufBlobField.BlobBuffer^.Buffer^,ABufBlobField.BlobBuffer^.Size);
  315. AField.SetData(@ABufBlobField);
  316. end
  317. else
  318. AField.AsString := s; // set it to the filterbuffer
  319. end
  320. end;
  321. end;
  322. procedure TXMLDatapacketReader.StoreRecord(ADataset : TCustomBufDataset; ARowState : TRowState; AUpdOrder : integer = 0);
  323. var FieldNr : Integer;
  324. AFieldDef: TFieldDef;
  325. s: string;
  326. ARecordNode : TDOMElement;
  327. begin
  328. inc(FEntryNr);
  329. ARecordNode := XMLDocument.CreateElement('ROW');
  330. for FieldNr := 0 to ADataset.FieldDefs.Count-1 do
  331. begin
  332. AFieldDef := ADataset.FieldDefs[FieldNr];
  333. s := ADataset.Fields.FieldByNumber(AFieldDef.FieldNo).AsString;
  334. if AFieldDef.DataType in [ftBlob, ftBytes, ftVarBytes] then
  335. ARecordNode.SetAttribute(AFieldDef.Name, EncodeStringBase64(s))
  336. else
  337. ARecordNode.SetAttribute(AFieldDef.Name, s);
  338. end;
  339. if ARowState<>[] then
  340. begin
  341. ARecordNode.SetAttribute('RowState',inttostr(RowStateToByte(ARowState)));
  342. if AUpdOrder>=length(FChangeLog) then
  343. setlength(FChangeLog,AUpdOrder+1);
  344. if (rsvOriginal in ARowState) or (rsvDeleted in ARowState) then
  345. FChangeLog[AUpdOrder].OrigEntry:=FEntryNr;
  346. if (rsvDeleted in ARowState) or (rsvUpdated in ARowState) or (rsvInserted in ARowState) then
  347. FChangeLog[AUpdOrder].NewEntry:=FEntryNr;
  348. if ARowState=[rsvUpdated] then
  349. FChangeLog[AUpdOrder].UpdateKind := ukModify;
  350. if ARowState=[rsvInserted] then
  351. FChangeLog[AUpdOrder].UpdateKind := ukInsert;
  352. if ARowState=[rsvDeleted] then
  353. FChangeLog[AUpdOrder].UpdateKind := ukDelete;
  354. end;
  355. FRowDataNode.AppendChild(ARecordNode);
  356. end;
  357. class function TXMLDatapacketReader.RecognizeStream(AStream: TStream): boolean;
  358. const XmlStart = '<?xml';
  359. var s : string;
  360. len : integer;
  361. begin
  362. Len := length(XmlStart);
  363. setlength(s,len);
  364. if (AStream.Read (s[1],len) = len)
  365. and (s=XmlStart) then
  366. Result := True
  367. else
  368. Result := False;
  369. end;
  370. procedure TXMLDatapacketReader.GotoNextRecord;
  371. begin
  372. FRecordNode := FRecordNode.NextSibling;
  373. inc(FEntryNr);
  374. while assigned(FRecordNode) and (FRecordNode.CompareName('ROW')<>0) do
  375. FRecordNode := FRecordNode.NextSibling;
  376. end;
  377. initialization
  378. RegisterDatapacketReader(TXMLDatapacketReader,dfXML);
  379. end.