1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.IO;
8 using System.Text;
9 using System.Xml.Schema;
10 
11 namespace System.Xml.Xsl
12 {
13     /// <summary>
14     /// XmlQueryType contains static type information that describes the structure and possible values of dynamic
15     /// instances of the Xml data model.
16     ///
17     /// Every XmlQueryType is composed of a Prime type and a cardinality.  The Prime type itself may be a union
18     /// between several item types.  The XmlQueryType IList<XmlQueryType/> implementation allows callers
19     /// to enumerate the item types.  Other properties expose other information about the type.
20     /// </summary>
21     internal abstract class XmlQueryType : ListBase<XmlQueryType>
22     {
23         private static readonly BitMatrix s_typeCodeDerivation;
24         private int _hashCode;
25 
26 
27         //-----------------------------------------------
28         // Static Constructor
29         //-----------------------------------------------
XmlQueryType()30         static XmlQueryType()
31         {
32             s_typeCodeDerivation = new BitMatrix(s_baseTypeCodes.Length);
33 
34             // Build derivation matrix
35             for (int i = 0; i < s_baseTypeCodes.Length; i++)
36             {
37                 int nextAncestor = i;
38 
39                 while (true)
40                 {
41                     s_typeCodeDerivation[i, nextAncestor] = true;
42                     if ((int)s_baseTypeCodes[nextAncestor] == nextAncestor)
43                         break;
44 
45                     nextAncestor = (int)s_baseTypeCodes[nextAncestor];
46                 }
47             }
48         }
49 
50 
51         //-----------------------------------------------
52         // ItemType, OccurrenceIndicator Properties
53         //-----------------------------------------------
54 
55         /// <summary>
56         /// Static data type code.  The dynamic type is guaranteed to be this type or a subtype of this code.
57         /// This type code includes support for XQuery types that are not part of Xsd, such as Item,
58         /// Node, AnyAtomicType, and Comment.
59         /// </summary>
60         public abstract XmlTypeCode TypeCode { get; }
61 
62         /// <summary>
63         /// Set of allowed names for element, document{element}, attribute and PI
64         /// Returns XmlQualifiedName.Wildcard for all other types
65         /// </summary>
66         public abstract XmlQualifiedNameTest NameTest { get; }
67 
68         /// <summary>
69         /// Static Xsd schema type.  The dynamic type is guaranteed to be this type or a subtype of this type.
70         /// SchemaType will follow these rules:
71         ///   1. If TypeCode is an atomic type code, then SchemaType will be the corresponding non-null simple type
72         ///   2. If TypeCode is Element or Attribute, then SchemaType will be the non-null content type
73         ///   3. If TypeCode is Item, Node, Comment, PI, Text, Document, Namespace, None, then SchemaType will be AnyType
74         /// </summary>
75         public abstract XmlSchemaType SchemaType { get; }
76 
77         /// <summary>
78         /// Permits the element or document{element} node to have the nilled property.
79         /// Returns false for all other types
80         /// </summary>
81         public abstract bool IsNillable { get; }
82 
83         /// <summary>
84         /// This property is always XmlNodeKindFlags.None unless TypeCode = XmlTypeCode.Node, in which case this
85         /// property lists all node kinds that instances of this type may be.
86         /// </summary>
87         public abstract XmlNodeKindFlags NodeKinds { get; }
88 
89         /// <summary>
90         /// If IsStrict is true, then the dynamic type is guaranteed to be the exact same as the static type, and
91         /// will therefore never be a subtype of the static type.
92         /// </summary>
93         public abstract bool IsStrict { get; }
94 
95         /// <summary>
96         /// This property specifies the possible cardinalities that instances of this type may have.
97         /// </summary>
98         public abstract XmlQueryCardinality Cardinality { get; }
99 
100         /// <summary>
101         /// This property returns this type's Prime type, which is always cardinality One.
102         /// </summary>
103         public abstract XmlQueryType Prime { get; }
104 
105         /// <summary>
106         /// True if dynamic data type of all items in this sequence is guaranteed to be not a subtype of Rtf.
107         /// </summary>
108         public abstract bool IsNotRtf { get; }
109 
110         /// <summary>
111         /// True if items in the sequence are guaranteed to be nodes in document order with no duplicates.
112         /// </summary>
113         public abstract bool IsDod { get; }
114 
115         /// <summary>
116         /// The XmlValueConverter maps each XmlQueryType to various Clr types which are capable of representing it.
117         /// </summary>
118         public abstract XmlValueConverter ClrMapping { get; }
119 
120 
121         //-----------------------------------------------
122         // Type Operations
123         //-----------------------------------------------
124 
125         /// <summary>
126         /// Returns true if every possible dynamic instance of this type is also an instance of "baseType".
127         /// </summary>
IsSubtypeOf(XmlQueryType baseType)128         public bool IsSubtypeOf(XmlQueryType baseType)
129         {
130             XmlQueryType thisPrime, basePrime;
131 
132             // Check cardinality sub-typing rules
133             if (!(Cardinality <= baseType.Cardinality) || (!IsDod && baseType.IsDod))
134                 return false;
135 
136             if (!IsDod && baseType.IsDod)
137                 return false;
138 
139             // Check early for common case that two types are the same object
140             thisPrime = Prime;
141             basePrime = baseType.Prime;
142             if ((object)thisPrime == (object)basePrime)
143                 return true;
144 
145             // Check early for common case that two prime types are item types
146             if (thisPrime.Count == 1 && basePrime.Count == 1)
147                 return thisPrime.IsSubtypeOfItemType(basePrime);
148 
149             // Check that each item type in this type is a subtype of some item type in "baseType"
150             foreach (XmlQueryType thisItem in thisPrime)
151             {
152                 bool match = false;
153 
154                 foreach (XmlQueryType baseItem in basePrime)
155                 {
156                     if (thisItem.IsSubtypeOfItemType(baseItem))
157                     {
158                         match = true;
159                         break;
160                     }
161                 }
162 
163                 if (match == false)
164                     return false;
165             }
166 
167             return true;
168         }
169 
170         /// <summary>
171         /// Returns true if a dynamic instance (type None never has an instance) of this type can never be a subtype of "baseType".
172         /// </summary>
NeverSubtypeOf(XmlQueryType baseType)173         public bool NeverSubtypeOf(XmlQueryType baseType)
174         {
175             // Check cardinalities
176             if (Cardinality.NeverSubset(baseType.Cardinality))
177                 return true;
178 
179             // If both this type and "other" type might be empty, it doesn't matter what the prime types are
180             if (MaybeEmpty && baseType.MaybeEmpty)
181                 return false;
182 
183             // None is subtype of every other type
184             if (Count == 0)
185                 return false;
186 
187             // Check item types
188             foreach (XmlQueryType typThis in this)
189             {
190                 foreach (XmlQueryType typThat in baseType)
191                 {
192                     if (typThis.HasIntersectionItemType(typThat))
193                         return false;
194                 }
195             }
196 
197             return true;
198         }
199 
200         /// <summary>
201         /// Strongly-typed Equals that returns true if this type and "that" type are equivalent.
202         /// </summary>
Equals(XmlQueryType that)203         public bool Equals(XmlQueryType that)
204         {
205             if (that == null)
206                 return false;
207 
208             // Check cardinality and DocOrderDistinct property
209             if (Cardinality != that.Cardinality || IsDod != that.IsDod)
210                 return false;
211 
212             // Check early for common case that two types are the same object
213             XmlQueryType thisPrime = Prime;
214             XmlQueryType thatPrime = that.Prime;
215             if ((object)thisPrime == (object)thatPrime)
216                 return true;
217 
218             // Check that count of item types is equal
219             if (thisPrime.Count != thatPrime.Count)
220                 return false;
221 
222             // Check early for common case that two prime types are item types
223             if (thisPrime.Count == 1)
224             {
225                 return (thisPrime.TypeCode == thatPrime.TypeCode &&
226                         thisPrime.NameTest == thatPrime.NameTest &&
227                         thisPrime.SchemaType == thatPrime.SchemaType &&
228                         thisPrime.IsStrict == thatPrime.IsStrict &&
229                         thisPrime.IsNotRtf == thatPrime.IsNotRtf);
230             }
231 
232 
233             // Check that each item type in this type is equal to some item type in "baseType"
234             // (string | int) should be the same type as (int | string)
235             foreach (XmlQueryType thisItem in this)
236             {
237                 bool match = false;
238 
239                 foreach (XmlQueryType thatItem in that)
240                 {
241                     if (thisItem.TypeCode == thatItem.TypeCode &&
242                         thisItem.NameTest == thatItem.NameTest &&
243                         thisItem.SchemaType == thatItem.SchemaType &&
244                         thisItem.IsStrict == thatItem.IsStrict &&
245                         thisItem.IsNotRtf == thatItem.IsNotRtf)
246                     {
247                         // Found match so proceed to next type
248                         match = true;
249                         break;
250                     }
251                 }
252 
253                 if (match == false)
254                     return false;
255             }
256 
257             return true;
258         }
259 
260         /// <summary>
261         /// Overload == operator to call Equals rather than do reference equality.
262         /// </summary>
operator ==(XmlQueryType left, XmlQueryType right)263         public static bool operator ==(XmlQueryType left, XmlQueryType right)
264         {
265             if ((object)left == null)
266                 return ((object)right == null);
267 
268             return left.Equals(right);
269         }
270 
271         /// <summary>
272         /// Overload != operator to call Equals rather than do reference inequality.
273         /// </summary>
operator !=(XmlQueryType left, XmlQueryType right)274         public static bool operator !=(XmlQueryType left, XmlQueryType right)
275         {
276             if ((object)left == null)
277                 return ((object)right != null);
278 
279             return !left.Equals(right);
280         }
281 
282 
283         //-----------------------------------------------
284         // Convenience Properties
285         //-----------------------------------------------
286 
287         /// <summary>
288         /// True if dynamic cardinality of this sequence is guaranteed to be 0.
289         /// </summary>
290         public bool IsEmpty
291         {
292             get { return Cardinality <= XmlQueryCardinality.Zero; }
293         }
294 
295         /// <summary>
296         /// True if dynamic cardinality of this sequence is guaranteed to be 1.
297         /// </summary>
298         public bool IsSingleton
299         {
300             get { return Cardinality <= XmlQueryCardinality.One; }
301         }
302 
303         /// <summary>
304         /// True if dynamic cardinality of this sequence might be 0.
305         /// </summary>
306         public bool MaybeEmpty
307         {
308             get { return XmlQueryCardinality.Zero <= Cardinality; }
309         }
310 
311         /// <summary>
312         /// True if dynamic cardinality of this sequence might be >1.
313         /// </summary>
314         public bool MaybeMany
315         {
316             get { return XmlQueryCardinality.More <= Cardinality; }
317         }
318 
319         /// <summary>
320         /// True if dynamic data type of all items in this sequence is guaranteed to be a subtype of Node.
321         /// Equivalent to calling IsSubtypeOf(TypeFactory.NodeS).
322         /// </summary>
323         public bool IsNode
324         {
325             get { return (s_typeCodeToFlags[(int)TypeCode] & TypeFlags.IsNode) != 0; }
326         }
327 
328         /// <summary>
329         /// True if dynamic data type of all items in this sequence is guaranteed to be a subtype of AnyAtomicType.
330         /// Equivalent to calling IsSubtypeOf(TypeFactory.AnyAtomicTypeS).
331         /// </summary>
332         public bool IsAtomicValue
333         {
334             get { return (s_typeCodeToFlags[(int)TypeCode] & TypeFlags.IsAtomicValue) != 0; }
335         }
336 
337         /// <summary>
338         /// True if dynamic data type of all items in this sequence is guaranteed to be a subtype of Decimal, Double, or Float.
339         /// Equivalent to calling IsSubtypeOf(TypeFactory.NumericS).
340         /// </summary>
341         public bool IsNumeric
342         {
343             get { return (s_typeCodeToFlags[(int)TypeCode] & TypeFlags.IsNumeric) != 0; }
344         }
345 
346 
347         //-----------------------------------------------
348         // System.Object implementation
349         //-----------------------------------------------
350 
351         /// <summary>
352         /// True if "obj" is an XmlQueryType, and this type is the exact same static type.
353         /// </summary>
Equals(object obj)354         public override bool Equals(object obj)
355         {
356             XmlQueryType that = obj as XmlQueryType;
357 
358             if (that == null)
359                 return false;
360 
361             return Equals(that);
362         }
363 
364         /// <summary>
365         /// Return hash code of this instance.
366         /// </summary>
GetHashCode()367         public override int GetHashCode()
368         {
369             if (_hashCode == 0)
370             {
371                 int hash;
372                 XmlSchemaType schemaType;
373 
374                 hash = (int)TypeCode;
375                 schemaType = SchemaType;
376 
377                 unchecked
378                 {
379                     if (schemaType != null)
380                         hash += (hash << 7) ^ schemaType.GetHashCode();
381 
382                     hash += (hash << 7) ^ (int)NodeKinds;
383                     hash += (hash << 7) ^ Cardinality.GetHashCode();
384                     hash += (hash << 7) ^ (IsStrict ? 1 : 0);
385 
386                     // Mix hash code a bit more
387                     hash -= hash >> 17;
388                     hash -= hash >> 11;
389                     hash -= hash >> 5;
390                 }
391 
392                 // Save hashcode.  Don't save 0, so that it won't ever be recomputed.
393                 _hashCode = (hash == 0) ? 1 : hash;
394             }
395 
396             return _hashCode;
397         }
398 
399         /// <summary>
400         /// Return a user-friendly string representation of the XmlQueryType.
401         /// </summary>
ToString()402         public override string ToString()
403         {
404             return ToString("G");
405         }
406 
407         /// <summary>
408         /// Return a string representation of the XmlQueryType using the specified format.  The following formats are
409         /// supported:
410         ///
411         ///   "G" (General): This is the default mode, and is used if no other format is recognized.  This format is
412         ///                  easier to read than the canonical format, since it excludes redundant information.
413         ///                  (e.g. element instead of element(*, xs:anyType))
414         ///
415         ///   "X" (XQuery): Return the canonical XQuery representation, which excludes Qil specific information and
416         ///                 includes extra, redundant information, such as fully specified types.
417         ///                 (e.g. element(*, xs:anyType) instead of element)
418         ///
419         ///   "S" (Serialized): This format is used to serialize parts of the type which can be serialized easily, in
420         ///                     a format that is easy to parse.  Only the cardinality, type code, and strictness flag
421         ///                     are serialized.  User-defined type information and element/attribute content types
422         ///                     are lost.
423         ///                     (e.g. One;Attribute|String|Int;true)
424         ///
425         /// </summary>
ToString(string format)426         public string ToString(string format)
427         {
428             string[] sa;
429             StringBuilder sb;
430             bool isXQ;
431 
432             if (format == "S")
433             {
434                 sb = new StringBuilder();
435                 sb.Append(Cardinality.ToString(format));
436                 sb.Append(';');
437 
438                 for (int i = 0; i < Count; i++)
439                 {
440                     if (i != 0)
441                         sb.Append("|");
442                     sb.Append(this[i].TypeCode.ToString());
443                 }
444 
445                 sb.Append(';');
446                 sb.Append(IsStrict);
447                 return sb.ToString();
448             }
449 
450             isXQ = (format == "X");
451 
452             if (Cardinality == XmlQueryCardinality.None)
453             {
454                 return "none";
455             }
456             else if (Cardinality == XmlQueryCardinality.Zero)
457             {
458                 return "empty";
459             }
460 
461             sb = new StringBuilder();
462 
463             switch (Count)
464             {
465                 case 0:
466                     // This assert depends on the way we are going to represent None
467                     // Debug.Assert(false);
468                     sb.Append("none");
469                     break;
470                 case 1:
471                     sb.Append(this[0].ItemTypeToString(isXQ));
472                     break;
473                 default:
474                     sa = new string[Count];
475                     for (int i = 0; i < Count; i++)
476                         sa[i] = this[i].ItemTypeToString(isXQ);
477 
478                     Array.Sort(sa);
479 
480                     sb = new StringBuilder();
481                     sb.Append('(');
482                     sb.Append(sa[0]);
483                     for (int i = 1; i < sa.Length; i++)
484                     {
485                         sb.Append(" | ");
486                         sb.Append(sa[i]);
487                     }
488 
489                     sb.Append(')');
490                     break;
491             }
492 
493             sb.Append(Cardinality.ToString());
494 
495             if (!isXQ && IsDod)
496                 sb.Append('#');
497 
498             return sb.ToString();
499         }
500 
501 
502         //-----------------------------------------------
503         // Serialization
504         //-----------------------------------------------
505 
506         /// <summary>
507         /// Serialize the object to BinaryWriter.
508         /// </summary>
GetObjectData(BinaryWriter writer)509         public abstract void GetObjectData(BinaryWriter writer);
510 
511         //-----------------------------------------------
512         // Helpers
513         //-----------------------------------------------
514 
515         /// <summary>
516         /// Returns true if this item type is a subtype of another item type.
517         /// </summary>
IsSubtypeOfItemType(XmlQueryType baseType)518         private bool IsSubtypeOfItemType(XmlQueryType baseType)
519         {
520             Debug.Assert(Count == 1 && IsSingleton, "This method should only be called for item types.");
521             Debug.Assert(baseType.Count == 1 && baseType.IsSingleton, "This method should only be called for item types.");
522             Debug.Assert(!IsDod && !baseType.IsDod, "Singleton types may not have DocOrderDistinct property");
523             XmlSchemaType baseSchemaType = baseType.SchemaType;
524 
525             if (TypeCode != baseType.TypeCode)
526             {
527                 // If "baseType" is strict, then IsSubtypeOf must be false
528                 if (baseType.IsStrict)
529                     return false;
530 
531                 // If type codes are not the same, then IsSubtypeOf can return true *only* if "baseType" is a built-in type
532                 XmlSchemaType builtInType = XmlSchemaType.GetBuiltInSimpleType(baseType.TypeCode);
533                 if (builtInType != null && baseSchemaType != builtInType)
534                     return false;
535 
536                 // Now check whether TypeCode is derived from baseType.TypeCode
537                 return s_typeCodeDerivation[TypeCode, baseType.TypeCode];
538             }
539             else if (baseType.IsStrict)
540             {
541                 // only atomic values can be strict
542                 Debug.Assert(IsAtomicValue && baseType.IsAtomicValue);
543 
544                 // If schema types are not the same, then IsSubtype is false if "baseType" is strict
545                 return IsStrict && SchemaType == baseSchemaType;
546             }
547             else
548             {
549                 // Otherwise, check derivation tree
550                 return (IsNotRtf || !baseType.IsNotRtf) && NameTest.IsSubsetOf(baseType.NameTest) &&
551                        (baseSchemaType == XmlSchemaComplexType.AnyType || XmlSchemaType.IsDerivedFrom(SchemaType, baseSchemaType, /* except:*/XmlSchemaDerivationMethod.Empty)) &&
552                        (!IsNillable || baseType.IsNillable);
553             }
554         }
555 
556         /// <summary>
557         /// Returns true if the intersection between this item type and "other" item type is not empty.
558         /// </summary>
HasIntersectionItemType(XmlQueryType other)559         private bool HasIntersectionItemType(XmlQueryType other)
560         {
561             Debug.Assert(this.Count == 1 && this.IsSingleton, "this should be an item");
562             Debug.Assert(other.Count == 1 && other.IsSingleton, "other should be an item");
563 
564             if (this.TypeCode == other.TypeCode && (this.NodeKinds & (XmlNodeKindFlags.Document | XmlNodeKindFlags.Element | XmlNodeKindFlags.Attribute)) != 0)
565             {
566                 if (this.TypeCode == XmlTypeCode.Node)
567                     return true;
568 
569                 // Intersect name tests
570                 if (!this.NameTest.HasIntersection(other.NameTest))
571                     return false;
572 
573                 if (!XmlSchemaType.IsDerivedFrom(this.SchemaType, other.SchemaType, /* except:*/XmlSchemaDerivationMethod.Empty) &&
574                     !XmlSchemaType.IsDerivedFrom(other.SchemaType, this.SchemaType, /* except:*/XmlSchemaDerivationMethod.Empty))
575                 {
576                     return false;
577                 }
578 
579                 return true;
580             }
581             else if (this.IsSubtypeOf(other) || other.IsSubtypeOf(this))
582             {
583                 return true;
584             }
585 
586             return false;
587         }
588 
589         /// <summary>
590         /// Return the string representation of an item type (cannot be a union or a sequence).
591         /// </summary>
ItemTypeToString(bool isXQ)592         private string ItemTypeToString(bool isXQ)
593         {
594             string s;
595             Debug.Assert(Count == 1, "Do not pass a Union type to this method.");
596             Debug.Assert(IsSingleton, "Do not pass a Sequence type to this method.");
597 
598             if (IsNode)
599             {
600                 // Map TypeCode to string
601                 s = s_typeNames[(int)TypeCode];
602 
603                 switch (TypeCode)
604                 {
605                     case XmlTypeCode.Document:
606                         if (!isXQ)
607                             goto case XmlTypeCode.Element;
608 
609                         s += "{(element" + NameAndType(true) + "?&text?&comment?&processing-instruction?)*}";
610                         break;
611 
612                     case XmlTypeCode.Element:
613                     case XmlTypeCode.Attribute:
614                         s += NameAndType(isXQ);
615                         break;
616                 }
617             }
618             else if (SchemaType != XmlSchemaComplexType.AnyType)
619             {
620                 // Get QualifiedName from SchemaType
621                 if (SchemaType.QualifiedName.IsEmpty)
622                     s = "<:" + s_typeNames[(int)TypeCode];
623                 else
624                     s = QNameToString(SchemaType.QualifiedName);
625             }
626             else
627             {
628                 // Map TypeCode to string
629                 s = s_typeNames[(int)TypeCode];
630             }
631 
632             if (!isXQ && IsStrict)
633                 s += "=";
634 
635             return s;
636         }
637 
638         /// <summary>
639         /// Return "(name-test, type-name)" for this type.  If isXQ is false, normalize xs:anySimpleType and
640         /// xs:anyType to "*".
641         /// </summary>
NameAndType(bool isXQ)642         private string NameAndType(bool isXQ)
643         {
644             string nodeName = NameTest.ToString();
645             string typeName = "*";
646 
647             if (SchemaType.QualifiedName.IsEmpty)
648             {
649                 typeName = "typeof(" + nodeName + ")";
650             }
651             else
652             {
653                 if (isXQ || (SchemaType != XmlSchemaComplexType.AnyType && SchemaType != DatatypeImplementation.AnySimpleType))
654                     typeName = QNameToString(SchemaType.QualifiedName);
655             }
656 
657             if (IsNillable)
658                 typeName += " nillable";
659 
660             // Normalize "(*, *)" to ""
661             if (nodeName == "*" && typeName == "*")
662                 return "";
663 
664             return "(" + nodeName + ", " + typeName + ")";
665         }
666 
667         /// <summary>
668         /// Convert an XmlQualifiedName to a string, using somewhat different rules than XmlQualifiedName.ToString():
669         ///   1. Empty QNames are assumed to be wildcard names, so return "*"
670         ///   2. Recognize the built-in xs: and xdt: namespaces and print the short prefix rather than the long namespace
671         ///   3. Use brace characters "{", "}" around the namespace portion of the QName
672         /// </summary>
QNameToString(XmlQualifiedName name)673         private static string QNameToString(XmlQualifiedName name)
674         {
675             if (name.IsEmpty)
676             {
677                 return "*";
678             }
679             else if (name.Namespace.Length == 0)
680             {
681                 return name.Name;
682             }
683             else if (name.Namespace == XmlReservedNs.NsXs)
684             {
685                 return "xs:" + name.Name;
686             }
687             else if (name.Namespace == XmlReservedNs.NsXQueryDataType)
688             {
689                 return "xdt:" + name.Name;
690             }
691             else
692             {
693                 return "{" + name.Namespace + "}" + name.Name;
694             }
695         }
696 
697         #region TypeFlags
698         private enum TypeFlags
699         {
700             None = 0,
701             IsNode = 1,
702             IsAtomicValue = 2,
703             IsNumeric = 4,
704         }
705         #endregion
706 
707         #region  TypeCodeToFlags
708         private static readonly TypeFlags[] s_typeCodeToFlags = {
709                 /* XmlTypeCode.None                  */ TypeFlags.IsNode | TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
710                 /* XmlTypeCode.Item                  */ TypeFlags.None,
711                 /* XmlTypeCode.Node                  */ TypeFlags.IsNode,
712                 /* XmlTypeCode.Document              */ TypeFlags.IsNode,
713                 /* XmlTypeCode.Element               */ TypeFlags.IsNode,
714                 /* XmlTypeCode.Attribute             */ TypeFlags.IsNode,
715                 /* XmlTypeCode.Namespace             */ TypeFlags.IsNode,
716                 /* XmlTypeCode.ProcessingInstruction */ TypeFlags.IsNode,
717                 /* XmlTypeCode.Comment               */ TypeFlags.IsNode,
718                 /* XmlTypeCode.Text                  */ TypeFlags.IsNode,
719                 /* XmlTypeCode.AnyAtomicType         */ TypeFlags.IsAtomicValue,
720                 /* XmlTypeCode.UntypedAtomic         */ TypeFlags.IsAtomicValue,
721                 /* XmlTypeCode.String                */ TypeFlags.IsAtomicValue,
722                 /* XmlTypeCode.Boolean               */ TypeFlags.IsAtomicValue,
723                 /* XmlTypeCode.Decimal               */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
724                 /* XmlTypeCode.Float                 */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
725                 /* XmlTypeCode.Double                */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
726                 /* XmlTypeCode.Duration              */ TypeFlags.IsAtomicValue,
727                 /* XmlTypeCode.DateTime              */ TypeFlags.IsAtomicValue,
728                 /* XmlTypeCode.Time                  */ TypeFlags.IsAtomicValue,
729                 /* XmlTypeCode.Date                  */ TypeFlags.IsAtomicValue,
730                 /* XmlTypeCode.GYearMonth            */ TypeFlags.IsAtomicValue,
731                 /* XmlTypeCode.GYear                 */ TypeFlags.IsAtomicValue,
732                 /* XmlTypeCode.GMonthDay             */ TypeFlags.IsAtomicValue,
733                 /* XmlTypeCode.GDay                  */ TypeFlags.IsAtomicValue,
734                 /* XmlTypeCode.GMonth                */ TypeFlags.IsAtomicValue,
735                 /* XmlTypeCode.HexBinary             */ TypeFlags.IsAtomicValue,
736                 /* XmlTypeCode.Base64Binary          */ TypeFlags.IsAtomicValue,
737                 /* XmlTypeCode.AnyUri                */ TypeFlags.IsAtomicValue,
738                 /* XmlTypeCode.QName                 */ TypeFlags.IsAtomicValue,
739                 /* XmlTypeCode.Notation              */ TypeFlags.IsAtomicValue,
740                 /* XmlTypeCode.NormalizedString      */ TypeFlags.IsAtomicValue,
741                 /* XmlTypeCode.Token                 */ TypeFlags.IsAtomicValue,
742                 /* XmlTypeCode.Language              */ TypeFlags.IsAtomicValue,
743                 /* XmlTypeCode.NmToken               */ TypeFlags.IsAtomicValue,
744                 /* XmlTypeCode.Name                  */ TypeFlags.IsAtomicValue,
745                 /* XmlTypeCode.NCName                */ TypeFlags.IsAtomicValue,
746                 /* XmlTypeCode.Id                    */ TypeFlags.IsAtomicValue,
747                 /* XmlTypeCode.Idref                 */ TypeFlags.IsAtomicValue,
748                 /* XmlTypeCode.Entity                */ TypeFlags.IsAtomicValue,
749                 /* XmlTypeCode.Integer               */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
750                 /* XmlTypeCode.NonPositiveInteger    */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
751                 /* XmlTypeCode.NegativeInteger       */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
752                 /* XmlTypeCode.Long                  */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
753                 /* XmlTypeCode.Int                   */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
754                 /* XmlTypeCode.Short                 */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
755                 /* XmlTypeCode.Byte                  */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
756                 /* XmlTypeCode.NonNegativeInteger    */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
757                 /* XmlTypeCode.UnsignedLong          */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
758                 /* XmlTypeCode.UnsignedInt           */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
759                 /* XmlTypeCode.UnsignedShort         */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
760                 /* XmlTypeCode.UnsignedByte          */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
761                 /* XmlTypeCode.PositiveInteger       */ TypeFlags.IsAtomicValue | TypeFlags.IsNumeric,
762                 /* XmlTypeCode.YearMonthDuration     */ TypeFlags.IsAtomicValue,
763                 /* XmlTypeCode.DayTimeDuration       */ TypeFlags.IsAtomicValue,
764         };
765 
766         private static readonly XmlTypeCode[] s_baseTypeCodes = {
767             /* None                        */ XmlTypeCode.None,
768             /* Item                        */ XmlTypeCode.Item,
769             /* Node                        */ XmlTypeCode.Item,
770             /* Document                    */ XmlTypeCode.Node,
771             /* Element                     */ XmlTypeCode.Node,
772             /* Attribute                   */ XmlTypeCode.Node,
773             /* Namespace                   */ XmlTypeCode.Node,
774             /* ProcessingInstruction       */ XmlTypeCode.Node,
775             /* Comment                     */ XmlTypeCode.Node,
776             /* Text                        */ XmlTypeCode.Node,
777             /* AnyAtomicType               */ XmlTypeCode.Item,
778             /* UntypedAtomic               */ XmlTypeCode.AnyAtomicType,
779             /* String                      */ XmlTypeCode.AnyAtomicType,
780             /* Boolean                     */ XmlTypeCode.AnyAtomicType,
781             /* Decimal                     */ XmlTypeCode.AnyAtomicType,
782             /* Float                       */ XmlTypeCode.AnyAtomicType,
783             /* Double                      */ XmlTypeCode.AnyAtomicType,
784             /* Duration                    */ XmlTypeCode.AnyAtomicType,
785             /* DateTime                    */ XmlTypeCode.AnyAtomicType,
786             /* Time                        */ XmlTypeCode.AnyAtomicType,
787             /* Date                        */ XmlTypeCode.AnyAtomicType,
788             /* GYearMonth                  */ XmlTypeCode.AnyAtomicType,
789             /* GYear                       */ XmlTypeCode.AnyAtomicType,
790             /* GMonthDay                   */ XmlTypeCode.AnyAtomicType,
791             /* GDay                        */ XmlTypeCode.AnyAtomicType,
792             /* GMonth                      */ XmlTypeCode.AnyAtomicType,
793             /* HexBinary                   */ XmlTypeCode.AnyAtomicType,
794             /* Base64Binary                */ XmlTypeCode.AnyAtomicType,
795             /* AnyUri                      */ XmlTypeCode.AnyAtomicType,
796             /* QName                       */ XmlTypeCode.AnyAtomicType,
797             /* Notation                    */ XmlTypeCode.AnyAtomicType,
798             /* NormalizedString            */ XmlTypeCode.String,
799             /* Token                       */ XmlTypeCode.NormalizedString,
800             /* Language                    */ XmlTypeCode.Token,
801             /* NmToken                     */ XmlTypeCode.Token,
802             /* Name                        */ XmlTypeCode.Token,
803             /* NCName                      */ XmlTypeCode.Name,
804             /* Id                          */ XmlTypeCode.NCName,
805             /* Idref                       */ XmlTypeCode.NCName,
806             /* Entity                      */ XmlTypeCode.NCName,
807             /* Integer                     */ XmlTypeCode.Decimal,
808             /* NonPositiveInteger          */ XmlTypeCode.Integer,
809             /* NegativeInteger             */ XmlTypeCode.NonPositiveInteger,
810             /* Long                        */ XmlTypeCode.Integer,
811             /* Int                         */ XmlTypeCode.Long,
812             /* Short                       */ XmlTypeCode.Int,
813             /* Byte                        */ XmlTypeCode.Short,
814             /* NonNegativeInteger          */ XmlTypeCode.Integer,
815             /* UnsignedLong                */ XmlTypeCode.NonNegativeInteger,
816             /* UnsignedInt                 */ XmlTypeCode.UnsignedLong,
817             /* UnsignedShort               */ XmlTypeCode.UnsignedInt,
818             /* UnsignedByte                */ XmlTypeCode.UnsignedShort,
819             /* PositiveInteger             */ XmlTypeCode.NonNegativeInteger,
820             /* YearMonthDuration           */ XmlTypeCode.Duration,
821             /* DayTimeDuration             */ XmlTypeCode.Duration,
822         };
823 
824         private static readonly string[] s_typeNames = {
825             /* None                        */ "none",
826             /* Item                        */ "item",
827             /* Node                        */ "node",
828             /* Document                    */ "document",
829             /* Element                     */ "element",
830             /* Attribute                   */ "attribute",
831             /* Namespace                   */ "namespace",
832             /* ProcessingInstruction       */ "processing-instruction",
833             /* Comment                     */ "comment",
834             /* Text                        */ "text",
835             /* AnyAtomicType               */ "xdt:anyAtomicType",
836             /* UntypedAtomic               */ "xdt:untypedAtomic",
837             /* String                      */ "xs:string",
838             /* Boolean                     */ "xs:boolean",
839             /* Decimal                     */ "xs:decimal",
840             /* Float                       */ "xs:float",
841             /* Double                      */ "xs:double",
842             /* Duration                    */ "xs:duration",
843             /* DateTime                    */ "xs:dateTime",
844             /* Time                        */ "xs:time",
845             /* Date                        */ "xs:date",
846             /* GYearMonth                  */ "xs:gYearMonth",
847             /* GYear                       */ "xs:gYear",
848             /* GMonthDay                   */ "xs:gMonthDay",
849             /* GDay                        */ "xs:gDay",
850             /* GMonth                      */ "xs:gMonth",
851             /* HexBinary                   */ "xs:hexBinary",
852             /* Base64Binary                */ "xs:base64Binary",
853             /* AnyUri                      */ "xs:anyUri",
854             /* QName                       */ "xs:QName",
855             /* Notation                    */ "xs:NOTATION",
856             /* NormalizedString            */ "xs:normalizedString",
857             /* Token                       */ "xs:token",
858             /* Language                    */ "xs:language",
859             /* NmToken                     */ "xs:NMTOKEN",
860             /* Name                        */ "xs:Name",
861             /* NCName                      */ "xs:NCName",
862             /* Id                          */ "xs:ID",
863             /* Idref                       */ "xs:IDREF",
864             /* Entity                      */ "xs:ENTITY",
865             /* Integer                     */ "xs:integer",
866             /* NonPositiveInteger          */ "xs:nonPositiveInteger",
867             /* NegativeInteger             */ "xs:negativeInteger",
868             /* Long                        */ "xs:long",
869             /* Int                         */ "xs:int",
870             /* Short                       */ "xs:short",
871             /* Byte                        */ "xs:byte",
872             /* NonNegativeInteger          */ "xs:nonNegativeInteger",
873             /* UnsignedLong                */ "xs:unsignedLong",
874             /* UnsignedInt                 */ "xs:unsignedInt",
875             /* UnsignedShort               */ "xs:unsignedShort",
876             /* UnsignedByte                */ "xs:unsignedByte",
877             /* PositiveInteger             */ "xs:positiveInteger",
878             /* YearMonthDuration           */ "xdt:yearMonthDuration",
879             /* DayTimeDuration             */ "xdt:dayTimeDuration",
880         };
881         #endregion
882 
883         /// <summary>
884         /// Implements an NxN bit matrix.
885         /// </summary>
886         private sealed class BitMatrix
887         {
888             private ulong[] _bits;
889 
890             /// <summary>
891             /// Create NxN bit matrix, where N = count.
892             /// </summary>
BitMatrix(int count)893             public BitMatrix(int count)
894             {
895                 Debug.Assert(count < 64, "BitMatrix currently only handles up to 64x64 matrix.");
896                 _bits = new ulong[count];
897             }
898 
899             //            /// <summary>
900             //            /// Return the number of rows and columns in the matrix.
901             //            /// </summary>
902             //            public int Size {
903             //                get { return bits.Length; }
904             //            }
905             //
906             /// <summary>
907             /// Get or set a bit in the matrix at position (index1, index2).
908             /// </summary>
909             public bool this[int index1, int index2]
910             {
911                 get
912                 {
913                     Debug.Assert(index1 < _bits.Length && index2 < _bits.Length, "Index out of range.");
914                     return (_bits[index1] & ((ulong)1 << index2)) != 0;
915                 }
916                 set
917                 {
918                     Debug.Assert(index1 < _bits.Length && index2 < _bits.Length, "Index out of range.");
919                     if (value == true)
920                     {
921                         _bits[index1] |= (ulong)1 << index2;
922                     }
923                     else
924                     {
925                         _bits[index1] &= ~((ulong)1 << index2);
926                     }
927                 }
928             }
929 
930             /// <summary>
931             /// Strongly typed indexer.
932             /// </summary>
933             public bool this[XmlTypeCode index1, XmlTypeCode index2]
934             {
935                 get
936                 {
937                     return this[(int)index1, (int)index2];
938                 }
939                 //                set {
940                 //                    this[(int)index1, (int)index2] = value;
941                 //                }
942             }
943         }
944     }
945 }
946