xmldatapacketreader.pp 12 KB

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