Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / Schema / ContentValidator.cs
bloba2eb25b667edbbfe6b2596ae13a648a33187bc46
1 //------------------------------------------------------------------------------
2 // <copyright file="ContentValidator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7 using System;
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Globalization;
11 using System.Text;
12 using System.Diagnostics;
14 namespace System.Xml.Schema {
16 #region ExceptionSymbolsPositions
18 /// <summary>
19 /// UPA violations will throw this exception
20 /// </summary>
21 class UpaException : Exception {
22 object particle1;
23 object particle2;
24 public UpaException(object particle1, object particle2) {
25 this.particle1 = particle1;
26 this.particle2 = particle2;
28 public object Particle1 { get { return particle1; } }
29 public object Particle2 { get { return particle2; } }
32 /// <summary>
33 /// SymbolsDictionary is a map between names that ContextValidator recognizes and symbols - int symbol[XmlQualifiedName name].
34 /// There are two types of name - full names and wildcards (namespace is specified, local name is anythig).
35 /// Wildcard excludes all full names that would match by the namespace part.
36 /// SymbolsDictionry alwas recognizes all the symbols - the last one is a true wildcard -
37 /// both name and namespace can be anything that none of the other symbols matched.
38 /// </summary>
39 class SymbolsDictionary {
40 int last = 0;
41 Hashtable names;
42 Hashtable wildcards = null;
43 ArrayList particles;
44 object particleLast = null;
45 bool isUpaEnforced = true;
47 public SymbolsDictionary() {
48 names = new Hashtable();
49 particles = new ArrayList();
52 public int Count {
53 // last one is a "*:*" any wildcard
54 get { return last + 1; }
57 public int CountOfNames {
58 get { return names.Count; }
61 /// <summary>
62 /// True is particle can be deterministically attributed from the symbol and conversion to DFA is possible.
63 /// </summary>
64 public bool IsUpaEnforced {
65 get { return isUpaEnforced; }
66 set { isUpaEnforced = value; }
69 /// <summary>
70 /// Add name and return it's number
71 /// </summary>
72 public int AddName(XmlQualifiedName name, object particle) {
73 object lookup = names[name];
74 if (lookup != null) {
75 int symbol = (int)lookup;
76 if (particles[symbol] != particle) {
77 isUpaEnforced = false;
79 return symbol;
81 else {
82 names.Add(name, last);
83 particles.Add(particle);
84 Debug.Assert(particles.Count == last + 1);
85 return last ++;
89 public void AddNamespaceList(NamespaceList list, object particle, bool allowLocal) {
90 switch (list.Type) {
91 case NamespaceList.ListType.Any:
92 particleLast = particle;
93 break;
94 case NamespaceList.ListType.Other:
95 // Create a symbol for the excluded namespace, but don't set a particle for it.
96 AddWildcard(list.Excluded, null);
97 if (!allowLocal) {
98 AddWildcard(string.Empty, null); //##local is not allowed
100 break;
101 case NamespaceList.ListType.Set:
102 foreach(string wildcard in list.Enumerate) {
103 AddWildcard(wildcard, particle);
105 break;
109 private void AddWildcard(string wildcard, object particle) {
110 if (wildcards == null) {
111 wildcards = new Hashtable();
113 object lookup = wildcards[wildcard];
114 if (lookup == null) {
115 wildcards.Add(wildcard, last);
116 particles.Add(particle);
117 Debug.Assert(particles.Count == last + 1);
118 last ++;
120 else if (particle != null) {
121 particles[(int)lookup] = particle;
125 public ICollection GetNamespaceListSymbols(NamespaceList list) {
126 ArrayList match = new ArrayList();
127 foreach(XmlQualifiedName name in names.Keys) {
128 if (name != XmlQualifiedName.Empty && list.Allows(name)) {
129 match.Add(names[name]);
132 if (wildcards != null) {
133 foreach(string wildcard in wildcards.Keys) {
134 if (list.Allows(wildcard)) {
135 match.Add(wildcards[wildcard]);
139 if (list.Type == NamespaceList.ListType.Any || list.Type == NamespaceList.ListType.Other) {
140 match.Add(last); // add wildcard
142 return match;
145 /// <summary>
146 /// Find the symbol for the given name. If neither names nor wilcards match it last (*.*) symbol will be returned
147 /// </summary>
148 public int this[XmlQualifiedName name] {
149 get {
150 object lookup = names[name];
151 if (lookup != null) {
152 return (int)lookup;
154 if (wildcards != null) {
155 lookup = wildcards[name.Namespace];
156 if (lookup != null) {
157 return (int)lookup;
160 return last; // true wildcard
164 /// <summary>
165 /// Check if a name exists in the symbol dictionary
166 /// </summary>
167 public bool Exists(XmlQualifiedName name) {
169 object lookup = names[name];
170 if (lookup != null) {
171 return true;
173 return false;
176 /// <summary>
177 /// Return content processing mode for the symbol
178 /// </summary>
179 public object GetParticle(int symbol) {
180 return symbol == last ? particleLast : particles[symbol];
183 /// <summary>
184 /// Output symbol's name
185 /// </summary>
186 public string NameOf(int symbol) {
187 foreach (DictionaryEntry de in names) {
188 if ((int)de.Value == symbol) {
189 return ((XmlQualifiedName)de.Key).ToString();
192 if (wildcards != null) {
193 foreach (DictionaryEntry de in wildcards) {
194 if ((int)de.Value == symbol) {
195 return (string)de.Key + ":*";
199 return "##other:*";
203 struct Position {
204 public int symbol;
205 public object particle;
206 public Position(int symbol, object particle) {
207 this.symbol = symbol;
208 this.particle = particle;
212 class Positions {
213 ArrayList positions = new ArrayList();
215 public int Add(int symbol, object particle) {
216 return positions.Add(new Position(symbol, particle));
219 public Position this[int pos] {
220 get { return (Position)positions[pos]; }
223 public int Count {
224 get { return positions.Count; }
227 #endregion
229 #region SystaxTree
230 /// <summary>
231 /// Base class for the systax tree nodes
232 /// </summary>
233 abstract class SyntaxTreeNode {
234 /// <summary>
235 /// Expand NamesapceListNode and RangeNode nodes. All other nodes
236 /// </summary>
237 public abstract void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions);
239 /// <summary>
240 /// Clone the syntaxTree. We need to pass symbolsByPosition because leaf nodes have to add themselves to it.
241 /// </summary>
242 public abstract SyntaxTreeNode Clone(Positions positions);
244 /// <summary>
245 /// From a regular expression to a DFA
246 /// Compilers by Aho, Sethi, Ullman.
247 /// ISBN 0-201-10088-6, p135
248 /// Construct firstpos, lastpos and calculate followpos
249 /// </summary>
250 public abstract void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos);
252 /// <summary>
253 /// Returns nullable property that is being used by ConstructPos
254 /// </summary>
255 public abstract bool IsNullable { get; }
257 /// <summary>
258 /// Returns true if node is a range node
259 /// </summary>
260 public virtual bool IsRangeNode {
261 get {
262 return false;
266 /// <summary>
267 /// Print syntax tree
268 /// </summary>
269 #if DEBUG
270 public abstract void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions);
271 #endif
274 /// <summary>
275 /// Terminal of the syntax tree
276 /// </summary>
277 class LeafNode : SyntaxTreeNode {
278 int pos;
280 public LeafNode(int pos) {
281 this.pos = pos;
284 public int Pos {
285 get { return pos;}
286 set { pos = value; }
289 public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
290 // do nothing
293 public override SyntaxTreeNode Clone(Positions positions) {
294 return new LeafNode(positions.Add(positions[pos].symbol, positions[pos].particle));
297 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
298 firstpos.Set(pos);
299 lastpos.Set(pos);
302 public override bool IsNullable {
303 get { return false; }
306 #if DEBUG
307 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
308 bb.Append("\"" + symbols.NameOf(positions[pos].symbol) + "\"");
310 #endif
313 /// <summary>
314 /// Temporary node to represent NamespaceList. Will be expended as a choice of symbols
315 /// </summary>
316 class NamespaceListNode : SyntaxTreeNode {
317 protected NamespaceList namespaceList;
318 protected object particle;
320 public NamespaceListNode(NamespaceList namespaceList, object particle) {
321 this.namespaceList = namespaceList;
322 this.particle = particle;
325 public override SyntaxTreeNode Clone(Positions positions) {
326 // NamespaceListNode nodes have to be removed prior to that
327 throw new InvalidOperationException();
330 public virtual ICollection GetResolvedSymbols(SymbolsDictionary symbols) {
331 return symbols.GetNamespaceListSymbols(namespaceList);
334 public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
335 SyntaxTreeNode replacementNode = null;
336 foreach(int symbol in GetResolvedSymbols(symbols)) {
337 if (symbols.GetParticle(symbol) != particle) {
338 symbols.IsUpaEnforced = false;
340 LeafNode node = new LeafNode(positions.Add(symbol, particle));
341 if (replacementNode == null) {
342 replacementNode = node;
344 else {
345 InteriorNode choice = new ChoiceNode();
346 choice.LeftChild = replacementNode;
347 choice.RightChild = node;
348 replacementNode = choice;
351 if (parent.LeftChild == this) {
352 parent.LeftChild = replacementNode;
354 else {
355 parent.RightChild = replacementNode;
359 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
360 // NamespaceListNode nodes have to be removed prior to that
361 throw new InvalidOperationException();
364 public override bool IsNullable {
365 // NamespaceListNode nodes have to be removed prior to that
366 get { throw new InvalidOperationException(); }
369 #if DEBUG
370 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
371 bb.Append("[" + namespaceList.ToString() + "]");
373 #endif
376 /// <summary>
377 /// Base class for all internal node. Note that only sequence and choice have right child
378 /// </summary>
379 abstract class InteriorNode : SyntaxTreeNode {
380 SyntaxTreeNode leftChild;
381 SyntaxTreeNode rightChild;
383 public SyntaxTreeNode LeftChild {
384 get { return leftChild;}
385 set { leftChild = value;}
388 public SyntaxTreeNode RightChild {
389 get { return rightChild;}
390 set { rightChild = value;}
393 public override SyntaxTreeNode Clone(Positions positions) {
394 InteriorNode other = (InteriorNode)this.MemberwiseClone();
395 other.LeftChild = leftChild.Clone(positions);
396 if (rightChild != null) {
397 other.RightChild = rightChild.Clone(positions);
399 return other;
402 //no recursive version of expand tree for Sequence and Choice node
403 protected void ExpandTreeNoRecursive(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
404 Stack<InteriorNode> nodeStack = new Stack<InteriorNode>();
405 InteriorNode this_ = this;
406 while (true) {
407 if (this_.leftChild is ChoiceNode || this_.leftChild is SequenceNode) {
408 nodeStack.Push(this_);
409 this_ = (InteriorNode)this_.leftChild;
410 continue;
412 this_.leftChild.ExpandTree(this_, symbols, positions);
414 ProcessRight:
415 if (this_.rightChild != null) {
416 this_.rightChild.ExpandTree(this_, symbols, positions);
419 if (nodeStack.Count == 0)
420 break;
422 this_ = nodeStack.Pop();
423 goto ProcessRight;
427 public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
428 leftChild.ExpandTree(this, symbols, positions);
429 if (rightChild != null) {
430 rightChild.ExpandTree(this, symbols, positions);
436 sealed class SequenceNode : InteriorNode {
438 struct SequenceConstructPosContext {
439 public SequenceNode this_;
440 public BitSet firstpos;
441 public BitSet lastpos;
442 public BitSet lastposLeft;
443 public BitSet firstposRight;
445 public SequenceConstructPosContext(SequenceNode node, BitSet firstpos, BitSet lastpos) {
446 this_ = node;
447 this.firstpos = firstpos;
448 this.lastpos = lastpos;
450 lastposLeft = null;
451 firstposRight = null;
455 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
457 Stack<SequenceConstructPosContext> contextStack = new Stack<SequenceConstructPosContext>();
458 SequenceConstructPosContext context = new SequenceConstructPosContext(this, firstpos, lastpos);
460 while (true) {
461 SequenceNode this_ = context.this_;
462 context.lastposLeft = new BitSet(lastpos.Count);
463 if (this_.LeftChild is SequenceNode) {
464 contextStack.Push(context);
465 context = new SequenceConstructPosContext((SequenceNode)this_.LeftChild, context.firstpos, context.lastposLeft);
466 continue;
468 this_.LeftChild.ConstructPos(context.firstpos, context.lastposLeft, followpos);
470 ProcessRight:
471 context.firstposRight = new BitSet(firstpos.Count);
472 this_.RightChild.ConstructPos(context.firstposRight, context.lastpos, followpos);
474 if (this_.LeftChild.IsNullable && !this_.RightChild.IsRangeNode) {
475 context.firstpos.Or(context.firstposRight);
477 if (this_.RightChild.IsNullable) {
478 context.lastpos.Or(context.lastposLeft);
480 for (int pos = context.lastposLeft.NextSet(-1); pos != -1; pos = context.lastposLeft.NextSet(pos)) {
481 followpos[pos].Or(context.firstposRight);
483 if (this_.RightChild.IsRangeNode) { //firstpos is leftchild.firstpos as the or with firstposRight has not been done as it is a rangenode
484 ((LeafRangeNode)this_.RightChild).NextIteration = context.firstpos.Clone();
487 if (contextStack.Count == 0)
488 break;
490 context = contextStack.Pop();
491 this_ = context.this_;
492 goto ProcessRight;
496 public override bool IsNullable {
497 get {
498 SyntaxTreeNode n;
499 SequenceNode this_ = this;
500 do {
501 if (this_.RightChild.IsRangeNode && ((LeafRangeNode)this_.RightChild).Min == 0)
502 return true;
503 if (!this_.RightChild.IsNullable && !this_.RightChild.IsRangeNode)
504 return false;
505 n = this_.LeftChild;
506 this_ = n as SequenceNode;
508 while (this_ != null);
509 return n.IsNullable;
513 public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
514 ExpandTreeNoRecursive(parent, symbols, positions);
517 #if DEBUG
518 internal static void WritePos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
519 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "FirstPos: ");
520 WriteBitSet(firstpos);
522 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "LastPos: ");
523 WriteBitSet(lastpos);
525 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "Followpos: ");
526 for(int i =0; i < followpos.Length; i++) {
527 WriteBitSet(followpos[i]);
530 internal static void WriteBitSet(BitSet curpos) {
531 int[] list = new int[curpos.Count];
532 for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
533 list[pos] = 1;
535 for(int i = 0; i < list.Length; i++) {
536 Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, list[i] + " ");
538 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
542 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
543 Stack<SequenceNode> nodeStack = new Stack<SequenceNode>();
544 SequenceNode this_ = this;
546 while (true) {
547 bb.Append("(");
548 if (this_.LeftChild is SequenceNode) {
549 nodeStack.Push(this_);
550 this_ = (SequenceNode)this_.LeftChild;
551 continue;
553 this_.LeftChild.Dump(bb, symbols, positions);
555 ProcessRight:
556 bb.Append(", ");
557 this_.RightChild.Dump(bb, symbols, positions);
558 bb.Append(")");
559 if (nodeStack.Count == 0)
560 break;
562 this_ = nodeStack.Pop();
563 goto ProcessRight;
566 #endif
570 sealed class ChoiceNode : InteriorNode {
572 private static void ConstructChildPos(SyntaxTreeNode child, BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
573 BitSet firstPosTemp = new BitSet(firstpos.Count);
574 BitSet lastPosTemp = new BitSet(lastpos.Count);
575 child.ConstructPos(firstPosTemp, lastPosTemp, followpos);
576 firstpos.Or(firstPosTemp);
577 lastpos.Or(lastPosTemp);
580 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
582 BitSet firstPosTemp = new BitSet(firstpos.Count);
583 BitSet lastPosTemp = new BitSet(lastpos.Count);
584 SyntaxTreeNode n;
585 ChoiceNode this_ = this;
586 do {
587 ConstructChildPos(this_.RightChild, firstPosTemp, lastPosTemp, followpos);
588 n = this_.LeftChild;
589 this_ = n as ChoiceNode;
590 } while (this_ != null);
592 n.ConstructPos(firstpos, lastpos, followpos);
593 firstpos.Or(firstPosTemp);
594 lastpos.Or(lastPosTemp);
597 public override bool IsNullable {
598 get {
599 SyntaxTreeNode n;
600 ChoiceNode this_ = this;
601 do {
602 if (this_.RightChild.IsNullable)
603 return true;
604 n = this_.LeftChild;
605 this_ = n as ChoiceNode;
607 while (this_ != null);
608 return n.IsNullable;
612 public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
613 ExpandTreeNoRecursive(parent, symbols, positions);
616 #if DEBUG
617 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
618 Stack<ChoiceNode> nodeStack = new Stack<ChoiceNode>();
619 ChoiceNode this_ = this;
621 while (true) {
622 bb.Append("(");
623 if (this_.LeftChild is ChoiceNode) {
624 nodeStack.Push(this_);
625 this_ = (ChoiceNode)this_.LeftChild;
626 continue;
628 this_.LeftChild.Dump(bb, symbols, positions);
630 ProcessRight:
631 bb.Append(" | ");
632 this_.RightChild.Dump(bb, symbols, positions);
633 bb.Append(")");
634 if (nodeStack.Count == 0)
635 break;
637 this_ = nodeStack.Pop();
638 goto ProcessRight;
641 #endif
644 sealed class PlusNode : InteriorNode {
645 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
646 LeftChild.ConstructPos(firstpos, lastpos, followpos);
647 for (int pos = lastpos.NextSet(-1); pos != -1; pos = lastpos.NextSet(pos)) {
648 followpos[pos].Or(firstpos);
652 public override bool IsNullable {
653 get { return LeftChild.IsNullable; }
656 #if DEBUG
657 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
658 LeftChild.Dump(bb, symbols, positions);
659 bb.Append("+");
661 #endif
664 sealed class QmarkNode : InteriorNode {
665 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
666 LeftChild.ConstructPos(firstpos, lastpos, followpos);
669 public override bool IsNullable {
670 get { return true; }
673 #if DEBUG
674 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
675 LeftChild.Dump(bb, symbols, positions);
676 bb.Append("?");
678 #endif
681 sealed class StarNode : InteriorNode {
682 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
683 LeftChild.ConstructPos(firstpos, lastpos, followpos);
684 for (int pos = lastpos.NextSet(-1); pos != -1; pos = lastpos.NextSet(pos)) {
685 followpos[pos].Or(firstpos);
689 public override bool IsNullable {
690 get { return true; }
693 #if DEBUG
694 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
695 LeftChild.Dump(bb, symbols, positions);
696 bb.Append("*");
698 #endif
701 #if EXPANDRANGE
702 /// <summary>
703 /// Temporary node to occurance range. Will be expended to a sequence of terminals
704 /// </summary>
705 sealed class RangeNode : InteriorNode {
706 int min;
707 int max;
709 public RangeNode(int min, int max) {
710 this.min = min;
711 this.max = max;
714 public int Max {
715 get { return max;}
718 public int Min {
719 get { return min;}
722 public override SyntaxTreeNode Clone(Positions positions) {
723 // range nodes have to be removed prior to that
724 throw new InvalidOperationException();
727 /// <summary>
728 /// Expand tree will replace a{min, max} using following algorithm. Bare in mind that this sequence will have at least two leaves
729 /// if min == 0 (max cannot be unbounded)
730 /// a?, ... a?
731 /// \__ __/
732 /// max
733 /// else
734 /// if max == unbounded
735 /// a, ... a, a*
736 /// \__ __/
737 /// min
738 /// else
739 /// a, ... a, a?, ... a?
740 /// \__ __/ \__ __/
741 /// min max - min
742 /// </summary>
743 public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
744 LeftChild.ExpandTree(this, symbols, positions);
745 SyntaxTreeNode replacementNode = null;
746 if (min == 0) {
747 Debug.Assert(max != int.MaxValue);
748 replacementNode = NewQmark(LeftChild);
749 for (int i = 0; i < max - 1; i ++) {
750 replacementNode = NewSequence(replacementNode, NewQmark(LeftChild.Clone(positions)));
753 else {
754 replacementNode = LeftChild;
755 for (int i = 0; i < min - 1; i ++) {
756 replacementNode = NewSequence(replacementNode, LeftChild.Clone(positions));
758 if (max == int.MaxValue) {
759 replacementNode = NewSequence(replacementNode, NewStar(LeftChild.Clone(positions)));
761 else {
762 for (int i = 0; i < max - min; i ++) {
763 replacementNode = NewSequence(replacementNode, NewQmark(LeftChild.Clone(positions)));
767 if (parent.LeftChild == this) {
768 parent.LeftChild = replacementNode;
770 else {
771 parent.RightChild = replacementNode;
775 private SyntaxTreeNode NewSequence(SyntaxTreeNode leftChild, SyntaxTreeNode rightChild) {
776 InteriorNode sequence = new SequenceNode();
777 sequence.LeftChild = leftChild;
778 sequence.RightChild = rightChild;
779 return sequence;
782 private SyntaxTreeNode NewStar(SyntaxTreeNode leftChild) {
783 InteriorNode star = new StarNode();
784 star.LeftChild = leftChild;
785 return star;
788 private SyntaxTreeNode NewQmark(SyntaxTreeNode leftChild) {
789 InteriorNode qmark = new QmarkNode();
790 qmark.LeftChild = leftChild;
791 return qmark;
794 public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
795 throw new InvalidOperationException();
798 public override bool IsNullable {
799 get { throw new InvalidOperationException(); }
802 public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
803 LeftChild.Dump(bb, symbols, positions);
804 bb.Append("{" + Convert.ToString(min, NumberFormatInfo.InvariantInfo) + ", " + Convert.ToString(max, NumberFormatInfo.InvariantInfo) + "}");
808 #endif
811 /// <summary>
812 /// Using range node as one of the terminals
813 /// </summary>
814 sealed class LeafRangeNode : LeafNode {
815 decimal min;
816 decimal max;
817 BitSet nextIteration;
819 public LeafRangeNode(decimal min, decimal max) : this(-1, min, max) {}
821 public LeafRangeNode(int pos, decimal min, decimal max) : base(pos) {
822 this.min = min;
823 this.max = max;
826 public decimal Max {
827 get { return max;}
830 public decimal Min {
831 get { return min;}
834 public BitSet NextIteration {
835 get {
836 return nextIteration;
838 set {
839 nextIteration = value;
843 public override SyntaxTreeNode Clone(Positions positions) {
844 return new LeafRangeNode(this.Pos, this.min, this.max);
847 public override bool IsRangeNode {
848 get {
849 return true;
853 public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
854 Debug.Assert(parent is SequenceNode);
855 Debug.Assert(this == parent.RightChild);
856 //change the range node min to zero if left is nullable
857 if (parent.LeftChild.IsNullable) {
858 min = 0;
863 #endregion
865 #region ContentValidator
866 /// <summary>
867 /// Basic ContentValidator
868 /// </summary>
869 class ContentValidator {
870 XmlSchemaContentType contentType;
871 bool isOpen; //For XDR Content Models or ANY
872 bool isEmptiable;
874 public static readonly ContentValidator Empty = new ContentValidator(XmlSchemaContentType.Empty);
875 public static readonly ContentValidator TextOnly = new ContentValidator(XmlSchemaContentType.TextOnly, false, false);
876 public static readonly ContentValidator Mixed = new ContentValidator(XmlSchemaContentType.Mixed);
877 public static readonly ContentValidator Any = new ContentValidator(XmlSchemaContentType.Mixed, true, true);
879 public ContentValidator(XmlSchemaContentType contentType) {
880 this.contentType = contentType;
881 this.isEmptiable = true;
884 protected ContentValidator(XmlSchemaContentType contentType, bool isOpen, bool isEmptiable) {
885 this.contentType = contentType;
886 this.isOpen = isOpen;
887 this.isEmptiable = isEmptiable;
890 public XmlSchemaContentType ContentType {
891 get { return contentType; }
894 public bool PreserveWhitespace {
895 get { return contentType == XmlSchemaContentType.TextOnly || contentType == XmlSchemaContentType.Mixed; }
898 public virtual bool IsEmptiable {
899 get { return isEmptiable; }
902 public bool IsOpen {
903 get {
904 if (contentType == XmlSchemaContentType.TextOnly || contentType == XmlSchemaContentType.Empty)
905 return false;
906 else
907 return isOpen;
909 set { isOpen = value; }
912 public virtual void InitValidation(ValidationState context) {
913 // do nothin'
916 public virtual object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
917 if (contentType == XmlSchemaContentType.TextOnly || contentType == XmlSchemaContentType.Empty) { //Cannot have elements in TextOnly or Empty content
918 context.NeedValidateChildren = false;
920 errorCode = -1;
921 return null;
924 public virtual bool CompleteValidation(ValidationState context) {
925 return true;
928 public virtual ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
929 return null;
932 public virtual ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
933 return null;
936 public static void AddParticleToExpected(XmlSchemaParticle p, XmlSchemaSet schemaSet, ArrayList particles) {
937 AddParticleToExpected(p, schemaSet, particles, false);
940 public static void AddParticleToExpected(XmlSchemaParticle p, XmlSchemaSet schemaSet, ArrayList particles, bool global) {
941 if (!particles.Contains(p)) {
942 particles.Add(p);
944 //Only then it can be head of substitutionGrp, if it is, add its members
945 XmlSchemaElement elem = p as XmlSchemaElement;
946 if (elem != null && (global ||!elem.RefName.IsEmpty)) {
947 XmlSchemaObjectTable substitutionGroups = schemaSet.SubstitutionGroups;
948 XmlSchemaSubstitutionGroup grp = (XmlSchemaSubstitutionGroup)substitutionGroups[elem.QualifiedName];
949 if (grp != null) {
950 //Grp members wil contain the head as well, so filter head as we added it already
951 for (int i = 0; i < grp.Members.Count; ++i) {
952 XmlSchemaElement member = (XmlSchemaElement)grp.Members[i];
953 if (!elem.QualifiedName.Equals(member.QualifiedName) && !particles.Contains(member)) { //A member might have been directly present as an element in the content model
954 particles.Add(member);
962 sealed class ParticleContentValidator : ContentValidator {
963 SymbolsDictionary symbols;
964 Positions positions;
965 Stack stack; // parsing context
966 SyntaxTreeNode contentNode; // content model points to syntax tree
967 bool isPartial; // whether the closure applies to partial or the whole node that is on top of the stack
968 int minMaxNodesCount;
969 bool enableUpaCheck;
971 public ParticleContentValidator(XmlSchemaContentType contentType) : this(contentType, true) {
974 public ParticleContentValidator(XmlSchemaContentType contentType, bool enableUpaCheck) : base(contentType) {
975 this.enableUpaCheck = enableUpaCheck;
978 public override void InitValidation(ValidationState context) {
979 // ParticleContentValidator cannot be used during validation
980 throw new InvalidOperationException();
983 public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
984 // ParticleContentValidator cannot be used during validation
985 throw new InvalidOperationException();
988 public override bool CompleteValidation(ValidationState context) {
989 // ParticleContentValidator cannot be used during validation
990 throw new InvalidOperationException();
993 public void Start() {
994 symbols = new SymbolsDictionary();
995 positions = new Positions();
996 stack = new Stack();
999 public void OpenGroup() {
1000 stack.Push(null);
1003 public void CloseGroup() {
1004 SyntaxTreeNode node = (SyntaxTreeNode)stack.Pop();
1005 if (node == null) {
1006 return;
1008 if (stack.Count == 0) {
1009 contentNode = node;
1010 isPartial = false;
1012 else {
1013 // some collapsing to do...
1014 InteriorNode inNode = (InteriorNode)stack.Pop();
1015 if (inNode != null) {
1016 inNode.RightChild = node;
1017 node = inNode;
1018 isPartial = true;
1020 else {
1021 isPartial = false;
1023 stack.Push(node);
1027 public bool Exists(XmlQualifiedName name) {
1028 if (symbols.Exists(name)) {
1029 return true;
1031 return false;
1034 public void AddName(XmlQualifiedName name, object particle) {
1035 AddLeafNode(new LeafNode(positions.Add(symbols.AddName(name, particle), particle)));
1038 public void AddNamespaceList(NamespaceList namespaceList, object particle) {
1039 symbols.AddNamespaceList(namespaceList, particle, false);
1040 AddLeafNode(new NamespaceListNode(namespaceList, particle));
1043 private void AddLeafNode(SyntaxTreeNode node) {
1044 if (stack.Count > 0) {
1045 InteriorNode inNode = (InteriorNode)stack.Pop();
1046 if (inNode != null) {
1047 inNode.RightChild = node;
1048 node = inNode;
1051 stack.Push( node );
1052 isPartial = true;
1055 public void AddChoice() {
1056 SyntaxTreeNode node = (SyntaxTreeNode)stack.Pop();
1057 InteriorNode choice = new ChoiceNode();
1058 choice.LeftChild = node;
1059 stack.Push(choice);
1062 public void AddSequence() {
1063 SyntaxTreeNode node = (SyntaxTreeNode)stack.Pop();
1064 InteriorNode sequence = new SequenceNode();
1065 sequence.LeftChild = node;
1066 stack.Push(sequence);
1069 public void AddStar() {
1070 Closure(new StarNode());
1073 public void AddPlus() {
1074 Closure(new PlusNode());
1077 public void AddQMark() {
1078 Closure(new QmarkNode());
1081 public void AddLeafRange(decimal min, decimal max) {
1082 LeafRangeNode rNode = new LeafRangeNode(min, max);
1083 int pos = positions.Add(-2, rNode);
1084 rNode.Pos = pos;
1086 InteriorNode sequence = new SequenceNode();
1087 sequence.RightChild = rNode;
1088 Closure(sequence);
1089 minMaxNodesCount++;
1092 #if EXPANDRANGE
1093 public void AddRange(int min, int max) {
1094 Closure(new RangeNode(min, max));
1096 #endif
1097 private void Closure(InteriorNode node) {
1098 if (stack.Count > 0) {
1099 SyntaxTreeNode topNode = (SyntaxTreeNode)stack.Pop();
1100 InteriorNode inNode = topNode as InteriorNode;
1101 if (isPartial && inNode != null) {
1102 // need to reach in and wrap right hand side of element.
1103 // and n remains the same.
1104 node.LeftChild = inNode.RightChild;
1105 inNode.RightChild = node;
1107 else {
1108 // wrap terminal or any node
1109 node.LeftChild = topNode;
1110 topNode = node;
1112 stack.Push(topNode);
1114 else if (contentNode != null) { //If there is content to wrap
1115 // wrap whole content
1116 node.LeftChild = contentNode;
1117 contentNode = node;
1121 public ContentValidator Finish() {
1122 return Finish(true);
1125 public ContentValidator Finish(bool useDFA) {
1126 Debug.Assert(ContentType == XmlSchemaContentType.ElementOnly || ContentType == XmlSchemaContentType.Mixed);
1127 if (contentNode == null) {
1128 if (ContentType == XmlSchemaContentType.Mixed) {
1129 string ctype = IsOpen ? "Any" : "TextOnly";
1130 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "\t\t\tContentType: " + ctype);
1131 return IsOpen ? ContentValidator.Any : ContentValidator.TextOnly;
1133 else {
1134 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "\t\t\tContent: EMPTY");
1135 Debug.Assert(!IsOpen);
1136 return ContentValidator.Empty;
1140 #if DEBUG
1141 StringBuilder bb = new StringBuilder();
1142 contentNode.Dump(bb, symbols, positions);
1143 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "\t\t\tContent : " + bb.ToString());
1144 #endif
1146 // Add end marker
1147 InteriorNode contentRoot = new SequenceNode();
1148 contentRoot.LeftChild = contentNode;
1149 LeafNode endMarker = new LeafNode(positions.Add(symbols.AddName(XmlQualifiedName.Empty, null), null));
1150 contentRoot.RightChild = endMarker;
1152 // Eliminate NamespaceListNode(s) and RangeNode(s)
1153 contentNode.ExpandTree(contentRoot, symbols, positions);
1155 #if DEBUG
1156 bb = new StringBuilder();
1157 contentRoot.LeftChild.Dump(bb, symbols, positions);
1158 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "\t\t\tExpended: " + bb.ToString());
1159 #endif
1161 // calculate followpos
1162 int symbolsCount = symbols.Count;
1163 int positionsCount = positions.Count;
1164 BitSet firstpos = new BitSet(positionsCount);
1165 BitSet lastpos = new BitSet(positionsCount);
1166 BitSet[] followpos = new BitSet[positionsCount];
1167 for (int i = 0; i < positionsCount; i++) {
1168 followpos[i] = new BitSet(positionsCount);
1170 contentRoot.ConstructPos(firstpos, lastpos, followpos);
1171 #if DEBUG
1172 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "firstpos, lastpos, followpos");
1173 SequenceNode.WritePos(firstpos, lastpos, followpos);
1174 #endif
1175 if (minMaxNodesCount > 0) { //If the tree has any terminal range nodes
1176 BitSet positionsWithRangeTerminals;
1177 BitSet[] minMaxFollowPos = CalculateTotalFollowposForRangeNodes(firstpos, followpos, out positionsWithRangeTerminals);
1179 if (enableUpaCheck) {
1180 CheckCMUPAWithLeafRangeNodes(GetApplicableMinMaxFollowPos(firstpos, positionsWithRangeTerminals, minMaxFollowPos));
1181 for (int i = 0; i < positionsCount; i++) {
1182 CheckCMUPAWithLeafRangeNodes(GetApplicableMinMaxFollowPos(followpos[i], positionsWithRangeTerminals, minMaxFollowPos));
1185 return new RangeContentValidator(firstpos, followpos, symbols, positions, endMarker.Pos, this.ContentType, contentRoot.LeftChild.IsNullable, positionsWithRangeTerminals, minMaxNodesCount);
1187 else {
1188 int[][] transitionTable = null;
1189 // if each symbol has unique particle we are golden
1190 if (!symbols.IsUpaEnforced) {
1191 if (enableUpaCheck) {
1192 // multiple positions that match the same symbol have different particles, but they never follow the same position
1193 CheckUniqueParticleAttribution(firstpos, followpos);
1196 else if (useDFA) {
1197 // Can return null if the number of states reaches higher than 8192 / positionsCount
1198 transitionTable = BuildTransitionTable(firstpos, followpos, endMarker.Pos);
1200 #if DEBUG
1201 bb = new StringBuilder();
1202 Dump(bb, followpos, transitionTable);
1203 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, bb.ToString());
1204 #endif
1205 if (transitionTable != null) {
1206 return new DfaContentValidator(transitionTable, symbols,this.ContentType, this.IsOpen, contentRoot.LeftChild.IsNullable);
1207 } else {
1208 return new NfaContentValidator(firstpos, followpos, symbols, positions, endMarker.Pos, this.ContentType, this.IsOpen, contentRoot.LeftChild.IsNullable);
1213 private BitSet[] CalculateTotalFollowposForRangeNodes(BitSet firstpos, BitSet[] followpos, out BitSet posWithRangeTerminals) {
1214 int positionsCount = positions.Count; //terminals
1215 posWithRangeTerminals = new BitSet(positionsCount);
1217 //Compute followpos for each range node
1218 //For any range node that is surrounded by an outer range node, its follow positions will include those of the outer range node
1219 BitSet[] minmaxFollowPos = new BitSet[minMaxNodesCount];
1220 int localMinMaxNodesCount = 0;
1222 for (int i = positionsCount - 1; i >= 0; i--) {
1223 Position p = positions[i];
1224 if (p.symbol == -2) { //P is a LeafRangeNode
1225 LeafRangeNode lrNode = p.particle as LeafRangeNode;
1226 Debug.Assert(lrNode != null);
1227 BitSet tempFollowPos = new BitSet(positionsCount);
1228 tempFollowPos.Clear();
1229 tempFollowPos.Or(followpos[i]); //Add the followpos of the range node
1230 if (lrNode.Min != lrNode.Max) { //If they are the same, then followpos cannot include the firstpos
1231 tempFollowPos.Or(lrNode.NextIteration); //Add the nextIteration of the range node (this is the firstpos of its parent's leftChild)
1234 //For each position in the bitset, if it is a outer range node (pos > i), then add its followpos as well to the current node's followpos
1235 for (int pos = tempFollowPos.NextSet(-1); pos != -1; pos = tempFollowPos.NextSet(pos)) {
1236 if (pos > i) {
1237 Position p1 = positions[pos];
1238 if (p1.symbol == -2) {
1239 LeafRangeNode lrNode1 = p1.particle as LeafRangeNode;
1240 Debug.Assert(lrNode1 != null);
1241 tempFollowPos.Or(minmaxFollowPos[lrNode1.Pos]);
1245 //set the followpos built to the index in the BitSet[]
1246 minmaxFollowPos[localMinMaxNodesCount] = tempFollowPos;
1247 lrNode.Pos = localMinMaxNodesCount++;
1248 posWithRangeTerminals.Set(i);
1251 return minmaxFollowPos;
1254 private void CheckCMUPAWithLeafRangeNodes(BitSet curpos) {
1255 object[] symbolMatches = new object[symbols.Count];
1256 for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
1257 Position currentPosition = positions[pos];
1258 int symbol = currentPosition.symbol;
1259 if (symbol >= 0) { //its not a range position
1260 if (symbolMatches[symbol] != null) {
1261 throw new UpaException(symbolMatches[symbol], currentPosition.particle);
1263 else {
1264 symbolMatches[symbol] = currentPosition.particle;
1270 //For each position, this method calculates the additional follows of any range nodes that need to be added to its followpos
1271 //((ab?)2-4)c, Followpos of a is b as well as that of node R(2-4) = c
1272 private BitSet GetApplicableMinMaxFollowPos(BitSet curpos, BitSet posWithRangeTerminals, BitSet[] minmaxFollowPos) {
1273 if (curpos.Intersects(posWithRangeTerminals)) {
1274 BitSet newSet = new BitSet(positions.Count); //Doing work again
1275 newSet.Or(curpos);
1276 newSet.And(posWithRangeTerminals);
1277 curpos = curpos.Clone();
1278 for (int pos = newSet.NextSet(-1); pos != -1; pos = newSet.NextSet(pos)) {
1279 LeafRangeNode lrNode = positions[pos].particle as LeafRangeNode;
1280 curpos.Or(minmaxFollowPos[lrNode.Pos]);
1283 return curpos;
1286 private void CheckUniqueParticleAttribution(BitSet firstpos, BitSet[] followpos) {
1287 CheckUniqueParticleAttribution(firstpos);
1288 for (int i = 0; i < positions.Count; i++) {
1289 CheckUniqueParticleAttribution(followpos[i]);
1293 private void CheckUniqueParticleAttribution(BitSet curpos) {
1294 // particles will be attributed uniquely if the same symbol never poins to two different ones
1295 object[] particles = new object[symbols.Count];
1296 for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
1297 // if position can follow
1298 int symbol = positions[pos].symbol;
1299 if (particles[symbol] == null) {
1300 // set particle for the symbol
1301 particles[symbol] = positions[pos].particle;
1303 else if (particles[symbol] != positions[pos].particle) {
1304 throw new UpaException(particles[symbol], positions[pos].particle);
1306 // two different position point to the same symbol and particle - that's OK
1310 /// <summary>
1311 /// Algorithm 3.5 Construction of a DFA from a regular expression
1312 /// </summary>
1313 private int[][] BuildTransitionTable(BitSet firstpos, BitSet[] followpos, int endMarkerPos) {
1314 const int TimeConstant = 8192; //(MaxStates * MaxPositions should be a constant)
1315 int positionsCount = positions.Count;
1316 int MaxStatesCount = TimeConstant / positionsCount;
1317 int symbolsCount = symbols.Count;
1319 // transition table (Dtran in the book)
1320 ArrayList transitionTable = new ArrayList();
1322 // state lookup table (Dstate in the book)
1323 Hashtable stateTable = new Hashtable();
1325 // Add empty set that would signal an error
1326 stateTable.Add(new BitSet(positionsCount), -1);
1328 // lists unmarked states
1329 Queue unmarked = new Queue();
1331 // initially, the only unmarked state in Dstates is firstpo(root)
1332 int state = 0;
1333 unmarked.Enqueue(firstpos);
1334 stateTable.Add(firstpos, 0);
1335 transitionTable.Add(new int[symbolsCount + 1]);
1337 // while there is an umnarked state T in Dstates do begin
1338 while (unmarked.Count > 0) {
1339 BitSet statePosSet = (BitSet)unmarked.Dequeue(); // all positions that constitute DFA state
1340 Debug.Assert(state == (int)stateTable[statePosSet]); // just make sure that statePosSet is for correct state
1341 int[] transition = (int[])transitionTable[state];
1342 if (statePosSet[endMarkerPos]) {
1343 transition[symbolsCount] = 1; // accepting
1346 // for each input symbol a do begin
1347 for (int symbol = 0; symbol < symbolsCount; symbol ++) {
1348 // let U be the set of positions that are in followpos(p)
1349 // for some position p in T
1350 // such that the symbol at position p is a
1351 BitSet newset = new BitSet(positionsCount);
1352 for (int pos = statePosSet.NextSet(-1); pos != -1; pos = statePosSet.NextSet(pos)) {
1353 if (symbol == positions[pos].symbol) {
1354 newset.Or(followpos[pos]);
1358 // if U is not empty and is not in Dstates then
1359 // add U as an unmarked state to Dstates
1360 object lookup = stateTable[newset];
1361 if (lookup != null) {
1362 transition[symbol] = (int)lookup;
1364 else {
1365 // construct new state
1366 int newState = stateTable.Count - 1;
1367 if (newState >= MaxStatesCount) {
1368 return null;
1370 unmarked.Enqueue(newset);
1371 stateTable.Add(newset, newState);
1372 transitionTable.Add(new int[symbolsCount + 1]);
1373 transition[symbol] = newState;
1376 state++;
1378 // now convert transition table to array
1379 return (int[][])transitionTable.ToArray(typeof(int[]));
1382 #if DEBUG
1383 private void Dump(StringBuilder bb, BitSet[] followpos, int[][] transitionTable) {
1384 // Temporary printout
1385 bb.AppendLine("Positions");
1386 for (int i = 0; i < positions.Count; i ++) {
1387 bb.AppendLine(i + " " + positions[i].symbol.ToString(NumberFormatInfo.InvariantInfo) + " " + symbols.NameOf(positions[i].symbol));
1389 bb.AppendLine("Followpos");
1390 for (int i = 0; i < positions.Count; i++) {
1391 for (int j = 0; j < positions.Count; j++) {
1392 bb.Append(followpos[i][j] ? "X" : "O");
1394 bb.AppendLine();
1396 if (transitionTable != null) {
1397 // Temporary printout
1398 bb.AppendLine("Transitions");
1399 for (int i = 0; i < transitionTable.Length; i++) {
1400 for (int j = 0; j < symbols.Count; j++) {
1401 if (transitionTable[i][j] == -1) {
1402 bb.Append(" x ");
1404 else {
1405 bb.AppendFormat(" {0:000} ", transitionTable[i][j]);
1408 bb.AppendLine(transitionTable[i][symbols.Count] == 1 ? "+" : "");
1412 #endif
1415 /// <summary>
1416 /// Deterministic Finite Automata
1417 /// Compilers by Aho, Sethi, Ullman.
1418 /// ISBN 0-201-10088-6, pp. 115, 116, 140
1419 /// </summary>
1420 sealed class DfaContentValidator : ContentValidator {
1421 int[][] transitionTable;
1422 SymbolsDictionary symbols;
1424 /// <summary>
1425 /// Algorithm 3.5 Construction of a DFA from a regular expression
1426 /// </summary>
1427 internal DfaContentValidator(
1428 int[][] transitionTable, SymbolsDictionary symbols,
1429 XmlSchemaContentType contentType, bool isOpen, bool isEmptiable) : base(contentType, isOpen, isEmptiable) {
1430 this.transitionTable = transitionTable;
1431 this.symbols = symbols;
1434 public override void InitValidation(ValidationState context) {
1435 context.CurrentState.State = 0;
1436 context.HasMatched = transitionTable[0][symbols.Count] > 0;
1439 /// <summary>
1440 /// Algorithm 3.1 Simulating a DFA
1441 /// </summary>
1442 public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
1443 int symbol = symbols[name];
1444 int state = transitionTable[context.CurrentState.State][symbol];
1445 errorCode = 0;
1446 if (state != -1) {
1447 context.CurrentState.State = state;
1448 context.HasMatched = transitionTable[context.CurrentState.State][symbols.Count] > 0;
1449 return symbols.GetParticle(symbol); // OK
1451 if (IsOpen && context.HasMatched) {
1452 // XDR allows any well-formed contents after matched.
1453 return null;
1455 context.NeedValidateChildren = false;
1456 errorCode = -1;
1457 return null; // will never be here
1460 public override bool CompleteValidation(ValidationState context) {
1461 if (!context.HasMatched) {
1462 return false;
1464 return true;
1467 public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
1468 ArrayList names = null;
1469 int[] transition = transitionTable[context.CurrentState.State];
1470 if (transition != null) {
1471 for (int i = 0; i < transition.Length - 1; i ++) {
1472 if (transition[i] != -1) {
1473 if (names == null) {
1474 names = new ArrayList();
1476 XmlSchemaParticle p = (XmlSchemaParticle)symbols.GetParticle(i);
1477 if (p == null) {
1478 string s = symbols.NameOf(i);
1479 if (s.Length != 0) {
1480 names.Add(s);
1483 else {
1484 string s = p.NameString;
1485 if (!names.Contains(s)) {
1486 names.Add(s);
1492 return names;
1495 public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
1496 ArrayList particles = new ArrayList();
1497 int[] transition = transitionTable[context.CurrentState.State];
1498 if (transition != null) {
1499 for (int i = 0; i < transition.Length - 1; i ++) {
1500 if (transition[i] != -1) {
1501 XmlSchemaParticle p = (XmlSchemaParticle)symbols.GetParticle(i);
1502 if (p == null) {
1503 continue;
1505 AddParticleToExpected(p, schemaSet, particles);
1509 return particles;
1513 /// <summary>
1514 /// Nondeterministic Finite Automata
1515 /// Compilers by Aho, Sethi, Ullman.
1516 /// ISBN 0-201-10088-6, pp. 126,140
1517 /// </summary>
1518 sealed class NfaContentValidator : ContentValidator {
1519 BitSet firstpos;
1520 BitSet[] followpos;
1521 SymbolsDictionary symbols;
1522 Positions positions;
1523 int endMarkerPos;
1525 internal NfaContentValidator(
1526 BitSet firstpos, BitSet[] followpos, SymbolsDictionary symbols, Positions positions, int endMarkerPos,
1527 XmlSchemaContentType contentType, bool isOpen, bool isEmptiable) : base(contentType, isOpen, isEmptiable) {
1528 this.firstpos = firstpos;
1529 this.followpos = followpos;
1530 this.symbols = symbols;
1531 this.positions = positions;
1532 this.endMarkerPos = endMarkerPos;
1535 public override void InitValidation(ValidationState context) {
1536 context.CurPos[0] = firstpos.Clone();
1537 context.CurPos[1] = new BitSet(firstpos.Count);
1538 context.CurrentState.CurPosIndex = 0;
1541 /// <summary>
1542 /// Algorithm 3.4 Simulation of an NFA
1543 /// </summary>
1544 public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
1545 BitSet curpos = context.CurPos[context.CurrentState.CurPosIndex];
1546 int next = (context.CurrentState.CurPosIndex + 1) % 2;
1547 BitSet nextpos = context.CurPos[next];
1548 nextpos.Clear();
1549 int symbol = symbols[name];
1550 object particle = null;
1551 errorCode = 0;
1552 for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
1553 // if position can follow
1554 if (symbol == positions[pos].symbol) {
1555 nextpos.Or(followpos[pos]);
1556 particle = positions[pos].particle; //Between element and wildcard, element will be in earlier pos than wildcard since we add the element nodes to the list of positions first
1557 break; // and then ExpandTree for the namespace nodes which adds the wildcards to the positions list
1560 if (!nextpos.IsEmpty) {
1561 context.CurrentState.CurPosIndex = next;
1562 return particle;
1564 if (IsOpen && curpos[endMarkerPos]) {
1565 // XDR allows any well-formed contents after matched.
1566 return null;
1568 context.NeedValidateChildren = false;
1569 errorCode = -1;
1570 return null; // will never be here
1574 #if FINDUPA_PARTICLE
1575 private bool FindUPAParticle(ref object originalParticle, object newParticle) {
1576 if (originalParticle == null) {
1577 originalParticle = newParticle;
1578 if (originalParticle is XmlSchemaElement) { //if the first particle is element, then break, otherwise try to find an element
1579 return true;
1582 else if (newParticle is XmlSchemaElement) {
1583 if (originalParticle is XmlSchemaAny) { //Weak wildcards, element takes precendence over any
1584 originalParticle = newParticle;
1585 return true;
1588 return false;
1590 #endif
1592 public override bool CompleteValidation(ValidationState context) {
1593 if (!context.CurPos[context.CurrentState.CurPosIndex][endMarkerPos]) {
1594 return false;
1596 return true;
1599 public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
1600 ArrayList names = null;
1601 BitSet curpos = context.CurPos[context.CurrentState.CurPosIndex];
1602 for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
1603 if (names == null) {
1604 names = new ArrayList();
1606 XmlSchemaParticle p = (XmlSchemaParticle)positions[pos].particle;
1607 if (p == null) {
1608 string s = symbols.NameOf(positions[pos].symbol);
1609 if (s.Length != 0) {
1610 names.Add(s);
1613 else {
1614 string s = p.NameString;
1615 if (!names.Contains(s)) {
1616 names.Add(s);
1620 return names;
1623 public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
1624 ArrayList particles = new ArrayList();
1625 BitSet curpos = context.CurPos[context.CurrentState.CurPosIndex];
1626 for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
1627 XmlSchemaParticle p = (XmlSchemaParticle)positions[pos].particle;
1628 if (p == null) {
1629 continue;
1631 else {
1632 AddParticleToExpected(p, schemaSet, particles);
1635 return particles;
1639 internal struct RangePositionInfo {
1640 public BitSet curpos;
1641 public decimal[] rangeCounters;
1644 sealed class RangeContentValidator : ContentValidator {
1645 BitSet firstpos;
1646 BitSet[] followpos;
1647 BitSet positionsWithRangeTerminals;
1648 SymbolsDictionary symbols;
1649 Positions positions;
1650 int minMaxNodesCount;
1651 int endMarkerPos;
1653 internal RangeContentValidator(
1654 BitSet firstpos, BitSet[] followpos, SymbolsDictionary symbols, Positions positions, int endMarkerPos, XmlSchemaContentType contentType, bool isEmptiable, BitSet positionsWithRangeTerminals, int minmaxNodesCount) : base(contentType, false, isEmptiable) {
1655 this.firstpos = firstpos;
1656 this.followpos = followpos;
1657 this.symbols = symbols;
1658 this.positions = positions;
1659 this.positionsWithRangeTerminals = positionsWithRangeTerminals;
1660 this.minMaxNodesCount = minmaxNodesCount;
1661 this.endMarkerPos = endMarkerPos;
1664 public override void InitValidation(ValidationState context) {
1665 int positionsCount = positions.Count;
1666 List<RangePositionInfo> runningPositions = context.RunningPositions;
1667 if (runningPositions != null) {
1668 Debug.Assert(minMaxNodesCount != 0);
1669 runningPositions.Clear();
1671 else {
1672 runningPositions = new List<RangePositionInfo>();
1673 context.RunningPositions = runningPositions;
1675 RangePositionInfo rposInfo = new RangePositionInfo();
1676 rposInfo.curpos = firstpos.Clone();
1678 rposInfo.rangeCounters = new decimal[minMaxNodesCount];
1679 runningPositions.Add(rposInfo);
1680 context.CurrentState.NumberOfRunningPos = 1;
1681 context.HasMatched = rposInfo.curpos.Get(endMarkerPos);
1684 public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
1685 errorCode = 0;
1686 int symbol = symbols[name];
1687 bool hasSeenFinalPosition = false;
1688 List<RangePositionInfo> runningPositions = context.RunningPositions;
1689 int matchCount = context.CurrentState.NumberOfRunningPos;
1690 int k = 0;
1691 RangePositionInfo rposInfo;
1693 int pos = -1;
1694 int firstMatchedIndex = -1;
1695 bool matched = false;
1697 #if RANGE_DEBUG
1698 WriteRunningPositions("Current running positions to match", runningPositions, name, matchCount);
1699 #endif
1701 while (k < matchCount) { //we are looking for the first match in the list of bitsets
1702 rposInfo = runningPositions[k];
1703 BitSet curpos = rposInfo.curpos;
1704 for (int matchpos = curpos.NextSet(-1); matchpos != -1; matchpos = curpos.NextSet(matchpos)) { //In all sets, have to scan all positions because of Disabled UPA possibility
1705 if (symbol == positions[matchpos].symbol) {
1706 pos = matchpos;
1707 if (firstMatchedIndex == -1) { // get the first match for this symbol
1708 firstMatchedIndex = k;
1710 matched = true;
1711 break;
1714 if (matched && positions[pos].particle is XmlSchemaElement) { //We found a match in the list, break at that bitset
1715 break;
1717 else {
1718 k++;
1722 if (k == matchCount && pos != -1) { // we did find a match but that was any and hence continued ahead for element
1723 k = firstMatchedIndex;
1725 if (k < matchCount) { //There is a match
1726 if (k != 0) { //If the first bitset itself matched, then no need to remove anything
1727 #if RANGE_DEBUG
1728 WriteRunningPositions("Removing unmatched entries till the first match", runningPositions, XmlQualifiedName.Empty, k);
1729 #endif
1730 runningPositions.RemoveRange(0, k); //Delete entries from 0 to k-1
1732 matchCount = matchCount - k;
1733 k = 0; // Since we re-sized the array
1734 while (k < matchCount) {
1735 rposInfo = runningPositions[k];
1736 matched = rposInfo.curpos.Get(pos); //Look for the bitset that matches the same position as pos
1737 if (matched) { //If match found, get the follow positions of the current matched position
1738 #if RANGE_DEBUG
1739 Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "Matched position: " + pos + " "); SequenceNode.WriteBitSet(rposInfo.curpos);
1740 Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "Follow pos of Matched position: "); SequenceNode.WriteBitSet(followpos[pos]);
1741 #endif
1742 rposInfo.curpos = followpos[pos]; //Note that we are copying the same counters of the current position to that of the follow position
1743 runningPositions[k] = rposInfo;
1744 k++;
1746 else { //Clear the current pos and get another position from the list to start matching
1747 matchCount--;
1748 if (matchCount > 0) {
1749 RangePositionInfo lastrpos = runningPositions[matchCount];
1750 runningPositions[matchCount] = runningPositions[k];
1751 runningPositions[k] = lastrpos;
1756 else { //There is no match
1757 matchCount = 0;
1760 if (matchCount > 0) {
1761 Debug.Assert(minMaxNodesCount > 0);
1762 if (matchCount >= 10000) {
1763 context.TooComplex = true;
1764 matchCount /= 2;
1766 #if RANGE_DEBUG
1767 WriteRunningPositions("Matched positions to expand ", runningPositions, name, matchCount);
1768 #endif
1770 for (k = matchCount - 1; k >= 0; k--) {
1771 int j = k;
1772 BitSet currentRunningPosition = runningPositions[k].curpos;
1773 hasSeenFinalPosition = hasSeenFinalPosition || currentRunningPosition.Get(endMarkerPos); //Accepting position reached if the current position BitSet contains the endPosition
1774 while (matchCount < 10000 && currentRunningPosition.Intersects(positionsWithRangeTerminals)) {
1775 //Now might add 2 more positions to followpos
1776 //1. nextIteration of the rangeNode, which is firstpos of its parent's leftChild
1777 //2. Followpos of the range node
1779 BitSet countingPosition = currentRunningPosition.Clone();
1780 countingPosition.And(positionsWithRangeTerminals);
1781 int cPos = countingPosition.NextSet(-1); //Get the first position where leaf range node appears
1782 LeafRangeNode lrNode = positions[cPos].particle as LeafRangeNode; //For a position with leaf range node, the particle is the node itself
1783 Debug.Assert(lrNode != null);
1785 rposInfo = runningPositions[j];
1786 if (matchCount + 2 >= runningPositions.Count) {
1787 runningPositions.Add(new RangePositionInfo());
1788 runningPositions.Add(new RangePositionInfo());
1790 RangePositionInfo newRPosInfo = runningPositions[matchCount];
1791 if (newRPosInfo.rangeCounters == null) {
1792 newRPosInfo.rangeCounters = new decimal[minMaxNodesCount];
1794 Array.Copy(rposInfo.rangeCounters, 0, newRPosInfo.rangeCounters, 0, rposInfo.rangeCounters.Length);
1795 decimal count = ++newRPosInfo.rangeCounters[lrNode.Pos];
1797 if (count == lrNode.Max) {
1798 newRPosInfo.curpos = followpos[cPos]; //since max has been reached, Get followposition of range node
1799 newRPosInfo.rangeCounters[lrNode.Pos] = 0; //reset counter
1800 runningPositions[matchCount] = newRPosInfo;
1801 j = matchCount++;
1803 else if (count < lrNode.Min) {
1804 newRPosInfo.curpos = lrNode.NextIteration;
1805 runningPositions[matchCount] = newRPosInfo;
1806 matchCount++;
1807 break;
1809 else { // min <= count < max
1810 newRPosInfo.curpos = lrNode.NextIteration; //set currentpos to firstpos of node which has the range
1811 runningPositions[matchCount] = newRPosInfo;
1812 j = matchCount + 1;
1813 newRPosInfo = runningPositions[j];
1814 if (newRPosInfo.rangeCounters == null) {
1815 newRPosInfo.rangeCounters = new decimal[minMaxNodesCount];
1817 Array.Copy(rposInfo.rangeCounters, 0, newRPosInfo.rangeCounters, 0, rposInfo.rangeCounters.Length);
1818 newRPosInfo.curpos = followpos[cPos];
1819 newRPosInfo.rangeCounters[lrNode.Pos] = 0;
1820 runningPositions[j] = newRPosInfo;
1821 matchCount += 2;
1823 currentRunningPosition = runningPositions[j].curpos;
1824 hasSeenFinalPosition = hasSeenFinalPosition || currentRunningPosition.Get(endMarkerPos);
1827 context.HasMatched = hasSeenFinalPosition;
1828 context.CurrentState.NumberOfRunningPos = matchCount;
1829 return positions[pos].particle;
1830 } //matchcount > 0
1831 errorCode = -1;
1832 context.NeedValidateChildren = false;
1833 return null;
1836 #if RANGE_DEBUG
1837 private void WriteRunningPositions(string text, List<RangePositionInfo> runningPositions, XmlQualifiedName name, int length) {
1838 int counter = 0;
1839 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
1840 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
1841 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, text + name.Name);
1842 while (counter < length) {
1843 BitSet curpos = runningPositions[counter].curpos;
1844 SequenceNode.WriteBitSet(curpos);
1845 for(int rcnt = 0; rcnt < runningPositions[counter].rangeCounters.Length; rcnt++) {
1846 Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "RangeCounter[" + rcnt + "]" + runningPositions[counter].rangeCounters[rcnt] + " ");
1848 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
1849 Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
1850 counter++;
1853 #endif
1854 public override bool CompleteValidation(ValidationState context) {
1855 return context.HasMatched;
1858 public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
1859 ArrayList names = null;
1860 BitSet expectedPos;
1861 if (context.RunningPositions != null) {
1862 List<RangePositionInfo> runningPositions = context.RunningPositions;
1863 expectedPos = new BitSet(positions.Count);
1864 for (int i = context.CurrentState.NumberOfRunningPos - 1; i >=0; i--) {
1865 Debug.Assert(runningPositions[i].curpos != null);
1866 expectedPos.Or(runningPositions[i].curpos);
1868 for (int pos = expectedPos.NextSet(-1); pos != -1; pos = expectedPos.NextSet(pos)) {
1869 if (names == null) {
1870 names = new ArrayList();
1872 int symbol = positions[pos].symbol;
1873 if (symbol >= 0) { //non range nodes
1874 XmlSchemaParticle p = positions[pos].particle as XmlSchemaParticle;
1875 if (p == null) {
1876 string s = symbols.NameOf(positions[pos].symbol);
1877 if (s.Length != 0) {
1878 names.Add(s);
1881 else {
1882 string s = p.NameString;
1883 if (!names.Contains(s)) {
1884 names.Add(s);
1890 return names;
1893 public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
1894 ArrayList particles = new ArrayList();
1895 BitSet expectedPos;
1896 if (context.RunningPositions != null) {
1897 List<RangePositionInfo> runningPositions = context.RunningPositions;
1898 expectedPos = new BitSet(positions.Count);
1899 for (int i = context.CurrentState.NumberOfRunningPos - 1; i >=0; i--) {
1900 Debug.Assert(runningPositions[i].curpos != null);
1901 expectedPos.Or(runningPositions[i].curpos);
1903 for (int pos = expectedPos.NextSet(-1); pos != -1; pos = expectedPos.NextSet(pos)) {
1904 int symbol = positions[pos].symbol;
1905 if (symbol >= 0) { //non range nodes
1906 XmlSchemaParticle p = positions[pos].particle as XmlSchemaParticle;
1907 if (p == null) {
1908 continue;
1910 AddParticleToExpected(p, schemaSet, particles);
1914 return particles;
1918 sealed class AllElementsContentValidator : ContentValidator {
1919 Hashtable elements; // unique terminal names to positions in Bitset mapping
1920 object[] particles;
1921 BitSet isRequired; // required flags
1922 int countRequired = 0;
1924 public AllElementsContentValidator(XmlSchemaContentType contentType, int size, bool isEmptiable) : base(contentType, false, isEmptiable) {
1925 elements = new Hashtable(size);
1926 particles = new object[size];
1927 isRequired = new BitSet(size);
1930 public bool AddElement(XmlQualifiedName name, object particle, bool isEmptiable) {
1931 if (elements[name] != null) {
1932 return false;
1934 int i = elements.Count;
1935 elements.Add(name, i);
1936 particles[i] = particle;
1937 if (!isEmptiable) {
1938 isRequired.Set(i);
1939 countRequired ++;
1941 return true;
1944 public override bool IsEmptiable {
1945 get { return base.IsEmptiable || countRequired == 0; }
1948 public override void InitValidation(ValidationState context) {
1949 Debug.Assert(elements.Count > 0);
1950 context.AllElementsSet = new BitSet(elements.Count); //
1951 context.CurrentState.AllElementsRequired = -1; // no elements at all
1954 public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
1955 object lookup = elements[name];
1956 errorCode = 0;
1957 if (lookup == null) {
1958 context.NeedValidateChildren = false;
1959 return null;
1961 int index = (int)lookup;
1962 if (context.AllElementsSet[index]) {
1963 errorCode = -2;
1964 return null;
1966 if (context.CurrentState.AllElementsRequired == -1) {
1967 context.CurrentState.AllElementsRequired = 0;
1969 context.AllElementsSet.Set(index);
1970 if (isRequired[index]) {
1971 context.CurrentState.AllElementsRequired++;
1973 return particles[index];
1976 public override bool CompleteValidation(ValidationState context) {
1977 if (context.CurrentState.AllElementsRequired == countRequired || IsEmptiable && context.CurrentState.AllElementsRequired == -1) {
1978 return true;
1980 return false;
1983 public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
1984 ArrayList names = null;
1985 foreach (DictionaryEntry entry in elements) {
1986 if (!context.AllElementsSet[(int)entry.Value] && (!isRequiredOnly || isRequired[(int)entry.Value])) {
1987 if (names == null) {
1988 names = new ArrayList();
1990 names.Add(entry.Key);
1993 return names;
1996 public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
1997 ArrayList expectedParticles = new ArrayList();
1998 foreach (DictionaryEntry entry in elements) {
1999 if (!context.AllElementsSet[(int)entry.Value] && (!isRequiredOnly || isRequired[(int)entry.Value])) {
2000 AddParticleToExpected(this.particles[(int)entry.Value] as XmlSchemaParticle, schemaSet, expectedParticles);
2003 return expectedParticles;
2006 #endregion