BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XNodeNavigator.cs
1 //
2 // Authors:
3 //   Atsushi Enomoto
4 //
5 // Copyright 2007 Novell (http://www.novell.com)
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
26
27 #if !MOONLIGHT
28
29 using System;
30 using System.IO;
31 using System.Text;
32 using System.Xml;
33 using System.Xml.Schema;
34 using System.Xml.XPath;
35
36 using XPI = System.Xml.Linq.XProcessingInstruction;
37
38 namespace System.Xml.Linq
39 {
40         internal class XNodeNavigator : XPathNavigator
41         {
42                 static readonly XAttribute attr_ns_xml = new XAttribute (XNamespace.Xmlns.GetName ("xml"), XNamespace.Xml.NamespaceName);
43
44                 XNode node;
45                 XAttribute attr;
46                 XmlNameTable name_table;
47
48                 public XNodeNavigator (XNode node, XmlNameTable nameTable)
49                 {
50                         this.node = node;
51                         this.name_table = nameTable;
52                 }
53
54                 public XNodeNavigator (XNodeNavigator other)
55                 {
56                         this.node = other.node;
57                         this.attr = other.attr;
58                         this.name_table = other.name_table;
59                 }
60
61                 public override string BaseURI {
62                         get { return node.BaseUri ?? String.Empty; }
63                 }
64
65                 public override bool CanEdit {
66                         get { return true; }
67                 }
68
69                 public override bool HasAttributes {
70                         get {
71                                 if (attr != null)
72                                         return false;
73                                 XElement el = node as XElement;
74                                 if (el == null)
75                                         return false;
76                                 foreach (var at in el.Attributes ())
77                                         if (!at.IsNamespaceDeclaration)
78                                                 return true;
79                                 return false;
80                         }
81                 }
82
83                 public override bool HasChildren {
84                         get {
85                                 if (attr != null)
86                                         return false;
87                                 XContainer c = node as XContainer;
88                                 return c != null && c.FirstNode != null;
89                         }
90                 }
91
92                 public override bool IsEmptyElement {
93                         get {
94                                 if (attr != null)
95                                         return false;
96                                 XElement el = node as XElement;
97                                 return el != null && el.IsEmpty;
98                         }
99                 }
100
101                 public override string LocalName {
102                         get {
103                                 switch (NodeType) {
104                                 case XPathNodeType.Namespace:
105                                         return attr.Name.Namespace == XNamespace.None ? String.Empty : attr.Name.LocalName;
106                                 case XPathNodeType.Attribute:
107                                         return attr.Name.LocalName;
108                                 case XPathNodeType.Element:
109                                         return ((XElement) node).Name.LocalName;
110                                 case XPathNodeType.ProcessingInstruction:
111                                         return ((XPI) node).Target;
112                                 default:
113                                         return String.Empty;
114                                 }
115                         }
116                 }
117
118                 public override string Name {
119                         get {
120                                 XName name = null;
121                                 switch (NodeType) {
122                                 case XPathNodeType.Attribute:
123                                         name = attr.Name;
124                                         break;
125                                 case XPathNodeType.Element:
126                                         name = ((XElement) node).Name;
127                                         break;
128                                 default:
129                                         return LocalName;
130                                 }
131                                 if (name.Namespace == XNamespace.None)
132                                         return name.LocalName;
133                                 XElement el = (node as XElement) ?? node.Parent;
134                                 if (el == null)
135                                         return name.LocalName;
136                                 string prefix = el.GetPrefixOfNamespace (name.Namespace);
137                                 return prefix.Length > 0 ? String.Concat (prefix, ":", name.LocalName) : name.LocalName;
138                         }
139                 }
140
141                 public override string NamespaceURI {
142                         get {
143                                 switch (NodeType) {
144                                 case XPathNodeType.ProcessingInstruction:
145                                 case XPathNodeType.Namespace:
146                                         return String.Empty;
147                                 case XPathNodeType.Attribute:
148                                         return attr.Name.NamespaceName;
149                                 case XPathNodeType.Element:
150                                         return ((XElement) node).Name.NamespaceName;
151                                 default:
152                                         return String.Empty;
153                                 }
154                         }
155                 }
156
157                 public override XmlNameTable NameTable {
158                         get { return name_table; }
159                 }
160
161                 public override XPathNodeType NodeType {
162                         get {
163                                 if (attr != null)
164                                         return  attr.IsNamespaceDeclaration ?
165                                                 XPathNodeType.Namespace :
166                                                 XPathNodeType.Attribute;
167                                 switch (node.NodeType) {
168                                 case XmlNodeType.Element:
169                                         return XPathNodeType.Element;
170                                 case XmlNodeType.Document:
171                                         return XPathNodeType.Root;
172                                 case XmlNodeType.Comment:
173                                         return XPathNodeType.Comment;
174                                 case XmlNodeType.ProcessingInstruction:
175                                         return XPathNodeType.ProcessingInstruction;
176                                 default:
177                                         return XPathNodeType.Text;
178                                 }
179                         }
180                 }
181
182                 public override string Prefix {
183                         get {
184                                 XName name = null;
185                                 switch (NodeType) {
186                                 case XPathNodeType.ProcessingInstruction:
187                                 case XPathNodeType.Namespace:
188                                         return String.Empty;
189                                 case XPathNodeType.Attribute:
190                                         name = attr.Name;
191                                         break;
192                                 case XPathNodeType.Element:
193                                         name = ((XElement) node).Name;
194                                         break;
195                                 default:
196                                         return LocalName;
197                                 }
198                                 if (name.Namespace == XNamespace.None)
199                                         return String.Empty;
200                                 XElement el = (node as XElement) ?? node.Parent;
201                                 if (el == null)
202                                         return String.Empty;
203                                 return el.GetPrefixOfNamespace (name.Namespace);
204                         }
205                 }
206
207                 public override IXmlSchemaInfo SchemaInfo {
208                         get { return null; }
209                 }
210
211                 public override object UnderlyingObject {
212                         get { return attr != null ? (object) attr : node; }
213                 }
214
215                 public override string Value {
216                         get {
217                                 if (attr != null)
218                                         return attr.Value;
219                                 else
220                                 switch (NodeType) {
221                                 case XPathNodeType.Comment:
222                                         return ((XComment) node).Value;
223                                 case XPathNodeType.ProcessingInstruction:
224                                         return ((XPI) node).Data;
225                                 case XPathNodeType.Text:
226                                         string s = String.Empty;
227                                         for (var xn = node as XText; xn != null; xn = xn.NextNode as XText)
228                                                 s += xn.Value;
229                                         return s;
230                                 case XPathNodeType.Element:
231                                 case XPathNodeType.Root:
232                                         return GetInnerText ((XContainer) node);
233                                 }
234                                 return String.Empty;
235                         }
236                 }
237
238                 string GetInnerText (XContainer node)
239                 {
240                         StringBuilder sb = null;
241                         foreach (XNode n in node.Nodes ())
242                                 GetInnerText (n, ref sb);
243                         return sb != null ? sb.ToString () : String.Empty;
244                 }
245
246                 void GetInnerText (XNode n, ref StringBuilder sb)
247                 {
248                         switch (n.NodeType) {
249                         case XmlNodeType.Element:
250                                 foreach (XNode c in ((XElement) n).Nodes ())
251                                         GetInnerText (c, ref sb);
252                                 break;
253                         case XmlNodeType.Text:
254                         case XmlNodeType.CDATA:
255                                 if (sb == null)
256                                         sb = new StringBuilder ();
257                                 sb.Append (((XText) n).Value);
258                                 break;
259                         }
260                 }
261
262                 public override XPathNavigator Clone ()
263                 {
264                         return new XNodeNavigator (this);
265                 }
266
267                 public override bool IsSamePosition (XPathNavigator other)
268                 {
269                         XNodeNavigator nav = other as XNodeNavigator;
270                         if (nav == null || nav.node.Owner != node.Owner)
271                                 return false;
272                         return node == nav.node && attr == nav.attr;
273                 }
274
275                 public override bool MoveTo (XPathNavigator other)
276                 {
277                         XNodeNavigator nav = other as XNodeNavigator;
278                         if (nav == null || nav.node.Document != node.Document)
279                                 return false;
280                         node = nav.node;
281                         attr = nav.attr;
282                         return true;
283                 }
284
285                 public override bool MoveToFirstAttribute ()
286                 {
287                         if (attr != null)
288                                 return false;
289                         XElement el = node as XElement;
290                         if (el == null || !el.HasAttributes)
291                                 return false;
292                         foreach (XAttribute a in el.Attributes ())
293                                 if (!a.IsNamespaceDeclaration) {
294                                         attr = a;
295                                         return true;
296                                 }
297                         return false;
298                 }
299
300                 public override bool MoveToFirstChild ()
301                 {
302                         if (attr != null)
303                                 return false;
304                         XContainer c = node as XContainer;
305                         if (c == null || c.FirstNode == null)
306                                 return false;
307                         node = c.FirstNode;
308                         attr = null;
309                         return true;
310                 }
311
312                 public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
313                 {
314                         if (NodeType != XPathNodeType.Element)
315                                 return false;
316                         for (XElement el = node as XElement; el != null; el = el.Parent) {
317                                 foreach (XAttribute a in el.Attributes ())
318                                         if (a.IsNamespaceDeclaration) {
319                                                 attr = a;
320                                                 return true;
321                                         }
322                                 if (scope == XPathNamespaceScope.Local)
323                                         return false;
324                         }
325                         if (scope != XPathNamespaceScope.All)
326                                 return false;
327                         attr = attr_ns_xml;
328                         return true;
329                 }
330
331                 public override bool MoveToId (string id)
332                 {
333                         throw new NotSupportedException ("This XPathNavigator does not support IDs");
334                 }
335
336                 public override bool MoveToNext ()
337                 {
338                         XNode xn = node.NextNode;
339                         if (node is XText)
340                                 for (; xn != null; xn = xn.NextNode)
341                                         if (!(xn.NextNode is XText))
342                                                 break;
343                         if (xn == null)
344                                 return false;
345                         node = xn;
346                         attr = null;
347                         return true;
348                 }
349
350                 public override bool MoveToNextAttribute ()
351                 {
352                         if (attr == null)
353                                 return false;
354                         if (attr.NextAttribute == null)
355                                 return false;
356                         for (XAttribute a = attr.NextAttribute; a != null; a = a.NextAttribute)
357                                 if (!a.IsNamespaceDeclaration) {
358                                         attr = a;
359                                         return true;
360                                 }
361                         return false;
362                 }
363
364                 public override bool MoveToNextNamespace (XPathNamespaceScope scope)
365                 {
366                         if (attr == null)
367                                 return false;
368                         for (XAttribute a = attr.NextAttribute; a != null; a = a.NextAttribute)
369                                 if (a.IsNamespaceDeclaration) {
370                                         attr = a;
371                                         return true;
372                                 }
373
374                         if (scope == XPathNamespaceScope.Local)
375                                 return false;
376
377                         if (attr == attr_ns_xml)
378                                 return false; // no next attribute
379
380                         for (XElement el = ((XElement) attr.Parent).Parent; el != null; el = el.Parent) {
381                                 foreach (XAttribute a in el.Attributes ())
382                                         if (a.IsNamespaceDeclaration) {
383                                                 attr = a;
384                                                 return true;
385                                         }
386                         }
387                         if (scope != XPathNamespaceScope.All)
388                                 return false;
389                         attr = attr_ns_xml;
390                         return true;
391                 }
392
393                 public override bool MoveToParent ()
394                 {
395                         if (attr != null) {
396                                 attr = null;
397                                 return true;
398                         }
399                         if (node.Owner == null)
400                                 return false;
401                         node = node.Owner;
402                         return true;
403                 }
404
405                 public override bool MoveToPrevious ()
406                 {
407                         if (node.PreviousNode == null)
408                                 return false;
409                         node = node.PreviousNode;
410                         attr = null;
411                         return true;
412                 }
413
414                 public override void MoveToRoot ()
415                 {
416                         node = node.Document ?? node;
417                         attr = null;
418                 }
419         }
420 }
421
422 #endif