//------------------------------------------------------------------------------
// <copyright file="ContentValidator.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Diagnostics;

namespace System.Xml.Schema {

    #region ExceptionSymbolsPositions
    
    /// <summary>
    /// UPA violations will throw this exception
    /// </summary>
    class UpaException : Exception {
        object particle1;
        object particle2;
        public UpaException(object particle1, object particle2) {
            this.particle1 = particle1;
            this.particle2 = particle2;
        }
        public object Particle1 { get { return particle1; } }
        public object Particle2 { get { return particle2; } }
    }

    /// <summary>
    /// SymbolsDictionary is a map between names that ContextValidator recognizes and symbols - int symbol[XmlQualifiedName name].
    /// There are two types of name - full names and wildcards (namespace is specified, local name is anythig).
    /// Wildcard excludes all full names that would match by the namespace part.
    /// SymbolsDictionry alwas recognizes all the symbols - the last one is a true wildcard - 
    ///      both name and namespace can be anything that none of the other symbols matched.
    /// </summary>
    class SymbolsDictionary {
        int last = 0;
        Hashtable names;
        Hashtable wildcards = null;
        ArrayList particles;
        object particleLast = null; 
        bool isUpaEnforced = true;

        public SymbolsDictionary() {
            names = new Hashtable();
            particles = new ArrayList();
        }

        public int Count {
            // last one is a "*:*" any wildcard
            get { return last + 1; }
        }
        
        public int CountOfNames {
            get { return names.Count; }
        }

        /// <summary>
        /// True is particle can be deterministically attributed from the symbol and conversion to DFA is possible.
        /// </summary>
        public bool IsUpaEnforced {
            get { return isUpaEnforced; }
            set { isUpaEnforced = value; }
        }
        
        /// <summary>
        /// Add name  and return it's number
        /// </summary>
        public int AddName(XmlQualifiedName name, object particle) {
            object lookup = names[name];
            if (lookup != null) {
                int symbol = (int)lookup;
                if (particles[symbol] != particle) {
                    isUpaEnforced = false;
                }
                return symbol;
            }
            else {
                names.Add(name, last);
                particles.Add(particle);
                Debug.Assert(particles.Count == last + 1);
                return last ++;
            }
        }
        
        public void AddNamespaceList(NamespaceList list, object particle, bool allowLocal) {
            switch (list.Type) {
            case NamespaceList.ListType.Any:
                particleLast = particle;   
                break;
            case NamespaceList.ListType.Other:
                // Create a symbol for the excluded namespace, but don't set a particle for it.
                AddWildcard(list.Excluded, null);
                if (!allowLocal) {
                    AddWildcard(string.Empty, null); //##local is not allowed
                }
                break; 
            case NamespaceList.ListType.Set:
                foreach(string wildcard in list.Enumerate) {
                    AddWildcard(wildcard, particle);
                }
                break;
            }
        }

        private void AddWildcard(string wildcard, object particle) {
            if (wildcards == null) {
                wildcards = new Hashtable();
            }
            object lookup = wildcards[wildcard];
            if (lookup == null) {
                wildcards.Add(wildcard, last);
                particles.Add(particle);
                Debug.Assert(particles.Count == last + 1);
                last ++;
            }
            else if (particle != null) {
                particles[(int)lookup] = particle;    
            }
        }

        public ICollection GetNamespaceListSymbols(NamespaceList list) {
            ArrayList match = new ArrayList();
            foreach(XmlQualifiedName name in names.Keys) {
                if (name != XmlQualifiedName.Empty && list.Allows(name)) {
                    match.Add(names[name]);
                }
            }
            if (wildcards != null) {
                foreach(string wildcard in wildcards.Keys) {
                    if (list.Allows(wildcard)) {
                        match.Add(wildcards[wildcard]);
                    }
                }              
            }
            if (list.Type == NamespaceList.ListType.Any || list.Type == NamespaceList.ListType.Other) {
                match.Add(last); // add wildcard
            }
            return match;
        }

        /// <summary>
        /// Find the symbol for the given name. If neither names nor wilcards match it last (*.*) symbol will be returned
        /// </summary>
        public int this[XmlQualifiedName name] {
            get {
                object lookup = names[name];
                if (lookup != null) {
                    return (int)lookup;
                }
                if (wildcards != null) {
                    lookup = wildcards[name.Namespace];
                    if (lookup != null) {
                        return (int)lookup;
                    }   
                }
                return last; // true wildcard
            }
        }
        
        /// <summary>
        /// Check if a name exists in the symbol dictionary
        /// </summary>
        public bool Exists(XmlQualifiedName name) {

            object lookup = names[name];
            if (lookup != null) {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Return content processing mode for the symbol
        /// </summary>
        public object GetParticle(int symbol) {
            return symbol == last ? particleLast : particles[symbol];
        }

        /// <summary>
        /// Output symbol's name
        /// </summary>
        public string NameOf(int symbol) {
            foreach (DictionaryEntry de in names) {
                if ((int)de.Value == symbol) {
                    return ((XmlQualifiedName)de.Key).ToString();
                }
            }
            if (wildcards != null) {
                foreach (DictionaryEntry de in wildcards) {
                    if ((int)de.Value == symbol) {
                        return (string)de.Key + ":*";
                    }
                }
            }
            return "##other:*";
        }
    }

    struct Position {
        public int symbol;
        public object particle;
        public Position(int symbol, object particle) {
            this.symbol = symbol;
            this.particle = particle;
        }
    }

    class Positions {
        ArrayList positions = new ArrayList();

        public int Add(int symbol, object particle) {
            return positions.Add(new Position(symbol, particle));
        }

        public Position this[int pos] {
            get { return (Position)positions[pos]; }
        }

        public int Count {
            get { return positions.Count; }
        }
    }
    #endregion

    #region SystaxTree
    /// <summary>
    /// Base class for the systax tree nodes
    /// </summary>
    abstract class SyntaxTreeNode {
        /// <summary>
        /// Expand NamesapceListNode and RangeNode nodes. All other nodes
        /// </summary>
        public abstract void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions);

        /// <summary>
        /// Clone the syntaxTree. We need to pass symbolsByPosition because leaf nodes have to add themselves to it.
        /// </summary>
        public abstract SyntaxTreeNode Clone(Positions positions);

        /// <summary>
        /// From a regular expression to a DFA
        /// Compilers by Aho, Sethi, Ullman.
        /// ISBN 0-201-10088-6, p135 
        /// Construct firstpos, lastpos and calculate followpos 
        /// </summary>
        public abstract void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos);

        /// <summary>
        /// Returns nullable property that is being used by ConstructPos
        /// </summary>
        public abstract bool IsNullable { get; }

        /// <summary>
        /// Returns true if node is a range node
        /// </summary>
        public virtual bool IsRangeNode { 
            get {
                return false;
            }
        }

        /// <summary>
        /// Print syntax tree
        /// </summary>
#if DEBUG        
        public abstract void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions);
#endif
    }

    /// <summary>
    /// Terminal of the syntax tree
    /// </summary>
    class LeafNode : SyntaxTreeNode {
        int pos;

        public LeafNode(int pos) {
            this.pos = pos;
        }

        public int Pos {
            get { return pos;}
            set { pos = value; }
        }

        public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
            // do nothing
        }

        public override SyntaxTreeNode Clone(Positions positions) {
            return new LeafNode(positions.Add(positions[pos].symbol, positions[pos].particle));
        }

        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            firstpos.Set(pos);
            lastpos.Set(pos);
        }

        public override bool IsNullable {
            get { return false; }
        }

#if DEBUG
        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            bb.Append("\"" + symbols.NameOf(positions[pos].symbol) + "\"");
        }
