merged Sys.Web.Services 2.0 support in my branch:
[mono.git] / mcs / class / Mono.Xml.Ext / Mono.Xml.XPath2 / SequenceType.cs
1 //
2 // SequenceType.cs - represents XPath 2.0 item type
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 //
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 #if NET_2_0
31 using System;
32 using System.Collections;
33 using System.Globalization;
34 using System.Xml;
35 using System.Xml.Schema;
36 using System.Xml.Query;
37 using System.Xml.XPath;
38 using Mono.Xml;
39
40 namespace Mono.Xml.XPath2
41 {
42         public class SequenceType
43         {
44                 static SequenceType singleItem = new SequenceType (ItemType.AnyItem, Occurence.One);
45                 static SequenceType singleAnyAtomic = new SequenceType (ItemType.AnyAtomicType, Occurence.One);
46
47                 internal static SequenceType AnyType {
48                         get { return Create (InternalPool.XsAnyType, Occurence.ZeroOrMore); }
49                 }
50                 internal static SequenceType SingleItem {
51                         get { return singleItem; }
52                 }
53                 internal static SequenceType SingleAnyAtomic {
54                         get { return singleAnyAtomic; }
55                 }
56
57                 internal static SequenceType Node {
58                         get { return Create (XmlTypeCode.Node, Occurence.One); }
59                 }
60                 internal static SequenceType Document {
61                         get { return Create (XmlTypeCode.Document, Occurence.One); }
62                 }
63                 internal static SequenceType Element {
64                         get { return Create (XmlTypeCode.Element, Occurence.One); }
65                 }
66                 internal static SequenceType Attribute {
67                         get { return Create (XmlTypeCode.Attribute, Occurence.One); }
68                 }
69                 internal static SequenceType Namespace {
70                         get { return Create (XmlTypeCode.Namespace, Occurence.One); }
71                 }
72                 internal static SequenceType Text {
73                         get { return Create (XmlTypeCode.Text, Occurence.One); }
74                 }
75                 internal static SequenceType XmlPI {
76                         get { return Create (XmlTypeCode.ProcessingInstruction, Occurence.One); }
77                 }
78                 internal static SequenceType Comment {
79                         get { return Create (XmlTypeCode.Comment, Occurence.One); }
80                 }
81
82                 internal static SequenceType AtomicString {
83                         get { return Create (InternalPool.XsString, Occurence.One); }
84                 }
85                 internal static SequenceType Boolean {
86                         get { return Create (InternalPool.XsBoolean, Occurence.One); }
87                 }
88                 internal static SequenceType Decimal {
89                         get { return Create (InternalPool.XsDecimal, Occurence.One); }
90                 }
91                 internal static SequenceType Integer {
92                         get { return Create (InternalPool.XsInteger, Occurence.One); }
93                 }
94                 internal static SequenceType Int {
95                         get { return Create (InternalPool.XsInt, Occurence.One); }
96                 }
97                 internal static SequenceType Short {
98                         get { return Create (InternalPool.XsShort, Occurence.One); }
99                 }
100                 internal static SequenceType UnsignedInt {
101                         get { return Create (InternalPool.XsUnsignedInt, Occurence.One); }
102                 }
103                 internal static SequenceType UnsignedShort {
104                         get { return Create (InternalPool.XsUnsignedShort, Occurence.One); }
105                 }
106                 internal static SequenceType Double {
107                         get { return Create (InternalPool.XsDouble, Occurence.One); }
108                 }
109                 internal static SequenceType Single {
110                         get { return Create (InternalPool.XsFloat, Occurence.One); }
111                 }
112                 internal static SequenceType DateTime {
113                         get { return Create (InternalPool.XsDateTime, Occurence.One); }
114                 }
115                 internal static SequenceType QName {
116                         get { return Create (InternalPool.XsQName, Occurence.One); }
117                 }
118
119                 internal static SequenceType IntegerList {
120                         get { return Create (XmlTypeCode.Integer, Occurence.ZeroOrMore); }
121                 }
122
123
124                 static Hashtable standardTypes = new Hashtable ();
125
126                 internal static SequenceType Create (Type cliType)
127                 {
128                         // typed Array
129                         if (cliType.IsArray)
130                                 return Create (InternalPool.XmlTypeCodeFromRuntimeType (cliType.GetElementType (), true), Occurence.ZeroOrMore);
131 //                      if (cliType.GetInterface ("System.Collections.IEnumerable") != null)
132 //                              return Create (XmlTypeCode.Item, Occurence.ZeroOrMore);
133                         if (cliType == typeof (XmlQualifiedName))
134                                 return QName;
135                         if (cliType == typeof (XPathNavigator) || cliType.IsSubclassOf (typeof (XPathNavigator)))
136                                 return Node;
137                         if (cliType == typeof (XPathAtomicValue))
138                                 return SingleAnyAtomic;
139                         if (cliType == typeof (XPathItem))
140                                 return SingleItem;
141                         // FIXME: handle Nullable type
142                         return Create (InternalPool.XmlTypeCodeFromRuntimeType (cliType, true), Occurence.One);
143                 }
144
145                 internal static SequenceType Create (XmlTypeCode typeCode, Occurence occurence)
146                 {
147                         switch (typeCode) {
148                         case XmlTypeCode.Item:
149                         case XmlTypeCode.AnyAtomicType:
150                         case XmlTypeCode.Node:
151                         case XmlTypeCode.Document:
152                         case XmlTypeCode.Element:
153                         case XmlTypeCode.Attribute:
154                         case XmlTypeCode.ProcessingInstruction:
155                         case XmlTypeCode.Comment:
156                         case XmlTypeCode.Namespace:
157                         case XmlTypeCode.Text:
158                                 return new SequenceType (new ItemType (typeCode), occurence);
159                         default:
160                                 return Create (XmlSchemaType.GetBuiltInSimpleType (typeCode), occurence);
161                         }
162                 }
163
164                 internal static SequenceType Create (XmlSchemaType schemaType, Occurence occurence)
165                 {
166                         switch (schemaType.QualifiedName.Namespace) {
167                         case XmlSchema.Namespace:
168                         case InternalPool.XdtNamespace:
169                                 break;
170                         default:
171                                 return new SequenceType (schemaType, occurence);
172                         }
173
174                         Hashtable cacheForType = standardTypes [schemaType] as Hashtable;
175                         if (cacheForType == null) {
176                                 cacheForType = new Hashtable ();
177                                 standardTypes [schemaType] = cacheForType;
178                         } else {
179                                 SequenceType type = cacheForType [occurence] as SequenceType;
180                                 if (type != null)
181                                         return type;
182                         }
183                         SequenceType t = new SequenceType (schemaType, occurence);
184                         cacheForType [occurence] = t;
185                         return t;
186                 }
187
188                 [MonoTODO]
189                 internal static SequenceType ComputeCommonBase (SequenceType t1, SequenceType t2)
190                 {
191                         // FIXME: implement
192                         // throw new NotImplementedException ();
193                         return SequenceType.AnyType;
194                 }
195
196                 internal static bool IsNumeric (XmlTypeCode code)
197                 {
198                         switch (code) {
199                         case XmlTypeCode.Decimal:
200                         case XmlTypeCode.Float:
201                         case XmlTypeCode.Double:
202                         case XmlTypeCode.Integer:
203                         case XmlTypeCode.NonPositiveInteger:
204                         case XmlTypeCode.NegativeInteger:
205                         case XmlTypeCode.Long:
206                         case XmlTypeCode.Int:
207                         case XmlTypeCode.Short:
208                         case XmlTypeCode.Byte:
209                         case XmlTypeCode.NonNegativeInteger:
210                         case XmlTypeCode.UnsignedLong:
211                         case XmlTypeCode.UnsignedInt:
212                         case XmlTypeCode.UnsignedShort:
213                         case XmlTypeCode.UnsignedByte:
214                         case XmlTypeCode.PositiveInteger:
215                                 return true;
216                         }
217                         return false;
218                 }
219
220                 // Instance members
221
222                 private SequenceType (XmlSchemaType schemaType, Occurence occurence)
223                 {
224                         this.schemaType = schemaType;
225                         this.itemType = ItemType.AnyItem;
226                         this.occurence = occurence;
227                 }
228
229                 internal SequenceType (ItemType itemType, Occurence occurence)
230                 {
231                         this.schemaType = InternalPool.XsAnyType;
232                         this.itemType = itemType;
233                         this.occurence = occurence;
234                 }
235
236                 XmlSchemaType schemaType;
237                 Occurence occurence;
238                 ItemType itemType;
239
240                 public XmlSchemaType SchemaType {
241                         get { return schemaType; }
242                 }
243
244                 public ItemType ItemType {
245                         get { return itemType; }
246                 }
247
248                 public Occurence Occurence {
249                         get { return occurence; }
250                 }
251
252                 internal bool Matches (XPathSequence iter)
253                 {
254                         throw new NotImplementedException ();
255                 }
256
257                 [MonoTODO]
258                 internal bool CanConvertTo (SequenceType other)
259                 {
260                         // FIXME: implement precisely
261                         return this == other;
262                         // throw new NotImplementedException ();
263                 }
264
265                 internal bool CanConvert (XPathSequence iter)
266                 {
267                         bool occured = false;
268                         bool onlyOnce = (occurence == Occurence.One || occurence == Occurence.Optional);
269                         bool required = (occurence == Occurence.One || occurence == Occurence.OneOrMore);
270                         foreach (XPathItem item in iter) {
271                                 if (occured && onlyOnce)
272                                         return false;
273                                 if (!CanConvert (item))
274                                         return false;
275                         }
276                         return occured || !required;
277                 }
278
279                 public bool CanConvert (XPathItem item)
280                 {
281                         throw new NotImplementedException ();
282                 }
283
284                 public bool IsInstance (XPathItem item)
285                 {
286                         throw new NotImplementedException ();
287                 }
288
289                 public XPathItem Convert (XPathItem item)
290                 {
291                         throw new NotImplementedException ();
292                 }
293
294                 public object ToRuntimeType (XPathSequence seq)
295                 {
296                         // FIXME: handle ZeroOrMore|OneOrMore
297                         switch (occurence) {
298                         case Occurence.One:
299                         case Occurence.Optional:
300                                 if (!seq.MoveNext ())
301                                         return null;
302                                 XPathItem item = seq.Current;
303                                 // FIXME: should check and reject two or 
304                                 // more items??
305                                 return item.TypedValue;
306                         }
307                         ArrayList al = new ArrayList ();
308                         while (seq.MoveNext ())
309                                 al.Add (seq.Current.TypedValue);
310                         return al.ToArray (InternalPool.RuntimeTypeFromXmlTypeCode (schemaType.TypeCode));
311 //                      return seq;
312                 }
313         }
314
315         public enum Occurence
316         {
317                 One,
318                 Optional,
319                 ZeroOrMore,
320                 OneOrMore,
321         }
322
323         #region NodeTest
324
325         public enum XPathAxisType
326         {
327                 Child,
328                 Descendant,
329                 Attribute,
330                 Self,
331                 DescendantOrSelf,
332                 FollowingSibling,
333                 Following,
334                 Parent,
335                 Ancestor,
336                 PrecedingSibling,
337                 Preceding,
338                 AncestorOrSelf,
339                 Namespace // only applicable under XPath 2.0, not XQuery 1.0
340         }
341
342         public class XPathAxis
343         {
344                 // FIXME: add more parameters to distinguish them
345                 private XPathAxis (XPathAxisType axisType)
346                 {
347                         this.axisType = axisType;
348                         switch (axisType) {
349                         case XPathAxisType.Parent:
350                         case XPathAxisType.Ancestor:
351                         case XPathAxisType.AncestorOrSelf:
352                         case XPathAxisType.Preceding:
353                         case XPathAxisType.PrecedingSibling:
354                                 this.reverse = true;
355                                 break;
356                         }
357                 }
358
359                 bool reverse;
360                 XPathAxisType axisType;
361
362                 public bool ReverseAxis {
363                         get { return reverse; }
364                 }
365
366                 public XPathAxisType AxisType {
367                         get { return axisType; }
368                 }
369
370                 static XPathAxis child, descendant, attribute, self, 
371                         descendantOrSelf, followingSibling, following, 
372                         parent, ancestor, precedingSibling, preceding, 
373                         ancestorOrSelf, namespaceAxis;
374
375                 static XPathAxis ()
376                 {
377                         child = new XPathAxis (XPathAxisType.Child);
378                         descendant = new XPathAxis (XPathAxisType.Descendant);
379                         attribute = new XPathAxis (XPathAxisType.Attribute);
380                         self = new XPathAxis (XPathAxisType.Self);
381                         descendantOrSelf = new XPathAxis (XPathAxisType.DescendantOrSelf);
382                         followingSibling = new XPathAxis (XPathAxisType.FollowingSibling);
383                         following = new XPathAxis (XPathAxisType.Following);
384                         parent = new XPathAxis (XPathAxisType.Parent);
385                         ancestor = new XPathAxis (XPathAxisType.Ancestor);
386                         precedingSibling = new XPathAxis (XPathAxisType.PrecedingSibling);
387                         preceding = new XPathAxis (XPathAxisType.Preceding);
388                         ancestorOrSelf = new XPathAxis (XPathAxisType.AncestorOrSelf);
389                         namespaceAxis = new XPathAxis (XPathAxisType.Namespace);
390                 }
391
392                 public static XPathAxis Child {
393                         get { return child; }
394                 }
395
396                 public static XPathAxis Descendant {
397                         get { return descendant; }
398                 }
399
400                 public static XPathAxis Attribute {
401                         get { return attribute; }
402                 }
403
404                 public static XPathAxis Self {
405                         get { return self; }
406                 }
407
408                 public static XPathAxis DescendantOrSelf {
409                         get { return descendantOrSelf; }
410                 }
411
412                 public static XPathAxis FollowingSibling {
413                         get { return followingSibling; }
414                 }
415
416                 public static XPathAxis Following {
417                         get { return following; }
418                 }
419
420                 public static XPathAxis NamespaceAxis {
421                         get { return namespaceAxis; }
422                 }
423
424                 public static XPathAxis Parent {
425                         get { return parent; }
426                 }
427
428                 public static XPathAxis Ancestor {
429                         get { return ancestor; }
430                 }
431
432                 public static XPathAxis PrecedingSibling {
433                         get { return precedingSibling; }
434                 }
435
436                 public static XPathAxis Preceding {
437                         get { return preceding; }
438                 }
439
440                 public static XPathAxis AncestorOrSelf {
441                         get { return ancestorOrSelf; }
442                 }
443         }
444
445         // ItemType
446         public class ItemType
447         {
448                 static ItemType anyItem = new ItemType (XmlTypeCode.Item);
449                 static ItemType anyAtomicType = new ItemType (XmlTypeCode.AnyAtomicType);
450
451                 public static ItemType AnyItem {
452                         get { return anyItem; }
453                 }
454
455                 public static ItemType AnyAtomicType {
456                         get { return anyAtomicType; }
457                 }
458
459                 XmlTypeCode typeCode;
460
461                 public ItemType (XmlTypeCode typeCode)
462                 {
463                         this.typeCode = typeCode;
464                 }
465
466                 public XmlTypeCode TypeCode {
467                         get { return typeCode; }
468                 }
469
470                 internal virtual void CheckReference (XQueryASTCompiler compiler)
471                 {
472                 }
473         }
474
475         // KindTest
476
477         public class KindTest : ItemType
478         {
479                 public KindTest (XmlTypeCode type)
480                         : base (type)
481                 {
482                 }
483
484                 internal virtual void Compile (XQueryASTCompiler compiler)
485                 {
486                 }
487
488                 public virtual bool Matches (XPathItem item)
489                 {
490                         XPathNavigator nav = item as XPathNavigator;
491                         if (nav == null)
492                                 return false;
493                         // FIXME: is it true? ('untyped' means 'matches with any type' ?
494                         if (item.XmlType == null)
495                                 return true;
496                         if (item.XmlType.TypeCode != TypeCode)
497                                 return false;
498                         return true;
499                 }
500         }
501
502         public class DocumentTest : KindTest
503         {
504                 ElementTest content;
505
506                 public DocumentTest (ElementTest content)
507                         : base (XmlTypeCode.Document)
508                 {
509                         this.content = content;
510                 }
511
512                 public ElementTest Content {
513                         get { return content; }
514                 }
515
516                 internal override void CheckReference (XQueryASTCompiler compiler)
517                 {
518                         content.CheckReference (compiler);
519                 }
520
521                 internal override void Compile (XQueryASTCompiler compiler)
522                 {
523                 }
524
525                 public override bool Matches (XPathItem item)
526                 {
527                         XPathNavigator nav = item as XPathNavigator;
528                         if (nav == null)
529                                 return false;
530
531                         if (item.XmlType.TypeCode != XmlTypeCode.Document)
532                                 return false;
533
534                         if (Content == null)
535                                 return true;
536
537                         nav = nav.Clone ();
538                         nav.MoveToFirstChild ();
539                         while (nav.NodeType != XPathNodeType.Element)
540                                 if (!nav.MoveToNext ())
541                                         return false;
542                         return Content.Matches (nav);
543                 }
544         }
545
546         public class ElementTest : KindTest
547         {
548                 XmlQualifiedName name;
549                 XmlQualifiedName typeName;
550                 XmlSchemaType schemaType;
551                 bool nillable;
552
553                 public ElementTest (XmlQualifiedName name)
554                         : base (XmlTypeCode.Element)
555                 {
556                         this.name = name;
557                 }
558
559                 public ElementTest (XmlQualifiedName name, XmlQualifiedName type, bool nillable)
560                         : base (XmlTypeCode.Element)
561                 {
562                         this.name = name;
563                         this.typeName = type;
564                         this.nillable = nillable;
565                 }
566
567                 public XmlQualifiedName Name {
568                         get { return name; }
569                 }
570
571                 public XmlQualifiedName TypeName {
572                         get { return typeName; }
573                 }
574
575                 public XmlSchemaType SchemaType {
576                         get {
577                                 return schemaType;
578                         }
579                 }
580
581                 public bool Nillable {
582                         get { return nillable; }
583                 }
584
585                 internal override void CheckReference (XQueryASTCompiler compiler)
586                 {
587                         compiler.CheckSchemaTypeName (typeName);
588                 }
589
590                 internal override void Compile (XQueryASTCompiler compiler)
591                 {
592                         schemaType = compiler.ResolveSchemaType (TypeName);
593                         if (schemaType == null)
594                                 throw new XmlQueryCompileException ("Specified schema type was not found.");
595                 }
596
597                 public override bool Matches (XPathItem item)
598                 {
599                         XPathNavigator nav = item as XPathNavigator;
600                         if (nav == null)
601                                 return false;
602
603                         if (item.XmlType.TypeCode != XmlTypeCode.Element)
604                                 return false;
605
606                         if (Name != XmlQualifiedName.Empty)
607                                 if (nav.LocalName != Name.Name || nav.NamespaceURI != Name.Namespace)
608                                         return false;
609
610                         // FIXME: it won't be XQueryConvert.CanConvert(), but other strict-matching evaluation
611                         if (SchemaType != null && !XQueryConvert.CanConvert (item, SchemaType))
612                                 return false;
613                         // FIXME: check nillable
614
615                         return true;
616                 }
617         }
618
619         public class AttributeTest : KindTest
620         {
621                 static AttributeTest anyAttribute;
622
623                 static AttributeTest ()
624                 {
625                         anyAttribute = new AttributeTest (XmlQualifiedName.Empty);
626                 }
627
628                 public static AttributeTest AnyAttribute {
629                         get { return anyAttribute; }
630                 }
631
632                 public AttributeTest (XmlQualifiedName name)
633                         : base (XmlTypeCode.Attribute)
634                 {
635                         this.name = name;
636                 }
637
638                 public AttributeTest (XmlQualifiedName name, XmlQualifiedName typeName)
639                         : base (XmlTypeCode.Attribute)
640                 {
641                         this.name = name;
642                         this.typeName = typeName;
643                 }
644
645                 XmlQualifiedName name;
646                 XmlQualifiedName typeName;
647                 XmlSchemaType schemaType;
648
649                 public XmlQualifiedName Name {
650                         get { return name; }
651                 }
652
653                 public XmlQualifiedName TypeName {
654                         get { return typeName; }
655                 }
656
657                 public XmlSchemaType SchemaType {
658                         get { return schemaType; }
659                 }
660
661                 internal override void CheckReference (XQueryASTCompiler compiler)
662                 {
663                         compiler.CheckSchemaTypeName (typeName);
664                 }
665
666                 internal override void Compile (XQueryASTCompiler compiler)
667                 {
668                         schemaType = compiler.ResolveSchemaType (TypeName);
669                         if (schemaType == null)
670                                 throw new XmlQueryCompileException ("Specified schema type was not found.");
671                 }
672
673                 public override bool Matches (XPathItem item)
674                 {
675                         XPathNavigator nav = item as XPathNavigator;
676                         if (nav == null)
677                                 return false;
678
679                         if (item.XmlType.TypeCode != XmlTypeCode.Attribute)
680                                 return false;
681
682                         if (Name != XmlQualifiedName.Empty)
683                                 if (nav.LocalName != Name.Name || nav.NamespaceURI != Name.Namespace)
684                                         return false;
685
686                         // FIXME: it won't be XQueryConvert.CanConvert(), but other strict-matching evaluation
687                         if (SchemaType != null && !XQueryConvert.CanConvert (item, SchemaType))
688                                 return false;
689
690                         return true;
691                 }
692         }
693
694         public class XmlPITest : KindTest
695         {
696                 string name;
697
698                 public XmlPITest (string nameTest)
699                         : base (XmlTypeCode.ProcessingInstruction)
700                 {
701                         this.name = nameTest;
702                 }
703
704                 public string Name {
705                         get { return name; }
706                 }
707
708                 internal override void CheckReference (XQueryASTCompiler compiler)
709                 {
710                 }
711
712                 internal override void Compile (XQueryASTCompiler compiler)
713                 {
714                 }
715
716                 public override bool Matches (XPathItem item)
717                 {
718                         XPathNavigator nav = item as XPathNavigator;
719                         if (nav == null)
720                                 return false;
721
722                         if (item.XmlType.TypeCode != XmlTypeCode.ProcessingInstruction)
723                                 return false;
724                         if (Name != String.Empty && nav.LocalName != Name)
725                                 return false;
726                         return true;
727                 }
728         }
729
730         #endregion
731 }
732
733 #endif