XmlTextWriter.cs 28 KB

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