XmlTextWriter.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. //
  2. // System.Xml.XmlTextWriter
  3. //
  4. // Author:
  5. // Kral Ferch <[email protected]>
  6. // Atsushi Enomoto <[email protected]>
  7. //
  8. // (C) 2002 Kral Ferch
  9. // (C) 2003 Atsushi Enomoto
  10. //
  11. using System;
  12. using System.Collections;
  13. using System.IO;
  14. using System.Text;
  15. namespace System.Xml
  16. {
  17. public class XmlTextWriter : XmlWriter
  18. {
  19. #region Fields
  20. const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
  21. WriteState ws = WriteState.Start;
  22. TextWriter w;
  23. bool nullEncoding = false;
  24. bool openWriter = true;
  25. bool openStartElement = false;
  26. bool documentStarted = false;
  27. bool namespaces = true;
  28. bool openAttribute = false;
  29. bool attributeWrittenForElement = false;
  30. ArrayList openElements = new ArrayList ();
  31. int openElementCount;
  32. Formatting formatting = Formatting.None;
  33. int indentation = 2;
  34. char indentChar = ' ';
  35. string indentChars = " ";
  36. char quoteChar = '\"';
  37. int indentLevel = 0;
  38. string indentFormatting;
  39. Stream baseStream = null;
  40. string xmlLang = null;
  41. XmlSpace xmlSpace = XmlSpace.None;
  42. bool openXmlLang = false;
  43. bool openXmlSpace = false;
  44. string openElementPrefix;
  45. string openElementNS;
  46. bool hasRoot = false;
  47. Hashtable newAttributeNamespaces = new Hashtable ();
  48. Hashtable userWrittenNamespaces = new Hashtable ();
  49. StringBuilder cachedStringBuilder;
  50. XmlNamespaceManager namespaceManager = new XmlNamespaceManager (new NameTable ());
  51. string savingAttributeValue = String.Empty;
  52. bool saveAttributeValue;
  53. string savedAttributePrefix;
  54. bool shouldAddSavedNsToManager;
  55. bool shouldCheckElementXmlns;
  56. #endregion
  57. #region Constructors
  58. public XmlTextWriter (TextWriter w) : base ()
  59. {
  60. this.w = w;
  61. nullEncoding = (w.Encoding == null);
  62. StreamWriter sw = w as StreamWriter;
  63. if (sw != null)
  64. baseStream = sw.BaseStream;
  65. }
  66. public XmlTextWriter (Stream w, Encoding encoding) : base ()
  67. {
  68. if (encoding == null) {
  69. nullEncoding = true;
  70. this.w = new StreamWriter (w);
  71. } else
  72. this.w = new StreamWriter (w, encoding);
  73. baseStream = w;
  74. }
  75. public XmlTextWriter (string filename, Encoding encoding) :
  76. this (new FileStream (filename, FileMode.Create, FileAccess.Write, FileShare.None), encoding)
  77. {
  78. }
  79. #endregion
  80. #region Properties
  81. public Stream BaseStream {
  82. get { return baseStream; }
  83. }
  84. public Formatting Formatting {
  85. get { return formatting; }
  86. set { formatting = value; }
  87. }
  88. private bool IndentingOverriden
  89. {
  90. get {
  91. if (openElementCount == 0)
  92. return false;
  93. else
  94. return ((XmlTextWriterOpenElement)
  95. openElements [openElementCount - 1]).IndentingOverriden;
  96. }
  97. set {
  98. if (openElementCount > 0)
  99. ((XmlTextWriterOpenElement) openElements [openElementCount - 1]).IndentingOverriden = value;
  100. }
  101. }
  102. public int Indentation {
  103. get { return indentation; }
  104. set {
  105. indentation = value;
  106. UpdateIndentChars ();
  107. }
  108. }
  109. public char IndentChar {
  110. get { return indentChar; }
  111. set {
  112. indentChar = value;
  113. UpdateIndentChars ();
  114. }
  115. }
  116. public bool Namespaces {
  117. get { return namespaces; }
  118. set {
  119. if (ws != WriteState.Start)
  120. throw new InvalidOperationException ("NotInWriteState.");
  121. namespaces = value;
  122. }
  123. }
  124. public char QuoteChar {
  125. get { return quoteChar; }
  126. set {
  127. if ((value != '\'') && (value != '\"'))
  128. throw new ArgumentException ("This is an invalid XML attribute quote character. Valid attribute quote characters are ' and \".");
  129. quoteChar = value;
  130. }
  131. }
  132. public override WriteState WriteState {
  133. get { return ws; }
  134. }
  135. public override string XmlLang {
  136. get {
  137. string xmlLang = null;
  138. int i;
  139. for (i = openElementCount - 1; i >= 0; i--) {
  140. xmlLang = ((XmlTextWriterOpenElement) openElements [i]).XmlLang;
  141. if (xmlLang != null)
  142. break;
  143. }
  144. return xmlLang;
  145. }
  146. }
  147. public override XmlSpace XmlSpace {
  148. get {
  149. XmlSpace xmlSpace = XmlSpace.None;
  150. int i;
  151. for (i = openElementCount - 1; i >= 0; i--) {
  152. xmlSpace = ((XmlTextWriterOpenElement)openElements [i]).XmlSpace;
  153. if (xmlSpace != XmlSpace.None)
  154. break;
  155. }
  156. return xmlSpace;
  157. }
  158. }
  159. #endregion
  160. #region Methods
  161. private void AddMissingElementXmlns ()
  162. {
  163. // output namespace declaration if not exist.
  164. string prefix = openElementPrefix;
  165. string ns = openElementNS;
  166. openElementPrefix = null;
  167. openElementNS = null;
  168. // LAMESPEC: If prefix was already assigned another nsuri, then this element's nsuri goes away!
  169. if (this.shouldCheckElementXmlns) {
  170. string formatXmlns = String.Empty;
  171. if (userWrittenNamespaces [prefix] == null) {
  172. if (prefix != string.Empty) {
  173. w.Write (" xmlns:");
  174. w.Write (prefix);
  175. w.Write ('=');
  176. w.Write (quoteChar);
  177. w.Write (EscapeString (ns, false));
  178. w.Write (quoteChar);
  179. }
  180. else {
  181. w.Write (" xmlns=");
  182. w.Write (quoteChar);
  183. w.Write (EscapeString (ns, false));
  184. w.Write (quoteChar);
  185. }
  186. }
  187. shouldCheckElementXmlns = false;
  188. }
  189. if (newAttributeNamespaces.Count > 0)
  190. {
  191. foreach (DictionaryEntry ent in newAttributeNamespaces)
  192. {
  193. string ans = (string) ent.Value;
  194. string aprefix = (string) ent.Key;
  195. if (namespaceManager.LookupNamespace (aprefix) == ans)
  196. continue;
  197. w.Write (" xmlns:");
  198. w.Write (aprefix);
  199. w.Write ('=');
  200. w.Write (quoteChar);
  201. w.Write (EscapeString (ans, false));
  202. w.Write (quoteChar);
  203. }
  204. newAttributeNamespaces.Clear ();
  205. }
  206. }
  207. private void CheckState ()
  208. {
  209. if (!openWriter) {
  210. throw new InvalidOperationException ("The Writer is closed.");
  211. }
  212. if ((documentStarted == true) && (formatting == Formatting.Indented) && (!IndentingOverriden)) {
  213. indentFormatting = w.NewLine;
  214. if (indentLevel > 0) {
  215. for (int i = 0; i < indentLevel; i++)
  216. indentFormatting += indentChars;
  217. }
  218. }
  219. else
  220. indentFormatting = "";
  221. documentStarted = true;
  222. }
  223. public override void Close ()
  224. {
  225. CloseOpenAttributeAndElements ();
  226. w.Close();
  227. ws = WriteState.Closed;
  228. openWriter = false;
  229. }
  230. private void CloseOpenAttributeAndElements ()
  231. {
  232. if (openAttribute)
  233. WriteEndAttribute ();
  234. while (openElementCount > 0) {
  235. WriteEndElement();
  236. }
  237. }
  238. private void CloseStartElement ()
  239. {
  240. if (!openStartElement)
  241. return;
  242. AddMissingElementXmlns ();
  243. w.Write (">");
  244. ws = WriteState.Content;
  245. openStartElement = false;
  246. attributeWrittenForElement = false;
  247. newAttributeNamespaces.Clear ();
  248. userWrittenNamespaces.Clear ();
  249. }
  250. public override void Flush ()
  251. {
  252. w.Flush ();
  253. }
  254. public override string LookupPrefix (string ns)
  255. {
  256. if (ns == null || ns == String.Empty)
  257. throw new ArgumentException ("The Namespace cannot be empty.");
  258. string prefix = namespaceManager.LookupPrefix (ns);
  259. // XmlNamespaceManager has changed to return null when NSURI not found.
  260. // (Contradiction to the ECMA documentation.)
  261. return prefix;
  262. }
  263. private void UpdateIndentChars ()
  264. {
  265. indentChars = "";
  266. for (int i = 0; i < indentation; i++)
  267. indentChars += indentChar;
  268. }
  269. public override void WriteBase64 (byte[] buffer, int index, int count)
  270. {
  271. CheckState ();
  272. if (!openAttribute) {
  273. IndentingOverriden = true;
  274. CloseStartElement ();
  275. }
  276. w.Write (Convert.ToBase64String (buffer, index, count));
  277. }
  278. public override void WriteBinHex (byte[] buffer, int index, int count)
  279. {
  280. CheckState ();
  281. if (!openAttribute) {
  282. IndentingOverriden = true;
  283. CloseStartElement ();
  284. }
  285. if (index < 0)
  286. throw new ArgumentOutOfRangeException ("index", index, "index must be non negative integer.");
  287. if (count < 0)
  288. throw new ArgumentOutOfRangeException ("count", count, "count must be non negative integer.");
  289. if (buffer.Length < index + count)
  290. throw new ArgumentOutOfRangeException ("index and count must be smaller than the length of the buffer.");
  291. for (int i = index; i < count; i++) {
  292. int val = buffer [i];
  293. int high = val >> 4;
  294. int low = val & 15;
  295. if (high > 9)
  296. w.Write ((char) (high + 55));
  297. else
  298. w.Write ((char) (high + 0x30));
  299. if (low > 9)
  300. w.Write ((char) (low + 55));
  301. else
  302. w.Write ((char) (low + 0x30));
  303. }
  304. }
  305. public override void WriteCData (string text)
  306. {
  307. if (text.IndexOf ("]]>") >= 0)
  308. throw new ArgumentException ();
  309. CheckState ();
  310. IndentingOverriden = true;
  311. CloseStartElement ();
  312. w.Write ("<![CDATA[");
  313. w.Write (text);
  314. w.Write ("]]>");
  315. }
  316. public override void WriteCharEntity (char ch)
  317. {
  318. Int16 intCh = (Int16)ch;
  319. // Make sure the character is not in the surrogate pair
  320. // character range, 0xd800- 0xdfff
  321. if ((intCh >= -10240) && (intCh <= -8193))
  322. throw new ArgumentException ("Surrogate Pair is invalid.");
  323. w.Write("&#x{0:X};", intCh);
  324. }
  325. public override void WriteChars (char[] buffer, int index, int count)
  326. {
  327. CheckState ();
  328. if (!openAttribute) {
  329. IndentingOverriden = true;
  330. CloseStartElement ();
  331. }
  332. w.Write (buffer, index, count);
  333. }
  334. public override void WriteComment (string text)
  335. {
  336. if ((text.EndsWith("-")) || (text.IndexOf("--") > 0)) {
  337. throw new ArgumentException ();
  338. }
  339. CheckState ();
  340. CloseStartElement ();
  341. w.Write ("<!--");
  342. w.Write (text);
  343. w.Write ("-->");
  344. }
  345. public override void WriteDocType (string name, string pubid, string sysid, string subset)
  346. {
  347. if (name == null || name.Trim (XmlChar.WhitespaceChars).Length == 0)
  348. throw new ArgumentException ("Invalid DOCTYPE name", "name");
  349. if (ws == WriteState.Prolog && formatting == Formatting.Indented)
  350. w.WriteLine ();
  351. w.Write ("<!DOCTYPE ");
  352. w.Write (name);
  353. if (pubid != null) {
  354. w.Write (" PUBLIC ");
  355. w.Write (quoteChar);
  356. w.Write (pubid);
  357. w.Write (quoteChar);
  358. w.Write (' ');
  359. w.Write (quoteChar);
  360. w.Write (sysid);
  361. w.Write (quoteChar);
  362. } else if (sysid != null) {
  363. w.Write (" SYSTEM ");
  364. w.Write (quoteChar);
  365. w.Write (sysid);
  366. w.Write (quoteChar);
  367. }
  368. if (subset != null) {
  369. w.Write ('[');
  370. w.Write (subset);
  371. w.Write (']');
  372. }
  373. w.Write('>');
  374. }
  375. public override void WriteEndAttribute ()
  376. {
  377. if (!openAttribute)
  378. throw new InvalidOperationException("Token EndAttribute in state Start would result in an invalid XML document.");
  379. CheckState ();
  380. if (openXmlLang) {
  381. w.Write (xmlLang);
  382. openXmlLang = false;
  383. ((XmlTextWriterOpenElement) openElements [openElementCount - 1]).XmlLang = xmlLang;
  384. }
  385. if (openXmlSpace)
  386. {
  387. if (xmlSpace == XmlSpace.Preserve)
  388. w.Write ("preserve");
  389. else if (xmlSpace == XmlSpace.Default)
  390. w.Write ("default");
  391. openXmlSpace = false;
  392. ((XmlTextWriterOpenElement) openElements [openElementCount - 1]).XmlSpace = xmlSpace;
  393. }
  394. w.Write (quoteChar);
  395. openAttribute = false;
  396. if (saveAttributeValue) {
  397. if (savedAttributePrefix.Length > 0 && savingAttributeValue.Length == 0)
  398. throw new ArgumentException ("Cannot use prefix with an empty namespace.");
  399. // add namespace
  400. if (shouldAddSavedNsToManager) // not OLD one
  401. namespaceManager.AddNamespace (savedAttributePrefix, savingAttributeValue);
  402. userWrittenNamespaces [savedAttributePrefix] = savingAttributeValue;
  403. saveAttributeValue = false;
  404. savedAttributePrefix = String.Empty;
  405. savingAttributeValue = String.Empty;
  406. }
  407. }
  408. public override void WriteEndDocument ()
  409. {
  410. CloseOpenAttributeAndElements ();
  411. if (!hasRoot)
  412. throw new ArgumentException ("This document does not have a root element.");
  413. ws = WriteState.Start;
  414. hasRoot = false;
  415. }
  416. public override void WriteEndElement ()
  417. {
  418. WriteEndElementInternal (false);
  419. }
  420. private void WriteEndElementInternal (bool fullEndElement)
  421. {
  422. if (openElementCount == 0)
  423. throw new InvalidOperationException("There was no XML start tag open.");
  424. if (openAttribute)
  425. WriteEndAttribute ();
  426. indentLevel--;
  427. CheckState ();
  428. AddMissingElementXmlns ();
  429. if (openStartElement) {
  430. if (openAttribute)
  431. WriteEndAttribute ();
  432. if (fullEndElement) {
  433. w.Write ('>');
  434. w.Write (indentFormatting);
  435. w.Write ("</");
  436. XmlTextWriterOpenElement el = (XmlTextWriterOpenElement) openElements [openElementCount - 1];
  437. if (el.Prefix != String.Empty) {
  438. w.Write (el.Prefix);
  439. w.Write (':');
  440. }
  441. w.Write (el.LocalName);
  442. w.Write ('>');
  443. } else
  444. w.Write (" />");
  445. openElementCount--;
  446. openStartElement = false;
  447. } else {
  448. w.Write (indentFormatting);
  449. w.Write ("</");
  450. XmlTextWriterOpenElement el = (XmlTextWriterOpenElement) openElements [openElementCount - 1];
  451. openElementCount--;
  452. if (el.Prefix != String.Empty) {
  453. w.Write (el.Prefix);
  454. w.Write (':');
  455. }
  456. w.Write (el.LocalName);
  457. w.Write ('>');
  458. }
  459. namespaceManager.PopScope();
  460. }
  461. public override void WriteEntityRef (string name)
  462. {
  463. WriteRaw ("&");
  464. WriteStringInternal (name, true);
  465. WriteRaw (";");
  466. }
  467. public override void WriteFullEndElement ()
  468. {
  469. WriteEndElementInternal (true);
  470. }
  471. public override void WriteName (string name)
  472. {
  473. if (!XmlChar.IsName (name))
  474. throw new ArgumentException ("There is an invalid character: '" + name [0] +
  475. "'", "name");
  476. w.Write (name);
  477. }
  478. public override void WriteNmToken (string name)
  479. {
  480. if (!XmlChar.IsNmToken (name))
  481. throw new ArgumentException ("There is an invalid character: '" + name [0] +
  482. "'", "name");
  483. w.Write (name);
  484. }
  485. // LAMESPEC: It should reject such name that starts with "x" "m" "l" by XML specification, but
  486. // in fact it is used to write XmlDeclaration in WriteNode() (and it is inevitable since
  487. // WriteStartDocument() cannot specify encoding, while WriteNode() can write it).
  488. public override void WriteProcessingInstruction (string name, string text)
  489. {
  490. if ((name == null) || (name == string.Empty))
  491. throw new ArgumentException ();
  492. if (!XmlChar.IsName (name))
  493. throw new ArgumentException ("Invalid processing instruction name.");
  494. if ((text.IndexOf("?>") > 0))
  495. throw new ArgumentException ("Processing instruction cannot contain \"?>\" as its value.");
  496. CheckState ();
  497. CloseStartElement ();
  498. w.Write (indentFormatting);
  499. w.Write ("<?");
  500. w.Write (name);
  501. w.Write (' ');
  502. w.Write (text);
  503. w.Write ("?>");
  504. }
  505. public override void WriteQualifiedName (string localName, string ns)
  506. {
  507. if (localName == null || localName == String.Empty)
  508. throw new ArgumentException ();
  509. CheckState ();
  510. if (!openAttribute)
  511. CloseStartElement ();
  512. w.Write (namespaceManager.LookupPrefix (ns));
  513. w.Write (':');
  514. w.Write (localName);
  515. }
  516. public override void WriteRaw (string data)
  517. {
  518. WriteStringInternal (data, false);
  519. }
  520. public override void WriteRaw (char[] buffer, int index, int count)
  521. {
  522. // WriteRawInternal (new string (buffer, index, count));
  523. WriteStringInternal (new string (buffer, index, count), false);
  524. }
  525. public override void WriteStartAttribute (string prefix, string localName, string ns)
  526. {
  527. if (prefix == "xml") {
  528. // MS.NET looks to allow other names than
  529. // lang and space (e.g. xml:link, xml:hack).
  530. ns = XmlNamespaceManager.XmlnsXml;
  531. if (localName == "lang")
  532. openXmlLang = true;
  533. else if (localName == "space")
  534. openXmlSpace = true;
  535. }
  536. if (prefix == null)
  537. prefix = String.Empty;
  538. if (prefix.Length > 0 && (ns == null || ns.Length == 0))
  539. if (prefix != "xmlns")
  540. throw new ArgumentException ("Cannot use prefix with an empty namespace.");
  541. if ((prefix == "xmlns") && (localName.ToLower ().StartsWith ("xml")))
  542. throw new ArgumentException ("Prefixes beginning with \"xml\" (regardless of whether the characters are uppercase, lowercase, or some combination thereof) are reserved for use by XML: " + prefix + ":" + localName);
  543. // Note that null namespace with "xmlns" are allowed.
  544. #if NET_1_0
  545. if ((prefix == "xmlns" || localName == "xmlns" && prefix == String.Empty) && ns != XmlnsNamespace)
  546. #else
  547. if ((prefix == "xmlns" || localName == "xmlns" && prefix == String.Empty) && ns != null && ns != XmlnsNamespace)
  548. #endif
  549. throw new ArgumentException (String.Format ("The 'xmlns' attribute is bound to the reserved namespace '{0}'", XmlnsNamespace));
  550. CheckState ();
  551. if (ws == WriteState.Content)
  552. throw new InvalidOperationException ("Token StartAttribute in state " + WriteState + " would result in an invalid XML document.");
  553. if (prefix == null)
  554. prefix = String.Empty;
  555. if (ns == null)
  556. ns = String.Empty;
  557. string formatPrefix = "";
  558. string formatSpace = "";
  559. if (ns != String.Empty && prefix != "xmlns") {
  560. string existingPrefix = namespaceManager.LookupPrefix (ns);
  561. if (existingPrefix == null || existingPrefix == "") {
  562. bool createPrefix = false;
  563. if (prefix == "")
  564. createPrefix = true;
  565. else {
  566. string existingNs = namespaceManager.LookupNamespace (prefix);
  567. if (existingNs != null) {
  568. namespaceManager.RemoveNamespace (prefix, existingNs);
  569. if (namespaceManager.LookupNamespace (prefix) != existingNs) {
  570. createPrefix = true;
  571. namespaceManager.AddNamespace (prefix, existingNs);
  572. }
  573. }
  574. }
  575. if (createPrefix)
  576. prefix = "d" + indentLevel + "p" + (newAttributeNamespaces.Count + 1);
  577. // check if prefix exists. If yes - check if namespace is the same.
  578. if (newAttributeNamespaces [prefix] == null)
  579. newAttributeNamespaces.Add (prefix, ns);
  580. else if (!newAttributeNamespaces [prefix].Equals (ns))
  581. throw new ArgumentException ("Duplicate prefix with different namespace");
  582. }
  583. if (prefix == String.Empty && ns != XmlnsNamespace)
  584. prefix = (existingPrefix == null) ?
  585. String.Empty : existingPrefix;
  586. }
  587. if (prefix != String.Empty)
  588. {
  589. formatPrefix = prefix + ":";
  590. }
  591. if (openStartElement || attributeWrittenForElement)
  592. formatSpace = " ";
  593. w.Write (formatSpace);
  594. w.Write (formatPrefix);
  595. w.Write (localName);
  596. w.Write ('=');
  597. w.Write (quoteChar);
  598. openAttribute = true;
  599. attributeWrittenForElement = true;
  600. ws = WriteState.Attribute;
  601. if (prefix == "xmlns" || prefix == String.Empty && localName == "xmlns") {
  602. if (prefix != openElementPrefix || openElementNS == null)
  603. shouldAddSavedNsToManager = true;
  604. saveAttributeValue = true;
  605. savedAttributePrefix = (prefix == "xmlns") ? localName : String.Empty;
  606. savingAttributeValue = String.Empty;
  607. }
  608. }
  609. public override void WriteStartDocument ()
  610. {
  611. WriteStartDocument ("");
  612. }
  613. public override void WriteStartDocument (bool standalone)
  614. {
  615. string standaloneFormatting;
  616. if (standalone == true)
  617. standaloneFormatting = String.Format (" standalone={0}yes{0}", quoteChar);
  618. else
  619. standaloneFormatting = String.Format (" standalone={0}no{0}", quoteChar);
  620. WriteStartDocument (standaloneFormatting);
  621. }
  622. private void WriteStartDocument (string standaloneFormatting)
  623. {
  624. if (documentStarted == true)
  625. throw new InvalidOperationException("WriteStartDocument should be the first call.");
  626. if (hasRoot)
  627. throw new XmlException ("WriteStartDocument called twice.");
  628. CheckState ();
  629. string encodingFormatting = "";
  630. if (!nullEncoding)
  631. encodingFormatting = String.Format (" encoding={0}{1}{0}", quoteChar, w.Encoding.WebName);
  632. w.Write("<?xml version={0}1.0{0}{1}{2}?>", quoteChar, encodingFormatting, standaloneFormatting);
  633. ws = WriteState.Prolog;
  634. }
  635. public override void WriteStartElement (string prefix, string localName, string ns)
  636. {
  637. if (!Namespaces && (((prefix != null) && (prefix != String.Empty))
  638. || ((ns != null) && (ns != String.Empty))))
  639. throw new ArgumentException ("Cannot set the namespace if Namespaces is 'false'.");
  640. if ((prefix != null && prefix != String.Empty) && ((ns == null) || (ns == String.Empty)))
  641. throw new ArgumentException ("Cannot use a prefix with an empty namespace.");
  642. // ignore non-namespaced node's prefix.
  643. if (ns == null || ns == String.Empty)
  644. prefix = String.Empty;
  645. WriteStartElementInternal (prefix, localName, ns);
  646. }
  647. private void WriteStartElementInternal (string prefix, string localName, string ns)
  648. {
  649. hasRoot = true;
  650. CheckState ();
  651. CloseStartElement ();
  652. newAttributeNamespaces.Clear ();
  653. userWrittenNamespaces.Clear ();
  654. shouldCheckElementXmlns = false;
  655. if (prefix == null && ns != null)
  656. prefix = namespaceManager.LookupPrefix (ns);
  657. if (prefix == null)
  658. prefix = String.Empty;
  659. w.Write (indentFormatting);
  660. w.Write ('<');
  661. if (prefix != String.Empty) {
  662. w.Write (prefix);
  663. w.Write (':');
  664. }
  665. w.Write (localName);
  666. if (openElements.Count == openElementCount)
  667. openElements.Add (new XmlTextWriterOpenElement (prefix, localName));
  668. else
  669. ((XmlTextWriterOpenElement) openElements [openElementCount]).Reset (prefix, localName);
  670. openElementCount++;
  671. ws = WriteState.Element;
  672. openStartElement = true;
  673. openElementNS = ns;
  674. openElementPrefix = prefix;
  675. namespaceManager.PushScope ();
  676. indentLevel++;
  677. if (ns != null) {
  678. if (ns.Length > 0) {
  679. string existing = LookupPrefix (ns);
  680. if (existing != prefix) {
  681. shouldCheckElementXmlns = true;
  682. namespaceManager.AddNamespace (prefix, ns);
  683. }
  684. } else {
  685. if (ns != namespaceManager.DefaultNamespace) {
  686. shouldCheckElementXmlns = true;
  687. namespaceManager.AddNamespace ("", ns);
  688. }
  689. }
  690. }
  691. }
  692. public override void WriteString (string text)
  693. {
  694. if (ws == WriteState.Prolog)
  695. throw new InvalidOperationException ("Token content in state Prolog would result in an invalid XML document.");
  696. WriteStringInternal (text, true);
  697. // MS.NET (1.0) saves attribute value only at WriteString.
  698. if (saveAttributeValue)
  699. // In most cases it will be called one time, so simply use string + string.
  700. savingAttributeValue += text;
  701. }
  702. string [] replacements = new string [] {
  703. "&amp;", "&lt;", "&gt;", "&quot;", "&apos;",
  704. "&#xD;", "&#xA;"};
  705. private string EscapeString (string source, bool skipQuotations)
  706. {
  707. int start = 0;
  708. int pos = 0;
  709. int count = source.Length;
  710. for (int i = 0; i < count; i++) {
  711. switch (source [i]) {
  712. case '&': pos = 0; break;
  713. case '<': pos = 1; break;
  714. case '>': pos = 2; break;
  715. case '\"':
  716. if (skipQuotations) continue;
  717. if (QuoteChar == '\'') continue;
  718. pos = 3; break;
  719. case '\'':
  720. if (skipQuotations) continue;
  721. if (QuoteChar == '\"') continue;
  722. pos = 4; break;
  723. case '\r':
  724. if (skipQuotations) continue;
  725. pos = 5; break;
  726. case '\n':
  727. if (skipQuotations) continue;
  728. pos = 6; break;
  729. default:
  730. continue;
  731. }
  732. if (cachedStringBuilder == null)
  733. cachedStringBuilder = new StringBuilder ();
  734. cachedStringBuilder.Append (source.Substring (start, i - start));
  735. cachedStringBuilder.Append (replacements [pos]);
  736. start = i + 1;
  737. }
  738. if (start == 0)
  739. return source;
  740. else if (start < count)
  741. cachedStringBuilder.Append (source.Substring (start, count - start));
  742. string s = cachedStringBuilder.ToString ();
  743. cachedStringBuilder.Length = 0;
  744. return s;
  745. }
  746. private void WriteStringInternal (string text, bool entitize)
  747. {
  748. if (text == null)
  749. text = String.Empty;
  750. if (text != String.Empty) {
  751. CheckState ();
  752. if (entitize)
  753. text = EscapeString (text, !openAttribute);
  754. if (!openAttribute)
  755. {
  756. IndentingOverriden = true;
  757. CloseStartElement ();
  758. }
  759. if (!openXmlLang && !openXmlSpace)
  760. w.Write (text);
  761. else
  762. {
  763. if (openXmlLang)
  764. xmlLang = text;
  765. else
  766. {
  767. switch (text)
  768. {
  769. case "default":
  770. xmlSpace = XmlSpace.Default;
  771. break;
  772. case "preserve":
  773. xmlSpace = XmlSpace.Preserve;
  774. break;
  775. default:
  776. throw new ArgumentException ("'{0}' is an invalid xml:space value.");
  777. }
  778. }
  779. }
  780. }
  781. }
  782. public override void WriteSurrogateCharEntity (char lowChar, char highChar)
  783. {
  784. if (lowChar < '\uDC00' || lowChar > '\uDFFF' ||
  785. highChar < '\uD800' || highChar > '\uDBFF')
  786. throw new ArgumentException ("Invalid (low, high) pair of characters was specified.");
  787. CheckState ();
  788. if (!openAttribute) {
  789. IndentingOverriden = true;
  790. CloseStartElement ();
  791. }
  792. w.Write ("&#x");
  793. w.Write (((int) ((highChar - 0xD800) * 0x400 + (lowChar - 0xDC00) + 0x10000)).ToString ("X"));
  794. w.Write (';');
  795. }
  796. public override void WriteWhitespace (string ws)
  797. {
  798. if (!XmlChar.IsWhitespace (ws))
  799. throw new ArgumentException ("Invalid Whitespace");
  800. CheckState ();
  801. if (!openAttribute) {
  802. IndentingOverriden = true;
  803. CloseStartElement ();
  804. }
  805. w.Write (ws);
  806. }
  807. #endregion
  808. }
  809. }