xmlwrite.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. {
  2. This file is part of the Free Component Library
  3. XML writing routines
  4. Copyright (c) 1999-2000 by Sebastian Guenther, [email protected]
  5. See the file COPYING.modifiedLGPL, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit XMLWrite;
  12. {$MODE objfpc}
  13. {$H+}
  14. interface
  15. uses Classes, DOM;
  16. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String); overload;
  17. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text); overload;
  18. procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream); overload;
  19. procedure WriteXML(Element: TDOMNode; const AFileName: String); overload;
  20. procedure WriteXML(Element: TDOMNode; var AFile: Text); overload;
  21. procedure WriteXML(Element: TDOMNode; AStream: TStream); overload;
  22. // ===================================================================
  23. implementation
  24. uses SysUtils;
  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(const Buffer; Count: Longint);
  56. threadvar
  57. f: ^Text;
  58. stream: TStream;
  59. wrt, wrtln: TOutputProc;
  60. InsideTextNode: Boolean;
  61. procedure Text_Write(const Buffer; Count: Longint);
  62. var s: string;
  63. begin
  64. if Count>0 then begin
  65. SetLength(s,Count);
  66. System.Move(Buffer,s[1],Count);
  67. Write(f^, s);
  68. end;
  69. end;
  70. procedure Text_WriteLn(const Buffer; Count: Longint);
  71. var s: string;
  72. begin
  73. if Count>0 then begin
  74. SetLength(s,Count);
  75. System.Move(Buffer,s[1],Count);
  76. writeln(f^, s);
  77. end;
  78. end;
  79. procedure Stream_Write(const Buffer; Count: Longint);
  80. begin
  81. if Count > 0 then begin
  82. stream.Write(Buffer, Count);
  83. end;
  84. end;
  85. procedure Stream_WriteLn(const Buffer; Count: Longint);
  86. begin
  87. if Count > 0 then begin
  88. stream.Write(Buffer, Count);
  89. stream.WriteByte(10);
  90. end;
  91. end;
  92. procedure wrtStr(const s: string);
  93. begin
  94. if s<>'' then
  95. wrt(s[1],length(s));
  96. end;
  97. procedure wrtStrLn(const s: string);
  98. begin
  99. if s<>'' then
  100. wrtln(s[1],length(s));
  101. end;
  102. procedure wrtChr(c: char);
  103. begin
  104. wrt(c,1);
  105. end;
  106. procedure wrtLineEnd;
  107. begin
  108. wrt(#10,1);
  109. end;
  110. // -------------------------------------------------------------------
  111. // Indent handling
  112. // -------------------------------------------------------------------
  113. threadvar
  114. Indent: String;
  115. IndentCount: integer;
  116. procedure wrtIndent;
  117. var i: integer;
  118. begin
  119. for i:=1 to IndentCount do
  120. wrtStr(Indent);
  121. end;
  122. procedure IncIndent;
  123. begin
  124. inc(IndentCount);
  125. end;
  126. procedure DecIndent;
  127. begin
  128. if IndentCount>0 then dec(IndentCount);
  129. end;
  130. // -------------------------------------------------------------------
  131. // String conversion
  132. // -------------------------------------------------------------------
  133. type
  134. TCharacters = set of Char;
  135. TSpecialCharCallback = procedure(c: Char);
  136. const
  137. AttrSpecialChars = ['<', '>', '"', '&'];
  138. TextSpecialChars = ['<', '>', '&'];
  139. procedure ConvWrite(const s: String; const SpecialChars: TCharacters;
  140. const SpecialCharCallback: TSpecialCharCallback);
  141. var
  142. StartPos, EndPos: Integer;
  143. begin
  144. StartPos := 1;
  145. EndPos := 1;
  146. while EndPos <= Length(s) do
  147. begin
  148. if s[EndPos] in SpecialChars then
  149. begin
  150. wrt(s[StartPos],EndPos - StartPos);
  151. SpecialCharCallback(s[EndPos]);
  152. StartPos := EndPos + 1;
  153. end;
  154. Inc(EndPos);
  155. end;
  156. if StartPos <= length(s) then
  157. wrt(s[StartPos], EndPos - StartPos);
  158. end;
  159. procedure AttrSpecialCharCallback(c: Char);
  160. const
  161. QuotStr = '&quot;';
  162. AmpStr = '&amp;';
  163. begin
  164. if c = '"' then
  165. wrtStr(QuotStr)
  166. else if c = '&' then
  167. wrtStr(AmpStr)
  168. else
  169. wrt(c,1);
  170. end;
  171. procedure TextnodeSpecialCharCallback(c: Char);
  172. const
  173. ltStr = '&lt;';
  174. gtStr = '&gt;';
  175. AmpStr = '&amp;';
  176. begin
  177. if c = '<' then
  178. wrtStr(ltStr)
  179. else if c = '>' then
  180. wrtStr(gtStr)
  181. else if c = '&' then
  182. wrtStr(AmpStr)
  183. else
  184. wrt(c,1);
  185. end;
  186. // -------------------------------------------------------------------
  187. // Node writers implementations
  188. // -------------------------------------------------------------------
  189. procedure WriteElement(node: TDOMNode);
  190. var
  191. i: Integer;
  192. attr, child: TDOMNode;
  193. SavedInsideTextNode: Boolean;
  194. s: String;
  195. begin
  196. if not InsideTextNode then
  197. wrtIndent;
  198. wrtChr('<');
  199. wrtStr(node.NodeName);
  200. for i := 0 to node.Attributes.Length - 1 do
  201. begin
  202. attr := node.Attributes.Item[i];
  203. wrtChr(' ');
  204. wrtStr(attr.NodeName);
  205. wrtChr('=');
  206. s := attr.NodeValue;
  207. // !!!: Replace special characters in "s" such as '&', '<', '>'
  208. wrtChr('"');
  209. ConvWrite(s, AttrSpecialChars, @AttrSpecialCharCallback);
  210. wrtChr('"');
  211. end;
  212. Child := node.FirstChild;
  213. if Child = nil then begin
  214. wrtChr('/');
  215. wrtChr('>');
  216. if not InsideTextNode then wrtLineEnd;
  217. end else
  218. begin
  219. SavedInsideTextNode := InsideTextNode;
  220. wrtChr('>');
  221. if not (InsideTextNode or Child.InheritsFrom(TDOMText)) then
  222. wrtLineEnd;
  223. IncIndent;
  224. repeat
  225. if Child.InheritsFrom(TDOMText) then
  226. InsideTextNode := True;
  227. WriteNode(Child);
  228. Child := Child.NextSibling;
  229. until child = nil;
  230. DecIndent;
  231. if not InsideTextNode then
  232. wrtIndent;
  233. InsideTextNode := SavedInsideTextNode;
  234. wrtChr('<');
  235. wrtChr('/');
  236. wrtStr(node.NodeName);
  237. wrtChr('>');
  238. if not InsideTextNode then
  239. wrtLineEnd;
  240. end;
  241. end;
  242. procedure WriteAttribute(node: TDOMNode);
  243. begin
  244. if node=nil then ;
  245. end;
  246. procedure WriteText(node: TDOMNode);
  247. begin
  248. ConvWrite(node.NodeValue, TextSpecialChars, @TextnodeSpecialCharCallback);
  249. if node=nil then ;
  250. end;
  251. procedure WriteCDATA(node: TDOMNode);
  252. begin
  253. if not InsideTextNode then
  254. wrtStr('<![CDATA[' + node.NodeValue + ']]>')
  255. else begin
  256. wrtIndent;
  257. wrtStrln('<![CDATA[' + node.NodeValue + ']]>')
  258. end;
  259. end;
  260. procedure WriteEntityRef(node: TDOMNode);
  261. begin
  262. wrtChr('&');
  263. wrtStr(node.NodeName);
  264. wrtChr(';');
  265. end;
  266. procedure WriteEntity(node: TDOMNode);
  267. begin
  268. if node=nil then ;
  269. end;
  270. procedure WritePI(node: TDOMNode);
  271. begin
  272. if not InsideTextNode then wrtIndent;
  273. wrtChr('<'); wrtChr('!');
  274. wrtStr(TDOMProcessingInstruction(node).Target);
  275. wrtChr(' ');
  276. wrtStr(TDOMProcessingInstruction(node).Data);
  277. wrtChr('>');
  278. if not InsideTextNode then wrtLineEnd;
  279. end;
  280. procedure WriteComment(node: TDOMNode);
  281. begin
  282. if not InsideTextNode then wrtIndent;
  283. wrtStr('<!--');
  284. wrtStr(node.NodeValue);
  285. wrtStr('-->');
  286. if not InsideTextNode then wrtLineEnd;
  287. end;
  288. procedure WriteDocument(node: TDOMNode);
  289. begin
  290. if node=nil then ;
  291. end;
  292. procedure WriteDocumentType(node: TDOMNode);
  293. begin
  294. if node=nil then ;
  295. end;
  296. procedure WriteDocumentFragment(node: TDOMNode);
  297. begin
  298. if node=nil then ;
  299. end;
  300. procedure WriteNotation(node: TDOMNode);
  301. begin
  302. if node=nil then ;
  303. end;
  304. procedure InitWriter;
  305. begin
  306. InsideTextNode := False;
  307. SetLength(Indent, 0);
  308. end;
  309. procedure RootWriter(doc: TXMLDocument);
  310. var
  311. Child: TDOMNode;
  312. begin
  313. InitWriter;
  314. wrtStr('<?xml version="');
  315. if Length(doc.XMLVersion) > 0 then
  316. ConvWrite(doc.XMLVersion, AttrSpecialChars, @AttrSpecialCharCallback)
  317. else
  318. wrtStr('1.0');
  319. wrtChr('"');
  320. if Length(doc.Encoding) > 0 then
  321. begin
  322. wrtStr(' encoding="');
  323. ConvWrite(doc.Encoding, AttrSpecialChars, @AttrSpecialCharCallback);
  324. wrtStr('"');
  325. end;
  326. wrtStrln('?>');
  327. if Length(doc.StylesheetType) > 0 then
  328. begin
  329. wrtStr('<?xml-stylesheet type="');
  330. ConvWrite(doc.StylesheetType, AttrSpecialChars, @AttrSpecialCharCallback);
  331. wrtStr('" href="');
  332. ConvWrite(doc.StylesheetHRef, AttrSpecialChars, @AttrSpecialCharCallback);
  333. wrtStrln('"?>');
  334. end;
  335. Indent := ' ';
  336. IndentCount := 0;
  337. child := doc.FirstChild;
  338. while Assigned(Child) do
  339. begin
  340. WriteNode(Child);
  341. Child := Child.NextSibling;
  342. end;
  343. end;
  344. procedure WriteXMLMemStream(doc: TXMLDocument);
  345. // internally used by the WriteXMLFile procedures
  346. begin
  347. Stream:=TMemoryStream.Create;
  348. WriteXMLFile(doc,Stream);
  349. Stream.Position:=0;
  350. end;
  351. // -------------------------------------------------------------------
  352. // Interface implementation
  353. // -------------------------------------------------------------------
  354. {$IFDEF FPC}
  355. {$DEFINE UsesFPCWidestrings}
  356. {$ENDIF}
  357. {$IFDEF UsesFPCWidestrings}
  358. {procedure SimpleWide2AnsiMove(source:pwidechar;dest:pchar;len:sizeint);
  359. var
  360. i : sizeint;
  361. begin
  362. for i:=1 to len do
  363. begin
  364. if word(source^)<256 then
  365. dest^:=char(word(source^))
  366. else
  367. dest^:='?';
  368. inc(dest);
  369. inc(source);
  370. end;
  371. end;
  372. procedure SimpleAnsi2WideMove(source:pchar;dest:pwidechar;len:sizeint);
  373. var
  374. i : sizeint;
  375. begin
  376. for i:=1 to len do
  377. begin
  378. dest^:=widechar(byte(source^));
  379. inc(dest);
  380. inc(source);
  381. end;
  382. end;}
  383. {$ENDIF}
  384. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  385. var
  386. fs: TFileStream;
  387. begin
  388. // write first to memory buffer and then as one whole block to file
  389. WriteXMLMemStream(doc);
  390. try
  391. fs := TFileStream.Create(AFileName, fmCreate);
  392. fs.CopyFrom(Stream,Stream.Size);
  393. fs.Free;
  394. finally
  395. Stream.Free;
  396. end;
  397. end;
  398. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  399. {$IFDEF UsesFPCWidestrings}
  400. var
  401. MyWideStringManager,OldWideStringManager: TWideStringManager;
  402. {$ENDIF}
  403. begin
  404. {$IFDEF UsesFPCWidestrings}
  405. GetWideStringManager(MyWideStringManager);
  406. MyWideStringManager.Wide2AnsiMoveProc:=@defaultWide2AnsiMove;
  407. MyWideStringManager.Ansi2WideMoveProc:=@defaultAnsi2WideMove;
  408. SetWideStringManager(MyWideStringManager, OldWideStringManager);
  409. try
  410. {$ENDIF}
  411. f := @AFile;
  412. wrt := @Text_Write;
  413. wrtln := @Text_WriteLn;
  414. RootWriter(doc);
  415. {$IFDEF UsesFPCWidestrings}
  416. finally
  417. SetWideStringManager(OldWideStringManager);
  418. end;
  419. {$ENDIF}
  420. end;
  421. procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream);
  422. {$IFDEF UsesFPCWidestrings}
  423. var
  424. OldWideStringManager: TWideStringManager;
  425. {$ENDIF}
  426. begin
  427. {$IFDEF UsesFPCWidestrings}
  428. SetWideStringManager(WideStringManager, OldWideStringManager);
  429. try
  430. {$ENDIF}
  431. Stream := AStream;
  432. wrt := @Stream_Write;
  433. wrtln := @Stream_WriteLn;
  434. RootWriter(doc);
  435. {$IFDEF UsesFPCWidestrings}
  436. finally
  437. SetWideStringManager(OldWideStringManager);
  438. end;
  439. {$ENDIF}
  440. end;
  441. procedure WriteXML(Element: TDOMNode; const AFileName: String);
  442. {$IFDEF UsesFPCWidestrings}
  443. var
  444. OldWideStringManager: TWideStringManager;
  445. {$ENDIF}
  446. begin
  447. {$IFDEF UsesFPCWidestrings}
  448. SetWideStringManager(WideStringManager, OldWideStringManager);
  449. try
  450. {$ENDIF}
  451. Stream := TFileStream.Create(AFileName, fmCreate);
  452. wrt := @Stream_Write;
  453. wrtln := @Stream_WriteLn;
  454. InitWriter;
  455. WriteNode(Element);
  456. Stream.Free;
  457. {$IFDEF UsesFPCWidestrings}
  458. finally
  459. SetWideStringManager(OldWideStringManager);
  460. end;
  461. {$ENDIF}
  462. end;
  463. procedure WriteXML(Element: TDOMNode; var AFile: Text);
  464. {$IFDEF UsesFPCWidestrings}
  465. var
  466. OldWideStringManager: TWideStringManager;
  467. {$ENDIF}
  468. begin
  469. {$IFDEF UsesFPCWidestrings}
  470. SetWideStringManager(WideStringManager, OldWideStringManager);
  471. try
  472. {$ENDIF}
  473. f := @AFile;
  474. wrt := @Text_Write;
  475. wrtln := @Text_WriteLn;
  476. InitWriter;
  477. WriteNode(Element);
  478. {$IFDEF UsesFPCWidestrings}
  479. finally
  480. SetWideStringManager(OldWideStringManager);
  481. end;
  482. {$ENDIF}
  483. end;
  484. procedure WriteXML(Element: TDOMNode; AStream: TStream);
  485. {$IFDEF UsesFPCWidestrings}
  486. var
  487. OldWideStringManager: TWideStringManager;
  488. {$ENDIF}
  489. begin
  490. {$IFDEF UsesFPCWidestrings}
  491. SetWideStringManager(WideStringManager, OldWideStringManager);
  492. try
  493. {$ENDIF}
  494. stream := AStream;
  495. wrt := @Stream_Write;
  496. wrtln := @Stream_WriteLn;
  497. InitWriter;
  498. WriteNode(Element);
  499. {$IFDEF UsesFPCWidestrings}
  500. finally
  501. SetWideStringManager(OldWideStringManager);
  502. end;
  503. {$ENDIF}
  504. end;
  505. end.