xmlwrite.pp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. {
  2. $Id$
  3. This file is part of the Free Component Library
  4. XML writing routines
  5. Copyright (c) 1999-2000 by Sebastian Guenther
  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(Element: TDOMElement; const AFileName: String);
  21. procedure WriteXML(Element: TDOMElement; var AFile: Text);
  22. procedure WriteXML(Element: TDOMElement; var AStream: TStream);
  23. // ===================================================================
  24. implementation
  25. // -------------------------------------------------------------------
  26. // Writers for the different node types
  27. // -------------------------------------------------------------------
  28. procedure WriteElement(node: TDOMNode); forward;
  29. procedure WriteAttribute(node: TDOMNode); forward;
  30. procedure WriteText(node: TDOMNode); forward;
  31. procedure WriteCDATA(node: TDOMNode); forward;
  32. procedure WriteEntityRef(node: TDOMNode); forward;
  33. procedure WriteEntity(node: TDOMNode); forward;
  34. procedure WritePI(node: TDOMNode); forward;
  35. procedure WriteComment(node: TDOMNode); forward;
  36. procedure WriteDocument(node: TDOMNode); forward;
  37. procedure WriteDocumentType(node: TDOMNode); forward;
  38. procedure WriteDocumentFragment(node: TDOMNode); forward;
  39. procedure WriteNotation(node: TDOMNode); forward;
  40. type
  41. TWriteNodeProc = procedure(node: TDOMNode);
  42. const
  43. WriteProcs: array[ELEMENT_NODE..NOTATION_NODE] of TWriteNodeProc =
  44. (WriteElement, WriteAttribute, WriteText, WriteCDATA, WriteEntityRef,
  45. WriteEntity, WritePI, WriteComment, WriteDocument, WriteDocumentType,
  46. WriteDocumentFragment, WriteNotation);
  47. procedure WriteNode(node: TDOMNode);
  48. begin
  49. WriteProcs[node.NodeType](node);
  50. end;
  51. // -------------------------------------------------------------------
  52. // Text file and TStream support
  53. // -------------------------------------------------------------------
  54. type
  55. TOutputProc = procedure(s: String);
  56. var
  57. f: ^Text;
  58. stream: TStream;
  59. wrt, wrtln: TOutputProc;
  60. InsideTextNode: Boolean;
  61. procedure Text_Write(s: String);
  62. begin
  63. Write(f^, s);
  64. end;
  65. procedure Text_WriteLn(s: String);
  66. begin
  67. WriteLn(f^, s);
  68. end;
  69. procedure Stream_Write(s: String);
  70. begin
  71. if Length(s) > 0 then
  72. stream.Write(s[1], Length(s));
  73. end;
  74. procedure Stream_WriteLn(s: String);
  75. begin
  76. if Length(s) > 0 then
  77. stream.Write(s[1], Length(s));
  78. stream.WriteByte(10);
  79. end;
  80. // -------------------------------------------------------------------
  81. // Indent handling
  82. // -------------------------------------------------------------------
  83. var
  84. Indent: String;
  85. procedure IncIndent;
  86. begin
  87. Indent := Indent + ' ';
  88. end;
  89. procedure DecIndent;
  90. begin
  91. if Length(Indent) >= 2 then
  92. SetLength(Indent, Length(Indent) - 2);
  93. end;
  94. // -------------------------------------------------------------------
  95. // String conversion
  96. // -------------------------------------------------------------------
  97. type
  98. TCharacters = set of Char;
  99. TSpecialCharCallback = procedure(c: Char);
  100. const
  101. AttrSpecialChars = ['"', '&'];
  102. TextSpecialChars = ['<', '>', '&'];
  103. procedure ConvWrite(const s: String; const SpecialChars: TCharacters;
  104. const SpecialCharCallback: TSpecialCharCallback);
  105. var
  106. StartPos, EndPos: Integer;
  107. begin
  108. StartPos := 1;
  109. EndPos := 1;
  110. while EndPos <= Length(s) do
  111. begin
  112. if s[EndPos] in SpecialChars then
  113. begin
  114. wrt(Copy(s, StartPos, EndPos - StartPos));
  115. SpecialCharCallback(s[EndPos]);
  116. StartPos := EndPos + 1;
  117. end;
  118. Inc(EndPos);
  119. end;
  120. if EndPos > StartPos then
  121. wrt(Copy(s, StartPos, EndPos - StartPos));
  122. end;
  123. procedure AttrSpecialCharCallback(c: Char);
  124. begin
  125. if c = '"' then
  126. wrt('&quot;')
  127. else if c = '&' then
  128. wrt('&amp;')
  129. else
  130. wrt(c);
  131. end;
  132. procedure TextnodeSpecialCharCallback(c: Char);
  133. begin
  134. if c = '<' then
  135. wrt('&lt;')
  136. else if c = '>' then
  137. wrt('&gt;')
  138. else if c = '&' then
  139. wrt('&amp;')
  140. else
  141. wrt(c);
  142. end;
  143. // -------------------------------------------------------------------
  144. // Node writers implementations
  145. // -------------------------------------------------------------------
  146. procedure WriteElement(node: TDOMNode);
  147. var
  148. i: Integer;
  149. attr, child: TDOMNode;
  150. SavedInsideTextNode: Boolean;
  151. s: String;
  152. begin
  153. if not InsideTextNode then
  154. wrt(Indent);
  155. wrt('<' + node.NodeName);
  156. for i := 0 to node.Attributes.Length - 1 do
  157. begin
  158. attr := node.Attributes.Item[i];
  159. wrt(' ' + attr.NodeName + '=');
  160. s := attr.NodeValue;
  161. // !!!: Replace special characters in "s" such as '&', '<', '>'
  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. begin
  222. WriteLn('WritePI');
  223. end;
  224. procedure WriteComment(node: TDOMNode);
  225. begin
  226. if InsideTextNode then
  227. wrt('<!--' + node.NodeValue + '-->')
  228. else
  229. wrtln(Indent + '<!--' + node.NodeValue + '-->')
  230. end;
  231. procedure WriteDocument(node: TDOMNode);
  232. begin
  233. WriteLn('WriteDocument');
  234. end;
  235. procedure WriteDocumentType(node: TDOMNode);
  236. begin
  237. WriteLn('WriteDocumentType');
  238. end;
  239. procedure WriteDocumentFragment(node: TDOMNode);
  240. begin
  241. WriteLn('WriteDocumentFragment');
  242. end;
  243. procedure WriteNotation(node: TDOMNode);
  244. begin
  245. WriteLn('WriteNotation');
  246. end;
  247. procedure InitWriter;
  248. begin
  249. InsideTextNode := False;
  250. end;
  251. procedure RootWriter(doc: TXMLDocument);
  252. var
  253. Child: TDOMNode;
  254. begin
  255. InitWriter;
  256. wrt('<?xml version="');
  257. if doc.XMLVersion <> '' then
  258. wrt(doc.XMLVersion)
  259. else
  260. wrt('1.0');
  261. wrt('"');
  262. if doc.Encoding <> '' then
  263. wrt(' encoding="' + doc.Encoding + '"');
  264. wrtln('?>');
  265. indent := '';
  266. child := doc.FirstChild;
  267. while Assigned(Child) do
  268. begin
  269. WriteNode(Child);
  270. Child := Child.NextSibling;
  271. end;
  272. end;
  273. // -------------------------------------------------------------------
  274. // Interface implementation
  275. // -------------------------------------------------------------------
  276. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  277. begin
  278. Stream := TFileStream.Create(AFileName, fmCreate);
  279. wrt := @Stream_Write;
  280. wrtln := @Stream_WriteLn;
  281. RootWriter(doc);
  282. Stream.Free;
  283. end;
  284. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  285. begin
  286. f := @AFile;
  287. wrt := @Text_Write;
  288. wrtln := @Text_WriteLn;
  289. RootWriter(doc);
  290. end;
  291. procedure WriteXMLFile(doc: TXMLDocument; var AStream: TStream);
  292. begin
  293. Stream := AStream;
  294. wrt := @Stream_Write;
  295. wrtln := @Stream_WriteLn;
  296. RootWriter(doc);
  297. end;
  298. procedure WriteXML(Element: TDOMElement; const AFileName: String);
  299. begin
  300. Stream := TFileStream.Create(AFileName, fmCreate);
  301. wrt := @Stream_Write;
  302. wrtln := @Stream_WriteLn;
  303. InitWriter;
  304. WriteNode(Element);
  305. Stream.Free;
  306. end;
  307. procedure WriteXML(Element: TDOMElement; var AFile: Text);
  308. begin
  309. f := @AFile;
  310. wrt := @Text_Write;
  311. wrtln := @Text_WriteLn;
  312. InitWriter;
  313. WriteNode(Element);
  314. end;
  315. procedure WriteXML(Element: TDOMElement; var AStream: TStream);
  316. begin
  317. stream := AStream;
  318. wrt := @Stream_Write;
  319. wrtln := @Stream_WriteLn;
  320. InitWriter;
  321. WriteNode(Element);
  322. end;
  323. end.
  324. {
  325. $Log$
  326. Revision 1.3 2000-07-25 09:20:08 sg
  327. * Fixed some small bugs
  328. - some methods where 'virtual' instead of 'override' in dom.pp
  329. - corrections regaring wether NodeName or NodeValue is used, for
  330. some node types (Entity, EntityReference)
  331. Revision 1.2 2000/07/13 11:33:08 michael
  332. + removed logs
  333. }