Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / System.XML / Mono.Xml.XPath / XPathNavigatorReader.cs
1 //\r
2 // XPathNavigatorReader.cs\r
3 //\r
4 // Author:\r
5 //      Atsushi Enomoto <ginga@kit.hi-ho.ne.jp>\r
6 //\r
7 // 2003 Atsushi Enomoto. No rights reserved.\r
8 // Copyright (C) 2004 Novell Inc.\r
9 \r
10 //\r
11 // Permission is hereby granted, free of charge, to any person obtaining\r
12 // a copy of this software and associated documentation files (the\r
13 // "Software"), to deal in the Software without restriction, including\r
14 // without limitation the rights to use, copy, modify, merge, publish,\r
15 // distribute, sublicense, and/or sell copies of the Software, and to\r
16 // permit persons to whom the Software is furnished to do so, subject to\r
17 // the following conditions:\r
18 // \r
19 // The above copyright notice and this permission notice shall be\r
20 // included in all copies or substantial portions of the Software.\r
21 // \r
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
29 //\r
30 \r
31 using System;\r
32 using System.Text;\r
33 using System.Xml;\r
34 using System.Xml.Schema;\r
35 using System.Xml.XPath;\r
36 \r
37 namespace Mono.Xml.XPath\r
38 {\r
39 \r
40         internal class XPathNavigatorReader : XmlReader\r
41         {\r
42                 public XPathNavigatorReader (XPathNavigator nav)\r
43                 {\r
44                         current = nav.Clone ();\r
45                 }\r
46 \r
47                 XPathNavigator root;\r
48                 XPathNavigator current;\r
49                 bool started;\r
50                 bool closed;\r
51                 bool endElement;\r
52                 bool attributeValueConsumed;\r
53                 StringBuilder readStringBuffer = new StringBuilder ();\r
54 \r
55                 int depth;\r
56                 int attributeCount;\r
57                 bool eof;\r
58 \r
59                 #region Properties\r
60 \r
61                 public override bool CanReadBinaryContent {\r
62                         get { return true; }\r
63                 }\r
64 \r
65                 public override bool CanReadValueChunk {\r
66                         get { return true; }\r
67                 }\r
68 \r
69                 public override XmlNodeType NodeType {\r
70                         get {\r
71                                 if (ReadState != ReadState.Interactive)\r
72                                         return XmlNodeType.None;\r
73                                 if (endElement)\r
74                                         return XmlNodeType.EndElement;\r
75                                 if (attributeValueConsumed)\r
76                                         // Is there any way to get other kind of nodes than Text?\r
77                                         return XmlNodeType.Text;\r
78 \r
79                                 switch (current.NodeType) {\r
80                                 case XPathNodeType.Namespace:\r
81                                 case XPathNodeType.Attribute:\r
82                                         return XmlNodeType.Attribute;\r
83                                 case XPathNodeType.Comment:\r
84                                         return XmlNodeType.Comment;\r
85                                 case XPathNodeType.Element:\r
86                                         return XmlNodeType.Element;\r
87                                 case XPathNodeType.ProcessingInstruction:\r
88                                         return XmlNodeType.ProcessingInstruction;\r
89                                 case XPathNodeType.Root:\r
90                                         // It is actually Document, but in XmlReader there is no such situation to return Document.\r
91                                         return XmlNodeType.None;\r
92                                 case XPathNodeType.SignificantWhitespace:\r
93                                         return XmlNodeType.SignificantWhitespace;\r
94                                 case XPathNodeType.Text:\r
95                                         return XmlNodeType.Text;\r
96                                 case XPathNodeType.Whitespace:\r
97                                         return XmlNodeType.Whitespace;\r
98                                 default:\r
99                                         throw new InvalidOperationException (String.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current.NodeType));\r
100                                 }\r
101                         }\r
102                 }\r
103 \r
104                 public override string Name {\r
105                         get {\r
106                                 if (ReadState != ReadState.Interactive)\r
107                                         return String.Empty;\r
108                                 if (attributeValueConsumed)\r
109                                         return String.Empty;\r
110                                 else if (current.NodeType == XPathNodeType.Namespace)\r
111                                         return current.Name == String.Empty ? "xmlns" : "xmlns:" + current.Name;\r
112                                 else\r
113                                         return current.Name;\r
114                         }\r
115                 }\r
116 \r
117                 public override string LocalName {\r
118                         get {\r
119                                 if (ReadState != ReadState.Interactive)\r
120                                         return String.Empty;\r
121                                 if (attributeValueConsumed)\r
122                                         return String.Empty;\r
123                                 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName == String.Empty)\r
124                                         return "xmlns";\r
125                                 else\r
126                                         return current.LocalName;\r
127                         }\r
128                 }\r
129 \r
130                 public override string NamespaceURI {\r
131                         get {\r
132                                 if (ReadState != ReadState.Interactive)\r
133                                         return String.Empty;\r
134                                 if (attributeValueConsumed)\r
135                                         return String.Empty;\r
136                                 else if (current.NodeType == XPathNodeType.Namespace)\r
137                                         return "http://www.w3.org/2000/xmlns/";\r
138                                 else\r
139                                         return current.NamespaceURI;\r
140                         }\r
141                 }\r
142 \r
143                 public override string Prefix {\r
144                         get {\r
145                                 if (ReadState != ReadState.Interactive)\r
146                                         return String.Empty;\r
147                                 if (attributeValueConsumed)\r
148                                         return String.Empty;\r
149                                 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName != String.Empty)\r
150                                         return "xmlns";\r
151                                 else\r
152                                         return current.Prefix;\r
153                         }\r
154                 }\r
155 \r
156                 public override bool HasValue {\r
157                         get {\r
158                                 switch (current.NodeType) {\r
159                                 case XPathNodeType.Namespace:\r
160                                 case XPathNodeType.Attribute:\r
161                                 case XPathNodeType.Comment:\r
162                                 case XPathNodeType.ProcessingInstruction:\r
163                                 case XPathNodeType.SignificantWhitespace:\r
164                                 case XPathNodeType.Text:\r
165                                 case XPathNodeType.Whitespace:\r
166                                         return true;\r
167                                 }\r
168                                 return false;\r
169                         }\r
170                 }\r
171 \r
172                 public override int Depth {\r
173                         get {\r
174                                 if (ReadState != ReadState.Interactive)\r
175                                         return 0;\r
176 \r
177                                 if (NodeType == XmlNodeType.Attribute)\r
178                                         return depth + 1;\r
179                                 if (attributeValueConsumed)\r
180                                         return depth + 2;\r
181                                 return depth;\r
182                         }\r
183                 }\r
184 \r
185                 public override string Value {\r
186                         get {\r
187                                 if (ReadState != ReadState.Interactive)\r
188                                         return String.Empty;\r
189                                 switch (current.NodeType) {\r
190                                 case XPathNodeType.Namespace:\r
191                                 case XPathNodeType.Attribute:\r
192                                 case XPathNodeType.Comment:\r
193                                 case XPathNodeType.ProcessingInstruction:\r
194                                 case XPathNodeType.SignificantWhitespace:\r
195                                 case XPathNodeType.Text:\r
196                                 case XPathNodeType.Whitespace:\r
197                                         return current.Value;\r
198                                 case XPathNodeType.Element:\r
199                                 case XPathNodeType.Root:\r
200                                         return String.Empty;\r
201                                 default:\r
202                                         throw new InvalidOperationException ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.");\r
203                                 }\r
204                         }\r
205                 }\r
206 \r
207                 public override string BaseURI {\r
208                         get { return current.BaseURI; }\r
209                 }\r
210 \r
211                 public override bool IsEmptyElement {\r
212                         get {\r
213                                 if (ReadState != ReadState.Interactive)\r
214                                         return false;\r
215                                 return current.IsEmptyElement;\r
216                         }\r
217                 }\r
218 \r
219                 public override bool IsDefault {\r
220                         get {\r
221 #if NET_2_0\r
222                                 IXmlSchemaInfo si = current as IXmlSchemaInfo;\r
223                                 return si != null && si.IsDefault;\r
224 #else\r
225                                 return false; // no way to check this.\r
226 #endif\r
227                         }\r
228                 }\r
229 \r
230                 // It makes no sense.\r
231                 public override char QuoteChar {\r
232                         get { return '\"'; }\r
233                 }\r
234 \r
235 #if NET_2_0\r
236                 public override IXmlSchemaInfo SchemaInfo {\r
237                         get { return current.SchemaInfo; }\r
238                 }\r
239 #endif\r
240 \r
241                 public override string XmlLang {\r
242                         get { return current.XmlLang; }\r
243                 }\r
244 \r
245                 // It is meaningless.\r
246                 public override XmlSpace XmlSpace {\r
247                         get { return XmlSpace.None; }\r
248                 }\r
249 \r
250                 public override int AttributeCount {\r
251                         get { return attributeCount; }\r
252                 }\r
253 \r
254                 private int GetAttributeCount ()\r
255                 {\r
256                         if (ReadState != ReadState.Interactive)\r
257                                 return 0;\r
258                         int count = 0;\r
259                         if (current.MoveToFirstAttribute ()) {\r
260                                 do {\r
261                                         count++;\r
262                                 } while (current.MoveToNextAttribute ());\r
263                                 current.MoveToParent ();\r
264                         }\r
265                         if (current.MoveToFirstNamespace (XPathNamespaceScope.Local)) {\r
266                                 do {\r
267                                         count++;\r
268                                 } while (current.MoveToNextNamespace (XPathNamespaceScope.Local));\r
269                                 current.MoveToParent ();\r
270                         }\r
271                         return count;\r
272                 }\r
273                 \r
274                 /*\r
275                 private bool MoveToAttributeNavigator (int i)\r
276                 {\r
277                         if (ReadState != ReadState.Interactive)\r
278                                 return false;\r
279                         switch (current.NodeType) {\r
280                         case XPathNodeType.Namespace:\r
281                         case XPathNodeType.Attribute:\r
282                                 this.MoveToElement ();\r
283                                 goto case XPathNodeType.Element;\r
284                         case XPathNodeType.Element:\r
285                                 int count = 0;\r
286                                 if (MoveToFirstAttribute ()) {\r
287                                         if (i == 0)\r
288                                                 return true;\r
289                                 }\r
290                                 for (count++; this.MoveToNextAttribute (); count++) {\r
291                                         if (count == i)\r
292                                                 return true;\r
293                                 }\r
294                                 break;\r
295                         }\r
296                         return false;\r
297                 }\r
298 \r
299                 public override string this [int i] {\r
300                         get {\r
301                                 XPathNavigator backup = current.Clone ();\r
302                                 try {\r
303                                         if (MoveToAttributeNavigator (i))\r
304                                                 return Value;\r
305                                         else\r
306                                                 throw new ArgumentOutOfRangeException ();\r
307                                 } finally {\r
308                                         current.MoveTo (backup);\r
309                                 }\r
310                         }\r
311                 }\r
312                 */\r
313 \r
314                 private void SplitName (string name, out string localName, out string ns)\r
315                 {\r
316                         if (name == "xmlns") {\r
317                                 localName = "xmlns";\r
318                                 ns = XmlNamespaceManager.XmlnsXmlns;\r
319                                 return;\r
320                         }\r
321                         localName = name;\r
322                         ns = String.Empty;\r
323                         int colon = name.IndexOf (':');\r
324                         if (colon > 0) {\r
325                                 localName = name.Substring (colon + 1, name.Length - colon - 1);\r
326                                 ns = this.LookupNamespace (name.Substring (0, colon));\r
327                                 if (name.Substring (0, colon) == "xmlns")\r
328                                         ns = XmlNamespaceManager.XmlnsXmlns;\r
329                         }\r
330                 }\r
331 \r
332                 public override string this [string name] {\r
333                         get {\r
334                                 string localName;\r
335                                 string ns;\r
336                                 SplitName (name, out localName, out ns);\r
337                                 return this [localName, ns];\r
338                         }\r
339                 }\r
340 \r
341                 public override string this [string localName, string namespaceURI] {\r
342                         get {\r
343                                 string v = current.GetAttribute (localName, namespaceURI);\r
344                                 if (v != String.Empty)\r
345                                         return v;\r
346                                 XPathNavigator tmp = current.Clone ();\r
347                                 return tmp.MoveToAttribute (localName, namespaceURI) ? String.Empty : null;\r
348                         }\r
349                 }\r
350 \r
351                 public override bool EOF {\r
352                         get { return ReadState == ReadState.EndOfFile; }\r
353                 }\r
354 \r
355                 public override ReadState ReadState {\r
356                         get {\r
357                                 if (eof)\r
358                                         return ReadState.EndOfFile;\r
359                                 if (closed)\r
360                                         return ReadState.Closed;\r
361                                 else if (!started)\r
362                                         return ReadState.Initial;\r
363                                 return ReadState.Interactive;\r
364                         }\r
365                 }\r
366 \r
367                 public override XmlNameTable NameTable {\r
368                         get { return current.NameTable; }\r
369                 }\r
370                 #endregion\r
371 \r
372                 #region Methods\r
373 \r
374                 public override string GetAttribute (string name)\r
375                 {\r
376                         string localName;\r
377                         string ns;\r
378                         SplitName (name, out localName, out ns);\r
379                         return this [localName, ns];\r
380                 }\r
381 \r
382                 public override string GetAttribute (string localName, string namespaceURI)\r
383                 {\r
384                         return this [localName, namespaceURI];\r
385                 }\r
386 \r
387                 public override string GetAttribute (int i)\r
388                 {\r
389                         return this [i];\r
390                 }\r
391 \r
392                 private bool CheckAttributeMove (bool b)\r
393                 {\r
394                         if (b)\r
395                                 attributeValueConsumed = false;\r
396                         return b;\r
397                 }\r
398 \r
399                 public override bool MoveToAttribute (string name)\r
400                 {\r
401                         string localName;\r
402                         string ns;\r
403                         SplitName (name, out localName, out ns);\r
404                         return MoveToAttribute (localName, ns);\r
405                 }\r
406 \r
407                 public override bool MoveToAttribute (string localName, string namespaceURI)\r
408                 {\r
409                         bool isNS = namespaceURI == "http://www.w3.org/2000/xmlns/";\r
410                         XPathNavigator backup = null;\r
411                         switch (current.NodeType) {\r
412                         case XPathNodeType.Namespace:\r
413                         case XPathNodeType.Attribute:\r
414                                 backup = current.Clone ();\r
415                                 this.MoveToElement ();\r
416                                 goto case XPathNodeType.Element;\r
417                         case XPathNodeType.Element:\r
418                                 if (MoveToFirstAttribute ()) {\r
419                                         do {\r
420                                                 bool match = false;\r
421                                                 if (isNS) {\r
422                                                         if (localName == "xmlns")\r
423                                                                 match = current.Name == String.Empty;\r
424                                                         else\r
425                                                                 match = localName == current.Name;\r
426                                                 }\r
427                                                 else\r
428                                                         match = current.LocalName == localName &&\r
429                                                                 current.NamespaceURI == namespaceURI;\r
430                                                 if (match) {\r
431                                                         attributeValueConsumed = false;\r
432                                                         return true;\r
433                                                 }\r
434                                         } while (MoveToNextAttribute ());\r
435                                         MoveToElement ();\r
436                                 }\r
437                                 break;\r
438                         }\r
439                         if (backup != null)\r
440                                 current = backup;\r
441                         return false;\r
442                 }\r
443 \r
444                 public override bool MoveToFirstAttribute ()\r
445                 {\r
446                         switch (current.NodeType) {\r
447                         case XPathNodeType.Element:\r
448                                 if (CheckAttributeMove (current.MoveToFirstNamespace (XPathNamespaceScope.Local)))\r
449                                         return true;\r
450                                 return CheckAttributeMove (current.MoveToFirstAttribute ());\r
451                         case XPathNodeType.Namespace:\r
452                         case XPathNodeType.Attribute:\r
453                                 current.MoveToParent ();\r
454                                 goto case XPathNodeType.Element;\r
455                         default:\r
456                                 return false;\r
457                         }\r
458                 }\r
459 \r
460                 public override bool MoveToNextAttribute ()\r
461                 {\r
462                         switch (current.NodeType) {\r
463                         case XPathNodeType.Element:\r
464                                 return MoveToFirstAttribute ();\r
465                         case XPathNodeType.Namespace:\r
466                                 if (CheckAttributeMove (current.MoveToNextNamespace (XPathNamespaceScope.Local)))\r
467                                         return true;\r
468                                 XPathNavigator bak = current.Clone ();\r
469                                 current.MoveToParent ();\r
470                                 if (CheckAttributeMove (current.MoveToFirstAttribute ()))\r
471                                         return true;\r
472                                 current.MoveTo (bak);\r
473                                 return false;\r
474                         case XPathNodeType.Attribute:\r
475                                 return CheckAttributeMove (current.MoveToNextAttribute ());\r
476                         default:\r
477                                 return false;\r
478                         }\r
479                 }\r
480 \r
481                 public override bool MoveToElement ()\r
482                 {\r
483                         if (current.NodeType == XPathNodeType.Attribute ||\r
484                                 current.NodeType == XPathNodeType.Namespace) {\r
485                                 attributeValueConsumed = false;\r
486                                 return current.MoveToParent ();\r
487                         }\r
488                         return false;\r
489                 }\r
490 \r
491                 public override void Close ()\r
492                 {\r
493                         closed = true;\r
494                         eof = true;\r
495                 }\r
496 \r
497                 public override bool Read ()\r
498                 {\r
499                         if (eof)\r
500                                 return false;\r
501 #if NET_2_0\r
502                         if (Binary != null)\r
503                                 Binary.Reset ();\r
504 #endif\r
505 \r
506                         switch (ReadState) {\r
507                         case ReadState.Interactive:\r
508                                 if ((IsEmptyElement || endElement) && root.IsSamePosition (current)) {\r
509                                         eof = true;\r
510                                         return false;\r
511                                 }\r
512                                 break;\r
513                         case ReadState.EndOfFile:\r
514                         case ReadState.Closed:\r
515                         case ReadState.Error:\r
516                                 return false;\r
517                         case ReadState.Initial:\r
518                                 started = true;\r
519                                 root = current.Clone ();\r
520                                 if (current.NodeType == XPathNodeType.Root &&\r
521                                         !current.MoveToFirstChild ()) {\r
522                                         endElement = false;\r
523                                         eof = true;\r
524                                         return false;\r
525                                 }\r
526                                 attributeCount = GetAttributeCount ();\r
527                                 return true;\r
528                         }\r
529 \r
530                         MoveToElement ();\r
531 \r
532                         if (endElement || !current.MoveToFirstChild ()) {\r
533                                 // EndElement of current element.\r
534                                 if (!endElement && !current.IsEmptyElement &&\r
535                                         current.NodeType == XPathNodeType.Element)\r
536                                         endElement = true;\r
537                                 else if (!current.MoveToNext ()) {\r
538                                         current.MoveToParent ();\r
539                                         if (current.NodeType == XPathNodeType.Root) {\r
540                                                 endElement = false;\r
541                                                 eof = true;\r
542                                                 return false;\r
543                                         }\r
544                                         endElement = (current.NodeType == XPathNodeType.Element);\r
545                                         if (endElement)\r
546                                                 depth--;\r
547                                 }\r
548                                 else\r
549                                         endElement = false;\r
550                         }\r
551                         else\r
552                                 depth++;\r
553 \r
554                         if (!endElement && current.NodeType == XPathNodeType.Element)\r
555                                 attributeCount = GetAttributeCount ();\r
556                         else\r
557                                 attributeCount = 0;\r
558 \r
559                         return true;\r
560                 }\r
561 \r
562                 public override string ReadString ()\r
563                 {\r
564                         readStringBuffer.Length = 0;\r
565 \r
566                         switch (NodeType) {\r
567                         default:\r
568                                 return String.Empty;\r
569                         case XmlNodeType.Element:\r
570                                 if (IsEmptyElement)\r
571                                         return String.Empty;\r
572                                 do {\r
573                                         Read ();\r
574                                         switch (NodeType) {\r
575                                         case XmlNodeType.Text:\r
576                                         case XmlNodeType.CDATA:\r
577                                         case XmlNodeType.Whitespace:\r
578                                         case XmlNodeType.SignificantWhitespace:\r
579                                                 readStringBuffer.Append (Value);\r
580                                                 continue;\r
581                                         }\r
582                                         break;\r
583                                 } while (true);\r
584                                 break;\r
585                         case XmlNodeType.Text:\r
586                         case XmlNodeType.CDATA:\r
587                         case XmlNodeType.Whitespace:\r
588                         case XmlNodeType.SignificantWhitespace:\r
589                                 do {\r
590                                         switch (NodeType) {\r
591                                         case XmlNodeType.Text:\r
592                                         case XmlNodeType.CDATA:\r
593                                         case XmlNodeType.Whitespace:\r
594                                         case XmlNodeType.SignificantWhitespace:\r
595                                                 readStringBuffer.Append (Value);\r
596                                                 Read ();\r
597                                                 continue;\r
598                                         }\r
599                                         break;\r
600                                 } while (true);\r
601                                 break;\r
602                         }\r
603                         string ret = readStringBuffer.ToString ();\r
604                         readStringBuffer.Length = 0;\r
605                         return ret;\r
606                 }\r
607 \r
608 \r
609                 public override string LookupNamespace (string prefix)\r
610                 {\r
611                         XPathNavigator backup = current.Clone ();\r
612                         try {\r
613                                 this.MoveToElement ();\r
614                                 if (current.NodeType != XPathNodeType.Element) // text etc.\r
615                                         current.MoveToParent ();\r
616                                 if (current.MoveToFirstNamespace ()) {\r
617                                         do {\r
618                                                 if (current.LocalName == prefix)\r
619                                                         return current.Value;\r
620                                         } while (current.MoveToNextNamespace ());\r
621                                 }\r
622                                 return null;\r
623                         } finally {\r
624                                 current = backup;\r
625                         }\r
626                 }\r
627 \r
628                 // It does not support entity resolution.\r
629                 public override void ResolveEntity ()\r
630                 {\r
631                         throw new InvalidOperationException ();\r
632                 }\r
633 \r
634                 public override bool ReadAttributeValue () {\r
635                         if (NodeType != XmlNodeType.Attribute)\r
636                                 return false;\r
637                         if (attributeValueConsumed)\r
638                                 return false;\r
639                         attributeValueConsumed = true;\r
640                         return true;\r
641                 }\r
642                 #endregion\r
643         }\r
644 }\r