#endif
    }

    /// <summary>
    /// Temporary node to represent NamespaceList. Will be expended as a choice of symbols
    /// </summary>
    class NamespaceListNode : SyntaxTreeNode {
        protected NamespaceList namespaceList;
        protected object particle;

        public NamespaceListNode(NamespaceList namespaceList, object particle) {
            this.namespaceList = namespaceList;
            this.particle = particle;
        }

        public override SyntaxTreeNode Clone(Positions positions) {
            // NamespaceListNode nodes have to be removed prior to that
            throw new InvalidOperationException();
        }

        public virtual ICollection GetResolvedSymbols(SymbolsDictionary symbols)  {
            return symbols.GetNamespaceListSymbols(namespaceList);
        }

        public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
            SyntaxTreeNode replacementNode = null;
            foreach(int symbol in GetResolvedSymbols(symbols)) {
                if (symbols.GetParticle(symbol) != particle) {
                    symbols.IsUpaEnforced = false;
                }
                LeafNode node = new LeafNode(positions.Add(symbol, particle));
                if (replacementNode == null) {
                    replacementNode = node;
                }
                else {
                    InteriorNode choice = new ChoiceNode();
                    choice.LeftChild = replacementNode;
                    choice.RightChild = node;
                    replacementNode = choice;
                }
            }
            if (parent.LeftChild == this) {
                parent.LeftChild = replacementNode;
            }
            else {
                parent.RightChild = replacementNode;
            }
        }

        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            // NamespaceListNode nodes have to be removed prior to that
            throw new InvalidOperationException();
        }

        public override bool IsNullable {
            // NamespaceListNode nodes have to be removed prior to that
            get { throw new InvalidOperationException(); }
        }

#if DEBUG
        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            bb.Append("[" + namespaceList.ToString() + "]");
        }
#endif
    }

    /// <summary>
    /// Base class for all internal node. Note that only sequence and choice have right child
    /// </summary>
    abstract class InteriorNode : SyntaxTreeNode {
        SyntaxTreeNode leftChild;
        SyntaxTreeNode rightChild;

        public SyntaxTreeNode LeftChild {
            get { return leftChild;}
            set { leftChild = value;}
        }

        public SyntaxTreeNode RightChild {
            get { return rightChild;}
            set { rightChild = value;}
        }

        public override SyntaxTreeNode Clone(Positions positions) {
            InteriorNode other = (InteriorNode)this.MemberwiseClone();
            other.LeftChild = leftChild.Clone(positions);
            if (rightChild != null) {
                other.RightChild = rightChild.Clone(positions);
            }
            return other;
        }

        //no recursive version of expand tree for Sequence and Choice node
        protected void ExpandTreeNoRecursive(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
            Stack<InteriorNode> nodeStack = new Stack<InteriorNode>();
            InteriorNode this_ = this;
            while (true) {
                if (this_.leftChild is ChoiceNode || this_.leftChild is SequenceNode) {
                    nodeStack.Push(this_);
                    this_ = (InteriorNode)this_.leftChild;
                    continue;
                }
                this_.leftChild.ExpandTree(this_, symbols, positions);

            ProcessRight:
                if (this_.rightChild != null) {
                    this_.rightChild.ExpandTree(this_, symbols, positions);
                }

                if (nodeStack.Count == 0)
                    break;

                this_ = nodeStack.Pop();
                goto ProcessRight;
            }
        }

        public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
            leftChild.ExpandTree(this, symbols, positions);
            if (rightChild != null) {
                rightChild.ExpandTree(this, symbols, positions);
            }
        }
    }

    
    sealed class SequenceNode : InteriorNode {

        struct SequenceConstructPosContext {
            public SequenceNode this_;
            public BitSet firstpos;
            public BitSet lastpos;
            public BitSet lastposLeft;
            public BitSet firstposRight;

            public SequenceConstructPosContext(SequenceNode node, BitSet firstpos, BitSet lastpos) {
                this_ = node;
                this.firstpos = firstpos;
                this.lastpos = lastpos;

                lastposLeft = null;
                firstposRight = null;
            }
        }

        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {

            Stack<SequenceConstructPosContext> contextStack = new Stack<SequenceConstructPosContext>();
            SequenceConstructPosContext context = new SequenceConstructPosContext(this, firstpos, lastpos);

            while (true) {
                SequenceNode this_ = context.this_;
                context.lastposLeft = new BitSet(lastpos.Count);
                if (this_.LeftChild is SequenceNode) {
                    contextStack.Push(context);
                    context = new SequenceConstructPosContext((SequenceNode)this_.LeftChild, context.firstpos, context.lastposLeft);
                    continue;
                }
                this_.LeftChild.ConstructPos(context.firstpos, context.lastposLeft, followpos);

            ProcessRight:
                context.firstposRight = new BitSet(firstpos.Count);
                this_.RightChild.ConstructPos(context.firstposRight, context.lastpos, followpos);

                if (this_.LeftChild.IsNullable && !this_.RightChild.IsRangeNode) {
                    context.firstpos.Or(context.firstposRight);
                }
                if (this_.RightChild.IsNullable) {
                    context.lastpos.Or(context.lastposLeft);
                }
                for (int pos = context.lastposLeft.NextSet(-1); pos != -1; pos = context.lastposLeft.NextSet(pos)) {
                    followpos[pos].Or(context.firstposRight);
                }
                if (this_.RightChild.IsRangeNode) { //firstpos is leftchild.firstpos as the or with firstposRight has not been done as it is a rangenode
                    ((LeafRangeNode)this_.RightChild).NextIteration = context.firstpos.Clone();
                }

                if (contextStack.Count == 0)
                    break;

                context = contextStack.Pop();
                this_ = context.this_;
                goto ProcessRight;
            }
        }

        public override bool IsNullable {
            get {
                SyntaxTreeNode n;
                SequenceNode this_ = this;
                do {
                    if (this_.RightChild.IsRangeNode && ((LeafRangeNode)this_.RightChild).Min == 0)
                        return true;
                    if (!this_.RightChild.IsNullable && !this_.RightChild.IsRangeNode)
                        return false;
                    n = this_.LeftChild;
                    this_ = n as SequenceNode;
                }
                while (this_ != null);
                return n.IsNullable;
            }
        }

        public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
 	        ExpandTreeNoRecursive(parent, symbols, positions);
        }

#if DEBUG
        internal static void WritePos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "FirstPos:  ");
            WriteBitSet(firstpos);

            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "LastPos:  ");
            WriteBitSet(lastpos);

            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "Followpos:  ");
            for(int i =0; i < followpos.Length; i++) {
                WriteBitSet(followpos[i]);
            }
        }
        internal static void WriteBitSet(BitSet curpos) {
            int[] list = new int[curpos.Count];
            for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
                list[pos] = 1;
            }
            for(int i = 0; i < list.Length; i++) {
                Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, list[i] + " ");
            }
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
        }
       

        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            Stack<SequenceNode> nodeStack = new Stack<SequenceNode>();
            SequenceNode this_ = this;

            while (true) {
                bb.Append("(");
                if (this_.LeftChild is SequenceNode) {
                    nodeStack.Push(this_);
                    this_ = (SequenceNode)this_.LeftChild;
                    continue;
                }
                this_.LeftChild.Dump(bb, symbols, positions);

            ProcessRight:
                bb.Append(", ");
                this_.RightChild.Dump(bb, symbols, positions);
                bb.Append(")");
                if (nodeStack.Count == 0)
                    break;

                this_ = nodeStack.Pop();
                goto ProcessRight;
            }
        }
#endif

    }

    sealed class ChoiceNode : InteriorNode {

        private static void ConstructChildPos(SyntaxTreeNode child, BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            BitSet firstPosTemp = new BitSet(firstpos.Count);
            BitSet lastPosTemp = new BitSet(lastpos.Count);
            child.ConstructPos(firstPosTemp, lastPosTemp, followpos);
            firstpos.Or(firstPosTemp);
            lastpos.Or(lastPosTemp);
        }

        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {

            BitSet firstPosTemp = new BitSet(firstpos.Count);
            BitSet lastPosTemp = new BitSet(lastpos.Count);
            SyntaxTreeNode n;
            ChoiceNode this_ = this;
            do {
                ConstructChildPos(this_.RightChild, firstPosTemp, lastPosTemp, followpos);
                n = this_.LeftChild;
                this_ = n as ChoiceNode;
            } while (this_ != null);

            n.ConstructPos(firstpos, lastpos, followpos);
            firstpos.Or(firstPosTemp);
            lastpos.Or(lastPosTemp);
        }

        public override bool IsNullable {
            get {
                SyntaxTreeNode n;
                ChoiceNode this_ = this;
                do {
                    if (this_.RightChild.IsNullable)
                        return true;
                    n = this_.LeftChild;
                    this_ = n as ChoiceNode;
                }
                while (this_ != null);
                return n.IsNullable;
            }
        }

        public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
 	        ExpandTreeNoRecursive(parent, symbols, positions);
        }

