XmlNodeWriter.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. //
  2. // Mono.Xml.XmlNodeWriter
  3. //
  4. // Author:
  5. // Atsushi Enomoto ([email protected])
  6. //
  7. // (C)2003 Atsushi Enomoto
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System;
  30. using System.Xml;
  31. namespace System.Xml
  32. {
  33. internal class XmlNodeWriter : XmlWriter
  34. {
  35. public XmlNodeWriter () : this (true)
  36. {
  37. }
  38. // It should be public after some tests are done :-)
  39. public XmlNodeWriter (bool isDocumentEntity)
  40. {
  41. doc = new XmlDocument ();
  42. state = XmlNodeType.None;
  43. this.isDocumentEntity = isDocumentEntity;
  44. if (!isDocumentEntity)
  45. current = fragment = doc.CreateDocumentFragment ();
  46. }
  47. XmlDocument doc;
  48. bool isClosed;
  49. // If it is not null, then we are now inside the element.
  50. XmlNode current;
  51. // If it is not null, then we are now inside the attribute.
  52. XmlAttribute attribute;
  53. // If it is false, then allow to contain multiple document elements.
  54. bool isDocumentEntity;
  55. XmlDocumentFragment fragment;
  56. // None: started or closed.
  57. // XmlDeclaration: after xmldecl. Never allow xmldecl.
  58. // DocumentType: after doctype. Never allow xmldecl and doctype.
  59. // Element: inside document element.
  60. //
  61. XmlNodeType state;
  62. // Properties
  63. public XmlNode Document {
  64. get { return isDocumentEntity ? (XmlNode)doc : (XmlNode)fragment; }
  65. }
  66. public override WriteState WriteState {
  67. get {
  68. if (isClosed)
  69. return WriteState.Closed;
  70. if (attribute != null)
  71. return WriteState.Attribute;
  72. switch (state) {
  73. case XmlNodeType.None:
  74. return WriteState.Start;
  75. case XmlNodeType.XmlDeclaration:
  76. return WriteState.Prolog;
  77. case XmlNodeType.DocumentType:
  78. return WriteState.Element;
  79. default:
  80. return WriteState.Content;
  81. }
  82. }
  83. }
  84. public override string XmlLang {
  85. get {
  86. for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement)
  87. if (n.HasAttribute ("xml:lang"))
  88. return n.GetAttribute ("xml:lang");
  89. return String.Empty;
  90. }
  91. }
  92. public override XmlSpace XmlSpace {
  93. get {
  94. for (XmlElement n = current as XmlElement; n != null; n = n.ParentNode as XmlElement) {
  95. string xs = n.GetAttribute ("xml:space");
  96. switch (xs) {
  97. case "preserve":
  98. return XmlSpace.Preserve;
  99. case "default":
  100. return XmlSpace.Default;
  101. case "":
  102. continue;
  103. default:
  104. throw new InvalidOperationException (String.Format ("Invalid xml:space {0}.", xs));
  105. }
  106. }
  107. return XmlSpace.None;
  108. }
  109. }
  110. // Private Methods
  111. private void CheckState ()
  112. {
  113. if (isClosed)
  114. throw new InvalidOperationException ();
  115. }
  116. private void WritePossiblyTopLevelNode (XmlNode n, bool possiblyAttribute)
  117. {
  118. CheckState ();
  119. if (!possiblyAttribute && attribute != null)
  120. throw new InvalidOperationException (String.Format ("Current state is not acceptable for {0}.", n.NodeType));
  121. if (state != XmlNodeType.Element)
  122. Document.AppendChild (n);
  123. else if (attribute != null)
  124. attribute.AppendChild (n);
  125. else
  126. current.AppendChild (n);
  127. if (state == XmlNodeType.None)
  128. state = XmlNodeType.XmlDeclaration;
  129. }
  130. // Public Methods
  131. public override void Close ()
  132. {
  133. CheckState ();
  134. isClosed = true;
  135. }
  136. public override void Flush ()
  137. {
  138. }
  139. public override string LookupPrefix (string ns)
  140. {
  141. CheckState ();
  142. if (current == null)
  143. throw new InvalidOperationException ();
  144. return current.GetPrefixOfNamespace (ns);
  145. }
  146. // StartDocument
  147. public override void WriteStartDocument ()
  148. {
  149. WriteStartDocument (null);
  150. }
  151. public override void WriteStartDocument (bool standalone)
  152. {
  153. WriteStartDocument (standalone ? "yes" : "no");
  154. }
  155. private void WriteStartDocument (string sddecl)
  156. {
  157. CheckState ();
  158. if (state != XmlNodeType.None)
  159. throw new InvalidOperationException ("Current state is not acceptable for xmldecl.");
  160. doc.AppendChild (doc.CreateXmlDeclaration ("1.0", null, sddecl));
  161. state = XmlNodeType.XmlDeclaration;
  162. }
  163. // EndDocument
  164. public override void WriteEndDocument ()
  165. {
  166. CheckState ();
  167. isClosed = true;
  168. }
  169. // DocumentType
  170. public override void WriteDocType (string name, string publicId, string systemId, string internalSubset)
  171. {
  172. CheckState ();
  173. switch (state) {
  174. case XmlNodeType.None:
  175. case XmlNodeType.XmlDeclaration:
  176. doc.AppendChild (doc.CreateDocumentType (name, publicId, systemId, internalSubset));
  177. state = XmlNodeType.DocumentType;
  178. break;
  179. default:
  180. throw new InvalidOperationException ("Current state is not acceptable for doctype.");
  181. }
  182. }
  183. // StartElement
  184. public override void WriteStartElement (string prefix, string name, string ns)
  185. {
  186. CheckState ();
  187. if (isDocumentEntity && state == XmlNodeType.EndElement && doc.DocumentElement != null)
  188. throw new InvalidOperationException ("Current state is not acceptable for startElement.");
  189. XmlElement el = doc.CreateElement (prefix, name, ns);
  190. if (current == null) {
  191. Document.AppendChild (el);
  192. state = XmlNodeType.Element;
  193. } else {
  194. current.AppendChild (el);
  195. state = XmlNodeType.Element;
  196. }
  197. current = el;
  198. }
  199. // EndElement
  200. public override void WriteEndElement ()
  201. {
  202. WriteEndElementInternal (false);
  203. }
  204. public override void WriteFullEndElement ()
  205. {
  206. WriteEndElementInternal (true);
  207. }
  208. private void WriteEndElementInternal (bool forceFull)
  209. {
  210. CheckState ();
  211. if (current == null)
  212. throw new InvalidOperationException ("Current state is not acceptable for endElement.");
  213. if (!forceFull && current.FirstChild == null)
  214. ((XmlElement) current).IsEmpty = true;
  215. if (isDocumentEntity && current.ParentNode == doc)
  216. state = XmlNodeType.EndElement;
  217. else
  218. current = current.ParentNode;
  219. }
  220. // StartAttribute
  221. public override void WriteStartAttribute (string prefix, string name, string ns)
  222. {
  223. CheckState ();
  224. if (attribute != null)
  225. throw new InvalidOperationException ("There is an open attribute.");
  226. if (!(current is XmlElement))
  227. throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
  228. attribute = doc.CreateAttribute (prefix, name, ns);
  229. ((XmlElement)current).SetAttributeNode (attribute);
  230. }
  231. public override void WriteEndAttribute ()
  232. {
  233. CheckState ();
  234. if (attribute == null)
  235. throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
  236. attribute = null;
  237. }
  238. public override void WriteCData (string data)
  239. {
  240. CheckState ();
  241. if (current == null)
  242. throw new InvalidOperationException ("Current state is not acceptable for CDATAsection.");
  243. current.AppendChild (doc.CreateCDataSection (data));
  244. }
  245. public override void WriteComment (string comment)
  246. {
  247. WritePossiblyTopLevelNode (doc.CreateComment (comment), false);
  248. }
  249. public override void WriteProcessingInstruction (string name, string value)
  250. {
  251. WritePossiblyTopLevelNode (
  252. doc.CreateProcessingInstruction (name, value), false);
  253. }
  254. public override void WriteEntityRef (string name)
  255. {
  256. WritePossiblyTopLevelNode (doc.CreateEntityReference (name), true);
  257. }
  258. public override void WriteCharEntity (char c)
  259. {
  260. WritePossiblyTopLevelNode (doc.CreateTextNode (new string (new char [] {c}, 0, 1)), true);
  261. }
  262. public override void WriteWhitespace (string ws)
  263. {
  264. WritePossiblyTopLevelNode (doc.CreateWhitespace (ws), true);
  265. }
  266. public override void WriteString (string data)
  267. {
  268. CheckState ();
  269. if (current == null)
  270. throw new InvalidOperationException ("Current state is not acceptable for Text.");
  271. if (attribute != null)
  272. attribute.AppendChild (doc.CreateTextNode (data));
  273. else {
  274. XmlText last = current.LastChild as XmlText;
  275. if (last == null)
  276. current.AppendChild(doc.CreateTextNode(data));
  277. else
  278. last.AppendData(data);
  279. }
  280. }
  281. public override void WriteName (string name)
  282. {
  283. WriteString (name);
  284. }
  285. public override void WriteNmToken (string nmtoken)
  286. {
  287. WriteString (nmtoken);
  288. }
  289. public override void WriteQualifiedName (string name, string ns)
  290. {
  291. string prefix = LookupPrefix (ns);
  292. if (prefix == null)
  293. throw new ArgumentException (String.Format ("Invalid namespace {0}", ns));
  294. if (prefix != String.Empty)
  295. WriteString (name);
  296. else
  297. WriteString (prefix + ":" + name);
  298. }
  299. public override void WriteChars (char [] chars, int start, int len)
  300. {
  301. WriteString (new string (chars, start, len));
  302. }
  303. public override void WriteRaw (string data)
  304. {
  305. // It never supports raw string.
  306. WriteString (data);
  307. }
  308. public override void WriteRaw (char [] chars, int start, int len)
  309. {
  310. // It never supports raw string.
  311. WriteChars (chars, start, len);
  312. }
  313. public override void WriteBase64 (byte [] data, int start, int len)
  314. {
  315. // It never supports raw string.
  316. WriteString (Convert.ToBase64String (data, start, len));
  317. }
  318. public override void WriteBinHex (byte [] data, int start, int len)
  319. {
  320. throw new NotImplementedException ();
  321. }
  322. public override void WriteSurrogateCharEntity (char c1, char c2)
  323. {
  324. throw new NotImplementedException ();
  325. }
  326. }
  327. }