xmlread.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. {
  2. This file is part of the Free Component Library
  3. XML reading routines.
  4. Copyright (c) 1999-2000 by Sebastian Guenther, [email protected]
  5. Modified in 2006 by Sergei Gorelkin, [email protected]
  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 XMLRead;
  13. {$ifdef fpc}
  14. {$MODE objfpc}{$H+}
  15. {$endif}
  16. interface
  17. uses
  18. SysUtils, Classes, DOM, xmlutils, xmlreader, xmltextreader;
  19. type
  20. TErrorSeverity = xmlreader.TErrorSeverity;
  21. EXMLReadError = xmlreader.EXMLReadError;
  22. TXMLInputSource = xmlreader.TXMLInputSource;
  23. const
  24. esWarning = xmlreader.esWarning;
  25. esError = xmlreader.esError;
  26. esFatal = xmlreader.esFatal;
  27. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  28. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text); overload;
  29. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream); overload;
  30. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String); overload;
  31. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String); overload;
  32. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text); overload;
  33. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream); overload;
  34. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream; const ABaseURI: String); overload;
  35. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String); overload;
  36. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text); overload;
  37. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream); overload;
  38. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String); overload;
  39. type
  40. TXMLErrorEvent = xmlreader.TXMLErrorEvent;
  41. TDOMParseOptions = xmlreader.TXMLReaderSettings;
  42. // NOTE: DOM 3 LS ACTION_TYPE enumeration starts at 1
  43. TXMLContextAction = (
  44. xaAppendAsChildren = 1,
  45. xaReplaceChildren,
  46. xaInsertBefore,
  47. xaInsertAfter,
  48. xaReplace);
  49. TDOMParser = class(TObject)
  50. private
  51. FOptions: TDOMParseOptions;
  52. function GetOnError: TXMLErrorEvent;
  53. procedure SetOnError(value: TXMLErrorEvent);
  54. public
  55. constructor Create;
  56. destructor Destroy; override;
  57. procedure Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
  58. procedure ParseUri(const URI: XMLString; out ADoc: TXMLDocument);
  59. function ParseWithContext(Src: TXMLInputSource; Context: TDOMNode;
  60. Action: TXMLContextAction): TDOMNode;
  61. property Options: TDOMParseOptions read FOptions;
  62. property OnError: TXMLErrorEvent read GetOnError write SetOnError;
  63. end;
  64. TDecoder = xmltextreader.TDecoder;
  65. TGetDecoderProc = xmltextreader.TGetDecoderProc;
  66. procedure RegisterDecoder(Proc: TGetDecoderProc);
  67. // =======================================================
  68. implementation
  69. uses
  70. UriParser, dtdmodel;
  71. type
  72. TLoader = object
  73. doc: TDOMDocument;
  74. reader: TXMLTextReader;
  75. function CreateCDATANode(currnode: PNodeData): TDOMNode;
  76. function CreatePINode(currnode: PNodeData): TDOMNode;
  77. procedure ParseContent(cursor: TDOMNode_WithChildren);
  78. procedure ProcessXML(ADoc: TDOMDocument; AReader: TXMLTextReader);
  79. procedure ProcessFragment(AOwner: TDOMNode; AReader: TXMLTextReader);
  80. procedure ProcessDTD(ADoc: TDOMDocument; AReader: TXMLTextReader);
  81. procedure ProcessEntity(Sender: TXMLTextReader; AEntity: TEntityDecl);
  82. end;
  83. procedure RegisterDecoder(Proc: TGetDecoderProc);
  84. begin
  85. xmltextreader.RegisterDecoder(Proc);
  86. end;
  87. { TDOMParser }
  88. constructor TDOMParser.Create;
  89. begin
  90. FOptions := TDOMParseOptions.Create;
  91. end;
  92. destructor TDOMParser.Destroy;
  93. begin
  94. FOptions.Free;
  95. inherited Destroy;
  96. end;
  97. function TDOMParser.GetOnError: TXMLErrorEvent;
  98. begin
  99. result := Options.OnError;
  100. end;
  101. procedure TDOMParser.SetOnError(value: TXMLErrorEvent);
  102. begin
  103. Options.OnError := value;
  104. end;
  105. procedure TDOMParser.Parse(Src: TXMLInputSource; out ADoc: TXMLDocument);
  106. var
  107. Reader: TXMLTextReader;
  108. ldr: TLoader;
  109. begin
  110. ADoc := TXMLDocument.Create;
  111. Options.NameTable := ADoc.Names;
  112. Reader := TXMLTextReader.Create(Src, Options);
  113. try
  114. ldr.ProcessXML(ADoc, Reader);
  115. finally
  116. Reader.Free;
  117. end;
  118. end;
  119. procedure TDOMParser.ParseUri(const URI: XMLString; out ADoc: TXMLDocument);
  120. var
  121. Reader: TXMLTextReader;
  122. ldr: TLoader;
  123. begin
  124. ADoc := TXMLDocument.Create;
  125. Options.NameTable := ADoc.Names;
  126. Reader := TXMLTextReader.Create(URI, Options);
  127. try
  128. ldr.ProcessXML(ADoc, Reader)
  129. finally
  130. Reader.Free;
  131. end;
  132. end;
  133. function TDOMParser.ParseWithContext(Src: TXMLInputSource;
  134. Context: TDOMNode; Action: TXMLContextAction): TDOMNode;
  135. var
  136. Frag: TDOMDocumentFragment;
  137. node: TDOMNode;
  138. reader: TXMLTextReader;
  139. ldr: TLoader;
  140. doc: TDOMDocument;
  141. begin
  142. if Action in [xaInsertBefore, xaInsertAfter, xaReplace] then
  143. node := Context.ParentNode
  144. else
  145. node := Context;
  146. // TODO: replacing document isn't yet supported
  147. if (Action = xaReplaceChildren) and (node.NodeType = DOCUMENT_NODE) then
  148. raise EDOMNotSupported.Create('DOMParser.ParseWithContext');
  149. if not (node.NodeType in [ELEMENT_NODE, DOCUMENT_FRAGMENT_NODE]) then
  150. raise EDOMHierarchyRequest.Create('DOMParser.ParseWithContext');
  151. if Context.NodeType = DOCUMENT_NODE then
  152. doc := TDOMDocument(Context)
  153. else
  154. doc := Context.OwnerDocument;
  155. Options.NameTable := doc.Names;
  156. reader := TXMLTextReader.Create(Src, Options);
  157. try
  158. Frag := doc.CreateDocumentFragment;
  159. try
  160. ldr.ProcessFragment(Frag, reader);
  161. Result := Frag.FirstChild;
  162. case Action of
  163. xaAppendAsChildren: Context.AppendChild(Frag);
  164. xaReplaceChildren: begin
  165. Context.TextContent := ''; // removes children
  166. Context.ReplaceChild(Frag, Context.FirstChild);
  167. end;
  168. xaInsertBefore: node.InsertBefore(Frag, Context);
  169. xaInsertAfter: node.InsertBefore(Frag, Context.NextSibling);
  170. xaReplace: node.ReplaceChild(Frag, Context);
  171. end;
  172. finally
  173. Frag.Free;
  174. end;
  175. finally
  176. reader.Free;
  177. end;
  178. end;
  179. procedure TLoader.ProcessXML(ADoc: TDOMDocument; AReader: TXMLTextReader);
  180. begin
  181. doc := ADoc;
  182. reader := AReader;
  183. reader.OnEntity := @ProcessEntity;
  184. doc.documentURI := reader.BaseURI;
  185. reader.FragmentMode := False;
  186. ParseContent(doc);
  187. doc.XMLStandalone := reader.Standalone;
  188. if reader.Validate then
  189. reader.ValidateIdRefs;
  190. doc.IDs := reader.IDMap;
  191. reader.IDMap := nil;
  192. end;
  193. procedure TLoader.ProcessFragment(AOwner: TDOMNode; AReader: TXMLTextReader);
  194. var
  195. DoctypeNode: TDOMDocumentType;
  196. begin
  197. doc := AOwner.OwnerDocument;
  198. reader := AReader;
  199. reader.OnEntity := @ProcessEntity;
  200. reader.FragmentMode := True;
  201. reader.XML11 := doc.XMLVersion = '1.1';
  202. DoctypeNode := doc.DocType;
  203. if Assigned(DoctypeNode) then
  204. reader.DtdSchemaInfo := DocTypeNode.Model.Reference;
  205. ParseContent(aOwner as TDOMNode_WithChildren);
  206. end;
  207. procedure TLoader.ProcessEntity(Sender: TXMLTextReader; AEntity: TEntityDecl);
  208. var
  209. DoctypeNode: TDOMDocumentType;
  210. Ent: TDOMEntity;
  211. src: TXMLCharSource;
  212. InnerReader: TXMLTextReader;
  213. InnerLoader: TLoader;
  214. begin
  215. DoctypeNode := TDOMDocument(doc).DocType;
  216. if DoctypeNode = nil then
  217. Exit;
  218. Ent := TDOMEntity(DocTypeNode.Entities.GetNamedItem(AEntity.FName));
  219. if Ent = nil then
  220. Exit;
  221. Sender.EntityToSource(AEntity, Src);
  222. if Src = nil then
  223. Exit;
  224. InnerReader := TXMLTextReader.Create(Src, Sender);
  225. try
  226. Ent.SetReadOnly(False);
  227. InnerLoader.ProcessFragment(Ent, InnerReader);
  228. AEntity.FResolved := True;
  229. finally
  230. InnerReader.Free;
  231. AEntity.FOnStack := False;
  232. Ent.SetReadOnly(True);
  233. end;
  234. end;
  235. procedure TLoader.ParseContent(cursor: TDOMNode_WithChildren);
  236. var
  237. element: TDOMElement;
  238. currnodeptr: PPNodeData;
  239. currnode: PNodeData;
  240. begin
  241. currnodeptr := (reader as IGetNodeDataPtr).CurrentNodePtr;
  242. if reader.ReadState = rsInitial then
  243. begin
  244. if not reader.Read then
  245. Exit;
  246. case cursor.NodeType of
  247. DOCUMENT_NODE, ENTITY_NODE:
  248. (cursor as TDOMNode_TopLevel).SetHeaderData(reader.XMLVersion,reader.XMLEncoding);
  249. end;
  250. end;
  251. with reader do
  252. repeat
  253. if Validate then
  254. ValidateCurrentNode;
  255. currnode := currnodeptr^;
  256. case currnode^.FNodeType of
  257. ntText:
  258. cursor.InternalAppend(doc.CreateTextNodeBuf(currnode^.FValueStart, currnode^.FValueLength, False));
  259. ntWhitespace, ntSignificantWhitespace:
  260. if PreserveWhitespace then
  261. cursor.InternalAppend(doc.CreateTextNodeBuf(currnode^.FValueStart, currnode^.FValueLength, currnode^.FNodeType = ntWhitespace));
  262. ntCDATA:
  263. cursor.InternalAppend(CreateCDATANode(currnode));
  264. ntProcessingInstruction:
  265. cursor.InternalAppend(CreatePINode(currnode));
  266. ntComment:
  267. if not IgnoreComments then
  268. cursor.InternalAppend(doc.CreateCommentBuf(currnode^.FValueStart, currnode^.FValueLength));
  269. ntElement:
  270. begin
  271. element := LoadElement(doc, currnode, reader.AttributeCount);
  272. cursor.InternalAppend(element);
  273. cursor := element;
  274. end;
  275. ntEndElement:
  276. cursor := TDOMNode_WithChildren(cursor.ParentNode);
  277. ntDocumentType:
  278. cursor.InternalAppend(TDOMDocumentType.Create(doc, DtdSchemaInfo));
  279. ntEntityReference:
  280. begin
  281. cursor.InternalAppend(doc.CreateEntityReference(currnode^.FQName^.Key));
  282. { Seeing an entity reference while expanding means that the entity
  283. fails to expand. }
  284. if not ExpandEntities then
  285. begin
  286. { Make reader iterate through contents of the reference,
  287. to ensure correct validation events and character counts. }
  288. ResolveEntity;
  289. while currnodeptr^^.FNodeType <> ntEndEntity do
  290. Read;
  291. end;
  292. end;
  293. end;
  294. until not Read;
  295. end;
  296. function TLoader.CreatePINode(currnode: PNodeData): TDOMNode;
  297. var
  298. s: DOMString;
  299. begin
  300. SetString(s, currnode^.FValueStart, currnode^.FValueLength);
  301. result := Doc.CreateProcessingInstruction(currnode^.FQName^.Key, s);
  302. end;
  303. function TLoader.CreateCDATANode(currnode: PNodeData): TDOMNode;
  304. var
  305. s: XMLString;
  306. begin
  307. SetString(s, currnode^.FValueStart, currnode^.FValueLength);
  308. result := doc.CreateCDATASection(s);
  309. end;
  310. procedure TLoader.ProcessDTD(ADoc: TDOMDocument; AReader: TXMLTextReader);
  311. begin
  312. AReader.DtdSchemaInfo := TDTDModel.Create(AReader.NameTable);
  313. // TODO: DTD labeled version 1.1 will be rejected - must set FXML11 flag
  314. doc.AppendChild(TDOMDocumentType.Create(doc, AReader.DtdSchemaInfo));
  315. AReader.ParseDTD;
  316. end;
  317. { plain calls }
  318. procedure ReadXMLFile(out ADoc: TXMLDocument; var f: Text);
  319. var
  320. Reader: TXMLTextReader;
  321. ldr: TLoader;
  322. begin
  323. ADoc := TXMLDocument.Create;
  324. Reader := TXMLTextReader.Create(f, ADoc.Names);
  325. try
  326. ldr.ProcessXML(ADoc,Reader);
  327. finally
  328. Reader.Free;
  329. end;
  330. end;
  331. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String);
  332. var
  333. Reader: TXMLTextReader;
  334. ldr: TLoader;
  335. begin
  336. ADoc := TXMLDocument.Create;
  337. Reader := TXMLTextReader.Create(f, ABaseURI, ADoc.Names);
  338. try
  339. ldr.ProcessXML(ADoc, Reader);
  340. finally
  341. Reader.Free;
  342. end;
  343. end;
  344. procedure ReadXMLFile(out ADoc: TXMLDocument; f: TStream);
  345. begin
  346. ReadXMLFile(ADoc, f, 'stream:');
  347. end;
  348. procedure ReadXMLFile(out ADoc: TXMLDocument; const AFilename: String);
  349. var
  350. FileStream: TStream;
  351. begin
  352. ADoc := nil;
  353. FileStream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  354. try
  355. ReadXMLFile(ADoc, FileStream, FilenameToURI(AFilename));
  356. finally
  357. FileStream.Free;
  358. end;
  359. end;
  360. procedure ReadXMLFragment(AParentNode: TDOMNode; var f: Text);
  361. var
  362. Reader: TXMLTextReader;
  363. ldr: TLoader;
  364. begin
  365. Reader := TXMLTextReader.Create(f, AParentNode.OwnerDocument.Names);
  366. try
  367. ldr.ProcessFragment(AParentNode, Reader);
  368. finally
  369. Reader.Free;
  370. end;
  371. end;
  372. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream; const ABaseURI: String);
  373. var
  374. Reader: TXMLTextReader;
  375. ldr: TLoader;
  376. begin
  377. Reader := TXMLTextReader.Create(f, ABaseURI, AParentNode.OwnerDocument.Names);
  378. try
  379. ldr.ProcessFragment(AParentNode, Reader);
  380. finally
  381. Reader.Free;
  382. end;
  383. end;
  384. procedure ReadXMLFragment(AParentNode: TDOMNode; f: TStream);
  385. begin
  386. ReadXMLFragment(AParentNode, f, 'stream:');
  387. end;
  388. procedure ReadXMLFragment(AParentNode: TDOMNode; const AFilename: String);
  389. var
  390. Stream: TStream;
  391. begin
  392. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  393. try
  394. ReadXMLFragment(AParentNode, Stream, FilenameToURI(AFilename));
  395. finally
  396. Stream.Free;
  397. end;
  398. end;
  399. procedure ReadDTDFile(out ADoc: TXMLDocument; var f: Text);
  400. var
  401. Reader: TXMLTextReader;
  402. ldr: TLoader;
  403. begin
  404. ADoc := TXMLDocument.Create;
  405. Reader := TXMLTextReader.Create(f, ADoc.Names);
  406. try
  407. ldr.ProcessDTD(ADoc,Reader);
  408. finally
  409. Reader.Free;
  410. end;
  411. end;
  412. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream; const ABaseURI: String);
  413. var
  414. Reader: TXMLTextReader;
  415. ldr: TLoader;
  416. begin
  417. ADoc := TXMLDocument.Create;
  418. Reader := TXMLTextReader.Create(f, ABaseURI, ADoc.Names);
  419. try
  420. ldr.ProcessDTD(ADoc,Reader);
  421. finally
  422. Reader.Free;
  423. end;
  424. end;
  425. procedure ReadDTDFile(out ADoc: TXMLDocument; f: TStream);
  426. begin
  427. ReadDTDFile(ADoc, f, 'stream:');
  428. end;
  429. procedure ReadDTDFile(out ADoc: TXMLDocument; const AFilename: String);
  430. var
  431. Stream: TStream;
  432. begin
  433. ADoc := nil;
  434. Stream := TFileStream.Create(AFilename, fmOpenRead+fmShareDenyWrite);
  435. try
  436. ReadDTDFile(ADoc, Stream, FilenameToURI(AFilename));
  437. finally
  438. Stream.Free;
  439. end;
  440. end;
  441. end.