XPathBuilder.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / XmlUtils / System / Xml / Xsl / XPath / XPathBuilder.cs / 2 / XPathBuilder.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization; 
using System.Xml.Schema;
using System.Xml.XPath;
using System.Xml.Xsl.Qil;
 
//#define StopMaskOptimisation
 
namespace System.Xml.Xsl.XPath { 
    using FunctionInfo  = XPathBuilder.FunctionInfo;
    using Res           = System.Xml.Utils.Res; 
    using T             = XmlQueryTypeFactory;

    internal class XPathBuilder : IXPathBuilder, IXPathEnvironment {
        private   XPathQilFactory   f; 
        private   IXPathEnvironment environment;
        private   bool              inTheBuild; 
 
        // Singleton nodes used as fixup markers
        protected QilNode           fixupCurrent, fixupPosition, fixupLast; 

        // Number of unresolved fixup nodes
        protected int               numFixupCurrent, numFixupPosition, numFixupLast;
        private   FixupVisitor      fixupVisitor; 

        /*  --------------------------------------------------------------------------- 
            IXPathEnvironment interface 
        */
        QilNode IFocus.GetCurrent()  { return GetCurrentNode    (); } 
        QilNode IFocus.GetPosition() { return GetCurrentPosition(); }
        QilNode IFocus.GetLast()     { return GetLastPosition   (); }

        XPathQilFactory IXPathEnvironment.Factory   { get { return f; } } 

        QilNode IXPathEnvironment.ResolveVariable(string prefix, string name) { 
            return Variable(prefix, name); 
        }
        QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList args, IFocus env) { 
            Debug.Fail("Must not be called");
            return null;
        }
        string  IXPathEnvironment.ResolvePrefix(string prefix) { 
            return environment.ResolvePrefix(prefix);
        } 
        //  --------------------------------------------------------------------------- 

        public XPathBuilder(IXPathEnvironment environment) { 
            this.environment = environment;
            this.f = this.environment.Factory;
            this.fixupCurrent   = f.Unknown(T.NodeNotRtf);
            this.fixupPosition  = f.Unknown(T.DoubleX); 
            this.fixupLast      = f.Unknown(T.DoubleX);
            this.fixupVisitor   = new FixupVisitor(f, fixupCurrent, fixupPosition, fixupLast); 
        } 

        public virtual void StartBuild() { 
            Debug.Assert(! inTheBuild, "XPathBuilder is busy!");
            inTheBuild = true;
            numFixupCurrent = numFixupPosition = numFixupLast = 0;
        } 

        public virtual QilNode EndBuild(QilNode result) { 
            if (result == null) { // special door to clean builder state in exception handlers 
                inTheBuild = false;
                return result; 
            }
            Debug.Assert(inTheBuild, "StartBuild() wasn't called");
            if (result.XmlType.MaybeMany && result.XmlType.IsNode && result.XmlType.IsNotRtf) {
                result = f.DocOrderDistinct(result); 
            }
            result = fixupVisitor.Fixup(result, /*environment:*/this.environment); 
            numFixupCurrent  -= fixupVisitor.numCurrent ; 
            numFixupPosition -= fixupVisitor.numPosition;
            numFixupLast     -= fixupVisitor.numLast    ; 

            // All these variables will be positive for "false() and (. = position() + last())"
            // since QilPatternFactory eliminates the right operand of 'and'
            Debug.Assert(numFixupCurrent  >= 0, "Context fixup error"); 
            Debug.Assert(numFixupPosition >= 0, "Context fixup error");
            Debug.Assert(numFixupLast     >= 0, "Context fixup error"); 
            inTheBuild = false; 
            return result;
        } 

        private QilNode GetCurrentNode    () { numFixupCurrent  ++; return fixupCurrent ; }
        private QilNode GetCurrentPosition() { numFixupPosition ++; return fixupPosition; }
        private QilNode GetLastPosition   () { numFixupLast     ++; return fixupLast    ; } 

        public virtual QilNode String(string value) { 
            return f.String(value); 
        }
 
        public virtual QilNode Number(double value) {
            return f.Double(value);
        }
 