#if DEBUG
        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            Stack<ChoiceNode> nodeStack = new Stack<ChoiceNode>();
            ChoiceNode this_ = this;

            while (true) {
                bb.Append("(");
                if (this_.LeftChild is ChoiceNode) {
                    nodeStack.Push(this_);
                    this_ = (ChoiceNode)this_.LeftChild;
                    continue;
                }
                this_.LeftChild.Dump(bb, symbols, positions);

            ProcessRight:
                bb.Append(" | ");
                this_.RightChild.Dump(bb, symbols, positions);
                bb.Append(")");
                if (nodeStack.Count == 0)
                    break;

                this_ = nodeStack.Pop();
                goto ProcessRight;
            }
        }
#endif
    }
    
    sealed class PlusNode : InteriorNode {
        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            LeftChild.ConstructPos(firstpos, lastpos, followpos);
            for (int pos = lastpos.NextSet(-1); pos != -1; pos = lastpos.NextSet(pos)) {
                followpos[pos].Or(firstpos);
            }
        }
        
        public override bool IsNullable {
            get { return LeftChild.IsNullable; }
        }

#if DEBUG
        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            LeftChild.Dump(bb, symbols, positions);
            bb.Append("+");
        }
#endif
    }
    
    sealed class QmarkNode : InteriorNode {
        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            LeftChild.ConstructPos(firstpos, lastpos, followpos);
        }

        public override bool IsNullable {
            get { return true; }
        }

#if DEBUG
        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            LeftChild.Dump(bb, symbols, positions);
            bb.Append("?");
        }
#endif
    }

    sealed class StarNode : InteriorNode {
        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            LeftChild.ConstructPos(firstpos, lastpos, followpos);
            for (int pos = lastpos.NextSet(-1); pos != -1; pos = lastpos.NextSet(pos)) {
                followpos[pos].Or(firstpos);
            }
        }
        
        public override bool IsNullable {
            get { return true; }
        }

#if DEBUG
        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            LeftChild.Dump(bb, symbols, positions);
            bb.Append("*");
        }
#endif
    }

#if EXPANDRANGE
    /// <summary>
    /// Temporary node to occurance range. Will be expended to a sequence of terminals
    /// </summary>
    sealed class RangeNode : InteriorNode {
        int min;
        int max;
        
        public RangeNode(int min, int max) {
            this.min = min;
            this.max = max;
        }

        public int Max {
            get { return max;}
        }

        public int Min {
            get { return min;}
        }

        public override SyntaxTreeNode Clone(Positions positions) {
            // range nodes have to be removed prior to that
            throw new InvalidOperationException();
        }

        /// <summary>
        /// Expand tree will replace a{min, max} using following algorithm. Bare in mind that this sequence will have at least two leaves
        /// if min == 0 (max cannot be unbounded)
        ///         a?, ...  a?
        ///         \__     __/
        ///             max
        /// else
        ///     if max == unbounded
        ///         a,  ...   a, a*
        ///         \__     __/
        ///             min
        ///     else
        ///         a,  ...   a, a?,   ...     a?
        ///         \__     __/  \__          __/
        ///             min          max - min
        /// </summary>
        public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
            LeftChild.ExpandTree(this, symbols, positions);
            SyntaxTreeNode replacementNode = null;
            if (min == 0) {
                Debug.Assert(max != int.MaxValue);
                replacementNode = NewQmark(LeftChild);
                for (int i = 0; i < max - 1; i ++) {
                    replacementNode = NewSequence(replacementNode, NewQmark(LeftChild.Clone(positions)));
                }
            }
            else {
                replacementNode = LeftChild;
                for (int i = 0; i < min - 1; i ++) {
                    replacementNode = NewSequence(replacementNode, LeftChild.Clone(positions));
                }
                if (max == int.MaxValue) {
                    replacementNode = NewSequence(replacementNode, NewStar(LeftChild.Clone(positions)));
                }
                else {
                    for (int i = 0; i < max - min; i ++) {
                        replacementNode = NewSequence(replacementNode, NewQmark(LeftChild.Clone(positions)));
                    }
                }
            }
            if (parent.LeftChild == this) {
                parent.LeftChild = replacementNode;
            }
            else {
                parent.RightChild = replacementNode;
            }
        }

        private SyntaxTreeNode NewSequence(SyntaxTreeNode leftChild, SyntaxTreeNode rightChild) {
            InteriorNode sequence = new SequenceNode();
            sequence.LeftChild = leftChild;
            sequence.RightChild = rightChild;
            return sequence;
        }

        private SyntaxTreeNode NewStar(SyntaxTreeNode leftChild) {
            InteriorNode star = new StarNode();
            star.LeftChild = leftChild;
            return star;
        }

        private SyntaxTreeNode NewQmark(SyntaxTreeNode leftChild) {
            InteriorNode qmark = new QmarkNode();
            qmark.LeftChild = leftChild;
            return qmark;
        }
        
        public override void ConstructPos(BitSet firstpos, BitSet lastpos, BitSet[] followpos) {
            throw new InvalidOperationException();
        }

        public override bool IsNullable {
            get { throw new InvalidOperationException(); }
        }

        public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions positions) {
            LeftChild.Dump(bb, symbols, positions);
            bb.Append("{" + Convert.ToString(min, NumberFormatInfo.InvariantInfo) + ", " + Convert.ToString(max, NumberFormatInfo.InvariantInfo) + "}");
        }

    }
