xmlwrite.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. {
  2. This file is part of the Free Component Library
  3. XML writing 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 XMLWrite;
  13. {$ifdef fpc}
  14. {$MODE objfpc}
  15. {$INLINE ON}
  16. {$H+}
  17. {$endif}
  18. interface
  19. uses Classes, DOM;
  20. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String); overload;
  21. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text); overload;
  22. procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream); overload;
  23. procedure WriteXML(Element: TDOMNode; const AFileName: String); overload;
  24. procedure WriteXML(Element: TDOMNode; var AFile: Text); overload;
  25. procedure WriteXML(Element: TDOMNode; AStream: TStream); overload;
  26. // ===================================================================
  27. implementation
  28. uses SysUtils;
  29. // -------------------------------------------------------------------
  30. // Text file and TStream support
  31. // -------------------------------------------------------------------
  32. type
  33. TOutputProc = procedure(const Buffer; Count: Longint) of object;
  34. TCharacters = set of Char;
  35. TSpecialCharCallback = procedure(c: Char) of object;
  36. TXMLWriter = class(TObject) // (TAbstractDOMVisitor)?
  37. private
  38. FInsideTextNode: Boolean;
  39. FIndent: string;
  40. FIndentCount: Integer;
  41. procedure IncIndent; {$IFDEF FPC} inline; {$ENDIF}
  42. procedure DecIndent; {$IFDEF FPC} inline; {$ENDIF}
  43. procedure wrtStr(const s: string);
  44. procedure wrtChr(c: char);
  45. procedure wrtLineEnd; {$IFDEF FPC} inline; {$ENDIF}
  46. procedure wrtIndent;
  47. procedure ConvWrite(const s: String; const SpecialChars: TCharacters;
  48. const SpecialCharCallback: TSpecialCharCallback);
  49. procedure AttrSpecialCharCallback(c: Char);
  50. procedure TextNodeSpecialCharCallback(c: Char);
  51. protected
  52. Procedure Write(Const Buffer; Count : Longint); virtual;Abstract;
  53. Procedure Writeln(Const Buffer; Count : Longint); virtual;
  54. procedure WriteNode(Node: TDOMNode);
  55. procedure VisitDocument(Node: TDOMNode); // override;
  56. procedure VisitElement(Node: TDOMNode);
  57. procedure VisitText(Node: TDOMNode);
  58. procedure VisitCDATA(Node: TDOMNode);
  59. procedure VisitComment(Node: TDOMNode);
  60. procedure VisitFragment(Node: TDOMNode);
  61. procedure VisitAttribute(Node: TDOMNode);
  62. procedure VisitEntity(Node: TDOMNode);
  63. procedure VisitEntityRef(Node: TDOMNode);
  64. procedure VisitDocumentType(Node: TDOMNode);
  65. procedure VisitPI(Node: TDOMNode);
  66. procedure VisitNotation(Node: TDOMNode);
  67. end;
  68. TTextXMLWriter = Class(TXMLWriter)
  69. Private
  70. F : ^Text;
  71. Protected
  72. Procedure Write(Const Buffer; Count : Longint);override;
  73. Public
  74. procedure WriteXML(Root: TDomNode; var AFile: Text); overload;
  75. end;
  76. TStreamXMLWriter = Class(TXMLWriter)
  77. Private
  78. F : TStream;
  79. Protected
  80. Procedure Write(Const Buffer; Count : Longint);override;
  81. Public
  82. procedure WriteXML(Root: TDomNode; AStream : TStream); overload;
  83. end;
  84. { ---------------------------------------------------------------------
  85. TTextXMLWriter
  86. ---------------------------------------------------------------------}
  87. procedure TTextXMLWriter.Write(const Buffer; Count: Longint);
  88. var
  89. s: string;
  90. begin
  91. if Count>0 then
  92. begin
  93. SetString(s, PChar(Buffer), Count);
  94. system.Write(f^, s);
  95. end;
  96. end;
  97. { ---------------------------------------------------------------------
  98. TStreamXMLWriter
  99. ---------------------------------------------------------------------}
  100. procedure TStreamXMLWriter.Write(const Buffer; Count: Longint);
  101. begin
  102. if Count > 0 then
  103. F.Write(Buffer, Count);
  104. end;
  105. { ---------------------------------------------------------------------
  106. TXMLWriter
  107. ---------------------------------------------------------------------}
  108. Procedure TXMLWriter.Writeln(Const Buffer; Count : Longint);
  109. begin
  110. Write(buffer,count);
  111. Wrtstr(slinebreak);
  112. end;
  113. procedure TXMLWriter.wrtStr(const s: string);
  114. begin
  115. if s<>'' then
  116. write(s[1],length(s));
  117. end;
  118. procedure TXMLWriter.wrtChr(c: char);
  119. begin
  120. write(c,1);
  121. end;
  122. procedure TXMLWriter.wrtLineEnd;
  123. begin
  124. wrtstr(slinebreak);
  125. end;
  126. procedure TXMLWriter.wrtIndent;
  127. var
  128. I: Integer;
  129. begin
  130. for I:=1 to FIndentCount do
  131. wrtStr(FIndent);
  132. end;
  133. procedure TXMLWriter.IncIndent;
  134. begin
  135. Inc(FIndentCount);
  136. end;
  137. procedure TXMLWriter.DecIndent;
  138. begin
  139. if FIndentCount>0 then dec(FIndentCount);
  140. end;
  141. const
  142. AttrSpecialChars = ['<', '>', '"', '&'];
  143. TextSpecialChars = ['<', '>', '&'];
  144. procedure TXMLWriter.ConvWrite(const s: String; const SpecialChars: TCharacters;
  145. const SpecialCharCallback: TSpecialCharCallback);
  146. var
  147. StartPos, EndPos: Integer;
  148. begin
  149. StartPos := 1;
  150. EndPos := 1;
  151. while EndPos <= Length(s) do
  152. begin
  153. if s[EndPos] in SpecialChars then
  154. begin
  155. write(s[StartPos],EndPos - StartPos);
  156. SpecialCharCallback(s[EndPos]);
  157. StartPos := EndPos + 1;
  158. end;
  159. Inc(EndPos);
  160. end;
  161. if StartPos <= length(s) then
  162. write(s[StartPos], EndPos - StartPos);
  163. end;
  164. procedure TXMLWriter.AttrSpecialCharCallback(c: Char);
  165. const
  166. QuotStr = '&quot;';
  167. AmpStr = '&amp;';
  168. ltStr = '&lt;';
  169. begin
  170. if c = '"' then
  171. wrtStr(QuotStr)
  172. else if c = '&' then
  173. wrtStr(AmpStr)
  174. else if c = '<' then
  175. wrtStr(ltStr)
  176. else
  177. write(c,1);
  178. end;
  179. procedure TXMLWriter.TextnodeSpecialCharCallback(c: Char);
  180. const
  181. ltStr = '&lt;';
  182. gtStr = '&gt;';
  183. AmpStr = '&amp;';
  184. begin
  185. if c = '<' then
  186. wrtStr(ltStr)
  187. else if c = '>' then
  188. wrtStr(gtStr)
  189. else if c = '&' then
  190. wrtStr(AmpStr)
  191. else
  192. write(c,1);
  193. end;
  194. procedure TXMLWriter.WriteNode(node: TDOMNode);
  195. begin
  196. // Must be: node.Accept(Self);
  197. case node.NodeType of
  198. ELEMENT_NODE: VisitElement(node);
  199. ATTRIBUTE_NODE: VisitAttribute(node);
  200. TEXT_NODE: VisitText(node);
  201. CDATA_SECTION_NODE: VisitCDATA(node);
  202. ENTITY_REFERENCE_NODE: VisitEntityRef(node);
  203. ENTITY_NODE: VisitEntity(node);
  204. PROCESSING_INSTRUCTION_NODE: VisitPI(node);
  205. COMMENT_NODE: VisitComment(node);
  206. DOCUMENT_NODE: VisitDocument(node);
  207. DOCUMENT_TYPE_NODE: VisitDocumentType(node);
  208. DOCUMENT_FRAGMENT_NODE: VisitFragment(node);
  209. NOTATION_NODE: VisitNotation(node);
  210. end;
  211. end;
  212. procedure TXMLWriter.VisitElement(node: TDOMNode);
  213. var
  214. i: Integer;
  215. attr, child: TDOMNode;
  216. SavedInsideTextNode: Boolean;
  217. s: DOMString;
  218. begin
  219. if not FInsideTextNode then
  220. wrtIndent;
  221. wrtChr('<');
  222. wrtStr(UTF8Encode(node.NodeName));
  223. for i := 0 to node.Attributes.Length - 1 do
  224. begin
  225. attr := node.Attributes.Item[i];
  226. wrtChr(' ');
  227. wrtStr(UTF8Encode(attr.NodeName));
  228. wrtChr('=');
  229. s := attr.NodeValue;
  230. // !!!: Replace special characters in "s" such as '&', '<', '>'
  231. wrtChr('"');
  232. ConvWrite(UTF8Encode(s), AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback);
  233. wrtChr('"');
  234. end;
  235. Child := node.FirstChild;
  236. if Child = nil then begin
  237. wrtChr('/');
  238. wrtChr('>');
  239. if not FInsideTextNode then wrtLineEnd;
  240. end else
  241. begin
  242. SavedInsideTextNode := FInsideTextNode;
  243. wrtChr('>');
  244. if not (FInsideTextNode or Child.InheritsFrom(TDOMText)) then
  245. wrtLineEnd;
  246. IncIndent;
  247. repeat
  248. if Child.InheritsFrom(TDOMText) then
  249. FInsideTextNode := True
  250. else // <-- fix case when CDATA is first child
  251. FInsideTextNode := False;
  252. WriteNode(Child);
  253. Child := Child.NextSibling;
  254. until child = nil;
  255. DecIndent;
  256. if not FInsideTextNode then
  257. wrtIndent;
  258. FInsideTextNode := SavedInsideTextNode;
  259. wrtChr('<');
  260. wrtChr('/');
  261. wrtStr(UTF8Encode(node.NodeName));
  262. wrtChr('>');
  263. if not FInsideTextNode then
  264. wrtLineEnd;
  265. end;
  266. end;
  267. procedure TXMLWriter.VisitText(node: TDOMNode);
  268. begin
  269. ConvWrite(UTF8Encode(node.NodeValue), TextSpecialChars, {$IFDEF FPC}@{$ENDIF}TextnodeSpecialCharCallback);
  270. end;
  271. procedure TXMLWriter.VisitCDATA(node: TDOMNode);
  272. begin
  273. if not FInsideTextNode then
  274. wrtStr('<![CDATA[' + UTF8Encode(node.NodeValue) + ']]>')
  275. else begin
  276. wrtIndent;
  277. wrtStr('<![CDATA[' + UTF8Encode(node.NodeValue) + ']]>');
  278. wrtLineEnd;
  279. end;
  280. end;
  281. procedure TXMLWriter.VisitEntityRef(node: TDOMNode);
  282. begin
  283. wrtChr('&');
  284. wrtStr(UTF8Encode(node.NodeName));
  285. wrtChr(';');
  286. end;
  287. procedure TXMLWriter.VisitEntity(node: TDOMNode);
  288. begin
  289. end;
  290. procedure TXMLWriter.VisitPI(node: TDOMNode);
  291. begin
  292. if not FInsideTextNode then wrtIndent;
  293. wrtChr('<'); wrtChr('?');
  294. wrtStr(UTF8Encode(TDOMProcessingInstruction(node).Target));
  295. wrtChr(' ');
  296. wrtStr(UTF8Encode(TDOMProcessingInstruction(node).Data));
  297. wrtChr('?'); wrtChr('>');
  298. if not FInsideTextNode then wrtLineEnd;
  299. end;
  300. procedure TXMLWriter.VisitComment(node: TDOMNode);
  301. begin
  302. if not FInsideTextNode then wrtIndent;
  303. wrtStr('<!--');
  304. wrtStr(UTF8Encode(node.NodeValue));
  305. wrtStr('-->');
  306. if not FInsideTextNode then wrtLineEnd;
  307. end;
  308. procedure TXMLWriter.VisitDocument(node: TDOMNode);
  309. var
  310. child: TDOMNode;
  311. begin
  312. wrtStr('<?xml version="');
  313. if Length(TXMLDocument(node).XMLVersion) > 0 then
  314. ConvWrite(TXMLDocument(node).XMLVersion, AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback)
  315. else
  316. wrtStr('1.0');
  317. wrtChr('"');
  318. if Length(TXMLDocument(node).Encoding) > 0 then
  319. begin
  320. wrtStr(' encoding="');
  321. ConvWrite(TXMLDocument(node).Encoding, AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback);
  322. wrtStr('"');
  323. end;
  324. wrtStr('?>');
  325. wrtLineEnd;
  326. if Length(TXMLDocument(node).StylesheetType) > 0 then
  327. begin
  328. wrtStr('<?xml-stylesheet type="');
  329. ConvWrite(TXMLDocument(node).StylesheetType, AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback);
  330. wrtStr('" href="');
  331. ConvWrite(TXMLDocument(node).StylesheetHRef, AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback);
  332. wrtStr('"?>');
  333. wrtLineEnd;
  334. end;
  335. FIndent := ' ';
  336. FIndentCount := 0;
  337. child := node.FirstChild;
  338. while Assigned(Child) do
  339. begin
  340. WriteNode(Child);
  341. Child := Child.NextSibling;
  342. end;
  343. if node=nil then ;
  344. end;
  345. procedure TXMLWriter.VisitAttribute(Node: TDOMNode);
  346. begin
  347. end;
  348. procedure TXMLWriter.VisitDocumentType(Node: TDOMNode);
  349. begin
  350. end;
  351. procedure TXMLWriter.VisitFragment(Node: TDOMNode);
  352. begin
  353. VisitElement(Node);
  354. end;
  355. procedure TXMLWriter.VisitNotation(Node: TDOMNode);
  356. begin
  357. end;
  358. procedure TStreamXMLWriter.WriteXML(Root: TDOMNode; AStream: TStream);
  359. begin
  360. F:=AStream;
  361. WriteNode(Root);
  362. end;
  363. procedure TTextXMLWriter.WriteXML(Root: TDOMNode; var AFile: Text);
  364. begin
  365. f := @AFile;
  366. WriteNode(Root);
  367. end;
  368. // -------------------------------------------------------------------
  369. // Interface implementation
  370. // -------------------------------------------------------------------
  371. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  372. var
  373. fs: TFileStream;
  374. begin
  375. fs := TFileStream.Create(AFileName, fmCreate);
  376. try
  377. WriteXMLFile(doc, fs);
  378. finally
  379. fs.Free;
  380. end;
  381. end;
  382. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  383. begin
  384. with TTextXMLWriter.Create do
  385. try
  386. WriteXML(doc, AFile);
  387. finally
  388. Free;
  389. end;
  390. end;
  391. procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream);
  392. begin
  393. with TStreamXMLWriter.Create do
  394. try
  395. WriteXML(doc, AStream);
  396. finally
  397. Free;
  398. end;
  399. end;
  400. procedure WriteXML(Element: TDOMNode; const AFileName: String);
  401. begin
  402. WriteXML(TXMLDocument(Element), AFileName);
  403. end;
  404. procedure WriteXML(Element: TDOMNode; var AFile: Text);
  405. begin
  406. WriteXML(TXMLDocument(Element), AFile);
  407. end;
  408. procedure WriteXML(Element: TDOMNode; AStream: TStream);
  409. begin
  410. WriteXML(TXMLDocument(Element), AStream);
  411. end;
  412. end.