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