#endif


    /// <summary>
    /// Using range node as one of the terminals
    /// </summary>
    sealed class LeafRangeNode : LeafNode {
        decimal min;
        decimal max;
        BitSet nextIteration;

        public LeafRangeNode(decimal min, decimal max) : this(-1, min, max) {}

        public LeafRangeNode(int pos, decimal min, decimal max) : base(pos) {
            this.min = min;
            this.max = max;
        }

        public decimal Max {
            get { return max;}
        }

        public decimal Min {
            get { return min;}
        }

        public BitSet NextIteration {
            get {
                return nextIteration;
            }
            set {
                nextIteration = value;
            }
        }

        public override SyntaxTreeNode Clone(Positions positions) {
            return new LeafRangeNode(this.Pos, this.min, this.max);
        }
        
        public override bool IsRangeNode { 
            get {
                return true;
            }
        }

        public override void ExpandTree(InteriorNode parent, SymbolsDictionary symbols, Positions positions) {
            Debug.Assert(parent is SequenceNode);
            Debug.Assert(this == parent.RightChild);
            //change the range node min to zero if left is nullable
            if (parent.LeftChild.IsNullable) {
                min = 0;
            }
        }
    }

    #endregion

    #region ContentValidator
    /// <summary>
    /// Basic ContentValidator
    /// </summary>
    class ContentValidator {
        XmlSchemaContentType contentType;
        bool isOpen;  //For XDR Content Models or ANY
        bool isEmptiable;

        public static readonly ContentValidator Empty = new ContentValidator(XmlSchemaContentType.Empty);
        public static readonly ContentValidator TextOnly = new ContentValidator(XmlSchemaContentType.TextOnly, false, false);
        public static readonly ContentValidator Mixed = new ContentValidator(XmlSchemaContentType.Mixed);
        public static readonly ContentValidator Any = new ContentValidator(XmlSchemaContentType.Mixed, true, true);

        public ContentValidator(XmlSchemaContentType contentType) {
            this.contentType = contentType;
            this.isEmptiable = true;
        }
        
        protected ContentValidator(XmlSchemaContentType contentType, bool isOpen, bool isEmptiable) {
            this.contentType = contentType;
            this.isOpen = isOpen;
            this.isEmptiable = isEmptiable;
        }
        
        public XmlSchemaContentType ContentType { 
            get { return contentType; }
        }

        public bool PreserveWhitespace {
            get { return contentType == XmlSchemaContentType.TextOnly || contentType == XmlSchemaContentType.Mixed; }
        }

        public virtual bool IsEmptiable { 
            get { return isEmptiable; }
        }
        
        public bool IsOpen {
            get { 
                if (contentType == XmlSchemaContentType.TextOnly || contentType == XmlSchemaContentType.Empty)
                    return false;
                else
                    return isOpen; 
            }
            set { isOpen = value; }
        }

        public virtual void InitValidation(ValidationState context) {
            // do nothin'
        }

        public virtual object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
            if (contentType == XmlSchemaContentType.TextOnly || contentType == XmlSchemaContentType.Empty) { //Cannot have elements in TextOnly or Empty content
                context.NeedValidateChildren = false;
            }
            errorCode = -1;
            return null;
        }

        public virtual bool CompleteValidation(ValidationState context) {
            return true;
        }

        public virtual ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
            return null;
        }

        public virtual ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
            return null;
        }

        public static void AddParticleToExpected(XmlSchemaParticle p, XmlSchemaSet schemaSet, ArrayList particles) {
            AddParticleToExpected(p, schemaSet, particles, false);
        }

        public static void AddParticleToExpected(XmlSchemaParticle p, XmlSchemaSet schemaSet, ArrayList particles, bool global) {
            if (!particles.Contains(p)) {
                particles.Add(p);
            }
            //Only then it can be head of substitutionGrp, if it is, add its members 
            XmlSchemaElement elem = p as XmlSchemaElement;
            if (elem != null && (global ||!elem.RefName.IsEmpty)) { 
				XmlSchemaObjectTable substitutionGroups = schemaSet.SubstitutionGroups;
                XmlSchemaSubstitutionGroup grp = (XmlSchemaSubstitutionGroup)substitutionGroups[elem.QualifiedName];
                if (grp != null) {
                    //Grp members wil contain the head as well, so filter head as we added it already
                    for (int i = 0; i < grp.Members.Count; ++i) {
                        XmlSchemaElement member = (XmlSchemaElement)grp.Members[i];
                        if (!elem.QualifiedName.Equals(member.QualifiedName) && !particles.Contains(member)) { //A member might have been directly present as an element in the content model
                            particles.Add(member);
                        }
                    }
                }
            }
        }
    }

    sealed class ParticleContentValidator : ContentValidator {
        SymbolsDictionary symbols;          
        Positions positions;                
        Stack stack;                        // parsing context
        SyntaxTreeNode contentNode;         // content model points to syntax tree
        bool isPartial;                     // whether the closure applies to partial or the whole node that is on top of the stack
        int minMaxNodesCount;
        bool enableUpaCheck;

        public ParticleContentValidator(XmlSchemaContentType contentType) : this(contentType, true) {
        }

        public ParticleContentValidator(XmlSchemaContentType contentType, bool enableUpaCheck) : base(contentType) {
            this.enableUpaCheck = enableUpaCheck;
        }

        public override void InitValidation(ValidationState context) {
            // ParticleContentValidator cannot be used during validation
            throw new InvalidOperationException();
        }

        public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
            // ParticleContentValidator cannot be used during validation
            throw new InvalidOperationException();
        }

        public override bool CompleteValidation(ValidationState context) {
            // ParticleContentValidator cannot be used during validation
            throw new InvalidOperationException();
        }

        public void Start() {
            symbols = new SymbolsDictionary();
            positions = new Positions();
            stack = new Stack();
        }

        public void OpenGroup() {
            stack.Push(null);
        }

        public void CloseGroup() {
            SyntaxTreeNode node = (SyntaxTreeNode)stack.Pop();
            if (node == null) {
                return;
            }
            if (stack.Count == 0) {
                contentNode = node;
                isPartial = false;
            }
            else {
                // some collapsing to do...
                InteriorNode inNode = (InteriorNode)stack.Pop();
                if (inNode != null) {
                    inNode.RightChild = node;
                    node = inNode;
                    isPartial = true;
                }
                else {
                    isPartial = false;
                }
                stack.Push(node);
            }
        }
        
        public bool Exists(XmlQualifiedName name) {
            if (symbols.Exists(name)) {
                return true;
            }
            return false;
        }

        public void AddName(XmlQualifiedName name, object particle) {
            AddLeafNode(new LeafNode(positions.Add(symbols.AddName(name, particle), particle)));
        }
        
        public void AddNamespaceList(NamespaceList namespaceList, object particle) {
            symbols.AddNamespaceList(namespaceList, particle, false);
            AddLeafNode(new NamespaceListNode(namespaceList, particle));
        }

        private void AddLeafNode(SyntaxTreeNode node) {
            if (stack.Count > 0) {
                InteriorNode inNode = (InteriorNode)stack.Pop();
                if (inNode != null) {
                    inNode.RightChild = node;
                    node = inNode;
                }
            }
            stack.Push( node );
            isPartial = true;
        }

        public void AddChoice() {
            SyntaxTreeNode node = (SyntaxTreeNode)stack.Pop();
            InteriorNode choice = new ChoiceNode();
            choice.LeftChild = node;
            stack.Push(choice);
        }

        public void AddSequence() {
            SyntaxTreeNode node = (SyntaxTreeNode)stack.Pop();
            InteriorNode sequence = new SequenceNode();
            sequence.LeftChild = node;
            stack.Push(sequence);
        }

        public void AddStar() {
            Closure(new StarNode());
        }

        public void AddPlus() {
            Closure(new PlusNode());
        }

        public void AddQMark() {
            Closure(new QmarkNode());
        }

        public void AddLeafRange(decimal min, decimal max) {
            LeafRangeNode rNode = new LeafRangeNode(min, max);
            int pos = positions.Add(-2, rNode);
            rNode.Pos = pos;

            InteriorNode sequence = new SequenceNode();
            sequence.RightChild = rNode;
            Closure(sequence);
            minMaxNodesCount++;
        }

#if EXPANDRANGE
        public void AddRange(int min, int max) {
            Closure(new RangeNode(min, max));
        }
#endif
        private void Closure(InteriorNode node) {
            if (stack.Count > 0) {
                SyntaxTreeNode topNode = (SyntaxTreeNode)stack.Pop();
                InteriorNode inNode = topNode as InteriorNode;
                if (isPartial && inNode != null) {
                    // need to reach in and wrap right hand side of element.
                    // and n remains the same.
                    node.LeftChild = inNode.RightChild;
                    inNode.RightChild = node;
                }
                else {
                    // wrap terminal or any node
                    node.LeftChild = topNode;
                    topNode = node;
                }
                stack.Push(topNode);
            }
            else if (contentNode != null) { //If there is content to wrap
                // wrap whole content
                node.LeftChild = contentNode;
                contentNode = node;
            }
        }

        public ContentValidator Finish() {
            return Finish(true);
        }

        public ContentValidator Finish(bool useDFA) {
            Debug.Assert(ContentType == XmlSchemaContentType.ElementOnly || ContentType == XmlSchemaContentType.Mixed);
            if (contentNode == null) {
                if (ContentType == XmlSchemaContentType.Mixed) {
                    string ctype = IsOpen ? "Any" : "TextOnly";
                    Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled,  "\t\t\tContentType:  " + ctype);
                    return IsOpen ? ContentValidator.Any : ContentValidator.TextOnly;
                }
                else {
                    Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled,  "\t\t\tContent:   EMPTY");
                    Debug.Assert(!IsOpen);
                    return ContentValidator.Empty;
                }
            }

            #if DEBUG
            StringBuilder bb = new StringBuilder();
            contentNode.Dump(bb, symbols, positions);
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled,  "\t\t\tContent :   " + bb.ToString());
            #endif

            // Add end marker
            InteriorNode contentRoot = new SequenceNode();
            contentRoot.LeftChild = contentNode;
            LeafNode endMarker = new LeafNode(positions.Add(symbols.AddName(XmlQualifiedName.Empty, null), null));
            contentRoot.RightChild = endMarker;

            // Eliminate NamespaceListNode(s) and RangeNode(s)
            contentNode.ExpandTree(contentRoot, symbols, positions);

            #if DEBUG
            bb = new StringBuilder();
            contentRoot.LeftChild.Dump(bb, symbols, positions);
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled,  "\t\t\tExpended:   " + bb.ToString());
            #endif

            // calculate followpos
            int symbolsCount = symbols.Count;
            int positionsCount = positions.Count;
            BitSet firstpos = new BitSet(positionsCount);
            BitSet lastpos = new BitSet(positionsCount);
            BitSet[] followpos = new BitSet[positionsCount];
            for (int i = 0; i < positionsCount; i++) {
                followpos[i] = new BitSet(positionsCount);
            }
            contentRoot.ConstructPos(firstpos, lastpos, followpos);