        public virtual QilNode Operator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op != XPathOperator.Unknown); 
            switch (OperatorGroup[(int)op]) { 
            case XPathOperatorGroup.Logical    : return LogicalOperator   (op, left, right);
            case XPathOperatorGroup.Equality   : return EqualityOperator  (op, left, right); 
            case XPathOperatorGroup.Relational : return RelationalOperator(op, left, right);
            case XPathOperatorGroup.Arithmetic : return ArithmeticOperator(op, left, right);
            case XPathOperatorGroup.Negate     : return NegateOperator    (op, left, right);
            case XPathOperatorGroup.Union      : return UnionOperator     (op, left, right); 
            default:
                Debug.Fail(op + " is not a valid XPathOperator"); 
                return null; 
            }
        } 

        QilNode LogicalOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.Or || op == XPathOperator.And);
            left  = f.ConvertToBoolean(left ); 
            right = f.ConvertToBoolean(right);
            return ( 
                op == XPathOperator.Or ? f.Or (left, right) : 
                /*default*/            f.And(left, right)
            ); 
        }

        QilNode CompareValues(XPathOperator op, QilNode left, QilNode right, XmlTypeCode compType) {
            Debug.Assert(compType == XmlTypeCode.Boolean || compType == XmlTypeCode.Double || compType == XmlTypeCode.String); 
            Debug.Assert(compType == XmlTypeCode.Boolean || left.XmlType.IsSingleton && right.XmlType.IsSingleton, "Both comparison operands must be singletons");
            left  = f.ConvertToType(compType, left ); 
            right = f.ConvertToType(compType, right); 

            switch (op) { 
            case XPathOperator.Eq : return f.Eq(left, right);
            case XPathOperator.Ne : return f.Ne(left, right);
            case XPathOperator.Lt : return f.Lt(left, right);
            case XPathOperator.Le : return f.Le(left, right); 
            case XPathOperator.Gt : return f.Gt(left, right);
            case XPathOperator.Ge : return f.Ge(left, right); 
            default : 
                Debug.Fail("Wrong operator type");
                return null; 
            }
        }

        QilNode CompareNodeSetAndValue(XPathOperator op, QilNode nodeset, QilNode val, XmlTypeCode compType) { 
            f.CheckNodeSet(nodeset);
            Debug.Assert(val.XmlType.IsSingleton); 
            Debug.Assert(compType == XmlTypeCode.Boolean || compType == XmlTypeCode.Double || compType == XmlTypeCode.String, "I don't know what to do with RTF here"); 
            if (compType == XmlTypeCode.Boolean || nodeset.XmlType.IsSingleton) {
                return CompareValues(op, nodeset, val, compType); 
            } else {
                QilIterator it = f.For(nodeset);
                return f.Not(f.IsEmpty(f.Filter(it, CompareValues(op, f.XPathNodeValue(it), val, compType))));
            } 
        }
 
        // Inverts relational operator in order to swap operands of the comparison 
        static XPathOperator InvertOp(XPathOperator op) {
            return ( 
                op == XPathOperator.Lt ? XPathOperator.Gt : // '<'  --> '>'
                op == XPathOperator.Le ? XPathOperator.Ge : // '<=' --> '>='
                op == XPathOperator.Gt ? XPathOperator.Lt : // '>'  --> '<'
                op == XPathOperator.Ge ? XPathOperator.Le : // '>=' --> '<=' 
                /*default:*/           op
            ); 
        } 

        QilNode CompareNodeSetAndNodeSet(XPathOperator op, QilNode left, QilNode right, XmlTypeCode compType) { 
            f.CheckNodeSet(left);
            f.CheckNodeSet(right);
            if (right.XmlType.IsSingleton) {
                return CompareNodeSetAndValue(op, /*nodeset:*/left, /*value:*/right, compType); 
            }
            if (left.XmlType.IsSingleton) { 
                op = InvertOp(op); 
                return CompareNodeSetAndValue(op, /*nodeset:*/right, /*value:*/left, compType);
            } 
            QilIterator leftEnd  = f.For(left );
            QilIterator rightEnd = f.For(right);
            return f.Not(f.IsEmpty(f.Loop(leftEnd, f.Filter(rightEnd, CompareValues(op, f.XPathNodeValue(leftEnd), f.XPathNodeValue(rightEnd), compType)))));
        } 

        QilNode EqualityOperator(XPathOperator op, QilNode left, QilNode right) { 
            Debug.Assert(op == XPathOperator.Eq || op == XPathOperator.Ne); 
            XmlQueryType  leftType =  left.XmlType;
            XmlQueryType rightType = right.XmlType; 

            if (f.IsAnyType(left) || f.IsAnyType(right)) {
                return f.InvokeEqualityOperator(QilOperator[(int)op], left, right);
            } else if (leftType.IsNode && rightType.IsNode) { 
                return CompareNodeSetAndNodeSet(op, left, right, XmlTypeCode.String);
            } else if (leftType.IsNode) { 
                return CompareNodeSetAndValue(op, /*nodeset:*/left, /*val:*/right, rightType.TypeCode); 
            } else if (rightType.IsNode) {
                return CompareNodeSetAndValue(op, /*nodeset:*/right, /*val:*/left, leftType.TypeCode); 
            } else {
                XmlTypeCode compType = (
                    leftType.TypeCode == XmlTypeCode.Boolean || rightType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean :
                    leftType.TypeCode == XmlTypeCode.Double  || rightType.TypeCode == XmlTypeCode.Double  ? XmlTypeCode.Double  : 
                    /*default:*/                                                                            XmlTypeCode.String
                ); 
                return CompareValues(op, left, right, compType); 
            }
        } 

        QilNode RelationalOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.Lt || op == XPathOperator.Le || op == XPathOperator.Gt || op == XPathOperator.Ge);
            XmlQueryType  leftType =  left.XmlType; 
            XmlQueryType rightType = right.XmlType;
 
            if (f.IsAnyType(left) || f.IsAnyType(right)) { 
                return f.InvokeRelationalOperator(QilOperator[(int)op], left, right);
            } else if (leftType.IsNode && rightType.IsNode) { 
                return CompareNodeSetAndNodeSet(op, left, right, XmlTypeCode.Double);
            } else if (leftType.IsNode) {
                XmlTypeCode compType = rightType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean : XmlTypeCode.Double;
                return CompareNodeSetAndValue(op, /*nodeset:*/left, /*val:*/right, compType); 
            } else if (rightType.IsNode) {
                XmlTypeCode compType = leftType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean : XmlTypeCode.Double; 
                op = InvertOp(op); 
                return CompareNodeSetAndValue(op, /*nodeset:*/right, /*val:*/left, compType);
            } else { 
                return CompareValues(op, left, right, XmlTypeCode.Double);
            }
        }
 
        QilNode NegateOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.UnaryMinus); 
            Debug.Assert(right == null); 
            return f.Negate(f.ConvertToNumber(left));
        } 

        QilNode ArithmeticOperator(XPathOperator op, QilNode left, QilNode right) {
            left  = f.ConvertToNumber(left );
            right = f.ConvertToNumber(right); 
            switch (op) {
            case XPathOperator.Plus     : return f.Add(     left, right); 
            case XPathOperator.Minus    : return f.Subtract(left, right); 
            case XPathOperator.Multiply : return f.Multiply(left, right);
            case XPathOperator.Divide   : return f.Divide(  left, right); 
            case XPathOperator.Modulo   : return f.Modulo(  left, right);
            default :
                Debug.Fail("Wrong operator type");
                return null; 
            }
        } 
 
        QilNode UnionOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.Union); 
            if (left == null) {
                return f.EnsureNodeSet(right);
            }
            left  = f.EnsureNodeSet(left ); 
            right = f.EnsureNodeSet(right);
            if (left.NodeType == QilNodeType.Sequence) { 
                // ToDo: drop this logic or move it to QilPatternFactory.Union() 
                ((QilList)left).Add(right);
                return left; 
            } else {
                return f.Union(left, right);
            }
        } 

        // also called by XPathPatternBuilder 
        public static XmlNodeKindFlags AxisTypeMask(XmlNodeKindFlags inputTypeMask, XPathNodeType nodeType, XPathAxis xpathAxis) { 
            return (XmlNodeKindFlags) (
                (int) inputTypeMask & 
                (int) XPathNodeType2QilXmlNodeKind[(int) nodeType] & (int) XPathAxisMask[(int) xpathAxis]
            );
        }
 
        QilNode BuildAxisFilter(QilNode qilAxis, XPathAxis xpathAxis, XPathNodeType nodeType, string name, string nsUri) {
            XmlNodeKindFlags original = qilAxis.XmlType.NodeKinds; 
            XmlNodeKindFlags required = AxisTypeMask(original, nodeType, xpathAxis); 

            QilIterator itr; 

            if (required == 0) {
                return f.Sequence();
            } else if (required == original) { 
            } else {
                qilAxis = f.Filter(itr = f.For(qilAxis), f.IsType(itr, T.NodeChoice(required))); 
                qilAxis.XmlType = T.PrimeProduct(T.NodeChoice(required), qilAxis.XmlType.Cardinality); 

 
                // Without code bellow IlGeneragion gives stack overflow exception for the following passage.
                //
                //    
                //         
                //    
                // 
 
                // ToDo: remove this code when IlGen bug will be fixed.
                if (qilAxis.NodeType == QilNodeType.Filter) { 
                    QilLoop filter = (QilLoop) qilAxis;
                    filter.Body = f.And(filter.Body,
                        name  != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri))    : // ns:bar || bar
                        nsUri != null                  ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:* 
                        name  != null                  ? f.Eq(f.LocalNameOf(itr), f.String(name))     : // *:foo
                        /*name  == nsUri == null*/       f.True()                                       // * 
                    ); 
                    return filter;
                } 
            }

            return f.Filter(itr = f.For(qilAxis),
                name  != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri))    : // ns:bar || bar 
                nsUri != null                  ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:*
                name  != null                  ? f.Eq(f.LocalNameOf(itr), f.String(name))     : // *:foo 
                /*name  == nsUri == null*/       f.True()                                       // * 
            );
        } 

        // XmlNodeKindFlags from XPathNodeType
        static XmlNodeKindFlags[] XPathNodeType2QilXmlNodeKind = {
                /*Root                 */ XmlNodeKindFlags.Document, 
                /*Element              */ XmlNodeKindFlags.Element,
                /*Attribute            */ XmlNodeKindFlags.Attribute, 
                /*Namespace            */ XmlNodeKindFlags.Namespace, 
                /*Text                 */ XmlNodeKindFlags.Text,
                /*SignificantWhitespace*/ XmlNodeKindFlags.Text, 
                /*Whitespace           */ XmlNodeKindFlags.Text,
                /*ProcessingInstruction*/ XmlNodeKindFlags.PI,
                /*Comment              */ XmlNodeKindFlags.Comment,
                /*All                  */ XmlNodeKindFlags.Any 
        };
 
        QilNode BuildAxis(XPathAxis xpathAxis, XPathNodeType nodeType, string nsUri, string name) { 
            QilNode currentNode = GetCurrentNode();
            QilNode qilAxis; 

            switch (xpathAxis) {
            case XPathAxis.Ancestor         : qilAxis = f.Ancestor             (currentNode); break;
            case XPathAxis.AncestorOrSelf   : qilAxis = f.AncestorOrSelf       (currentNode); break; 
            case XPathAxis.Attribute        : qilAxis = f.Content              (currentNode); break;
            case XPathAxis.Child            : qilAxis = f.Content              (currentNode); break; 
            case XPathAxis.Descendant       : qilAxis = f.Descendant           (currentNode); break; 
            case XPathAxis.DescendantOrSelf : qilAxis = f.DescendantOrSelf     (currentNode); break;
            case XPathAxis.Following        : qilAxis = f.XPathFollowing       (currentNode); break; 
            case XPathAxis.FollowingSibling : qilAxis = f.FollowingSibling     (currentNode); break;
            case XPathAxis.Namespace        : qilAxis = f.XPathNamespace       (currentNode); break;
            case XPathAxis.Parent           : qilAxis = f.Parent               (currentNode); break;
            case XPathAxis.Preceding        : qilAxis = f.XPathPreceding       (currentNode); break; 
            case XPathAxis.PrecedingSibling : qilAxis = f.PrecedingSibling     (currentNode); break;
            case XPathAxis.Self             : qilAxis =                        (currentNode); break; 
            // Can be done using BuildAxisFilter() but f.Root() sets wrong XmlNodeKindFlags 
            case XPathAxis.Root             : return f.Root                    (currentNode);
            default                         : 
                qilAxis = null;
                Debug.Fail("Invalid EnumValue 'XPathAxis'");
                break;
            } 

            QilNode result = BuildAxisFilter(qilAxis, xpathAxis, nodeType, name, nsUri); 
            if ( 
                xpathAxis == XPathAxis.Ancestor       || xpathAxis == XPathAxis.Preceding ||
                xpathAxis == XPathAxis.AncestorOrSelf || xpathAxis == XPathAxis.PrecedingSibling 
            ) {
                result = f.BaseFactory.DocOrderDistinct(result);
                // To make grouping operator NOP we should always return path expressions in DOD.
                // I can't use Pattern factory here becasue Predicate() depends on fact that DOD() is 
                //     outmost node in reverse steps
            } 
            return result; 
        }
 
        public virtual QilNode Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string prefix, string name) {
            string nsUri = prefix == null ? null : this.environment.ResolvePrefix(prefix);
            return BuildAxis(xpathAxis, nodeType, nsUri, name);
        } 

        // "left/right" 
        public virtual QilNode JoinStep(QilNode left, QilNode right) { 
            f.CheckNodeSet(right);
            QilIterator leftIt = f.For(f.EnsureNodeSet(left)); 
            // in XPath 1.0 step is always nodetest and as a result it can't contain last().
            right = fixupVisitor.Fixup(right, /*current:*/leftIt, /*last:*/null);
            numFixupCurrent  -= fixupVisitor.numCurrent ;
            numFixupPosition -= fixupVisitor.numPosition; 
            numFixupLast     -= fixupVisitor.numLast    ;
            return f.DocOrderDistinct(f.Loop(leftIt, right)); 
        } 

        // "nodeset[predicate]" 
        // XPath spec $3.3 (para 5)
        public virtual QilNode Predicate(QilNode nodeset, QilNode predicate, bool isReverseStep) {
            if (isReverseStep) {
                Debug.Assert(nodeset.NodeType == QilNodeType.DocOrderDistinct, 
                    "ReverseAxe in Qil is actuly reverse and we compile them here in builder by wrapping to DocOrderDistinct()"
                ); 
                // The trick here is that we unwarp it back, compile as regular predicate and wrap again. 
                // this way this wat we hold invariant that path expresion are always DOD and make predicates on reverse axe
                // work as specified in XPath 2.0 FS: http://www.w3.org/TR/xquery-semantics/#id-axis-steps 
                nodeset = ((QilUnary) nodeset).Child;
            }

            nodeset = f.EnsureNodeSet(nodeset); 

            // Prepocess predicate: if (predicate is number) then predicate := (position() == predicate) 
            if (!f.IsAnyType(predicate)) { 
                if (predicate.XmlType.TypeCode == XmlTypeCode.Double) {
                    predicate = f.Eq(GetCurrentPosition(), predicate); 
                } else {
                    predicate = f.ConvertToBoolean(predicate);
                }
            } else { 
                QilIterator i;
                predicate = f.Loop(i = f.Let(predicate), 
                    f.Conditional(f.IsType(i, T.Double), 
                        f.Eq(GetCurrentPosition(), f.TypeAssert(i, T.DoubleX)),
                        f.ConvertToBoolean(i) 
                    )
                );
            }
 
            // Mirgeing nodeset and predicate:
            // 1. Predicate contains 0 last() : 
            //      for $i in nodeset 
            //      where predicate
            //      return $i 
            //   ToDo: Currently we are keepeing old output to minimize diff.
            // 2. Predicate contains 1 last()
            //      let $cach := nodeset return
            //          for $i in $cach 
            //          where predicate(length($cach))
            //          return $i 
            //   ToDo: This is a little optimisation we can do or don't do. 
            // 3. Predicate contains 2+ last()
            //      let $cash := nodeset return 
            //          let $size := length($cash) return
            //              for $i in $cash
            //              where predicate($size)
            //              return $i 

            QilNode result; 
            if (numFixupLast != 0 && fixupVisitor.CountUnfixedLast(predicate) != 0) { 
                // this subtree has unfixed last() nodes
                QilIterator cash = f.Let(nodeset); 
                QilIterator size = f.Let(f.XsltConvert(f.Length(cash), T.DoubleX));
                QilIterator it   = f.For(cash);
                predicate = fixupVisitor.Fixup(predicate, /*current:*/it, /*last:*/size);
                numFixupCurrent  -= fixupVisitor.numCurrent ; 
                numFixupPosition -= fixupVisitor.numPosition;
                numFixupLast     -= fixupVisitor.numLast    ; 
                result = f.Loop(cash, f.Loop(size, f.Filter(it, predicate))); 
            }else {
                QilIterator it = f.For(nodeset); 
                predicate = fixupVisitor.Fixup(predicate, /*current:*/it, /*last:*/null);
                numFixupCurrent  -= fixupVisitor.numCurrent ;
                numFixupPosition -= fixupVisitor.numPosition;
                numFixupLast     -= fixupVisitor.numLast    ; 
                result = f.Filter(it, predicate);
            } 
            if (isReverseStep) { 
                result = f.DocOrderDistinct(result);
            } 
            return result;
        }

        public virtual QilNode Variable(string prefix, string name) { 
            return this.environment.ResolveVariable(prefix, name);
        } 
 
        public virtual QilNode Function(string prefix, string name, IList args) {
            Debug.Assert(!args.IsReadOnly, "Writable collection expected"); 
            if (prefix.Length == 0) {
                FunctionInfo func;
                if (FunctionTable.TryGetValue(name, out func)) {
                    func.CastArguments(args, name, f); 

                    switch (func.id) { 
                    case FuncId.Not             : return f.Not(args[0]); 
                    case FuncId.Last            : return GetLastPosition();
                    case FuncId.Position        : return GetCurrentPosition(); 
                    case FuncId.Count           : return f.XsltConvert(f.Length(f.DocOrderDistinct(args[0])), T.DoubleX);
                    case FuncId.LocalName       : return args.Count == 0 ? f.LocalNameOf(GetCurrentNode()) : LocalNameOfFirstNode(args[0]);
                    case FuncId.NamespaceUri    : return args.Count == 0 ? f.NamespaceUriOf(GetCurrentNode()) : NamespaceOfFirstNode(args[0]);
                    case FuncId.Name            : return args.Count == 0 ? NameOf(GetCurrentNode()) : NameOfFirstNode(args[0]); 
                    case FuncId.String          : return args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : f.ConvertToString(args[0]);
                    case FuncId.Number          : return args.Count == 0 ? f.XsltConvert(f.XPathNodeValue(GetCurrentNode()), T.DoubleX) : f.ConvertToNumber(args[0]); 
                    case FuncId.Boolean         : return f.ConvertToBoolean(args[0]); 
                    case FuncId.True            : return f.True();
                    case FuncId.False           : return f.False(); 
                    case FuncId.Id              : return f.DocOrderDistinct(f.Id(GetCurrentNode(), args[0]));
                    case FuncId.Concat          : return f.StrConcat(args);
                    case FuncId.StartsWith      : return f.InvokeStartsWith(args[0], args[1]);
                    case FuncId.Contains        : return f.InvokeContains(args[0], args[1]); 
                    case FuncId.SubstringBefore : return f.InvokeSubstringBefore(args[0], args[1]);
                    case FuncId.SubstringAfter  : return f.InvokeSubstringAfter(args[0], args[1]); 
                    case FuncId.Substring       : 
                        return args.Count == 2 ? f.InvokeSubstring(args[0], args[1]) : f.InvokeSubstring(args[0], args[1], args[2]);
                    case FuncId.StringLength    : 
                        return f.XsltConvert(f.StrLength(args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : args[0]), T.DoubleX);
                    case FuncId.Normalize       :
                        return f.InvokeNormalizeSpace(args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : args[0]);
                    case FuncId.Translate       : return f.InvokeTranslate(args[0], args[1], args[2]); 
                    case FuncId.Lang            : return f.InvokeLang(args[0], GetCurrentNode());
                    case FuncId.Sum             : return Sum(f.DocOrderDistinct(args[0])); 
                    case FuncId.Floor           : return f.InvokeFloor(args[0]); 
                    case FuncId.Ceiling         : return f.InvokeCeiling(args[0]);
                    case FuncId.Round           : return f.InvokeRound(args[0]); 
                    default:
                        Debug.Fail(func.id + " is present in the function table, but absent from the switch");
                        return null;
                    } 
                }
            } 
 
            return this.environment.ResolveFunction(prefix, name, args, (IFocus)this);
        } 

        QilNode LocalNameOfFirstNode(QilNode arg) {
            f.CheckNodeSet(arg);
            if (arg.XmlType.IsSingleton) { 
                return f.LocalNameOf(arg);
            } else { 
                QilIterator i; 
                return f.StrConcat(f.Loop(i = f.FirstNode(arg), f.LocalNameOf(i)));
            } 
        }

        QilNode NamespaceOfFirstNode(QilNode arg) {
            f.CheckNodeSet(arg); 
            if (arg.XmlType.IsSingleton) {
                return f.NamespaceUriOf(arg); 
            } else { 
                QilIterator i;
                return f.StrConcat(f.Loop(i = f.FirstNode(arg), f.NamespaceUriOf(i))); 
            }
        }

        QilNode NameOf(QilNode arg) { 
            f.CheckNodeNotRtf(arg);
            // ToDo: NameOf QIL node returns QName, so we cannot use it here. 
            // We may want to introduce a new QIL node that returns a string. 
            if (arg is QilIterator) {
                QilIterator p, ln; 
                return f.Loop(p = f.Let(f.PrefixOf(arg)), f.Loop(ln = f.Let(f.LocalNameOf(arg)),
                    f.Conditional(f.Eq(f.StrLength(p), f.Int32(0)), ln, f.StrConcat(p, f.String(":"), ln)
                )));
            } else { 
                QilIterator let = f.Let(arg);
                return f.Loop(let, /*recursion:*/NameOf(let)); 
            } 
        }
 
        QilNode NameOfFirstNode(QilNode arg) {
            f.CheckNodeSet(arg);
            if (arg.XmlType.IsSingleton) {
                return NameOf(arg); 
            } else {
                QilIterator i; 
                return f.StrConcat(f.Loop(i = f.FirstNode(arg), NameOf(i))); 
            }
        } 

        QilNode Sum(QilNode arg) {
            f.CheckNodeSet(arg);
            QilIterator i; 
            return f.Sum(f.Sequence(f.Double(0d), f.Loop(i = f.For(arg), f.ConvertToNumber(i))));
        } 
 
        enum XPathOperatorGroup {
            Unknown   , 
            Logical   ,
            Equality  ,
            Relational,
            Arithmetic, 
            Negate    ,
            Union     , 
        } 

        static XPathOperatorGroup[] OperatorGroup = { 
            /*Unknown   */ XPathOperatorGroup.Unknown   ,
            /*Or        */ XPathOperatorGroup.Logical   ,
            /*And       */ XPathOperatorGroup.Logical   ,
            /*Eq        */ XPathOperatorGroup.Equality  , 
            /*Ne        */ XPathOperatorGroup.Equality  ,
            /*Lt        */ XPathOperatorGroup.Relational, 
            /*Le        */ XPathOperatorGroup.Relational, 
            /*Gt        */ XPathOperatorGroup.Relational,
            /*Ge        */ XPathOperatorGroup.Relational, 
            /*Plus      */ XPathOperatorGroup.Arithmetic,
            /*Minus     */ XPathOperatorGroup.Arithmetic,
            /*Multiply  */ XPathOperatorGroup.Arithmetic,
            /*Divide    */ XPathOperatorGroup.Arithmetic, 
            /*Modulo    */ XPathOperatorGroup.Arithmetic,
            /*UnaryMinus*/ XPathOperatorGroup.Negate    , 
            /*Union     */ XPathOperatorGroup.Union     , 
        };
 
        static QilNodeType[] QilOperator = {
            /*Unknown    */ QilNodeType.Unknown ,
            /*Or         */ QilNodeType.Or      ,
            /*And        */ QilNodeType.And     , 
            /*Eq         */ QilNodeType.Eq      ,
            /*Ne         */ QilNodeType.Ne      , 
            /*Lt         */ QilNodeType.Lt      , 
            /*Le         */ QilNodeType.Le      ,
            /*Gt         */ QilNodeType.Gt      , 
            /*Ge         */ QilNodeType.Ge      ,
            /*Plus       */ QilNodeType.Add     ,
            /*Minus      */ QilNodeType.Subtract,
            /*Multiply   */ QilNodeType.Multiply, 
            /*Divide     */ QilNodeType.Divide  ,
            /*Modulo     */ QilNodeType.Modulo  , 
            /*UnaryMinus */ QilNodeType.Negate  , 
            /*Union      */ QilNodeType.Sequence,
        }; 

        // XmlNodeType(s) of nodes by XPathAxis
        static XmlNodeKindFlags[] XPathAxisMask = {
            /*Unknown         */ XmlNodeKindFlags.None, 
            /*Ancestor        */ XmlNodeKindFlags.Element | XmlNodeKindFlags.Document,
            /*AncestorOrSelf  */ XmlNodeKindFlags.Any, 
            /*Attribute       */ XmlNodeKindFlags.Attribute, 
            /*Child           */ XmlNodeKindFlags.Content,
            /*Descendant      */ XmlNodeKindFlags.Content, 
            /*DescendantOrSelf*/ XmlNodeKindFlags.Any,
            /*Following       */ XmlNodeKindFlags.Content,
            /*FollowingSibling*/ XmlNodeKindFlags.Content,
            /*Namespace       */ XmlNodeKindFlags.Namespace, 
            /*Parent          */ XmlNodeKindFlags.Element | XmlNodeKindFlags.Document,
            /*Preceding       */ XmlNodeKindFlags.Content, 
            /*PrecedingSibling*/ XmlNodeKindFlags.Content, 
            /*Self            */ XmlNodeKindFlags.Any,
            /*Root            */ XmlNodeKindFlags.Document, 
        };

        // ----------------------------------------------------------------
        internal enum FuncId { 
            Last = 0,
            Position, 
            Count, 
            LocalName,
            NamespaceUri, 
            Name,
            String,
            Number,
            Boolean, 
            True,
            False, 
            Not, 
            Id,
            Concat, 
            StartsWith,
            Contains,
            SubstringBefore,
            SubstringAfter, 
            Substring,
            StringLength, 
            Normalize, 
            Translate,
            Lang, 
            Sum,
            Floor,
            Ceiling,
            Round 
        };
 
        public static readonly XmlTypeCode[] argAny      = {XmlTypeCode.Item}; 
        public static readonly XmlTypeCode[] argNodeSet  = {XmlTypeCode.Node};
        public static readonly XmlTypeCode[] argBoolean  = {XmlTypeCode.Boolean}; 
        public static readonly XmlTypeCode[] argDouble   = {XmlTypeCode.Double};
        public static readonly XmlTypeCode[] argString   = {XmlTypeCode.String};
        public static readonly XmlTypeCode[] argString2  = {XmlTypeCode.String, XmlTypeCode.String};
        public static readonly XmlTypeCode[] argString3  = {XmlTypeCode.String, XmlTypeCode.String, XmlTypeCode.String}; 
        public static readonly XmlTypeCode[] argFnSubstr = {XmlTypeCode.String, XmlTypeCode.Double, XmlTypeCode.Double};
 
        public static Dictionary FunctionTable = CreateFunctionTable(); 
        private static Dictionary CreateFunctionTable() {
            Dictionary table = new Dictionary(36); 
            table.Add("last"               , new FunctionInfo(FuncId.Last           , 0, 0, null));
            table.Add("position"           , new FunctionInfo(FuncId.Position       , 0, 0, null));
            table.Add("name"               , new FunctionInfo(FuncId.Name           , 0, 1, argNodeSet));
            table.Add("namespace-uri"      , new FunctionInfo(FuncId.NamespaceUri   , 0, 1, argNodeSet)); 
            table.Add("local-name"         , new FunctionInfo(FuncId.LocalName      , 0, 1, argNodeSet));
            table.Add("count"              , new FunctionInfo(FuncId.Count          , 1, 1, argNodeSet)); 
            table.Add("id"                 , new FunctionInfo(FuncId.Id             , 1, 1, argAny)); 
            table.Add("string"             , new FunctionInfo(FuncId.String         , 0, 1, argAny));
            table.Add("concat"             , new FunctionInfo(FuncId.Concat         , 2, FunctionInfo.Infinity, null)); 
            table.Add("starts-with"        , new FunctionInfo(FuncId.StartsWith     , 2, 2, argString2));
            table.Add("contains"           , new FunctionInfo(FuncId.Contains       , 2, 2, argString2));
            table.Add("substring-before"   , new FunctionInfo(FuncId.SubstringBefore, 2, 2, argString2));
            table.Add("substring-after"    , new FunctionInfo(FuncId.SubstringAfter , 2, 2, argString2)); 
            table.Add("substring"          , new FunctionInfo(FuncId.Substring      , 2, 3, argFnSubstr));
            table.Add("string-length"      , new FunctionInfo(FuncId.StringLength   , 0, 1, argString)); 
            table.Add("normalize-space"    , new FunctionInfo(FuncId.Normalize      , 0, 1, argString)); 
            table.Add("translate"          , new FunctionInfo(FuncId.Translate      , 3, 3, argString3));
            table.Add("boolean"            , new FunctionInfo(FuncId.Boolean        , 1, 1, argAny)); 
            table.Add("not"                , new FunctionInfo(FuncId.Not            , 1, 1, argBoolean));
            table.Add("true"               , new FunctionInfo(FuncId.True           , 0, 0, null));
            table.Add("false"              , new FunctionInfo(FuncId.False          , 0, 0, null));
            table.Add("lang"               , new FunctionInfo(FuncId.Lang           , 1, 1, argString)); 
            table.Add("number"             , new FunctionInfo(FuncId.Number         , 0, 1, argAny));
            table.Add("sum"                , new FunctionInfo(FuncId.Sum            , 1, 1, argNodeSet)); 
            table.Add("floor"              , new FunctionInfo(FuncId.Floor          , 1, 1, argDouble)); 
            table.Add("ceiling"            , new FunctionInfo(FuncId.Ceiling        , 1, 1, argDouble));
            table.Add("round"              , new FunctionInfo(FuncId.Round          , 1, 1, argDouble)); 
            return table;
        }

        public static bool IsFunctionAvailable(string localName, string nsUri) { 
            if (nsUri.Length != 0) {
                return false; 
            } 
            return FunctionTable.ContainsKey(localName);
        } 

        private class FixupVisitor : QilReplaceVisitor {
            new QilPatternFactory f;
            QilNode     fixupCurrent, fixupPosition, fixupLast; // fixup nodes we are replacing 
            QilIterator current;
            QilNode     last;               // expressions we are using to replace fixupNodes 
            bool        justCount;          // Don't change tree, just count 
            IXPathEnvironment environment;  // temp solution
            public int  numCurrent, numPosition, numLast; // here we are counting all replacements we have made 

            public FixupVisitor(QilPatternFactory f, QilNode fixupCurrent, QilNode fixupPosition, QilNode fixupLast) : base(f.BaseFactory) {
                this.f             = f;
                this.fixupCurrent  = fixupCurrent; 
                this.fixupPosition = fixupPosition;
                this.fixupLast     = fixupLast    ; 
            } 

            public QilNode Fixup(QilNode inExpr, QilIterator current, QilNode last) { 
                this.current  = current ;
                this.last     = last    ;
                Debug.Assert(current != null);
                this.justCount  = false; 
                this.environment = null;
                numCurrent = numPosition = numLast = 0; 
                inExpr = VisitAssumeReference(inExpr); 
#if StopMaskOptimisation
                SetStopVisitMark(inExpr, /*stop*/true); 
#endif
                return inExpr;
            }
 
            public QilNode Fixup(QilNode inExpr, IXPathEnvironment environment) {
                Debug.Assert(environment != null); 
                this.justCount   = false; 
                this.current     = null;
                this.environment = environment; 
                numCurrent = numPosition = numLast = 0;
                inExpr = VisitAssumeReference(inExpr);
#if StopMaskOptimisation
                // Don't need 
                //SetStopVisitMark(inExpr, /*stop*/true);
#endif 
                return inExpr; 
            }
 
            public int CountUnfixedLast(QilNode inExpr) {
                this.justCount  = true;
                numCurrent = numPosition = numLast = 0;
                VisitAssumeReference(inExpr); 
                return numLast;
            } 
 
            protected override QilNode VisitUnknown(QilNode unknown) {
                Debug.Assert(unknown.NodeType == QilNodeType.Unknown); 
                if (unknown == fixupCurrent) {
                    numCurrent ++;
                    if (! justCount) {
                        if (this.environment != null) { 
                            unknown = this.environment.GetCurrent();
                        } else if (this.current != null) { 
                            unknown = this.current; 
                        }
                    } 
                } else if (unknown == fixupPosition) {
                    numPosition ++;
                    if (! justCount) {
                        if (this.environment != null) { 
                            unknown = this.environment.GetPosition();
                        } else if (this.current != null) { 
                            // position can be in predicate only and in predicate current olways an iterator 
                            unknown = f.XsltConvert(f.PositionOf((QilIterator)this.current), T.DoubleX);
                        } 
                    }
                } else if (unknown == fixupLast) {
                    numLast ++;
                    if (! justCount) { 
                        if (this.environment != null) {
                            unknown = this.environment.GetLast(); 
                        } else if (this.current != null) { 
                            Debug.Assert(this.last != null);
                            unknown = this.last; 
                        }
                    }
                }
                Debug.Assert(unknown != null); 
                return unknown;
            } 
 
#if StopMaskOptimisation
            // This optimisation marks subtrees that was fixed already and prevents FixupVisitor from 
            // visiting them again. The logic is brokken, because when unfixed tree is added inside fixed one
            // it never fixed anymore.
            // This happens in all cortasian productions now.
            // Excample "a/b=c". 'c' is added inside 'b' 

            // I belive some optimisation is posible and would be nice to have. 
            // We may change the way we generating cortasian product. 

            protected override QilNode Visit(QilNode n) { 
                if (GetStopVisitMark(n)) {
                    // Optimisation:
                    // This subtree was fixed already. No need to go inside it.
                    if (! justCount) { 
                        SetStopVisitMark(n, /*stop*/false); // We clean this annotation
                    } 
                    return n; 
                }
                return base.Visit(n); 
            }

            void SetStopVisitMark(QilNode n, bool stop) {
                if (n.Type != QilNodeType.For && n.Type != QilNodeType.Let) { 
                    XsltAnnotation.Write(n)[0] = (stop ? /*any object*/fixupCurrent : null);
                } else { 
                    // we shouldn't alter annotation of "reference" nodes (Iterators, Functions, ...) 
                }
            } 
            bool GetStopVisitMark(QilNode n) {
                return XsltAnnotation.Write(n)[0] != null;
            }
#endif 
        }
 
        internal class FunctionInfo { 
            public T                id;
            public int              minArgs; 
            public int              maxArgs;
            public XmlTypeCode[]    argTypes;

            public const int        Infinity = int.MaxValue; 

            public FunctionInfo(T id, int minArgs, int maxArgs, XmlTypeCode[] argTypes) { 
                Debug.Assert(maxArgs == 0 || maxArgs == Infinity || argTypes != null && argTypes.Length == maxArgs); 
                this.id       = id;
                this.minArgs  = minArgs; 
                this.maxArgs  = maxArgs;
                this.argTypes = argTypes;
            }
 
            public static void CheckArity(int minArgs, int maxArgs, string name, int numArgs) {
                if (minArgs <= numArgs && numArgs <= maxArgs) { 
                    return; 
                }
 
                // Possible cases:
                // [0, 0], [1, 1], [2, 2], [3, 3]
                // [0, 1], [1, 2], [2, 3], [2, +inf]
                // [1, 3], [2, 4] 
                string resId;
                if (minArgs == maxArgs) { 
                    resId = Res.XPath_NArgsExpected; 
                } else {
                    if (maxArgs == minArgs + 1) { 
                        resId = Res.XPath_NOrMArgsExpected;
                    } else if (numArgs < minArgs) {
                        resId = Res.XPath_AtLeastNArgsExpected;
                    } else { 
                        // This case is impossible for standard XPath/XSLT functions
                        Debug.Assert(numArgs > maxArgs); 
                        resId = Res.XPath_AtMostMArgsExpected; 
                    }
                } 
                throw new XPathCompileException(resId, name, minArgs.ToString(CultureInfo.InvariantCulture), maxArgs.ToString(CultureInfo.InvariantCulture));
            }

            public void CastArguments(IList args, string name, XPathQilFactory f) { 
                CheckArity(this.minArgs, this.maxArgs, name, args.Count);
 
                // Convert arguments to the appropriate types 
                if (maxArgs == Infinity) {
                    // Special case for concat() function 
                    for (int i = 0; i < args.Count; i++) {
                        args[i] = f.ConvertToType(XmlTypeCode.String, args[i]);
                    }
                } else { 
                    for (int i = 0; i < args.Count; i++) {
                        if (argTypes[i] == XmlTypeCode.Node && f.CannotBeNodeSet(args[i])) { 
                            throw new XPathCompileException(Res.XPath_NodeSetArgumentExpected, name, (i + 1).ToString(CultureInfo.InvariantCulture)); 
                        }
                        args[i] = f.ConvertToType(argTypes[i], args[i]); 
                    }
                }
            }
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization; 
using System.Xml.Schema;
using System.Xml.XPath;
using System.Xml.Xsl.Qil;
 
//#define StopMaskOptimisation
 
namespace System.Xml.Xsl.XPath { 
    using FunctionInfo  = XPathBuilder.FunctionInfo;
    using Res           = System.Xml.Utils.Res; 
    using T             = XmlQueryTypeFactory;

    internal class XPathBuilder : IXPathBuilder, IXPathEnvironment {
        private   XPathQilFactory   f; 
        private   IXPathEnvironment environment;
        private   bool              inTheBuild; 
 
        // Singleton nodes used as fixup markers
        protected QilNode           fixupCurrent, fixupPosition, fixupLast; 

        // Number of unresolved fixup nodes
        protected int               numFixupCurrent, numFixupPosition, numFixupLast;
        private   FixupVisitor      fixupVisitor; 

        /*  --------------------------------------------------------------------------- 
            IXPathEnvironment interface 
        */
        QilNode IFocus.GetCurrent()  { return GetCurrentNode    (); } 
        QilNode IFocus.GetPosition() { return GetCurrentPosition(); }
        QilNode IFocus.GetLast()     { return GetLastPosition   (); }

        XPathQilFactory IXPathEnvironment.Factory   { get { return f; } } 

        QilNode IXPathEnvironment.ResolveVariable(string prefix, string name) { 
            return Variable(prefix, name); 
        }
        QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList args, IFocus env) { 
            Debug.Fail("Must not be called");
            return null;
        }
        string  IXPathEnvironment.ResolvePrefix(string prefix) { 
            return environment.ResolvePrefix(prefix);
        } 
        //  --------------------------------------------------------------------------- 

        public XPathBuilder(IXPathEnvironment environment) { 
            this.environment = environment;
            this.f = this.environment.Factory;
            this.fixupCurrent   = f.Unknown(T.NodeNotRtf);
            this.fixupPosition  = f.Unknown(T.DoubleX); 
            this.fixupLast      = f.Unknown(T.DoubleX);
            this.fixupVisitor   = new FixupVisitor(f, fixupCurrent, fixupPosition, fixupLast); 
        } 

        public virtual void StartBuild() { 
            Debug.Assert(! inTheBuild, "XPathBuilder is busy!");
            inTheBuild = true;
            numFixupCurrent = numFixupPosition = numFixupLast = 0;
        } 

        public virtual QilNode EndBuild(QilNode result) { 
            if (result == null) { // special door to clean builder state in exception handlers 
                inTheBuild = false;
                return result; 
            }
            Debug.Assert(inTheBuild, "StartBuild() wasn't called");
            if (result.XmlType.MaybeMany && result.XmlType.IsNode && result.XmlType.IsNotRtf) {
                result = f.DocOrderDistinct(result); 
            }
            result = fixupVisitor.Fixup(result, /*environment:*/this.environment); 
            numFixupCurrent  -= fixupVisitor.numCurrent ; 
            numFixupPosition -= fixupVisitor.numPosition;
            numFixupLast     -= fixupVisitor.numLast    ; 

            // All these variables will be positive for "false() and (. = position() + last())"
            // since QilPatternFactory eliminates the right operand of 'and'
            Debug.Assert(numFixupCurrent  >= 0, "Context fixup error"); 
            Debug.Assert(numFixupPosition >= 0, "Context fixup error");
            Debug.Assert(numFixupLast     >= 0, "Context fixup error"); 
            inTheBuild = false; 
            return result;
        } 

        private QilNode GetCurrentNode    () { numFixupCurrent  ++; return fixupCurrent ; }
        private QilNode GetCurrentPosition() { numFixupPosition ++; return fixupPosition; }
        private QilNode GetLastPosition   () { numFixupLast     ++; return fixupLast    ; } 

        public virtual QilNode String(string value) { 
            return f.String(value); 
        }
 
        public virtual QilNode Number(double value) {
            return f.Double(value);
        }
 
        public virtual QilNode Operator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op != XPathOperator.Unknown); 
            switch (OperatorGroup[(int)op]) { 
            case XPathOperatorGroup.Logical    : return LogicalOperator   (op, left, right);
            case XPathOperatorGroup.Equality   : return EqualityOperator  (op, left, right); 
            case XPathOperatorGroup.Relational : return RelationalOperator(op, left, right);
            case XPathOperatorGroup.Arithmetic : return ArithmeticOperator(op, left, right);
            case XPathOperatorGroup.Negate     : return NegateOperator    (op, left, right);
            case XPathOperatorGroup.Union      : return UnionOperator     (op, left, right); 
            default:
                Debug.Fail(op + " is not a valid XPathOperator"); 
                return null; 
            }
        } 

        QilNode LogicalOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.Or || op == XPathOperator.And);
            left  = f.ConvertToBoolean(left ); 
            right = f.ConvertToBoolean(right);
            return ( 
                op == XPathOperator.Or ? f.Or (left, right) : 
                /*default*/            f.And(left, right)
            ); 
        }

        QilNode CompareValues(XPathOperator op, QilNode left, QilNode right, XmlTypeCode compType) {
            Debug.Assert(compType == XmlTypeCode.Boolean || compType == XmlTypeCode.Double || compType == XmlTypeCode.String); 
            Debug.Assert(compType == XmlTypeCode.Boolean || left.XmlType.IsSingleton && right.XmlType.IsSingleton, "Both comparison operands must be singletons");
            left  = f.ConvertToType(compType, left ); 
            right = f.ConvertToType(compType, right); 

            switch (op) { 
            case XPathOperator.Eq : return f.Eq(left, right);
            case XPathOperator.Ne : return f.Ne(left, right);
            case XPathOperator.Lt : return f.Lt(left, right);
            case XPathOperator.Le : return f.Le(left, right); 
            case XPathOperator.Gt : return f.Gt(left, right);
            case XPathOperator.Ge : return f.Ge(left, right); 
            default : 
                Debug.Fail("Wrong operator type");
                return null; 
            }
        }

        QilNode CompareNodeSetAndValue(XPathOperator op, QilNode nodeset, QilNode val, XmlTypeCode compType) { 
            f.CheckNodeSet(nodeset);
            Debug.Assert(val.XmlType.IsSingleton); 
            Debug.Assert(compType == XmlTypeCode.Boolean || compType == XmlTypeCode.Double || compType == XmlTypeCode.String, "I don't know what to do with RTF here"); 
            if (compType == XmlTypeCode.Boolean || nodeset.XmlType.IsSingleton) {
                return CompareValues(op, nodeset, val, compType); 
            } else {
                QilIterator it = f.For(nodeset);
                return f.Not(f.IsEmpty(f.Filter(it, CompareValues(op, f.XPathNodeValue(it), val, compType))));
            } 
        }
 
        // Inverts relational operator in order to swap operands of the comparison 
        static XPathOperator InvertOp(XPathOperator op) {
            return ( 
                op == XPathOperator.Lt ? XPathOperator.Gt : // '<'  --> '>'
                op == XPathOperator.Le ? XPathOperator.Ge : // '<=' --> '>='
                op == XPathOperator.Gt ? XPathOperator.Lt : // '>'  --> '<'
                op == XPathOperator.Ge ? XPathOperator.Le : // '>=' --> '<=' 
                /*default:*/           op
            ); 
        } 

        QilNode CompareNodeSetAndNodeSet(XPathOperator op, QilNode left, QilNode right, XmlTypeCode compType) { 
            f.CheckNodeSet(left);
            f.CheckNodeSet(right);
            if (right.XmlType.IsSingleton) {
                return CompareNodeSetAndValue(op, /*nodeset:*/left, /*value:*/right, compType); 
            }
            if (left.XmlType.IsSingleton) { 
                op = InvertOp(op); 
                return CompareNodeSetAndValue(op, /*nodeset:*/right, /*value:*/left, compType);
            } 
            QilIterator leftEnd  = f.For(left );
            QilIterator rightEnd = f.For(right);
            return f.Not(f.IsEmpty(f.Loop(leftEnd, f.Filter(rightEnd, CompareValues(op, f.XPathNodeValue(leftEnd), f.XPathNodeValue(rightEnd), compType)))));
        } 

        QilNode EqualityOperator(XPathOperator op, QilNode left, QilNode right) { 
            Debug.Assert(op == XPathOperator.Eq || op == XPathOperator.Ne); 
            XmlQueryType  leftType =  left.XmlType;
            XmlQueryType rightType = right.XmlType; 

            if (f.IsAnyType(left) || f.IsAnyType(right)) {
                return f.InvokeEqualityOperator(QilOperator[(int)op], left, right);
            } else if (leftType.IsNode && rightType.IsNode) { 
                return CompareNodeSetAndNodeSet(op, left, right, XmlTypeCode.String);
            } else if (leftType.IsNode) { 
                return CompareNodeSetAndValue(op, /*nodeset:*/left, /*val:*/right, rightType.TypeCode); 
            } else if (rightType.IsNode) {
                return CompareNodeSetAndValue(op, /*nodeset:*/right, /*val:*/left, leftType.TypeCode); 
            } else {
                XmlTypeCode compType = (
                    leftType.TypeCode == XmlTypeCode.Boolean || rightType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean :
                    leftType.TypeCode == XmlTypeCode.Double  || rightType.TypeCode == XmlTypeCode.Double  ? XmlTypeCode.Double  : 
                    /*default:*/                                                                            XmlTypeCode.String
                ); 
                return CompareValues(op, left, right, compType); 
            }
        } 

        QilNode RelationalOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.Lt || op == XPathOperator.Le || op == XPathOperator.Gt || op == XPathOperator.Ge);
            XmlQueryType  leftType =  left.XmlType; 
            XmlQueryType rightType = right.XmlType;
 
            if (f.IsAnyType(left) || f.IsAnyType(right)) { 
                return f.InvokeRelationalOperator(QilOperator[(int)op], left, right);
            } else if (leftType.IsNode && rightType.IsNode) { 
                return CompareNodeSetAndNodeSet(op, left, right, XmlTypeCode.Double);
            } else if (leftType.IsNode) {
                XmlTypeCode compType = rightType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean : XmlTypeCode.Double;
                return CompareNodeSetAndValue(op, /*nodeset:*/left, /*val:*/right, compType); 
            } else if (rightType.IsNode) {
                XmlTypeCode compType = leftType.TypeCode == XmlTypeCode.Boolean ? XmlTypeCode.Boolean : XmlTypeCode.Double; 
                op = InvertOp(op); 
                return CompareNodeSetAndValue(op, /*nodeset:*/right, /*val:*/left, compType);
            } else { 
                return CompareValues(op, left, right, XmlTypeCode.Double);
            }
        }
 
        QilNode NegateOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.UnaryMinus); 
            Debug.Assert(right == null); 
            return f.Negate(f.ConvertToNumber(left));
        } 

        QilNode ArithmeticOperator(XPathOperator op, QilNode left, QilNode right) {
            left  = f.ConvertToNumber(left );
            right = f.ConvertToNumber(right); 
            switch (op) {
            case XPathOperator.Plus     : return f.Add(     left, right); 
            case XPathOperator.Minus    : return f.Subtract(left, right); 
            case XPathOperator.Multiply : return f.Multiply(left, right);
            case XPathOperator.Divide   : return f.Divide(  left, right); 
            case XPathOperator.Modulo   : return f.Modulo(  left, right);
            default :
                Debug.Fail("Wrong operator type");
                return null; 
            }
        } 
 
        QilNode UnionOperator(XPathOperator op, QilNode left, QilNode right) {
            Debug.Assert(op == XPathOperator.Union); 
            if (left == null) {
                return f.EnsureNodeSet(right);
            }
            left  = f.EnsureNodeSet(left ); 
            right = f.EnsureNodeSet(right);
            if (left.NodeType == QilNodeType.Sequence) { 
                // ToDo: drop this logic or move it to QilPatternFactory.Union() 
                ((QilList)left).Add(right);
                return left; 
            } else {
                return f.Union(left, right);
            }
        } 

        // also called by XPathPatternBuilder 
        public static XmlNodeKindFlags AxisTypeMask(XmlNodeKindFlags inputTypeMask, XPathNodeType nodeType, XPathAxis xpathAxis) { 
            return (XmlNodeKindFlags) (
                (int) inputTypeMask & 
                (int) XPathNodeType2QilXmlNodeKind[(int) nodeType] & (int) XPathAxisMask[(int) xpathAxis]
            );
        }
 
        QilNode BuildAxisFilter(QilNode qilAxis, XPathAxis xpathAxis, XPathNodeType nodeType, string name, string nsUri) {
            XmlNodeKindFlags original = qilAxis.XmlType.NodeKinds; 
            XmlNodeKindFlags required = AxisTypeMask(original, nodeType, xpathAxis); 

            QilIterator itr; 

            if (required == 0) {
                return f.Sequence();
            } else if (required == original) { 
            } else {
                qilAxis = f.Filter(itr = f.For(qilAxis), f.IsType(itr, T.NodeChoice(required))); 
                qilAxis.XmlType = T.PrimeProduct(T.NodeChoice(required), qilAxis.XmlType.Cardinality); 

 
                // Without code bellow IlGeneragion gives stack overflow exception for the following passage.
                //
                //    
                //         
                //    
                // 
 
                // ToDo: remove this code when IlGen bug will be fixed.
                if (qilAxis.NodeType == QilNodeType.Filter) { 
                    QilLoop filter = (QilLoop) qilAxis;
                    filter.Body = f.And(filter.Body,
                        name  != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri))    : // ns:bar || bar
                        nsUri != null                  ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:* 
                        name  != null                  ? f.Eq(f.LocalNameOf(itr), f.String(name))     : // *:foo
                        /*name  == nsUri == null*/       f.True()                                       // * 
                    ); 
                    return filter;
                } 
            }

            return f.Filter(itr = f.For(qilAxis),
                name  != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri))    : // ns:bar || bar 
                nsUri != null                  ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:*
                name  != null                  ? f.Eq(f.LocalNameOf(itr), f.String(name))     : // *:foo 
                /*name  == nsUri == null*/       f.True()                                       // * 
            );
        } 

        // XmlNodeKindFlags from XPathNodeType
        static XmlNodeKindFlags[] XPathNodeType2QilXmlNodeKind = {
                /*Root                 */ XmlNodeKindFlags.Document, 
                /*Element              */ XmlNodeKindFlags.Element,
                /*Attribute            */ XmlNodeKindFlags.Attribute, 
                /*Namespace            */ XmlNodeKindFlags.Namespace, 
                /*Text                 */ XmlNodeKindFlags.Text,
                /*SignificantWhitespace*/ XmlNodeKindFlags.Text, 
                /*Whitespace           */ XmlNodeKindFlags.Text,
                /*ProcessingInstruction*/ XmlNodeKindFlags.PI,
                /*Comment              */ XmlNodeKindFlags.Comment,
                /*All                  */ XmlNodeKindFlags.Any 
        };
 
        QilNode BuildAxis(XPathAxis xpathAxis, XPathNodeType nodeType, string nsUri, string name) { 
            QilNode currentNode = GetCurrentNode();
            QilNode qilAxis; 

            switch (xpathAxis) {
            case XPathAxis.Ancestor         : qilAxis = f.Ancestor             (currentNode); break;
            case XPathAxis.AncestorOrSelf   : qilAxis = f.AncestorOrSelf       (currentNode); break; 
            case XPathAxis.Attribute        : qilAxis = f.Content              (currentNode); break;
            case XPathAxis.Child            : qilAxis = f.Content              (currentNode); break; 
            case XPathAxis.Descendant       : qilAxis = f.Descendant           (currentNode); break; 
            case XPathAxis.DescendantOrSelf : qilAxis = f.DescendantOrSelf     (currentNode); break;
            case XPathAxis.Following        : qilAxis = f.XPathFollowing       (currentNode); break; 
            case XPathAxis.FollowingSibling : qilAxis = f.FollowingSibling     (currentNode); break;
            case XPathAxis.Namespace        : qilAxis = f.XPathNamespace       (currentNode); break;
            case XPathAxis.Parent           : qilAxis = f.Parent               (currentNode); break;
            case XPathAxis.Preceding        : qilAxis = f.XPathPreceding       (currentNode); break; 
            case XPathAxis.PrecedingSibling : qilAxis = f.PrecedingSibling     (currentNode); break;
            case XPathAxis.Self             : qilAxis =                        (currentNode); break; 
            // Can be done using BuildAxisFilter() but f.Root() sets wrong XmlNodeKindFlags 
            case XPathAxis.Root             : return f.Root                    (currentNode);
            default                         : 
                qilAxis = null;
                Debug.Fail("Invalid EnumValue 'XPathAxis'");
                break;
            } 

            QilNode result = BuildAxisFilter(qilAxis, xpathAxis, nodeType, name, nsUri); 
            if ( 
                xpathAxis == XPathAxis.Ancestor       || xpathAxis == XPathAxis.Preceding ||
                xpathAxis == XPathAxis.AncestorOrSelf || xpathAxis == XPathAxis.PrecedingSibling 
            ) {
                result = f.BaseFactory.DocOrderDistinct(result);
                // To make grouping operator NOP we should always return path expressions in DOD.
                // I can't use Pattern factory here becasue Predicate() depends on fact that DOD() is 
                //     outmost node in reverse steps
            } 
            return result; 
        }
 
        public virtual QilNode Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string prefix, string name) {
            string nsUri = prefix == null ? null : this.environment.ResolvePrefix(prefix);
            return BuildAxis(xpathAxis, nodeType, nsUri, name);
        } 

        // "left/right" 
        public virtual QilNode JoinStep(QilNode left, QilNode right) { 
            f.CheckNodeSet(right);
            QilIterator leftIt = f.For(f.EnsureNodeSet(left)); 
            // in XPath 1.0 step is always nodetest and as a result it can't contain last().
            right = fixupVisitor.Fixup(right, /*current:*/leftIt, /*last:*/null);
            numFixupCurrent  -= fixupVisitor.numCurrent ;
            numFixupPosition -= fixupVisitor.numPosition; 
            numFixupLast     -= fixupVisitor.numLast    ;
            return f.DocOrderDistinct(f.Loop(leftIt, right)); 
        } 

        // "nodeset[predicate]" 
        // XPath spec $3.3 (para 5)
        public virtual QilNode Predicate(QilNode nodeset, QilNode predicate, bool isReverseStep) {
            if (isReverseStep) {
                Debug.Assert(nodeset.NodeType == QilNodeType.DocOrderDistinct, 
                    "ReverseAxe in Qil is actuly reverse and we compile them here in builder by wrapping to DocOrderDistinct()"
                ); 
                // The trick here is that we unwarp it back, compile as regular predicate and wrap again. 
                // this way this wat we hold invariant that path expresion are always DOD and make predicates on reverse axe
                // work as specified in XPath 2.0 FS: http://www.w3.org/TR/xquery-semantics/#id-axis-steps 
                nodeset = ((QilUnary) nodeset).Child;
            }

            nodeset = f.EnsureNodeSet(nodeset); 

            // Prepocess predicate: if (predicate is number) then predicate := (position() == predicate) 
            if (!f.IsAnyType(predicate)) { 
                if (predicate.XmlType.TypeCode == XmlTypeCode.Double) {
                    predicate = f.Eq(GetCurrentPosition(), predicate); 
                } else {
                    predicate = f.ConvertToBoolean(predicate);
                }
            } else { 
                QilIterator i;
                predicate = f.Loop(i = f.Let(predicate), 
                    f.Conditional(f.IsType(i, T.Double), 
                        f.Eq(GetCurrentPosition(), f.TypeAssert(i, T.DoubleX)),
                        f.ConvertToBoolean(i) 
                    )
                );
            }
 
            // Mirgeing nodeset and predicate:
            // 1. Predicate contains 0 last() : 
            //      for $i in nodeset 
            //      where predicate
            //      return $i 
            //   ToDo: Currently we are keepeing old output to minimize diff.
            // 2. Predicate contains 1 last()
            //      let $cach := nodeset return
            //          for $i in $cach 
            //          where predicate(length($cach))
            //          return $i 
            //   ToDo: This is a little optimisation we can do or don't do. 
            // 3. Predicate contains 2+ last()
            //      let $cash := nodeset return 
            //          let $size := length($cash) return
            //              for $i in $cash
            //              where predicate($size)
            //              return $i 

            QilNode result; 
            if (numFixupLast != 0 && fixupVisitor.CountUnfixedLast(predicate) != 0) { 
                // this subtree has unfixed last() nodes
                QilIterator cash = f.Let(nodeset); 
                QilIterator size = f.Let(f.XsltConvert(f.Length(cash), T.DoubleX));
                QilIterator it   = f.For(cash);
                predicate = fixupVisitor.Fixup(predicate, /*current:*/it, /*last:*/size);
                numFixupCurrent  -= fixupVisitor.numCurrent ; 
                numFixupPosition -= fixupVisitor.numPosition;
                numFixupLast     -= fixupVisitor.numLast    ; 
                result = f.Loop(cash, f.Loop(size, f.Filter(it, predicate))); 
            }else {
                QilIterator it = f.For(nodeset); 
                predicate = fixupVisitor.Fixup(predicate, /*current:*/it, /*last:*/null);
                numFixupCurrent  -= fixupVisitor.numCurrent ;
                numFixupPosition -= fixupVisitor.numPosition;
                numFixupLast     -= fixupVisitor.numLast    ; 
                result = f.Filter(it, predicate);
            } 
            if (isReverseStep) { 
                result = f.DocOrderDistinct(result);
            } 
            return result;
        }

        public virtual QilNode Variable(string prefix, string name) { 
            return this.environment.ResolveVariable(prefix, name);
        } 
 
        public virtual QilNode Function(string prefix, string name, IList args) {
            Debug.Assert(!args.IsReadOnly, "Writable collection expected"); 
            if (prefix.Length == 0) {
                FunctionInfo func;
                if (FunctionTable.TryGetValue(name, out func)) {
                    func.CastArguments(args, name, f); 

                    switch (func.id) { 
                    case FuncId.Not             : return f.Not(args[0]); 
                    case FuncId.Last            : return GetLastPosition();
                    case FuncId.Position        : return GetCurrentPosition(); 
                    case FuncId.Count           : return f.XsltConvert(f.Length(f.DocOrderDistinct(args[0])), T.DoubleX);
                    case FuncId.LocalName       : return args.Count == 0 ? f.LocalNameOf(GetCurrentNode()) : LocalNameOfFirstNode(args[0]);
                    case FuncId.NamespaceUri    : return args.Count == 0 ? f.NamespaceUriOf(GetCurrentNode()) : NamespaceOfFirstNode(args[0]);
                    case FuncId.Name            : return args.Count == 0 ? NameOf(GetCurrentNode()) : NameOfFirstNode(args[0]); 
                    case FuncId.String          : return args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : f.ConvertToString(args[0]);
                    case FuncId.Number          : return args.Count == 0 ? f.XsltConvert(f.XPathNodeValue(GetCurrentNode()), T.DoubleX) : f.ConvertToNumber(args[0]); 
                    case FuncId.Boolean         : return f.ConvertToBoolean(args[0]); 
                    case FuncId.True            : return f.True();
                    case FuncId.False           : return f.False(); 
                    case FuncId.Id              : return f.DocOrderDistinct(f.Id(GetCurrentNode(), args[0]));
                    case FuncId.Concat          : return f.StrConcat(args);
                    case FuncId.StartsWith      : return f.InvokeStartsWith(args[0], args[1]);
                    case FuncId.Contains        : return f.InvokeContains(args[0], args[1]); 
                    case FuncId.SubstringBefore : return f.InvokeSubstringBefore(args[0], args[1]);
                    case FuncId.SubstringAfter  : return f.InvokeSubstringAfter(args[0], args[1]); 
                    case FuncId.Substring       : 
                        return args.Count == 2 ? f.InvokeSubstring(args[0], args[1]) : f.InvokeSubstring(args[0], args[1], args[2]);
                    case FuncId.StringLength    : 
                        return f.XsltConvert(f.StrLength(args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : args[0]), T.DoubleX);
                    case FuncId.Normalize       :
                        return f.InvokeNormalizeSpace(args.Count == 0 ? f.XPathNodeValue(GetCurrentNode()) : args[0]);
                    case FuncId.Translate       : return f.InvokeTranslate(args[0], args[1], args[2]); 
                    case FuncId.Lang            : return f.InvokeLang(args[0], GetCurrentNode());
                    case FuncId.Sum             : return Sum(f.DocOrderDistinct(args[0])); 
                    case FuncId.Floor           : return f.InvokeFloor(args[0]); 
                    case FuncId.Ceiling         : return f.InvokeCeiling(args[0]);
                    case FuncId.Round           : return f.InvokeRound(args[0]); 
                    default:
                        Debug.Fail(func.id + " is present in the function table, but absent from the switch");
                        return null;
                    } 
                }
            } 
 
            return this.environment.ResolveFunction(prefix, name, args, (IFocus)this);
        } 

        QilNode LocalNameOfFirstNode(QilNode arg) {
            f.CheckNodeSet(arg);
            if (arg.XmlType.IsSingleton) { 
                return f.LocalNameOf(arg);
            } else { 
                QilIterator i; 
                return f.StrConcat(f.Loop(i = f.FirstNode(arg), f.LocalNameOf(i)));
            } 
        }

        QilNode NamespaceOfFirstNode(QilNode arg) {
            f.CheckNodeSet(arg); 
            if (arg.XmlType.IsSingleton) {
                return f.NamespaceUriOf(arg); 
            } else { 
                QilIterator i;
                return f.StrConcat(f.Loop(i = f.FirstNode(arg), f.NamespaceUriOf(i))); 
            }
        }

        QilNode NameOf(QilNode arg) { 
            f.CheckNodeNotRtf(arg);
            // ToDo: NameOf QIL node returns QName, so we cannot use it here. 
            // We may want to introduce a new QIL node that returns a string. 
            if (arg is QilIterator) {
                QilIterator p, ln; 
                return f.Loop(p = f.Let(f.PrefixOf(arg)), f.Loop(ln = f.Let(f.LocalNameOf(arg)),
                    f.Conditional(f.Eq(f.StrLength(p), f.Int32(0)), ln, f.StrConcat(p, f.String(":"), ln)
                )));
            } else { 
                QilIterator let = f.Let(arg);
                return f.Loop(let, /*recursion:*/NameOf(let)); 
            } 
        }
 
        QilNode NameOfFirstNode(QilNode arg) {
            f.CheckNodeSet(arg);
            if (arg.XmlType.IsSingleton) {
                return NameOf(arg); 
            } else {
                QilIterator i; 
                return f.StrConcat(f.Loop(i = f.FirstNode(arg), NameOf(i))); 
            }
        } 

        QilNode Sum(QilNode arg) {
            f.CheckNodeSet(arg);
            QilIterator i; 
            return f.Sum(f.Sequence(f.Double(0d), f.Loop(i = f.For(arg), f.ConvertToNumber(i))));
        } 
 
        enum XPathOperatorGroup {
            Unknown   , 
            Logical   ,
            Equality  ,
            Relational,
            Arithmetic, 
            Negate    ,
            Union     , 
        } 

        static XPathOperatorGroup[] OperatorGroup = { 
            /*Unknown   */ XPathOperatorGroup.Unknown   ,
            /*Or        */ XPathOperatorGroup.Logical   ,
            /*And       */ XPathOperatorGroup.Logical   ,
            /*Eq        */ XPathOperatorGroup.Equality  , 
            /*Ne        */ XPathOperatorGroup.Equality  ,
            /*Lt        */ XPathOperatorGroup.Relational, 
            /*Le        */ XPathOperatorGroup.Relational, 
            /*Gt        */ XPathOperatorGroup.Relational,
            /*Ge        */ XPathOperatorGroup.Relational, 
            /*Plus      */ XPathOperatorGroup.Arithmetic,
            /*Minus     */ XPathOperatorGroup.Arithmetic,
            /*Multiply  */ XPathOperatorGroup.Arithmetic,
            /*Divide    */ XPathOperatorGroup.Arithmetic, 
            /*Modulo    */ XPathOperatorGroup.Arithmetic,
            /*UnaryMinus*/ XPathOperatorGroup.Negate    , 
            /*Union     */ XPathOperatorGroup.Union     , 
        };
 
        static QilNodeType[] QilOperator = {
            /*Unknown    */ QilNodeType.Unknown ,
            /*Or         */ QilNodeType.Or      ,
            /*And        */ QilNodeType.And     , 
            /*Eq         */ QilNodeType.Eq      ,
            /*Ne         */ QilNodeType.Ne      , 
            /*Lt         */ QilNodeType.Lt      , 
            /*Le         */ QilNodeType.Le      ,
            /*Gt         */ QilNodeType.Gt      , 
            /*Ge         */ QilNodeType.Ge      ,
            /*Plus       */ QilNodeType.Add     ,
            /*Minus      */ QilNodeType.Subtract,
            /*Multiply   */ QilNodeType.Multiply, 
            /*Divide     */ QilNodeType.Divide  ,
            /*Modulo     */ QilNodeType.Modulo  , 
            /*UnaryMinus */ QilNodeType.Negate  , 
            /*Union      */ QilNodeType.Sequence,
        }; 

        // XmlNodeType(s) of nodes by XPathAxis
        static XmlNodeKindFlags[] XPathAxisMask = {
            /*Unknown         */ XmlNodeKindFlags.None, 
            /*Ancestor        */ XmlNodeKindFlags.Element | XmlNodeKindFlags.Document,
            /*AncestorOrSelf  */ XmlNodeKindFlags.Any, 
            /*Attribute       */ XmlNodeKindFlags.Attribute, 
            /*Child           */ XmlNodeKindFlags.Content,
            /*Descendant      */ XmlNodeKindFlags.Content, 
            /*DescendantOrSelf*/ XmlNodeKindFlags.Any,
            /*Following       */ XmlNodeKindFlags.Content,
            /*FollowingSibling*/ XmlNodeKindFlags.Content,
            /*Namespace       */ XmlNodeKindFlags.Namespace, 
            /*Parent          */ XmlNodeKindFlags.Element | XmlNodeKindFlags.Document,
            /*Preceding       */ XmlNodeKindFlags.Content, 
            /*PrecedingSibling*/ XmlNodeKindFlags.Content, 
            /*Self            */ XmlNodeKindFlags.Any,
            /*Root            */ XmlNodeKindFlags.Document, 
        };

        // ----------------------------------------------------------------
        internal enum FuncId { 
            Last = 0,
            Position, 
            Count, 
            LocalName,
            NamespaceUri, 
            Name,
            String,
            Number,
            Boolean, 
            True,
            False, 
            Not, 
            Id,
            Concat, 
            StartsWith,
            Contains,
            SubstringBefore,
            SubstringAfter, 
            Substring,
            StringLength, 
            Normalize, 
            Translate,
            Lang, 
            Sum,
            Floor,
            Ceiling,
            Round 
        };
 
        public static readonly XmlTypeCode[] argAny      = {XmlTypeCode.Item}; 
        public static readonly XmlTypeCode[] argNodeSet  = {XmlTypeCode.Node};
        public static readonly XmlTypeCode[] argBoolean  = {XmlTypeCode.Boolean}; 
        public static readonly XmlTypeCode[] argDouble   = {XmlTypeCode.Double};
        public static readonly XmlTypeCode[] argString   = {XmlTypeCode.String};
        public static readonly XmlTypeCode[] argString2  = {XmlTypeCode.String, XmlTypeCode.String};
        public static readonly XmlTypeCode[] argString3  = {XmlTypeCode.String, XmlTypeCode.String, XmlTypeCode.String}; 
        public static readonly XmlTypeCode[] argFnSubstr = {XmlTypeCode.String, XmlTypeCode.Double, XmlTypeCode.Double};
 
        public static Dictionary FunctionTable = CreateFunctionTable(); 
        private static Dictionary CreateFunctionTable() {
            Dictionary table = new Dictionary(36); 
            table.Add("last"               , new FunctionInfo(FuncId.Last           , 0, 0, null));
            table.Add("position"           , new FunctionInfo(FuncId.Position       , 0, 0, null));
            table.Add("name"               , new FunctionInfo(FuncId.Name           , 0, 1, argNodeSet));
            table.Add("namespace-uri"      , new FunctionInfo(FuncId.NamespaceUri   , 0, 1, argNodeSet)); 
            table.Add("local-name"         , new FunctionInfo(FuncId.LocalName      , 0, 1, argNodeSet));
            table.Add("count"              , new FunctionInfo(FuncId.Count          , 1, 1, argNodeSet)); 
            table.Add("id"                 , new FunctionInfo(FuncId.Id             , 1, 1, argAny)); 
            table.Add("string"             , new FunctionInfo(FuncId.String         , 0, 1, argAny));
            table.Add("concat"             , new FunctionInfo(FuncId.Concat         , 2, FunctionInfo.Infinity, null)); 
            table.Add("starts-with"        , new FunctionInfo(FuncId.StartsWith     , 2, 2, argString2));
            table.Add("contains"           , new FunctionInfo(FuncId.Contains       , 2, 2, argString2));
            table.Add("substring-before"   , new FunctionInfo(FuncId.SubstringBefore, 2, 2, argString2));
            table.Add("substring-after"    , new FunctionInfo(FuncId.SubstringAfter , 2, 2, argString2)); 
            table.Add("substring"          , new FunctionInfo(FuncId.Substring      , 2, 3, argFnSubstr));
            table.Add("string-length"      , new FunctionInfo(FuncId.StringLength   , 0, 1, argString)); 
            table.Add("normalize-space"    , new FunctionInfo(FuncId.Normalize      , 0, 1, argString)); 
            table.Add("translate"          , new FunctionInfo(FuncId.Translate      , 3, 3, argString3));
            table.Add("boolean"            , new FunctionInfo(FuncId.Boolean        , 1, 1, argAny)); 
            table.Add("not"                , new FunctionInfo(FuncId.Not            , 1, 1, argBoolean));
            table.Add("true"               , new FunctionInfo(FuncId.True           , 0, 0, null));
            table.Add("false"              , new FunctionInfo(FuncId.False          , 0, 0, null));
            table.Add("lang"               , new FunctionInfo(FuncId.Lang           , 1, 1, argString)); 
            table.Add("number"             , new FunctionInfo(FuncId.Number         , 0, 1, argAny));
            table.Add("sum"                , new FunctionInfo(FuncId.Sum            , 1, 1, argNodeSet)); 
            table.Add("floor"              , new FunctionInfo(FuncId.Floor          , 1, 1, argDouble)); 
            table.Add("ceiling"            , new FunctionInfo(FuncId.Ceiling        , 1, 1, argDouble));
            table.Add("round"              , new FunctionInfo(FuncId.Round          , 1, 1, argDouble)); 
            return table;
        }

        public static bool IsFunctionAvailable(string localName, string nsUri) { 
            if (nsUri.Length != 0) {
                return false; 
            } 
            return FunctionTable.ContainsKey(localName);
        } 

        private class FixupVisitor : QilReplaceVisitor {
            new QilPatternFactory f;
            QilNode     fixupCurrent, fixupPosition, fixupLast; // fixup nodes we are replacing 
            QilIterator current;
            QilNode     last;               // expressions we are using to replace fixupNodes 
            bool        justCount;          // Don't change tree, just count 
            IXPathEnvironment environment;  // temp solution
            public int  numCurrent, numPosition, numLast; // here we are counting all replacements we have made 

            public FixupVisitor(QilPatternFactory f, QilNode fixupCurrent, QilNode fixupPosition, QilNode fixupLast) : base(f.BaseFactory) {
                this.f             = f;
                this.fixupCurrent  = fixupCurrent; 
                this.fixupPosition = fixupPosition;
                this.fixupLast     = fixupLast    ; 
            } 

            public QilNode Fixup(QilNode inExpr, QilIterator current, QilNode last) { 
                this.current  = current ;
                this.last     = last    ;
                Debug.Assert(current != null);
                this.justCount  = false; 
                this.environment = null;
                numCurrent = numPosition = numLast = 0; 
                inExpr = VisitAssumeReference(inExpr); 
#if StopMaskOptimisation
                SetStopVisitMark(inExpr, /*stop*/true); 
#endif
                return inExpr;
            }
 
            public QilNode Fixup(QilNode inExpr, IXPathEnvironment environment) {
                Debug.Assert(environment != null); 
                this.justCount   = false; 
                this.current     = null;
                this.environment = environment; 
                numCurrent = numPosition = numLast = 0;
                inExpr = VisitAssumeReference(inExpr);
#if StopMaskOptimisation
                // Don't need 
                //SetStopVisitMark(inExpr, /*stop*/true);
#endif 
                return inExpr; 
            }
 
            public int CountUnfixedLast(QilNode inExpr) {
                this.justCount  = true;
                numCurrent = numPosition = numLast = 0;
                VisitAssumeReference(inExpr); 
                return numLast;
            } 
 
            protected override QilNode VisitUnknown(QilNode unknown) {
                Debug.Assert(unknown.NodeType == QilNodeType.Unknown); 
                if (unknown == fixupCurrent) {
                    numCurrent ++;
                    if (! justCount) {
                        if (this.environment != null) { 
                            unknown = this.environment.GetCurrent();
                        } else if (this.current != null) { 
                            unknown = this.current; 
                        }
                    } 
                } else if (unknown == fixupPosition) {
                    numPosition ++;
                    if (! justCount) {
                        if (this.environment != null) { 
                            unknown = this.environment.GetPosition();
                        } else if (this.current != null) { 
                            // position can be in predicate only and in predicate current olways an iterator 
                            unknown = f.XsltConvert(f.PositionOf((QilIterator)this.current), T.DoubleX);
                        } 
                    }
                } else if (unknown == fixupLast) {
                    numLast ++;
                    if (! justCount) { 
                        if (this.environment != null) {
                            unknown = this.environment.GetLast(); 
                        } else if (this.current != null) { 
                            Debug.Assert(this.last != null);
                            unknown = this.last; 
                        }
                    }
                }
                Debug.Assert(unknown != null); 
                return unknown;
            } 
 
#if StopMaskOptimisation
            // This optimisation marks subtrees that was fixed already and prevents FixupVisitor from 
            // visiting them again. The logic is brokken, because when unfixed tree is added inside fixed one
            // it never fixed anymore.
            // This happens in all cortasian productions now.
            // Excample "a/b=c". 'c' is added inside 'b' 

            // I belive some optimisation is posible and would be nice to have. 
            // We may change the way we generating cortasian product. 

            protected override QilNode Visit(QilNode n) { 
                if (GetStopVisitMark(n)) {
                    // Optimisation:
                    // This subtree was fixed already. No need to go inside it.
                    if (! justCount) { 
                        SetStopVisitMark(n, /*stop*/false); // We clean this annotation
                    } 
                    return n; 
                }
                return base.Visit(n); 
            }

            void SetStopVisitMark(QilNode n, bool stop) {
                if (n.Type != QilNodeType.For && n.Type != QilNodeType.Let) { 
                    XsltAnnotation.Write(n)[0] = (stop ? /*any object*/fixupCurrent : null);
                } else { 
                    // we shouldn't alter annotation of "reference" nodes (Iterators, Functions, ...) 
                }
            } 
            bool GetStopVisitMark(QilNode n) {
                return XsltAnnotation.Write(n)[0] != null;
            }
#endif 
        }
 
        internal class FunctionInfo { 
            public T                id;
            public int              minArgs; 
            public int              maxArgs;
            public XmlTypeCode[]    argTypes;

            public const int        Infinity = int.MaxValue; 

            public FunctionInfo(T id, int minArgs, int maxArgs, XmlTypeCode[] argTypes) { 
                Debug.Assert(maxArgs == 0 || maxArgs == Infinity || argTypes != null && argTypes.Length == maxArgs); 
                this.id       = id;
                this.minArgs  = minArgs; 
                this.maxArgs  = maxArgs;
                this.argTypes = argTypes;
            }
 
            public static void CheckArity(int minArgs, int maxArgs, string name, int numArgs) {
                if (minArgs <= numArgs && numArgs <= maxArgs) { 
                    return; 
                }
 
                // Possible cases:
                // [0, 0], [1, 1], [2, 2], [3, 3]
                // [0, 1], [1, 2], [2, 3], [2, +inf]
                // [1, 3], [2, 4] 
                string resId;
                if (minArgs == maxArgs) { 
                    resId = Res.XPath_NArgsExpected; 
                } else {
                    if (maxArgs == minArgs + 1) { 
                        resId = Res.XPath_NOrMArgsExpected;
                    } else if (numArgs < minArgs) {
                        resId = Res.XPath_AtLeastNArgsExpected;
                    } else { 
                        // This case is impossible for standard XPath/XSLT functions
                        Debug.Assert(numArgs > maxArgs); 
                        resId = Res.XPath_AtMostMArgsExpected; 
                    }
                } 
                throw new XPathCompileException(resId, name, minArgs.ToString(CultureInfo.InvariantCulture), maxArgs.ToString(CultureInfo.InvariantCulture));
            }

            public void CastArguments(IList args, string name, XPathQilFactory f) { 
                CheckArity(this.minArgs, this.maxArgs, name, args.Count);
 
                // Convert arguments to the appropriate types 
                if (maxArgs == Infinity) {
                    // Special case for concat() function 
                    for (int i = 0; i < args.Count; i++) {
                        args[i] = f.ConvertToType(XmlTypeCode.String, args[i]);
                    }
                } else { 
                    for (int i = 0; i < args.Count; i++) {
                        if (argTypes[i] == XmlTypeCode.Node && f.CannotBeNodeSet(args[i])) { 
                            throw new XPathCompileException(Res.XPath_NodeSetArgumentExpected, name, (i + 1).ToString(CultureInfo.InvariantCulture)); 
                        }
                        args[i] = f.ConvertToType(argTypes[i], args[i]); 
                    }
                }
            }
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK