xmldatapacketreader.pp 13 KB

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