#if DEBUG
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "firstpos, lastpos, followpos");
            SequenceNode.WritePos(firstpos, lastpos, followpos);
#endif
            if (minMaxNodesCount > 0) { //If the tree has any terminal range nodes
                BitSet positionsWithRangeTerminals;
                BitSet[] minMaxFollowPos = CalculateTotalFollowposForRangeNodes(firstpos, followpos, out positionsWithRangeTerminals);
                
                if (enableUpaCheck) {
                    CheckCMUPAWithLeafRangeNodes(GetApplicableMinMaxFollowPos(firstpos, positionsWithRangeTerminals, minMaxFollowPos));
                    for (int i = 0; i < positionsCount; i++) {
                        CheckCMUPAWithLeafRangeNodes(GetApplicableMinMaxFollowPos(followpos[i], positionsWithRangeTerminals, minMaxFollowPos));
                    }
                }
                return new RangeContentValidator(firstpos, followpos, symbols, positions, endMarker.Pos, this.ContentType, contentRoot.LeftChild.IsNullable, positionsWithRangeTerminals, minMaxNodesCount); 
            }
            else {
                int[][] transitionTable = null;
                // if each symbol has unique particle we are golden
                if (!symbols.IsUpaEnforced) {
                    if (enableUpaCheck) {
                        // multiple positions that match the same symbol have different particles, but they never follow the same position
                        CheckUniqueParticleAttribution(firstpos, followpos);
                    }
                }
                else if (useDFA) {
                    // Can return null if the number of states reaches higher than 8192 / positionsCount
                    transitionTable = BuildTransitionTable(firstpos, followpos, endMarker.Pos); 
                }
                #if DEBUG
                bb = new StringBuilder();
                Dump(bb, followpos, transitionTable);    
                Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, bb.ToString());
                #endif
                if (transitionTable != null) {
                    return new DfaContentValidator(transitionTable, symbols,this.ContentType, this.IsOpen, contentRoot.LeftChild.IsNullable);
                } else {
                    return new NfaContentValidator(firstpos, followpos, symbols, positions, endMarker.Pos, this.ContentType, this.IsOpen, contentRoot.LeftChild.IsNullable);
                }
            }
        }

        private BitSet[] CalculateTotalFollowposForRangeNodes(BitSet firstpos, BitSet[] followpos, out BitSet posWithRangeTerminals) {
            int positionsCount = positions.Count; //terminals
            posWithRangeTerminals = new BitSet(positionsCount);
            
            //Compute followpos for each range node
            //For any range node that is surrounded by an outer range node, its follow positions will include those of the outer range node
            BitSet[] minmaxFollowPos = new BitSet[minMaxNodesCount];
            int localMinMaxNodesCount = 0;
            
            for (int i = positionsCount - 1;  i >= 0; i--) { 
                Position p = positions[i];
                if (p.symbol == -2) { //P is a LeafRangeNode
                    LeafRangeNode lrNode = p.particle as LeafRangeNode;
                    Debug.Assert(lrNode != null);
                    BitSet tempFollowPos = new BitSet(positionsCount);
                    tempFollowPos.Clear();
                    tempFollowPos.Or(followpos[i]); //Add the followpos of the range node
                    if (lrNode.Min != lrNode.Max) { //If they are the same, then followpos cannot include the firstpos
                        tempFollowPos.Or(lrNode.NextIteration); //Add the nextIteration of the range node (this is the firstpos of its parent's leftChild)
                    }

                    //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
                    for (int pos = tempFollowPos.NextSet(-1); pos != -1; pos = tempFollowPos.NextSet(pos)) {
                        if (pos > i) {
                            Position p1 = positions[pos];
                            if (p1.symbol == -2) {
                                LeafRangeNode lrNode1 = p1.particle as LeafRangeNode;
                                Debug.Assert(lrNode1 != null);
                                tempFollowPos.Or(minmaxFollowPos[lrNode1.Pos]);
                            }
                        }
                    }
                    //set the followpos built to the index in the BitSet[]
                    minmaxFollowPos[localMinMaxNodesCount] = tempFollowPos; 
                    lrNode.Pos = localMinMaxNodesCount++; 
                    posWithRangeTerminals.Set(i);
                }
            }
            return minmaxFollowPos;
        }

        private void CheckCMUPAWithLeafRangeNodes(BitSet curpos) {
            object[] symbolMatches = new object[symbols.Count];
            for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
                Position currentPosition = positions[pos];
                int symbol = currentPosition.symbol;
                if (symbol >= 0) { //its not a range position
                    if (symbolMatches[symbol] != null) {
                        throw new UpaException(symbolMatches[symbol], currentPosition.particle);
                    }
                    else {
                        symbolMatches[symbol] = currentPosition.particle;
                    }
                }
            }
        }

        //For each position, this method calculates the additional follows of any range nodes that need to be added to its followpos
        //((ab?)2-4)c, Followpos of a is b as well as that of node R(2-4) = c
        private BitSet GetApplicableMinMaxFollowPos(BitSet curpos, BitSet posWithRangeTerminals, BitSet[] minmaxFollowPos) {
            if (curpos.Intersects(posWithRangeTerminals)) {
                BitSet newSet = new BitSet(positions.Count); //Doing work again 
                newSet.Or(curpos);
                newSet.And(posWithRangeTerminals);
                curpos = curpos.Clone();
                for (int pos = newSet.NextSet(-1); pos != -1; pos = newSet.NextSet(pos)) {
                    LeafRangeNode lrNode = positions[pos].particle as LeafRangeNode;
                    curpos.Or(minmaxFollowPos[lrNode.Pos]);
                }
            }
            return curpos;
        }

        private void CheckUniqueParticleAttribution(BitSet firstpos, BitSet[] followpos) {
            CheckUniqueParticleAttribution(firstpos);
            for (int i = 0; i < positions.Count; i++) {
                CheckUniqueParticleAttribution(followpos[i]);
            }
        }

        private void CheckUniqueParticleAttribution(BitSet curpos) {
            // particles will be attributed uniquely if the same symbol never poins to two different ones
            object[] particles = new object[symbols.Count]; 
            for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
                // if position can follow
                int symbol = positions[pos].symbol;
                if (particles[symbol] == null) {
                    // set particle for the symbol
                    particles[symbol] = positions[pos].particle;
                }
                else if (particles[symbol] != positions[pos].particle) {
                    throw new UpaException(particles[symbol], positions[pos].particle);
                }
                // two different position point to the same symbol and particle - that's OK
            }
        }

        /// <summary>
        /// Algorithm 3.5 Construction of a DFA from a regular expression
        /// </summary>
        private int[][] BuildTransitionTable(BitSet firstpos, BitSet[] followpos, int endMarkerPos) {
            const int TimeConstant = 8192; //(MaxStates * MaxPositions should be a constant) 
            int positionsCount = positions.Count;
            int MaxStatesCount = TimeConstant / positionsCount;
            int symbolsCount = symbols.Count;
            
            // transition table (Dtran in the book)
            ArrayList transitionTable = new ArrayList();
            
            // state lookup table (Dstate in the book)
            Hashtable stateTable = new Hashtable();
            
            // Add empty set that would signal an error
            stateTable.Add(new BitSet(positionsCount), -1);

            // lists unmarked states
            Queue unmarked = new Queue();

            // initially, the only unmarked state in Dstates is firstpo(root) 
            int state = 0;
            unmarked.Enqueue(firstpos);
            stateTable.Add(firstpos, 0);
            transitionTable.Add(new int[symbolsCount + 1]);

            // while there is an umnarked state T in Dstates do begin
            while (unmarked.Count > 0) {
                BitSet statePosSet = (BitSet)unmarked.Dequeue(); // all positions that constitute DFA state 
                Debug.Assert(state == (int)stateTable[statePosSet]); // just make sure that statePosSet is for correct state
                int[] transition = (int[])transitionTable[state];
                if (statePosSet[endMarkerPos]) {
                    transition[symbolsCount] = 1;   // accepting
                }

                // for each input symbol a do begin
                for (int symbol = 0; symbol < symbolsCount; symbol ++) {
                    // let U be the set of positions that are in followpos(p)
                    //       for some position p in T
                    //       such that the symbol at position p is a
                    BitSet newset = new BitSet(positionsCount);
                    for (int pos = statePosSet.NextSet(-1); pos != -1; pos = statePosSet.NextSet(pos)) {
                        if (symbol == positions[pos].symbol) {
                            newset.Or(followpos[pos]);
                        }
                    }

                    // if U is not empty and is not in Dstates then
                    //      add U as an unmarked state to Dstates
                    object lookup = stateTable[newset];
                    if (lookup != null) {
                        transition[symbol] = (int)lookup;
                    }
                    else {
                        // construct new state
                        int newState = stateTable.Count - 1;
                        if (newState >= MaxStatesCount) {
                            return null;
                        }
                        unmarked.Enqueue(newset);
                        stateTable.Add(newset, newState);
                        transitionTable.Add(new int[symbolsCount + 1]);
                        transition[symbol] = newState;
                    }
                }
                state++;
            }
            // now convert transition table to array
            return (int[][])transitionTable.ToArray(typeof(int[]));
        }

