xmlwrite.pp 13 KB

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