xmlwrite.pp 10.0 KB

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