#if DEBUG
        private void Dump(StringBuilder bb, BitSet[] followpos, int[][] transitionTable) {
            // Temporary printout
            bb.AppendLine("Positions");
            for (int i = 0; i < positions.Count; i ++) {
                bb.AppendLine(i + " " + positions[i].symbol.ToString(NumberFormatInfo.InvariantInfo) + " " + symbols.NameOf(positions[i].symbol));
            }
            bb.AppendLine("Followpos");
            for (int i = 0; i < positions.Count; i++) {
                for (int j = 0; j < positions.Count; j++) {
                    bb.Append(followpos[i][j] ? "X" : "O");
                }
               bb.AppendLine();
            }
            if (transitionTable != null) {
                // Temporary printout
                bb.AppendLine("Transitions");
                for (int i = 0; i < transitionTable.Length; i++) {
                    for (int j = 0; j < symbols.Count; j++) {
                        if (transitionTable[i][j] == -1) {
                            bb.Append("  x  ");
                        }
                        else {
                            bb.AppendFormat(" {0:000} ", transitionTable[i][j]);
                        }
                    }
                    bb.AppendLine(transitionTable[i][symbols.Count] == 1 ? "+" : "");
                }
            }
        }
#endif
    }

    /// <summary>
    /// Deterministic Finite Automata
    /// Compilers by Aho, Sethi, Ullman.
    /// ISBN 0-201-10088-6, pp. 115, 116, 140 
    /// </summary>
    sealed class DfaContentValidator : ContentValidator {
        int[][] transitionTable;
        SymbolsDictionary symbols;

        /// <summary>
        /// Algorithm 3.5 Construction of a DFA from a regular expression
        /// </summary>
        internal DfaContentValidator(
            int[][] transitionTable, SymbolsDictionary symbols,
            XmlSchemaContentType contentType, bool isOpen, bool isEmptiable)  : base(contentType, isOpen, isEmptiable) {
            this.transitionTable = transitionTable;
            this.symbols = symbols;
        }

        public override void InitValidation(ValidationState context) {
            context.CurrentState.State = 0;
            context.HasMatched = transitionTable[0][symbols.Count] > 0;
        }

        /// <summary>
        /// Algorithm 3.1 Simulating a DFA
        /// </summary>
        public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
            int symbol = symbols[name];
            int state = transitionTable[context.CurrentState.State][symbol];
            errorCode = 0;
            if (state != -1) {
                context.CurrentState.State = state;
                context.HasMatched = transitionTable[context.CurrentState.State][symbols.Count] > 0;
                return symbols.GetParticle(symbol); // OK
            }
            if (IsOpen && context.HasMatched) {
                // XDR allows any well-formed contents after matched.
                return null;
            }
            context.NeedValidateChildren = false;
            errorCode = -1;
            return null; // will never be here
        }

        public override bool CompleteValidation(ValidationState context) {
            if (!context.HasMatched) {
                return false;
            }
            return true;
        }

        public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
            ArrayList names = null;
            int[] transition = transitionTable[context.CurrentState.State];
            if (transition != null) {
                for (int i = 0; i < transition.Length - 1; i ++) {
                    if (transition[i] != -1) {
                        if (names == null) {
                            names = new ArrayList();
                        }
                        XmlSchemaParticle p = (XmlSchemaParticle)symbols.GetParticle(i);
                        if (p == null) {
                            string s = symbols.NameOf(i);
                            if (s.Length != 0) {
                                names.Add(s);
                            }
                        }
                        else {
                            string s = p.NameString;
                            if (!names.Contains(s)) {
                                names.Add(s);
                            }
                        }
                    }
                }
            }
            return names;
        }

        public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
            ArrayList particles = new ArrayList();
            int[] transition = transitionTable[context.CurrentState.State];
            if (transition != null) {
                for (int i = 0; i < transition.Length - 1; i ++) {
                    if (transition[i] != -1) {
                        XmlSchemaParticle p = (XmlSchemaParticle)symbols.GetParticle(i);
                        if (p == null) {
                            continue;
                        }
                        AddParticleToExpected(p, schemaSet, particles);
                    }
                }
            }
            return particles;
        }        
    }

    /// <summary>
    /// Nondeterministic Finite Automata
    /// Compilers by Aho, Sethi, Ullman.
    /// ISBN 0-201-10088-6, pp. 126,140 
    /// </summary>
    sealed class NfaContentValidator : ContentValidator {
        BitSet firstpos;
        BitSet[] followpos;
        SymbolsDictionary symbols;
        Positions positions;
        int endMarkerPos;

        internal NfaContentValidator(
            BitSet firstpos, BitSet[] followpos, SymbolsDictionary symbols, Positions positions, int endMarkerPos,
            XmlSchemaContentType contentType, bool isOpen, bool isEmptiable)  : base(contentType, isOpen, isEmptiable) {
            this.firstpos = firstpos;
            this.followpos = followpos;
            this.symbols = symbols;
            this.positions = positions;
            this.endMarkerPos = endMarkerPos;
        }

        public override void InitValidation(ValidationState context) {
            context.CurPos[0] = firstpos.Clone();
            context.CurPos[1] = new BitSet(firstpos.Count);
            context.CurrentState.CurPosIndex = 0;
        }

        /// <summary>
        /// Algorithm 3.4 Simulation of an NFA
        /// </summary>
        public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
            BitSet curpos = context.CurPos[context.CurrentState.CurPosIndex];
            int next = (context.CurrentState.CurPosIndex + 1) % 2;
            BitSet nextpos = context.CurPos[next];
            nextpos.Clear();
            int symbol = symbols[name];
            object particle = null;
            errorCode = 0;
            for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
                // if position can follow
                if (symbol == positions[pos].symbol) {
                    nextpos.Or(followpos[pos]);
                    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
                    break;                              // and then ExpandTree for the namespace nodes which adds the wildcards to the positions list
                }
            }
            if (!nextpos.IsEmpty) {
                context.CurrentState.CurPosIndex = next;
                return particle;
            }
            if (IsOpen && curpos[endMarkerPos]) {
                // XDR allows any well-formed contents after matched.
                return null;
            }
            context.NeedValidateChildren = false;
            errorCode = -1;
            return null; // will never be here

        }

#if FINDUPA_PARTICLE
        private bool FindUPAParticle(ref object originalParticle, object newParticle) {
            if (originalParticle == null) { 
                originalParticle = newParticle;
                if (originalParticle is XmlSchemaElement) { //if the first particle is element, then break, otherwise try to find an element
                    return true;
                }
            }
            else if (newParticle is XmlSchemaElement) {
                if (originalParticle is XmlSchemaAny) { //Weak wildcards, element takes precendence over any
                    originalParticle = newParticle;
                    return true;
                }
            }
            return false;
        }
#endif

        public override bool CompleteValidation(ValidationState context) {
            if (!context.CurPos[context.CurrentState.CurPosIndex][endMarkerPos]) {
                return false;
            }
            return true;
        }

        public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
            ArrayList names = null;
            BitSet curpos = context.CurPos[context.CurrentState.CurPosIndex];
            for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
                if (names == null) {
                    names = new ArrayList();
                }
                XmlSchemaParticle p = (XmlSchemaParticle)positions[pos].particle;
                if (p == null) {
                    string s = symbols.NameOf(positions[pos].symbol);
                    if (s.Length != 0) {
                        names.Add(s);
                    }
                }
                else {
                    string s = p.NameString;
                    if (!names.Contains(s)) {
                        names.Add(s);
                    }
                }
            }
            return names;
        }

        public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
            ArrayList particles = new ArrayList();
            BitSet curpos = context.CurPos[context.CurrentState.CurPosIndex];
            for (int pos = curpos.NextSet(-1); pos != -1; pos = curpos.NextSet(pos)) {
                XmlSchemaParticle p = (XmlSchemaParticle)positions[pos].particle;
                if (p == null) {
                    continue;
                }
                else {
                    AddParticleToExpected(p, schemaSet, particles);
                }
            }
            return particles;
        }
    }

    internal struct RangePositionInfo {
        public BitSet curpos;            
        public decimal[] rangeCounters;
    }
    
    sealed class RangeContentValidator : ContentValidator {
        BitSet firstpos;
        BitSet[] followpos;
        BitSet positionsWithRangeTerminals;
        SymbolsDictionary symbols;
        Positions positions;
        int minMaxNodesCount;    
        int endMarkerPos;

        internal RangeContentValidator(
            BitSet firstpos, BitSet[] followpos, SymbolsDictionary symbols, Positions positions, int endMarkerPos, XmlSchemaContentType contentType, bool isEmptiable, BitSet positionsWithRangeTerminals, int minmaxNodesCount)  : base(contentType, false, isEmptiable) {
            this.firstpos = firstpos;
            this.followpos = followpos;
            this.symbols = symbols;
            this.positions = positions;
            this.positionsWithRangeTerminals = positionsWithRangeTerminals;
            this.minMaxNodesCount = minmaxNodesCount;
            this.endMarkerPos = endMarkerPos;
        }

        public override void InitValidation(ValidationState context) {
            int positionsCount = positions.Count;
            List<RangePositionInfo> runningPositions = context.RunningPositions;
            if (runningPositions != null) {
                Debug.Assert(minMaxNodesCount != 0);
                runningPositions.Clear();
            }
            else {
                runningPositions = new List<RangePositionInfo>();
                context.RunningPositions = runningPositions;
            }
            RangePositionInfo rposInfo = new RangePositionInfo();
            rposInfo.curpos = firstpos.Clone();

            rposInfo.rangeCounters = new decimal[minMaxNodesCount];
            runningPositions.Add(rposInfo);
            context.CurrentState.NumberOfRunningPos = 1;
            context.HasMatched = rposInfo.curpos.Get(endMarkerPos);
        }

        public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
            errorCode = 0;
            int symbol = symbols[name];
            bool hasSeenFinalPosition = false;
            List<RangePositionInfo> runningPositions = context.RunningPositions;
            int matchCount = context.CurrentState.NumberOfRunningPos;
            int k = 0; 
            RangePositionInfo rposInfo;
            
            int pos = -1;
            int firstMatchedIndex = -1;
            bool matched = false;

#if RANGE_DEBUG
            WriteRunningPositions("Current running positions to match", runningPositions, name, matchCount);
#endif

            while (k < matchCount) { //we are looking for the first match in the list of bitsets
                rposInfo = runningPositions[k];
                BitSet curpos = rposInfo.curpos;
                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
                    if (symbol == positions[matchpos].symbol) {
                        pos = matchpos; 
                        if (firstMatchedIndex == -1) { // get the first match for this symbol
                            firstMatchedIndex = k;
                        }
                        matched = true;
                        break;
                    }
                }
                if (matched && positions[pos].particle is XmlSchemaElement) { //We found a match in the list, break at that bitset
                    break;
                }
                else {
                    k++;
                }
            }

            if (k == matchCount && pos != -1) { // we did find a match but that was any and hence continued ahead for element
                k = firstMatchedIndex;
            }
            if (k < matchCount) { //There is a match
                if (k != 0) { //If the first bitset itself matched, then no need to remove anything
#if RANGE_DEBUG
                    WriteRunningPositions("Removing unmatched entries till the first match", runningPositions, XmlQualifiedName.Empty, k);
#endif
                    runningPositions.RemoveRange(0, k); //Delete entries from 0 to k-1
                }
                matchCount = matchCount - k;
                k = 0; // Since we re-sized the array
                while (k < matchCount) {
                    rposInfo = runningPositions[k];
                    matched = rposInfo.curpos.Get(pos); //Look for the bitset that matches the same position as pos
                    if (matched) { //If match found, get the follow positions of the current matched position
#if RANGE_DEBUG
                        Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "Matched position: " + pos + " "); SequenceNode.WriteBitSet(rposInfo.curpos);
                        Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "Follow pos of Matched position: "); SequenceNode.WriteBitSet(followpos[pos]);
#endif
                        rposInfo.curpos = followpos[pos]; //Note that we are copying the same counters of the current position to that of the follow position
                        runningPositions[k] = rposInfo; 
                        k++;
                    }
                    else { //Clear the current pos and get another position from the list to start matching
                        matchCount--;
                        if (matchCount > 0) {
                            RangePositionInfo lastrpos = runningPositions[matchCount];
                            runningPositions[matchCount] = runningPositions[k];
                            runningPositions[k] = lastrpos;
                        }
                    }
                }
            }
            else { //There is no match
                matchCount = 0;
            }

            if (matchCount > 0) {
                Debug.Assert(minMaxNodesCount > 0);
                if (matchCount >= 10000) {
                    context.TooComplex = true;
                    matchCount /= 2;
                }
#if RANGE_DEBUG
                WriteRunningPositions("Matched positions to expand ", runningPositions, name, matchCount);
#endif

                for (k = matchCount - 1; k >= 0; k--) { 
                    int j = k;
                    BitSet currentRunningPosition = runningPositions[k].curpos;
                    hasSeenFinalPosition = hasSeenFinalPosition || currentRunningPosition.Get(endMarkerPos); //Accepting position reached if the current position BitSet contains the endPosition
                    while (matchCount < 10000 && currentRunningPosition.Intersects(positionsWithRangeTerminals)) {
                        //Now might add 2 more positions to followpos 
                        //1. nextIteration of the rangeNode, which is firstpos of its parent's leftChild
                        //2. Followpos of the range node

                        BitSet countingPosition = currentRunningPosition.Clone();
                        countingPosition.And(positionsWithRangeTerminals);
                        int cPos = countingPosition.NextSet(-1); //Get the first position where leaf range node appears
                        LeafRangeNode lrNode = positions[cPos].particle as LeafRangeNode; //For a position with leaf range node, the particle is the node itself
                        Debug.Assert(lrNode != null);

                        rposInfo = runningPositions[j];
                        if (matchCount + 2 >= runningPositions.Count) {
                            runningPositions.Add(new RangePositionInfo());
                            runningPositions.Add(new RangePositionInfo());
                        }
                        RangePositionInfo newRPosInfo = runningPositions[matchCount];
                        if (newRPosInfo.rangeCounters == null) {
                            newRPosInfo.rangeCounters = new decimal[minMaxNodesCount];
                        }
                        Array.Copy(rposInfo.rangeCounters, 0, newRPosInfo.rangeCounters, 0, rposInfo.rangeCounters.Length);
                        decimal count = ++newRPosInfo.rangeCounters[lrNode.Pos];

                        if (count == lrNode.Max) {
                            newRPosInfo.curpos = followpos[cPos]; //since max has been reached, Get followposition of range node
                            newRPosInfo.rangeCounters[lrNode.Pos] = 0; //reset counter
                            runningPositions[matchCount] = newRPosInfo;
                            j = matchCount++;
                        }
                        else if (count < lrNode.Min) {
                            newRPosInfo.curpos = lrNode.NextIteration;
                            runningPositions[matchCount] = newRPosInfo;
                            matchCount++;
                            break;
                        }
                        else { // min <= count < max
                            newRPosInfo.curpos = lrNode.NextIteration; //set currentpos to firstpos of node which has the range
                            runningPositions[matchCount] = newRPosInfo;
                            j = matchCount + 1;
                            newRPosInfo = runningPositions[j];
                            if (newRPosInfo.rangeCounters == null) {
                                newRPosInfo.rangeCounters = new decimal[minMaxNodesCount];
                            }
                            Array.Copy(rposInfo.rangeCounters, 0, newRPosInfo.rangeCounters, 0, rposInfo.rangeCounters.Length);
                            newRPosInfo.curpos = followpos[cPos];
                            newRPosInfo.rangeCounters[lrNode.Pos] = 0;
                            runningPositions[j] = newRPosInfo;
                            matchCount += 2;
                        }
                        currentRunningPosition = runningPositions[j].curpos;
                        hasSeenFinalPosition = hasSeenFinalPosition || currentRunningPosition.Get(endMarkerPos);
                    }
                }
                context.HasMatched = hasSeenFinalPosition;
                context.CurrentState.NumberOfRunningPos = matchCount;
                return positions[pos].particle;
            } //matchcount > 0
            errorCode = -1;
            context.NeedValidateChildren = false;
            return null;
        }

#if RANGE_DEBUG
        private void WriteRunningPositions(string text, List<RangePositionInfo> runningPositions, XmlQualifiedName name, int length) {
            int counter = 0;
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
            Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, text + name.Name);
            while (counter < length) {
                BitSet curpos = runningPositions[counter].curpos;
                SequenceNode.WriteBitSet(curpos);
                for(int rcnt = 0; rcnt < runningPositions[counter].rangeCounters.Length; rcnt++) {
                    Debug.WriteIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "RangeCounter[" + rcnt + "]" + runningPositions[counter].rangeCounters[rcnt] + " ");                    
                }
                Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
                Debug.WriteLineIf(DiagnosticsSwitches.XmlSchemaContentModel.Enabled, "");
                counter++;
            }
        }
#endif
        public override bool CompleteValidation(ValidationState context) {
            return context.HasMatched;
        }

        public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
            ArrayList names = null;
            BitSet expectedPos;
            if (context.RunningPositions != null) {
                List<RangePositionInfo> runningPositions = context.RunningPositions;
                expectedPos = new BitSet(positions.Count);
                for (int i = context.CurrentState.NumberOfRunningPos - 1; i >=0; i--) {
                    Debug.Assert(runningPositions[i].curpos != null);
                    expectedPos.Or(runningPositions[i].curpos);
                }
                for (int pos = expectedPos.NextSet(-1); pos != -1; pos = expectedPos.NextSet(pos)) {
                    if (names == null) {
                        names = new ArrayList();
                    }
                    int symbol = positions[pos].symbol;
                    if (symbol >= 0) { //non range nodes
                        XmlSchemaParticle p = positions[pos].particle as XmlSchemaParticle;
                        if (p == null) {
                            string s = symbols.NameOf(positions[pos].symbol);
                            if (s.Length != 0) {
                                names.Add(s);
                            }
                        }
                        else {
                            string s = p.NameString;
                            if (!names.Contains(s)) {
                                names.Add(s);
                            }
                        }
                    }
                }
            }
            return names;
        }

        public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
            ArrayList particles = new ArrayList();
            BitSet expectedPos;
            if (context.RunningPositions != null) {
                List<RangePositionInfo> runningPositions = context.RunningPositions;
                expectedPos = new BitSet(positions.Count);
                for (int i = context.CurrentState.NumberOfRunningPos - 1; i >=0; i--) { 
                    Debug.Assert(runningPositions[i].curpos != null);
                    expectedPos.Or(runningPositions[i].curpos);
                }
                for (int pos = expectedPos.NextSet(-1); pos != -1; pos = expectedPos.NextSet(pos)) {
                    int symbol = positions[pos].symbol;
                    if (symbol >= 0) { //non range nodes
                        XmlSchemaParticle p = positions[pos].particle as XmlSchemaParticle;
                        if (p == null) {
                           continue;
                        }
                        AddParticleToExpected(p, schemaSet, particles);
                    }
                }
            }
            return particles;
        }
    }

    sealed class AllElementsContentValidator : ContentValidator {
        Hashtable elements;     // unique terminal names to positions in Bitset mapping
        object[] particles;
        BitSet isRequired;      // required flags
        int countRequired = 0;

        public AllElementsContentValidator(XmlSchemaContentType contentType, int size, bool isEmptiable) : base(contentType, false, isEmptiable) {
            elements = new Hashtable(size);
            particles = new object[size];
            isRequired = new BitSet(size);
        }

        public bool AddElement(XmlQualifiedName name, object particle, bool isEmptiable) {
            if (elements[name] != null) {
                return false;
            }
            int i = elements.Count;
            elements.Add(name, i);
            particles[i] = particle;
            if (!isEmptiable) {
                isRequired.Set(i);
                countRequired ++;
            }
            return true;
        }

        public override bool IsEmptiable { 
            get { return base.IsEmptiable || countRequired == 0; }
        }

        public override void InitValidation(ValidationState context) {
            Debug.Assert(elements.Count > 0);
            context.AllElementsSet = new BitSet(elements.Count); //
            context.CurrentState.AllElementsRequired = -1; // no elements at all
        }

        public override object ValidateElement(XmlQualifiedName name, ValidationState context, out int errorCode) {
            object lookup = elements[name];
            errorCode = 0;
            if (lookup == null) {
                context.NeedValidateChildren = false;
                return null;
            }
            int index = (int)lookup;
            if (context.AllElementsSet[index]) {
                errorCode = -2;
                return null;
            }
            if (context.CurrentState.AllElementsRequired == -1) {
                context.CurrentState.AllElementsRequired = 0;
            }
            context.AllElementsSet.Set(index);
            if (isRequired[index]) {
                context.CurrentState.AllElementsRequired++;
            }
            return particles[index];
        }
 
        public override bool CompleteValidation(ValidationState context) {
            if (context.CurrentState.AllElementsRequired == countRequired || IsEmptiable && context.CurrentState.AllElementsRequired == -1) {
                return true;
            }
            return false;
        }

        public override ArrayList ExpectedElements(ValidationState context, bool isRequiredOnly) {
            ArrayList names = null;
            foreach (DictionaryEntry entry in elements) {
                if (!context.AllElementsSet[(int)entry.Value] && (!isRequiredOnly || isRequired[(int)entry.Value])) {
                    if (names == null) {
                        names = new ArrayList();
                    }
                    names.Add(entry.Key);
                }
            }
            return names;
        }

        public override ArrayList ExpectedParticles(ValidationState context, bool isRequiredOnly, XmlSchemaSet schemaSet) {
            ArrayList expectedParticles = new ArrayList();
            foreach (DictionaryEntry entry in elements) {
                if (!context.AllElementsSet[(int)entry.Value] && (!isRequiredOnly || isRequired[(int)entry.Value])) {
                    AddParticleToExpected(this.particles[(int)entry.Value] as XmlSchemaParticle, schemaSet, expectedParticles);
                }
            }
            return expectedParticles;
        }
    }
    #endregion
}
