xmlwrite.pp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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, [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(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. 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. // !!!: Replace special characters in "s" such as '&', '<', '>'
  163. wrt('"');
  164. ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback);
  165. wrt('"');
  166. end;
  167. Child := node.FirstChild;
  168. if Child = nil then
  169. if InsideTextNode then
  170. wrt('/>')
  171. else
  172. wrtln('/>')
  173. else
  174. begin
  175. SavedInsideTextNode := InsideTextNode;
  176. if InsideTextNode or Child.InheritsFrom(TDOMText) then
  177. wrt('>')
  178. else
  179. wrtln('>');
  180. IncIndent;
  181. repeat
  182. if Child.InheritsFrom(TDOMText) then
  183. InsideTextNode := True;
  184. WriteNode(Child);
  185. Child := Child.NextSibling;
  186. until child = nil;
  187. DecIndent;
  188. if not InsideTextNode then
  189. wrt(Indent);
  190. InsideTextNode := SavedInsideTextNode;
  191. s := '</' + node.NodeName + '>';
  192. if InsideTextNode then
  193. wrt(s)
  194. else
  195. wrtln(s);
  196. end;
  197. end;
  198. procedure WriteAttribute(node: TDOMNode);
  199. begin
  200. WriteLn('WriteAttribute');
  201. end;
  202. procedure WriteText(node: TDOMNode);
  203. begin
  204. ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback);
  205. end;
  206. procedure WriteCDATA(node: TDOMNode);
  207. begin
  208. if InsideTextNode then
  209. wrt('<![CDATA[' + node.NodeValue + ']]>')
  210. else
  211. wrtln(Indent + '<![CDATA[' + node.NodeValue + ']]>')
  212. end;
  213. procedure WriteEntityRef(node: TDOMNode);
  214. begin
  215. wrt('&' + node.NodeName + ';');
  216. end;
  217. procedure WriteEntity(node: TDOMNode);
  218. begin
  219. WriteLn('WriteEntity');
  220. end;
  221. procedure WritePI(node: TDOMNode);
  222. var
  223. s: String;
  224. begin
  225. s := '<!' + TDOMProcessingInstruction(node).Target + ' ' +
  226. TDOMProcessingInstruction(node).Data + '>';
  227. if InsideTextNode then
  228. wrt(s)
  229. else
  230. wrtln(Indent + s);
  231. end;
  232. procedure WriteComment(node: TDOMNode);
  233. begin
  234. if InsideTextNode then
  235. wrt('<!--' + node.NodeValue + '-->')
  236. else
  237. wrtln(Indent + '<!--' + node.NodeValue + '-->')
  238. end;
  239. procedure WriteDocument(node: TDOMNode);
  240. begin
  241. WriteLn('WriteDocument');
  242. end;
  243. procedure WriteDocumentType(node: TDOMNode);
  244. begin
  245. WriteLn('WriteDocumentType');
  246. end;
  247. procedure WriteDocumentFragment(node: TDOMNode);
  248. begin
  249. WriteLn('WriteDocumentFragment');
  250. end;
  251. procedure WriteNotation(node: TDOMNode);
  252. begin
  253. WriteLn('WriteNotation');
  254. end;
  255. procedure InitWriter;
  256. begin
  257. InsideTextNode := False;
  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. wrt(doc.XMLVersion)
  267. else
  268. wrt('1.0');
  269. wrt('"');
  270. if Length(doc.Encoding) > 0 then
  271. wrt(' encoding="' + doc.Encoding + '"');
  272. wrtln('?>');
  273. if Length(doc.StylesheetType) > 0 then
  274. // !!!: Can't handle with HRefs which contain special chars (" and so on)
  275. wrtln(Format('<?xml-stylesheet type="%s" href="%s"?>',
  276. [doc.StylesheetType, doc.StylesheetHRef]));
  277. indent := '';
  278. child := doc.FirstChild;
  279. while Assigned(Child) do
  280. begin
  281. WriteNode(Child);
  282. Child := Child.NextSibling;
  283. end;
  284. end;
  285. // -------------------------------------------------------------------
  286. // Interface implementation
  287. // -------------------------------------------------------------------
  288. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  289. begin
  290. Stream := TFileStream.Create(AFileName, fmCreate);
  291. wrt := @Stream_Write;
  292. wrtln := @Stream_WriteLn;
  293. RootWriter(doc);
  294. Stream.Free;
  295. end;
  296. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  297. begin
  298. f := @AFile;
  299. wrt := @Text_Write;
  300. wrtln := @Text_WriteLn;
  301. RootWriter(doc);
  302. end;
  303. procedure WriteXMLFile(doc: TXMLDocument; var AStream: TStream);
  304. begin
  305. Stream := AStream;
  306. wrt := @Stream_Write;
  307. wrtln := @Stream_WriteLn;
  308. RootWriter(doc);
  309. end;
  310. procedure WriteXML(Element: TDOMElement; const AFileName: String);
  311. begin
  312. Stream := TFileStream.Create(AFileName, fmCreate);
  313. wrt := @Stream_Write;
  314. wrtln := @Stream_WriteLn;
  315. InitWriter;
  316. WriteNode(Element);
  317. Stream.Free;
  318. end;
  319. procedure WriteXML(Element: TDOMElement; var AFile: Text);
  320. begin
  321. f := @AFile;
  322. wrt := @Text_Write;
  323. wrtln := @Text_WriteLn;
  324. InitWriter;
  325. WriteNode(Element);
  326. end;
  327. procedure WriteXML(Element: TDOMElement; var AStream: TStream);
  328. begin
  329. stream := AStream;
  330. wrt := @Stream_Write;
  331. wrtln := @Stream_WriteLn;
  332. InitWriter;
  333. WriteNode(Element);
  334. end;
  335. end.
  336. {
  337. $Log$
  338. Revision 1.5 2000-10-03 20:16:31 sg
  339. * Now writes Processing Instructions and a stylesheet link, if set
  340. Revision 1.4 2000/07/29 14:52:25 sg
  341. * Modified the copyright notice to remove ambiguities
  342. Revision 1.3 2000/07/25 09:20:08 sg
  343. * Fixed some small bugs
  344. - some methods where 'virtual' instead of 'override' in dom.pp
  345. - corrections regaring wether NodeName or NodeValue is used, for
  346. some node types (Entity, EntityReference)
  347. Revision 1.2 2000/07/13 11:33:08 michael
  348. + removed logs
  349. }