Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XNodeReader.cs
1 //
2 // Authors:
3 //   Atsushi Enomoto
4 //
5 // Copyright 2007 Novell (http://www.novell.com)
6 // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 //
27
28 using System;
29 using System.Xml;
30
31 using XPI = System.Xml.Linq.XProcessingInstruction;
32
33 namespace System.Xml.Linq
34 {
35         internal class XNodeReader : XmlReader, IXmlLineInfo
36         {
37                 ReadState state = ReadState.Initial;
38                 XNode node, start;
39                 int attr = -1;
40                 bool attr_value;
41                 bool end_element;
42                 NameTable name_table = new NameTable ();
43
44                 public XNodeReader (XNode node)
45                 {
46                         this.node = node;
47                         start = node;
48                 }
49                 
50 #if NET_4_0
51                 internal bool OmitDuplicateNamespaces { get; set; }
52 #endif
53
54                 int IXmlLineInfo.LineNumber {
55                         get {
56                                 var o = (XObject) GetCurrentAttribute () ?? node;
57                                 return o != null ? o.LineNumber : 0;
58                         }
59                 }
60                 int IXmlLineInfo.LinePosition {
61                         get {
62                                 var o = (XObject) GetCurrentAttribute () ?? node;
63                                 return o != null ? o.LinePosition : 0;
64                         }
65                 }
66                 bool IXmlLineInfo.HasLineInfo ()
67                 {
68                                 var o = (XObject) GetCurrentAttribute () ?? node;
69                                 return o != null ? ((IXmlLineInfo) o).HasLineInfo () : false;
70                 }
71         
72                 public override int AttributeCount {
73                         get {
74                                 if (state != ReadState.Interactive || end_element)
75                                         return 0;
76                                 int i = 0;
77                                 switch (node.NodeType) {
78                                 case XmlNodeType.Document: // this means xmldecl
79                                         XDeclaration xd = ((XDocument) node).Declaration;
80                                         i = (xd.Version != null ? 1 : 0) +
81                                             (xd.Encoding != null ? 1 : 0) +
82                                             (xd.Standalone != null ? 1 : 0);
83                                         return i;
84                                 case XmlNodeType.DocumentType:
85                                         XDocumentType dtd = (XDocumentType) node;
86                                         i = (dtd.PublicId != null ? 1 : 0) +
87                                             (dtd.SystemId != null ? 1 : 0) +
88                                             (dtd.InternalSubset != null ? 1 : 0);
89                                         return i;
90                                 case XmlNodeType.Element:
91                                         XElement el = (XElement) node;
92                                         for (XAttribute a = el.FirstAttribute; a != null; a = a.NextAttribute)
93                                                 i++;
94                                         return i;
95                                 }
96                                 return 0;
97                         }
98                 }
99
100                 public override string BaseURI {
101                         get { return node.BaseUri ?? String.Empty; }
102                 }
103
104                 public override int Depth {
105                         get {
106                                 if (EOF)
107                                         return 0;
108                                 int i = 0;
109                                 // document.Depth = 0, root.Depth = 0, others.Depth = they depend
110                                 for (XNode n = node.Parent; n != null; n = n.Parent)
111                                         i++;
112                                 if (attr >= 0)
113                                         i++;
114                                 if (attr_value)
115                                         i++;
116                                 return i;
117                         }
118                 }
119
120                 public override bool EOF {
121                         get { return state == ReadState.EndOfFile || state == ReadState.Error; }
122                 }
123
124                 public override bool HasAttributes {
125                         get {
126                                 if (EOF || end_element || node == null)
127                                         return false;
128
129                                 if (node is XElement)
130                                         return ((XElement) node).HasAttributes;
131                                 return AttributeCount > 0;
132                         }
133                 }
134
135                 public override bool HasValue {
136                         get {
137                                 if (EOF)
138                                         return false;
139                                 if (attr >= 0)
140                                         return true;
141                                 switch (node.NodeType) {
142                                 case XmlNodeType.Element:
143                                 case XmlNodeType.Document:
144                                 case XmlNodeType.EndElement:
145                                         return false;
146                                 default:
147                                         return true;
148                                 }
149                         }
150                 }
151
152                 public override bool IsEmptyElement {
153                         get { return !EOF && attr < 0 && node is XElement ? ((XElement) node).IsEmpty : false; }
154                 }
155
156                 internal XAttribute GetCurrentAttribute ()
157                 {
158                         return GetXAttribute (attr);
159                 }
160
161                 XAttribute GetXAttribute (int idx)
162                 {
163                         if (EOF)
164                                 return null;
165                         XElement el = node as XElement;
166                         if (el == null)
167                                 return null;
168                         int i = 0;
169                         foreach (XAttribute a in el.Attributes ())
170                                 if (i++ == idx)
171                                         return a;
172                         return null;
173                 }
174
175                 // XName for element and attribute, string for xmldecl attributes, doctype attribute, doctype name and PI, null for empty.
176                 object GetCurrentName ()
177                 {
178                         if (EOF || attr_value)
179                                 return null;
180                         return GetName (attr);
181                 }
182
183                 object GetName (int attr)
184                 {
185                         if (attr >= 0) {
186                                 switch (node.NodeType) {
187                                 case XmlNodeType.Element:
188                                         XAttribute a = GetXAttribute (attr);
189                                         return a.Name;
190                                 case XmlNodeType.DocumentType:
191                                         if (attr == 0)
192                                                 return ((XDocumentType) node).PublicId != null ? "PUBLIC" : "SYSTEM";
193                                         return "SYSTEM";
194                                 case XmlNodeType.Document:
195                                         XDeclaration xd = ((XDocument) node).Declaration;
196                                         switch (attr) {
197                                         case 0:
198                                                 return xd.Version != null ? "version" : xd.Encoding != null ? "encoding" : "standalone";
199                                         case 1:
200                                                 return xd.Version != null ? (xd.Encoding != null ? "encoding" : "standalone") : "standalone";
201                                         }
202                                         return "standalone";
203                                 }
204                         } else {
205                                 switch (node.NodeType) {
206                                 case XmlNodeType.Document:
207                                         return "xml"; // xmldecl
208                                 case XmlNodeType.Element:
209                                         return ((XElement) node).Name;
210                                 case XmlNodeType.ProcessingInstruction:
211                                         return ((XPI) node).Target;
212                                 case XmlNodeType.DocumentType:
213                                         return ((XDocumentType) node).Name;
214                                 }
215                         }
216                         return null;
217                 }
218
219                 public override string LocalName {
220                         get {
221                                 object name = GetCurrentName ();
222                                 if (name == null)
223                                         return String.Empty;
224                                 if (name is string)
225                                         return (string) name;
226                                 return ((XName) name).LocalName;
227                         }
228                 }
229
230                 public override string NamespaceURI {
231                         get {
232                                 XName name = GetCurrentName () as XName;
233                                 if (name != null)
234                                         // XName for "xmlns" has NamespaceName as "", so we have to return w3c xmlns as a special case.
235                                         return name.LocalName == "xmlns" && name.Namespace == XNamespace.None ?
236                                                 XNamespace.Xmlns.NamespaceName :
237                                                 name.NamespaceName;
238                                 return String.Empty;
239                         }
240                 }
241
242                 public override XmlNameTable NameTable {
243                         get { return name_table; }
244                 }
245
246                 public override XmlNodeType NodeType {
247                         get {
248                                 return  state != ReadState.Interactive ? XmlNodeType.None :
249                                         end_element ? XmlNodeType.EndElement :
250                                         attr_value ? XmlNodeType.Text :
251                                         attr >= 0 ? XmlNodeType.Attribute :
252                                         node.NodeType == XmlNodeType.Document ? XmlNodeType.XmlDeclaration :
253                                         node.NodeType;
254                         }
255                 }
256
257                 public override string Prefix {
258                         get {
259                                 XName name = GetCurrentName () as XName;
260                                 if (name == null || name.Namespace == XNamespace.None)
261                                         return String.Empty;
262                                 XElement el = (node as XElement) ?? node.Parent;
263                                 if (el == null)
264                                         return String.Empty;
265                                 return el.GetPrefixOfNamespace (name.Namespace) ?? String.Empty;
266                         }
267                 }
268
269                 public override ReadState ReadState {
270                         get { return state; }
271                 }
272
273                 public override string Value {
274                         get {
275                                 if (ReadState != ReadState.Interactive)
276                                         return String.Empty;
277                                 XAttribute a = GetCurrentAttribute ();
278                                 if (a != null)
279                                         return a.Value;
280                                 switch (node.NodeType) {
281                                 case XmlNodeType.Document:
282                                         XDeclaration xd = ((XDocument) node).Declaration;
283                                         if (attr >= 0) {
284                                                 switch (LocalName) {
285                                                 case "version":
286                                                         return xd.Version;
287                                                 case "encoding":
288                                                         return xd.Encoding;
289                                                 default:
290                                                         return xd.Standalone;
291                                                 }
292                                         } else {
293                                                 string s = xd.ToString ();
294                                                 return s.Substring (6, s.Length - 6 - 2);
295                                         }
296                                 case XmlNodeType.DocumentType:
297                                         XDocumentType dtd = (XDocumentType) node;
298                                         switch (LocalName) {
299                                         case "PUBLIC":
300                                                 return dtd.PublicId;
301                                         case "SYSTEM":
302                                                 return dtd.SystemId;
303                                         default:
304                                                 return dtd.InternalSubset;
305                                         }
306                                 case XmlNodeType.ProcessingInstruction:
307                                         return ((XPI) node).Data;
308                                 case XmlNodeType.CDATA:
309                                 case XmlNodeType.Text:
310                                         return ((XText) node).Value;
311                                 case XmlNodeType.Comment:
312                                         return ((XComment) node).Value;
313                                 }
314                                 return String.Empty;
315                         }
316                 }
317
318                 public override void Close ()
319                 {
320                         state = ReadState.Closed;
321                 }
322
323                 public override string LookupNamespace (string prefix)
324                 {
325                         if (EOF)
326                                 return null;
327                         XElement el = (node as XElement) ?? node.Parent;
328                         if (el == null)
329                                 return null;
330                         var xn = el.GetNamespaceOfPrefix (prefix);
331                         return xn != XNamespace.None ? xn.NamespaceName : null;
332                 }
333
334                 public override bool MoveToElement ()
335                 {
336                         if (attr >= 0) {
337                                 attr_value = false;
338                                 attr = -1;
339                                 return true;
340                         }
341                         return false;
342                 }
343
344                 public override bool MoveToFirstAttribute ()
345                 {
346                         if (AttributeCount > 0) {
347                                 attr = 0;
348                                 attr_value = false;
349                                 return true;
350                         }
351                         return false;
352                 }
353
354                 public override bool MoveToNextAttribute ()
355                 {
356                         int c = AttributeCount;
357                         if (attr + 1 < c) {
358                                 attr++;
359                                 attr_value = false;
360                                 return true;
361                         }
362                         return false;
363                 }
364
365                 public override bool MoveToAttribute (string name)
366                 {
367                         if (name == null)
368                                 throw new ArgumentNullException ("name");
369
370                         int c = AttributeCount;
371                         bool match = false;
372                         for (int i = 0; i < c; i++) {
373                                 object o = GetName (i);
374                                 if (o == null)
375                                         continue;
376                                 if ((o as string) == name)
377                                         match = true;
378                                 XName n = (XName) o;
379                                 if (name.EndsWith (n.LocalName, StringComparison.Ordinal) && name == GetPrefixedName ((XName) o))
380                                         match = true;
381                                 if (match) {
382                                         attr = i;
383                                         attr_value = false;
384                                         return true;
385                                 }
386                         }
387                         return false;
388                 }
389
390                 string GetPrefixedName (XName name)
391                 {
392                         XElement el = (node as XElement) ?? node.Parent;
393                         if (el == null ||
394                             name.Namespace == XNamespace.None ||
395                             el.GetPrefixOfNamespace (name.Namespace) == String.Empty)
396                                 return name.LocalName;
397                         return String.Concat (el.GetPrefixOfNamespace (name.Namespace), ":", name.LocalName);
398                 }
399
400                 public override bool MoveToAttribute (string local, string ns)
401                 {
402                         if (local == null)
403                                 throw new ArgumentNullException ("local");
404                         if (ns == null)
405                                 throw new ArgumentNullException ("ns");
406
407                         int c = AttributeCount;
408                         bool match = false;
409                         for (int i = 0; i < c; i++) {
410                                 object o = GetName (i);
411                                 if (o == null)
412                                         continue;
413                                 if ((o as string) == local && ns.Length == 0)
414                                         match = true;
415                                 XName n = (XName) o;
416                                 if (local == n.LocalName && ns == n.NamespaceName)
417                                         match = true;
418                                 if (match) {
419                                         attr = i;
420                                         attr_value = false;
421                                         return true;
422                                 }
423                         }
424                         return false;
425                 }
426
427                 public override string GetAttribute (int i)
428                 {
429                         int a_bak = attr;
430                         bool av_bak = attr_value;
431                         try {
432                                 MoveToElement ();
433                                 MoveToAttribute (i);
434                                 return Value;
435                         } finally {
436                                 attr = a_bak;
437                                 attr_value = av_bak;
438                         }
439                 }
440
441                 public override string GetAttribute (string name)
442                 {
443                         int a_bak = attr;
444                         bool av_bak = attr_value;
445                         try {
446                                 MoveToElement ();
447                                 return MoveToAttribute (name) ? Value : null;
448                         } finally {
449                                 attr = a_bak;
450                                 attr_value = av_bak;
451                         }
452                 }
453
454                 public override string GetAttribute (string local, string ns)
455                 {
456                         int a_bak = attr;
457                         bool av_bak = attr_value;
458                         try {
459                                 MoveToElement ();
460                                 return MoveToAttribute (local, ns) ? Value : null;
461                         } finally {
462                                 attr = a_bak;
463                                 attr_value = av_bak;
464                         }
465                 }
466
467                 public override bool Read ()
468                 {
469                         // clear attribute state on element/xmldecl/dtd.
470                         attr = -1;
471                         attr_value = false;
472
473                         switch (state) {
474                         case ReadState.Initial:
475                                 state = ReadState.Interactive;
476                                 XDocument doc = node as XDocument;
477                                 if (doc != null) {
478                                         if (doc.Declaration != null)
479                                                 return true;
480                                 }
481                                 else
482                                         return true; // any other root
483                                 break;
484                         case ReadState.Interactive:
485                                 break;
486                         default:
487                                 return false;
488                         }
489
490                         // when positioned on xmldecl, move to children
491                         if (node is XDocument) {
492                                 XDocument doc = node as XDocument;
493                                 node = doc.FirstNode;
494                                 if (node == null) {
495                                         state = ReadState.EndOfFile;
496                                         return false;
497                                 }
498                                 node = doc.FirstNode;
499                                 return true;
500                         }
501
502                         XElement c = node as XElement;
503                         if (c != null && !end_element) {
504                                 if (c.FirstNode != null) {
505                                         node = c.FirstNode;
506                                         return true;
507                                 } else if (!c.IsEmpty) {
508                                         // empty but full EndElement
509                                         end_element = true;
510                                         return true;
511                                 }
512                         }
513                         end_element = false;
514                         if (node.NextNode != null && node != start) {
515                                 node = node.NextNode;
516                                 return true;
517                         }
518                         if (node.Parent == null || node == start) {
519                                 state = ReadState.EndOfFile;
520                                 return false;
521                         }
522                         node = node.Parent;
523                         end_element = true;
524                         return true;
525                 }
526
527                 public
528                 override
529                 bool ReadAttributeValue ()
530                 {
531                         if (attr < 0 || attr_value)
532                                 return false;
533                         attr_value = true;
534                         return true;
535                 }
536
537                 public override void ResolveEntity ()
538                 {
539                         throw new NotSupportedException ();
540                 }
541                 
542                 // Note that this does not return attribute node.
543                 internal XNode CurrentNode {
544                         get { return node; }
545                 }
546         }
547 }