Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / Mono.Xml.Ext / 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         public class XPathNavigatorReader : XmlReader\r
41         {\r
42                 public XPathNavigatorReader (XPathNavigator nav)\r
43                 {\r
44                         // It seems that this class have only to support linked\r
45                         // node as its parameter\r
46                         switch (nav.NodeType) {\r
47                         case XPathNodeType.Attribute:\r
48                         case XPathNodeType.Namespace:\r
49                                 throw new InvalidOperationException (String.Format ("NodeType {0} is not supported to read as a subtree of an XPathNavigator.", nav.NodeType));\r
50                         }\r
51                         root = nav.Clone ();\r
52                         current = nav.Clone ();\r
53                 }\r
54 \r
55                 XPathNavigator root;\r
56                 XPathNavigator current;\r
57                 bool started;\r
58                 bool closed;\r
59                 bool endElement;\r
60                 bool attributeValueConsumed;\r
61                 StringBuilder readStringBuffer = new StringBuilder ();\r
62                 StringBuilder innerXmlBuilder = new StringBuilder ();\r
63 \r
64                 int depth = 0;\r
65                 int attributeCount = 0;\r
66                 bool eof;\r
67                 bool nextIsEOF;\r
68 \r
69                 #region Properties\r
70                 public override XmlNodeType NodeType \r
71                 {\r
72                         get {\r
73                                 if (ReadState != ReadState.Interactive)\r
74                                         return XmlNodeType.None;\r
75                                 if (endElement)\r
76                                         return XmlNodeType.EndElement;\r
77                                 if (attributeValueConsumed)\r
78                                         // Is there any way to get other kind of nodes than Text?\r
79                                         return XmlNodeType.Text;\r
80 \r
81                                 switch (current.NodeType) {\r
82                                 case XPathNodeType.Namespace:\r
83                                 case XPathNodeType.Attribute:\r
84                                         return XmlNodeType.Attribute;\r
85                                 case XPathNodeType.Comment:\r
86                                         return XmlNodeType.Comment;\r
87                                 case XPathNodeType.Element:\r
88                                         return XmlNodeType.Element;\r
89                                 case XPathNodeType.ProcessingInstruction:\r
90                                         return XmlNodeType.ProcessingInstruction;\r
91                                 case XPathNodeType.Root:\r
92                                         // It is actually Document, but in XmlReader there is no such situation to return Document.\r
93                                         return XmlNodeType.None;\r
94                                 case XPathNodeType.SignificantWhitespace:\r
95                                         return XmlNodeType.SignificantWhitespace;\r
96                                 case XPathNodeType.Text:\r
97                                         return XmlNodeType.Text;\r
98                                 case XPathNodeType.Whitespace:\r
99                                         return XmlNodeType.Whitespace;\r
100                                 default:\r
101                                         throw new InvalidOperationException (String.Format ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.", current.NodeType));\r
102                                 }\r
103                         }\r
104                 }\r
105 \r
106                 public override string Name {\r
107                         get {\r
108                                 if (eof)\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 (eof)\r
120                                         return String.Empty;\r
121                                 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName == String.Empty)\r
122                                         return "xmlns";\r
123                                 else\r
124                                         return current.LocalName;\r
125                         }\r
126                 }\r
127 \r
128                 public override string NamespaceURI {\r
129                         get {\r
130                                 if (eof)\r
131                                         return String.Empty;\r
132                                 else if (current.NodeType == XPathNodeType.Namespace)\r
133                                         return "http://www.w3.org/2000/xmlns/";\r
134                                 else\r
135                                         return current.NamespaceURI;\r
136                         }\r
137                 }\r
138 \r
139                 public override string Prefix {\r
140                         get {\r
141                                 if (eof)\r
142                                         return String.Empty;\r
143                                 else if (current.NodeType == XPathNodeType.Namespace && current.LocalName != String.Empty)\r
144                                         return "xmlns";\r
145                                 else\r
146                                         return current.Prefix;\r
147                         }\r
148                 }\r
149 \r
150                 public override bool HasValue {\r
151                         get {\r
152                                 switch (current.NodeType) {\r
153                                 case XPathNodeType.Namespace:\r
154                                 case XPathNodeType.Attribute:\r
155                                 case XPathNodeType.Comment:\r
156                                 case XPathNodeType.ProcessingInstruction:\r
157                                 case XPathNodeType.SignificantWhitespace:\r
158                                 case XPathNodeType.Text:\r
159                                 case XPathNodeType.Whitespace:\r
160                                         return true;\r
161                                 }\r
162                                 return false;\r
163                         }\r
164                 }\r
165 \r
166                 public override int Depth {\r
167                         get {\r
168                                 switch (ReadState) {\r
169                                 case ReadState.EndOfFile:\r
170                                 case ReadState.Initial:\r
171                                 case ReadState.Closed:\r
172                                         return 0;\r
173                                 }\r
174                                 return depth;\r
175                         }\r
176                 }\r
177 \r
178                 public override string Value {\r
179                         get {\r
180                                 switch (current.NodeType) {\r
181                                 case XPathNodeType.Namespace:\r
182                                 case XPathNodeType.Attribute:\r
183                                 case XPathNodeType.Comment:\r
184                                 case XPathNodeType.ProcessingInstruction:\r
185                                 case XPathNodeType.SignificantWhitespace:\r
186                                 case XPathNodeType.Text:\r
187                                 case XPathNodeType.Whitespace:\r
188                                         return current.Value;\r
189                                 case XPathNodeType.Element:\r
190                                 case XPathNodeType.Root:\r
191                                         return String.Empty;\r
192                                 default:\r
193                                         throw new InvalidOperationException ("Current XPathNavigator status is {0} which is not acceptable to XmlReader.");\r
194                                 }\r
195                         }\r
196                 }\r
197 \r
198                 public override string BaseURI {\r
199                         get { return current.BaseURI; }\r
200                 }\r
201 \r
202                 public override bool IsEmptyElement {\r
203                         get { return current.IsEmptyElement; }\r
204                 }\r
205 \r
206                 public override bool IsDefault {\r
207                         get {\r
208 #if NET_2_0\r
209                                 IXmlSchemaInfo si = current as IXmlSchemaInfo;\r
210                                 return si != null && si.IsDefault;\r
211 #else\r
212                                 return false; // no way to check this.\r
213 #endif\r
214                         }\r
215                 }\r
216 \r
217                 // It makes no sense.\r
218                 public override char QuoteChar {\r
219                         get { return '\"'; }\r
220                 }\r
221 \r
222                 public override string XmlLang {\r
223                         get { return current.XmlLang; }\r
224                 }\r
225 \r
226                 // It is meaningless.\r
227                 public override XmlSpace XmlSpace {\r
228                         get { return XmlSpace.None; }\r
229                 }\r
230 \r
231                 public override int AttributeCount {\r
232                         get { return attributeCount; }\r
233                 }\r
234 \r
235                 private int GetAttributeCount ()\r
236                 {\r
237                         int count = 0;\r
238                         if (current.MoveToFirstAttribute ()) {\r
239                                 do {\r
240                                         count++;\r
241                                 } while (current.MoveToNextAttribute ());\r
242                                 current.MoveToParent ();\r
243                         }\r
244                         if (current.MoveToFirstNamespace (XPathNamespaceScope.Local)) {\r
245                                 do {\r
246                                         count++;\r
247                                 } while (current.MoveToNextNamespace (XPathNamespaceScope.Local));\r
248                                 current.MoveToParent ();\r
249                         }\r
250                         return count;\r
251                 }\r
252                 \r
253                 private bool MoveToAttributeNavigator (int i)\r
254                 {\r
255                         switch (current.NodeType) {\r
256                         case XPathNodeType.Namespace:\r
257                         case XPathNodeType.Attribute:\r
258                                 this.MoveToElement ();\r
259                                 goto case XPathNodeType.Element;\r
260                         case XPathNodeType.Element:\r
261                                 int count = 0;\r
262                                 if (MoveToFirstAttribute ()) {\r
263                                         if (i == 0)\r
264                                                 return true;\r
265                                 }\r
266                                 for (count++; this.MoveToNextAttribute (); count++) {\r
267                                         if (count == i)\r
268                                                 return true;\r
269                                 }\r
270                                 break;\r
271                         }\r
272                         return false;\r
273                 }\r
274 \r
275                 public override string this [int i] {\r
276                         get {\r
277                                 XPathNavigator backup = current.Clone ();\r
278                                 try {\r
279                                         if (MoveToAttributeNavigator (i))\r
280                                                 return Value;\r
281                                         else\r
282                                                 throw new ArgumentOutOfRangeException ();\r
283                                 } finally {\r
284                                         current.MoveTo (backup);\r
285                                 }\r
286                         }\r
287                 }\r
288 \r
289                 private void SplitName (string name, out string localName, out string ns)\r
290                 {\r
291                         localName = name;\r
292                         ns = String.Empty;\r
293                         int colon = name.IndexOf (':');\r
294                         if (colon > 0) {\r
295                                 localName = name.Substring (colon + 1, name.Length - colon - 1);\r
296                                 ns = this.LookupNamespace (name.Substring (0, colon));\r
297                         }\r
298                 }\r
299 \r
300                 public override string this [string name] {\r
301                         get {\r
302                                 string localName;\r
303                                 string ns;\r
304                                 SplitName (name, out localName, out ns);\r
305                                 return this [localName, ns];\r
306                         }\r
307                 }\r
308 \r
309                 public override string this [string localName, string namespaceURI] {\r
310                         get {\r
311                                 string v = current.GetAttribute (localName, namespaceURI);\r
312                                 if (v != String.Empty)\r
313                                         return v;\r
314                                 XPathNavigator tmp = current.Clone ();\r
315                                 return tmp.MoveToAttribute (localName, namespaceURI) ? String.Empty : null;\r
316                         }\r
317                 }\r
318 \r
319                 public override bool EOF {\r
320                         get { return ReadState == ReadState.EndOfFile; }\r
321                 }\r
322 \r
323                 public override ReadState ReadState {\r
324                         get {\r
325                                 if (eof)\r
326                                         return ReadState.EndOfFile;\r
327                                 if (closed)\r
328                                         return ReadState.Closed;\r
329                                 else if (!started)\r
330                                         return ReadState.Initial;\r
331                                 return ReadState.Interactive;\r
332                         }\r
333                 }\r
334 \r
335                 public override XmlNameTable NameTable {\r
336                         get { return current.NameTable; }\r
337                 }\r
338                 #endregion\r
339 \r
340                 #region Methods\r
341 \r
342                 public override string GetAttribute (string name)\r
343                 {\r
344                         string localName;\r
345                         string ns;\r
346                         SplitName (name, out localName, out ns);\r
347                         return this [localName, ns];\r
348                 }\r
349 \r
350                 public override string GetAttribute (string localName, string namespaceURI)\r
351                 {\r
352                         return this [localName, namespaceURI];\r
353                 }\r
354 \r
355                 public override string GetAttribute (int i)\r
356                 {\r
357                         return this [i];\r
358                 }\r
359 \r
360                 private bool CheckAttributeMove (bool b)\r
361                 {\r
362                         if (b)\r
363                                 attributeValueConsumed = false;\r
364                         return b;\r
365                 }\r
366 \r
367                 public override bool MoveToAttribute (string name)\r
368                 {\r
369                         string localName;\r
370                         string ns;\r
371                         SplitName (name, out localName, out ns);\r
372                         return CheckAttributeMove (MoveToAttribute (localName, ns));\r
373                 }\r
374 \r
375                 public override bool MoveToAttribute (string localName, string namespaceURI)\r
376                 {\r
377                         XPathNavigator backup = null;\r
378                         switch (current.NodeType) {\r
379                         case XPathNodeType.Attribute:\r
380                                 backup = current.Clone ();\r
381                                 this.MoveToElement ();\r
382                                 goto case XPathNodeType.Element;\r
383                         case XPathNodeType.Element:\r
384                                 while (MoveToNextAttribute ())\r
385                                         if (current.LocalName == localName && current.NamespaceURI == namespaceURI) {\r
386                                                 attributeValueConsumed = false;\r
387                                                 return true;\r
388                                         }\r
389                                 break;\r
390                         }\r
391                         if (backup != null)\r
392                                 current = backup;\r
393                         return false;\r
394                 }\r
395 \r
396                 public override void MoveToAttribute (int i)\r
397                 {\r
398                         if (!MoveToAttributeNavigator (i))\r
399                                 throw new ArgumentOutOfRangeException ();\r
400                 }\r
401 \r
402                 public override bool MoveToFirstAttribute ()\r
403                 {\r
404                         bool b = CheckAttributeMove (current.MoveToFirstNamespace (XPathNamespaceScope.Local));\r
405                         if (b)\r
406                                 return true;\r
407                         return CheckAttributeMove (current.MoveToFirstAttribute ());\r
408                 }\r
409 \r
410                 public override bool MoveToNextAttribute ()\r
411                 {\r
412                         if (current.NodeType == XPathNodeType.Namespace) {\r
413                                 bool b = CheckAttributeMove (current.MoveToNextNamespace (XPathNamespaceScope.Local));\r
414                                 if (b)\r
415                                         return true;\r
416                                 current.MoveToParent ();\r
417                                 b = CheckAttributeMove (current.MoveToFirstAttribute ());\r
418                                 if (b)\r
419                                         return true;\r
420                         }\r
421                         return CheckAttributeMove (current.MoveToNextAttribute ());\r
422                 }\r
423 \r
424                 public override bool MoveToElement ()\r
425                 {\r
426                         if (current.NodeType == XPathNodeType.Attribute ||\r
427                                 current.NodeType == XPathNodeType.Namespace) {\r
428                                 attributeValueConsumed = false;\r
429                                 return current.MoveToParent ();\r
430                         }\r
431                         return false;\r
432                 }\r
433 \r
434                 public override void Close ()\r
435                 {\r
436                         closed = true;\r
437                         eof = true;\r
438                 }\r
439 \r
440                 public override bool Read ()\r
441                 {\r
442                         switch (ReadState) {\r
443                         case ReadState.EndOfFile:\r
444                         case ReadState.Closed:\r
445                         case ReadState.Error:\r
446                                 return false;\r
447                         case ReadState.Initial:\r
448                                 started = true;\r
449                                 switch (current.NodeType) {\r
450                                 case XPathNodeType.Root:\r
451                                         // recurse, but as Interactive\r
452                                         return Read ();\r
453                                 case XPathNodeType.Element:\r
454                                         if (current.IsEmptyElement)\r
455                                                 nextIsEOF = true;\r
456                                         attributeCount = GetAttributeCount ();\r
457                                         return true;\r
458                                 default:\r
459                                         nextIsEOF = true;\r
460                                         return true;\r
461                                 }\r
462                                 break;\r
463                         }\r
464 \r
465                         if (nextIsEOF) {\r
466                                 nextIsEOF = false;\r
467                                 eof = true;\r
468                                 return false;\r
469                         }\r
470 \r
471                         MoveToElement ();\r
472 \r
473                         if (endElement || !current.MoveToFirstChild ()) {\r
474                                 if (current.IsSamePosition (root)) {    // It should happen only when the root node was empty.\r
475                                         eof = true;\r
476                                         return false;\r
477                                 }\r
478                                 if (!current.MoveToNext ()) {\r
479                                         current.MoveToParent ();\r
480                                         depth--;\r
481                                         endElement = (current.NodeType == XPathNodeType.Element);\r
482                                         if (current.IsSamePosition (root)) {\r
483                                                 if (current.NodeType == XPathNodeType.Element)\r
484                                                         nextIsEOF = true;\r
485                                                 else {\r
486                                                         endElement = false;\r
487                                                         eof = true;\r
488                                                         return false;\r
489                                                 }\r
490                                         }\r
491                                 } else\r
492                                         endElement = false;\r
493                         }\r
494                         else if (!endElement)\r
495                                 depth++;\r
496                         attributeCount = GetAttributeCount ();\r
497                         return true;\r
498                 }\r
499 \r
500                 public override string ReadString ()\r
501                 {\r
502                         readStringBuffer.Length = 0;\r
503 \r
504                         switch (NodeType) {\r
505                         default:\r
506                                 return String.Empty;\r
507                         case XmlNodeType.Element:\r
508                                 if (IsEmptyElement)\r
509                                         return String.Empty;\r
510                                 do {\r
511                                         Read ();\r
512                                         switch (NodeType) {\r
513                                         case XmlNodeType.Text:\r
514                                         case XmlNodeType.CDATA:\r
515                                         case XmlNodeType.Whitespace:\r
516                                         case XmlNodeType.SignificantWhitespace:\r
517                                                 readStringBuffer.Append (Value);\r
518                                                 continue;\r
519                                         }\r
520                                         break;\r
521                                 } while (true);\r
522                                 break;\r
523                         case XmlNodeType.Text:\r
524                         case XmlNodeType.CDATA:\r
525                         case XmlNodeType.Whitespace:\r
526                         case XmlNodeType.SignificantWhitespace:\r
527                                 do {\r
528                                         switch (NodeType) {\r
529                                         case XmlNodeType.Text:\r
530                                         case XmlNodeType.CDATA:\r
531                                         case XmlNodeType.Whitespace:\r
532                                         case XmlNodeType.SignificantWhitespace:\r
533                                                 readStringBuffer.Append (Value);\r
534                                                 Read ();\r
535                                                 continue;\r
536                                         }\r
537                                         break;\r
538                                 } while (true);\r
539                                 break;\r
540                         }\r
541                         string ret = readStringBuffer.ToString ();\r
542                         readStringBuffer.Length = 0;\r
543                         return ret;\r
544                 }\r
545 \r
546                 public override string LookupNamespace (string prefix)\r
547                 {\r
548                         XPathNavigator backup = current.Clone ();\r
549                         try {\r
550                                 this.MoveToElement ();\r
551                                 if (current.MoveToFirstNamespace ()) {\r
552                                         do {\r
553                                                 if (current.LocalName == prefix)\r
554                                                         return current.Value;\r
555                                         } while (current.MoveToNextNamespace ());\r
556                                 }\r
557                                 return null;\r
558                         } finally {\r
559                                 current = backup;\r
560                         }\r
561                 }\r
562 \r
563                 // It does not support entity resolution.\r
564                 public override void ResolveEntity ()\r
565                 {\r
566                         throw new InvalidOperationException ();\r
567                 }\r
568 \r
569                 public override bool ReadAttributeValue () {\r
570                         if (NodeType != XmlNodeType.Attribute)\r
571                                 return false;\r
572                         if (attributeValueConsumed)\r
573                                 return false;\r
574                         attributeValueConsumed = true;\r
575                         return true;\r
576                 }\r
577                 #endregion\r
578         }\r
579 }\r
580 \r