Merge pull request #558 from mkorkalo/master
[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 using System;
28 using System.IO;
29 using System.Text;
30 using System.Xml;
31 using System.Xml.Schema;
32 using System.Xml.XPath;
33
34 using XPI = System.Xml.Linq.XProcessingInstruction;
35
36 namespace System.Xml.Linq
37 {
38         internal class XNodeNavigator : XPathNavigator
39         {
40                 static readonly XAttribute attr_ns_xml = new XAttribute (XNamespace.Xmlns.GetName ("xml"), XNamespace.Xml.NamespaceName);
41
42                 XNode node;
43                 XAttribute attr;
44                 XmlNameTable name_table;
45
46                 public XNodeNavigator (XNode node, XmlNameTable nameTable)
47                 {
48                         this.node = node;
49                         this.name_table = nameTable;
50                 }
51
52                 public XNodeNavigator (XNodeNavigator other)
53                 {
54                         this.node = other.node;
55                         this.attr = other.attr;
56                         this.name_table = other.name_table;
57                 }
58
59                 public override string BaseURI {
60                         get { return node.BaseUri ?? String.Empty; }
61                 }
62
63                 public override bool CanEdit {
64                         get { return true; }
65                 }
66
67                 public override bool HasAttributes {
68                         get {
69                                 if (attr != null)
70                                         return false;
71                                 XElement el = node as XElement;
72                                 if (el == null)
73                                         return false;
74                                 foreach (var at in el.Attributes ())
75                                         if (!at.IsNamespaceDeclaration)
76                                                 return true;
77                                 return false;
78                         }
79                 }
80
81                 public override bool HasChildren {
82                         get {
83                                 if (attr != null)
84                                         return false;
85                                 XContainer c = node as XContainer;
86                                 return c != null && c.FirstNode != null;
87                         }
88                 }
89
90                 public override bool IsEmptyElement {
91                         get {
92                                 if (attr != null)
93                                         return false;
94                                 XElement el = node as XElement;
95                                 return el != null && el.IsEmpty;
96                         }
97                 }
98
99                 public override string LocalName {
100                         get {
101                                 switch (NodeType) {
102                                 case XPathNodeType.Namespace:
103                                         return attr.Name.Namespace == XNamespace.None ? String.Empty : attr.Name.LocalName;
104                                 case XPathNodeType.Attribute:
105                                         return attr.Name.LocalName;
106                                 case XPathNodeType.Element:
107                                         return ((XElement) node).Name.LocalName;
108                                 case XPathNodeType.ProcessingInstruction:
109                                         return ((XPI) node).Target;
110                                 default:
111                                         return String.Empty;
112                                 }
113                         }
114                 }
115
116                 public override string Name {
117                         get {
118                                 XName name = null;
119                                 switch (NodeType) {
120                                 case XPathNodeType.Attribute:
121                                         name = attr.Name;
122                                         break;
123                                 case XPathNodeType.Element:
124                                         name = ((XElement) node).Name;
125                                         break;
126                                 default:
127                                         return LocalName;
128                                 }
129                                 if (name.Namespace == XNamespace.None)
130                                         return name.LocalName;
131                                 XElement el = (node as XElement) ?? node.Parent;
132                                 if (el == null)
133                                         return name.LocalName;
134                                 string prefix = el.GetPrefixOfNamespace (name.Namespace);
135                                 return prefix.Length > 0 ? String.Concat (prefix, ":", name.LocalName) : name.LocalName;
136                         }
137                 }
138
139                 public override string NamespaceURI {
140                         get {
141                                 switch (NodeType) {
142                                 case XPathNodeType.ProcessingInstruction:
143                                 case XPathNodeType.Namespace:
144                                         return String.Empty;
145                                 case XPathNodeType.Attribute:
146                                         return attr.Name.NamespaceName;
147                                 case XPathNodeType.Element:
148                                         return ((XElement) node).Name.NamespaceName;
149                                 default:
150                                         return String.Empty;
151                                 }
152                         }
153                 }
154
155                 public override XmlNameTable NameTable {
156                         get { return name_table; }
157                 }
158
159                 public override XPathNodeType NodeType {
160                         get {
161                                 if (attr != null)
162                                         return  attr.IsNamespaceDeclaration ?
163                                                 XPathNodeType.Namespace :
164                                                 XPathNodeType.Attribute;
165                                 switch (node.NodeType) {
166                                 case XmlNodeType.Element:
167                                         return XPathNodeType.Element;
168                                 case XmlNodeType.Document:
169                                         return XPathNodeType.Root;
170                                 case XmlNodeType.Comment:
171                                         return XPathNodeType.Comment;
172                                 case XmlNodeType.ProcessingInstruction:
173                                         return XPathNodeType.ProcessingInstruction;
174                                 default:
175                                         return XPathNodeType.Text;
176                                 }
177                         }
178                 }
179
180                 public override string Prefix {
181                         get {
182                                 XName name = null;
183                                 switch (NodeType) {
184                                 case XPathNodeType.ProcessingInstruction:
185                                 case XPathNodeType.Namespace:
186                                         return String.Empty;
187                                 case XPathNodeType.Attribute:
188                                         name = attr.Name;
189                                         break;
190                                 case XPathNodeType.Element:
191                                         name = ((XElement) node).Name;
192                                         break;
193                                 default:
194                                         return LocalName;
195                                 }
196                                 if (name.Namespace == XNamespace.None)
197                                         return String.Empty;
198                                 XElement el = (node as XElement) ?? node.Parent;
199                                 if (el == null)
200                                         return String.Empty;
201                                 return el.GetPrefixOfNamespace (name.Namespace);
202                         }
203                 }
204
205                 public override IXmlSchemaInfo SchemaInfo {
206                         get { return null; }
207                 }
208
209                 public override object UnderlyingObject {
210                         get { return attr != null ? (object) attr : node; }
211                 }
212
213                 public override string Value {
214                         get {
215                                 if (attr != null)
216                                         return attr.Value;
217                                 else
218                                 switch (NodeType) {
219                                 case XPathNodeType.Comment:
220                                         return ((XComment) node).Value;
221                                 case XPathNodeType.ProcessingInstruction:
222                                         return ((XPI) node).Data;
223                                 case XPathNodeType.Text:
224                                         string s = String.Empty;
225                                         for (var xn = node as XText; xn != null; xn = xn.NextNode as XText)
226                                                 s += xn.Value;
227                                         return s;
228                                 case XPathNodeType.Element:
229                                 case XPathNodeType.Root:
230                                         return GetInnerText ((XContainer) node);
231                                 }
232                                 return String.Empty;
233                         }
234                 }
235
236                 string GetInnerText (XContainer node)
237                 {
238                         StringBuilder sb = null;
239                         foreach (XNode n in node.Nodes ())
240                                 GetInnerText (n, ref sb);
241                         return sb != null ? sb.ToString () : String.Empty;
242                 }
243
244                 void GetInnerText (XNode n, ref StringBuilder sb)
245                 {
246                         switch (n.NodeType) {
247                         case XmlNodeType.Element:
248                                 foreach (XNode c in ((XElement) n).Nodes ())
249                                         GetInnerText (c, ref sb);
250                                 break;
251                         case XmlNodeType.Text:
252                         case XmlNodeType.CDATA:
253                                 if (sb == null)
254                                         sb = new StringBuilder ();
255                                 sb.Append (((XText) n).Value);
256                                 break;
257                         }
258                 }
259
260                 public override XPathNavigator Clone ()
261                 {
262                         return new XNodeNavigator (this);
263                 }
264
265                 public override bool IsSamePosition (XPathNavigator other)
266                 {
267                         XNodeNavigator nav = other as XNodeNavigator;
268                         if (nav == null || nav.node.Owner != node.Owner)
269                                 return false;
270                         return node == nav.node && attr == nav.attr;
271                 }
272
273                 public override bool MoveTo (XPathNavigator other)
274                 {
275                         XNodeNavigator nav = other as XNodeNavigator;
276                         if (nav == null || nav.node.Document != node.Document)
277                                 return false;
278                         node = nav.node;
279                         attr = nav.attr;
280                         return true;
281                 }
282
283                 public override bool MoveToFirstAttribute ()
284                 {
285                         if (attr != null)
286                                 return false;
287                         XElement el = node as XElement;
288                         if (el == null || !el.HasAttributes)
289                                 return false;
290                         foreach (XAttribute a in el.Attributes ())
291                                 if (!a.IsNamespaceDeclaration) {
292                                         attr = a;
293                                         return true;
294                                 }
295                         return false;
296                 }
297
298                 public override bool MoveToFirstChild ()
299                 {
300                         if (attr != null)
301                                 return false;
302                         XContainer c = node as XContainer;
303                         if (c == null || c.FirstNode == null)
304                                 return false;
305                         node = c.FirstNode;
306                         attr = null;
307                         return true;
308                 }
309
310                 public override bool MoveToFirstNamespace (XPathNamespaceScope scope)
311                 {
312                         if (NodeType != XPathNodeType.Element)
313                                 return false;
314                         for (XElement el = node as XElement; el != null; el = el.Parent) {
315                                 foreach (XAttribute a in el.Attributes ())
316                                         if (a.IsNamespaceDeclaration) {
317                                                 attr = a;
318                                                 return true;
319                                         }
320                                 if (scope == XPathNamespaceScope.Local)
321                                         return false;
322                         }
323                         if (scope != XPathNamespaceScope.All)
324                                 return false;
325                         attr = attr_ns_xml;
326                         return true;
327                 }
328
329                 public override bool MoveToId (string id)
330                 {
331                         throw new NotSupportedException ("This XPathNavigator does not support IDs");
332                 }
333
334                 public override bool MoveToNext ()
335                 {
336                         XNode xn = node.NextNode;
337                         if (xn is XText)
338                                 for (; xn != null; xn = xn.NextNode)
339                                         if (!(xn.NextNode is XText))
340                                                 break;
341                         if (xn == null)
342                                 return false;
343                         node = xn;
344                         attr = null;
345                         return true;
346                 }
347
348                 public override bool MoveToNextAttribute ()
349                 {
350                         if (attr == null)
351                                 return false;
352                         if (attr.NextAttribute == null)
353                                 return false;
354                         for (XAttribute a = attr.NextAttribute; a != null; a = a.NextAttribute)
355                                 if (!a.IsNamespaceDeclaration) {
356                                         attr = a;
357                                         return true;
358                                 }
359                         return false;
360                 }
361
362                 public override bool MoveToNextNamespace (XPathNamespaceScope scope)
363                 {
364                         if (attr == null)
365                                 return false;
366                         for (XAttribute a = attr.NextAttribute; a != null; a = a.NextAttribute)
367                                 if (a.IsNamespaceDeclaration) {
368                                         attr = a;
369                                         return true;
370                                 }
371
372                         if (scope == XPathNamespaceScope.Local)
373                                 return false;
374
375                         if (attr == attr_ns_xml)
376                                 return false; // no next attribute
377
378                         for (XElement el = ((XElement) attr.Parent).Parent; el != null; el = el.Parent) {
379                                 foreach (XAttribute a in el.Attributes ())
380                                         if (a.IsNamespaceDeclaration) {
381                                                 attr = a;
382                                                 return true;
383                                         }
384                         }
385                         if (scope != XPathNamespaceScope.All)
386                                 return false;
387                         attr = attr_ns_xml;
388                         return true;
389                 }
390
391                 public override bool MoveToParent ()
392                 {
393                         if (attr != null) {
394                                 attr = null;
395                                 return true;
396                         }
397                         if (node.Owner == null)
398                                 return false;
399                         node = node.Owner;
400                         return true;
401                 }
402
403                 public override bool MoveToPrevious ()
404                 {
405                         if (node.PreviousNode == null)
406                                 return false;
407                         node = node.PreviousNode;
408                         attr = null;
409                         return true;
410                 }
411
412                 public override void MoveToRoot ()
413                 {
414                         attr = null;
415                         if (node.Document != null)
416                                 node = node.Document;
417                         else
418                                 while (node.Owner != null)
419                                         node = node.Owner;
420                 }
421         }
422 }