XmlDocumentNavigator.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. //
  2. // System.Xml.XmlDocumentNavigator
  3. //
  4. // Authors:
  5. // Jason Diamond <[email protected]>
  6. // Atsushi Enomoto <[email protected]>
  7. //
  8. // (C) 2002 Jason Diamond
  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.Xml;
  34. using System.Xml.XPath;
  35. namespace System.Xml
  36. {
  37. internal class XmlDocumentNavigator : XPathNavigator, IHasXmlNode
  38. {
  39. #region Constructors
  40. internal XmlDocumentNavigator (XmlNode node)
  41. : this (node, null)
  42. {
  43. nsNodeXml = document.CreateAttribute ("xmlns", "xml", Xmlns);
  44. nsNodeXml.Value = XmlnsXML;
  45. if (node.NodeType == XmlNodeType.Attribute && node.NamespaceURI == XmlNamespaceManager.XmlnsXmlns) {
  46. nsNode = (XmlAttribute) node;
  47. node = nsNode.OwnerElement;
  48. }
  49. }
  50. private XmlDocumentNavigator (XmlNode node, XmlAttribute nsNodeXml)
  51. {
  52. this.node = node;
  53. this.document = node.NodeType == XmlNodeType.Document ?
  54. node as XmlDocument : node.OwnerDocument;
  55. this.nsNodeXml = nsNodeXml;
  56. }
  57. #endregion
  58. #region Fields
  59. private const string Xmlns = "http://www.w3.org/2000/xmlns/";
  60. private const string XmlnsXML = "http://www.w3.org/XML/1998/namespace";
  61. private XmlAttribute nsNodeXml;
  62. private XmlNode node;
  63. private XmlDocument document;
  64. // Current namespace node (ancestor's attribute of current node).
  65. private XmlAttribute nsNode;
  66. private ArrayList iteratedNsNames;
  67. #endregion
  68. #region Properties
  69. public override string BaseURI {
  70. get {
  71. return node.BaseURI;
  72. }
  73. }
  74. public override bool HasAttributes {
  75. get {
  76. if (NsNode != null)
  77. return false;
  78. if (node.Attributes != null)
  79. for (int i = 0; i < node.Attributes.Count; i++)
  80. if (node.Attributes [i].NamespaceURI != Xmlns)
  81. return true;
  82. return false;
  83. }
  84. }
  85. public override bool HasChildren {
  86. get {
  87. if (NsNode != null)
  88. return false;
  89. XPathNodeType nodeType = NodeType;
  90. bool canHaveChildren = nodeType == XPathNodeType.Root || nodeType == XPathNodeType.Element;
  91. return canHaveChildren && GetFirstChild (node) != null;
  92. }
  93. }
  94. public override bool IsEmptyElement {
  95. get {
  96. if (NsNode != null)
  97. return false;
  98. return node.NodeType == XmlNodeType.Element
  99. && ((XmlElement) node).IsEmpty;
  100. }
  101. }
  102. public XmlAttribute NsNode {
  103. get { return nsNode; }
  104. set {
  105. if (value == null) {
  106. if (iteratedNsNames != null)
  107. iteratedNsNames.Clear ();
  108. }
  109. else {
  110. if (iteratedNsNames == null)
  111. iteratedNsNames = new ArrayList ();
  112. iteratedNsNames.Add (value.Name);
  113. }
  114. nsNode = value;
  115. }
  116. }
  117. public override string LocalName {
  118. get {
  119. XmlAttribute nsNode = NsNode;
  120. if (nsNode != null) {
  121. if (nsNode == nsNodeXml)
  122. return "xml";
  123. else
  124. return (nsNode.Name == "xmlns") ? String.Empty : nsNode.LocalName;
  125. }
  126. XPathNodeType nodeType = NodeType;
  127. bool canHaveName =
  128. nodeType == XPathNodeType.Element ||
  129. nodeType == XPathNodeType.Attribute ||
  130. nodeType == XPathNodeType.ProcessingInstruction ||
  131. nodeType == XPathNodeType.Namespace;
  132. return canHaveName ? node.LocalName : String.Empty;
  133. }
  134. }
  135. public override string Name {
  136. get {
  137. if (NsNode != null)
  138. return LocalName;
  139. XPathNodeType nodeType = NodeType;
  140. bool canHaveName =
  141. nodeType == XPathNodeType.Element ||
  142. nodeType == XPathNodeType.Attribute ||
  143. nodeType == XPathNodeType.ProcessingInstruction ||
  144. nodeType == XPathNodeType.Namespace;
  145. return canHaveName ? node.Name : String.Empty;
  146. }
  147. }
  148. public override string NamespaceURI {
  149. get { return (NsNode != null) ? String.Empty : node.NamespaceURI; }
  150. }
  151. public override XmlNameTable NameTable {
  152. get {
  153. return document.NameTable;
  154. }
  155. }
  156. public override XPathNodeType NodeType {
  157. get {
  158. if (NsNode != null)
  159. return XPathNodeType.Namespace;
  160. XmlNode n = node;
  161. bool sw = false;
  162. do {
  163. switch (n.NodeType) {
  164. case XmlNodeType.SignificantWhitespace:
  165. sw = true;
  166. n = GetNextSibling (node);
  167. break;
  168. case XmlNodeType.Whitespace:
  169. n = GetNextSibling (node);
  170. break;
  171. case XmlNodeType.Text:
  172. case XmlNodeType.CDATA:
  173. return XPathNodeType.Text;
  174. default:
  175. n = null;
  176. break;
  177. }
  178. } while (n != null);
  179. return sw ?
  180. XPathNodeType.SignificantWhitespace :
  181. node.XPathNodeType;
  182. }
  183. }
  184. public override string Prefix {
  185. get { return (NsNode != null) ? String.Empty : node.Prefix; }
  186. }
  187. public override string Value {
  188. get {
  189. switch (NodeType) {
  190. case XPathNodeType.Attribute:
  191. case XPathNodeType.Comment:
  192. case XPathNodeType.ProcessingInstruction:
  193. return node.Value;
  194. case XPathNodeType.Text:
  195. case XPathNodeType.Whitespace:
  196. case XPathNodeType.SignificantWhitespace:
  197. string value = node.Value;
  198. for (XmlNode n = GetNextSibling (node); n != null; n = GetNextSibling (n)) {
  199. switch (n.XPathNodeType) {
  200. case XPathNodeType.Text:
  201. case XPathNodeType.Whitespace:
  202. case XPathNodeType.SignificantWhitespace:
  203. value += n.Value;
  204. continue;
  205. }
  206. break;
  207. }
  208. return value;
  209. case XPathNodeType.Element:
  210. case XPathNodeType.Root:
  211. return node.InnerText;
  212. case XPathNodeType.Namespace:
  213. return NsNode == nsNodeXml ? XmlnsXML : NsNode.Value;
  214. }
  215. return String.Empty;
  216. }
  217. }
  218. public override string XmlLang {
  219. get {
  220. return node.XmlLang;
  221. }
  222. }
  223. #endregion
  224. #region Methods
  225. private bool CheckNsNameAppearance (string name, string ns)
  226. {
  227. if (iteratedNsNames != null && iteratedNsNames.Contains (name))
  228. return true;
  229. // default namespace erasure - just add name and never return this node
  230. if (ns == String.Empty) {
  231. if (iteratedNsNames == null)
  232. iteratedNsNames = new ArrayList ();
  233. iteratedNsNames.Add ("xmlns");
  234. return true;
  235. }
  236. return false;
  237. }
  238. public override XPathNavigator Clone ()
  239. {
  240. XmlDocumentNavigator clone = new XmlDocumentNavigator (node, nsNodeXml);
  241. clone.nsNode = nsNode;
  242. if (iteratedNsNames != null)
  243. clone.iteratedNsNames = (ArrayList) iteratedNsNames.Clone ();
  244. return clone;
  245. }
  246. public override string GetAttribute (string localName, string namespaceURI)
  247. {
  248. if (HasAttributes) {
  249. XmlElement el = Node as XmlElement;
  250. return el != null ? el.GetAttribute (localName, namespaceURI) : String.Empty;
  251. }
  252. return String.Empty;
  253. }
  254. public override string GetNamespace (string name)
  255. {
  256. // MSDN says "String.Empty if a matching namespace
  257. // node is not found or if the navigator is not
  258. // positioned on an element node", but in fact it
  259. // returns actual namespace for the other nodes.
  260. return Node.GetNamespaceOfPrefix (name);
  261. }
  262. public override bool IsSamePosition (XPathNavigator other)
  263. {
  264. XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
  265. if (otherDocumentNavigator != null)
  266. return node == otherDocumentNavigator.node
  267. && NsNode == otherDocumentNavigator.NsNode;
  268. return false;
  269. }
  270. public override bool MoveTo (XPathNavigator other)
  271. {
  272. XmlDocumentNavigator otherDocumentNavigator = other as XmlDocumentNavigator;
  273. if (otherDocumentNavigator != null) {
  274. if (document == otherDocumentNavigator.document) {
  275. node = otherDocumentNavigator.node;
  276. NsNode = otherDocumentNavigator.NsNode;
  277. return true;
  278. }
  279. }
  280. return false;
  281. }
  282. public override bool MoveToAttribute (string localName, string namespaceURI)
  283. {
  284. if (node.Attributes != null) {
  285. for (int i = 0; i < node.Attributes.Count; i++) {
  286. XmlAttribute attr = node.Attributes [i];
  287. if (attr.LocalName == localName
  288. && attr.NamespaceURI == namespaceURI) {
  289. node = attr;
  290. NsNode = null;
  291. return true;
  292. }
  293. }
  294. }
  295. return false;
  296. }
  297. public override bool MoveToFirst ()
  298. {
  299. if (NsNode == null && node.NodeType != XmlNodeType.Attribute) {
  300. if (!MoveToParent ())
  301. return false;
  302. // Follow these 2 steps so that we can skip
  303. // some types of nodes .
  304. MoveToFirstChild ();
  305. return true;
  306. }
  307. return false;
  308. }
  309. public override bool MoveToFirstAttribute ()
  310. {
  311. if (node.Attributes == null)
  312. return false;
  313. if (NodeType == XPathNodeType.Element) {
  314. for (int i = 0; i < node.Attributes.Count; i++) {
  315. XmlAttribute attr = node.Attributes [i];
  316. if (attr.NamespaceURI != Xmlns) {
  317. node = attr;
  318. NsNode = null;
  319. return true;
  320. }
  321. }
  322. }
  323. return false;
  324. }
  325. public override bool MoveToFirstChild ()
  326. {
  327. if (HasChildren) {
  328. XmlNode n = GetFirstChild (node);
  329. if (n == null)
  330. return false;
  331. node = n;
  332. return true;
  333. }
  334. return false;
  335. }
  336. public override bool MoveToFirstNamespace (XPathNamespaceScope namespaceScope)
  337. {
  338. if (NodeType != XPathNodeType.Element)
  339. return false;
  340. XmlElement el = node as XmlElement;
  341. if (node.Attributes != null) {
  342. do {
  343. for (int i = 0; i < el.Attributes.Count; i++) {
  344. XmlAttribute attr = el.Attributes [i];
  345. if (attr.NamespaceURI == Xmlns) {
  346. if (CheckNsNameAppearance (attr.Name, attr.Value))
  347. continue;
  348. NsNode = attr;
  349. return true;
  350. }
  351. }
  352. if (namespaceScope == XPathNamespaceScope.Local)
  353. return false;
  354. el = GetParentNode (el) as XmlElement;
  355. } while (el != null);
  356. }
  357. if (namespaceScope == XPathNamespaceScope.All) {
  358. if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
  359. return false;
  360. NsNode = nsNodeXml;
  361. return true;
  362. }
  363. else
  364. return false;
  365. }
  366. public override bool MoveToId (string id)
  367. {
  368. XmlElement eltNew = document.GetElementById (id);
  369. if (eltNew == null)
  370. return false;
  371. node = eltNew;
  372. return true;
  373. }
  374. public override bool MoveToNamespace (string name)
  375. {
  376. if (name == "xml") {
  377. NsNode = nsNodeXml;
  378. return true;
  379. }
  380. if (NodeType != XPathNodeType.Element)
  381. return false;
  382. XmlElement el = node as XmlElement;
  383. if (node.Attributes != null) {
  384. do {
  385. for (int i = 0; i < el.Attributes.Count; i++) {
  386. XmlAttribute attr = el.Attributes [i];
  387. if (attr.NamespaceURI == Xmlns && attr.Name == name) {
  388. NsNode = attr;
  389. return true;
  390. }
  391. }
  392. el = GetParentNode (node) as XmlElement;
  393. } while (el != null);
  394. }
  395. return false;
  396. }
  397. public override bool MoveToNext ()
  398. {
  399. if (NsNode != null)
  400. return false;
  401. XmlNode n = node;
  402. if (NodeType == XPathNodeType.Text) {
  403. do {
  404. n = GetNextSibling (n);
  405. if (n == null)
  406. return false;
  407. switch (n.NodeType) {
  408. case XmlNodeType.CDATA:
  409. case XmlNodeType.SignificantWhitespace:
  410. case XmlNodeType.Text:
  411. case XmlNodeType.Whitespace:
  412. continue;
  413. default:
  414. break;
  415. }
  416. break;
  417. } while (true);
  418. }
  419. else
  420. n = GetNextSibling (n);
  421. if (n == null)
  422. return false;
  423. node = n;
  424. return true;
  425. }
  426. public override bool MoveToNextAttribute ()
  427. {
  428. if (node == null)
  429. return false;
  430. if (NodeType != XPathNodeType.Attribute)
  431. return false;
  432. // Find current attribute.
  433. int pos = 0;
  434. XmlElement owner = ((XmlAttribute) node).OwnerElement;
  435. if (owner == null)
  436. return false;
  437. int count = owner.Attributes.Count;
  438. for(; pos < count; pos++)
  439. if (owner.Attributes [pos] == node)
  440. break;
  441. if (pos == count)
  442. return false; // Where is current attribute? Maybe removed.
  443. // Find next attribute.
  444. for(pos++; pos < count; pos++) {
  445. if (owner.Attributes [pos].NamespaceURI != Xmlns) {
  446. node = owner.Attributes [pos];
  447. NsNode = null;
  448. return true;
  449. }
  450. }
  451. return false;
  452. }
  453. public override bool MoveToNextNamespace (XPathNamespaceScope namespaceScope)
  454. {
  455. if (NsNode == nsNodeXml)
  456. // Current namespace is "xml", so there should be no more namespace nodes.
  457. return false;
  458. if (NsNode == null)
  459. return false;
  460. // Get current attribute's position.
  461. int pos = 0;
  462. XmlElement owner = ((XmlAttribute) NsNode).OwnerElement;
  463. if (owner == null)
  464. return false;
  465. int count = owner.Attributes.Count;
  466. for(; pos < count; pos++)
  467. if (owner.Attributes [pos] == NsNode)
  468. break;
  469. if (pos == count)
  470. return false; // Where is current attribute? Maybe removed.
  471. // Find next namespace from the same element as current ns node.
  472. for(pos++; pos < count; pos++) {
  473. if (owner.Attributes [pos].NamespaceURI == Xmlns) {
  474. XmlAttribute a = owner.Attributes [pos];
  475. if (CheckNsNameAppearance (a.Name, a.Value))
  476. continue;
  477. NsNode = a;
  478. return true;
  479. }
  480. }
  481. // If not found more, then find from ancestors.
  482. // But if scope is Local, then it returns false here.
  483. if (namespaceScope == XPathNamespaceScope.Local)
  484. return false;
  485. owner = GetParentNode (owner) as XmlElement;
  486. while (owner != null) {
  487. for (int i = 0; i < owner.Attributes.Count; i++) {
  488. XmlAttribute attr = owner.Attributes [i];
  489. if (attr.NamespaceURI == Xmlns) {
  490. if (CheckNsNameAppearance (attr.Name, attr.Value))
  491. continue;
  492. NsNode = attr;
  493. return true;
  494. }
  495. }
  496. owner = GetParentNode (owner) as XmlElement;
  497. }
  498. if (namespaceScope == XPathNamespaceScope.All) {
  499. if (CheckNsNameAppearance (nsNodeXml.Name, nsNodeXml.Value))
  500. return false;
  501. NsNode = nsNodeXml;
  502. return true;
  503. }
  504. return false;
  505. }
  506. public override bool MoveToParent ()
  507. {
  508. if (NsNode != null) {
  509. NsNode = null;
  510. return true;
  511. }
  512. else if (node.NodeType == XmlNodeType.Attribute) {
  513. XmlElement ownerElement = ((XmlAttribute)node).OwnerElement;
  514. if (ownerElement != null) {
  515. node = ownerElement;
  516. NsNode = null;
  517. return true;
  518. }
  519. else
  520. return false;
  521. }
  522. XmlNode n = GetParentNode (node);
  523. if (n == null)
  524. return false;
  525. node = n;
  526. NsNode = null;
  527. return true;
  528. }
  529. public override bool MoveToPrevious ()
  530. {
  531. if (NsNode != null)
  532. return false;
  533. XmlNode p = GetPreviousSibling (node);
  534. if (p == null)
  535. return false;
  536. node = p;
  537. return true;
  538. }
  539. public override void MoveToRoot ()
  540. {
  541. XmlAttribute attr = node as XmlAttribute;
  542. XmlNode tmp = attr != null ? attr.OwnerElement : node;
  543. for (XmlNode tmp2 = GetParentNode (tmp); tmp2 != null; tmp2 = GetParentNode (tmp2))
  544. tmp = tmp2;
  545. node = tmp;
  546. NsNode = null;
  547. }
  548. private XmlNode Node { get { return NsNode != null ? NsNode : node; } }
  549. XmlNode IHasXmlNode.GetNode ()
  550. {
  551. return Node;
  552. }
  553. private XmlNode GetFirstChild (XmlNode n)
  554. {
  555. if (n.FirstChild == null)
  556. return null;
  557. switch (n.FirstChild.NodeType) {
  558. case XmlNodeType.XmlDeclaration:
  559. case XmlNodeType.DocumentType:
  560. return GetNextSibling (n.FirstChild);
  561. case XmlNodeType.EntityReference:
  562. foreach (XmlNode c in n.ChildNodes) {
  563. if (c.NodeType == XmlNodeType.EntityReference) {
  564. XmlNode ec = GetFirstChild (c);
  565. if (ec != null)
  566. return ec;
  567. }
  568. return c;
  569. }
  570. return null;
  571. default:
  572. return n.FirstChild;
  573. }
  574. }
  575. private XmlNode GetLastChild (XmlNode n)
  576. {
  577. if (n.LastChild == null)
  578. return null;
  579. switch (n.LastChild.NodeType) {
  580. case XmlNodeType.XmlDeclaration:
  581. case XmlNodeType.DocumentType:
  582. return GetPreviousSibling (n.LastChild);
  583. case XmlNodeType.EntityReference:
  584. for (XmlNode c = n.LastChild; c != null; c = c.PreviousSibling) {
  585. if (c.NodeType == XmlNodeType.EntityReference) {
  586. XmlNode ec = GetLastChild (c);
  587. if (ec != null)
  588. return ec;
  589. }
  590. return c;
  591. }
  592. return null;
  593. default:
  594. return n.LastChild;
  595. }
  596. }
  597. private XmlNode GetPreviousSibling (XmlNode n)
  598. {
  599. if (n.PreviousSibling != null) {
  600. switch (n.PreviousSibling.NodeType) {
  601. case XmlNodeType.EntityReference:
  602. XmlNode c = GetLastChild (n.PreviousSibling);
  603. if (c != null)
  604. return c;
  605. else // empty entity reference etc.
  606. return GetPreviousSibling (n.PreviousSibling);
  607. case XmlNodeType.XmlDeclaration:
  608. case XmlNodeType.DocumentType:
  609. return GetPreviousSibling (n.PreviousSibling);
  610. default:
  611. return n.PreviousSibling;
  612. }
  613. } else {
  614. if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
  615. return null;
  616. return GetPreviousSibling (n.ParentNode);
  617. }
  618. }
  619. private XmlNode GetNextSibling (XmlNode n)
  620. {
  621. if (n.NextSibling != null) {
  622. switch (n.NextSibling.NodeType) {
  623. case XmlNodeType.EntityReference:
  624. XmlNode c = GetFirstChild (n.NextSibling);
  625. if (c != null)
  626. return c;
  627. else // empty entity reference etc.
  628. return GetNextSibling (n.NextSibling);
  629. case XmlNodeType.XmlDeclaration:
  630. case XmlNodeType.DocumentType:
  631. return GetNextSibling (n.NextSibling);
  632. default:
  633. return n.NextSibling;
  634. }
  635. } else {
  636. if (n.ParentNode == null || n.ParentNode.NodeType != XmlNodeType.EntityReference)
  637. return null;
  638. return GetNextSibling (n.ParentNode);
  639. }
  640. }
  641. private XmlNode GetParentNode (XmlNode n)
  642. {
  643. if (n.ParentNode == null)
  644. return null;
  645. for (XmlNode p = n.ParentNode; p != null; p = p.ParentNode)
  646. if (p.NodeType != XmlNodeType.EntityReference)
  647. return p;
  648. return null;
  649. }
  650. #endregion
  651. }
  652. }