xmlwrite.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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
  128. wrt(c);
  129. end;
  130. procedure TextnodeSpecialCharCallback(c: Char);
  131. begin
  132. if c = '<' then
  133. wrt('&lt;')
  134. else
  135. wrt(c);
  136. end;
  137. // -------------------------------------------------------------------
  138. // Node writers implementations
  139. // -------------------------------------------------------------------
  140. procedure WriteElement(node: TDOMNode);
  141. var
  142. i: Integer;
  143. attr, child: TDOMNode;
  144. SavedInsideTextNode: Boolean;
  145. s: String;
  146. begin
  147. if not InsideTextNode then
  148. wrt(Indent);
  149. wrt('<' + node.NodeName);
  150. for i := 0 to node.Attributes.Length - 1 do
  151. begin
  152. attr := node.Attributes.Item[i];
  153. wrt(' ' + attr.NodeName + '=');
  154. s := attr.NodeValue;
  155. // !!!: Replace special characters in "s" such as '&', '<', '>'
  156. wrt('"');
  157. ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback);
  158. wrt('"');
  159. end;
  160. Child := node.FirstChild;
  161. if Child = nil then
  162. if InsideTextNode then
  163. wrt('/>')
  164. else
  165. wrtln('/>')
  166. else
  167. begin
  168. SavedInsideTextNode := InsideTextNode;
  169. if InsideTextNode or Child.InheritsFrom(TDOMText) then
  170. wrt('>')
  171. else
  172. wrtln('>');
  173. IncIndent;
  174. repeat
  175. if Child.InheritsFrom(TDOMText) then
  176. InsideTextNode := True;
  177. WriteNode(Child);
  178. Child := Child.NextSibling;
  179. until child = nil;
  180. DecIndent;
  181. if not InsideTextNode then
  182. wrt(Indent);
  183. InsideTextNode := SavedInsideTextNode;
  184. s := '</' + node.NodeName + '>';
  185. if InsideTextNode then
  186. wrt(s)
  187. else
  188. wrtln(s);
  189. end;
  190. end;
  191. procedure WriteAttribute(node: TDOMNode);
  192. begin
  193. WriteLn('WriteAttribute');
  194. end;
  195. procedure WriteText(node: TDOMNode);
  196. begin
  197. ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback);
  198. end;
  199. procedure WriteCDATA(node: TDOMNode);
  200. begin
  201. if InsideTextNode then
  202. wrt('<![CDATA[' + node.NodeValue + ']]>')
  203. else
  204. wrtln(Indent + '<![CDATA[' + node.NodeValue + ']]>')
  205. end;
  206. procedure WriteEntityRef(node: TDOMNode);
  207. begin
  208. wrt('&' + node.NodeValue + ';');
  209. end;
  210. procedure WriteEntity(node: TDOMNode);
  211. begin
  212. WriteLn('WriteEntity');
  213. end;
  214. procedure WritePI(node: TDOMNode);
  215. begin
  216. WriteLn('WritePI');
  217. end;
  218. procedure WriteComment(node: TDOMNode);
  219. begin
  220. if InsideTextNode then
  221. wrt('<!--' + node.NodeValue + '-->')
  222. else
  223. wrtln(Indent + '<!--' + node.NodeValue + '-->')
  224. end;
  225. procedure WriteDocument(node: TDOMNode);
  226. begin
  227. WriteLn('WriteDocument');
  228. end;
  229. procedure WriteDocumentType(node: TDOMNode);
  230. begin
  231. WriteLn('WriteDocumentType');
  232. end;
  233. procedure WriteDocumentFragment(node: TDOMNode);
  234. begin
  235. WriteLn('WriteDocumentFragment');
  236. end;
  237. procedure WriteNotation(node: TDOMNode);
  238. begin
  239. WriteLn('WriteNotation');
  240. end;
  241. procedure InitWriter;
  242. begin
  243. InsideTextNode := False;
  244. end;
  245. procedure RootWriter(doc: TXMLDocument);
  246. var
  247. Child: TDOMNode;
  248. begin
  249. InitWriter;
  250. wrt('<?xml version="');
  251. if doc.XMLVersion <> '' then
  252. wrt(doc.XMLVersion)
  253. else
  254. wrt('1.0');
  255. wrt('"');
  256. if doc.Encoding <> '' then
  257. wrt(' encoding="' + doc.Encoding + '"');
  258. wrtln('?>');
  259. indent := '';
  260. child := doc.FirstChild;
  261. while Assigned(Child) do
  262. begin
  263. WriteNode(Child);
  264. Child := Child.NextSibling;
  265. end;
  266. end;
  267. // -------------------------------------------------------------------
  268. // Interface implementation
  269. // -------------------------------------------------------------------
  270. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  271. begin
  272. Stream := TFileStream.Create(AFileName, fmCreate);
  273. wrt := @Stream_Write;
  274. wrtln := @Stream_WriteLn;
  275. RootWriter(doc);
  276. Stream.Free;
  277. end;
  278. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  279. begin
  280. f := @AFile;
  281. wrt := @Text_Write;
  282. wrtln := @Text_WriteLn;
  283. RootWriter(doc);
  284. end;
  285. procedure WriteXMLFile(doc: TXMLDocument; var AStream: TStream);
  286. begin
  287. Stream := AStream;
  288. wrt := @Stream_Write;
  289. wrtln := @Stream_WriteLn;
  290. RootWriter(doc);
  291. end;
  292. procedure WriteXML(Element: TDOMElement; const AFileName: String);
  293. begin
  294. Stream := TFileStream.Create(AFileName, fmCreate);
  295. wrt := @Stream_Write;
  296. wrtln := @Stream_WriteLn;
  297. InitWriter;
  298. WriteNode(Element);
  299. Stream.Free;
  300. end;
  301. procedure WriteXML(Element: TDOMElement; var AFile: Text);
  302. begin
  303. f := @AFile;
  304. wrt := @Text_Write;
  305. wrtln := @Text_WriteLn;
  306. InitWriter;
  307. WriteNode(Element);
  308. end;
  309. procedure WriteXML(Element: TDOMElement; var AStream: TStream);
  310. begin
  311. stream := AStream;
  312. wrt := @Stream_Write;
  313. wrtln := @Stream_WriteLn;
  314. InitWriter;
  315. WriteNode(Element);
  316. end;
  317. end.
  318. {
  319. $Log$
  320. Revision 1.8 2000-06-29 08:45:32 sg
  321. * Now produces _much_ better output...!
  322. Revision 1.7 2000/04/20 14:15:45 sg
  323. * Minor bugfixes
  324. * Started support for DOM level 2
  325. }