XmlTextWriter.cs 25 KB

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