XmlNodeReader.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. //
  2. // System.Xml.XmlNodeReader.cs
  3. //
  4. // Author:
  5. // Duncan Mak ([email protected])
  6. // Atsushi Enomoto ([email protected])
  7. //
  8. // (C) Ximian, Inc.
  9. // (C) Atsushi Enomoto
  10. //
  11. using System;
  12. using System.Collections;
  13. using System.Xml;
  14. using System.Text;
  15. namespace System.Xml
  16. {
  17. public class XmlNodeReader : XmlReader
  18. {
  19. #region Constructor
  20. XmlNode startNode;
  21. XmlNode current;
  22. ReadState state = ReadState.Initial;
  23. int depth;
  24. bool isEndElement;
  25. bool isEndEntity;
  26. bool nextIsEndElement; // used for ReadString()
  27. bool alreadyRead;
  28. StringBuilder valueBuilder = new StringBuilder ();
  29. public XmlNodeReader (XmlNode node)
  30. {
  31. startNode = node;
  32. if (node.NodeType != XmlNodeType.Document
  33. && node.NodeType != XmlNodeType.DocumentFragment)
  34. alreadyRead = true;
  35. }
  36. #endregion
  37. #region Properties
  38. public override int AttributeCount {
  39. get {
  40. if (current == null || current.Attributes == null)
  41. return 0;
  42. return current.Attributes.Count;
  43. }
  44. }
  45. public override string BaseURI {
  46. get {
  47. if (current == null)
  48. return String.Empty;
  49. return current.BaseURI;
  50. }
  51. }
  52. [MonoTODO("wait for XML resolver")]
  53. public override bool CanResolveEntity {
  54. get {
  55. throw new NotImplementedException ();
  56. }
  57. }
  58. public override int Depth {
  59. get { return depth; }
  60. }
  61. public override bool EOF {
  62. get {
  63. return this.ReadState == ReadState.EndOfFile
  64. || this.ReadState == ReadState.Error;
  65. }
  66. }
  67. public override bool HasAttributes {
  68. get {
  69. if (current == null)
  70. return false;
  71. if (current.Attributes == null ||
  72. current.Attributes.Count == 0)
  73. return false;
  74. else
  75. return true;
  76. }
  77. }
  78. public override bool HasValue {
  79. get {
  80. if (current == null)
  81. return false;
  82. if (current.NodeType == XmlNodeType.Element ||
  83. current.NodeType == XmlNodeType.EntityReference ||
  84. current.NodeType == XmlNodeType.Document ||
  85. current.NodeType == XmlNodeType.DocumentFragment ||
  86. current.NodeType == XmlNodeType.Notation ||
  87. current.NodeType == XmlNodeType.EndElement ||
  88. current.NodeType == XmlNodeType.EndEntity)
  89. return false;
  90. else
  91. return true;
  92. }
  93. }
  94. [MonoTODO("waiting for DTD implementation")]
  95. public override bool IsDefault {
  96. get {
  97. if (current == null)
  98. return false;
  99. if (current.NodeType != XmlNodeType.Attribute)
  100. return false;
  101. else
  102. {
  103. return ((XmlAttribute) current).isDefault;
  104. }
  105. }
  106. }
  107. public override bool IsEmptyElement {
  108. get {
  109. if (current == null)
  110. return false;
  111. if(current.NodeType == XmlNodeType.Element)
  112. return ((XmlElement) current).IsEmpty;
  113. else
  114. return false;
  115. }
  116. }
  117. public override string this [int i] {
  118. get {
  119. if (current == null)
  120. return null;
  121. if (i < 0 || i > AttributeCount)
  122. throw new ArgumentOutOfRangeException ("i is out of range.");
  123. return current.Attributes [i].Value;
  124. }
  125. }
  126. public override string this [string name] {
  127. get {
  128. if (current == null)
  129. return null;
  130. XmlAttribute attr = current.Attributes [name];
  131. if (attr == null)
  132. return String.Empty;
  133. else
  134. return attr.Value;
  135. }
  136. }
  137. public override string this [string name, string namespaceURI] {
  138. get {
  139. if (current == null)
  140. return null;
  141. XmlAttribute attr = current.Attributes [name, namespaceURI];
  142. if (attr == null)
  143. return String.Empty;
  144. else
  145. return attr.Value;
  146. }
  147. }
  148. public override string LocalName {
  149. get {
  150. if (current == null)
  151. return String.Empty;
  152. switch (current.NodeType) {
  153. case XmlNodeType.Attribute:
  154. case XmlNodeType.DocumentType:
  155. case XmlNodeType.Element:
  156. case XmlNodeType.EntityReference:
  157. case XmlNodeType.ProcessingInstruction:
  158. case XmlNodeType.XmlDeclaration:
  159. return current.LocalName;
  160. }
  161. return String.Empty;
  162. }
  163. }
  164. public override string Name {
  165. get {
  166. if (current == null)
  167. return String.Empty;
  168. switch (current.NodeType) {
  169. case XmlNodeType.Attribute:
  170. case XmlNodeType.DocumentType:
  171. case XmlNodeType.Element:
  172. case XmlNodeType.EntityReference:
  173. case XmlNodeType.ProcessingInstruction:
  174. case XmlNodeType.XmlDeclaration:
  175. return current.Name;
  176. }
  177. return String.Empty;
  178. }
  179. }
  180. public override string NamespaceURI {
  181. get {
  182. if (current == null)
  183. return String.Empty;
  184. return current.NamespaceURI;
  185. }
  186. }
  187. public override XmlNameTable NameTable {
  188. get {
  189. XmlDocument doc =
  190. current.NodeType == XmlNodeType.Document ?
  191. current as XmlDocument : current.OwnerDocument;
  192. return doc.NameTable;
  193. }
  194. }
  195. public override XmlNodeType NodeType {
  196. get {
  197. if (current == null)
  198. return XmlNodeType.None;
  199. return isEndElement ? XmlNodeType.EndElement : current.NodeType;
  200. }
  201. }
  202. public override string Prefix {
  203. get {
  204. if (current == null)
  205. return String.Empty;
  206. return current.Prefix;
  207. }
  208. }
  209. public override char QuoteChar {
  210. get { return '"'; }
  211. }
  212. public override ReadState ReadState {
  213. get { return state; }
  214. }
  215. public override string Value {
  216. get {
  217. return HasValue ? current.Value : String.Empty;
  218. }
  219. }
  220. public override string XmlLang {
  221. get {
  222. if (current == null)
  223. return String.Empty;
  224. return current.XmlLang;
  225. }
  226. }
  227. public override XmlSpace XmlSpace {
  228. get {
  229. if (current == null)
  230. return XmlSpace.None;
  231. return current.XmlSpace;
  232. }
  233. }
  234. #endregion
  235. #region Methods
  236. public override void Close ()
  237. {
  238. current = null;
  239. state = ReadState.Closed;
  240. }
  241. public override string GetAttribute (int attributeIndex)
  242. {
  243. return this [attributeIndex];
  244. }
  245. public override string GetAttribute (string name)
  246. {
  247. return this [name];
  248. }
  249. public override string GetAttribute (string name, string namespaceURI)
  250. {
  251. return this [name, namespaceURI];
  252. }
  253. // FIXME: Its performance is not good.
  254. public override string LookupNamespace (string prefix)
  255. {
  256. XmlNamespaceManager nsmgr = current.ConstructNamespaceManager();
  257. return nsmgr.LookupNamespace (prefix);
  258. }
  259. public override void MoveToAttribute (int attributeIndex)
  260. {
  261. if (attributeIndex < 0 || attributeIndex > AttributeCount)
  262. throw new ArgumentOutOfRangeException ();
  263. state = ReadState.Interactive;
  264. current = current.Attributes [attributeIndex];
  265. }
  266. public override bool MoveToAttribute (string name)
  267. {
  268. if (GetAttribute (name) == null)
  269. return false;
  270. else {
  271. current = current.Attributes [name];
  272. return true;
  273. }
  274. }
  275. public override bool MoveToAttribute (string name, string namespaceURI)
  276. {
  277. if (GetAttribute (name, namespaceURI) == null)
  278. return false;
  279. else {
  280. current = current.Attributes [name, namespaceURI];
  281. return true;
  282. }
  283. }
  284. private void MoveToEndElement ()
  285. {
  286. isEndElement = true;
  287. depth--;
  288. current = current.ParentNode;
  289. }
  290. public override bool MoveToElement ()
  291. {
  292. if (current == null)
  293. return false;
  294. if (current.NodeType == XmlNodeType.Attribute) {
  295. current = ((XmlAttribute) current).OwnerElement;
  296. return true;
  297. } else
  298. return false;
  299. }
  300. public override bool MoveToFirstAttribute ()
  301. {
  302. if(current.Attributes.Count > 0)
  303. {
  304. current = current.Attributes [0];
  305. return true;
  306. }
  307. else
  308. return false;
  309. }
  310. public override bool MoveToNextAttribute ()
  311. {
  312. if (current.NodeType != XmlNodeType.Attribute)
  313. return MoveToFirstAttribute ();
  314. else
  315. {
  316. XmlAttributeCollection ac = ((XmlAttribute) current).OwnerElement.Attributes;
  317. for (int i=0; i<ac.Count-1; i++)
  318. {
  319. XmlAttribute attr = ac [i];
  320. if (attr == current)
  321. {
  322. current = ac [i+1];
  323. return true;
  324. }
  325. }
  326. return false;
  327. }
  328. }
  329. private bool MoveToNextSibling ()
  330. {
  331. if (nextIsEndElement) {
  332. // nextIsEndElement is set only by ReadString.
  333. nextIsEndElement = false;
  334. MoveToEndElement ();
  335. } else if (alreadyRead) {
  336. alreadyRead = false;
  337. return current != null;
  338. }
  339. if (current.NextSibling != null) {
  340. isEndElement = false;
  341. current = current.NextSibling;
  342. } else {
  343. MoveToEndElement ();
  344. }
  345. return current != null;
  346. }
  347. [MonoTODO("Entity handling is not supported.")]
  348. public override bool Read ()
  349. {
  350. if (EOF)
  351. return false;
  352. if (ReadState == ReadState.Initial) {
  353. current = startNode;
  354. state = ReadState.Interactive;
  355. // when startNode is document or fragment
  356. if (!alreadyRead)
  357. current = startNode.FirstChild;
  358. else
  359. alreadyRead = false;
  360. if (current == null) {
  361. state = ReadState.Error;
  362. return false;
  363. } else
  364. return true;
  365. }
  366. MoveToElement ();
  367. isEndEntity = false;
  368. if (IsEmptyElement || isEndElement) {
  369. // Then go up and move to next.
  370. // If no more nodes, then set EOF.
  371. isEndElement = false;
  372. if (current.ParentNode == null
  373. || current.ParentNode.NodeType == XmlNodeType.Document
  374. || current.ParentNode.NodeType == XmlNodeType.DocumentFragment) {
  375. current = null;
  376. state = ReadState.EndOfFile;
  377. return false;
  378. } else if (current.NextSibling == null) {
  379. depth--;
  380. current = current.ParentNode;
  381. isEndElement = true;
  382. return true;
  383. } else {
  384. current = current.NextSibling;
  385. return true;
  386. }
  387. } else if (nextIsEndElement) {
  388. // nextIsEndElement is set only by ReadString.
  389. nextIsEndElement = false;
  390. isEndElement = true;
  391. return current != null;
  392. } else if (alreadyRead) {
  393. alreadyRead = false;
  394. return current != null;
  395. }
  396. // hmm... here may be unnecessary codes. plz check anyone ;)
  397. if (!isEndElement && current.FirstChild != null) {
  398. isEndElement = false;
  399. current = current.FirstChild;
  400. depth++;
  401. } else if (depth == 0) {
  402. state = ReadState.EndOfFile;
  403. return false;
  404. } else
  405. MoveToNextSibling ();
  406. return current != null;
  407. }
  408. public override bool ReadAttributeValue ()
  409. {
  410. if (current.NodeType == XmlNodeType.Attribute) {
  411. current = current.FirstChild;
  412. return current != null;
  413. } else if (current.ParentNode.NodeType == XmlNodeType.Attribute) {
  414. current = current.NextSibling;
  415. return current != null;
  416. } else
  417. return false;
  418. }
  419. [MonoTODO("Need to move to next content.")]
  420. // Its traversal behavior is almost same as Read().
  421. public override string ReadInnerXml ()
  422. {
  423. if (ReadState == ReadState.Initial) {
  424. state = ReadState.Error;
  425. return String.Empty; // heh
  426. }
  427. if (current.NodeType != XmlNodeType.Attribute &&
  428. current.NodeType != XmlNodeType.Element)
  429. return String.Empty;
  430. else
  431. return current.InnerXml;
  432. }
  433. [MonoTODO("Need to move to next content.")]
  434. // Its traversal behavior is almost same as Read().
  435. public override string ReadOuterXml ()
  436. {
  437. if (NodeType == XmlNodeType.EndElement)
  438. return String.Empty;
  439. if (current.NodeType != XmlNodeType.Attribute &&
  440. current.NodeType != XmlNodeType.Element)
  441. return String.Empty;
  442. else
  443. return current.OuterXml;
  444. }
  445. public override string ReadString ()
  446. {
  447. XmlNode original = current;
  448. valueBuilder.Length = 0;
  449. switch (NodeType) {
  450. default:
  451. return String.Empty;
  452. case XmlNodeType.Element:
  453. if (IsEmptyElement)
  454. return String.Empty;
  455. foreach (XmlNode child in current.ChildNodes) {
  456. if (child is XmlCharacterData && !(child is XmlComment))
  457. valueBuilder.Append (child.Value);
  458. else {
  459. depth++;
  460. current = child;
  461. break;
  462. }
  463. }
  464. alreadyRead = true;
  465. if (current == original) {
  466. nextIsEndElement = true;
  467. Read ();
  468. }
  469. break;
  470. case XmlNodeType.Text:
  471. case XmlNodeType.CDATA:
  472. case XmlNodeType.Whitespace:
  473. case XmlNodeType.SignificantWhitespace:
  474. // [LAMESPEC] It is inconsistent with MS.NET.
  475. // MS ignores current text value, but such
  476. // behaviour is inconsistent with XmlTextReader
  477. // and I think it is bug.
  478. do {
  479. valueBuilder.Append (current.Value);
  480. if (current.NextSibling == null) {
  481. nextIsEndElement = true;
  482. break;
  483. } else if (current.NextSibling.NodeType == XmlNodeType.Comment)
  484. break;
  485. else
  486. current = current.NextSibling;
  487. } while (true);
  488. alreadyRead = true;
  489. if (current.NextSibling == null) {
  490. nextIsEndElement = true;
  491. Read ();
  492. }
  493. break;
  494. }
  495. return valueBuilder.ToString ();
  496. }
  497. [MonoTODO]
  498. public override void ResolveEntity ()
  499. {
  500. throw new NotImplementedException ();
  501. // if (current.NodeType != XmlNodeType.EntityReference)
  502. // throw new InvalidOperationException ("The current node is not an Entity Reference");
  503. }
  504. [MonoTODO("test it.")]
  505. public override void Skip ()
  506. {
  507. MoveToElement ();
  508. if(current.ChildNodes.Count > 0)
  509. MoveToNextSibling ();
  510. else
  511. Read ();
  512. }
  513. #endregion
  514. }
  515. }