xmlwrite.pp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. {
  2. $Id$
  3. This file is part of the Free Component Library
  4. XML writing routines
  5. Copyright (c) 1999-2002 by Sebastian Guenther, [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. {$MODE objfpc}
  14. {$H+}
  15. interface
  16. uses Classes, DOM;
  17. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  18. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  19. procedure WriteXMLFile(doc: TXMLDocument; var AStream: TStream);
  20. procedure WriteXML(Node: TDOMNode; const AFileName: String);
  21. procedure WriteXML(Node: TDOMNode; var AFile: Text);
  22. procedure WriteXML(Node: TDOMNode; var AStream: TStream);
  23. // ===================================================================
  24. implementation
  25. uses SysUtils;
  26. // -------------------------------------------------------------------
  27. // Writers for the different node types
  28. // -------------------------------------------------------------------
  29. procedure WriteElement(node: TDOMNode); forward;
  30. procedure WriteAttribute(node: TDOMNode); forward;
  31. procedure WriteText(node: TDOMNode); forward;
  32. procedure WriteCDATA(node: TDOMNode); forward;
  33. procedure WriteEntityRef(node: TDOMNode); forward;
  34. procedure WriteEntity(node: TDOMNode); forward;
  35. procedure WritePI(node: TDOMNode); forward;
  36. procedure WriteComment(node: TDOMNode); forward;
  37. procedure WriteDocument(node: TDOMNode); forward;
  38. procedure WriteDocumentType(node: TDOMNode); forward;
  39. procedure WriteDocumentFragment(node: TDOMNode); forward;
  40. procedure WriteNotation(node: TDOMNode); forward;
  41. type
  42. TWriteNodeProc = procedure(node: TDOMNode);
  43. const
  44. WriteProcs: array[ELEMENT_NODE..NOTATION_NODE] of TWriteNodeProc =
  45. (@WriteElement, @WriteAttribute, @WriteText, @WriteCDATA, @WriteEntityRef,
  46. @WriteEntity, @WritePI, @WriteComment, @WriteDocument, @WriteDocumentType,
  47. @WriteDocumentFragment, @WriteNotation);
  48. procedure WriteNode(node: TDOMNode);
  49. begin
  50. WriteProcs[node.NodeType](node);
  51. end;
  52. // -------------------------------------------------------------------
  53. // Text file and TStream support
  54. // -------------------------------------------------------------------
  55. type
  56. TOutputProc = procedure(s: String);
  57. var
  58. f: ^Text;
  59. stream: TStream;
  60. wrt, wrtln: TOutputProc;
  61. InsideTextNode: Boolean;
  62. procedure Text_Write(s: String);
  63. begin
  64. Write(f^, s);
  65. end;
  66. procedure Text_WriteLn(s: String);
  67. begin
  68. WriteLn(f^, s);
  69. end;
  70. procedure Stream_Write(s: String);
  71. begin
  72. if Length(s) > 0 then
  73. stream.Write(s[1], Length(s));
  74. end;
  75. procedure Stream_WriteLn(s: String);
  76. begin
  77. if Length(s) > 0 then
  78. stream.Write(s[1], Length(s));
  79. stream.WriteByte(10);
  80. end;
  81. // -------------------------------------------------------------------
  82. // Indent handling
  83. // -------------------------------------------------------------------
  84. var
  85. Indent: String;
  86. procedure IncIndent;
  87. begin
  88. Indent := Indent + ' ';
  89. end;
  90. procedure DecIndent;
  91. begin
  92. if Length(Indent) >= 2 then
  93. SetLength(Indent, Length(Indent) - 2);
  94. end;
  95. // -------------------------------------------------------------------
  96. // String conversion
  97. // -------------------------------------------------------------------
  98. type
  99. TCharacters = set of Char;
  100. TSpecialCharCallback = procedure(c: Char);
  101. const
  102. AttrSpecialChars = ['"', '&'];
  103. TextSpecialChars = ['<', '>', '&'];
  104. procedure ConvWrite(const s: String; const SpecialChars: TCharacters;
  105. const SpecialCharCallback: TSpecialCharCallback);
  106. var
  107. StartPos, EndPos: Integer;
  108. begin
  109. StartPos := 1;
  110. EndPos := 1;
  111. while EndPos <= Length(s) do
  112. begin
  113. if s[EndPos] in SpecialChars then
  114. begin
  115. wrt(Copy(s, StartPos, EndPos - StartPos));
  116. SpecialCharCallback(s[EndPos]);
  117. StartPos := EndPos + 1;
  118. end;
  119. Inc(EndPos);
  120. end;
  121. if EndPos > StartPos then
  122. wrt(Copy(s, StartPos, EndPos - StartPos));
  123. end;
  124. procedure AttrSpecialCharCallback(c: Char);
  125. begin
  126. if c = '"' then
  127. wrt('&quot;')
  128. else if c = '&' then
  129. wrt('&amp;')
  130. else
  131. wrt(c);
  132. end;
  133. procedure TextnodeSpecialCharCallback(c: Char);
  134. begin
  135. if c = '<' then
  136. wrt('&lt;')
  137. else if c = '>' then
  138. wrt('&gt;')
  139. else if c = '&' then
  140. wrt('&amp;')
  141. else
  142. wrt(c);
  143. end;
  144. // -------------------------------------------------------------------
  145. // Node writers implementations
  146. // -------------------------------------------------------------------
  147. procedure WriteElement(node: TDOMNode);
  148. var
  149. i: Integer;
  150. attr, child: TDOMNode;
  151. SavedInsideTextNode: Boolean;
  152. s: String;
  153. begin
  154. if not InsideTextNode then
  155. wrt(Indent);
  156. wrt('<' + node.NodeName);
  157. for i := 0 to node.Attributes.Length - 1 do
  158. begin
  159. attr := node.Attributes.Item[i];
  160. wrt(' ' + attr.NodeName + '=');
  161. s := attr.NodeValue;
  162. wrt('"');
  163. ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback);
  164. wrt('"');
  165. end;
  166. Child := node.FirstChild;
  167. if Child = nil then
  168. if InsideTextNode then
  169. wrt('/>')
  170. else
  171. wrtln('/>')
  172. else
  173. begin
  174. SavedInsideTextNode := InsideTextNode;
  175. if InsideTextNode or Child.InheritsFrom(TDOMText) then
  176. wrt('>')
  177. else
  178. wrtln('>');
  179. IncIndent;
  180. repeat
  181. if Child.InheritsFrom(TDOMText) then
  182. InsideTextNode := True;
  183. WriteNode(Child);
  184. Child := Child.NextSibling;
  185. until child = nil;
  186. DecIndent;
  187. if not InsideTextNode then
  188. wrt(Indent);
  189. InsideTextNode := SavedInsideTextNode;
  190. s := '</' + node.NodeName + '>';
  191. if InsideTextNode then
  192. wrt(s)
  193. else
  194. wrtln(s);
  195. end;
  196. end;
  197. procedure WriteAttribute(node: TDOMNode);
  198. begin
  199. WriteLn('WriteAttribute');
  200. end;
  201. procedure WriteText(node: TDOMNode);
  202. begin
  203. ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback);
  204. end;
  205. procedure WriteCDATA(node: TDOMNode);
  206. begin
  207. if InsideTextNode then
  208. wrt('<![CDATA[' + node.NodeValue + ']]>')
  209. else
  210. wrtln(Indent + '<![CDATA[' + node.NodeValue + ']]>')
  211. end;
  212. procedure WriteEntityRef(node: TDOMNode);
  213. begin
  214. wrt('&' + node.NodeName + ';');
  215. end;
  216. procedure WriteEntity(node: TDOMNode);
  217. begin
  218. WriteLn('WriteEntity');
  219. end;
  220. procedure WritePI(node: TDOMNode);
  221. var
  222. s: String;
  223. begin
  224. s := '<!' + TDOMProcessingInstruction(node).Target + ' ' +
  225. TDOMProcessingInstruction(node).Data + '>';
  226. if InsideTextNode then
  227. wrt(s)
  228. else
  229. wrtln(Indent + s);
  230. end;
  231. procedure WriteComment(node: TDOMNode);
  232. begin
  233. if InsideTextNode then
  234. wrt('<!--' + node.NodeValue + '-->')
  235. else
  236. wrtln(Indent + '<!--' + node.NodeValue + '-->')
  237. end;
  238. procedure WriteDocument(node: TDOMNode);
  239. begin
  240. WriteLn('WriteDocument');
  241. end;
  242. procedure WriteDocumentType(node: TDOMNode);
  243. begin
  244. WriteLn('WriteDocumentType');
  245. end;
  246. procedure WriteDocumentFragment(node: TDOMNode);
  247. begin
  248. WriteLn('WriteDocumentFragment');
  249. end;
  250. procedure WriteNotation(node: TDOMNode);
  251. begin
  252. WriteLn('WriteNotation');
  253. end;
  254. procedure InitWriter;
  255. begin
  256. InsideTextNode := False;
  257. SetLength(Indent, 0);
  258. end;
  259. procedure RootWriter(doc: TXMLDocument);
  260. var
  261. Child: TDOMNode;
  262. begin
  263. InitWriter;
  264. wrt('<?xml version="');
  265. if Length(doc.XMLVersion) > 0 then
  266. ConvWrite(doc.XMLVersion, AttrSpecialChars, @AttrSpecialCharCallback)
  267. else
  268. wrt('1.0');
  269. wrt('"');
  270. if Length(doc.Encoding) > 0 then
  271. begin
  272. wrt(' encoding="');
  273. ConvWrite(doc.Encoding, AttrSpecialChars, @AttrSpecialCharCallback);
  274. wrt('"');
  275. end;
  276. wrtln('?>');
  277. if Length(doc.StylesheetType) > 0 then
  278. begin
  279. wrt('<?xml-stylesheet type="');
  280. ConvWrite(doc.StylesheetType, AttrSpecialChars, @AttrSpecialCharCallback);
  281. wrt('" href="');
  282. ConvWrite(doc.StylesheetHRef, AttrSpecialChars, @AttrSpecialCharCallback);
  283. wrtln('"?>');
  284. end;
  285. SetLength(Indent, 0);
  286. child := doc.FirstChild;
  287. while Assigned(Child) do
  288. begin
  289. WriteNode(Child);
  290. Child := Child.NextSibling;
  291. end;
  292. end;
  293. // -------------------------------------------------------------------
  294. // Interface implementation
  295. // -------------------------------------------------------------------
  296. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  297. begin
  298. Stream := TFileStream.Create(AFileName, fmCreate);
  299. wrt := @Stream_Write;
  300. wrtln := @Stream_WriteLn;
  301. RootWriter(doc);
  302. Stream.Free;
  303. end;
  304. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  305. begin
  306. f := @AFile;
  307. wrt := @Text_Write;
  308. wrtln := @Text_WriteLn;
  309. RootWriter(doc);
  310. end;
  311. procedure WriteXMLFile(doc: TXMLDocument; var AStream: TStream);
  312. begin
  313. Stream := AStream;
  314. wrt := @Stream_Write;
  315. wrtln := @Stream_WriteLn;
  316. RootWriter(doc);
  317. end;
  318. procedure WriteXML(Node: TDOMNode; const AFileName: String);
  319. begin
  320. Stream := TFileStream.Create(AFileName, fmCreate);
  321. wrt := @Stream_Write;
  322. wrtln := @Stream_WriteLn;
  323. InitWriter;
  324. WriteNode(Node);
  325. Stream.Free;
  326. end;
  327. procedure WriteXML(Node: TDOMNode; var AFile: Text);
  328. begin
  329. f := @AFile;
  330. wrt := @Text_Write;
  331. wrtln := @Text_WriteLn;
  332. InitWriter;
  333. WriteNode(Node);
  334. end;
  335. procedure WriteXML(Node: TDOMNode; var AStream: TStream);
  336. begin
  337. stream := AStream;
  338. wrt := @Stream_Write;
  339. wrtln := @Stream_WriteLn;
  340. InitWriter;
  341. WriteNode(Node);
  342. end;
  343. end.
  344. {
  345. $Log$
  346. Revision 1.9 2002-09-20 11:36:51 sg
  347. * Argument escaping improvements
  348. * Indent fixed for consecutive WriteXML calls
  349. Revision 1.8 2002/09/20 11:04:21 michael
  350. + Changed writexml type to TDomNode instead of TDomeElement
  351. Revision 1.7 2002/09/07 15:15:29 peter
  352. * old logs removed and tabs fixed
  353. }