xmldatapacketreader.pp 13 KB

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