xmldatapacketreader.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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(AnAutoIncValue : integer); override;
  41. procedure StoreRecord(ARowState : TRowState; AUpdOrder : integer = 0); override;
  42. procedure FinalizeStoreRecords; override;
  43. procedure LoadFieldDefs(var AnAutoIncValue : integer); override;
  44. procedure InitLoadRecords; override;
  45. function GetCurrentRecord : boolean; override;
  46. function GetRecordRowState(out AUpdOrder : Integer) : TRowState; override;
  47. procedure RestoreRecord; 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. 'string:Guid', // ftGuid
  92. '',
  93. 'fixedFMT', // ftFmtBCD
  94. 'string.uni', // ftFixedWideChar
  95. 'bin.hex:WideText', // ftWideMemo
  96. '', // ftOraTimeStamp
  97. '', // ftOraInterval
  98. '', // ftLongWord
  99. '', // ftShortint
  100. '', // ftByte
  101. '' // ftExtended
  102. );
  103. resourcestring
  104. sUnknownXMLDatasetFormat = 'Unknown XML Dataset format';
  105. { TXMLDatapacketReader }
  106. destructor TXMLDatapacketReader.destroy;
  107. begin
  108. FieldsNode.Free;
  109. MetaDataNode.Free;
  110. DataPacketNode.Free;
  111. XMLDocument.Free;
  112. inherited destroy;
  113. end;
  114. procedure TXMLDatapacketReader.LoadFieldDefs(var AnAutoIncValue: integer);
  115. function GetNodeAttribute(const aNode : TDOMNode; AttName : String) : string;
  116. var AnAttr : TDomNode;
  117. begin
  118. AnAttr := ANode.Attributes.GetNamedItem(AttName);
  119. if assigned(AnAttr) then result := AnAttr.NodeValue
  120. else result := '';
  121. end;
  122. var i,s : integer;
  123. AFieldDef : TFieldDef;
  124. iFieldType : TFieldType;
  125. FTString : string;
  126. SubFTString : string;
  127. AFieldNode : TDOMNode;
  128. AnAutoIncNode: TDomNode;
  129. begin
  130. ReadXMLFile(XMLDocument,Stream);
  131. DataPacketNode := XMLDocument.FindNode('DATAPACKET') as TDOMElement;
  132. if not assigned(DataPacketNode) then DatabaseError(sUnknownXMLDatasetFormat);
  133. MetaDataNode := DataPacketNode.FindNode('METADATA');
  134. if not assigned(MetaDataNode) then DatabaseError(sUnknownXMLDatasetFormat);
  135. FieldsNode := MetaDataNode.FindNode('FIELDS');
  136. if not assigned(FieldsNode) then DatabaseError(sUnknownXMLDatasetFormat);
  137. with FieldsNode.ChildNodes do for i := 0 to Count - 1 do
  138. begin
  139. AFieldNode := item[i];
  140. if AFieldNode.CompareName('FIELD')=0 then
  141. begin
  142. AFieldDef := Dataset.FieldDefs.AddFieldDef;
  143. AFieldDef.DisplayName:=GetNodeAttribute(AFieldNode,'fieldname');
  144. AFieldDef.Name:=GetNodeAttribute(AFieldNode,'attrname');
  145. // Difference in casing between CDS and bufdataset...
  146. S:=StrToIntDef(GetNodeAttribute(AFieldNode,'width'),-1);
  147. if (S=-1) then
  148. S:=StrToIntDef(GetNodeAttribute(AFieldNode,'WIDTH'),0);
  149. AFieldDef.Size:=s;
  150. FTString:=GetNodeAttribute(AFieldNode,'fieldtype');
  151. SubFTString:=GetNodeAttribute(AFieldNode,'subtype');
  152. if SubFTString<>'' then
  153. FTString:=FTString+':'+SubFTString;
  154. AFieldDef.DataType:=ftUnknown;
  155. for iFieldType:=low(TFieldType) to high(TFieldType) do
  156. if SameText(XMLFieldtypenames[iFieldType],FTString) then
  157. begin
  158. AFieldDef.DataType:=iFieldType;
  159. break;
  160. end;
  161. end;
  162. end;
  163. FParamsNode := MetaDataNode.FindNode('PARAMS');
  164. if assigned(FParamsNode) then
  165. begin
  166. FChangeLogNode := FParamsNode.Attributes.GetNamedItem('CHANGE_LOG');
  167. AnAutoIncNode := FParamsNode.Attributes.GetNamedItem('AUTOINCVALUE');
  168. if assigned(AnAutoIncNode) then
  169. AnAutoIncValue := StrToIntDef(AnAutoIncNode.NodeValue,-1);
  170. end;
  171. FRowDataNode := DataPacketNode.FindNode('ROWDATA');
  172. FRecordNode := nil;
  173. end;
  174. procedure TXMLDatapacketReader.StoreFieldDefs(AnAutoIncValue: integer);
  175. var i,p : integer;
  176. AFieldNode : TDOMElement;
  177. AStringFT : string;
  178. begin
  179. XMLDocument := TXMLDocument.Create;
  180. DataPacketNode := XMLDocument.CreateElement('DATAPACKET');
  181. DataPacketNode.SetAttribute('Version','2.0');
  182. MetaDataNode := XMLDocument.CreateElement('METADATA');
  183. FieldsNode := XMLDocument.CreateElement('FIELDS');
  184. for i := 0 to DataSet.FieldDefs.Count - 1 do with DataSet.FieldDefs[i] do
  185. begin
  186. AFieldNode := XMLDocument.CreateElement('FIELD');
  187. if Name <> '' then AFieldNode.SetAttribute('fieldname',Name);
  188. AFieldNode.SetAttribute('attrname',DisplayName);
  189. if size <> 0 then AFieldNode.SetAttribute('width',IntToStr(Size));
  190. AStringFT:=XMLFieldtypenames[DataType];
  191. p := pos(':',AStringFT);
  192. if p > 1 then
  193. begin
  194. AFieldNode.SetAttribute('fieldtype',copy(AStringFT,1,p-1));
  195. AFieldNode.SetAttribute('subtype',copy(AStringFT,p+1,25));
  196. end
  197. else
  198. AFieldNode.SetAttribute('fieldtype',AStringFT);
  199. if faReadonly in Attributes then AFieldNode.SetAttribute('readonly','true');
  200. FieldsNode.AppendChild(AFieldNode);
  201. end;
  202. MetaDataNode.AppendChild(FieldsNode);
  203. FParamsNode := XMLDocument.CreateElement('PARAMS');
  204. if AnAutoIncValue>-1 then
  205. (FParamsNode as TDomElement).SetAttribute('AUTOINCVALUE',IntToStr(AnAutoIncValue));
  206. MetaDataNode.AppendChild(FParamsNode);
  207. DataPacketNode.AppendChild(MetaDataNode);
  208. FRowDataNode := XMLDocument.CreateElement('ROWDATA');
  209. setlength(FChangeLog,0);
  210. FEntryNr:=0;
  211. FLastChange:=-1;
  212. end;
  213. procedure TXMLDatapacketReader.FinalizeStoreRecords;
  214. var ChangeLogStr : String;
  215. i : integer;
  216. begin
  217. ChangeLogStr:='';
  218. for i := 0 to length(FChangeLog)-1 do with FChangeLog[i] do
  219. begin
  220. ChangeLogStr:=ChangeLogStr+' '+inttostr(NewEntry)+' '+inttostr(OrigEntry)+' ';
  221. if UpdateKind=ukModify then ChangeLogStr := ChangeLogStr+'8';
  222. if UpdateKind=ukInsert then ChangeLogStr := ChangeLogStr+'4';
  223. if UpdateKind=ukDelete then ChangeLogStr := ChangeLogStr+'2';
  224. end;
  225. setlength(FChangeLog,0);
  226. if ChangeLogStr<>'' then
  227. (FParamsNode as TDomElement).SetAttribute('CHANGE_LOG',Trim(ChangeLogStr));
  228. DataPacketNode.AppendChild(FRowDataNode);
  229. XMLDocument.AppendChild(DataPacketNode);
  230. WriteXML(XMLDocument,Stream);
  231. end;
  232. function TXMLDatapacketReader.GetCurrentRecord: boolean;
  233. begin
  234. Result := assigned(FRecordNode);
  235. end;
  236. function TXMLDatapacketReader.GetRecordRowState(out AUpdOrder: Integer
  237. ): TRowState;
  238. var ARowStateNode : TDOmNode;
  239. ARowState : integer;
  240. i : integer;
  241. begin
  242. ARowStateNode := FRecordNode.Attributes.GetNamedItem('RowState');
  243. if ARowStateNode = nil then // This item is not edited
  244. Result := []
  245. else
  246. begin
  247. Result := ByteToRowState(StrToIntDef(ARowStateNode.NodeValue,0));
  248. if Result = [rsvOriginal] then
  249. begin
  250. for i := 0 to length(FChangeLog)-1 do
  251. if FChangeLog[i].NewEntry=FEntryNr then break;
  252. assert(FChangeLog[i].NewEntry=FEntryNr);
  253. end
  254. else
  255. begin
  256. for i := 0 to length(FChangeLog)-1 do
  257. if FChangeLog[i].OrigEntry=FEntryNr then break;
  258. assert(FChangeLog[i].OrigEntry=FEntryNr);
  259. end;
  260. AUpdOrder:=i;
  261. end;
  262. end;
  263. procedure TXMLDatapacketReader.InitLoadRecords;
  264. var ChangeLogStr : String;
  265. i,cp : integer;
  266. ps : string;
  267. begin
  268. FRecordNode := FRowDataNode.FirstChild;
  269. FEntryNr := 1;
  270. setlength(FChangeLog,0);
  271. if assigned(FChangeLogNode) then
  272. ChangeLogStr:=FChangeLogNode.NodeValue
  273. else
  274. ChangeLogStr:='';
  275. ps := '';
  276. cp := 0;
  277. if ChangeLogStr<>'' then for i := 1 to length(ChangeLogStr)+1 do
  278. begin
  279. if not (ChangeLogStr[i] in [' ',#0]) then
  280. ps := ps + ChangeLogStr[i]
  281. else
  282. begin
  283. case (cp mod 3) of
  284. 0 : begin
  285. SetLength(FChangeLog,length(FChangeLog)+1);
  286. FChangeLog[cp div 3].OrigEntry:=StrToIntDef(ps,0);
  287. end;
  288. 1 : FChangeLog[cp div 3].NewEntry:=StrToIntDef(ps,0);
  289. 2 : begin
  290. if ps = '2' then
  291. FChangeLog[cp div 3].UpdateKind:=ukDelete
  292. else if ps = '4' then
  293. FChangeLog[cp div 3].UpdateKind:=ukInsert
  294. else if ps = '8' then
  295. FChangeLog[cp div 3].UpdateKind:=ukModify;
  296. end;
  297. end; {case}
  298. ps := '';
  299. inc(cp);
  300. end;
  301. end;
  302. end;
  303. procedure TXMLDatapacketReader.RestoreRecord;
  304. var FieldNr : integer;
  305. AFieldNode : TDomNode;
  306. ABufBlobField: TBufBlobField;
  307. AField: TField;
  308. s: string;
  309. ws: widestring;
  310. begin
  311. with DataSet do for FieldNr:=0 to FieldDefs.Count-1 do
  312. begin
  313. AField := Fields.FieldByNumber(FieldDefs[FieldNr].FieldNo);
  314. AFieldNode := FRecordNode.Attributes.GetNamedItem(FieldDefs[FieldNr].Name);
  315. if assigned(AFieldNode) then
  316. begin
  317. s := AFieldNode.NodeValue;
  318. if (FieldDefs[FieldNr].DataType in [ftBlob, ftBytes, ftVarBytes]) and (s <> '') then
  319. s := DecodeStringBase64(s);
  320. case FieldDefs[FieldNr].DataType of
  321. ftBlob, ftMemo:
  322. RestoreBlobField(AField, @s[1], length(s));
  323. ftWideMemo:
  324. begin
  325. ws := s;
  326. RestoreBlobField(AField, @ws[1], length(ws)*sizeof(WideChar));
  327. end
  328. else;
  329. AField.AsString := s; // set it to the filterbuffer
  330. end;
  331. end
  332. else
  333. AField.SetData(nil);
  334. end;
  335. end;
  336. procedure TXMLDatapacketReader.StoreRecord(ARowState : TRowState; AUpdOrder : integer = 0);
  337. var FieldNr : Integer;
  338. AFieldDef: TFieldDef;
  339. AField: TField;
  340. ARecordNode : TDOMElement;
  341. begin
  342. inc(FEntryNr);
  343. ARecordNode := XMLDocument.CreateElement('ROW');
  344. with DataSet do for FieldNr := 0 to FieldDefs.Count-1 do
  345. begin
  346. AFieldDef := FieldDefs[FieldNr];
  347. AField := Fields.FieldByNumber(AFieldDef.FieldNo);
  348. if not AField.IsNull then
  349. if AFieldDef.DataType in [ftBlob, ftBytes, ftVarBytes] then
  350. ARecordNode.SetAttribute(AFieldDef.Name, EncodeStringBase64(AField.AsString))
  351. else
  352. ARecordNode.SetAttribute(AFieldDef.Name, AField.AsString);
  353. end;
  354. if ARowState<>[] then
  355. begin
  356. ARecordNode.SetAttribute('RowState',inttostr(RowStateToByte(ARowState)));
  357. if AUpdOrder>=length(FChangeLog) then
  358. setlength(FChangeLog,AUpdOrder+1);
  359. if (rsvOriginal in ARowState) or (rsvDeleted in ARowState) then
  360. FChangeLog[AUpdOrder].OrigEntry:=FEntryNr;
  361. if (rsvDeleted in ARowState) or (rsvUpdated in ARowState) or (rsvInserted in ARowState) then
  362. FChangeLog[AUpdOrder].NewEntry:=FEntryNr;
  363. if ARowState=[rsvUpdated] then
  364. FChangeLog[AUpdOrder].UpdateKind := ukModify;
  365. if ARowState=[rsvInserted] then
  366. FChangeLog[AUpdOrder].UpdateKind := ukInsert;
  367. if ARowState=[rsvDeleted] then
  368. FChangeLog[AUpdOrder].UpdateKind := ukDelete;
  369. end;
  370. FRowDataNode.AppendChild(ARecordNode);
  371. end;
  372. class function TXMLDatapacketReader.RecognizeStream(AStream: TStream): boolean;
  373. const XmlStart = '<?xml';
  374. var s : string;
  375. len : integer;
  376. begin
  377. Len := length(XmlStart);
  378. setlength(s,len);
  379. if (AStream.Read (s[1],len) = len)
  380. and (s=XmlStart) then
  381. Result := True
  382. else
  383. Result := False;
  384. end;
  385. procedure TXMLDatapacketReader.GotoNextRecord;
  386. begin
  387. FRecordNode := FRecordNode.NextSibling;
  388. inc(FEntryNr);
  389. while assigned(FRecordNode) and (FRecordNode.CompareName('ROW')<>0) do
  390. FRecordNode := FRecordNode.NextSibling;
  391. end;
  392. initialization
  393. RegisterDatapacketReader(TXMLDatapacketReader,dfXML);
  394. end.