XmlTextWriter.cs 25 KB

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